Hatena::ブログ(Diary)

komiyakの通り道 このページをアンテナに追加 RSSフィード

2014-12-16

Unity を使いはじめたばかりの頃の自分に伝えたい、Unity の基本 【2014年版】

これは Unity Advent Calendar 2014 の16日目の記事です。
昨日は sassembla@github さんの AssetBundleを高速に作る でした。





こんにちは。komiyak です。


普段、私は Web 系の業務システム開発を請け負うフリーランスプログラマをしているのですが、
ここ半年ほど、Unity を使ったゲーム開発に従事しておりました。

私は、フリーランスになる以前は、
家庭用ゲームソフトのデベロッパーゲームプログラマーをしておりました。
そのためゲーム開発に関しては、多少の知見があります。

しかし初めて Unity を触った時は、いろいろ戸惑いが多かったです。
Unity のお作法的に、どういうふうに実装をすればゲームが組み上がっていくのか、
全然イメージが湧きませんでした...。


ある程度 Unity になれてきた、今思い返すと、
勉強しはじめたばかりの頃の自分に、このことを伝えられていれば、
その後どんなに楽だったかなぁと、思う事柄があります。



今回は、それをまとめてみようと思い立ち、記事に致しました。


Unity に関する情報は、どれも初歩的な内容のみを扱っています。

しかし、これだけは伝えておきたい!と思えるものを、あれもこれもと書き足していくうちに、
広い範囲の、わりとまとまりのない内容になってしまいました...。

このようなもので、皆さまのお役に立つ記事になるかどうか不安になってきましたが、
この記事が、少しでも皆様のお力になればと幸いです。


記事の注意点


筆者は Unity を使った モバイルプラットフォームAndroid, iOS)向けの
アプリ開発経験しかありません。
そのため内容が、モバイルデバイスに向けた最適化に偏っています。

こちらも、予めご了承いただければと思います。



Unity に関する情報をどこから集めるべきか


Unity は公式のドキュメント、リファレンスの質が大変すばらしく、
かなりの情報をここから得ることができます。

Unity のチュートリアルとしては、ユーザーマニュアルの「ユーザーガイド」 をお勧めいたします。
こちらにひと通り目を通せば、Unity を使いはじめる準備は十分です。
ドットインストール様の「Unity 入門」というコンテンツも、
チュートリアルとして優れておりますので、合わせてオススメさせて頂きます。)



少し慣れてきた頃に、読みたいもの

Unity に少し慣れてきた頃に、下記の記事を読むことをオススメします。

翻訳元となっているドキュメントは、2012年7月 頃に作成されたもので、
現在では少し情報が古くなっています。
しかし、未だに読んで為になる情報が多いです。


この頃になってくると、より詳しい Unity の挙動を知るために、
公式のリファレンスマニュアル に目を通すことが増えてくるかと思います。

文量が多いので、最初から全部読もうとはせずに、
興味のあるところから少しずつ読み進めてみてください。



最新の動向を追う

Unity に関する最新の情報は、公式ブログにかなり丁寧に紹介されています。

もうひとつ。Unity の技術カンファレンスが、東京都内で年に一度開催されています。

大変素晴らしいことに、この技術カンファレンスの情報は、
大部分の講演資料がダウンロードできるようになっており
各社の最新のノウハウを吸収することができます。


そして、必ず参加しておきたい開発コミュニティ
Facebook グループの「Unityユーザー助け合い所」の紹介は外せません。

このコミュニティは、5000人以上の参加者がおり、
実務で Unity を使いまくっている現場の人間がかなり参加しております。
現場の人間の生の声が豊富に残っていますので、こちらも是非ご参加ください。


この強力な開発コミュニティと、豊富に用意されたドキュメンテーションは、
他に追随を許さない Unity の大きな強みの一つであると断言します。




Unity の開発 OSWindowsMac (OS X))はどっちがいいか


Unity はマルチプラットフォームに対応しており、
Windows でも OS X でも開発環境として利用することができます。

ここで一つ疑問があります。
Unity の開発環境として、Windows を選ぶべきか、それとも OS X を選ぶべきなのか。

いろいろな意見があるかと思いますが、
ここから先は、私、個人な意見として聞いてください。

私は、Unity の開発環境として Windows をオススメします。

なぜなら Windows には、
強力なプログラミングIDEVisual Studio があるからです。



Windows を使うことのメリット、デメリット

