如何获得与 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);
}
}
我试图找到矢量或角度以计算两条“粗”线相交的点。最终目标是为粗线渲染器绘制简单的平面。
尽管我使用的是 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);
}
}