ラグドールに切り替えて、ゾンビが頭を撃たれると倒れるようにします。
前の記事のプレイヤーを追うキャラクターとは別に、ラグドールにするための同じモデルをシーンに新しく配置して、Createからラグドールを新規作成します。
体の部位をアタッチするウィンドウが出るので、ヒエラルキーウィンドウからリストの各部へゾンビキャラクターの対応する部位をドラッグアンドドロップしていきます。
ヒエラルキーウィンドウで選択したオブジェクトが体のどの部分なのかシーンで確認できます。
とりあえず適当に完成させてCreateを押してみます。
Createを押すと、キャラクターのモデルへコライダーやジョイントが自動で追加されますが、シーンをみるとコライダーの大きさや配置が不自然です。
ゲームを開始してみるとやはり動きが変です。
失敗したラグドールを削除してキャラクターのモデルをシーンに再配置し、ラグドールを新規作成し直して、今度は「Arm」に「Shoulder」をアタッチしてみます。
すると自然になりました。
このラグドールはわかりやすい名前に変えて、プロジェクトにドラッグアンドドロップしてプレハブ化しておきます。
ゾンビが頭を撃たれるとラグドールと切り替えて倒れるようにする
ゾンビが銃弾と衝突できるように、ゾンビの体にコライダーを付けます。今回はプレイヤーを追うゾンビもラグドールにしてみます。(以下では、ゾンビが撃たれた後に生成される方をラグドールと書いています。)
銃弾がどのオブジェクトと衝突したかを知るためにタグを使います。
コライダーとRigidbody、ジョイントが新しく追加された部位を選択して、頭以外のオブジェクトには「Agent」タグを付けました。
アニメーションで動かすので、RigidbodyでUse Gravityをオフにして、Is Kinematicをオンにしておきました。
そして、頭のオブジェクトにだけ「AgentHead」タグを付けました。
銃弾のプレハブに付けたスクリプトで、ゾンビへの衝突を検出します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletScript : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine("destroyBullet");
}
// Update is called once per frame
void Update()
{
}
private IEnumerator destroyBullet()
{
yield return new WaitForSeconds(0.5f);
Destroy(gameObject);
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Agent") // ゾンビの頭以外に銃弾が当たった時
{
}else if (collision.gameObject.tag == "AgentHead") // ゾンビの頭にあたった時
{
GameObject parent = collision.gameObject.transform.root.gameObject; // 階層の一番上のオブジェクト
// 撃たれたゾンビと同じ場所にラグドールを生成
GameObject ragdollIns = Instantiate(GunScript.girlRagdoll, parent.transform.position, parent.transform.rotation);
ragdollIns.GetComponent<ragDollScript>().impact = -collision.impulse; // ラグドールに力を伝える
Destroy(parent); // 撃たれたゾンビを削除
}
Destroy(gameObject); // 銃弾を削除
}
}
ゾンビに銃弾が衝突したときに、もし頭ならラグドールと入れ替えます。銃弾が衝突したオブジェクトは collision.gameObject で取得できますが、ゾンビ全体を削除したいので、その一番上の階層にあるオブジェクトを transform.root.gameObject で取得しています。
銃弾のプレハブにインスペクタからラグドールをアタッチすることはできないので、銃を発射するスクリプトのstatic変数にラグドールを入れておいてそれを使っています。
GameObject ragdollIns = Instantiate(GunScript.girlRagdoll, parent.transform.position, parent.transform.rotation);
銃を撃つスクリプトははじめからシーン上のオブジェクトについているので、そこにラグドールをアタッチして、さらにスタート時に静的変数に代入しています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class GunScript : MonoBehaviour
{
public ParticleSystem gunParticle;
public GameObject gunLight;
public Animator gunAnimator;
public float rate = 0.01f;
public GameObject bullet;
public GameObject playerFace;
public float power;
public GameObject Zombie;
public GameObject Ragdoll;
List<GameObject> zombieList;
public GameObject player;
public static GameObject girlRagdoll;
// Start is called before the first frame update
void Start()
{
zombieList = new List<GameObject>();
StartCoroutine("GenarateZombie");
girlRagdoll = Ragdoll;
}
// Update is called once per frame
void Update()
{
// default状態で、マウス左クリックを押した時
if (Input.GetMouseButtonDown(0) && gunAnimator.GetCurrentAnimatorStateInfo(0).IsName("defualt")) // ママ
{
gunParticle.Play(); // パーティクルの再生
gunAnimator.SetTrigger("Shoot");
StartCoroutine("Shoot");
}
// MachineGin_shoot状態で、マウス左クリックを離した時
else if (Input.GetMouseButtonUp(0) && gunAnimator.GetCurrentAnimatorStateInfo(0).IsName("MachineGin_shoot"))
{
gunParticle.Stop(); // パーティクルの停止
gunAnimator.SetTrigger("Shoot");
StopCoroutine("Shoot");
}
if (Input.GetKeyDown(KeyCode.T)) // Tキーを押した時
{
gunLight.SetActive(!gunLight.activeSelf); // ライトのオンオフ
}
if (Input.GetKeyDown(KeyCode.R))
{
if (gunParticle.isPlaying)
gunParticle.Stop(); // パーティクルの停止
gunAnimator.SetTrigger("DoReload");
}
if (Input.GetMouseButton(1) && gunAnimator.GetCurrentAnimatorStateInfo(1).normalizedTime >= 1 && (gunAnimator.GetCurrentAnimatorStateInfo(1).IsName("LookInAnimation2") || gunAnimator.GetCurrentAnimatorStateInfo(1).IsName("New State")))
{
gunAnimator.SetTrigger("LookIn");
}
else if (!Input.GetMouseButton(1) && gunAnimator.GetCurrentAnimatorStateInfo(1).normalizedTime >= 1 && gunAnimator.GetCurrentAnimatorStateInfo(1).IsName("LookInAnimation"))
{
gunAnimator.SetTrigger("LookIn");
}
}
private IEnumerator Shoot() // 弾を連射するコルーチン
{
while (true)
{
yield return new WaitForSeconds(rate);
Instantiate(bullet, playerFace.transform.position, playerFace.transform.rotation).GetComponent<Rigidbody>().AddForce(playerFace.transform.forward * power, ForceMode.Impulse);
}
}
private IEnumerator GenarateZombie() // 1秒ごとにゾンビを生成するコルーチン
{
while (true)
{
zombieList.Add(Instantiate(Zombie,new Vector3(Random.Range(-88,88),0f,Random.Range(-88,88)),transform.rotation)); // ゾンビをランダムの位置に生成
zombieList[zombieList.Count - 1].GetComponent<ZombieScript>().player = player;
yield return new WaitForSeconds(1);
}
}
}
生成したゾンビのインスタンスをリストに入れる必要は無いかもしれません。
銃弾の力が強いので、切り替わったラグドールが銃弾と衝突すると不自然にゾンビが吹っ飛んでしまうので、このラグドールと銃弾は衝突しないようにします。
それには倒れるラグドールのプレハブに「Agent」レイヤーを付けます。
Change Layerのウィンドウが出たらYesをクリックして、子オブジェクトのレイヤーも一括で変更します。
銃弾のプレハブには「Bullet」レイヤーが設定されています。
Edit -> Project Settings… -> Physics の下部のLayer Collision Matrixで、AgentとBulletの交点にチェックを入れます。
こうすると銃弾とラグドールは衝突せず、頭を撃たれるとそのまま真下に崩れ落ちます。