Unity Raycast, на Raycast оставить, как? C #

1

У меня проблема с пониманием того, как сохранить ссылку на объект, который ранее был поражен raycast.

например, у меня может быть сценарий raycast, установленный на камеру моего 1-го контролера человека, идущего от положения камеры к вектору forwad * некоторое значение

этот сценарий прикреплен к камере

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);
}

}

он отлично работает, за исключением случаев, когда я пытаюсь получить доступ к GameObject Hitten вне моего условия if (например, print 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);
    }

но он не работает ни

можете ли вы помочь мне понять логику, которую я должен использовать заранее

  • 0
    Я полагаю, что проблема в том, что ваш HitO GameObject не инициализирован, чтобы проверить, если это так, инициализируйте его к некоторому значению по умолчанию и посмотрите, что произойдет.
  • 0
    @MSB да, он не был инициализирован, поэтому я добавил void Start () { hitten = null; } но я не вижу никаких изменений
Показать ещё 1 комментарий
Теги:
unity3d
raycasting

3 ответа

3

У меня была такая же потребность, когда я пытался обнаружить, когда стена препятствует игроку. Раньше я никогда раньше не использовал Raycast (или Linecast), и был удивлен, что не было встроенного метода для обнаружения событий "Enter", "Stay" и "Leave".

Поэтому я создал этот простой класс для обработки деталей для меня. Я не стал создавать конструкторы классов и просто публиковал свойства. Он обрабатывает как Raycasts, так и 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);
    }

}
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;
        }
    }
}

}

0

Если ваш вопрос заключается в том, как получить доступ к последнему удару вашего raycast, тогда я предлагаю создать глобальную переменную, в которой вы можете ее сохранить.

Вместо того, чтобы устанавливать локальную переменную в свой метод, вы можете установить глобальную переменную, когда вы выполняете raycast. Таким образом, вы всегда можете получить доступ к объекту до тех пор, пока не нажмете новый (потому что он теперь хранится в вашей глобальной переменной)

EDIT: в случае, если вы хотите отслеживать все цели, которые у вас когда-либо были raycast, я предлагаю создать глобальный массив, в котором вы храните каждый элемент, добавляя их при попадании в новую цель.

  • 0
    Дело в том, что мне нужно что-то эквивалентное onMouseExit при обработке, так что я могу вернуться (здесь я прототип с переменной цвета) обратно в предыдущее состояние. Здесь у нас есть только что-то, эквивалентное onMouseOver, и когда я что-то делаю, я не могу повернуть его обратно в кадре, когда лучевая передача покидает объект. я пытаюсь сделать это со списком, но пока нет положительного результата
  • 0
    Можете ли вы попытаться уточнить, что вы хотите тогда? Если вы хотите изменить цвет объекта, который вы ударили с помощью raycast, а затем немного позже изменить его, то вполне возможно, что с помощью предписанного метода.
Показать ещё 10 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню