13.UniTask监听碰撞和点击

13.碰撞和点击


13.1 知识点

UniTask 在 Unity 中的碰撞与点击事件处理

在 Unity 开发中,处理异步事件(如碰撞和鼠标点击)通常需要编写复杂的协程或回调函数。UniTask 提供了一种更简洁高效的方式来处理这些异步事件。本文将通过具体示例,介绍如何使用 UniTask 监听游戏对象的碰撞和点击事件,以及实例化预制体并对其进行异步操作。

/*
 * 此脚本演示了UniTask在Unity中的使用:
 * 1. 对场景中指定的测试物体(testBall)进行碰撞和鼠标点击事件监听。
 * 2. 通过预制体(redBallPrefab)实例化小球,在生成点(bornPoint)生成,并对其进行异步移动和碰撞事件监听。
 *
 * 注意:确保项目中已导入Cysharp.Threading.Tasks相关插件,并在需要的物体上挂载Collider组件以触发碰撞事件。
 */

using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Triggers;
using System.Threading;
using Cysharp.Threading.Tasks.Linq;
using UnityEngine;

public class Lesson13_碰撞和点击 : MonoBehaviour
{
    // 用于检测碰撞和点击事件的测试物体(需要在场景中挂载,并具备Collider组件)
    public GameObject testBall;

    // 用于实例化小球的预制体以及生成位置
    public GameObject redBallPrefab;
    public Transform bornPoint;

    //是否碰撞
    private bool _isCollision;

    // 移动速度(用于在Update中让testBall持续运动)
    [SerializeField] private float speed = 1f;

    // 脚本启动时同时开启所有测试
    void Start()
    {
        // 同时监听testBall的碰撞和鼠标点击事件
        TestCollision().Forget();
        TestClick().Forget();

        // 实例化小球并开始对其实例的异步移动与碰撞事件监听
        TestUniTaskInstantiationRedBall().Forget();
    }

    // 每帧调用,使testBall沿X轴持续移动
    void Update()
    {
        if (testBall != null && !_isCollision)
            testBall.transform.Translate(new Vector3(speed, 0, 0) * Time.deltaTime);
    }


    // 异步等待testBall发生碰撞事件,并输出碰撞对象的信息
    async UniTaskVoid TestCollision()
    {
        var collisionTrigger = testBall.GetAsyncCollisionEnterTrigger();
        // 捕获返回的碰撞信息
        Collision collision = await collisionTrigger.OnCollisionEnterAsync();
        // 输出碰撞对象的名称
        Debug.Log("testBall发生碰撞,碰撞对象是:" + collision.gameObject.name);
        _isCollision = true;
    }

    // 异步等待testBall接收到鼠标点击事件,并在点击后输出日志
    async UniTaskVoid TestClick()
    {
        var clickTrigger = testBall.GetAsyncMouseDownTrigger();
        await clickTrigger.OnMouseDownAsync();
        Debug.Log("testBall被点击!");
    }

    // 实例化预制体小球,并启动异步移动与碰撞监听
    private async UniTaskVoid TestUniTaskInstantiationRedBall()
    {
        // 在指定位置生成红色小球
        var redBall = Instantiate(redBallPrefab, bornPoint.position, Quaternion.identity);

        // 获取当小球被销毁时的取消标记
        var token = redBall.GetCancellationTokenOnDestroy();

        // 异步移动生成的小球
        Move(redBall.transform, token).Forget();

        // 使用异步流监听生成小球的碰撞事件,并在碰撞时输出碰撞物体的名称
        redBall.GetAsyncCollisionEnterTrigger()
            .ForEachAsync(
                collision =>
                {
                    Debug.Log("生成的红色小球碰撞到:" + collision.gameObject.name);
                    _isCollision = true;
                },
                cancellationToken: token
            )
            .Forget();
    }

    // 异步移动指定物体的Transform,沿X轴移动,持续5秒,碰撞了也不移动
    async UniTaskVoid Move(Transform tf, CancellationToken token)
    {
        float totalTime = 5f;
        float elapsedTime = 0f;
        while (elapsedTime < totalTime && !_isCollision)
        {
            elapsedTime += Time.deltaTime;
            tf.position -= new Vector3(speed, 0, 0) * Time.deltaTime;
            await UniTask.NextFrame(token);
        }
    }
}

