
イベントでゲームの進行管理をする #3ではアイテムを掴んで動かす処理をプレイヤーのスクリプトに書いていました。それをアイテムのスクリプトに移してみました。
まず、インターフェースでクリックされたときとクリックが離されたときのメソッドを宣言しました。この時に、引数にメソッドを呼んだオブジェクトを入れることにしました。
using UnityEngine;
public interface ITouchable
{
    void Touch(GameObject sender);
    void Release(GameObject sender);
}
掴めるアイテムのスクリプトでは、Update()で呼ぶデリゲート型の変数を宣言して、クリックされた時にそれに掴まれているときのメソッドを入れ、離されたときはnullにします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[RequireComponent(typeof(Rigidbody))]
public class Item : MonoBehaviour, ITouchable
{
    Rigidbody rb;
    Action<GameObject> operation;
    GameObject manipulator;
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody>();
    }
    // Update is called once per frame
    void Update()
    {
        if (operation != null) operation(manipulator);
    }
    // クリックされたとき
    public void Touch(GameObject sender)
    {
        manipulator = sender;
        operation = Hold;
    }
    // クリックが離されたとき
    public void Release(GameObject sender)
    {
        operation = null;
    }
    // 掴まれているときの処理
    public void Hold(GameObject sender)
    {
        rb.velocity = (sender.transform.forward * 2f + sender.transform.position - rb.transform.position) * 10f;
        // rb.velocity = (manipulator.transform.forward * 2f + manipulator.transform.position - rb.transform.position) * 10f;
    }
}
掴まれているときにプレイヤーのトランスフォームが必要なので、プレイヤーのスクリプトでは自分のゲームオブジェクトを引数に渡しています。
 // プレイヤーのスクリプト
[SerializeField] Text centerText;
Action action;
Collider coll;
void Update()
{
    if(action != null) action();
}
// 何も操作してないとき
void Raycast()
{
    RaycastHit hitInfo;
    // レイを飛ばす
    if (Physics.Raycast(transform.position, transform.forward, out hitInfo, 4f, ~(1 << 9)))
    {
        centerText.text = "[LMB]";
        if (Input.GetMouseButtonDown(0))
        {
            centerText.text = "";
            coll = hitInfo.collider;
            // スクリプトを取得
            var sc = coll.GetComponent<ITouchable>();
            if (sc != null)
            {
                sc.Touch(gameObject); // クリックしたときのメソッドを呼ぶ
                action = Operation; // 操作中にする
// --- 
// 操作中
void Operation()
{
    // クリックを離したとき
    if (Input.GetMouseButtonUp(0))
    {
        action = Raycast; // 操作してない状態にする
            
        if (coll != null)
        {
            // ...
            var sc = coll.GetComponent<ITouchable>();
            if (sc != null)
            {
                sc.Release(gameObject); // 放すメソッドを呼ぶ
            }
        }          
    }
}
これで、掴んで動かす操作をプレイヤーのスクリプトに書く必要がなくなりました。

しかし、プレイヤーがアイテムの種類に応じてイベントを呼んでいたり、削除したオブジェクトのクリックを離すメソッドを呼ぶとエラーになったりと問題が多いです。プレイヤーがアイテムやドアの型を知らなくても操作できるようにしたいです。



