我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛
物理小游戏的功能需求:
在实时的绘制完成LineRenderer之后,会动态的添加PolygonCollider2D多边形碰撞体。读取LineRenderer的点位(一条折线),左右各偏移一定距离,形成一个闭合的线框,利用线框的点位修改PolygonCollider2D形状。
工作流程:
- 通过Input监听鼠标事件
- 按下鼠标,开始绘制LineRenderer图形
- 鼠标移动过程中,实时刷新图形
- 抬起鼠标,结束图形绘制,添加碰撞盒
说明:touchPosList是一条手指移动轨迹点位的连线,只是一条折线,可以用于绘制LineRenderer,并给可以用于绘制LineRenderer设置显示宽度;但不能直接用于生成PolygonCollider,需要将点位连线转换成一条有宽度的面。这里将一个点位沿法线方向与法线反方向各偏移一定距离,生成2个新的点位,按顺序连接新点位,形成一个闭合的轮廓。
绿色是捕捉到的点位,紫色是偏移后的点位:
LineRenderer.SetPositions(Vector3[] positions):
- 设置LineRenderer的图形绘制路径。 LineRenderer.SetWidth(float start,float end):
- 设置LineRenderer的起始、结束宽度。
PolygonCollider.SetPath(int index, Vector2[] points):
- index:多边形可能有孔和不连续部分,因此其形状不一定由单个路径定义。例如,多边形可能实际上有3个单独的路径。这种情况下将调用SetPath(0)、SetPath(1)、SetPath(2)。
- points:定义多边形轮廓的各点之间的线段的循环序列,points中的点位可以围成一个闭合且不交叉的折线。
完整测试代码,关键代码的作用都写在注释里:
using System.Collections.Generic;
using UnityEngine;
public class LineCollider : MonoBehaviour
{
//绘制过程中显示用LineRenderer
[SerializeField] LineRenderer drawingLine;
//带有碰撞体的LineRenderer, 要有PolygonCollider2D、LineRenderer组件,
[SerializeField] GameObject colliderLinePrefab;
//灵敏度
[SerializeField] float minTouchDistance = 0.1f;
//相机设置为正交模式,才能转换为世界坐标tmpTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition)
Camera mainCamera;
void Start()
{
mainCamera = Camera.main;
//
drawingLine.enabled = false;
drawingLine.positionCount = 0;
}
Vector3 tmpTouchPos;
bool tmpIsMouseDown;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
tmpIsMouseDown = true;
tmpTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
tmpTouchPos.z = 0;
OnPressScreen(tmpTouchPos);
}
else if (Input.GetMouseButton(0) && tmpIsMouseDown)
{
tmpTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
tmpTouchPos.z = 0;
OnTouchScreen(tmpTouchPos);
}
else if (Input.GetMouseButtonUp(0) && tmpIsMouseDown)
{
tmpTouchPos = mainCamera.ScreenToWorldPoint(Input.mousePosition);
tmpTouchPos.z = 0;
OnReleaseScreen(tmpTouchPos);
tmpIsMouseDown = false;
}
}
//鼠标点击
Vector3 lastPos;
List<Vector3> touchPosList = new List<Vector3>();
public void OnPressScreen(Vector3 pressPos)
{
touchPosList.Clear();
lastPos = pressPos;
drawingLine.enabled = true;
drawingLine.positionCount = 0;
}
//鼠标拖拽
Vector3 tmpStartPoint;
Vector3 tmpEndPoint;
public void OnTouchScreen(Vector3 touchPos)
{
tmpStartPoint = lastPos;
tmpEndPoint = touchPos;
//显示绘制line
if (Vector3.Distance(tmpStartPoint, tmpEndPoint) > minTouchDistance)
{
touchPosList.Add(lastPos);
drawingLine.positionCount = touchPosList.Count;
drawingLine.SetPosition(touchPosList.Count - 1, touchPosList[touchPosList.Count - 1]);
lastPos = touchPos;
}
}
//鼠标抬起
public void OnReleaseScreen(Vector3 releasePos)
{
drawingLine.positionCount = 0;
drawingLine.enabled = false;
//绘制结束,生成碰撞line
touchPosList.Add(releasePos);
if (touchPosList.Count > 3)
CreateColliderLine(touchPosList);
}
//生成碰撞line
void CreateColliderLine(List<Vector3> pointList)
{
GameObject prefab = Instantiate(colliderLinePrefab, transform);
LineRenderer lineRenderer = prefab.GetComponent<LineRenderer>();
PolygonCollider2D polygonCollider = prefab.GetComponent<PolygonCollider2D>();
lineRenderer.positionCount = pointList.Count;
lineRenderer.SetPositions(pointList.ToArray());
List<Vector2> colliderPath = GetColliderPath(pointList);
polygonCollider.SetPath(0, colliderPath.ToArray());
}
//计算碰撞体轮廓
float colliderWidth;
List<Vector2> pointList2 = new List<Vector2>();
List<Vector2> GetColliderPath(List<Vector3> pointList3)
{
//碰撞体宽度
colliderWidth = drawingLine.startWidth;
//Vector3转Vector2
pointList2.Clear();
for (int i = 0; i < pointList3.Count; i++)
{
pointList2.Add(pointList3[i]);
}
//碰撞体轮廓点位
List<Vector2> edgePointList = new List<Vector2>();
//以LineRenderer的点位为中心, 沿法线方向与法线反方向各偏移一定距离, 形成一个闭合且不交叉的折线
for (int j = 1; j < pointList2.Count; j++)
{
//当前点指向前一点的向量
Vector2 distanceVector = pointList2[j - 1] - pointList2[j];
//法线向量
Vector3 crossVector = Vector3.Cross(distanceVector, Vector3.forward);
//标准化, 单位向量
Vector2 offectVector = crossVector.normalized;
//沿法线方向与法线反方向各偏移一定距离
Vector2 up = pointList2[j - 1] + 0.5f * colliderWidth * offectVector;
Vector2 down = pointList2[j - 1] - 0.5f * colliderWidth * offectVector;
//分别加到List的首位和末尾, 保证List中的点位可以围成一个闭合且不交叉的折线
edgePointList.Insert(0, down);
edgePointList.Add(up);
//加入最后一点
if (j == pointList2.Count - 1)
{
up = pointList2[j] + 0.5f * colliderWidth * offectVector;
down = pointList2[j] - 0.5f * colliderWidth * offectVector;
edgePointList.Insert(0, down);
edgePointList.Add(up);
}
}
//返回点位
return edgePointList;
}
}