よく使うクラスの関係
初期ウィンドウサイズを変更する際の注意
1度ゲームを実行した後、プレイヤー設定でデフォルトウィンドウサイズを変更しても1度目のウィンドウサイズになっているのは
HKCU\Software\[company name]\[product name] (※Windows)
にウィンドウサイズなどの環境設定が保存されているため。
PlayerPrefs で書き込まれる場所もここになります。
null合体演算子は使えない
- nullでもnullと判定されない。
- シリアライズの関係?
UpdateとFixedUpdate
- InputはUpdate()で取る。
- RigidBodyはFixedUpdate()で触る。
- 物理的に正確でなくてもいい場合でもFixedUpdate()で呼ばないと、子オブジェクトがずれていったり、アニメーション次第では速度は正しく設定されているのにオブジェクトが全然動かないなんてことも。
パフォーマンス関連
- あからさまに処理落ちするのは、描画関連かオブジェクトの生成・破棄の連打しすぎが多い。
- 使いまわせるオブジェクトは破棄せず使いまわす(STGの敵弾など)。
- Rigidbodyの使用は最小限に
- Colliderオブジェクトを動かす場合Rigidbodyを追加して行う(static colliderオブジェクトを動かすコストがやばいらしい)
- DrawCallを減らす
- SpritePacker等を使っていく。ただし無限にパッキングできない(上限がある)ので、複数パックにせざるを得ないこともある。
- あらゆる機能をMonoBehaviourで作るのはUnityのコンポーネント指向を活かす意味ではいいかもしれないが、重い。
- オブジェクトに直接アタッチ(MonoBehaviourの使用)するスクリプトを最小限にして、そのスクリプトのUpdate()からMonoBehaviourを使わないコンポーネントのUpdate()を呼ぶなどする方が良さそう。
イベントを渡す方法
ここで言うイベントとは Collider の OnTriggerEnter() のようなもののことです。
SendMessage()を使う
- SendMessage(string メソッド名)を実行すると、そのgameObjectにアタッチされた全てのコンポーネントの名前を指定されたメソッドを実行する。
- 呼ばれるメソッドの引数をSendMessage()の第2引数で設定できる。
SendMessage("OnDamage", 10); //10ポイントのダメージを受けた
- デフォルトだと指定されたメソッドが存在しない場合エラーになります。
- 第2引数または第3引数に SendMessageOptions.DontRequireReceiver を指定することで回避できます。
- タイプミス対策になるので回避しない方がいいでしょう。
- 子オブジェクトには影響しません。
- 呼ばれるメソッドの引数をSendMessage()の第2引数で設定できる。
- メソッド名を文字列で指定するため、リファクタリング面で否定的な意見もある。
- 名前の衝突にも注意。
- 個人的には分かりやすい名前(On△△△など)を付ければ十分だと思う。
- On△△△を気楽に書き換えてしまう感覚の人は初心者だろうから、そういう人のコードは先輩や上司などの上級者が目を通すことになるだろうし。
- もちろん、そういう懸念を潰せるに越したことはないと思います。
- そんなことよりOn△△△といったメソッド名を知らないと使えないことの方が問題じゃないかな。
- 呼ばれる順番はアタッチされた順のはずだけど、それを頼りにするのはどうかと思う。
ExecuteEvents.Execute()を使う
- namespace は UnityEngine.EventSystems
- SendMessage()より早いらしい。
- interface の存在さえ把握していればリファクタリングの問題も無い。
使用例
もっといい例が思いついたら書き換えます_(:3」∠)_ というか誰かいい例題ください。
想定1:プレイヤーの描画や効果音の管理がそれぞれ独立したコンポーネントとなっている。
想定2:ダメージを受けた時に、その数値に対応する効果音や描画をする。
短く例を作るため今回は次のような処理にする
- Zキーを押した時に10ポイントのダメージを受ける
- Xキーを押した時に20ポイントのダメージを受ける
//インターフェース IDamage.cs
interface IDamage : UnityEngine.EventSystems.IEventSystemHandler
{
void OnDamage(int num);
}
//描画担当 Draw.cs
using UnityEngine;
public class Draw : MonoBehaviour, IDamage
{
public void OnDamage(int num)
{
if(num <= 10)
{
Debug.Log("小さなダメージを受けた描画");
}
else
{
Debug.Log("大さなダメージを受けた描画");
}
}
}
//効果音担当 Sound.cs
using UnityEngine;
public class Sound : MonoBehaviour, IDamage
{
public void OnDamage(int num)
{
if(num <= 10)
{
Debug.Log("小さなダメージを受けた効果音");
}
else
{
Debug.Log("大きなダメージを受けた効果音");
}
}
}
//プレイヤー本体 Player.cs
using UnityEngine;
using UnityEngine.EventSystems;
public class Player : MonoBehaviour
{
void Update()
{
if(Input.GetKeyDown(KeyCode.Z))
{
ExecuteEvents.Execute<IDamage>(
gameObject, //対象オブジェクト
null,
(x, y) => x.OnDamage(10)); //コンポーネントxに対する操作
}
else if(Input.GetKeyDown(KeyCode.X))
{
ExecuteEvents.Execute<IDamage>(
gameObject, //対象オブジェクト
null,
(x, y) => x.OnDamage(20)); //コンポーネントxに対する操作
}
}
}
ScriptableObject
- 基本はメモリ消費を減らすための機能。
- クラス属性にCreateAssetMenu属性を付けることでアセットを生成する項目を作ることができる(Unity5以降?)
- データ格納用のPrefabみたいな感じ。
- とはいってもメソッドも書けるし、それを継承してoverrideしてカスタマイズもできる。説明しにくいなあ。
- RPGのエンカウントパターンとか、店の品揃えパターンとかにも使えるね。
AddObjectToAsset
- 親アセットと子アセットがクラスが違っても同拡張子(ScriptableObject用の.assetなど)で取り扱うことが出来る場合、操作更新のためにAssetImportすると親アセットより名前が若い子アセットが新たに親アセットになってしまうっぽい?
- parent.asset に child をAddObjectToAssetすると、child が親アセットになり名前がアセットファイル名であるparentに変わり、元parentは名前そのままで子アセットになる?
parent ┗child
↓ImportAsset
parent(元child) ┗parent(元parent)