Windows を使うことのメリットは、
プログラミングIDE として Visual Studio を使うことができます。

Unity には標準で、マルチプラットフォームで動作する MonoDevelop という IDE が付属しています。
しかし残念ながら、Visual Studio と比べると
IDE としての完成度は低いと言わざるを得ません。

Visual Studio には、完璧な日本語入力、
強力なコード補完(IntelliSense)、エディタとして十分な機能が備わっており、
かつ洗練されています。

私が MonoDevelop を使うと、ときどき妙なところで突っかかります。
要するに、なんか色々としっくりこない。
息をするようにプログラミングをしたい私にとって、
この小さなノイズが積み重なると、耐え難いストレスとなって心身を襲います。


Visual Studio は高い?

いえいえ。
Visual Studio を使うことによって、
少なくとも、私の一日の生産性は 3〜5% は上昇しています。
MonoDevelop が 3〜5% の生産性を奪っている)

これが1人月100万として、10人のプログラミングチームに Visual Studio が行き渡れば、
単純計算で毎月 50万円ものコストカットになります。
そして、ちょっとだけプログラマーはハッピーに仕事ができます。



Windows を使うことのデメリットは、
(非常に大きい問題ですが) iOSビルドを実行できないことです。


OS X を使うことのメリット、デメリット

OS X を使うことのメリットは、
iOSAndroid、どちらのビルドにも対応できることです。

また OS X は、UNIX ベースの OS なので、
UNIX 関係のソフトウェア資源を利用することができるのも良い所です。


逆にデメリットですが、
OS X でのデファクトスタンダードである MonoDevelop の機能不足... これが痛いです。
この IDE の問題については、別途言及させていただきますが、
正直な所 MonoDevelop は実務で使うに耐えられません。

まず日本語が書けません。
プログラミングエディタとしての機能も、不足している感じがあります。

(ただし、最近 Xamarin Studio の躍進により、状況は改善しつつあります。
 この件に関しても後述いたします)



まとめ

モバイルデバイスに向けたアプリケーションの開発を行う場合、
AndroidiOS のどちらにも対応することは、ほぼ必須の要件となっています。
そのため、iOSビルドできないという1点で、OS X が選ばれてしまいがちです。


しかし、あえて言わせていただくと、iOSビルドは一時的な作業です。
私たちプログラマが、IDE を使っている時間は、それとは比べ物になりません。

できるだけ私たちが長く向き合う IDE こそ、最も品質が良い物を使うべきです。


私個人としては、次のような環境構成をお勧めしたいです。

まず十分な性能を持つ MacBook Pro を購入します。
そこに Boot Camp 構成で、Windowsインストールします。
普段の開発は、Windows 上で行い、iOS にまつわる作業を行うときだけ、
OS X に切り替えるという方法です。

よりお手軽な方法としては、
OS X 上に、VMware等で Windows の仮想環境を構築し、
普段はその仮想環境上で開発を行う、というのも一つありかなと思います。




Unity でのプログラミング IDE の選択肢


上記の項で、IDE に関することを幾つか論じさせていただきましたが、
他にどういう IDE の選択肢があるのか?ということも、
あわせて紹介させて頂きます。

そもそも Unity を使っている皆さんは、
どのような開発環境で、普段の開発を行っているのでしょうか?
興味深いアンケートが Facebook グループの「Unityユーザー助け合い所」に投稿されていました。

これを見るに、Unity の IDE の選択肢というのは、
おおよそ下記のいずれかということになりそうです。



MonoDevelop (★★★)

Unity に付属している MonoDevelop で頑張るという、ツライお話。
日本語が書けませんので、
日本語を書かないと決めてしまうか、別のテキストエディタで日本語を書いて、
MonoDevelopコピペしてくるとか。ああ..。


Sublime Text + SublimeSocketAsset(?)

実はこれだけ試したことない方法です。意外といいかもしれません。

Sublime Text に拡張プラグインを追加することで、編集できるようにします。
日本語も大丈夫。
http://sassembla.github.io/Public/2013:06:21%201-44-53/2013:06:21%201-44-53.html


Microsoft Visual Studio (☆☆☆)

最高。(ただし Windows ユーザーに限る)


Xamarin Studio (☆★★)

今のところ OS X で開発する場合、一番、誰にでもオススメできるのがこの IDE です。
Xamarin Studio は基本的なところは MonoDevelop なのですが、
名前が変わり、エディタの機能の改良がきちんと進んでいます。

