2014年12月06日

[Unity] 死亡時にぐったりと倒れるプレイヤーキャラクターの処理(ラグドール)

昔のゲームは敵にやられるとキャラクター「やられモーション」をとってゲームオーバーとなるが、最近の3Dゲームはもっぱら「ふっとんだり」「ぐったりする」だけってことが多いような気がする。そこで…

目的:敵にやられたらモデルをぐったりと倒れさせたい。

クリックしたらラグドールに切り替わるまで

現在、前回のThird Person Controllerを利用し、ニコニコ立体ちゃんを動かしているとする。

墓場の立体ちゃん

Hyeralchy上はこんな感じ↓

Alt text

Third Person Contollerの下にAlicia_solidが置いてある状態。これのキャラをぐったりさせるにはどうすればいいかと言うと、

ググる。

キャラクターをタッチ(クリック)したらRagdollが適用されてぐったりする - 子ノ刻ラボラトリィ
http://nenokoku-laboratory.hateblo.jp/entry/2013/08/10/023612-touch-and-ragdoll

「ラグドール」…ぐったりしたモデルのこと。これを予め作っておいて、やられたタイミングで元気なモデルからぐったり(ラグドール)モデルに切り替えればいいようだ。

Unity 3D Ragdoll Easy Tutorial - YouTube
https://www.youtube.com/watch?v=dCwNaE_eVsM

ラグドールはRagdollGeneratorで簡単につくれるようだ。というわけで動画に従い、Alicia_ragdollを作成。出来たラグドールモデルはThird Person Charactorの下に置いておく。ついでにラグドールモデルは非アクティブにしておく。こんな感じ↓

  • プレイヤー(動ける)キャラクター(Alicia_solid):最初はアクティブにしておき、死んだタイミングで非アクティブ
  • ラグドール(ぐったり)キャラクター(Alicia_ragdoll):最初は非アクティブにしておき、死んだタイミングで生成と同時にアクティブ

これで差し替わるはず。

新規スクリプト「RagdollGenerator.cs」を作成。上記サイトのひとつめのスクリプトを丸コピして保存。出来たスクリプトをThird Person Characterに放り込んでアタッチ。ragdollPrefabの欄にラグドールモデル(Alicia_ragdoll)を設定する。死亡時にこのモデルが生成されるようになる。

しかしこのスクリプトには気になるところがある…なぜそうしているのか理由がわからないのだが、既存のラグドールをコピーして新たに生成(Instantiate)している。ひとつのキャラクターから同じ死体が2つも3つも生まれるケースなんてありえないし、既存のラグドールがあるならそっちを使えばいいのでは…?

また、ついでなので非アクティブのオブジェクトをその場でアクティブにする処理も追加してみた。こんな感じ。

using UnityEngine;
using System.Collections;

// ラグドールの設定をするスクリプト。
public class RagdollGenerator : MonoBehaviour {

    // ラグドールのプレハブ。
    public GameObject ragdollPrefab ;

    // ラグドールの生成。
    public void Generate(Transform originalRoot , Vector3 velocity) {
        //(ラグドールモデルの生成処理の削除)
        //ラグドールを強制アクティブ化
        ragdollPrefab.SetActive(true);
        CopyTransformRecursively(originalRoot, ragdollPrefab.transform, velocity);
    }

    // トランスフォームを再帰的にコピーする。
    // ついでに初速の設定も行う。
    private void CopyTransformRecursively(Transform src, Transform dst, Vector3 velocity) {
        dst.localPosition = src.localPosition;
        dst.localRotation = src.localRotation;
        if (dst.rigidbody){
            dst.rigidbody.velocity = velocity;
        }
        foreach (Transform child in src) {
            Transform srcChild = child;
            Transform dstChild = dst.Find(srcChild.name);
            if (dstChild) CopyTransformRecursively(srcChild, dstChild, velocity);
        }
    }
}

さらに新規スクリプト「Alicia_Control.cs」を作成。Third Person Characterにアタッチ。ここに「クリックしたらラグドールに切り替わる」という処理を書くのだが、上記サイトのふたつめのスクリプトはタッチ処理や範囲の指定をしているので、そのあたり適当に省いてシンプルに書きかえる。

using UnityEngine;
using System.Collections;

