绘制电线,分解出来就一下几个问题需解决:
最终实现的效果如下:
现在我们来一一解决。
关于贝塞尔曲线的原理,讲解的文章很多,大多是列出公式,然后就提供代码,其实讲得大同小异,公式怎么来的也没整明白。如果要了解贝塞尔曲线的原理,推荐这篇文章贝塞尔曲线,讲得很透彻。
以下是根据3点生成曲线的代码。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BezierUtils
{
///
/// 获取存储贝塞尔曲线点的数组
///
/// 起始点
/// 控制点
/// 目标点
/// 采样点的数量
/// 存储贝塞尔曲线点的数组
public static Vector3[] GetBeizerList(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint, int segmentNum)
{
Vector3[] path = new Vector3[segmentNum+1];
path[0] = startPoint;
for (int i = 1; i <= segmentNum; i++)
{
float t = i / (float)segmentNum;
Vector3 pixel = CalculateCubicBezierPoint(t, startPoint,
controlPoint, endPoint);
path[i] = pixel;
}
return path;
}
///
/// 根据T值,计算贝塞尔曲线上面相对应的点
///
/// T值
/// 起始点
/// 控制点
/// 目标点
/// 根据T值计算出来的贝赛尔曲线点
private static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
{
float u = 1 - t;
float tt = t * t;
float uu = u * u;
Vector3 p = uu * p0;
p += 2 * u * t * p1;
p += tt * p2;
return p;
}
}
首先要明确两点是确定不了一条曲线的!至少要3个点。你看贝塞尔曲线的脚本也有一个控制点,所以现在问题转换为如何通过两点找第三个控制点的问题。
第三个点如何找呢?最简单的就是找两点连成线的中垂线,但是空间中一条线段的中垂线有无数条啊,你总得告诉我中垂线指向哪儿或者说在哪个平面上才能确定啊。对了,在哪个平面上?我们已经知道两条线段可以确定一个平面,且目前我们知道一条线段的两个点了,那再指定一个方向不就可以得出平面了吗。
所以现在你要做的是人为指定一个方向。
所以,如何求出中垂线呢?
在此之前需要理解一点:两个向量的叉乘得到的向量(法线)是垂直于两个向量所在平面的,即也垂直于这两个向量
步骤如下:
///
/// 获取控制点.
///
/// 起点.
/// 终点.
/// 偏移量.
private Vector3 CalcControlPos(Vector3 startPos, Vector3 endPos, float offset)
{
//方向(由起始点指向终点)
Vector3 dir = endPos - startPos;
//取另外一个方向. 这里取向上.
Vector3 otherDir = Vector3.up;
//求平面法线. 注意otherDir与dir不能调换位置,平面的法线是有方向的,(调换位置会导致法线方向相反)
//ps: 左手坐标系使用左手定则 右手坐标系使用右手定则 (具体什么是左右手坐标系这里不细说请Google)
//unity中世界坐标使用的是左手坐标系,所以法线的方向应该用左手定则判断.
Vector3 planeNormal = Vector3.Cross(otherDir, dir);
//再求startPos与endPos的垂线. 其实就是再求一次叉乘.
Vector3 vertical = Vector3.Cross(dir, planeNormal).normalized;
//中点.
Vector3 centerPos = (startPos + endPos) / 2f;
//控制点.
Vector3 controlPos = centerPos + vertical * offset;
return controlPos;
}
可以在Scene窗口实时查看中垂线的方向,以便确认计算是否正确,代码如下
private void OnDrawGizmos()
{
if(endPos == null)
{
Awake();
}
//方向(由起始点指向终点)
Vector3 dir = endPos.position - startPos.position;
//取另外一个方向. 这里取向上.
Vector3 otherDir = Vector3.up;
//求平面法线. 注意otherDir与dir不能调换位置,平面的法线是有方向的,(调换位置会导致法线方向相反)
//ps: 左手坐标系使用左手定则 右手坐标系使用右手定则 (具体什么是左右手坐标系这里不细说请Google)
//unity中世界坐标使用的是左手坐标系,所以法线的方向应该用左手定则判断.
Vector3 planeNormal = Vector3.Cross(otherDir, dir);
//再求startPos与endPos的垂线. 其实就是再求一次叉乘.
Vector3 vertical = Vector3.Cross(dir, planeNormal).normalized;
//中点.
Vector3 centerPos = (startPos.position + endPos.position) / 2f;
//控制点.
Vector3 controlPos = centerPos + vertical * offset;
//线段.
Gizmos.color = Color.white;
Gizmos.DrawLine(startPos.position, endPos.position);
//平面法线.
Gizmos.color = Color.red;
Gizmos.DrawLine(centerPos, controlPos + planeNormal.normalized * 5);
//人为确定的方向.
Gizmos.color = Color.blue;
Gizmos.DrawLine(startPos.position, startPos.position + otherDir.normalized * 5);
//中垂线.
Gizmos.color = Color.green;
Gizmos.DrawLine(centerPos, centerPos + vertical.normalized * 5);
}
两点生成曲线已经搞定啦,现在就差把线绘制出来并能交互。
把线绘制出来其实很简单,Unity自带的LineRendere已经能满足我们的要求。但是如何才能让LineRenderer可交互呢?添加碰撞器嘛!
有了碰撞器你想怎么个交互都行嘛。
添加碰撞器代码很简单,这个思路最重要。代码里面添加的是胶囊体碰撞器,如下:
///
/// 给线添加碰撞体.
///
/// 点集合.
/// 碰撞器的父物体.
/// 半径.
private void AttackCollider(Vector3[] poses, Transform colls, float radius)
{
Vector3 lastPos = poses[0];
for (int i = 1; i < poses.Length; i++)
{
Vector3 nextPos = poses[i];
GameObject colliderObj = null;
if (i <= colliders.Count-1)
{
colliderObj = colliders[i - 1];
}
else
{
colliderObj = new GameObject();
colliders.Add(colliderObj);
}
colliderObj.name = (i - 1).ToString();
colliderObj.transform.parent = colls;
colliderObj.transform.forward = (nextPos - lastPos).normalized;
CapsuleCollider coll = colliderObj.GetComponent();
if(coll == null)
{
coll = colliderObj.AddComponent();
}
Vector3 center = (lastPos + nextPos) / 2f;
//设置胶囊体参数.
colliderObj.transform.position = center;
coll.center = Vector3.zero;
coll.radius = radius;
coll.height = Vector3.Distance(lastPos, nextPos);
coll.direction = 2; //0-X 1-Y 2-Z
coll.tag = "Wire";
lastPos = nextPos;
}
}
项目上传到GitHub了可下载查看。
下次再会!