简体   繁体   English

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

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

I am trying to find the vector or angle in order to calculate the points where two "thick" lines would intersect.我试图找到向量或角度,以计算两条“粗”线相交的点。 The end goal is to draw simple flat planes for a thick line renderer.最终目标是为粗线渲染器绘制简单的平面。

Although I'm using Unity and am working with Vector3, for my purposes, assume the Z-value is always 0. This is a 2d problem.尽管我正在使用 Unity 并且正在使用 Vector3,但出于我的目的,假设 Z 值始终为 0。这是一个二维问题。 It is not a Unity specific problem and I'm not sure why I'm having a hard time finding a solution that I can understand.不是Unity 特定的问题,我不确定为什么我很难找到我能理解的解决方案。 Getting points for drawing a "thick" line is surely not new.获得绘制“粗”线的分数肯定不是什么新鲜事。 But for the life of me, I can't find a solution.但是对于我的生活,我找不到解决方案。

The closest I've come is figuring out points along the line perpendicular to the one I want.我最接近的是找出垂直于我想要的线的点。 I know that this is the "bounce" line.我知道这是“反弹”线。 In other words, with 3 points, I can get the vector for the line that represents the side of a billiard ball table assuming the middle point is the point of impact with the side of the table.换句话说,假设中间点是台球桌侧面的撞击点,我可以通过 3 个点获得表示台球桌侧面的线的向量。 What I want is the vector perpendicular to that side.我想要的是垂直于那条边的向量。 I worked out the code below according to the post here: https://answers.unity.com/questions/1503945/how-to-get-the-cross-direction-for-3-points.html However, it's not working and I don't understand matrix math or Vector API's enough to figure out how to rotate my points, etc. My math, vector, and trigonometry skills are just not up to this challenge.我根据这里的帖子计算出下面的代码: https : //answers.unity.com/questions/1503945/how-to-get-the-cross-direction-for-3-points.html但是,它不起作用而且我对矩阵数学或矢量 API 的了解不足以弄清楚如何旋转我的点等。我的数学、矢量和三角学技能无法应对这一挑战。

I've included an image from in Unity showing what I can get.我在 Unity 中包含了一张图片,展示了我能得到的东西。 You can see the blue line which is so close to what I want.您可以看到与我想要的非常接近的蓝线。 I can get points C1 and C2 that are an arbitrary distance (thickness) out from the point of impact.我可以得到距离撞击点任意距离(厚度)的点 C1 和 C2。 They just need to be rotated 90 degrees to give me D1 and D2.它们只需要旋转 90 度即可得到 D1 和 D2。 Then I can get the points to use in the drawing api.然后我可以得到要在绘图api中使用的点。

The second image illustrates the actual information I'm trying to get.第二张图片说明了我试图获得的实际信息。 If I can just get the points, I can work on the custom mesh rendering myself.如果我能得到分数,我就可以自己处理自定义网格渲染。

The code below should be ready to display instantly once added to an empty GameObject in Unity in case that helps.一旦添加到 Unity 中的空游戏对象,下面的代码应该可以立即显示,以防万一。

Maybe I'm trying to do this all wrong and need to instead start with two parallel lines set "thickness" apart from the points comprising the line and calculate the intersect points of those?也许我试图把这一切都做错了,而是需要从两条平行线开始,设置“厚度”,与组成线的点分开并计算它们的交点? Any feedback, not to mention an actual solution, would be greatly appreciated.任何反馈,更不用说实际的解决方案,将不胜感激。

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


  }
}

我有C点,我想要D点 我正在努力实现的另一个形象

Not 100% sure of the intended logic here, but the part that seems screwy to me in your code is this bit:不是 100% 确定这里的预期逻辑,但在你的代码中对我来说似乎很棘手的部分是这样的:

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

Here, you're getting a normal of one line and the inverted normal of the other line, which you later add together.在这里,您将获得一条线的法线和另一条线的反转法线,然后将它们相加。 This leads to the "bounce wall" line you described because you're mirroring one normal across the intersection point.这会导致您描述的“弹跳墙”线,因为您在交叉点上镜像了一个法线。 Don't you mean:你不是说:

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

When you make the signs the same by fixing that issue, both normals are on one side of the intersection point.当您通过解决该问题使标志相同时,两条法线都位于交点的一侧。 Adding them together then leads to y-components that somewhat cancel each other out, and an x-component on the correct side of the intersection point.将它们加在一起会导致 y 分量在某种程度上相互抵消,并且 x 分量位于交点的正确一侧。 The drawn blue line then takes the angle you want it to (fixed normals in red):绘制的蓝线然后采用您想要的角度(红色固定法线):

相交的几何线。

Full modified version of your code I used to make this:我用来做这个的代码的完整修改版本:

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

(Sorry I don't quite have the right vocabulary to describe this issue, it's been a long time since trigonometry. Hopefully this makes sense.) (对不起,我没有完全正确的词汇来描述这个问题,距三角学已经很长时间了。希望这是有道理的。)

Hello I am not really sure if I reaaally understood what you were trying to accomplish but to me your problem looks like this you got three points P1,P2,P3 and you wish to find exactly the "normal angle" the yellow angle between the P12 and P23 lines.您好,我不确定我是否真的理解您要完成的任务,但对我而言,您的问题看起来像这样,您得到了三个点 P1、P2、P3,并且您希望准确找到“法线角度”,即 P12 之间的黄色角度和 P23 线。

One way to do this is to calculate the absolute angles this means the angle formed between the x axis and each line section.In the image bellow they are the orange and purple angles.一种方法是计算绝对角度,这意味着 x 轴和每个线段之间形成的角度。在下图中,它们是橙色和紫色的角度。 Then the substraction will tell you the angle formed between P12 and P23 this is the green angle.然后减法会告诉你 P12 和 P23 之间形成的角度,这是绿色角度。 Finally to obtain what i believe is what you call the "normal angle", which lies precisely at the middle of the green angle, you just need to subtract half of the green angle from the bigger angle in this case the purple one.最后,为了获得我认为的所谓的“法线角”,它恰好位于绿色角的中间,在这种情况下,您只需要从较大的角减去绿色角的一半,即紫色角。

在此处输入图片说明

I made a simple Console program to make the calculations here'sthe code我做了一个简单的控制台程序来进行计算here'sthe code

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

The result is the following结果如下

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

I have come up with a solution for the nonce that relies on using parallel lines and line intersection to find the corner points I need in order to draw a "fat" line.我为 nonce 提出了一个解决方案,该解决方案依赖于使用平行线和线交点来找到绘制“粗”线所需的角点。

I'm not fully convinced this is the right way to do it and am open to accepting a better answer (or editing this one).我并不完全相信这是正确的方法,并且愿意接受更好的答案(或编辑这个答案)。 I hope however that a working answer will help lead to a better one.然而,我希望一个有效的答案将有助于产生更好的答案。

The picture below shows my end result.下图显示了我的最终结果。 Yes, there are obvious problems if p1 == p3.是的,如果 p1 == p3 有明显的问题。 And acute angles will similarly cause issues.锐角同样会引起问题。 However, at the moment this works for my use-case.但是,目前这适用于我的用例。

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

这显示了上述代码在 Unity 中的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM