如何获得与 3 个点的弹跳角垂直的角度或向量?

How to get the angle or vector perpendicular to the bounce angle for 3 points?

我试图找到矢量或角度以计算两条“粗”线相交的点。最终目标是为粗线渲染器绘制简单的平面。

尽管我使用的是 Unity 并使用 Vector3,但出于我的目的,假设 Z 值始终为 0。这是一个二维问题。这是 不是 Unity 特定问题,我不确定为什么我很难找到我能理解的解决方案。画“粗”线得到分数肯定不是什么新鲜事。但是对于我的生活,我找不到解决方案。

我最接近的是沿着垂直于我想要的点的直线找出点。我知道这是“反弹”线。换句话说,有了 3 个点,我可以得到表示台球侧面 table 的线的向量,假设中间点是 table 侧面的撞击点。我想要的是垂直于那一侧的矢量。我根据这里的 post 计算出了下面的代码:https://answers.unity.com/questions/1503945/how-to-get-the-cross-direction-for-3-points.html 但是,它不起作用,我对矩阵数学或矢量 API 的了解不足以弄清楚如何旋转我的点等。我的数学、矢量和三角函数技能无法应对这一挑战。

我在 Unity 中添加了一张图片,展示了我可以获得的内容。您可以看到与我想要的非常接近的蓝线。我可以得到距离撞击点任意距离(厚度)的点 C1 和 C2。他们只需要旋转 90 度就可以得到 D1 和 D2。然后我就可以得到点数来画图了api.

第二张图片说明了我试图获得的实际信息。如果我能得到分数,我可以自己处理自定义网格渲染。

下面的代码应该准备好在添加到 Unity 中的空游戏对象后立即显示,以防有帮助。

也许我试图做这一切都是错误的,需要从两条平行线开始,设置“厚度”与组成线的点分开,并计算它们的交点?任何反馈,更不用说实际的解决方案,将不胜感激。

using UnityEngine;

public class ThickLineRenderer : MonoBehaviour {

  private void OnDrawGizmos() {

    Vector3[] points = new Vector3[] { new Vector3(-1, -1), new Vector3(0, 0), new Vector3(-1, 1) };

    Vector3 p1 = points[0];
    Vector3 p2 = points[1];
    Vector3 p3 = points[2];
  
    Gizmos.color = Color.white;
    Gizmos.DrawLine(p1, p2);
    Gizmos.DrawLine(p2, p3);

    Vector3 n1 = (p2 - p1).normalized;
    Vector3 n2 = (p3 - p2).normalized;
  
    Vector3 n = (n1 + n2).normalized;

    Vector3 d = p2 + n;
    Vector3 d2 = p2 - n;
  
    Gizmos.color = Color.blue;
    Gizmos.DrawLine(p2, d);
    Gizmos.DrawLine(p2, d2);


  }
}

不是 100% 确定这里的预期逻辑,但在你的代码中我觉得有点古怪的部分是:

Vector3 n1 = (p2 - p1).normalized;
Vector3 n2 = (p3 - p2).normalized;

在这里,您将获得一条线的单位向量和另一条线的倒置单位向量,稍后将它们相加。这导致您描述的“弹跳墙”线,因为您在交叉点上镜像一个单位矢量。你不是说:

Vector3 n1 = (p3 - p2).normalized;
Vector3 n2 = (p1 - p2).normalized;

当您通过解决该问题使符号相同时,两个矢量都位于交点的一侧。将它们相加会导致 y 分量相互抵消,并在交点的正确一侧产生 x 分量。然后绘制的蓝线采用您想要的角度(红色的固定单位向量):

你的代码的完整修改版本我用来做这个:

using UnityEngine;

public class ThickLineRenderer : MonoBehaviour {
    public Vector3 line1Start = new Vector3(-1, -1);
    public Vector3 intersect = new Vector3(0, 0);
    public Vector3 line2Start = new Vector3(-1, 1);

