团结光线投射,关于光线投射,如何? C#

时间:2014-11-16 13:42:26

标签: c# unity3d raycasting

我在理解如何保持对先前被光线投射命中的对象的引用时遇到问题。

例如,我可以在我的第一人称控制器的相机上放置一个光线投射脚本,从相机位置到转发矢量*某个值

此脚本已附加到相机

public class raycast : MonoBehaviour {
float lenthRay = 10.0f;
Vector3 originePos;
Vector3 dir;
RaycastHit hitinfo;
GameObject hitten;
bool isHitting;
Color beforC;
int selectionLayer = 9;

void Update () {
    originePos = Camera.main.transform.position;
    dir = Camera.main.transform.forward * lenthRay;
    Debug.DrawRay(originePos, dir, Color.blue);

    if (Physics.Raycast(originePos, dir, out hitinfo, lenthRay , selectionLayer)) {
        hitten = hitinfo.transform.gameObject;
        MeshRenderer tmp = hitten.transform.GetComponent<MeshRenderer> ();
        beforC = tmp.material.color;
        tmp.material.color = Color.black;
    } 
    //hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
    print(hitten.name);
}

}

它工作得很好,除非我尝试在if条件之外访问GameObject Hitten(如打印print(hitten.name)

我在从右侧层击中一个对象之前得到此错误:

NullReferenceException: Object reference not set to an instance of an object
raycast.Update () (at Assets/raycast.cs:30)

然后当我点击对象时就可以了

但问题是,我不明白当光线退出对象后,当它变为(beforC)后,我可以将对象颜色更改为原始颜色Color.black

这是我在注释行中尝试做的事情,但我得到的错误与打印相同,没有任何东西变黑。

我试过这个:

originePos = Camera.main.transform.position;
    dir = Camera.main.transform.forward * lenthRay;
    Debug.DrawRay(originePos, dir, Color.blue);
    isHitting = Physics.Raycast (originePos, dir, out hitinfo, lenthRay, selectionLayer);
    if (isHitting) {
        hitten = hitinfo.transform.gameObject;
        MeshRenderer tmp = hitten.transform.GetComponent<MeshRenderer> ();
        beforC = tmp.material.color;
        tmp.material.color = Color.black;

    } 
    if(!isHitting){
        hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
        print(hitten.name);
    }

但它不起作用

你可以帮我理解我应该使用的逻辑吗? 提前谢谢

3 个答案:

答案 0 :(得分:3)

当我试图检测墙壁何时阻碍玩家时,我有同样的需求。我之前从未使用过Raycast(或Linecast),并且很惊讶没有内置的方法来检测'Enter','Stay'和'Leave'事件。

所以我创建了这个简单的类来处理我的细节。我没有打扰创建任何类构造函数,只是将属性公开为public。它处理Raycast和Linecast。

使用属性设置后,此类会为您跟踪每个帧的对象。

以下是一些示例代码,用于演示潜在用途(我用于墙壁检测的代码):

private RayCaster wallRay;

void Start() {
    wallRay = new RayCaster();
    wallRay.OnRayEnter += WallRay_OnEnter;
    wallRay.OnRayExit += WallRay_OnExit;
    wallRay.LayerMask = RayCaster.GetLayerMask("Wall");
    wallRay.StartTransform = camera.transform;
    wallRay.EndTransform = PlayerManager.Player.transform;
}

void Update() {
    wallRay.CastLine();
}

void WallRay_OnEnter(Collider collider) {
    // Fade OUT wall section       [Needs DOTween (free) installed]
    collider.gameObject.renderer.material.DOFade(0.65f, 0.2f);
}

void WallRay_OnExit(Collider collider) {
    // Fade IN wall section
    collider.gameObject.renderer.material.DOFade(1f, 0.2f);
}

这是 RayCaster 类:

using System;
using System.Collections;
using UnityEngine;

public class RayCaster {

    public Transform StartTransform;
    public Transform EndTransform;
    public Vector3 Direction;
    public float RayLength;
    public int LayerMask = 0;

    public event Action<Collider> OnRayEnter;
    public event Action<Collider> OnRayStay;
    public event Action<Collider> OnRayExit;

    Collider previous;
    RaycastHit hit = new RaycastHit();

    public bool CastRay() {
        Physics.Raycast(StartTransform.position, Direction, out hit, RayLength, LayerMask);
        ProcessCollision(hit.collider);
        return hit.collider != null ? true : false;
    }

    public bool CastLine() {
        Physics.Linecast(StartTransform.position, EndTransform.position, out hit, LayerMask);
        ProcessCollision(hit.collider);
        return hit.collider != null ? true : false;
    }

    private void ProcessCollision(Collider current) {
        // No collision this frame.
        if (current == null) {
            // But there was an object hit last frame.
            if (previous != null) {
                DoEvent(OnRayExit, previous);
            }
        }

        // The object is the same as last frame.
        else if (previous == current) {
            DoEvent(OnRayStay, current);
        }

        // The object is different than last frame.
        else if (previous != null) {
            DoEvent(OnRayExit, previous);
            DoEvent(OnRayEnter, current);
        }

        // There was no object hit last frame.
        else {
            DoEvent(OnRayEnter, current);
        }

        // Remember this object for comparing with next frame.
        previous = current;
    }


    private void DoEvent(Action<Collider> action, Collider collider) {
        if (action != null) {
            action(collider);
        }
    }

    public static int GetLayerMask(string layerName, int existingMask=0) {
        int layer = LayerMask.NameToLayer(layerName);
        return existingMask | (1 << layer);
    }

}

答案 1 :(得分:0)

如果您的问题是如何访问光线投射命中的最后一个对象,那么我建议您创建一个可以存储它的全局变量。

您可以在光线投射时设置全局变量,而不是在方法中设置局部变量。这样你就可以随时访问该对象,直到找到一个新对象(因为这个对象现在存储在你的全局变量中)

编辑:如果您希望能够跟踪您曾经使用过光线投影的所有目标,我建议您创建一个存储每个项目的全局数组,并在您点击新目标时附加它们。

答案 2 :(得分:0)

所以我用鼠标做了它,它的工作原理 你必须先把你的对象放在右边的层上(这里是第9个)

您在鼠标中间单击某个对象以更改其颜色(此处为黑色),然后右键单击任意位置(在对象上或不在该对象上)以更改其原始颜色

任何时刻只有一个对象的颜色发生了变化,(让我们称这个状态为“已选中”) 当您通过鼠标中键单击“选择”下一个对象时,其中一个已被“选中”,它会将第一个更改为其原始颜色,因为它现在是“未选中”

public class raycast : MonoBehaviour {
float lenthRay = 10.0f;
Vector3 originePos;
Vector3 dir;
RaycastHit hitinfo;
GameObject hitten;
bool isHitting;
Color beforC;
int selectionLayer = 9;
bool alreadyHitten =false;

void Update () {
    originePos = Camera.main.transform.position;
    dir = Camera.main.transform.forward * lenthRay;
    Debug.DrawRay(originePos, dir, Color.blue);

    if (Input.GetMouseButtonDown (2)) {
        isHitting = Physics.Raycast (originePos, dir, out hitinfo, lenthRay, selectionLayer);
        if(isHitting) {
            if(hitinfo.transform.gameObject == null){
                hitten= null;
            }
            if(hitten != null && hitinfo.transform.gameObject == hitten){
                alreadyHitten = true;
            }
            if(hitten != null && hitinfo.transform.gameObject != hitten){
                alreadyHitten = false;
                hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
                hitten = hitinfo.transform.gameObject;
            }
            hitten = hitinfo.transform.gameObject;
            if(hitten !=  null && !alreadyHitten){
                print (hitten.name);
                MeshRenderer tmp = hitten.transform.GetComponent<MeshRenderer> ();
                beforC = tmp.material.color;
                tmp.material.color = Color.black;
            }
        }
    }
    if (Input.GetMouseButtonDown (1)) {
        if(hitten != null){
            alreadyHitten = false;
            hitten.transform.GetComponent<MeshRenderer> ().material.color = beforC;
            hitten = null;
        }
    }
}

}