【開発メモ】アクション「壁ジャンプ」の実装

というわけで、今回のお題はこれです!

20160313

所謂「壁ジャンプ」「壁キック」と言われるものです!
壁を蹴ってジャンプし、高い足場へも登れるようにします。

実は旧ブログでこっそりと実装していたりしましたが、実用性のない微妙なものだったので、ちょっと作り変えてみました!
両側に壁がある場合に連続で壁ジャンプできるようにしたのが大きな変更です。

基本的な仕様

完全な2Dアクションならともかく、3Dアクションでどこでも壁蹴り出来るようにするのは厳しいです。
壁との接触判定が難しい上、意図しない場面で壁ジャンプが暴発する可能性もあります。

ということで、任天堂の某SFアクションと同じように「壁ジャンプ専用の壁」を用意します。
その壁に接触中にジャンプボタンを押した場合に壁ジャンプを行うようにします。

モーションはダッシュジャンプ時と一緒で、くるくる回転させます。
専用モーションを作るのが面倒なので、とりあえず回らせれば見栄えがよくなるためです!
壁ジャンプ実行前にAnimator.Play()で切り替えてしまいます。

壁ジャンプ専用オブジェクトとスクリプトの作成

まず「壁ジャンプ専用の壁」を作ります。
Cubeを生成し、適当にそれっぽいマテリアルを作って適用します。

shot2ss20160313142132946

判定をCollisionで行うのは厳しいため、壁本体の判定とは別でTrigger用の判定を付けておきます。
Triggerの判定を壁ジャンプが可能な方向に少しずらしておきます。

次にスクリプトを作成します。
以下は壁用オブジェクトに設定する「ClimbWall」クラスです。

【ClimbWallクラス】

using UnityEngine;
using System.Collections;

public class ClimbWall : MonoBehaviour {

    public Vector3 climbVect;

    private CharacterMotor charMotor;

    protected void OnTriggerStay(Collider c) {
        if (c.gameObject.tag == "Player") {
            if (Input.GetButtonDown("Jump")) {
                charMotor = c.gameObject.GetComponent<CharacterMotor>();

                if (!charMotor.IsGrounded()) {
                    c.GetComponent<Animator>().Play("DashJump");

                    charMotor.movement.velocity.y = 0;
                    charMotor.jumping.enabled = false;
                    iTween.Stop(c.gameObject);

                    c.transform.LookAt(c.transform.position + new Vector3(climbVect.x, 0f, climbVect.z));

                    iTween.ValueTo(c.gameObject, iTween.Hash(
                        "from", climbVect,
                        "to", Vector3.zero,
                        "time", 0.5f,
                        "delay", 0.1f,
                        "onupdate", "Thrust",
                        "onupdatetarget", c.gameObject,
                        "oncomplete", "resetJump",
                        "oncompletetarget", gameObject
                    ));
                }
            }
        }
    }

    private void resetJump() {
        charMotor.jumping.enabled = true;
    }
}

「移動系ならiTween.MoveToでOK」と思ったのですが、今回はValueToを使用しています。
理由は単純で、iTweenのMove系は接触判定を突き抜ける可能性が非常に高いためです。
恐らくpositionを直接変化させているためだと思われますが・・・。
CharacterController的には CharacterController.Move() を使う必要があるので、仕方なくPlayerクラスにMove専用の関数を設定し、そこで一定時間Moveを呼んでもらうことにします。

【Playerクラス(一部)】

public abstract class Player : MonoBehaviour {
    protected void characterMove( Vector3 moveVect) {
        charControl.Move(moveVect * Time .deltaTime * 100);
    }
}

壁ジャンプする方向は「climbVect」としてフィールドに持たせ、インスペクターから設定します。
当初はプレイヤーとオブジェクトの方向から自動的に算出しようとしましたが、2点間のベクトルに斜め方向も加わってしまい、想定した動きにならなかったので挫折。
ステージに配置する際のRotationを、なるべくカメラに対して直角になるよう調節する必要があります。
一応climbVectにY軸のみを設定することで真垂直に壁キックができるというメリットはあります。
また、CharacterControllerのジャンプ機能が働いていると大ジャンプになってしまうことがあるので、iTweenの実行中は無効化しておきます。

iTweenのonupdateやoncompleteのターゲットには注意が必要です。
ClimbWallクラスで行うのか、Playerクラスで行うのかを考えると分かりやすいです。
勿論指定を誤ると上手く動きません。
そもそも移動処理をClimbWallクラスで行うのが間違っている気がしますが、そのうちいい感じにします!

まとめ

ということで「壁ジャンプ」を実装してみました!
やや挙動がおかしい上、たまに正常に動作しなかったりしますが、とりあえずこれがあることを前提にステージを作っていきます。

