この記事はeeic Advent Calendar 2016 その2の10日目になります。
あまりeeic内部向けじゃないかもしれません。
#まえがき
この記事の目標は「ゲームを作ってみたい!」と思っていて、かつUnityをそこまでよく知らない方々にUnityによるゲーム製作のとっつきやすさ、そして効率の良さをお伝えすることです。せっかくのAdvent Calendarという機会に、多くの人にUnityを触ってもらえるきっかけを作りたいなと思った次第です。
今回はタイトルの通り3DのRPG、もしくはアクションゲームの土台ということで、
- 3Dのマップ上で、(3Dオブジェクトの描画)
- 3Dのキャラクターがキー操作で動いてくれて、(モーション)
- なんか画面の雰囲気もそこそこ良い(ライティング)
といったものを短時間で用意できることを目指します。括弧内の3Dオブジェクト描画、3Dモデルのアニメーション、そしてライティングといったものは3Dゲームを作る際に目に見えて重要なので、これらを「土台」と呼びたいと思います。
また、Unityの個々の機能の解説は余裕があれば、といった程度に抑えさせて頂きます。取り敢えず自分で作ったものが動くことが大事かなと思ったので。ごめんなさい。
#ゲーム製作の手段
まずはUnityを選ぶ理由から。早く動かしたい方は飛ばして頂いて構いません。
「そうだ、ゲーム作ろう。」と思った時、まずはどんな環境で作るのかを考える必要がありますね。よくある環境を列挙するとこんな感じ。
- RPGツクール
- DXライブラリ(もしくはDirectX直叩き)
- OpenGL
- Unity
この中から3Dゲームを作るという前提で、とっつきやすさ・効率の良さの観点から見ると、まずUnityに軍配が上がるでしょう。主な理由としては以下の通り。
- GUIが用意されており、デバッグが容易(開発画面の中でプレビュー出来る)
- マルチプラットフォームがデフォ(Win, Mac, Linux, Web, スマホ, PlayStation,...)
- 有志や企業が作ったAsset(主に素材やツール)が利用・購入できる
- 最近流行りなので新鮮な情報が豊富
- コンポーネント指向が分かりやすい(オブジェクトを置く! それを動かすためのスクリプトを書いてくっつける!)
- テクスチャの描画や3Dモデルの扱い等、面倒なところは全てUnityが引き受けてくれる
- 取り敢えず3Dゲーム製作における面倒なところはUnityがやってくれる
RPGツクールはコードを書く必要がないためとっつきやすさはダントツですが、3D描画が不可能な上にやはり自由度が低いです。また、DirectXは低レイヤのプログラムをC++でゴリゴリ書く強マンには使いこなせますが、とっつきやすいとは言えません。OpenGLは特に言うことがないからいいや。
要するにUnityは「自分の思い通りのゲームを見栄え良く作りたいけど面倒なのは勘弁」というワガママなゲーム作りたいマンにとって救世主のような存在です。早速作ってみましょう。
#Unityの導入
https://unity3d.com/jp/get-unity/download/archive
このページから環境に合うものをインストールしてください。インストーラに従えば問題はないと思います。現時点での最新版は5.5.0ですが、僕の環境は5.3.4なので、心配な方はそちらを導入してみてください。
#3Dマップを作る
###新規プロジェクトの作成とUI
RPGゲームですから、まずはマップがないと始まりません。なのでマップを作成する必要があるわけですが、ひとまずUnityを起動して、適当な名前を付けてNew Projectを作成します。
ここで、後半で使うAssetをロードしておきます。先程も出てきましたが、Assetというのは既存の素材やスクリプト、ツールを指します。Asset Storeで気に入ったものを探してダウンロード・購入するのが一般的ですが、デフォルトのAssetも充実しています。今回はCamerasとEffectsにチェックを付けて下さい。
起動すると、まっさらな画面が登場します。簡単にUIを説明しておくと、
- 左上・・・Hierarchyウィンドウ。編集中のシーンに配置されているオブジェクトの一覧。名前の通り、親子関係が作れる。
- 中上・・・Scene,Game,Animatorウィンドウ。Sceneウィンドウでは画面を自由な視点から眺め、オブジェクトを移動させたり出来る。スクロールでズーム、ドラッグで視点移動、ホイールドラッグで視点の回転が可能。Gameウィンドウでは、上部の再生ボタンを押すことで現在のシーンをプレビュー出来る。Animatorについては後述。
- 右上・・・Inspectorウィンドウ。選択中のオブジェクトの詳細が見られ、パラメータの設定やコンポーネントの追加もここから行う。
- 下・・・Projectウィンドウでは現在のProjectにロードされているファイルやAsset、シーンなどが一覧できる。Consoleではエラーや警告表示、ログ出力によるデバッグが行える。
[注]シーンというのは所謂「一つの場面」で、Unityではこれを単位に開発が進む。一般のゲームで画面が暗転する時を想像してもらえると、シーンの区切りが分かりやすい。例えば、タイトル画面<->プレイ画面、町マップ<->家マップなど。
###Terrain(大地)の作成
UIの確認を終えたところで、早速地面を作ります。ツールバーからGameObject->3D Object->Terrain(大地)を選択。するとSceneウィンドウに巨大な白い平面が現れるはずです。今からこの平面を凸凹させたり木や草を生やして地面っぽくしていくわけですが、木の3Dモデルを作っている暇はないのでAssetを入手しましょう。
中上部、Gameタブの隣にあるAsset Storeタブをクリックし、検索ボックスに"Nature Starter Kit2"と入力し、ヒットしたAssetをインポートしましょう。無償なので安心。
「インポート」をクリックし、ダウンロード後現れたウィンドウでもImportをクリックすると、現在のプロジェクトに勝手にインポートしてくれます。ProjectウィンドウにNatureStarterKit2が出現しているのが確認できますね。
HierarchyウィンドウでTerrainを選択し、Inspectorでコンポーネントの上部にあるブラシのようなマークをクリックすると、Edit Textures..から大地テクスチャを選べます。ProjectウィンドウでNatureStarterKit2->Texturesを辿り、ground1というテクスチャをAdd Terrain Textureウィンドウにドラッグして下さい。Addをクリックすれば、大地がテクスチャに染まってくれます。
もちろんテクスチャは複数選択可能で、砂っぽいテクスチャを追加してブラシでTerrainに塗ってあげれば、砂場を作ることも出来ます。
###木と草を生やす
次は植物ですね。ブラシマークの右にある木のマークをクリックすると、テクスチャと同様に木のモデルを追加できます。ProjectウィンドウからNatureStarterKit2->Natureを辿り、tree01-04の中から好きなものをAdd Treeにドラッグしてください。さらに右のエリアでは全く同じ要領で草も生やせます。草のテクスチャはTextureフォルダのgrass01あたりで良いと思います。
[注]Natureフォルダの中にある木や草のモデルは、Prefabと言ってメッシュやテクスチャの情報が一塊になったものです。Prefabはアタッチされたスクリプトやそのパラメータ(Public変数)まで保存でき、さらにスクリプトから簡単に複製・削除できるため実装で重宝します。冒頭の音ゲーではノードがPrefabでした。
好き勝手に生やすと、こんな感じ。距離が遠いので草は見えませんね。
###地形を弄る
さて、次は地形を変えます。Terrainコンポーネント上で、ブラシマークの二つ左にあるPaint Heightマークを押します。ここでブラシの形状とブラシサイズを好きに選び、マップ上をドラッグするとみるみるうちに地面が盛り上がり、丘や山、谷などを作ることができます。ブラシサイズは100にしちゃったほうが作りやすいですね。
そんなこんなで出来上がったマップがこれです。
なんかこれシムシティでやったことある。
ここまではゲーム制作というより完全にシムシティの最序盤(子供の頃を思い出した)でしかないのですが、次節からゲームっぽさが出てくるのでもう少し我慢して頂けるとありがたいです。
やってることはRPGツクールの3D版って感じなのですが、でもやっぱり3Dのマップをこんな風に手軽に作れちゃうのは便利です。3Dモデルを自作した場合のインポートも(今回は省きますが)とても簡単なので、ストレスレス。
#3Dのキャラクターを動かす
###Unityちゃんの入手と配置
マップが出来たので、次はキャラクターを置いて動かします。3Dキャラのモデリングには技術と莫大な労力が必要なので、ここでもAssetに頼ります。キャラの無償アセットもクオリティの高いものが数多く存在しますが、せっかくのUnity入門なのでUnityちゃんを使ってみましょう。
Asset Storeを開き、"unity-chan"で検索。先程と同じ手順でインポートします。ハイクオリティの3Dモデルが無償で配布されているのは本当にありがたい限りです。
Projectウィンドウを見ると、UnityChanフォルダが追加されているのが分かります。中には様々なフォルダがありますが、取り敢えずUnityちゃんの3Dモデルをシーンに配置するため、Modelsフォルダ内のunitychan.fbxをHierarchyウィンドウにドラッグ・ドロップします。
どこに配置されたか分かりづらいのですが、Hierarchyの一覧で"unitychan"をダブルクリックすればSceneウィンドウでUnityちゃんのいる位置にズームしてくれます。おそらく、初期状態だとTerrainの最果てに佇んでいるor埋まっているはずです。いい感じの位置に移動させたいのですが、Unityではオブジェクトの移動方法として、以下の二通りあります。
- 移動ツールを使う方法(画面左上、手のマークの右にあるツールボタンをクリック)
- Inspectorウィンドウ->Transformから、Positionのパラメータを設定する方法
後者のPositionはスクリプトから変更できるため、実装では度々使うことになりますが、今は前者の方法で良いと思います。移動ツールボタンを押すと、選択中のオブジェクトからX,Y,Z軸に沿った矢印が表示されます。
この状態で矢印をドラッグするか、オブジェクト自体をドラッグすることによってシーン上での移動が可能です。森の中にUnityちゃんを移動させましょう。
こんな感じに(謎ポーズはデフォルトなので気にしません)。
###モーションの遷移(Animator, Mecanim)
次に3Dモデルをキー操作で移動、アニメーションさせたいわけですが、ここで先程述べたAnimatorの出番です。Animatorというのは状態遷移図のような見た目をしており、モーションごとに状態とパラメータを用意して、パラメータの値によって状態を遷移させる(つまりモーションも変わる)という仕組みです。
自作した3Dモデルの場合は自分でAnimatorのコントローラ(状態遷移図)を作る必要がありますが、Unityちゃんには当然付属しているので早速開いてみましょう。まず、Unityちゃんを選択した状態でInspectorを見ると、Animatorというコンポーネントがアタッチされているのが分かります。この中のControllerパラメータが"None"になっていると思いますので、この部分にUnitychanフォルダ->Animators->UnityChanLocomotionsをドラッグ・ドロップします。ついでにそのファイルをダブルクリックすると、コントローラの詳細を見ることが出来ます。
もろ状態遷移図。この図では、Entry(初期状態)がIdleにリンクしているため、起動直後はIdle(待機)モーションが再生されます。状態をクリックしてInspectorで詳細を見てみましょう。
Motionパラメータにモーションファイルを設定することでその状態で何のモーションを再生するのか指定できます(ここでは既に"WAIT00"が設定されていますね)。Transitionsには遷移先と遷移条件が設定できますが、下の画像ではIdle状態->Locomoction状態の遷移条件が、「Speedパラメータが0.1より大きい」であることが分かりますね。
###キー入力で移動・モーションを制御するスクリプト
実際にこれらをキー操作と対応させるには、スクリプトからキーボード入力(ここでは例えばUpキー)を読み取り、押された長さに比例したパラメータ(ここではSpeed)をAnimatorのコントローラに渡してあげればOKです。これらキー入力等のインプットが関わる処理は、基本的に自分でスクリプトを書く必要があります。ですので、今回はAsset同梱のスクリプトを見ながら少しだけコードの解説をしてみようと思います。
HierarchyでUnityちゃんを選択し、Inspectorの空いている部分にUnityChanフォルダ->Scripts->UnityChanControlScriptWithRgidBodyをドラッグ・ドロップしてください。これでUnityちゃんを動かす準備は整ったわけですが、今追加したスクリプトをダブルクリックしてみると、MonoDevelopでコードが見られます。コード解説は、早く動かしたい方は飛ばして頂いても結構です。
using UnityEngine;
using System.Collections;
// 必要なコンポーネントの列記
[RequireComponent(typeof (Animator))]
[RequireComponent(typeof (CapsuleCollider))]
[RequireComponent(typeof (Rigidbody))]
UnityスクリプトではUnityEngine名前空間を使用します。その次のRequireComponentというのは、このスクリプトがアタッチされたオブジェクトに否応なく追加して欲しいコンポーネントの型を宣言するもので、先程このスクリプトをアタッチした瞬間、UnityちゃんにRigidbody(Unityエンジンの物理法則を適用)とCapsuleCollider(当たり判定)が追加されたのはこのためです。
void Start ()
{
// Animatorコンポーネントを取得する
anim = GetComponent<Animator>();
// CapsuleColliderコンポーネントを取得する(カプセル型コリジョン)
col = GetComponent<CapsuleCollider>();
rb = GetComponent<Rigidbody>();
//メインカメラを取得する
cameraObject = GameObject.FindWithTag("MainCamera");
// CapsuleColliderコンポーネントのHeight、Centerの初期値を保存する
orgColHight = col.height;
orgVectColCenter = col.center;
}
初期化メソッドでは、GetComponentが多用されています。これを用いると、このスクリプトがアタッチされたオブジェクトの他コンポーネントを取得することが出来ます(もちろん使い方によって他オブジェクトのコンポーネントも取得できる)。ですのでここでは、anim,col,rb変数にUnityちゃんの持っているコンポーネントを格納しています。
// 以下、メイン処理.リジッドボディと絡めるので、FixedUpdate内で処理を行う.
void FixedUpdate ()
{
float h = Input.GetAxis("Horizontal"); // 入力デバイスの水平軸をhで定義
float v = Input.GetAxis("Vertical"); // 入力デバイスの垂直軸をvで定義
anim.SetFloat("Speed", v); // Animator側で設定している"Speed"パラメタにvを渡す
anim.SetFloat("Direction", h); // Animator側で設定している"Direction"パラメタにhを渡す
anim.speed = animSpeed; // Animatorのモーション再生速度に animSpeedを設定する
currentBaseState = anim.GetCurrentAnimatorStateInfo(0); // 参照用のステート変数にBase Layer (0)の現在のステートを設定する
rb.useGravity = true;//ジャンプ中に重力を切るので、それ以外は重力の影響を受けるようにする
FixedUpdateの序盤です。Unityで描画を更新する際、1フレームごとに処理を行うUpdateメソッドが一般に使われますが、物理エンジンは時間に基づいて計算を行っているため、ここでは0.02秒ごとに処理を行うFixedUpdateメソッド内に処理を記述しています。
h変数に上下キーの入力を、v変数に左右キーの入力を与えていますね。次の部分がモーション遷移の肝で、先程取得したAnimatorコンポーネントの"Speed","Direction"パラメータにそれぞれvとhの値を渡しています。これで、vの値が0.1以上となった時にIdleからLocomotionにモーションが遷移するというわけです。
// 以下、キャラクターの移動処理
velocity = new Vector3(0, 0, v); // 上下のキー入力からZ軸方向の移動量を取得
// キャラクターのローカル空間での方向に変換
velocity = transform.TransformDirection(velocity);
===中略===
// 上下のキー入力でキャラクターを移動させる
transform.localPosition += velocity * Time.fixedDeltaTime;
// 左右のキー入力でキャラクタをY軸で旋回させる
transform.Rotate(0, h * rotateSpeed, 0);
先程Animatorにパラメータを渡したので、キー入力でUnityちゃんのモーションが変わるようになりました。しかし、このままではUnityちゃんの位置が変わらないため、transform(ロードの必要なし)を用いて座標を変化させます。まず、v変数を三次元ベクトルに変換し、さらにUnityちゃん基準のベクトルに変換しています。これを行うと、Unityちゃんが向いている方向に応じて進行方向を変えることができます。
間の処理は省略しますが、結果的にvelocityをlocalPositionに、hをRotateに渡してやることで、Unityちゃんがキー入力に応じて移動・アニメーションします。また、解説ではありませんが、スクリプト最下部のOnGUIメソッドはプレビュー時に邪魔なのでコメントアウトしておいてください。
###カメラの調整とシーンの実行
さて、いい加減動かしたいのですがその前にカメラの話をする必要があります。Hierarchyを見るとMain Cameraというオブジェクトが存在していますが、Unityではこのカメラに移っている部分を画面として描画します。おそらく今の状態でプレビューを行ってもMain CameraがTerrainの最果てにあるため、Unityちゃんは映りません。Unityちゃんを移動させた時と同じ要領で、カメラをUnityちゃんの背後に移動させてください。
この辺でしょうか。右下のCamera PreviewにUnityちゃんの背中が移っていますね。
[注]Main Camera->CameraコンポーネントのProjectionパラメータが"Orthographic"(正投影)になっている場合は"Perspective"(透視投影)に変更してください。正投影は物体の遠近を無視した描画ですので、3Dゲームでは用いる機会はあまり無いと思います。
そしてこの状態で画面上部の再生ボタンを押すと、ちゃんと上下左右のキー入力によってUnityちゃんが移動してくれます。
###当たり判定の修正とカメラ追尾
無事Unityちゃんを移動させることが出来ましたが、動かしていると気になる点が幾つかあります。
- 木にぶつかるとカオスになる・・・CapsuleColliderによって当たり判定は上手く動作しているのですが、Unityちゃんが全方向からの力を素直に受けてしまっているため、このような事態が起きます。RigidBodyコンポーネントのConstraints(制約)パラメータを以下のように変更してあげることで回避できます。物理法則による3Dモデルの回転を禁止するという設定ですね。また、Capsule ColliderのRadiusとHeightをゼロにしていてください(影の描画がおかしくなるのを防ぐ)。
- カメラが固定されている・・・これは簡単なものであればUnityちゃんの座標を取得して追尾するスクリプトを書き、Main CameraにアタッチすればOKです。ただせっかくStandard Assetにぬるぬる動くオートカメラが用意されていますので、使ってみましょう。
ProjectウィンドウからStandard Assets->Cameras->Prefabsを辿り、MultipurposeCameraRigをシーンにドラッグしてください。位置も先程と同様に、Unityちゃんが映る位置に動かします。このオブジェクトの子孫にもMain Cameraがおり、先程まで使っていたカメラと競合してしまうため、元々あったMain Cameraを非アクティブ化しましょう。Hierarchyで選択してInspectorの最上部、オブジェクト名の左にあるチェックを外せばOKです。
MultipurposeCameraRigを選択し、Inspector->Auto CamのTargetパラメータが"None"になっていると思いますので、そこにUnityちゃんオブジェクトをドラッグ・ドロップしてください。方法だけしか解説出来ませんでしたが、これでカメラ追尾の準備が整いました。
この状態で動かして見ると、ちゃんと木にぶつかり、また丘を登っても不自然な動きをしません。カメラもAssetを使ったので当然ながらぬるぬる動いてくれます。
[注]追尾カメラがちょっと遠いと感じたら、MultipurposeCameraRigの位置ではなく、子孫のMain Cameraの位置をUnityちゃんに近づけてみてください。
#画面を綺麗にする
最後のひと押しです。Unityちゃんが森の中を走り回れるようになったものの、画面がまだ味気ないですね。画面の見栄えが良いと製作側もプレイヤー側もテンションが上がるので、せっかくならもう少し格好良く(幻想的に?)したいところ。
###Image Effectを使ってみる(適当パラメータ付き)
ここで、Standard Assetの中のImage Effect群の出番です。これらのエフェクトはUnity 4まではPro版(有償)でしか使用できませんでしたが、Unity 5以降はFree版にも開放されています。元々Proにしか無かったエフェクトだけに、かなり強力です。公式マニュアルによると、「創作に多くの時間をつぎ込むことなく、ゲームの見た目と雰囲気を良くします。」とのこと。正直ここからは個人の好みな感じもあるのですが、せっかくなのでコテコテにエフェクト掛けて、動画を見せて終えようと思います。くどかったらごめんなさい。
Projectウィンドウ->Standard Assets->Effects->ImageEffects->Scriptsの中に、数多くのイメージエフェクトがあります。使い方も至って簡単で、好きなスクリプト(エフェクト)をMain Cameraにアタッチして、パラメータを自分で調節するだけ。
全て紹介しても良いほど面白く強力なエフェクト揃いですが、序盤で使ったNature Starter Kit 2のデモで非常に効果的に用いられている7つに絞って解説したいと思います。パラメータ付きですが、こちらも好みによるので色々と調整して楽しんでみてください。
###Antialiasing
一般的なアンチエイリアシングです。ポリゴン描画時のジャギー(ギザギザ)をなくすことによって、滑らかな画面を演出します。パラメータ調節の必要は特になし。
###Sun Shafts
直訳すると「日光の筋」ですが、木漏れ日なんかの表現に使えます。シーンに最初から配置されている光源であるDirectional LightのColorを黄色やオレンジにし、このエフェクトのShafts Colorパラメータを濃いオレンジにすれば夕方感が出ていい感じになりますね。
Antialiasingと一緒にエフェクトを掛けると下の画像のようになります。ちょっとかっこよくなった。
###Screen Space Ambient Occulusion
スクリーンスペースのアンビエントオクルージョン(意味不明)。アンビエントオクルージョンについてはこちらを読んでみてください。要するに大域照明を考慮して物体の陰影を綺麗に描画する技法で、レイトレーシングの一種だそうです。このエフェクトを用いることによって草や木の接地感が増し、より雰囲気がリアルになります。
###Depth of Field
カメラに多少詳しい人なら分かると思いますが、被写界深度です。カメラからどの距離の範囲に「ピントを合わせているように見せる」かというエフェクトであり、基本的にUnityちゃんを基準にピントを合わせればよいでしょう。カメラに近すぎる草や、遠すぎる木なんかはぼやけるといいた表現が可能です。
ここまでの4つのエフェクトを、上記のパラメータ通りに掛けた結果はこんな感じ。画像だと分かりづらいですが着実に雰囲気は良くなってきています。もうちょい頑張れそう。
###Color Correction Curves
赤、緑、青それぞれの色曲線を変化させることによる色調補正です。どのように曲線を弄ればどう変わるのは比較的分かりにくいですが、画面の雰囲気をガラッと変えることが出来ます。色々試してみてください。
###Vignette and Chromatic Aberration
画面の四隅を暗くしてブラーを掛けることでカメラのレンズを覗き込んだかのような画面が得られます(ビネット効果)。ゲームには邪魔だと思うかもしれませんが、ぼんやりと掛ければ幻想的な雰囲気作りに一役買うのではないでしょうか。
###Bloom
光源や反射光が周囲に漏れ出すような表現が出来るエフェクト。冒頭の音ゲーでも使ってました。これ単体でも十分雰囲気良くなります。ただ掛けすぎるとまぶしいので適度に。
さて、紹介したいエフェクトは以上ですが、画面はどんな感じになったでしょうか?
まずは画像。
とてもよい!
最後に動画です。Twitterだと雰囲気が伝わりにくいのでYoutubeに上げました。キャプチャしながらのプレビューなので序盤カクついてますが、Image Effectだけでここまで雰囲気を良くできました。
https://youtu.be/Qmia_O3ahhw
#あとがき
いかがでしたでしょうか。かなり駆け足になってしまいましたが、特にキャラのモーション周りとImage Effect周りでUnityの真骨頂をお伝えできたのではないでしょうか。しかも一切コーディングせずにここまで来てしまいました。
Assetをバンバン使ったからこの速度でここまで来られたのは確かです。しかし、他のゲームと見た目が被ってしまう可能性のある3Dモデルはともかくとして、スクリプトや便利ツールについてはAssetを使うデメリットは殆どないと思います。チューニングは動かしてからでも出来ますし、もし納得いかなかったら自分で実装しましょう。
実際にゲームとして遊べるものを作るには、コーディングを避けるのは不可能です。シーン遷移やフラグの管理、会話イベントや戦闘、UIの更新などなど……コードを書いて実装しなければならない部分は無限にありますし、もちろんゲームの規模に応じた時間が掛かります。
ですがUnityのメリットはそういった「どうしようもない部分」以外に必要な労力が最低限に抑えられていて、かつ高いクオリティを維持できる点ではないでしょうか。実際にUnityを使ってゲームを作ってみると分かりますが、非常に心が折れにくい(モチベーションを保ちやすい)です。
この記事を読んで、ゲーム開発の敷居の低さを感じて頂けたなら何よりです。まずはAsset使ってでも何でも良いので、作って動いて遊べる楽しさを知ってみると、どんどん開発を進める原動力が生まれると思います。
ゲーム、作りましょう!