using UnityEngine; namespace BrewMonster { /// /// Random walk movement with Bezier curves. /// Mirrors C++ CGfxRandMove exactly (A3DSkillGfxEvent2.cpp:358-458). /// 随机游走移动(带贝塞尔曲线),完全镜像C++ CGfxRandMove。 /// 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) { } /// /// Calculate step size based on size parameters. /// 根据尺寸参数计算步长。 /// 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; } /// /// Calculate vertical vector perpendicular to direction. /// 计算垂直于方向的垂直向量。 /// 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; } /// /// Check if position is within range bounds. /// 检查位置是否在范围边界内。 /// 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; } /// /// Calculate next step position in random walk. /// 计算随机游走中的下一步位置。 /// 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; } /// /// Bezier curve interpolation (4 control points). /// 贝塞尔曲线插值(4个控制点)。 /// 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; } /// /// Initialize random walk movement. /// 初始化随机游走移动。 /// 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; } /// /// Tick random walk movement. Returns false (hit triggered by timeout). /// 更新随机游走移动。返回false(命中由超时触发)。 /// 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; } /// /// Override to also read speed from param value. /// 重写以从参数值中读取速度。 /// public override void SetParam(GFX_SKILL_PARAM param) { base.SetParam(param); m_fSpeed = param.value.fVal; // C# union access: param.value.fVal } } }