UnityのAssetStoreで販売している、Playmakerを購入しました。
http://www.hutonggames.com/index.html
このPlaymakerは、スクリプトを一切書かなくても、Unityでゲームロジックが組めるということを売りにしていますが、自分の場合は、普段C#でスクリプトを組んでいるので、そのメリットがちょっと見えづらいという点がありました。
いろいろいじってた結果、Playmakerはゲームロジックのひとつである、状態変移に特化しており、その代わりに非同期処理や複数条件での分岐処理(n-stateを含む)を書くのが難しいことが分かりました。
またC#側ではメリットデメリットが逆転している(状態変移を書くと、Update関数内が変移チェック変数でごちゃごちゃする)ので、これらを使い分けると面白いのではないかと考えたわけです。
というわけで、自分の勉強もかねて、C#とPlaymaker両方を使って、状態変移の制御を行う簡単なやり方について考えてみました。
内容の理解度に関しては、一通りPlaymakerを触っている人が対象になります。
アルゴリズムについてですが、
- PlaymakerからGameObject(Prefab)のインスタンスを作成する。(1秒ごとに生成するループ)
- 生成されたGameObjectは一定時間(ランダム性あり)が来たら自己消滅する。その際にPlaymaker側に消滅したことを知らせる(カウンタを増やす)
- Playmaker側は一定数の消滅が来たら、生成ループから外れて、自己消滅する
という構成になっています。
まずはPlaymaker側の設定です。適当な空のGameObjectをHierarchyに追加し名前を"CubeControllerFSM"にし、PlaymakerのFSMを作ります。また、ついでにもうひとつ、"CubeOrigin"という名前の空のGameObjectを作っておきます。
シーンに初めてFSMを作った際の名前は、"FSM"になっていますが、それ以降は変わってきますので、この辺はスクリプトを制御する際に他のFSMと混同しないよう、FSMの名前は意識しておいたほうが良いと思います。
一つ目のActionはCreate Objectになります。このActionは実行時にGameObject(Prefab可)のインスタンスを作成する機能を持っています。Game Objectの箇所には生成したいGameObjectを、Spawn Pointには生成する原点となるGameObjectを設定します。
Store ObjectにはPlaymakerのVariablesの変数と紐付けできますが、増減するものに対しては使いづらいので、今回は無視します。
スクリーンショットでは既にCubeというGameObjectがつけられていますが、後でそのPrefabについて説明します。
二つ目のActionはInt Compareです。これはVariables中のint型の変数が、ある一定の数値未満(Less than)、同値(Equal)、それよりも大きい(Greater than)に、処理を分岐する機能です。
ここでは、IsNotOVERとIsOVERというカスタムEventを作っていますが、ラベル付けの役割だけですので、名前は何でも良いです。
IsNotOVERのほうは、WaitのActionに紐付けされ、IsOVERのほうはDestroy Selfに紐付けされています。
今回は変数にNumOfDestroyという変数を作っています。Variablesタブから変数を作っておきましょう。作ったらInteger1にNumOfDestroyをセットしましょう。
三つ目のActionはWaitです。あまり説明することは少ないですが、Waitは結構重要で、他のActionのLogicのうち、*** Changed系のActionがある場合は、これをかまさないとうまく動かないケースがあります。また、今回はPlaymakerが無駄にインスタンスを生成しないように、Waitを入れています。
このActionは一つ目のCreate ObjectのActionと紐付けされており、ループ処理が行われます。
最後のActionは、Destroy Selfになります。C#で言うDestroy(gameObject)を呼ぶのと同じ機能です。これはそのままなので、画像は省略します。
さて、今度はC#のスクリプト作りです。
名前は何でも良いですが、この記事では"FSMListner"というスクリプトにしています。コードは以下のとおりです。スクリプト自体は、上記の2の機能そのものです。
using UnityEngine;
using System.Collections;
using HutongGames.PlayMaker;
public class FSMListner : MonoBehaviour {
PlayMakerFSM fsm;
public string PlaymakerObject;
public string FsmName;
public string ParamName;
float lifetime;
// Use this for initialization
void Start () {
fsm = GameObject.Find(PlaymakerObject).GetComponent<PlayMakerFSM>();
if(fsm == null && fsm.name != FsmName) {
Debug.Log("FSMListner: " + PlaymakerObject + " is not found!");
}
lifetime = 1.0f + Random.value;
Vector3 vec = new Vector3();
vec.x = (Random.value - 0.5f) * 2f;
vec.y = (Random.value - 0.5f) * 2f;
gameObject.transform.position = vec;
}
// Update is called once per frame
void Update () {
lifetime = lifetime - Time.deltaTime;
if(lifetime < 0.0f) {
if(fsm != null) {
fsm.FsmVariables.GetFsmInt(ParamName).Value += 1;
}
Destroy(gameObject);
}
}
}
Start時に自己消滅までの時間を設定し(lifetimeがそれです)、生成位置をランダムに設定しています。また、
fsm = GameObject.Find(PlaymakerObject).GetComponent<PlayMakerFSM>();
の行で、PlaymakerFSMクラス(Playmakerの実体のクラス)から、特定のFSMを抽出できます。
Update関数内では、時間経過による処理のプログラムを書いています。Destroyを呼ぶ前の行の、
fsm.FsmVariables.GetFsmInt(ParamName).Value += 1;
で、FSM内のVariablesにアクセスし、値を書き換える(インクリメント)しています。
スクリプトは保存して、Assetにおいておきましょう。
次にCubeのPrefabとHierarchyについてです。
HierarchyにCubeのGameObjectを作成し、そこに"FSMListener"のスクリプトをD&Dし、CubeをAssetに追加し、Prefab化しておきます。一度Prefab化が終わっていれば、HierarchyからCubeを削除しても大丈夫です。
Inspectorの"FSMListener"の項目に、public変数がありますので、それぞれ、
Playmaker Objectには、"CubeControllerFSM"、
Fsm Nameには、"FSM"、
Param Nameには、"NumOfDestroy"
を忘れずに入れておきましょう、すべて文字列になっていますので、手打ちで文字列を
入力してください。
これが終わったら、再度CubeのPrefabを、Playmakerの一つ目のCreate ObjectのGameObjectにセットしておきましょう。
終わったら、実行して、どんな動作をするか確認しましょう。
実行中はCube(Clone)が生成され、消えては生成され、という具合になり、最後はCubeがすべて消え、CubeControllerFSMも消滅するという挙動になると思います。
さて、今回はPlaymakerとC#両方の操作で互いに影響するコードについて解説をしました。
今後もこのように、互いのメリットデメリットを補完することにより、より楽しいUnityコーディングができるようにいろいろ考えていこうと思います。