とあるプログラマの備忘録

都内某所に住むプログラマが自分用に備忘録を残すという趣旨のブログです。はてなダイアリーから移動しました!

Unity Stateパターンで状態管理の実装

例えばキャラクターの状態管理で
・アイドル
・走る
・攻撃
・ジャンプ

こんな状態を管理する際にどういう処理にしているでしょうか…?
一番簡単な方法で考えれば各状態のフラグを用意して状態遷移させる事や、
状態を管理するビットを用意して内包している状態を判定させる方法などがありそうですが、
どちらもupdate内が段々複雑になっていき、状態が多くなって行くほどにバグの温床になりそうな予感がぷんぷんします。

UnityではPlayMakerやuStateなどのAssetも用意されていますが、
もう少しフランクに状態を管理したいと思い作ってみる事にしました。

考え方はStateパターンの通りなのですが、
処理自体はMonoBehaviourを継承しているクラスが行い、
状態管理のみを行うクラスを作るという方法です。

まぁ百聞は一見に如かずという事で早速

オブジェクトの作成

まずは適当なオブジェクトを用意します。
今回分かりやすいようにテキストにしましたが、実際はキャラクターでも何でもかまいません。

f:id:raharu0425:20150612121606p:plain f:id:raharu0425:20150612121628p:plain

スクリプトの作成

f:id:raharu0425:20150612121645p:plain

TextController.csをCanvas/Textオブジェクトにアタッチします。

f:id:raharu0425:20150612121823p:plain

今回の処理は
初期状態から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);

    }
}

f:id:raharu0425:20150612125121g:plain

f:id:raharu0425:20150612124635p:plain

こんな感じでしょうか? TextStateクラスは完全に独立していますので、新しいStateを追加しても影響範囲はStateクラス内だけで留まります。
Controllerクラスは自分で行っているオブジェクトへの処理をStateクラスに委譲しているだけなので、必要なStateを必要な時に呼び出す事で状態遷移が可能です。
例えばこのTextControllerを基底クラスにして、子のControllerクラスを作成してもよいでしょう。

私はこんな感じでキャラクターの状態管理や、
ログイン処理の状態管理などを行っています。

もっといい方法があるよ!とかご意見マサカリ歓迎しておりますm( )m

Unity 3Dモデルに武器を持たせる

あれ?モデルに武器を持たせるのはどうすればいいのか…
と調べてみたところ、特に手から離れない武器であれば
手のボーンの子にすれば武器を振れる様です。

武器の指標になるボーンを追加する

このボーンが武器の角度になります。

f:id:raharu0425:20150604230139p:plain

モーション付ける

このボーンが武器だとおもってモーションを作成します。

f:id:raharu0425:20150604230404p:plain

この時の武器の角度傾きが実際の武器にも反映されます。

武器のモデル作成

f:id:raharu0425:20150604230548p:plain

これをBlenderに取り込んでボーンは無しでそのままFBX化します。

Unityで取り込む

右手のボーンにの子に武器をアタッチする

f:id:raharu0425:20150604231302p:plain

武器の位置とボーンを合わせる

f:id:raharu0425:20150604231402p:plain

これでモーションをさせると

f:id:raharu0425:20150604231653p:plain

ついでに盾もつけます(笑)


ランサーモーション

モデルが武器を持つ事ができました!
ただ手首空はなれる様な武器例えばファンネルとか法術で浮く杖みたいなのは
この方法では再現できません、其の時はその時また考えてみます

Unity Mecanimを使ってモーション遷移を作る

攻撃モーションも作成していい感じになってきたので、 今回はキャラクターのモーション遷移をつくります。

AnimatorControllerの作成

プロジェクトに追加します。

f:id:raharu0425:20150522094327p:plain

するとこんなのができあがります。

f:id:raharu0425:20150522094359p:plain

ステートとトランジションを作る

f:id:raharu0425:20150522095358p:plain

今回こんな感じでつくってみました。
もっといい方法あるよ!とかある方是非おしえてくださいm( )m

ステートにモーションをアタッチ

先ほど作った全ステートのInspectorからモーションをアタッチします。

f:id:raharu0425:20150522101823p:plain

こんな感じで全て

パラメータの追加

f:id:raharu0425:20150522095202p:plain

今回は
summon,attack,run,down

の4つを用意しました

コンディションの設定

矢印部分を選択して状態(Condition)をこんな感じでセットして行きます

f:id:raharu0425:20150522101229p:plain

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をのチェックをはずします。

f:id:raharu0425:20150522101651p:plain

キャラクターにコントローラーをアタッチ

いままでに作ってきたコントローラーをキャラクターにアタッチします。

f:id:raharu0425:20150522102125p:plain

