当前代码接收一个矩形并确定其角点的
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);
}
}
概述
目标是在 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];
}
}