素晴らしいことに、日本語入力も問題ありません。

私も OS X で開発が必要な場合は、Xamarin Studio を使うようにしています。

vi, emacs

という選択肢もあります。(一応)




Unity の実装言語として、C#JavaScript どちらを使うべきか?


これは、すでに開発をやっている人には常識です。

しかし Unity を始めたばかりのころ、
参考書には JavaScript で書かれてあることが多いし、
でも C# の方がいいという話も聞くし、結局どちらがいいのか分かりませんでした。


現時点での、ゲーム開発のデファクトスタンダードC# です。


基本的に、実務案件では C# が選ばれていることが圧倒的に多い印象です。

こちらからも、C# が支持されている様子がわかります。
https://www.facebook.com/groups/unityuserj/permalink/570934916299786



デバッグ情報を画面に表示する


この辺りから、実際的なプログラミングに関する話題に入り込んでいきます。
はじめに紹介しておきたいのは、
ゲーム開発特有の画面に表示されるログ、デバッグ表示の話です。

Unity のデバッグには伝統的な printf デバッグが使えます。

Debug.Log("position: " + this.transform.position.ToString());

パラメータの内容を確認したいときは、Debug.Log を利用して値の確認をします。

しかしこれだけでは、うまく対応できないケースがあります。

ゲームの処理は、非常に高速にループが回っているようなものなので、
例えば GameObject.Update などに、Debug.Log を仕込んでしまうと、
ログが大量に出力されてしまい、全然、結果を追うことができないという状況が起きます。

こういった、毎フレーム変化量を確認したいパラメータなどに関しては、
画面上にテキストを表示して、ここにデバッグ情報を出力するようなことをします。

f:id:komiyak:20141216014430p:image:w360

こうすると、Console の出力が汚れず、
プレイヤーの位置情報のようなものも確認しやすいですね。

FPS や FixedUpdate の周期なども表示してみました。

Unity のお陰で、こういった値は Unity 上でもリアルタイムに計測することができます。
わざわざ画面に出力しておく意味はないんじゃないの?と思われるかもしれませんが、
AndroidiOS 向けにビルドした時に、
こういったデバッグ情報が大切になるケースもあります。


こういったデバッグ出力機能は、実装は簡単なので、
開発初期のうちに整備しておきたいですね。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class DebugDisplayLog : MonoBehaviour
{
	static public List<string> displayLog = new List<string>();

	private void Start ()
	{
		this.oneSecondTime = 0f;
	}
	 
	private void Update ()
	{
		// FPS
		if (this.oneSecondTime >= 1f) {
			this.fps = this.fpsCounter;
			this.fixedFps = this.fixedFpsCounter;
			 
			// reset
			this.fpsCounter = 0;
			this.fixedFpsCounter = 0;
			this.oneSecondTime = 0f; 
		} else {

			this.fpsCounter ++;
			this.oneSecondTime += Time.deltaTime;
		}

		// structure debug string
		this.debugString = "";
		int count = DebugDisplayLog.displayLog.Count;
		
		for (int i=0; i<DebugDisplayLog.displayLog.Count; i++) {
			this.debugString += DebugDisplayLog.displayLog[i];
			this.debugString += "\n";
		}
		DebugDisplayLog.displayLog.Clear ();
	}

	private void FixedUpdate ()
	{
		this.fixedFpsCounter++;
	}

	private void OnGUI ()
	{
		GUI.Label (
			new Rect (0f, 0f, Screen.width, Screen.height), 
			"FPS: " + this.fps + "  FixedUpdate: " + this.fixedFps + "\n" + this.debugString);
	}

	// FPS
	private int fps;
	private int fpsCounter; 

	// Fixed FPS
	private int fixedFps; 
	private int fixedFpsCounter;

	// Debug Log
	private string debugString;

	// Timer
	private float oneSecondTime;
} 


Unity における、いわゆる「タスクシステム」


Unity を触り始めたばかりのころ、最初に思った疑問は、
プレイヤーや、敵など、目に見えるものではないものに関する処理は
一体どこに書いたらいいんだろうか?ということでした。

例えば、
ユーザーの入力を集中的に監視、リクエストを送信するようなコントローラー。
ゲームのクリア条件などの、ルールを司る処理。
サウンド関係の処理。などなど。

従来は、こういった処理は、いわゆる「タスクシステム」というものがあり、
そこに登録するなどして、使われていました。



