问题与解决思路
问题

当使用unity的射线组件时,发现unity只提供了射线与物体碰撞的时的响应办法。这就尴尬了,因为我的需求是获取射线在某物体停留、或是射线离开某物体的信息。本以为被碰撞物体的collider会解决这个问题,结果发现虽然使用raycast需要物体必须有collider,但并不会触发collider相关的事件。没办法,只能自己解决了。

解决思路

解决思路其实很简单,编写一个脚本,定义射线与物体碰撞、停留、离开三个事件,通过不断更新射线的扫描目标信息并与上一帧已保存的信息进行对比,来推算行为并触发相应事件。

实现过程
1. 创建组件:在资源窗口中创建脚本,起名为mRayCasterInput,并填入下列代码
using System;
using System.Collections;
using UnityEngine;

public class mRayCasterInput{
	// 定义三个事件,用来定义不同行为的不同相应
    public event Action<Collider> OnRayEnter;
    public event Action<Collider> OnRayStay;
    public event Action<Collider> OnRayExit;
	// 用来保存上一个射线扫到的物体
    Collider previousCollider;
    public void CastMouseRay(){
        // 从摄像机向鼠标位置发射一束射线,并返回相关信息
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hitInfo;
        Physics.Raycast(ray, out hitInfo);
        // 处理返回的信息
        CollisionProcess(hitInfo.collider);    
    }

    private void CollisionProcess(Collider current) {
        // 执行射线离开物体触发的事件
        // 如果在当前帧中,射线没有碰撞到任何物体,并且在记录中保存到碰撞体不为空,就触发该物体的结束射线碰撞事件
        if (current == null) {
            if (previousCollider != null) {
                OnRayExit?.Invoke(previousCollider);
            }
        }

        // 射线停留在某个物体时触发的事件
        // 如果当前射线碰撞的物体和上一个射线碰撞到物体相同,则触发射线停留事件
        else if (previousCollider == current) {
            OnRayStay?.Invoke(current);
        }

        // 射线更新事件
        // 如果当前射线和上一个射线碰撞的物体不是同一个,则上一个物体触发射线离开事件,当前物体触发射线碰撞事件
        else if (previousCollider != null) {
            OnRayEnter?.Invoke(current);
            OnRayExit?.Invoke(previousCollider);
        }
        // 如果之前帧中没有射线碰撞的物体,当前帧有射线碰撞到物体,则触发此事件。
        else {
            // no collider on last frame
            OnRayEnter?.Invoke(current);
        }
		
        // 将当前射线碰撞的物体保存为 上一个碰撞的物体
        previousCollider = current;
    }
}
2. 在脚本中调用新组件

在脚本中到start()周期中 注册相关相应事件,在update() 周期中更新射线信息,即可正常运作了。

private void Update(){
    // 更新射线信息
    rayCasterinput.CastMouseRay();
}

private void Start() {
    // 处理射线与物体碰撞时触发的方法
    rayCasterinput.OnRayEnter += (Collider c)=>{
        Debug.Log("enter name:"+c.gameObject.name);
    };
	// 处理射线与已碰撞物体结束碰撞时触发的方法
    rayCasterinput.OnRayExit += (Collider c)=>{
        Debug.Log("exit name:"+c.gameObject.name);
    };
}