1. 项目概述
最近在开发一个需要动态连接两个物体的游戏功能时,遇到了一个有趣的需求:要在Unity中实现从A点到B点的虚线流动效果。经过一番探索,我发现结合LineRenderer和自定义Shader可以完美实现这个效果。今天就来分享一下我的实现过程,包括贝塞尔曲线的生成、虚线效果的创建以及流动动画的实现。
这个方案特别适合需要表现能量流动、连接线或者引导线的场景。相比直接使用粒子系统,LineRenderer方案性能更好,控制也更灵活。下面我会从零开始,详细讲解每个步骤的实现原理和注意事项。
2. 环境准备与基础设置
2.1 创建LineRenderer对象
首先在Unity场景中创建一个空GameObject,命名为"BezierLine"。然后为这个对象添加LineRenderer组件。这个组件是Unity提供的专门用于绘制线条的工具,支持各种参数调整。
提示:建议将LineRenderer对象的Transform重置为初始状态(Position归零,Rotation归零,Scale为1),避免后续计算出现意外偏移。
2.2 配置LineRenderer基础参数
在Inspector面板中,我们需要对LineRenderer进行一些基础配置:
- Positions:暂时留空,后续通过脚本动态设置
- Width:设置为0.1(可根据实际需求调整)
- Texture Mode:选择Tile模式(这对虚线效果至关重要)
- Materials:暂时留空,稍后会创建专用材质
3. 创建虚线Shader
3.1 Shader基础结构
我们需要创建一个自定义Shader来实现虚线效果。在项目文件夹中右键创建Shader > Standard Surface Shader,然后重命名为"DashedLineFlow"。
这个Shader的核心思路是通过UV坐标和时间的结合,实现虚线分段和流动效果。下面是完整的Shader代码:
shader复制Shader "Custom/DashedLineFlow"
{
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_DashLength ("Dash Length", Float) = 0.2
_GapLength ("Gap Length", Float) = 0.2
_Speed ("Flow Speed", Float) = 1.0
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
fixed4 _Color;
float _DashLength;
float _GapLength;
float _Speed;
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
// 关键点:UV坐标随时间偏移,实现流动效果
o.uv = v.uv + float2(_Time.y * _Speed, 0);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
float total = _DashLength + _GapLength;
// 使用frac确保UV在0-1之间循环
float phase = frac(i.uv.x / total);
float dist = phase * total;
// 判断当前片段是虚线部分还是间隔部分
if (dist < _DashLength)
return _Color;
else
return fixed4(0, 0, 0, 0);
}
ENDCG
}
}
}
3.2 Shader参数解析
这个Shader有几个关键参数可以调节:
_Color:虚线颜色_DashLength:单个虚线段的长度_GapLength:虚线间隔的长度_Speed:流动速度
注意:
_DashLength和_GapLength的值是相对于UV空间的,不是实际的世界单位。通常需要根据线条长度调整这两个参数的比例。
3.3 创建材质球
Shader创建完成后,我们需要创建一个材质球来使用它:
- 在项目文件夹中右键创建Material
- 命名为"DashedLineMaterial"
- 将Shader选择为我们刚创建的"Custom/DashedLineFlow"
- 可以调整颜色、虚线长度、间隔长度和流动速度等参数
4. 实现贝塞尔曲线
4.1 贝塞尔曲线原理
贝塞尔曲线是计算机图形学中常用的参数曲线,由控制点定义。我们这里使用的是二次贝塞尔曲线,需要三个点:起点、控制点和终点。
曲线的数学表达式为:
B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂,其中t∈[0,1]
4.2 创建AutoBezierCurve脚本
创建一个C#脚本"AutoBezierCurve.cs",代码如下:
csharp复制using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class AutoBezierCurve : MonoBehaviour
{
public Transform startPoint;
public Transform endPoint;
[Range(10, 100)]
public int segments = 50; // 曲线精度(点越多越平滑)
[Range(0.1f, 2f)]
public float curveHeight = 0.5f; // 曲线高度系数
private LineRenderer lineRenderer;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
lineRenderer.positionCount = segments + 1;
lineRenderer.useWorldSpace = true;
}
void Update()
{
if (startPoint == null || endPoint == null) return;
Vector3 controlPoint = CalculateControlPoint(startPoint.position, endPoint.position);
for (int i = 0; i <= segments; i++)
{
float t = (float)i / segments;
Vector3 point = CalculateQuadraticBezier(startPoint.position, controlPoint, endPoint.position, t);
lineRenderer.SetPosition(i, point);
}
}
Vector3 CalculateControlPoint(Vector3 start, Vector3 end)
{
Vector3 midPoint = (start + end) / 2;
Vector3 direction = (end - start).normalized;
Vector3 perpendicularDirection = Quaternion.Euler(0, 90, 0) * direction;
// 控制点高度与两点距离成正比,使曲线更自然
return midPoint + perpendicularDirection * Vector3.Distance(start, end) * curveHeight;
}
Vector3 CalculateQuadraticBezier(Vector3 p0, Vector3 p1, Vector3 p2, float t)
{
float u = 1 - t;
return u * u * p0 + 2 * u * t * p1 + t * t * p2;
}
}
4.3 脚本参数说明
startPoint和endPoint:曲线的起点和终点Transformsegments:曲线分段数,值越大曲线越平滑curveHeight:控制曲线弯曲程度
提示:控制点的计算方式可以根据需求调整。这里使用的是垂直于两点连线的方向,使曲线呈现自然的弧线效果。
5. 完整组装与效果调整
5.1 组装所有组件
- 将AutoBezierCurve脚本挂载到之前创建的BezierLine对象上
- 在场景中创建两个空对象作为起点和终点,分别命名为"PointA"和"PointB"
- 将这两个对象分别赋值给AutoBezierCurve脚本的startPoint和endPoint
- 将之前创建的DashedLineMaterial材质球赋值给LineRenderer的Materials槽
5.2 参数调优技巧
-
虚线效果调整:
- 调整材质球中的_DashLength和_GapLength参数
- 通常保持_DashLength略大于_GapLength视觉效果更好
- 如果虚线看起来不连贯,可以尝试减小这两个值
-
流动速度调整:
- 修改_Speed参数控制流动速度
- 正值表示从A流向B,负值则相反
-
曲线形状调整:
- 修改AutoBezierCurve脚本中的curveHeight参数
- 值越大曲线弯曲程度越大
-
线条宽度调整:
- 直接在LineRenderer组件中调整Width参数
- 也可以使用Width Curve实现线条粗细变化
6. 进阶优化与问题排查
6.1 性能优化建议
- 降低分段数:在保证视觉效果的前提下,尽量减少segments的值
- 减少Update调用:如果起点和终点不常移动,可以将计算逻辑移到Start或按需调用
- 使用对象池:如果需要大量曲线,考虑使用对象池管理LineRenderer
6.2 常见问题解决
问题1:虚线显示不连贯
- 检查Texture Mode是否设置为Tile
- 确保_DashLength和_GapLength比例合适
- 尝试调整LineRenderer的Texture Scale
问题2:流动方向相反
- 将_Speed参数改为负值
- 或者交换startPoint和endPoint的赋值
问题3:曲线形状不理想
- 调整curveHeight参数
- 修改CalculateControlPoint方法中的控制点计算逻辑
问题4:线条闪烁或断裂
- 确保所有脚本组件正确挂载
- 检查起点和终点Transform是否有效
- 验证LineRenderer的positionCount设置是否正确
6.3 扩展思路
- 动态虚线长度:可以根据曲线长度动态调整_DashLength和_GapLength
- 颜色渐变:修改Shader支持颜色随时间或位置变化
- 多段贝塞尔曲线:扩展脚本支持更复杂的曲线路径
- 碰撞检测:为曲线添加碰撞体,实现交互功能
7. 实际应用案例
这个技术可以应用于多种游戏场景:
- 技能连接线:表现角色之间的技能连接
- 引导路径:为玩家提供移动或攻击路径提示
- 能量流动:表现能量在物体间的传递
- 特殊效果:如魔法阵、力场线等装饰性元素
我在一个塔防游戏中使用了这个技术来表现防御塔与敌人之间的攻击连接线。通过调整曲线高度和流动速度,可以清晰表达攻击的强度和方向,大大提升了游戏的视觉反馈。