监听游戏对象的碰撞事件

在 Unity 中,检测游戏对象的碰撞通常需要在 OnCollisionEnter 等方法中处理。使用 UniTask,可以更优雅地监听碰撞事件。

using Cysharp.Threading.Tasks;
using UnityEngine;

public class CollisionExample : MonoBehaviour
{
    public GameObject testBall;
    private bool isCollision = false;

    void Start()
    {
        TestCollision().Forget();
    }

    async UniTaskVoid TestCollision()
    {
        var collisionTrigger = testBall.GetAsyncCollisionEnterTrigger();
        Collision collision = await collisionTrigger.OnCollisionEnterAsync();
        Debug.Log($"[Collision] testBall 发生碰撞,碰撞对象是:{collision.gameObject.name}");
        isCollision = true;
    }
}

在上述示例中:

  • GetAsyncCollisionEnterTrigger() 用于获取 testBall 的碰撞触发器。
  • OnCollisionEnterAsync() 异步等待碰撞事件的发生,并返回碰撞信息。
  • testBall 发生碰撞时,输出碰撞对象的名称,并将 isCollision 标志设为 true

监听游戏对象的鼠标点击事件

除了碰撞事件,UniTask 也可以用于监听游戏对象的鼠标点击事件。

using Cysharp.Threading.Tasks;
using UnityEngine;

public class ClickExample : MonoBehaviour
{
    public GameObject testBall;

    void Start()
    {
        TestClick().Forget();
    }

    async UniTaskVoid TestClick()
    {
        var clickTrigger = testBall.GetAsyncMouseDownTrigger();
        await clickTrigger.OnMouseDownAsync();
        Debug.Log("[Click] testBall 被点击!");
    }
}

在上述示例中:

  • GetAsyncMouseDownTrigger() 获取 testBall 的鼠标点击触发器。
  • OnMouseDownAsync() 异步等待鼠标点击事件的发生。
  • testBall 被点击时,输出相应的日志信息。

实例化预制体并进行异步移动与碰撞监听

在游戏开发中,常需要实例化预制体(Prefab)并对其进行操作。UniTask 可以方便地实现对实例化对象的异步移动和事件监听。

using Cysharp.Threading.Tasks;
using UnityEngine;
using System.Threading;

public class InstantiateAndMoveExample : MonoBehaviour
{
    public GameObject redBallPrefab;
    public Transform bornPoint;
    private bool isCollision = false;
    [SerializeField] private float speed = 1f;

    void Start()
    {
        TestUniTaskInstantiationRedBall().Forget();
    }

    private async UniTaskVoid TestUniTaskInstantiationRedBall()
    {
        var redBall = Instantiate(redBallPrefab, bornPoint.position, Quaternion.identity);
        var token = redBall.GetCancellationTokenOnDestroy();

        Move(redBall.transform, token).Forget();

        redBall.GetAsyncCollisionEnterTrigger()
            .ForEachAsync(
                collision =>
                {
                    Debug.Log($"[Collision] 生成的红色小球碰撞到:{collision.gameObject.name}");
                    isCollision = true;
                },
                cancellationToken: token
            )
            .Forget();
    }

    async UniTaskVoid Move(Transform tf, CancellationToken token)
    {
        float totalTime = 5f;
        float elapsedTime = 0f;
        while (elapsedTime < totalTime && !isCollision)
        {
            elapsedTime += Time.deltaTime;
            tf.position -= new Vector3(speed, 0, 0) * Time.deltaTime;
            await UniTask.NextFrame(token);
        }
    }
}

在上述示例中:

  • bornPoint 位置实例化 redBallPrefab
  • 获取实例化对象的销毁取消标记 token,用于在对象销毁时取消异步操作。
  • Move 方法异步地沿 X 轴移动实例化的小球,持续 5 秒或直到发生碰撞。
  • 使用 GetAsyncCollisionEnterTrigger().ForEachAsync() 监听小球的碰撞事件,并在发生碰撞时输出日志信息。

总结

