Unity Stateパターンで状態管理の実装
例えばキャラクターの状態管理で
・アイドル
・走る
・攻撃
・ジャンプ
こんな状態を管理する際にどういう処理にしているでしょうか…?
一番簡単な方法で考えれば各状態のフラグを用意して状態遷移させる事や、
状態を管理するビットを用意して内包している状態を判定させる方法などがありそうですが、
どちらもupdate内が段々複雑になっていき、状態が多くなって行くほどにバグの温床になりそうな予感がぷんぷんします。
UnityではPlayMakerやuStateなどのAssetも用意されていますが、
もう少しフランクに状態を管理したいと思い作ってみる事にしました。
考え方はStateパターンの通りなのですが、
処理自体はMonoBehaviourを継承しているクラスが行い、
状態管理のみを行うクラスを作るという方法です。
まぁ百聞は一見に如かずという事で早速
オブジェクトの作成
まずは適当なオブジェクトを用意します。
今回分かりやすいようにテキストにしましたが、実際はキャラクターでも何でもかまいません。
スクリプトの作成
TextController.csをCanvas/Textオブジェクトにアタッチします。
今回の処理は
初期状態から1秒後にStateAに遷移
StateAから1秒後にStateBに遷移
StateBから1秒後に初期状態に遷移
という事をやります
TextState.cs
using UnityEngine; using System.Collections; //キューブオブジェクトのステート namespace TextState{ //ステートの実行を管理するクラス public class StateProcessor { //ステート本体 private TextState _State; public TextState State { set{ _State = value;} get{ return _State;} } // 実行ブリッジ public void Execute(){ State.Execute(); } } //ステートのクラス public abstract class TextState { //デリゲート public delegate void executeState(); public executeState execDelegate; //実行処理 public virtual void Execute(){ if(execDelegate != null){ execDelegate(); } } //ステート名を取得するメソッド public abstract string getStateName(); } // 以下状態クラス // DefaultPosition public class TextStateDefault : TextState { public override string getStateName() { return "State:Default"; } } // StateA public class TextStateA : TextState { public override string getStateName() { return "State:A"; } } // StateB public class TextStateB : TextState { public override string getStateName() { return "State:B"; } public override void Execute(){ Debug.Log ("特別な処理がある場合は子が処理してもよい"); if(execDelegate != null){ execDelegate(); } } } }
TextController.cs
using UnityEngine; using System.Collections; using TextState; using UniRx; using UnityEngine.UI; using System; public class TextController : MonoBehaviour { //変更前のステート名 private string _beforeStateName; //ステート public StateProcessor StateProcessor = new StateProcessor(); //プロセッサー public TextStateDefault StateDefault = new TextStateDefault(); public TextStateA StateA = new TextStateA(); public TextStateB StateB = new TextStateB(); // Use this for initialization void Start () { //DefaultState StateProcessor.State = StateDefault; StateDefault.execDelegate = Default; StateA.execDelegate = A; StateB.execDelegate = B; } // Update is called once per frame void Update () { //ステートの値が変更されたら実行処理を行う if(StateProcessor.State == null){ return; } if (StateProcessor.State.getStateName () != _beforeStateName) { Debug.Log (" Now State:" + StateProcessor.State.getStateName ()); _beforeStateName = StateProcessor.State.getStateName (); StateProcessor.Execute (); } } public void Default() { gameObject.transform.GetComponent<Text> ().text = "初期状態です"; //1秒後にStateAに状態遷移 Observable .Timer (TimeSpan.FromSeconds (1)) .Subscribe (x => StateProcessor.State = StateA); } public void A() { gameObject.transform.GetComponent<Text> ().text = "StateAです"; //1秒後にStateBに状態遷移 Observable .Timer (TimeSpan.FromSeconds (1)) .Subscribe (x => StateProcessor.State = StateB); } public void B() { gameObject.transform.GetComponent<Text> ().text = "StateBです"; //1秒後にDefaultに状態遷移 Observable .Timer (TimeSpan.FromSeconds (1)) .Subscribe (x => StateProcessor.State = StateDefault); } }
こんな感じでしょうか?
TextStateクラスは完全に独立していますので、新しいStateを追加しても影響範囲はStateクラス内だけで留まります。
Controllerクラスは自分で行っているオブジェクトへの処理をStateクラスに委譲しているだけなので、必要なStateを必要な時に呼び出す事で状態遷移が可能です。
例えばこのTextControllerを基底クラスにして、子のControllerクラスを作成してもよいでしょう。
私はこんな感じでキャラクターの状態管理や、
ログイン処理の状態管理などを行っています。
もっといい方法があるよ!とかご意見マサカリ歓迎しておりますm( )m
Unity 3Dモデルに武器を持たせる
あれ?モデルに武器を持たせるのはどうすればいいのか…
と調べてみたところ、特に手から離れない武器であれば
手のボーンの子にすれば武器を振れる様です。
武器の指標になるボーンを追加する
このボーンが武器の角度になります。
モーション付ける
このボーンが武器だとおもってモーションを作成します。
この時の武器の角度傾きが実際の武器にも反映されます。
武器のモデル作成
これをBlenderに取り込んでボーンは無しでそのままFBX化します。
Unityで取り込む
右手のボーンにの子に武器をアタッチする
武器の位置とボーンを合わせる
これでモーションをさせると
ついでに盾もつけます(笑)
モデルが武器を持つ事ができました!
ただ手首空はなれる様な武器例えばファンネルとか法術で浮く杖みたいなのは
この方法では再現できません、其の時はその時また考えてみます
Unity Mecanimを使ってモーション遷移を作る
攻撃モーションも作成していい感じになってきたので、 今回はキャラクターのモーション遷移をつくります。
AnimatorControllerの作成
プロジェクトに追加します。
するとこんなのができあがります。
ステートとトランジションを作る
今回こんな感じでつくってみました。
もっといい方法あるよ!とかある方是非おしえてくださいm( )m
ステートにモーションをアタッチ
先ほど作った全ステートのInspectorからモーションをアタッチします。
こんな感じで全て
パラメータの追加
今回は
summon,attack,run,down
の4つを用意しました
コンディションの設定
矢印部分を選択して状態(Condition)をこんな感じでセットして行きます
any -> Down down
any -> Summon summon
any -> Attack attack true
any -> Run run true
Summon -> Wait 無し
Attack -> Wait attack false
Run -> Wait run false
CanTransition To Selfのチェック外す
any -> Attack attack true
any -> Run run true
この2つの設定はCanTransition To Selfをのチェックをはずします。
キャラクターにコントローラーをアタッチ
いままでに作ってきたコントローラーをキャラクターにアタッチします。
これでよし。
早速動かしてみます。
Unity Mecanimを使ってAnimatorControllerを作る
Attack時やRun時にdownトリガーを引くとノックバックになります(笑)
面白いので何処かで使おうかとおもいました。
大分駆け足できましたが、これでキャラクターの基本動作ができましたこの状態で
Prefab化しておいて、呼び出す様にするのがいいかと思います。
もしもキャラクターの数が膨大になるようであればAseetBundle化するのもよいでしょう。
結構手探りでやってるのでご意見、マサカリ等歓迎致します。
Blenderで攻撃アニメーションを作る
さて折角モデルを作成したので、 アニメーションを付けて行きます。
※ボーンを入れて、ウェイトペイントは既に塗られているとします。
動かす時に塗りが甘かったり、違うところまでぬってしまうと全然違う所がうごいてしまうので
丁寧に塗りましょう。
まずは作ってみる
うん、攻撃してる様に見えるは見えるのですが、なんかもっさりしてます。
実際にこのモデルに殴られても余り痛くなさそうだな。。って感じです。
これは予備動作と反発動作がないのでなんかモサーっとしてるのだと思います。
格好いい攻撃モーションには、すべて理由があった! “身体の動きと原理から知る、闘うインゲームアニメーションの中身”リポート【CEDEC 2013】 - ファミ通.com http://t.co/MInP1KyMwo @famitsuさんから
— raharu0425 (@raharu0425) 2015, 5月 20
攻撃モーションを簡単にカッコ良くする3つのテクニック http://t.co/0Nkco3wafM #blomaga
— raharu0425 (@raharu0425) 2015, 5月 20
このとても参考になるサイトを読んで修正したのがこちら
通常体制 > 溜め > 筋肉を意識してピーク > スマッシュ! > 余韻 > 通常体制
の動作にしてみました。
ちょっと殴打音が聞こえてくる見たいになってきました。 まだまだ修行不足は否めませんがさっきよりいい感じです。
FBXの吐き出し
前回は.blendファイルを直接UnityにD&Dしましたが、
それだといらない物までついてきしまうので(カメラとかライトとか)こんな設定でexportします。
これを前回同様にアタッチすれば。。。
作り方はわかったので後は鍛錬あるのみです。
(あれ最近プログラマじゃ無くなってきてる。。?)
MagicaVoxelで作ったモデルをBlenderで読み込みUnityで表示する
Exportする
モデルをExportします左下のExportメニューで「obj」を選択します
magicavoxelのexportフォルダに吐き出されています(これ分かりにくい)
blenderにインポート
メニューからobjインポートします。
今のままじゃデカいのでサイズを0.3にしてライトを調整します。
もしもテクスチャが剥がれてしまって見えない場合は
[option + z]を押すと見れると思います。
ボーンをいれてウェイトペイントして、モーションを付けます。
こことてもはしょりましたが、初心者な私は大体2日程かかりました。 なにも分からないところから作るのは大変ですが頑張って作りましょう。
ボーン作る
ウェイトペイントを塗る
モーション作る
Unityに読み込む
なんと素晴らしい事にblenderのファイルを直接読み込めるので
Assetsの直下に.blendファイルをDDすると自動で読み込まれます。
インポートするとこんなフォルダが出来上がります
.blendファイルにはテクスチャが含まれていなかったので、 MagicaVoxelからExportしたpngもUnityで読み込みます。
これでそろいましたがまだテクスチャが貼られていませんが
先にマテリアルのシェーダーを変更します。
これに先ほど読みこんだテクスチャをアタッチします。
モデルにテクスチャが反映されました
これで適当なAnimatorを作成してrunのアニメーションをアタッチすれば
問題なくうごきますが、アニメーションのループ設定自体は本体からしか変更が出来ない??見たいです
ムービークリップを外だしに出来ればいいのですが。。
とりあえず問題無く動くようです
ここまで出来れば
MagicaVoxelでモデルを作る
Blenderでモーション作成
Unityに組み込み
という作業フローが確立出来そうです。
次からはどんどんキャラクター作るぞい!!!
【Unity】Blend Treeを使用したRPG風8方向移動の作成
前回RPG風のマップの作成をしたので今回は
キャラクターを置いて、一旦WASDキーで8方向に移動する様にしたいと思います。
キャラクターオブジェクトを作成する
空のオブジェクトを作成してCharaとでも適当に付けておきます。
これにこんな感じでコンポーネントをアタッチします。
Animator Controllerの作成
BaseCharacterMotionという名前でAnimatorControllerを作成します。
これを先ほどのキャラクターオブジェクトにアタッチしておきます。
8方向アニメーションの作成
walk@[方向]のネーミングでアニメーションを8個作ります。
アニメーションの作り方は大体ここに書いてあります。
raharu0425.hatenablog.com
方向のパラメータを追加
XYの移動方向をセットする為に2つパラメータを追加します。 名前は適当に
Direction_X,Direction_Yとでも付けておきます。
AnimatorにBlend Tree ステートを追加する。
名前をWalkにしておきます。
これでBlendTreeを持っているステートができました。
BrendTree設定
Walkステートをダブルクリックすると詳細が見れます。
InspectorのBrendTypeを[2D Simple Directional]に変更して
Parametersを[Direction_X]と[Direction_Y]をセットします。
Motion Filedを8つ追加します。
これに先ほど作成した8方向アニメーションを追加し、Positoinを8方向に調整します。
これでBlendTreeが完成しました。
スクリプトを作る
2つスクリプトファイルを作成します。
//キャラクターのコントローラー
BaseCharacterController
//モーションを管理する基底クラス
BaseMortionController
BaseCharacterController.cs
using UnityEngine; using System.Collections; using UnityStandardAssets.CrossPlatformInput; [RequireComponent(typeof (BaseMortionController))] public class BaseCharacterController : MonoBehaviour { private BaseMortionController m_Character; private void Awake() { m_Character = GetComponent<BaseMortionController>(); } private void Update() { } private void FixedUpdate() { // WASDの入力取得 float h = CrossPlatformInputManager.GetAxis("Horizontal"); float v = CrossPlatformInputManager.GetAxis ("Vertical"); //移動 m_Character.Move(h, v); } }
BaseMortionController.cs
using UnityEngine; using System.Collections; public class BaseMortionController : MonoBehaviour { private Animator m_Anim; private Rigidbody2D m_Rigidbody2D; [SerializeField] private float move_speed = 7.0f; private void Awake() { m_Anim = GetComponent<Animator>(); m_Rigidbody2D = GetComponent<Rigidbody2D>(); } /** * 移動 */ public void Move(float h_move, float v_move) { // 移動方向に力を加える Vector2 direction = new Vector2 (h_move, v_move).normalized; m_Rigidbody2D.velocity = direction * move_speed; //モーション判定用のパラメータ m_Anim.SetFloat("Direction_X", h_move); m_Anim.SetFloat("Direction_Y", v_move); } }
これらをCharaオブジェクトにアタッチして
完成
Unity4.3から追加されたらしいBlendTreeとても便利です。
簡単に8方向移動ができました。
素材はこちらを使わせて頂いております。有り難うございます。
こちらの記事を参考させて頂きました有り難うございます。
【Unity】TileMapEditorで作ったデータをX-UniTMXで読み込ませる 其の3 -RPGマップ編
5/19追記
X-UniTMXがil2cppでビルド出来ない現象を確認しましたのでこちらの記事は参考までにしてください。
最新のX-UniTMXであば対応されている可能性があります。
さて前回の記事で別に横スクロールが作りたい訳じゃないとかいっていましたが
正直何を作ろうか完全にぶれていますすいません
とりあえずRPG風のマップをつくってそれでゴニョゴニョしたくなったので
今回はRPGマップの作成を行います。
この素晴らしいマップチップにはこちらの素材を使用させて頂きました有り難うございます!
マップはレイヤーを分けて作る
レイヤーの順序はこんな感じで
レイヤー順に、
BackGround:背景
Terrain:地形
Road:道
Ladder:特殊道
Object:賑やかし
こうなります。
Colliderには侵入不可部分にオブジェクトを作成します。
こんな感じで出来上がり!
これを前回同様にUnityに読み込んで
ついでにキャラクタも置いてみます。
キャラクターはこちらを使わせて頂きました有り難うございます! こういうの配布してくださる方はもう神です。神としか言いようがありません!
こんな感じ!?
あれ大変だこれだけで面白そう!!!(錯覚)
こう感じてしまうのはきっと素材がいいからでしょう!
RPGのイベントトリガーとか挙動関連はちょっとやっておきたいのはあるのですが、
まだ何作ろうか思い浮かびません企画者を緩く募集してます(笑)
次回は8方向移動のキャラクターコントローラの記事でも書こうかと思ってます。