【Unity】iTween実行時のエラー「Material doesn’t have a color property ‘_Color’」について

※本記事投稿時に使用したiTweenのバージョンは2.0.5です。

shot2ss20160207230450889

というわけで、iTweenでColor系やFade系の関数を使った際、

「Material doesn’t have a color property ‘_Color’」

といったエラーが出ることがあります。
意味はそのままで、「マテリアルに_Colorというプロパティがない」といった感じでしょうか。

その「Color_」ですが、Unity5標準のシェーダーには大抵入っている基本的なプロパティです。
iTween実行時もデフォルトでは_Colorを変化させるようです。
ただし、パーティクル系のシェーダーなどは「_TintColor」となっていたりします。
故に「実行対象のオブジェクトの子にパーティクルがある」といったケースで発生しやすいエラーです。

対処法

エラーが出ても他のマテリアルの変化は正常に行われるわけですが、やはりエラーは嫌なものです。
ということで、iTween.csの該当部分を修正してみました!
バージョン2.0.5版でいくと3338行目付近になります。

for (int i = 0; i < GetComponent<Renderer>().materials.Length; i++) {
    if (gameObject.GetComponent<Renderer>().materials[i].HasProperty(namedcolorvalue.ToString())) {
        colors[i,0]=GetComponent<Renderer>().materials[i].GetColor(namedcolorvalue.ToString());
        colors[i,1]=GetComponent<Renderer>().materials[i].GetColor(namedcolorvalue.ToString());
    }
}

for文でマテリアルの数だけGetColorを実行しています。
その後のGetColor()でエラーが出ているため、その手前でMaterial.HasPropety()で該当のプロパティ(この場合は_Color)があるかをチェックします。
これでプロパティが存在しない場合でもエラーが出なくなります。

もし自作シェーダーなどでプロパティ名が統一されている場合、iTween実行時のパラメータ「NamedColorValue」を指定する手もあります。
これがデフォルトでは_Colorになっているので、文字列でプロパティ名を指定すればOKです。

まとめ

そんなわけで、超さくっとiTweenのエラーについてのお話でした!
その場で直しても時間が経つと忘れてしまったりするので、面倒でも記事にして残していきたいです。

そもそもiTweenのソースって勝手に変えていいものか分かっていなかったりします。
オープンソースだし大丈夫だと思われますが・・・。

【Unity】iTweenの停止と再実行について

というわけで、今回はiTweenに関するちょっとしたメモのような感じになります!
お題はタイトル通り、iTwwenの停止と再実行に関してです。

自分のゲームでは所謂「2段ジャンプ」をiTweenで行っています。
AnimatorControllerの2段ジャンプステートにスクリプトを付与し、StateMachineBehaviourのOnStateEnter()で行っています。
Animatorの詳しい実装は割愛しますが、「ジャンプ中に再度ジャンプボタン押下」で実行します。

・・・が、実行したiTweenが残っている間に再度2段ジャンプをすると上手く動作しません。
timeで指定する値を小さくする手もありますが、全体的なアニメーションまで変わってしまい、得策とは言えません。
ここでiTween.Stop()の出番です!

using UnityEngine;

namespace StateController {

    public class DoubleJump : StateMachineBehaviour {

        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            animator.gameObject.GetComponent<CharacterMotor>().movement.velocity.y = 0;
            iTween.Stop(animator.gameObject);

            iTween.MoveBy(animator.gameObject, iTween.Hash(
                "amount", new Vector3(0f, 25f, 0f),
                "time", 2f,
                "delay", 0.01f
            ));

            animator.ResetTrigger("Jump");
        }
    }
}

iTween.Stop()のオーバーロードは6種類あります。

・Stop()
・Stop(string type)
・Stop(GameObject target)
・Stop(GameObject target, bool includechildren)
・Stop(GameObject target, string type)
・Stop(GameObject target, string type, bool includechildren)

引数なしはシーン内の全iTweenが対象で、他は型や文字列、オブジェクトを指定して停止します。
「includechildren」で子オブジェクトを対象にするかを指定できます。
今回の対象はプレイヤー自身のiTweenなので、gameObjectを指定します。

iTween.Stop後にすぐ次のiTweenを実行すると上手くいかないようです。
自分の場合、引数として「delay」を指定し、体感できないレベルの遅延(0.01f)を入れてあげると上手くいきました!

iTweenは実行時にコンポーネントが付与されるので、それを削除・無効化するのも手かもしれません。
が、いちいち削除するのは面倒なので、自分は遅延させて実装しました。

まとめ

そんなわけで、iTweenの停止に関するちょっとしたお話でした。
iTweenは非常に便利ですが、思わぬところで詰まることがあるので、今回のような経験を覚えておきたいところです。