首页 > 解决方案 > 我怎样才能让光线投射命中只与玩家一起命中?

问题描述

我正在使用线条渲染器创建一个圆圈,并且在更新中我希望如果玩家触摸圆圈的绘制部分以检测到它击中它。问题是无论玩家是否在圆圈上,它总是能检测到地形。

此脚本附加到空的 GameObject。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[ExecuteAlways]
[RequireComponent(typeof(UnityEngine.LineRenderer))]
public class DrawCircle : MonoBehaviour
{
    public GameObject centerObject;
    [Range(1, 50)] public int segments = 50;
    [Range(1, 500)] public float xRadius = 5;
    [Range(1, 500)] public float yRadius = 5;
    [Range(0.1f, 5)] public float width = 0.1f;
    [Range(0, 100)] public float height = 0;
    public bool controlBothXradiusYradius = false;
    public bool draw = true;

    private Vector3 startPos;
    private Vector3 endPos;
    private bool detected = false;

    [SerializeField] private LineRenderer line;

    private void Start()
    {
        if (!line) line = GetComponent<LineRenderer>();

        

        if (draw)
            CreatePoints();

        startPos = line.GetPosition(0);
        endPos = line.GetPosition(line.positionCount - 1);

    }

    private void Update()
    {
        RaycastHit hit;
        if (Physics.Raycast(startPos, endPos - startPos, out hit))
        {
            if(hit.transform.gameObject.name == "Player")
            {
                print("player detected");
                detected = true;
            }
            else if(detected)
            {
                print("player NOT detected");
                detected = false;
            }
        }
    }

    public void CreatePoints()
    {
        line.enabled = true;
        line.widthMultiplier = width;
        line.useWorldSpace = false;
        line.widthMultiplier = width;
        line.positionCount = segments + 1;

        float x;
        float y;

        var angle = 20f;
        var points = new Vector3[segments + 1];
        for (int i = 0; i < segments + 1; i++)
        {
            x = Mathf.Sin(Mathf.Deg2Rad * angle) * xRadius;
            y = Mathf.Cos(Mathf.Deg2Rad * angle) * yRadius;

            points[i] = new Vector3(x, height, y);

            angle += (380f / segments);
        }

        // it's way more efficient to do this in one go!
        line.SetPositions(points);
    }

#if UNITY_EDITOR
    private float prevXRadius, prevYRadius;
    private int prevSegments;
    private float prevWidth;
    private float prevHeight;

    private void OnValidate()
    {
        // Can't set up our line if the user hasn't connected it yet.
        if (!line) line = GetComponent<LineRenderer>();
        if (!line) return;

        if (!draw)
        {
            // instead simply disable the component
            line.enabled = false;
        }
        else
        {
            // Otherwise re-enable the component
            // This will simply re-use the previously created points
            line.enabled = true;

            if (xRadius != prevXRadius || yRadius != prevYRadius || segments != prevSegments || width != prevWidth || height != prevHeight)
            {
                CreatePoints();

                // Cache our most recently used values.
                prevXRadius = xRadius;
                prevYRadius = yRadius;
                prevSegments = segments;
                prevWidth = width;
                prevHeight = height;
            }

            if (controlBothXradiusYradius)
            {
                yRadius = xRadius;
            }
        }
    }
#endif
}

播放器有一个刚体 3d 组件、胶囊碰撞器、第三人称用户控件和第三人称角色。我正在使用键 WSAD 来移动播放器。

我尝试使用 Physics.CheckSphere 但如果玩家在半径区域内,它也会显示“检测到玩家”,并且我希望它仅在圆的绘制线上检测玩家。

此屏幕截图显示玩家触摸圆圈线:这就是我的意思是触摸圆圈的绘制线:

玩家触摸圆圈

这是当玩家在圆圈内移动并且它也写玩家检测到但它应该写未检测到这就是我想要的,只有当他触摸线检测玩家时。

只有在接触圆圈线时,才不应检测到玩家在圆圈区域内。

这就是我在脚本中所做的更改:我添加了一个 targetLayers 变量,然后在 Update 中使用 Physics.CheckSphere。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[ExecuteAlways]
[RequireComponent(typeof(UnityEngine.LineRenderer))]
public class DrawCircle : MonoBehaviour
{
    public GameObject centerObject;
    [Range(1, 50)] public int segments = 50;
    [Range(1, 500)] public float xRadius = 5;
    [Range(1, 500)] public float yRadius = 5;
    [Range(0.1f, 5)] public float width = 0.1f;
    [Range(0, 100)] public float height = 0;
    public bool controlBothXradiusYradius = false;
    public bool draw = true;

    [SerializeField] private LayerMask targetLayers;

    private Vector3 startPos;
    private Vector3 endPos;

    [SerializeField] private LineRenderer line;

    private void Start()
    {
        if (!line) line = GetComponent<LineRenderer>();

        if (draw)
            CreatePoints();

        startPos = line.GetPosition(0);
        endPos = line.GetPosition(line.positionCount - 1);

    }

    private void Update()
    {
        if (Physics.CheckSphere(transform.position, xRadius, targetLayers))
        {
            Debug.Log("player detected");
        }
        else
        {
            Debug.Log("player NOT detected");
        }
    }

    public void CreatePoints()
    {
        line.enabled = true;
        line.widthMultiplier = width;
        line.useWorldSpace = false;
        line.widthMultiplier = width;
        line.positionCount = segments + 1;

        float x;
        float y;

        var angle = 20f;
        var points = new Vector3[segments + 1];
        for (int i = 0; i < segments + 1; i++)
        {
            x = Mathf.Sin(Mathf.Deg2Rad * angle) * xRadius;
            y = Mathf.Cos(Mathf.Deg2Rad * angle) * yRadius;

            points[i] = new Vector3(x, height, y);

            angle += (380f / segments);
        }

        // it's way more efficient to do this in one go!
        line.SetPositions(points);
    }

#if UNITY_EDITOR
    private float prevXRadius, prevYRadius;
    private int prevSegments;
    private float prevWidth;
    private float prevHeight;

    private void OnValidate()
    {
        // Can't set up our line if the user hasn't connected it yet.
        if (!line) line = GetComponent<LineRenderer>();
        if (!line) return;

        if (!draw)
        {
            // instead simply disable the component
            line.enabled = false;
        }
        else
        {
            // Otherwise re-enable the component
            // This will simply re-use the previously created points
            line.enabled = true;

            if (xRadius != prevXRadius || yRadius != prevYRadius || segments != prevSegments || width != prevWidth || height != prevHeight)
            {
                CreatePoints();

                // Cache our most recently used values.
                prevXRadius = xRadius;
                prevYRadius = yRadius;
                prevSegments = segments;
                prevWidth = width;
                prevHeight = height;
            }

            if (controlBothXradiusYradius)
            {
                yRadius = xRadius;
            }
        }
    }
#endif
}

标签: c#unity3d

解决方案


您的光线投射只能命中确定的。例如:

int layerMask1 = 1 << LayerMask.NameToLayer("EnemiesLayer");
int layerMask2 = 1 << LayerMask.NameToLayer("GroundLayer");
int layerMask3 = 1 << LayerMask.NameToLayer("PlayerPlayer");
int myLayerMask= layerMask1 | layerMask1 | layerMask3;

然后使用此光线投射重载,将图层作为参数,以便您可以检索包含任何相关图层的第一个命中。

public static bool Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance, int layerMask);

您只需要使用这种方法为您的播放器设置一个图层并为光线投射定义相应的图层。


推荐阅读