    private void OnDrawGizmos() {
        Vector3[] points = new Vector3[] { line1Start, intersect, line2Start };

        Vector3 p1 = points[0];
        Vector3 p2 = points[1];
        Vector3 p3 = points[2];

        Gizmos.color = Color.white;
        Gizmos.DrawLine(p1, p2);
        Gizmos.DrawLine(p2, p3);

        Vector3 n1 = (p3 - p2).normalized;
        Vector3 n2 = (p1 - p2).normalized;
        Gizmos.color = Color.red;
        Gizmos.DrawLine(p2, n1);
        Gizmos.DrawLine(p2, n2);

        Vector3 n = (n1 + n2).normalized;

        Vector3 d = p2 + n;
        Vector3 d2 = p2 - n;

        Gizmos.color = Color.blue;
        Gizmos.DrawLine(p2, d);
        Gizmos.DrawLine(p2, d2);
    }
}

(抱歉,我没有足够的词汇来描述这个问题,三角学已经很久了。希望这是有道理的。)

你好,我不太确定我是否真的理解你想要完成的事情,但对我来说你的问题看起来像这样你得到了三个点 P1、P2、P3 并且你希望准确地找到“正常角度” P12 和 P23 线之间的黄色角度。

一种方法是计算绝对角度,这意味着 x 轴和每条线之间形成的角度 section.In 下面的图像是橙色和紫色的角度。 然后减法会告诉你 P12 和 P23 之间形成的角度这是绿色角度。 最后要获得我认为是你所谓的“正常角度”,它恰好位于绿色角度的中间,你只需要从较大的角度减去绿色角度的一半,在这种情况下是紫色的。

我做了一个简单的控制台程序来计算这里的代码

using System;

namespace ConsoleApp1
{
    class Program
    {
        public static double GetAbsAngle(double x, double y)
        {
            double radsAbsAngle;
            //Get the absolute angle from respect to the x axis given a podouble x,y
            if (y > 0)
            {
                radsAbsAngle = Math.Atan2(y, x);
                return radsAbsAngle;
            }
            //Here Math.Atan2(y, x) will always result negative
            radsAbsAngle = 2*Math.PI+Math.Atan2(y, x);
            return radsAbsAngle;
        }
        public static double AngleBetweenPoints(double x1, double y1, double x2, double y2)
        {
            double absAngleP1 = Program.GetAbsAngle(x1, y1);
            Console.WriteLine("Abs angle P1 in degrees: {0}", Program.RadiansToDegrees(absAngleP1));
            double absAngleP2 = Program.GetAbsAngle(x2, y2);
            Console.WriteLine("Abs angle P2 in degrees: {0}", Program.RadiansToDegrees(absAngleP2));
            double angleBetween;
            angleBetween = (x1 > x2) ? absAngleP1 - absAngleP2 : absAngleP2 - absAngleP1;
            return angleBetween;
        }
        public static double RadiansToDegrees(double radians)
        {
            double degrees = (180 / Math.PI) * radians;
            return (degrees);
        }
        static void Main(string[] args)
        {
            //P1 with 
            double x1 = -4;
            double y1 = 4;
            //Assuming that P2 is always at 0,0
            //P3 with
            double x3 = -4;
            double y3 = -4;

            double angleBetween = Program.AngleBetweenPoints(x1, y1, x3, y3);
            Console.WriteLine("Angle between P1 and P3 in degrees: {0}",Program.RadiansToDegrees(angleBetween));
            double p1Angle = Program.GetAbsAngle(x1, y1);
            double p3Angle = Program.GetAbsAngle(x3, y3);
            double absNormalAngle = (p1Angle > p3Angle) ? p1Angle - (angleBetween/ 2) : p3Angle - (angleBetween / 2);
            Console.WriteLine("The normal abs angle between P1 and P3 in degrees: {0}", Program.RadiansToDegrees(absNormalAngle));
        }
        
    }
}

结果如下

Abs angle P1 in degrees: 135
Abs angle P2 in degrees: 225
Angle between P1 and P3 in degrees: 90
The normal abs angle between P1 and P3 in degrees: 180

我想出了一个 nonce 解决方案,该解决方案依赖于使用平行线和线交点来找到绘制“粗”线所需的角点。

我不完全相信这是正确的方法,并且愿意接受更好的答案(或编辑这个)。但是,我希望有效的答案将有助于得出更好的答案。

下图是我的最终结果。是的,如果 p1 == p3 有明显的问题。锐角同样会引起问题。但是,目前这适用于我的用例。

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

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
public class OutlineRenderer : MonoBehaviour {
  //Add 5 GameObjects as children to this one at:
  //0, 0, 0
  //12, 4.5, 0
  //10, -0.5, 0
  //15, -6, 0
  //2, -6, 0
  public GameObject[] points;

