根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` chore(config): 更新配置文件设置 - 调整项目配置参数 - 优化构建流程设置 - 更新依赖包版本信息 ``` 注意:由于未提供具体的代码差异信息,此为示例模板。实际使用时请根据具体变更内容调整type、scope、subject和body部分。
This commit is contained in:
1 parent
dcb87d5393
commit
fb353c45a7
37 files changed
+1651
-2461
No files matched your search
@@ -0,0 +1,140 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class StatusEffectController : MonoBehaviour
|
||||
{
|
||||
[Header("通用眩晕显示")]
|
||||
[SerializeField] private bool showStunIndicator = true;
|
||||
[SerializeField] private float indicatorHeight = 0.35f;
|
||||
[SerializeField] private float indicatorSize = 0.5f;
|
||||
|
||||
private Animator animator;
|
||||
private float stunnedRemaining;
|
||||
private float animatorSpeedBeforeStun = 1f;
|
||||
private GameObject stunIndicator;
|
||||
private Transform indicatorTransform;
|
||||
|
||||
public bool IsStunned => stunnedRemaining > 0f;
|
||||
public float StunnedRemaining => Mathf.Max(0f, stunnedRemaining);
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
animator = GetComponent<Animator>();
|
||||
}
|
||||
|
||||
public void ApplyStun(float duration)
|
||||
{
|
||||
if (duration <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsStunned && animator != null)
|
||||
{
|
||||
animatorSpeedBeforeStun = animator.speed;
|
||||
}
|
||||
|
||||
stunnedRemaining = Mathf.Max(stunnedRemaining, duration);
|
||||
if (animator != null)
|
||||
{
|
||||
animator.speed = 0f;
|
||||
}
|
||||
|
||||
if (showStunIndicator)
|
||||
{
|
||||
EnsureIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsStunned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stunnedRemaining -= Time.deltaTime;
|
||||
if (animator != null)
|
||||
{
|
||||
animator.speed = 0f;
|
||||
}
|
||||
|
||||
if (stunnedRemaining <= 0f)
|
||||
{
|
||||
stunnedRemaining = 0f;
|
||||
if (animator != null)
|
||||
{
|
||||
animator.speed = Mathf.Max(0.01f, animatorSpeedBeforeStun);
|
||||
}
|
||||
DestroyIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (indicatorTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Bounds bounds = new Bounds(transform.position, Vector3.zero);
|
||||
Renderer[] renderers = GetComponentsInChildren<Renderer>(true);
|
||||
if (renderers.Length > 0)
|
||||
{
|
||||
bounds = renderers[0].bounds;
|
||||
for (int i = 1; i < renderers.Length; i++)
|
||||
{
|
||||
bounds.Encapsulate(renderers[i].bounds);
|
||||
}
|
||||
}
|
||||
|
||||
indicatorTransform.position = new Vector3(
|
||||
bounds.center.x,
|
||||
bounds.max.y + indicatorHeight,
|
||||
bounds.center.z);
|
||||
|
||||
Camera camera = Camera.main;
|
||||
if (camera != null)
|
||||
{
|
||||
indicatorTransform.rotation = Quaternion.LookRotation(
|
||||
camera.transform.position - indicatorTransform.position,
|
||||
camera.transform.up)
|
||||
* Quaternion.Euler(0f, 0f, Time.unscaledTime * 180f);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureIndicator()
|
||||
{
|
||||
if (stunIndicator != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
stunIndicator = new GameObject("StunIndicator_Star");
|
||||
indicatorTransform = stunIndicator.transform;
|
||||
|
||||
TextMesh text = stunIndicator.AddComponent<TextMesh>();
|
||||
text.text = "★";
|
||||
text.anchor = TextAnchor.MiddleCenter;
|
||||
text.alignment = TextAlignment.Center;
|
||||
text.fontSize = 64;
|
||||
text.characterSize = indicatorSize;
|
||||
text.fontStyle = FontStyle.Bold;
|
||||
text.color = new Color(1f, 0.85f, 0.05f, 1f);
|
||||
}
|
||||
|
||||
private void DestroyIndicator()
|
||||
{
|
||||
if (stunIndicator != null)
|
||||
{
|
||||
Destroy(stunIndicator);
|
||||
}
|
||||
|
||||
stunIndicator = null;
|
||||
indicatorTransform = null;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyIndicator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: B3gatX/8UXLhzK2o+FhBOnvCE/cCS+iZRCUxmwg/CJ8qjZpm3ifTg08=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -9,6 +9,9 @@ public class StoneGolemSkill : UnitSkill
|
||||
[Min(0.1f)]
|
||||
public float skillRadius = 2f;
|
||||
|
||||
[Min(0f)]
|
||||
public float stunDuration = 1f;
|
||||
|
||||
[Header("技能动画")]
|
||||
[Min(0f)]
|
||||
public float skillAnimationDuration = 0f;
|
||||
@@ -26,7 +29,7 @@ public class StoneGolemSkill : UnitSkill
|
||||
unit.PlaySkillAnimation(OnSkillFinished, skillAnimationDuration);
|
||||
}
|
||||
|
||||
// 添加到地裂技能动画的命中帧;眩晕效果暂不处理。
|
||||
// 添加到地裂技能动画的命中帧;在命中帧结算伤害和眩晕。
|
||||
public void OnSkillHit1()
|
||||
{
|
||||
if (!isSkillReady)
|
||||
@@ -35,6 +38,7 @@ public class StoneGolemSkill : UnitSkill
|
||||
}
|
||||
|
||||
unit.ApplyAreaSkillDamage(skillRadius, skillDamage, false);
|
||||
unit.ApplyAreaStun(skillRadius, stunDuration);
|
||||
isSkillReady = false;
|
||||
}
|
||||
|
||||
|
||||
+109
-11
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(StatusEffectController))]
|
||||
public class UnitBase : MonoBehaviour
|
||||
{
|
||||
|
||||
@@ -19,6 +20,10 @@ public class UnitBase : MonoBehaviour
|
||||
//攻击范围
|
||||
public float attackRange = 5f;
|
||||
|
||||
//模型自身前方相对逻辑前方的水平修正角度。
|
||||
[Header("模型朝向修正")]
|
||||
public float modelFacingOffset = 0f;
|
||||
|
||||
//基础攻击力,实际伤害结算逻辑后续接入
|
||||
[Min(0f)]
|
||||
public float attackPower = 10f;
|
||||
@@ -79,11 +84,14 @@ public class UnitBase : MonoBehaviour
|
||||
// 自身的血条组件,同时负责显示技能能量条。
|
||||
private WorldHealthBar worldHealthBar;
|
||||
|
||||
// 通用状态效果组件。
|
||||
private StatusEffectController statusEffectController;
|
||||
|
||||
//垂直速度
|
||||
private float verticalVelocity;
|
||||
|
||||
//当前攻击开始时间,用于在没有结束 Event 时释放攻击状态。
|
||||
private float attackStartTime;
|
||||
// 攻击动画的有效经过时间;眩晕期间不增加。
|
||||
private float attackElapsedTime;
|
||||
|
||||
private bool isPlayingSkill;
|
||||
private float attackSpeedMultiplier = 1f;
|
||||
@@ -119,6 +127,7 @@ public class UnitBase : MonoBehaviour
|
||||
animator = GetComponent<Animator>();
|
||||
controller = GetComponent<CharacterController>();
|
||||
worldHealthBar = GetComponent<WorldHealthBar>();
|
||||
statusEffectController = EnsureStatusEffectController();
|
||||
unitSkill = GetComponent<UnitSkill>();
|
||||
|
||||
// 位移和朝向由 CharacterController 与 UnitBase 控制,避免攻击动画的根运动叠加旋转。
|
||||
@@ -128,15 +137,37 @@ public class UnitBase : MonoBehaviour
|
||||
randomTargetPos();
|
||||
}
|
||||
|
||||
private StatusEffectController EnsureStatusEffectController()
|
||||
{
|
||||
if (statusEffectController == null)
|
||||
{
|
||||
statusEffectController = GetComponent<StatusEffectController>();
|
||||
}
|
||||
|
||||
if (statusEffectController == null)
|
||||
{
|
||||
statusEffectController = gameObject.AddComponent<StatusEffectController>();
|
||||
}
|
||||
|
||||
return statusEffectController;
|
||||
}
|
||||
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
if (statusEffectController != null && statusEffectController.IsStunned)
|
||||
{
|
||||
animator.SetInteger("speed", 0);
|
||||
applyGravity();
|
||||
return;
|
||||
}
|
||||
|
||||
//攻击状态下等待Animator报告攻击动画播放完成。
|
||||
if (state == State.Attack)
|
||||
{
|
||||
animator.SetInteger("speed", 0);
|
||||
attackElapsedTime += Time.deltaTime;
|
||||
applyGravity();
|
||||
CheckAttackAnimationFinished();
|
||||
return;
|
||||
@@ -181,6 +212,13 @@ public class UnitBase : MonoBehaviour
|
||||
|
||||
public float AttackSpeedMultiplier => attackSpeedMultiplier;
|
||||
|
||||
public bool IsStunned => statusEffectController != null && statusEffectController.IsStunned;
|
||||
|
||||
public void ApplyStun(float duration)
|
||||
{
|
||||
EnsureStatusEffectController().ApplyStun(duration);
|
||||
}
|
||||
|
||||
//根据自身Tag找到敌对Tag
|
||||
private string GetOpponentTag()
|
||||
{
|
||||
@@ -377,7 +415,7 @@ public class UnitBase : MonoBehaviour
|
||||
animator.SetInteger("speed", 0);
|
||||
animator.SetTrigger("attack");
|
||||
state = State.Attack;
|
||||
attackStartTime = Time.time;
|
||||
attackElapsedTime = 0f;
|
||||
}
|
||||
|
||||
private void FaceTarget(GameObject opponent)
|
||||
@@ -394,14 +432,28 @@ public class UnitBase : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyFacingRotation(direction);
|
||||
}
|
||||
|
||||
private void ApplyFacingRotation(Vector3 direction)
|
||||
{
|
||||
direction.y = 0f;
|
||||
if (direction.sqrMagnitude <= 0.0001f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Quaternion targetRotation = Quaternion.LookRotation(direction, Vector3.up);
|
||||
transform.rotation = Quaternion.Euler(0f, targetRotation.eulerAngles.y, 0f);
|
||||
transform.rotation = Quaternion.Euler(
|
||||
0f,
|
||||
targetRotation.eulerAngles.y + modelFacingOffset,
|
||||
0f);
|
||||
}
|
||||
|
||||
//由攻击动画中最合适的帧通过 Animation Event 调用。
|
||||
public void OnAttackEffect()
|
||||
{
|
||||
if (state != State.Attack || target == null)
|
||||
if (IsStunned || state != State.Attack || target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -434,7 +486,7 @@ public class UnitBase : MonoBehaviour
|
||||
|
||||
public void ApplySkillHit(float damageMultiplier, bool registerEnergy)
|
||||
{
|
||||
if (target == null)
|
||||
if (IsStunned || target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -499,8 +551,55 @@ public class UnitBase : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyAreaStun(float radius, float duration)
|
||||
{
|
||||
if (duration <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string opponentTag = GetOpponentTag();
|
||||
if (string.IsNullOrEmpty(opponentTag))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float clampedRadius = Mathf.Max(0f, radius);
|
||||
float radiusSquared = clampedRadius * clampedRadius;
|
||||
GameObject[] opponents = GameObject.FindGameObjectsWithTag(opponentTag);
|
||||
HashSet<UnitBase> hitUnits = new HashSet<UnitBase>();
|
||||
|
||||
foreach (GameObject opponent in opponents)
|
||||
{
|
||||
if (opponent == null || opponent == gameObject || opponent.transform.root == transform.root)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector3 offset = opponent.transform.position - transform.position;
|
||||
offset.y = 0f;
|
||||
if (offset.sqrMagnitude > radiusSquared)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UnitBase unit = opponent.GetComponentInParent<UnitBase>();
|
||||
if (unit == null || !hitUnits.Add(unit))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unit.ApplyStun(duration);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterAttackHit()
|
||||
{
|
||||
if (IsStunned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddAttackEnergy();
|
||||
}
|
||||
|
||||
@@ -590,7 +689,6 @@ public class UnitBase : MonoBehaviour
|
||||
animator.speed = GetSkillAnimationSpeed(targetDuration);
|
||||
animator.SetInteger("speed", 0);
|
||||
animator.SetTrigger("skill");
|
||||
attackStartTime = Time.time;
|
||||
}
|
||||
|
||||
public void MultiplyAttackSpeed(float multiplier, float limit)
|
||||
@@ -624,7 +722,7 @@ public class UnitBase : MonoBehaviour
|
||||
? attackDuration
|
||||
: GetAttackAnimationDuration();
|
||||
|
||||
if (animationDuration > 0f && Time.time - attackStartTime >= animationDuration)
|
||||
if (animationDuration > 0f && attackElapsedTime >= animationDuration)
|
||||
{
|
||||
FinishAttack();
|
||||
return;
|
||||
@@ -696,9 +794,9 @@ public class UnitBase : MonoBehaviour
|
||||
Vector3 velocity = movement.normalized * speed;
|
||||
velocity.y = getVerticalVelocity();
|
||||
controller.Move(velocity * Time.deltaTime);
|
||||
//改变角色朝向目标点,只改变y轴
|
||||
transform.rotation = Quaternion.LookRotation(movement, Vector3.up);
|
||||
transform.rotation = Quaternion.Euler(0, transform.rotation.eulerAngles.y, 0);
|
||||
//移动时模型正面朝向移动方向;模型朝向修正只用于攻击动作。
|
||||
Quaternion movementRotation = Quaternion.LookRotation(movement, Vector3.up);
|
||||
transform.rotation = Quaternion.Euler(0f, movementRotation.eulerAngles.y, 0f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user