Unity にも、こういった処理を書く方法があります。
それを今から紹介いたします。

例えば ゲーム中の入力をつかさどる GameInputController というクラスを作ったとします。

using UnityEngine;
using System.Collections;

/// <summary>
/// Game input controller.
/// </summary>
public class GameInputController : MonoBehaviour
{  
	// implementation
}

こういった処理は、Unity の Hierarchy 上に空っぽの GameObject を作成し、
それに対してアタッチします。

このような感じです。

f:id:komiyak:20141216022141p:image

これだけで OK です。
ゲーム中の入力に関する定期的な処理は、private void Update() などに記述すると、
毎フレームその処理がきちんと実行されます。


では、この GameInputController へのアクセスしたい場合は、どうすればよいか?
考えられる方法は2つあります。
1つは GameInputController をシングルトンにしてしまう方法。
1つは 下記のように Find してアクセスする方法です。

// GameInputControler.GetStatus() にアクセスしたいとする
GameInputController gameInputController = GameObject.Find ("GameInputController").GetComponent<GameInputController> ();
gameInputController.GetStatus ();



もう一つコントローラーを追加してみる

今度は、サウンド関係の処理をつかさどる SoundController を追加してみます。
先ほどの GameInputController と同じように、
Hierarchy に Empty な GameObject を生成し、そこにスクリプトをアタッチします。

f:id:komiyak:20141216022142p:image

using UnityEngine;
using System.Collections;
 
/// <summary>
/// Sound controller.
/// </summary>
public class SoundController : MonoBehaviour
{ 
	// implementation
}

これで、今のシーンに2つのコントローラーが追加されました。


さて、シーン中に表示するわけではない管理系のスクリプトを、
GameObject としてアタッチして配置するというのは分かったけれど、
GameObject にくっついている余分な Transform って消すことできないのか?

Unity の GameObject には、必ず1つの Transform を含まなければいけないという規則があります。
そのため、この Transform コンポーネントはどうやっても削除することはできません。
これは、こういうものなのだと割り切るしかありません。


スクリプトの実行プライオリティを制御できないか?

タスクシステムには、タスクの実行優先度を指定できる機能が含まれることが多いです。
初期化処理のようなものは、どうしても他のスクリプトよりも先に実行したいケースがあります。

Unity にも、スクリプトを、任意のタイミングで実行させるための機能があります。
これは「Script Execution Order Settings」と呼ばれています。

他の GameObject よりも早く実行したいならば、DefaultTime より上方向に配置します(正数)。
遅く実行したいならば下方向に配置します(負数)。


さっそく、今回作成した GameInputController, SoundController の実行オーダーを設定してみました。

f:id:komiyak:20141216022143p:image

実行オーダーの設定で注意が必要なのは、
何でもかんでもスクリプトの実行順序を決めるようにしてしまうと、
煩雑ですし、管理するものが多くなりすぎて破綻します。

この「Script Execution Order Settings」は、気兼ねなく使って良いと思いますが、
ゲームの要素の中で、本当に実行順序を気にしなければいけないものはほんの一部だと思います。
ほとんどのスクリプトは、DefaultTime で実行されて問題無いと思いますので、
実行オーダーの設定は、必要に応じて設定するというので良いかと思います。



Transform の基礎知識


Transform コンポーネントは、その GameObject の位置、向き、スケールが
どのような値になっているかを管理するオブジェクトです。

GameObject を動かしたりするときは、Transform を介して操作することになります。

Transform には、グローバル座標系とローカル座標系のためのアクセス要素があります。

グローバル座標系
  • transform.position (位置)
  • transform.rotation (向き)
  • transform.lossyScale (スケール)

ローカル座標系
  • transform.localPosition (位置)
  • transform.localRotation (向き)
  • transform.localScale (スケール)


うーん。GameObject をただ動かしたいだけなんだけど、
これってどっちを使ったらいいんでしょうか?
これは Transform の基本的なことですが、重要な部分でもありますので、
最初にきっちりと学んでおいたほうが、後々の事を考えるといいかと思います。

不安な方は、
まず 3Dグラフィックスにおける「グローバル座標系」と「ローカル座標系」について、
これがどういうものか、一度しっかりと理解されたほうが安心です。

大雑把に説明すると、
グローバル座標系は、3D空間の原点からみた、位置、向き、スケールになります。
ローカル座標系は、親となる GameObject からの相対的な、位置、向き、スケールになります。

