贝塞尔曲线
关于贝塞尔曲线的介绍可以去看看其他更偏理论的博客,这里默认知道这是啥。一般需要三个点就够了:起点,终点,控制点。下面三种方法主要是针对终点的计算有所不同。(前两种之前写的...懒得找了,第三种应该是最复杂的,能理解的话前面的问题就不大)
贝塞尔曲线之直线变曲线
直接使用 transform.position
作为起点,发射 transform.forword
方向上的射线,判断目标是否可传送,可以的话,把控制点吊高(比如计算起点,终点的中间点,再把 y 增加),这样看起来就是一个传送曲线。
缺点:just 看起来,在 VR 项目中,手柄稍微抬高一点点,与地面没有交点的话,就不能传送了。
优点:非常省事
贝塞尔曲线之固定目的地
如果可传送点就几个地方的话,可以考虑这种方式,效果如下:
传送
通过控制朝向,在两个传送点之间切换。思路是先把所有的传送点位置存下来,然后在选择传送点的时候,计算当前朝向与所有传送点的点积(也就是夹角),选点积最大的(也就是夹角最小的)。
丑丑的图示
缺点:需要固定目的地
优点:可以固定目的地
贝塞尔曲线之计算目的地
原理就是设置一个最大的传送范围,然后物体抬得最高的时候,也就是图中 180° 的时候,算出一个当前朝向的最远的一个地方,再用从天而降的射线判断目的地是否可以传送,这样便计算出了贝塞尔曲线的目标点,控制点简单点就像这样,跟起点相切,长度为起点和终点一半的距离。
缺点:要定好最大传送的尺度,也就是说,如果你的地图只有半径 10 米,却设置了 100 的最大距离,那么只有 1/10 的范围是可用的。
优点:相对于固定目的地传送,这种可以在范围内随意传送,自由度高。
丑丑的图示
效果图
有高度
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
public float MaxTranlateDistance;
public float MaxTranlateHight;
private LineRenderer _lineRenderer;
private void Start()
{
_lineRenderer = GetComponent<LineRenderer>();
_lineRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
_lineRenderer.startWidth = 0.04f;
_lineRenderer.endWidth = 0.04f;
_lineRenderer.numCapVertices = 5;
_lineRenderer.numCornerVertices = 5;
_lineRenderer.positionCount = 30;
}
private void Update()
{
DrawLine();
}
private void DrawLine()
{
Vector3 startPoint = transform.position;
Vector3 horizontalForward = Vector3.Cross(transform.right, Vector3.up);
if (Vector3.Dot(horizontalForward, transform.forward) < 0)
{
horizontalForward = -horizontalForward;
}
Debug.DrawLine(transform.position, transform.position + Vector3.up, color: Color.green);
Debug.DrawLine(transform.position, transform.position + transform.right, color: Color.red);
Debug.DrawLine(transform.position, transform.position + horizontalForward, color: Color.blue);
float horizontal_t = Vector3.Dot(transform.forward, horizontalForward);
float t = horizontal_t / 2;
if (transform.forward.y > 0) // 当抬得比较高的时候,要把 t 的范围从 0.5-0 映射到 0.5-1
{
t = -t + 1;
}
Vector3 endPoint = transform.position + horizontalForward * t * MaxTranlateDistance;
// 注意这里我写了碰撞遮罩的检测,所以在场景中要设置好层级
if (Physics.Raycast(new Ray(endPoint + Vector3.up * MaxTranlateHight, Vector3.down),
out RaycastHit hit, 100, 1 << LayerMask.NameToLayer(ConstName.layer_translate_target), QueryTriggerInteraction.Collide))
{
endPoint = hit.point;
}
else
{
_lineRenderer.enabled = false;
return;
}
float d = (endPoint - startPoint).magnitude / 2;
Vector3 controlPoint = transform.position + transform.forward * d;
Vector3[] bcList = GetBeizerPathPointList(startPoint, controlPoint, endPoint, 30);
_lineRenderer.positionCount = bcList.Length + 1;
_lineRenderer.SetPosition(0, startPoint);
for (int i = 0; i < bcList.Length; i++)
{
Vector3 v = bcList[i];
_lineRenderer.SetPosition(i + 1, v);
}
_lineRenderer.enabled = true;
}
public static Vector3[] GetBeizerPathPointList(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint, int pointNum)
{
Vector3[] BeizerPathPointList = new Vector3[pointNum];
for (int i = 1; i <= pointNum; i++)
{
float t = i / (float)pointNum;
Vector3 point = GetBeizerPathPoint(t, startPoint,
controlPoint, endPoint);
BeizerPathPointList[i - 1] = point;
}
return BeizerPathPointList;
}
private static Vector3 GetBeizerPathPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
{
return (1 - t) * (1 - t) * p0 + 2 * t * (1 - t) * p1 + t * t * p2;
}
}
抛物线
手撸一个抛物线,之前写的抛物线找不到了...
缺点:落点不好判断
优点:比较真实
球形差值
TODO
网友评论