これでよし。
早速動かしてみます。


Unity Mecanimを使ってAnimatorControllerを作る

Attack時やRun時にdownトリガーを引くとノックバックになります(笑)
面白いので何処かで使おうかとおもいました。

大分駆け足できましたが、これでキャラクターの基本動作ができましたこの状態で
Prefab化しておいて、呼び出す様にするのがいいかと思います。

もしもキャラクターの数が膨大になるようであればAseetBundle化するのもよいでしょう。

結構手探りでやってるのでご意見、マサカリ等歓迎致します。

Blenderで攻撃アニメーションを作る

さて折角モデルを作成したので、 アニメーションを付けて行きます。

※ボーンを入れて、ウェイトペイントは既に塗られているとします。

動かす時に塗りが甘かったり、違うところまでぬってしまうと全然違う所がうごいてしまうので
丁寧に塗りましょう。

まずは作ってみる

f:id:raharu0425:20150521144413g:plain

うん、攻撃してる様に見えるは見えるのですが、なんかもっさりしてます。
実際にこのモデルに殴られても余り痛くなさそうだな。。って感じです。

これは予備動作と反発動作がないのでなんかモサーっとしてるのだと思います。

このとても参考になるサイトを読んで修正したのがこちら

f:id:raharu0425:20150521144631g:plain

通常体制 > 溜め > 筋肉を意識してピーク > スマッシュ! > 余韻 > 通常体制
の動作にしてみました。

ちょっと殴打音が聞こえてくる見たいになってきました。 まだまだ修行不足は否めませんがさっきよりいい感じです。

FBXの吐き出し

前回は.blendファイルを直接UnityにD&Dしましたが、
それだといらない物までついてきしまうので(カメラとかライトとか)こんな設定でexportします。

f:id:raharu0425:20150521145838p:plain

これを前回同様にアタッチすれば。。。

f:id:raharu0425:20150521150307g:plain

作り方はわかったので後は鍛錬あるのみです。
(あれ最近プログラマじゃ無くなってきてる。。?)

MagicaVoxelで作ったモデルをBlenderで読み込みUnityで表示する

Exportする

モデルをExportします左下のExportメニューで「obj」を選択します

f:id:raharu0425:20150517230403p:plain

magicavoxelのexportフォルダに吐き出されています(これ分かりにくい)

f:id:raharu0425:20150517230456p:plain

blenderにインポート

メニューからobjインポートします。

f:id:raharu0425:20150517230859p:plain

今のままじゃデカいのでサイズを0.3にしてライトを調整します。
もしもテクスチャが剥がれてしまって見えない場合は
[option + z]を押すと見れると思います。

f:id:raharu0425:20150517231018p:plain

ボーンをいれてウェイトペイントして、モーションを付けます。

こことてもはしょりましたが、初心者な私は大体2日程かかりました。 なにも分からないところから作るのは大変ですが頑張って作りましょう。

ボーン作る

f:id:raharu0425:20150517231503p:plain

ウェイトペイントを塗る

f:id:raharu0425:20150517231532p:plain

モーション作る


Blender 走るモーション

Unityに読み込む

なんと素晴らしい事にblenderのファイルを直接読み込めるので
Assetsの直下に.blendファイルをDDすると自動で読み込まれます。

インポートするとこんなフォルダが出来上がります
f:id:raharu0425:20150517232021p:plain

.blendファイルにはテクスチャが含まれていなかったので、 MagicaVoxelからExportしたpngもUnityで読み込みます。

f:id:raharu0425:20150517232617p:plain

これでそろいましたがまだテクスチャが貼られていませんが
先にマテリアルのシェーダーを変更します。

f:id:raharu0425:20150517233115p:plain

これに先ほど読みこんだテクスチャをアタッチします。

f:id:raharu0425:20150517233302p:plain

モデルにテクスチャが反映されました

f:id:raharu0425:20150517233407p:plain

これで適当なAnimatorを作成してrunのアニメーションをアタッチすれば
問題なくうごきますが、アニメーションのループ設定自体は本体からしか変更が出来ない??見たいです
ムービークリップを外だしに出来ればいいのですが。。

f:id:raharu0425:20150517233843p:plain

とりあえず問題無く動くようです

f:id:raharu0425:20150517234124p:plain

ここまで出来れば
MagicaVoxelでモデルを作る
Blenderでモーション作成
Unityに組み込み

という作業フローが確立出来そうです。
次からはどんどんキャラクター作るぞい!!!

【Unity】Blend Treeを使用したRPG風8方向移動の作成

前回RPG風のマップの作成をしたので今回は
キャラクターを置いて、一旦WASDキーで8方向に移動する様にしたいと思います。