この理解があれば、グローバル座標系、ローカル座標系、どちらのメソッドを使っても
GameObject を動かしても良いと思います。

単純にプレイヤーを動かすようなときには、
グローバル座標系(transform.position)で動かしたほうが素直です。



物理エンジンと Rigidbody(剛体)に関する、最低限の知識


Unity によって、物理エンジンというものが非常に身近になりましたが、
今日に至るまで、物理エンジンをちゃんと使ったことがあるゲームプログラマって、
なんとなく少なかったのではないかなと思います。(私もそうです)

Unity の物理エンジンは、なんとなーく扱えばそれっぽく動いているように見えるので
曖昧な知識のまま進みがちですが、
やはり最低限押さえておいたほうが良いポイントがあると思います。
以下は、それをご紹介いたします。


Static Collider の扱いに注意

Unity では、物理エンジンと当たり判定(Collision Detection)は、
お互いに密接に関わりあっており、切り離して、片方だけ使うということはできません。

例えば、物理挙動を使わないからといって、次のような GameObject を用意したとします。

f:id:komiyak:20141216042302p:image

表示のための Mesh Filter、Mesh Renderer、
当たり判定のために Sphere Collider を割り当てました。

さて、この GameObject、実行時に絶対動かしてはいけません。

これは、Unity のセオリーとしては悪いお作法ということになります。


このように GameObject に Collider が割り当ててあるが、
Rigidbody がアタッチされていないオブジェクトのことを
Unity では「Static Collider」と呼びます。

Static Collider に関する言及は、Unity の公式マニュアルにも記載されています。

リジッドボディのない Static Collider は有効化/無効化あるいは変更すべきではありません。Static Colliderの変更はPhysXでの内部計算のやり直しを発生させ、それが高価であるために大きなパフォーマンス低下につながります。ひどい場合にはコライダが仕様が未定義の状態になり、物理計算に不具合を生じさせるかもしれません


では、Unity で Collider 付きの GameObject を動かす際には、
どういったアプローチを取ればいいのでしょうか?


当たり判定(Collider)付きの GameObject を安全に動かす方法

当たり判定(Collider)付きの GameObject を安全に動かすには、
次の3つの方法があります。

  • (1)Rigidbody を含む GameObject を動かす
  • (2)Kinematic Rigidbody にした GameObject を動かす
  • (3)CharacterController を介して、GameObject を動かす


(1)Rigidbody を含む GameObject を動かす

Rigidbody をもつ GameObject は、物理エンジンの影響下に入ります。
この GameObject を動かしたいときは、次のようにする必要があります。

private void FixedUpdate()
{
    // 方法1:AddForce で動かす
    this.rigidbody.AddForce (Vector3.forward * 10f);

    // 方法2:velocity を直接変更する
    this.rigidbody.velocity = Vector3.forward * 10f;
}

Rigidbody を介してキャラクターを思ったとおりに動かすのは、難しいです。
物理法則を無視したような動き(例えば、キャラクターが急発進、急停止、方向転換するような動き)には
向いていません。

このやり方のいいところは、GameObject の挙動は物理エンジンが完全に面倒を見てくれるため、
例えば Collider を持つ壁を作れば、
その壁にめり込まないようにするなどは、物理エンジンが制御してくれます。

ちなみに、Rigidbody をもつ GameObject の transform を直接変更することは、
推奨されていません。


(2)Kinematic Rigidbody にした GameObject を動かす

Kinematic Rigidbody とは、
GameObject にアタッチされた Rigidbody の「Is Kinematic」が ON になっている
GameObject のことです。

f:id:komiyak:20141216043932p:image

Kinematic Rigidbody は、自分自身は、物理エンジンによる影響を受けません。

この Kinematic Rigidbody は、transform を使って直接位置を変更できます。

private void Update()
{
    Transform cacheTransform = this.transform;
    // 位置を更新
    cacheTransform.position = cacheTransform.position + (Vector3.forward * 10f * Time.deltaTime);
}

この Kinematic Rigidbody は、
自分の思った通りに GameObject を動かしやすいです。
その一方で、物理エンジンによる影響を受けないため、
当たり判定(何と衝突したか)はきちんと取れるのですが、
当たったあとの補完処理は自分で記述しなければいけません。

具体的には、Collider の設定された壁があるとします。
その方向に向かって、キャラクターを移動させます。