public class AliciaControl : MonoBehaviour {

    //元気なキャラ
    public GameObject model_genki;

    // Use this for initialization
    void Start () {
    }

    void Update () {
        //クリックしたら
        if (Input.GetButtonDown("Fire1")) {
            //元気なモデルを非アクティブ
            model_genki.SetActive(false);
            //ラグドールの設定とアクティブ化
            GetComponent<RagdollGenerator>().Generate(model_genki.transform, new Vector3 (0, 0, 0));
        }
    }
}

その後、model_genki に Alicia_solid をD&Dして設定する。

さらに改良

これで一応クリックで倒れるようにはなった。だが、原状では問題がいくつかある。

  1. 移動中のスピードをラグドールにも適用させたい。例えば前に走っている最中にやられたら、前のめりに倒れるなど。
  2. 現状、クリックする度に無限に死に続ける。できれば2回目のクリックはゲームをリスタートするようにしたい。
  3. やられた後でもキー操作できてしまう。(透明なキャラクターが走りだす)

1についてはすでにロジックが用意してあるので使わせていただく。RagdollGenerator.Generateの2番めの引数である。つまり

            //ラグドールの設定とアクティブ化
            GetComponent<RagdollGenerator>().Generate(model_genki.transform, rigidbody.velocity);

これでOK。

2は、死んだら(クリックしたら)死亡中のフラグをセットし、死亡中にさらにクリックされたらゲームをリスタートさせればよい。

シーンを再スタートする - Unityな日々(Unity Geek)
http://unitygeek.hatenablog.com/entry/2012/11/01/160325

    //死亡中フラグ
    bool isDead = false;

    /* --- (中略) --- */

    void Update () {
        //クリックしたら
        if (Input.GetButtonDown("Fire1")) {
            //死亡中なら
            if(isDead) {
                //リスタート
                Application.LoadLevel(0);
            } else {

                /* --- (中略) --- */

                //死亡中フラグ設置
                isDead = true;
            }
        }
    }

これでOK。

最後に3だが、これは面倒だ。キャラクターを動かしているスクリプトを止めれば解決しそうな気がするが、単純に無効化するだけでは、例えば走っている途中にやられてぐったりした場合、無効化した直前まで設定されていた速度のまま等速直線運動を続け、どこかに飛んでいってしまう。 「動かす」スクリプトを「止める」のではなく「入力を無効化する」処理を組み込む必要がありそうだ。

どうやらThirdPersonUserControl.csが入力を受け付けている部分らしいので、これを改造する。

    //入力の無効化フラグ
    public bool rejectInput = false;

    /* --- (中略) --- */

    // Fixed update is called in sync with physics
    void FixedUpdate ()
    {
        if (rejectInput) {
            // pass all parameters to the character control script
            // 無入力状態を送る
            character.Move( Vector3.zero, false, false, lookPos );
        } else {
            // read inputs
            bool crouch = Input.GetKey(KeyCode.C);

            /* --- (中略) --- */

            // pass all parameters to the character control script
            character.Move( move, crouch, jump, lookPos );
        }
    }

そしてAlicia_control.csにも以下の行を追加。

                //入力を無効化する
                GetComponent<ThirdPersonUserControl>().rejectInput = true;

これで実行してみる。

Alt text

晴れて「クリックしたらぐったり倒れてして、さらに動けない(再クリックでリスタート)」ことを確認した。めでたしめでたし…

じゃない。「敵にやられたら」の部分が抜けている。それはまた今度。

ここまでの成果

http://chomstudio.com/d/uni/ragdolltest/Build.html


ところで、立体ちゃんのラグドール、何度かボーン設定を変えて試して見たけど、どうもぐったりというよりは足をピンと伸ばして硬直したような倒れ方するんだよね…何が問題なんだろうか……。

あと、ジャンプ上昇中にクリックすると、上にスポーンと飛んでいって面白い。これで高い障害物を越えたりして、別のゲームが出来そうな予感がするw

posted by ちょむ at 23:36| Comment(0) | TrackBack(0) | Unity
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

※ブログオーナーが承認したコメントのみ表示されます。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/106203152
※ブログオーナーが承認したトラックバックのみ表示されます。
※言及リンクのないトラックバックは受信されません。

この記事へのトラックバック