UniTask 提供了强大的异步事件处理能力,使得在 Unity 中监听碰撞、鼠标点击等事件变得更加简洁和高效。通过 UniTask,可以避免传统回调和协程带来的复杂性,提高代码的可读性和维护性。


13.2 知识点代码

Lesson13_碰撞和点击

/*
 * 此脚本演示了UniTask在Unity中的使用:
 * 1. 对场景中指定的测试物体(testBall)进行碰撞和鼠标点击事件监听。
 * 2. 通过预制体(redBallPrefab)实例化小球,在生成点(bornPoint)生成,并对其进行异步移动和碰撞事件监听。
 *
 * 注意:确保项目中已导入Cysharp.Threading.Tasks相关插件,并在需要的物体上挂载Collider组件以触发碰撞事件。
 */

using Cysharp.Threading.Tasks;
using Cysharp.Threading.Tasks.Triggers;
using System.Threading;
using Cysharp.Threading.Tasks.Linq;
using UnityEngine;

public class Lesson13_碰撞和点击 : MonoBehaviour
{
    // 用于检测碰撞和点击事件的测试物体(需要在场景中挂载,并具备Collider组件)
    public GameObject testBall;

    // 用于实例化小球的预制体以及生成位置
    public GameObject redBallPrefab;
    public Transform bornPoint;

    //是否碰撞
    private bool _isCollision;

    // 移动速度(用于在Update中让testBall持续运动)
    [SerializeField] private float speed = 1f;

    // 脚本启动时同时开启所有测试
    void Start()
    {
        // 同时监听testBall的碰撞和鼠标点击事件
        TestCollision().Forget();
        TestClick().Forget();

        // 实例化小球并开始对其实例的异步移动与碰撞事件监听
        TestUniTaskInstantiationRedBall().Forget();
    }

    // 每帧调用,使testBall沿X轴持续移动
    void Update()
    {
        if (testBall != null && !_isCollision)
            testBall.transform.Translate(new Vector3(speed, 0, 0) * Time.deltaTime);
    }


    // 异步等待testBall发生碰撞事件,并输出碰撞对象的信息
    async UniTaskVoid TestCollision()
    {
        var collisionTrigger = testBall.GetAsyncCollisionEnterTrigger();
        // 捕获返回的碰撞信息
        Collision collision = await collisionTrigger.OnCollisionEnterAsync();
        // 输出碰撞对象的名称
        Debug.Log("testBall发生碰撞,碰撞对象是:" + collision.gameObject.name);
        _isCollision = true;
    }

    // 异步等待testBall接收到鼠标点击事件,并在点击后输出日志
    async UniTaskVoid TestClick()
    {
        var clickTrigger = testBall.GetAsyncMouseDownTrigger();
        await clickTrigger.OnMouseDownAsync();
        Debug.Log("testBall被点击!");
    }

    // 实例化预制体小球,并启动异步移动与碰撞监听
    private async UniTaskVoid TestUniTaskInstantiationRedBall()
    {
        // 在指定位置生成红色小球
        var redBall = Instantiate(redBallPrefab, bornPoint.position, Quaternion.identity);

        // 获取当小球被销毁时的取消标记
        var token = redBall.GetCancellationTokenOnDestroy();

        // 异步移动生成的小球
        Move(redBall.transform, token).Forget();

        // 使用异步流监听生成小球的碰撞事件,并在碰撞时输出碰撞物体的名称
        redBall.GetAsyncCollisionEnterTrigger()
            .ForEachAsync(
                collision =>
                {
                    Debug.Log("生成的红色小球碰撞到:" + collision.gameObject.name);
                    _isCollision = true;
                },
                cancellationToken: token
            )
            .Forget();
    }

    // 异步移动指定物体的Transform,沿X轴移动,持续5秒,碰撞了也不移动
    async UniTaskVoid Move(Transform tf, CancellationToken token)
    {
        float totalTime = 5f;
        float elapsedTime = 0f;
        while (elapsedTime < totalTime && !_isCollision)
        {
            elapsedTime += Time.deltaTime;
            tf.position -= new Vector3(speed, 0, 0) * Time.deltaTime;
            await UniTask.NextFrame(token);
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