Files
2026-03-02 17:14:59 +07:00

228 lines
7.8 KiB
C#

using UnityEngine;
namespace BrewMonster
{
/// <summary>
/// Random walk movement with Bezier curves.
/// Mirrors C++ CGfxRandMove exactly (A3DSkillGfxEvent2.cpp:358-458).
/// 随机游走移动(带贝塞尔曲线),完全镜像C++ CGfxRandMove。
/// </summary>
public class CGfxRandMove : CGfxMoveBase
{
protected float m_fStep;
protected float m_fSpeed;
protected float m_fTimeSpan;
protected float m_fCurSpan;
protected Vector3[] m_CtrlPoints = new Vector3[6];
private const float _move_step_co = 1.0f / 2.0f; // step coefficient, same as C++
private const float _max_yaw = 90.0f * Mathf.Deg2Rad; // max yaw angle (radians), same as C++
public CGfxRandMove(GfxMoveMode mode) : base(mode) { }
/// <summary>
/// Calculate step size based on size parameters.
/// 根据尺寸参数计算步长。
/// </summary>
protected void CalcStep()
{
m_fStep = 0;
if (m_vSize.x > m_fStep) m_fStep = m_vSize.x;
if (m_vSize.y > m_fStep) m_fStep = m_vSize.y;
if (m_vSize.z > m_fStep) m_fStep = m_vSize.z;
m_fStep *= _move_step_co;
}
/// <summary>
/// Calculate vertical vector perpendicular to direction.
/// 计算垂直于方向的垂直向量。
/// </summary>
protected Vector3 CalcVertVec(Vector3 vDir)
{
Vector3 vUp = Vector3.up;
if (Mathf.Abs(Vector3.Dot(vDir, vUp)) > 0.9f)
{
vUp = Vector3.forward;
}
Vector3 vVert = Vector3.Cross(vDir, vUp);
Normalize(ref vVert);
return vVert;
}
/// <summary>
/// Check if position is within range bounds.
/// 检查位置是否在范围边界内。
/// </summary>
protected bool IsPosInRange(Vector3 vPos)
{
if (m_Shape == EmitShape.enumBox)
{
if (Mathf.Abs(vPos.x) > m_vSize.x) return false;
if (Mathf.Abs(vPos.y) > m_vSize.y) return false;
if (Mathf.Abs(vPos.z) > m_vSize.z) return false;
return true;
}
else if (m_Shape == EmitShape.enumSphere)
{
return vPos.sqrMagnitude <= m_fSquare;
}
else if (m_Shape == EmitShape.enumCylinder)
{
if (Mathf.Abs(vPos.y) > m_vSize.y) return false;
if (vPos.x * vPos.x + vPos.z * vPos.z > m_fSquareH) return false;
return true;
}
return true;
}
/// <summary>
/// Calculate next step position in random walk.
/// 计算随机游走中的下一步位置。
/// </summary>
protected Vector3 GetNextStep(Vector3 vPos, Vector3 vDir, Vector3 vCenter)
{
Vector3 vUp, vNewDir, vNewPos;
vUp = CalcVertVec(vDir);
// Random rotation around direction axis
// 围绕方向轴的随机旋转
float fRotAngle = Random.value * Mathf.PI * 2f;
Quaternion q = Quaternion.AngleAxis(fRotAngle * Mathf.Rad2Deg, vDir);
vUp = q * vUp;
// Random yaw rotation
// 随机偏航旋转
float fYawAngle = Random.value * _max_yaw;
Quaternion q2 = Quaternion.AngleAxis(fYawAngle * Mathf.Rad2Deg, vUp);
vNewDir = q2 * vDir;
vNewPos = vPos + vNewDir * m_fStep;
// If out of range, move toward center
// 如果超出范围,向中心移动
if (!IsPosInRange(vNewPos - vCenter))
{
vNewDir = (vCenter - vPos).normalized;
vNewPos = vPos + vNewDir * m_fStep;
}
return vNewPos;
}
/// <summary>
/// Bezier curve interpolation (4 control points).
/// 贝塞尔曲线插值(4个控制点)。
/// </summary>
protected Vector3 Bezier4(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
float u = 1.0f - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;
return uuu * p0 + 3 * uu * t * p1 + 3 * u * tt * p2 + ttt * p3;
}
/// <summary>
/// Initialize random walk movement.
/// 初始化随机游走移动。
/// </summary>
public override void StartMove(Vector3 vHost, Vector3 vTarget)
{
CalcStep();
m_fTimeSpan = m_fStep / m_fSpeed;
m_fCurSpan = 0;
if (m_bArea)
{
CalcRange((vTarget - vHost).normalized);
m_vPos = vTarget + GetRandOff();
}
else
{
m_vPos = vTarget;
}
// Random initial direction
// 随机初始方向
m_vMoveDir.x = Random.Range(-1f, 1f);
m_vMoveDir.y = Random.Range(-1f, 1f);
m_vMoveDir.z = Random.Range(-1f, 1f);
Normalize(ref m_vMoveDir);
// Initialize Bezier control points
// 初始化贝塞尔控制点
m_CtrlPoints[0] = m_vPos;
m_CtrlPoints[3] = GetNextStep(m_vPos, m_vMoveDir, vTarget);
Vector3 vDelta1 = m_CtrlPoints[3] - m_vPos;
m_CtrlPoints[5] = GetNextStep(m_CtrlPoints[3], vDelta1.normalized, vTarget);
Vector3 vDiff = (m_CtrlPoints[5] - m_vPos) / 6.0f;
m_CtrlPoints[2] = m_CtrlPoints[3] - vDiff;
m_CtrlPoints[4] = m_CtrlPoints[3] + vDiff;
m_CtrlPoints[1] = m_CtrlPoints[2] + vDelta1 / 3.0f;
}
/// <summary>
/// Tick random walk movement. Returns false (hit triggered by timeout).
/// 更新随机游走移动。返回false(命中由超时触发)。
/// </summary>
public override bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos)
{
m_fCurSpan += dwDeltaTime / 1000.0f;
if (m_fCurSpan >= m_fTimeSpan)
{
// Move to next control point and calculate new path
// 移动到下一个控制点并计算新路径
m_vPos = m_CtrlPoints[3];
m_fCurSpan = 0;
m_CtrlPoints[0] = m_vPos;
m_CtrlPoints[3] = m_CtrlPoints[5];
Vector3 vDelta1 = m_CtrlPoints[3] - m_vPos;
m_CtrlPoints[5] = GetNextStep(m_CtrlPoints[3], vDelta1.normalized, vTargetPos);
m_CtrlPoints[1] = m_CtrlPoints[4];
Vector3 vDiff = (m_CtrlPoints[5] - m_vPos) / 6.0f;
m_CtrlPoints[2] = m_CtrlPoints[3] - vDiff;
m_CtrlPoints[4] = m_CtrlPoints[3] + vDiff;
}
else
{
// Interpolate along Bezier curve
// 沿贝塞尔曲线插值
Vector3 vOldPos = m_vPos;
m_vPos = Bezier4(
m_CtrlPoints[0],
m_CtrlPoints[1],
m_CtrlPoints[2],
m_CtrlPoints[3],
m_fCurSpan / m_fTimeSpan
);
Vector3 vNewDir = m_vPos - vOldPos;
float fMag = Normalize(ref vNewDir);
if (fMag > 0.0001f)
{
m_vMoveDir = vNewDir;
}
}
return false;
}
/// <summary>
/// Override to also read speed from param value.
/// 重写以从参数值中读取速度。
/// </summary>
public override void SetParam(GFX_SKILL_PARAM param)
{
base.SetParam(param);
m_fSpeed = param.value.fVal; // C# union access: param.value.fVal
}
}
}