131 lines
5.6 KiB
C#
131 lines
5.6 KiB
C#
using Cielonos.MainGame.Characters;
|
||
using Cielonos.MainGame.Inventory;
|
||
using SLSUtilities.General;
|
||
using UnityEngine;
|
||
|
||
namespace Cielonos.MainGame
|
||
{
|
||
public partial class NormalArea : AttackAreaBase
|
||
{
|
||
public override T Initialize<T>(CharacterBase creator, ItemBase itemSource, params Fraction[] targetFractions)
|
||
{
|
||
T area = base.Initialize<T>(creator, itemSource, targetFractions);
|
||
topParent.transform.localScale *= creator.attributeSm[CharacterAttribute.AttackRangeMultiplier];
|
||
return area;
|
||
}
|
||
|
||
private void OnTriggerStay(Collider other)
|
||
{
|
||
HitCharacter(other, default);
|
||
}
|
||
|
||
public override void HitCharacter(Collider characterCollider, Vector3 hitPosition)
|
||
{
|
||
// 既不在伤害阶段也不在反应窗口,直接跳过
|
||
if (!isEnabling && !isReactionActive)
|
||
{
|
||
return;
|
||
}
|
||
|
||
CharacterBase targetCharacter = characterCollider.GetComponentInParent<CharacterBase>();
|
||
|
||
if (!IsValidTarget(targetCharacter)) return;
|
||
|
||
// 已在 grace window 期间成功反应的目标不再处理
|
||
if (reactedTargets.Contains(targetCharacter.gameObject))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (hitSm.checkedObjects.Contains(targetCharacter.gameObject))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 仅在反应窗口内(enable 阶段之前或之后),只做反应检测
|
||
if (!isEnabling && isReactionActive)
|
||
{
|
||
HitOnTarget(characterCollider, areaCollider.ClosestPoint(targetCharacter.CenterPoint.position),
|
||
out Attack.Result graceResult, onlyCheckReaction: true);
|
||
if (graceResult.isBlocked || graceResult.isDodged)
|
||
{
|
||
reactedTargets.Add(targetCharacter.gameObject);
|
||
hitSm.AddCheckedObject(targetCharacter.gameObject);
|
||
Debug.Log($"[NormalArea] Target {targetCharacter.name} successfully reacted during grace window. Blocked: {graceResult.isBlocked}, Dodged: {graceResult.isDodged}");
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 正常 enable 阶段:造成伤害
|
||
hitSm.AddCheckedObject(targetCharacter.gameObject);
|
||
HitOnTarget(characterCollider, areaCollider.ClosestPoint(targetCharacter.CenterPoint.position), out _);
|
||
}
|
||
}
|
||
|
||
public partial class NormalArea
|
||
{
|
||
protected override GameObject GenerateHitEffect(CharacterBase target, Collider hitCollider, Vector3 hitPosition)
|
||
{
|
||
GameObject hitEffect = base.GenerateHitEffect(target, hitCollider, hitPosition);
|
||
if(hitEffect == null) return null;
|
||
Vector3 hitEffectDirection = GetHitEffectDirection(hitCollider.ClosestPoint(transform.position));
|
||
hitEffect.transform.rotation = Quaternion.LookRotation(hitEffectDirection);
|
||
return hitEffect;
|
||
}
|
||
}
|
||
|
||
public partial class NormalArea
|
||
{
|
||
/// <summary>
|
||
/// 计算命中特效应朝向的方向 (Z轴)。
|
||
/// 这个方向是刀光在命中点的旋转切线方向。
|
||
/// </summary>
|
||
/// <param name="hitPosition">世界空间中的命中点</param>
|
||
/// <returns>一个标准化的方向向量 (world space)</returns>
|
||
private Vector3 GetHitEffectDirection(Vector3 hitPosition)
|
||
{
|
||
// 1. 获取刀光旋转的中心点
|
||
// (假设 creator.flexibleCenterPoint.position 是旋转中心)
|
||
Vector3 rotationCenter = creator.CenterPoint.position;
|
||
|
||
// 2. 获取刀光旋转的法线 (旋转轴)
|
||
// 根据描述, 这是刀光 prefab 的 Y 轴正方向
|
||
Vector3 rotationNormal = transform.up; // 刀光的 local Y 轴 (in world space)
|
||
|
||
// 3. 计算从中心点到命中点的半径向量
|
||
Vector3 radiusVector = hitPosition - rotationCenter;
|
||
|
||
// 4. (重要) 将半径向量投影到旋转平面上.
|
||
// 这是为了确保我们得到一个与旋转轴垂直的向量,
|
||
// 防止 hitPosition 轻微偏离平面导致计算错误.
|
||
// 你的代码中 hitDirection.y = 0 是在错误的平面上投影.
|
||
Vector3 radiusOnPlane = Vector3.ProjectOnPlane(radiusVector, rotationNormal);
|
||
|
||
// 5. 计算切线.
|
||
// 需求: "Y轴正方向顺时针旋转"
|
||
// 在 Unity (左手坐标系) 中:
|
||
// - 轴 (Normal) = Y (0, 1, 0)
|
||
// - 半径 (Radius) = X (1, 0, 0)
|
||
// - 顺时针旋转 (Clockwise) 是从 +X 转向 -Z.
|
||
//
|
||
// - Vector3.Cross(rotationNormal, radiusOnPlane)
|
||
// = Cross((0,1,0), (1,0,0)) = (0, 0, -1) (-> 指向 -Z)
|
||
// - 这正是我们需要的顺时针切线方向.
|
||
Vector3 tangentDirection = Vector3.Cross(rotationNormal, radiusOnPlane);
|
||
|
||
// 6. (安全检查) 处理命中点在中心的情况
|
||
// 如果命中点非常接近旋转中心, 半径为0, 叉乘结果也为0.
|
||
// .normalized 会返回 NaN, 导致 LookRotation 失败.
|
||
if (tangentDirection.sqrMagnitude < 0.0001f)
|
||
{
|
||
// 此时没有明确的 "切线".
|
||
// 我们可以返回刀光的 "forward" (Z轴) 或 "right" (X轴) 作为备用.
|
||
// 让我们使用刀光的 X 轴 (即刀光"刀刃"的初始方向).
|
||
return transform.right;
|
||
}
|
||
|
||
// 7. 返回标准化的切线方向
|
||
return tangentDirection.normalized;
|
||
}
|
||
}
|
||
} |