キャラクターが移動中に、壁にぶつかった場合は、
その壁の前で停止させたいですが、
Kinematic Rigidbody の場合は、そのまま壁を貫通して、向こう側に行ってしまいます。

壁との当たり判定は行われているのですが、
当たった結果、どういう振る舞いをするのか(止まるのか、跳ね返るのかなど)は
自分で全てプログラミングしなければなりません。

思ったとおりに動かせるが、
手間のかかる実装であると言えます。


(3)CharacterController を介して、GameObject を動かす

Kinematic Rigidbody のように、物理エンジンの影響を受けずに自由に動かしたい。
だけど、地面とか、壁とかにめり込まないような処理は、ちゃんと入れたい。。

そんな時に使いたいのが、CharacterController というコンポーネントです。

CharacterController を介して、GameObject を動かすことで、
地面や、壁といったものにはめり込まないように、オブジェクトを動かすことができます。

private void Update()
{
    CharacterController characterController = this.GetComponent<CharacterController> ();
    characterController.Move (Vector3.forward * 10f * Time.deltaTime);
}



まとめ

一番手間が少ないのは「(1)Rigidbody を含む GameObject を動かす」ではないかなと思います。

実際、Unity をインストールした時に入っているデモプロジェクト「AngryBots」でも、
プレイヤーの移動には、この方法が使われています。

ただし、実際にこの方法で動かしてしまうと、
「全体的にもっさりしたような動きになってしまう」感覚があります。

皆さまが遊ばれている市販のゲームなども、
キャラクターの動きは、ほとんど物理的に正しい動きをしているわけではなく、
「人が遊んでみて心地よい動き」に調整されています。


こういった、思い道理のキャラクター制御をしたい場合は、
「(3)CharacterController を介して、GameObject を動かす」を使うといいと思います。

CharacterController で気になるのは、パフォーマンスです。
こちら調査したことがなく、憶測で話しますが、
CharacterController は Unity の Rigidbody や Collider を使わずに
当たり判定制御を行っているような仕組みらしく、
あまり大量に用いすぎると、コストが高くなるんじゃないのかな?と推測しています。

こちらは未調査のため、杞憂でしたら、ごめんなさい。



完全に自分で全ての面倒を見たいならば、
「(2)Kinematic Rigidbody にした GameObject を動かす」を使うといいと思います。
おそらく、この GameObject が一番パフォーマンスコストが安いのではないかなと思っています。
(余計なことを一切していないので)




モバイルデバイスで、何が一番パフォーマンス上の問題になりやすいか


こちらの記事に、言及があります。

パフォーマンス上の問題として代表的なものが2つ例示されています。

  • (1)フィルレート制限の問題
  • (2)多すぎるドローコールの問題


(1)フィルレート制限の問題

フィルレートとは、GPU が 画面に絵を描画する性能がどれくらいなのかを
あらわす性能指標のことです。

モバイルデバイスは、PC と比べると GPU の性能が高くありません。
そのため、高いテクスチャ解像度をもつ 3D オブジェクトを描画すると、
その高い解像度を GPU に転送する帯域が狭いため、
ここが渋滞を起こしてしまい、結果的に CPU が待たされて処理落ちするという現象が起きます。

これがフィルレート制限の問題と言われています。


つまり、あまり高い解像度のテクスチャをガンガン使っていると、
そのせいで処理落ちしてしまいますよということです。

Unity にはフィルレート制限の問題を改善する簡単な方法があります。
Unity 上の設定で、テクスチャの解像度を圧縮できます。

f:id:komiyak:20141216043933p:image

もちろん圧縮するごとに、テクスチャのクオリティが下がっていきますので、
品質とパフォーマンスのトレードオフで、最適化をお願い致します。

(2)多すぎるドローコールの問題

Unity が DirectXOpenGL などの低レベルなグラフィックAPIに対して、
描画命令を出す単位をドローコールと呼んでいるようです。

ドローコールを削減するコツのようなものがありますので、
詳しくは、上で紹介した「ドローコール バッチング」の記事を一読ください。




Unity での UI, HUD 開発(2D)


Unity での 2D は、アセットの NGUI というものが、デファクトスタンダードになっています。

有料アセットですが、
非常に古くからバージョンアップされ続けているアセットで、
数多くの製品で採用されており、実績もバツグンです。


NGUI を使うかどうか、迷う必要はなく、
ただ NGUI を採用すれば、UI の開発に関しては問題はありません。



