Unityでボーリングゲームを作る #4 倒れていないピンを持ち上げる

投稿者: | 2020-01-28


倒れていないピンを持ち上げる部分を作ります。

Cubeがアニメーションで下まで動いたら、ピンをCubeの子オブジェクトにして、Cubeが上下に動くアニメーションに合わせてピンも動くようにします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;


public class GameScript3 : MonoBehaviour
{
    public Text text;
    GameObject[] pins;
    public float q = 0.01f;

    Animator pinsetterAnimator;
    public GameObject pinsetter;

    bool setpin = false;

    // Start is called before the first frame update
    void Start()
    {
        text.text = "";
        pinsetterAnimator = pinsetter.GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.C) && !setpin)
        {
                text.text = CountStandingPins().ToString();          
        }

        if(Input.GetKeyDown(KeyCode.R))
        {
            SceneManager.LoadScene("Bowling");
        }
    }

    int CountStandingPins()
    {
        pins = GameObject.FindGameObjectsWithTag("Pin"); //「Pin」タグのついたオブジェクトを探して、全て配列pinsにいれる

        if(pins.Length == 0)
        {
            return 0;
        }
        else
        {
            int n = 0;

            foreach(GameObject pin in pins)
            {
                if(pin.transform.rotation.x * pin.transform.rotation.x < q && pin.transform.rotation.z * pin.transform.rotation.z < q)
                {
                    n++;
                    pin.GetComponent<Rigidbody>().isKinematic = true;
                }
            }

            if(n != 0) // 立っているピンがあるとき
            {

                StartCoroutine("SetPin", pins);
                setpin = true;
            }

            return n; // 立っているピンの個数を返す
        }     
    }


    IEnumerator SetPin()
    {
        pinsetterAnimator.SetTrigger("Pinsetter"); // 天井が降りる

        yield return new WaitForSeconds(1.5f);

        text.text = "";

        foreach (GameObject pin in pins)
        {
            if (pin.transform.rotation.x * pin.transform.rotation.x < q && pin.transform.rotation.z * pin.transform.rotation.z < q)
            {
                pin.transform.parent = pinsetter.transform; // 立っているピンを天井の子オブジェクトにする。
            }
        }

        pinsetterAnimator.SetTrigger("Pinsetter"); // 天井が上がる

        yield return new WaitForSeconds(2f);

        pinsetterAnimator.SetTrigger("Pinsetter"); // 天井が降りる

        yield return new WaitForSeconds(1.5f);

        foreach (GameObject pin in pins)
        {
            if (pin.transform.rotation.x * pin.transform.rotation.x < q && pin.transform.rotation.z * pin.transform.rotation.z < q)
            {
                pin.transform.parent = null; // 立っているピンと天井の親子関係を解く
                pin.GetComponent<Rigidbody>().isKinematic = false;
            }
        }

        pinsetterAnimator.SetTrigger("Pinsetter"); // 天井が上がる
        yield return new WaitForSeconds(1.1f);
        setpin = false;
    }

}

前の記事で作った倒れていないピンを数えるスクリプトに追加しました。

倒れていないピンを数えて、0でなければコルーチンがスタートします。コルーチンの中で1~2秒ごとにCubeのアニメーションを遷移させながら、ピンを持ち上げるための処理をしています。

transform.parent に親に設定したいオブジェクトのtransformを入れると親子関係を作れます。

foreach (GameObject pin in pins)
{
    if (pin.transform.rotation.x * pin.transform.rotation.x < q && pin.transform.rotation.z * pin.transform.rotation.z < q)
    {
        pin.transform.parent = pinsetter.transform; // 立っているピンを天井の子オブジェクトにする。
    }
}

Cubeが下まで降りてきたときに親子関係を作ると、親であるCubeが上に動いた時にピンもついていきます。ピンはもともとis Kinematicのチェックが外れていて、物理演算で動いているので、Transformを使って動かすためにis Kinematicをtrueにする必要があります。

pin.GetComponent<Rigidbody>().isKinematic = true;

Cubeが再度降りたあとに、ピンのparentメンバにnullを入れて親子関係を解消します。そしてisKinematicをfalseに戻します。

foreach (GameObject pin in pins)
{
    if (pin.transform.rotation.x * pin.transform.rotation.x < q && pin.transform.rotation.z * pin.transform.rotation.z < q)
    {
        pin.transform.parent = null; // 立っているピンと天井の親子関係を解く
        pin.GetComponent<Rigidbody>().isKinematic = false;
    }
}

ピンが落下するとピンにつけたスクリプトで、ピンを破壊するようにしていましたが、タグでピンを検索し配列に入れた後にその中のピンが無くなるとエラーになるようなので、破壊せずに動きを止めるようにしました。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PinScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if(transform.position.y < -5f)
        {
            GetComponent<Rigidbody>().isKinematic = true;
        }
    }
}

また、ピンとCubeは衝突しないようになっています。

オブジェクト同士を衝突しないようにする

ピンのプレハブとCubeにレイヤーを付けます。

Edit -> Project Settings… -> Physics の下の方のLayer Collision Matrixで、これらのレイヤーの交点のチェックボックスのチェックを外します。

これでこのレイヤーを設定したオブジェクト同士は衝突しなくなります。

Cubeのアニメーションを作る

まずピンの上にCubeを置きます。

Cubeを選択したままAnimationタブでCreateボタンを押し、アニメーションの名前を付けて保存します。

Add PropertyからPositionを追加します。

タイムラインの白い縦線を一番右に置いて、録画ボタンを押し、Cubeを少し下に動かします。

再度録画ボタンを押すとアニメーションが完成します。

同じ場所に白い縦線を置いたままでCubeのインスペクタを見ます。

Transformの右上の歯車ボタンからCopy ComponentをクリックしてTransformの値をコピーします。

アニメーション名を押して、Create New Clip…で新しいアニメーションを作ります。

再度Positionを追加して、タイムラインの最初に縦線を置いてから、録画ボタンを押します。

この状態で、TransformでPaste Component Valuesを選択し値をペーストして、また録画ボタンを押します。

Window -> Animation -> Animatorでアニメーターコントローラーを開きます。

すると、今作ったアニメーションがついた2つの灰色のステートが作られています。

アニメーターコントローラーの使い方はこちら

Startからまず上がるアニメーションに行ってそれからは、上がるアニメーションと下がるアニメーションが交互に遷移するように矢印を付けました。

トリガーパラメータを作って、白い矢印すべてに同じトリガーの条件を設定します。

ステートのインスペクタにあるクリップをダブルクリックしてアニメーションがループ再生されないように、Loop Timeのチェックを外しました。

スクリプトの中でSetTrigger()を使うと上下のアニメーションが交互に切り替わります。

pinsetterAnimator.SetTrigger("Pinsetter");

コメントを残す

メールアドレスが公開されることはありません。