  private void OnDrawGizmos() {
    //Hold a list of corner vertices alternating by left-right as the line progresses.
    List<Vector2> corners = new List<Vector2>();

    //For now, thickness is an inverse-multiplier.
    //With a little extra math, it can be converted to a scalar unit.
    float thickness = 1.5f;

    //This logic is going to connect the line into a loop (which is my end use-case).
    //For a straight line, modify the logic for the starting and ending vertices.
    for (int i = 0; i < points.Length; i++) {
      //The prev point. If p2 is index 0, then p1 is the last point in the list.
      Vector3 p1 = i > 0 ? points[i - 1].transform.position : points[points.Length - 1].transform.position; 
      //The current point.
      Vector3 p2 = points[i].transform.position;
  
      float dist = Vector2.Distance(p1, p2);
  
      float dx = p2.x - p1.x;
      float dy = p2.y - p1.y;
  
      dx /= dist * thickness;
      dy /= dist * thickness;

      Vector3 a = new Vector3(-dy + p1.x, dx + p1.y);
      Vector3 b = new Vector3(dy + p1.x, -dx + p1.y);
      Vector3 a2 = a + new Vector3(dx, dy);
      Vector3 b2 = b + new Vector3(dx, dy);

      //----------------------------------------
      //The next point. If p2 is the last index, then p3 is the first point in the list.
      Vector3 p3 = i < points.Length - 1 ? points[i + 1].transform.position : points[0].transform.position;

      dist = Vector2.Distance(p3, p2);

      dx = p2.x - p3.x;
      dy = p2.y - p3.y;

      dx /= dist * thickness;
      dy /= dist * thickness;

      Vector3 c = new Vector3(dy + p3.x, -dx + p3.y);
      Vector3 d = new Vector3(-dy + p3.x, dx + p3.y);
      Vector3 c2 = c + new Vector3(dx, dy);
      Vector3 d2 = d + new Vector3(dx, dy);
  
      Vector2 i1 = findSegmentIntersection(a, a2, c, c2);
      Vector2 i2 = findSegmentIntersection(b, b2, d, d2);

      corners.Add(i1);
      corners.Add(i2);
    }

    //Corners are the actual vertices I'm going to need.
    //Draw logic (for Gizmos only).
    //Mesh rendering is completely separate.
    int n = corners.Count;
    for (int i = 0; i < n - 2; i += 2) {
      Vector3 p = points[i / 2].transform.position;
      Gizmos.color = Color.blue;
      Gizmos.DrawLine(p, corners[i]);
      Gizmos.DrawLine(p, corners[i + 1]);
      Gizmos.color = Color.red;
      Gizmos.DrawLine(corners[i], corners[i + 2]);
      Gizmos.DrawLine(corners[i + 1], corners[i + 3]);
    }
    Gizmos.color = Color.blue;
    Gizmos.DrawLine(points[points.Length - 1].transform.position, corners[n - 2]);
    Gizmos.DrawLine(points[points.Length - 1].transform.position, corners[n - 1]);
    Gizmos.color = Color.red;
    Gizmos.DrawLine(corners[n - 2], corners[0]);
    Gizmos.DrawLine(corners[n - 1], corners[1]);
  }

  //A utility method I converted from ActionScript.
  //There's probably something in the Unity library that can also do it.
  public Vector2 findSegmentIntersection(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4) {
    var x1 = p1.x;
    var x2 = p2.x;
    var x3 = p3.x;
    var x4 = p4.x;
    var y1 = p1.y;
    var y2 = p2.y;
    var y3 = p3.y;
    var y4 = p4.y;
    var z1 = x1 - x2;
    var z2 = x3 - x4;
    var z3 = y1 - y2;
    var z4 = y3 - y4;
    var d = z1 * z4 - z3 * z2;

    //If d is zero, there is no intersection.
    if (d == 0) {
      throw new Exception("Lines do not intersect!");
    }

    //Get the x and y.
    var pre = x1 * y2 - y1 * x2;
    var post = x3 * y4 - y3 * x4;
    var x = (pre * z2 - z1 * post) / d;
    var y = (pre * z4 - z3 * post) / d;

    //Return the point of intersection.
    return new Vector2(x, y);
  }
}