获取从给定点开始且与某个给定对象相切的向量

问题描述 投票:0回答:1

当前代码接收一个矩形并确定其角点的

Vector2
位置。然后,它将使用
Sin
Cos
计算角点相对于矩形角度的新位置。目前它仅适用于矩形。

我的问题是如何更新代码以使其适用于任何类型的形状/对象。我不需要代码,只要解释一下算法就可以了。

foreach (var hit in hit_objects)
{
    Collider2D hitCollider = hit.collider;
    Bounds bounds = hitCollider.bounds;

    Vector2[] corners = new Vector2[4];
    corners[0] = new Vector2(bounds.min.x, bounds.min.y); 
    corners[1] = new Vector2(bounds.max.x, bounds.min.y); 
    corners[2] = new Vector2(bounds.max.x, bounds.max.y); 
    corners[3] = new Vector2(bounds.min.x, bounds.max.y);

    Vector2 center = bounds.center;
    float angle = hit.collider.gameObject.transform.rotation.eulerAngles.z * Mathf.Deg2Rad;

    for (int i = 0; i < corners.Length; i++)
    {
        Vector2 direction = corners[i] - center;
        float rotatedX = direction.x * Mathf.Cos(angle) - direction.y * Mathf.Sin(angle);
        float rotatedY = direction.x * Mathf.Sin(angle) + direction.y * Mathf.Cos(angle);
        corners[i] = new Vector2(rotatedX, rotatedY) + center;

        Debug.Log(corners[i] + "   " + i);

        Vector2 directionToCorner = (corners[i] - (Vector2)transform.position);

        Debug.DrawRay(transform.position, directionToCorner, Color.green);
    }
}
c# unity-game-engine math vector
1个回答
0
投票

概述

目标是在 Unity 2D 环境中找到从起始对象(我们称之为“A”)到障碍物(目标对象“B”)的切线。这是通过使用二分法逐渐缩小切线方向来实现的。这种方法可以通过测试和调整方向来帮助近似切线,直到它们与目标对象的边缘对齐。

代码说明

在每一帧中,脚本都会检测是否有障碍物。 调用

get_tangents
来启动检测到的每个障碍物的切线计算。 它计算连接“A”和“B”最近点的直线的垂直矢量。该垂直矢量有助于定义切线方向近似的起点。 然后该函数执行
bisection
,旨在每次迭代时更接近切线。 主要有两个点,位置 1(对象“B”内部)和位置 2(对象“B”外部)。该函数使用这两个位置的中点来测试经过“A”到该中点的向量是否与目标对象相交,如果相交,则该中点变为位置1,否则变为位置2

经过一番迭代,找到的切线相当准确。

using System.Collections;
using System.Collections.Generic;
using Unity.Burst.CompilerServices;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UIElements;

public class tangents : MonoBehaviour
{
    public LayerMask obstacleLayerMaskPF;
    public int max_number_of_iterations = 100;

    private Transform Player;
    private List<RaycastHit2D> hit_objects = new List<RaycastHit2D>();

    // Start is called before the first frame update
    void Start()
    {
        Player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>();
    }

    // Update is called once per frame
    void Update()
    {
        Vector2 rayStart = transform.position;
        Vector2 player_direction = (Player.position - transform.position);
        Debug.DrawRay(rayStart, player_direction, Color.red);

        // Perform a raycast and get all the hits
        RaycastHit2D[] hits = Physics2D.RaycastAll(rayStart, player_direction, player_direction.magnitude, obstacleLayerMaskPF);

        hit_objects.Clear();
        hit_objects.AddRange(hits);

        get_tangents(hit_objects, this.transform.position, 0, player_direction);
    }

    void get_tangents(List<RaycastHit2D> hit_objects, Vector2 starting_position, int iteration, Vector2 player_direction)
    {
        int i = 0;
        int j = 0;
        int k = 0;
        Vector2 center;
        Vector2 perpendicular_vector;
        Vector2[] start_middle_end = new Vector2[3];
        Vector2 closest_point_to_target;
        Vector2 vector_to_target;
        Vector2 perpendicular_vector_to_target;
        foreach (var hit in hit_objects)
        {
            closest_point_to_target = hit.collider.ClosestPoint(transform.position);
            vector_to_target = closest_point_to_target - (Vector2)transform.position;
            perpendicular_vector_to_target = new Vector2(vector_to_target.y, -vector_to_target.x);

            if (i == iteration)
            {
                center = hit.collider.transform.position;
                for (k = 0; k < 2; k++)
                {

                    perpendicular_vector = new Vector2(player_direction.y, -player_direction.x) * (k - 1 + (k * 2));
                    start_middle_end[0] = center;
                    start_middle_end[2] = (Vector2)transform.position + perpendicular_vector_to_target.normalized * hit.collider.bounds.extents.magnitude * 2 * (k - 1 + (k * 2));
                    
                    for (j = 0; j < max_number_of_iterations; j++) 
                    {
                        bisection(hit.collider.gameObject, perpendicular_vector, center, start_middle_end);
                    }
                    Debug.DrawRay(transform.position, (start_middle_end[2] - (Vector2)transform.position) * 5, Color.red);
                }
            }
            i++;
        }
    }

    void bisection(GameObject obstacle, Vector2 perpendicular_vector, Vector2 center, Vector2[] start_middle_end)
    {
        bool hit_the_obstacle = false;
        start_middle_end[1] = (start_middle_end[0] + start_middle_end[2]) / 2;
        RaycastHit2D[] hits = Physics2D.RaycastAll(transform.position, start_middle_end[1] - (Vector2)transform.position, (start_middle_end[1] - (Vector2)transform.position).magnitude * 2, obstacleLayerMaskPF);
        foreach (var hit in hits)
        {
            if (hit.collider != null)
            {
                if (hit.collider.gameObject == obstacle)
                {
                    hit_the_obstacle = true;
                }
            }
        }
        if (hit_the_obstacle) 
        {
            start_middle_end[0] = start_middle_end[1];
        }
        else start_middle_end[2] = start_middle_end[1];
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.