キャラクターオブジェクトを作成する

空のオブジェクトを作成してCharaとでも適当に付けておきます。
これにこんな感じでコンポーネントをアタッチします。 f:id:raharu0425:20150430224415p:plain

Animator Controllerの作成

BaseCharacterMotionという名前でAnimatorControllerを作成します。

f:id:raharu0425:20150430224858p:plain

これを先ほどのキャラクターオブジェクトにアタッチしておきます。

8方向アニメーションの作成

walk@[方向]のネーミングでアニメーションを8個作ります。
f:id:raharu0425:20150430232052p:plain

アニメーションの作り方は大体ここに書いてあります。
raharu0425.hatenablog.com

方向のパラメータを追加

XYの移動方向をセットする為に2つパラメータを追加します。 名前は適当に

Direction_X,Direction_Yとでも付けておきます。

f:id:raharu0425:20150430233201p:plain

AnimatorにBlend Tree ステートを追加する。

f:id:raharu0425:20150430232610p:plain
名前をWalkにしておきます。
f:id:raharu0425:20150430232653p:plain

これでBlendTreeを持っているステートができました。

BrendTree設定

Walkステートをダブルクリックすると詳細が見れます。

f:id:raharu0425:20150430233324p:plain

InspectorのBrendTypeを[2D Simple Directional]に変更して
Parametersを[Direction_X]と[Direction_Y]をセットします。

f:id:raharu0425:20150430233536p:plain

Motion Filedを8つ追加します。
f:id:raharu0425:20150430233757p:plain

これに先ほど作成した8方向アニメーションを追加し、Positoinを8方向に調整します。 f:id:raharu0425:20150430233857p:plain

これでBlendTreeが完成しました。
f:id:raharu0425:20150501182813p:plain

スクリプトを作る

2つスクリプトファイルを作成します。

//キャラクターのコントローラー
BaseCharacterController
//モーションを管理する基底クラス
BaseMortionController

f:id:raharu0425:20150430234317p:plain

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オブジェクトにアタッチして f:id:raharu0425:20150430235235p:plain

完成


Unity BlendTreeTest - YouTube

Unity4.3から追加されたらしいBlendTreeとても便利です。
簡単に8方向移動ができました。

素材はこちらを使わせて頂いております。有り難うございます。

ぴぽや倉庫

RPGツクール素材支部|臼井の会

こちらの記事を参考させて頂きました有り難うございます。

Unity4.3 で追加された Sprite 機能を使ってみよう - その1 - Unityではじめるゲーム開発 - Mobile Touch - モバイル/タブレット開発者およびデザイナー向け情報ポータル

【Unity】TileMapEditorで作ったデータをX-UniTMXで読み込ませる 其の3 -RPGマップ編

5/19追記
X-UniTMXがil2cppでビルド出来ない現象を確認しましたのでこちらの記事は参考までにしてください。
最新のX-UniTMXであば対応されている可能性があります。

さて前回の記事で別に横スクロールが作りたい訳じゃないとかいっていましたが
正直何を作ろうか完全にぶれていますすいません

とりあえずRPG風のマップをつくってそれでゴニョゴニョしたくなったので
今回はRPGマップの作成を行います。

この素晴らしいマップチップにはこちらの素材を使用させて頂きました有り難うございます!

piposozai.wiki.fc2.com

マップはレイヤーを分けて作る

レイヤーの順序はこんな感じで

f:id:raharu0425:20150428210806p:plain

レイヤー順に、

BackGround:背景
f:id:raharu0425:20150428211118p:plain
Terrain:地形
f:id:raharu0425:20150428211127p:plain
Road:道
f:id:raharu0425:20150428211135p:plain
Ladder:特殊道
f:id:raharu0425:20150428211147p:plain
Object:賑やかし
f:id:raharu0425:20150428211159p:plain

こうなります。

Colliderには侵入不可部分にオブジェクトを作成します。 f:id:raharu0425:20150428212054p:plain

こんな感じで出来上がり!

これを前回同様にUnityに読み込んで

raharu0425.hatenablog.com

ついでにキャラクタも置いてみます。

キャラクターはこちらを使わせて頂きました有り難うございます! こういうの配布してくださる方はもう神です。神としか言いようがありません!

usui.moo.jp

f:id:raharu0425:20150428212748p:plain

こんな感じ!?

あれ大変だこれだけで面白そう!!!(錯覚)
こう感じてしまうのはきっと素材がいいからでしょう!

RPGのイベントトリガーとか挙動関連はちょっとやっておきたいのはあるのですが、
まだ何作ろうか思い浮かびません企画者を緩く募集してます(笑)

次回は8方向移動のキャラクターコントローラの記事でも書こうかと思ってます。