UI の作成で、ピクセルパーフェクトにこだわる必要はない


UI を作っていた当初に、私が引っかかったことなのですが、
ピクセルパーフェクトが一番見栄えが美しくなるので、
ピクセルパーフェクトになるように UI を作ってしまっていた時期がありました。

これは、モバイルデバイス向けの製品の戦略としては、根本的に間違えています。

現在、モバイルデバイスには非常に多種多様な端末があり、
どのデバイスも解像度が一定ではありません。

この記事を見るだけでも、げんなりするほどの多様な解像度の端末があります。

では、現実的にどうしたらいいのかというと、
開発初期に、仮想的な(理想的な)デバイス解像度を決めておきます。
(例えば、1280×720 をこのゲームの仮想的なデバイス解像度にする! と決めてしまう)

その後の開発では、この取り決めた仮想的なデバイス解像度を前提に、
UI の実装を行います。

この仮想的なデバイス解像度と、
実際のハードウェアのリアルな解像度との差は、NGUI が吸収してくれます。

NGUI の UIRoot の設定には、「FixedSize」というものがあります

ここに、先ほど決めた仮想的なデバイス解像度の、高さを設定します。
(例えば 1280)

あとは、NGUI が勝手に、各デバイスの解像度にあわせて、
UI を拡縮して表示してくれます。

UI に十分な解像度があれば、
拡縮されているとはいえ、十分な品質で UI が表示されます。



検証環境として、AndroidiOS どちらが作りやすいか?


どちらも触ってみた結果、個人的な感想になってしまいますが、
Android のほうが開発、検証はやりやすいように思えます。


Android は端末さえあれば、余計な手続きはあまり必要ではなく、
USB で接続して、Unity 上で「Build and Run」することで、
すぐに実機検証ができます。

iOS は、まずビルドするために iOS Developer Program に加入しなければなりません。
ビルドしたアプリケーションインストールにも制約があり、
単純に IPA ファイルをデバイスインストールするだけというわけには行きません。

また「Build and Run」で、即座に起動というわけには行きません。
(間に Xcode の起動と、Xcode 上でのビルドが必要になる)



もう一つは、Unity Remote の使い勝手の良さです。

Unity Remote というアプリケーションが、AndroidiOS 向けに公式に配布されています。
アプリインストールして、起動した状態で、PC と USB 接続します。
この状態で、Unity エディタ上で、ゲームを再生するだけで、
ゲームの実行画面が Unity Remote と同期され、
簡易ではありますがモバイルデバイスでの挙動を確認することができます。

これで嬉しいのは、タッチデバイスの共有がなされていることで、
Unity Remote 上で、画面をタッチすると、
ちゃんと Unity 側にタッチされている処理が行われます。

タッチ入力に関する機能を実装しているときは、
簡易な検証としては、非常に便利なアプリケーションです。



今はちょっと、状況が違うかもしれませんが、
僕が触っていた半年ほど前は、Android の Unity Remote は USB 接続で簡単に同期ができました。
一方 iOS のほうは、 USB 接続による同期ができず
WiFi ネットワークによる接続共有という形でしかできませんでした。



あとがき


いろいろな情報を詰め込み過ぎた結果、とても長い記事になってしまいました。
また、執筆に時間がかかりすぎてしまい、Advent Calendar の締め切りに間に合いませんでした。
楽しみにしていてくださった皆さまには、大変申し訳ありませんでした。

十分に文章を推敲することができなかったため、
少々読みにくい文章になっている箇所も多いかと思います。重ねてお詫びいたします。




この記事は、仕事で Unity を使い、身につけたことの中から、
Unity を使いはじめたばかりの頃に戻って、そのときの自分に伝えられるならば伝えたい!
という動機で書き始めました。

そのため、とても基本的ななことしか書いておりませんし、
正直、認識が間違っている箇所もあるのではないかなと危惧しております。


もし間違い等を、この記事で見つけたら、
コメント欄等でご指摘いただけますと、望外の喜びです。

この記事が、Unity を始めたばかりの
プログラマの皆さまの助けとなれれば、嬉しいです。


※2014-12-18 記事の一部の誤字脱字等を修正、一部文章を変更しました。ホットエントリ入り、ありがとうございます!!





Unity Advent Calendar 2014
明日は neuecc さんの コルーチンの分解、或いはUniRxのMainThreadDispatcherについて です。

とおり係りとおり係り 2015/12/16 10:22 とても参考になりました

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証