C#のevent構文を使ってゲームの進行管理をしてみました。
シーンには床と2種類の計9つのアイテムと2つのトリガーコライダーがあります。アイテムは全てItemタグが付いていて、スクリプトに種類ごとの名前を持っています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemScript2 : MonoBehaviour
{
[SerializeField] string itemName;
public string GetItemName()
{
return itemName;
}
}
2つのコライダーには別々の名前と同じタグが付いていて、BoxコライダーのIs Triggerにチェックが入っています。
進行管理用のスクリプトには、テキストやマテリアルがアタッチされています。
プレイヤーのスクリプトでは、Updateでアイテムを拾ったときと、OnTriggerEnterで、進行管理のスクリプトのイベントを実行するメソッドを呼んでいます。
[SerializeField] Text centerText;
[SerializeField] GameScript5 gameScript5;
// ...
private void Update()
{
RaycastHit hit;
// FPSカメラから正面にレイを飛ばす
if (Physics.Raycast(m_Camera.transform.position, m_Camera.transform.forward, out hit, Mathf.Infinity, ~(1 << 9)))
{
if (hit.distance <= 2f)
{
if (hit.collider.tag == "Item")
{
centerText.text = "[LMB]拾う";
// アイテムをクリックしたら
if (Input.GetMouseButtonDown(0))
{
centerText.text = "";
gameScript5.PickItem(hit.collider.gameObject.GetComponent()); // イベントを実行するメソッド
hit.collider.gameObject.SetActive(false); // アイテムを非アクティブにする
}
}
else centerText.text = "";
}
else centerText.text = "";
}
else centerText.text = "";
// ...
private void OnTriggerEnter(Collider other)
{
if (other.tag == "Collider")
{
gameScript5.PlayerCollide(other.gameObject); // イベントを実行するメソッド
}
}
// ...
そして、進行管理でイベントの中身を入れ替えます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class GameScript5 : MonoBehaviour
{
public delegate void PickItemEventHandler(ItemScript2 item);
public event PickItemEventHandler pickItem;
public delegate void PlayerCollideEventHandler(GameObject obj);
public event PlayerCollideEventHandler playerCollide;
int itemCount; // 拾ったアイテムの数
[SerializeField] Text text;
[SerializeField] MeshRenderer planeMeshRenderer;
[SerializeField] Material[] materials;
// Start is called before the first frame update
void Start()
{
pickItem = PickItem1;
playerCollide = PlayerCollide1;
}
// Update is called once per frame
void Update()
{
}
// アイテム取得イベント実行
public void PickItem(ItemScript2 item)
{
if (pickItem != null) pickItem(item);
}
// アイテム取得1
void PickItem1(ItemScript2 item)
{
text.text = item.GetItemName() + "を拾った";
if(item.GetItemName() == "青い箱") itemCount++;
if (itemCount >= 3)
{
text.text = "アイテムを3つ拾った";
pickItem = PickItem2; // メソッドを変える
}
}
// アイテム取得2
void PickItem2(ItemScript2 item)
{
text.text = item.GetItemName() + "を拾った";
itemCount++;
if (itemCount >= 6)
{
pickItem = null; // 空にする
}
}
// 衝突イベント実行
public void PlayerCollide(GameObject obj)
{
if(playerCollide != null) playerCollide(obj);
}
// 衝突1
void PlayerCollide1(GameObject obj)
{
if (obj.name == "RedCollider")
{
text.text = "赤いコライダーに衝突した";
if (itemCount >= 3)
{
planeMeshRenderer.material = materials[0]; // 床の色を赤にする
playerCollide = PlayerCollide2; // メソッドを変える
}
}
else if (obj.name == "GreenCollider")
{
text.text = "緑のコライダーに衝突した";
}
}
// 衝突2
void PlayerCollide2(GameObject obj)
{
if (obj.name == "RedCollider")
{
text.text = "赤いコライダーに衝突した";
if (itemCount >= 6)
{
planeMeshRenderer.material = materials[1]; // 床の色を黄色にする
Destroy(obj); // 赤いコライダーを破壊
playerCollide = null; // 空にする
}
}
else if (obj.name == "GreenCollider")
{
text.text = "緑のコライダーに衝突した";
}
}
}
まず、スタート時には両方とも一つ目のメソッドが入っています。
void Start()
{
pickItem = PickItem1;
playerCollide = PlayerCollide1;
}
アイテムを拾った時には、アイテムの名前をスクリプトから取得してテキストに表示し、青い箱の時だけカウントアップします。カウントが3以上になると「アイテムを3つ拾った」と表示されて、2つ目のメソッドと入れ替えます。
// アイテムを拾った時にプレイヤーから呼ばれる
public void PickItem(ItemScript2 item)
{
if (pickItem != null) pickItem(item); // イベントを実行
}
// 1つ目
void PickItem1(ItemScript2 item)
{
text.text = item.GetItemName() + "を拾った";
if(item.GetItemName() == "青い箱") itemCount++;
if (itemCount >= 3)
{
text.text = "アイテムを3つ拾った";
pickItem = PickItem2; // メソッドを変える
}
}
プレイヤーがコライダーに入ったときには、衝突イベントを呼ぶメソッドが呼ばれ、一つ目のメソッドでは、衝突したコライダーを名前で判別して、青い箱を3つ以上取った後に赤いコライダーに入ると、床の色を変えて、2つ目のメソッドと入れ替えます。
public void PlayerCollide(GameObject obj)
{
if(playerCollide != null) playerCollide(obj);
}
void PlayerCollide1(GameObject obj)
{
if (obj.name == "RedCollider")
{
text.text = "赤いコライダーに衝突した";
if (itemCount >= 3)
{
planeMeshRenderer.material = materials[0]; // 床の色を赤にする
playerCollide = PlayerCollide2; // メソッドを変える
}
}
else if (obj.name == "GreenCollider")
{
text.text = "緑のコライダーに衝突した";
}
}
アイテムを近くで見ると中央にテキストが表示されます。
ここで左クリックすると、アイテムが非アクティブになって、アイテムを取るイベントを実行するメソッドが呼ばれます。なので、右上のテキストには取ったアイテムの名前が表示されます。
青→青→赤と3つのアイテムを取った後に赤いコライダーに入っても何も起こりません。
青い箱をもう1つ取ると表示が変わります。その後赤いコライダーに入ると、床が赤くなり、イベントの処理内容が変わります。
アイテム取得の2つ目のメソッドでは、青のときも赤のときもカウントアップし、合計で6個以上のときはイベントを空にします。
void PickItem2(ItemScript2 item)
{
text.text = item.GetItemName() + "を拾った";
itemCount++;
if (itemCount >= 6)
{
pickItem = null; // 空にする
}
}
衝突の2つ目のメソッドでは、アイテムのカウントが6以上のときに赤いコライダーに入ると、床の色を再度変えて、赤いコライダーを削除し、イベントを空にします。
void PlayerCollide2(GameObject obj)
{
if (obj.name == "RedCollider")
{
text.text = "赤いコライダーに衝突した";
if (itemCount >= 6)
{
planeMeshRenderer.material = materials[1]; // 床の色を黄色にする
Destroy(obj); // 赤いコライダーを破壊
playerCollide = null; // 空にする
}
}
else if (obj.name == "GreenCollider")
{
text.text = "緑のコライダーに衝突した";
}
}
その後は、緑のコライダーに入ったりアイテムを取ったりしても、表示が変わりません。
進行管理のUpdate()でデリゲートを呼んでみます。初めはアイテムや衝突のイベントは空で、5秒後に1つ目のメソッドを入れます。
Action updateAction;
float sec;
// ...
// Start is called before the first frame update
void Start()
{
updateAction = UpdateAction1;
}
// Update is called once per frame
void Update()
{
if(updateAction != null) updateAction();
}
void UpdateAction1()
{
sec += Time.deltaTime; // 時間を計る
if (sec >= 5f) // 5秒後
{
text.text = "スタート";
pickItem = PickItem1;
playerCollide = PlayerCollide1;
updateAction = null;
}
}
すると、開始5秒間はコライダーに入っても何も起こりません。