Unity UniRxをつかって似非ピンチ処理を作ってみる
ピンチインアウトって本来2点座標の中間地点を拡大縮小する機能な気がするんですけど、
今回似非なのでカメラの中心点を拡大する形になります。
またエディター上ではマルチタッチが再現できないので、
スペースキー + スワイプで代用します。
前回のソースに追記するので流れがわからない人はこちら
これに、ピンチ処理を作ってみます。
using UnityEngine; using System.Collections; using System; using UniRx; public class CameraMove : MonoBehaviour { //メインカメラ public Camera MainCamera; // Update void Update () { #if UNITY_EDITOR // マウスが押された場合 if (Input.GetMouseButtonUp(0)) { }else if(Input.GetKey(KeyCode.Space) && Input.GetMouseButton(0)){ //エディターではマルチタッチが再現できないので適当にスペースキー+スワイプでピンチを再現します。 MapPinch(); } else if (Input.GetMouseButton(0)) { MapSwipe(); } #elif UNITY_IOS || UNITY_ANDROID if (Input.touchCount == 0) { } else if (Input.touchCount == 1) { MapSwipe(); } else if (Input.touchCount >= 2) { MapPinch(); } #endif } //スワイプの処理は省略 ~Skip //ピンチの処理 public void MapPinch() { #if UNITY_EDITOR //エディターではマウスポジションで判定する(検証用) var pinch = Observable.EveryUpdate ().Select (pos_dist => Input.mousePosition.x); var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0)); #elif UNITY_IOS || UNITY_ANDROID //2点の距離を取得 var pinch = Observable.EveryUpdate () .Select (pos_dist => Vector2.Distance(Input.GetTouch (0).position, Input.GetTouch (1).position)); //タッチカウントが2では無くなったら購読停止 var stop = Observable.EveryUpdate ().Where (_ => Input.touchCount != 2); #endif //次フレームのポジションを取得して2点座標の差分を取得 IDisposable onPinch = pinch.Zip(pinch.Skip(1), (dist1, dist2) => new { diff = dist2 - dist1}) .TakeUntil(stop) .Subscribe(distanceParam => { // カメラのfieldOfViewを変更することでピンチを再現する MainCamera.fieldOfView -= distanceParam.diff / 30; //0以下になると反転してしまうので限界値を設ける if(MainCamera.fieldOfView < 4){ MainCamera.fieldOfView = 4; } if(MainCamera.fieldOfView > 70){ MainCamera.fieldOfView = 70; } }); } }
若干動きがあやしいです、
本来はスワイプ > ピンチの状態管理はもっと適切に行う必要があります。
ご意見マサカリ歓迎しています!
Unity UniRxをつかってスワイプでカメラ移動をしてみた
UniRxをAssetsストアから落としてきます。
アセット検索すれば出てきます。
パネルを用意する
今回わかりやすいように色をかえてみましたが
特に意味はありません。
カメラの位置を決定する
斜め45℃下を向くカメラを置き、パネル全体が写るように調整します。
カメラにRigidBodyを追加する。
適当なオブジェクトに下記スクリプトをアタッチする
名前は適当にCameraMove.csにしました。
MainCameraにカメラをタッチします。
using UnityEngine; using System.Collections; using System; using UniRx; public class CameraMove : MonoBehaviour { //メインカメラ public Camera MainCamera; // FixedUpdate void FixedUpdate () { // マウスが押された場合 if (Input.GetMouseButtonUp(0)) { } else if (Input.GetMouseButton(0)) { MapSwipe(); } } //スワイプの処理 public void MapSwipe() { //mousePositionの取得 var drug = Observable.EveryUpdate ().Select (pos => Input.mousePosition); //クリックが話されたら購読停止 var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0)); //マウスポジションの4フレーム後のポジションを比較してカメラのRigidbodyに力を加える。 IDisposable onDrug = drug.Zip (drug.Skip (4), (pos1, pos2) => new {x = pos2.x - pos1.x, z = pos2.y - pos1.y}) //クリックが話されたら購読停止 .TakeUntil(stop) .Subscribe (deltaPosition => { MainCamera.gameObject.GetComponent<Rigidbody>().velocity = new Vector3(deltaPosition.x, 0, deltaPosition.z) * -5; } ); } }
いい感じにうごきました、がブログ用に抜粋して書いたのでちょっといびつですね。
実際はupdate内で状態管理したりすると思います。
稼働領域の限界値はSubscribe内に書くと慣性の力で限界値よりも動いてしまう場合があるので
それはupdate内で監視しておいた方がいいかもしれません。
また、購読完了はTakeUntilでやりましたが、実際に使う場合は
停止は停止でSubscribe呼び出してその中でステート変更などを行うのが良いでしょう。
次は似非ピンチイン、ピンチアウトでも作成しようかと思います。
--- 追記
上記スクリプトはエディター専用で実機の場合は別途対応が必要になります。
面倒くさがらずにあるなら書けやと言われそうなので下記に記載しておきます。
参考までにどうぞmm
using UnityEngine; using System.Collections; using System; using UniRx; public class CameraMove : MonoBehaviour { //メインカメラ public Camera MainCamera; // FixedUpdate void FixedUpdate () { #if UNITY_EDITOR // マウスが押された場合 if (Input.GetMouseButtonUp(0)) { } else if (Input.GetMouseButton(0)) { MapSwipe(); } #elif UNITY_IOS || UNITY_ANDROID if (Input.touchCount == 0) { } else if (Input.touchCount == 1) { MapSwipe(); } #endif } //スワイプの処理 public void MapSwipe() { #if UNITY_EDITOR //mousePositionの取得 var drug = Observable.EveryUpdate ().Select (pos => Input.mousePosition); //クリックが話されたら購読停止 var stop = Observable.EveryUpdate ().Where(_ => Input.GetMouseButtonUp(0)); #elif UNITY_IOS || UNITY_ANDROID //positionの取得 var drug = Observable.EveryUpdate ().Select (pos => Input.GetTouch(0).position); //タッチカウントが変更され場合に購読停止 var stop = Observable.EveryUpdate ().Where(_ => Input.touchCount != 1); #endif //マウスポジションの4フレーム後のポジションを比較してカメラのRigidbodyに力を加える。 IDisposable onDrug = drug.Zip (drug.Skip (4), (pos1, pos2) => new {x = pos2.x - pos1.x, z = pos2.y - pos1.y}) //クリックが話されたら購読停止 .TakeUntil(stop) .Subscribe (deltaPosition => { MainCamera.gameObject.GetComponent<Rigidbody>().velocity = new Vector3(deltaPosition.x, 0, deltaPosition.z) * -5; } ); } }
ご意見マサカリ等歓迎しております!
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します。
これを前回同様にアタッチすれば。。。
作り方はわかったので後は鍛錬あるのみです。
(あれ最近プログラマじゃ無くなってきてる。。?)