123
This commit is contained in:
1 parent
2241642a97
commit
4216d27310
132 files changed
+3575
-137
No files matched your search
@@ -0,0 +1,47 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class HumanSoldierSkill : UnitSkill
|
||||
{
|
||||
[Min(0f)]
|
||||
public float bonusAttackMultiplier = 0.5f;
|
||||
|
||||
[Header("技能动画")]
|
||||
[Min(0f)]
|
||||
public float skillAnimationDuration = 0f;
|
||||
|
||||
private bool isSkillReady;
|
||||
|
||||
public override void ReleaseSkill()
|
||||
{
|
||||
isSkillReady = true;
|
||||
unit.PlaySkillAnimation(OnSkillFinished, skillAnimationDuration);
|
||||
}
|
||||
|
||||
// 添加到技能攻击动画的第一段命中帧。
|
||||
public void OnSkillHit1()
|
||||
{
|
||||
if (!isSkillReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unit.ApplySkillHit(1f, true);
|
||||
}
|
||||
|
||||
// 添加到技能攻击动画的第二段命中帧。
|
||||
public void OnSkillHit2()
|
||||
{
|
||||
if (!isSkillReady)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unit.ApplySkillHit(bonusAttackMultiplier, false);
|
||||
isSkillReady = false;
|
||||
}
|
||||
|
||||
private void OnSkillFinished()
|
||||
{
|
||||
isSkillReady = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: W34f4C/7USoWR/R5rwP9HkjbiTWMMxMnlwTiCpclEAjSKp6tZ2xZ9HQ=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,138 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ShieldBarrier : MonoBehaviour
|
||||
{
|
||||
[Header("盾牌状态")]
|
||||
[Min(0f)]
|
||||
public float maxDurability = 220f;
|
||||
|
||||
[Min(0f)]
|
||||
public float currentDurability;
|
||||
|
||||
[Min(0f)]
|
||||
public float duration = 6f;
|
||||
|
||||
[Range(-1f, 1f)]
|
||||
public float frontDotThreshold = 0.15f;
|
||||
|
||||
private UnitBase owner;
|
||||
private Collider barrierCollider;
|
||||
private float remainingTime;
|
||||
|
||||
public UnitBase Owner => owner;
|
||||
|
||||
public float CurrentDurability => currentDurability;
|
||||
|
||||
public bool IsActive =>
|
||||
isActiveAndEnabled &&
|
||||
barrierCollider != null &&
|
||||
barrierCollider.enabled &&
|
||||
currentDurability > 0f &&
|
||||
remainingTime > 0f;
|
||||
|
||||
public void Configure(UnitBase shieldOwner, Collider shieldCollider)
|
||||
{
|
||||
owner = shieldOwner;
|
||||
barrierCollider = shieldCollider;
|
||||
}
|
||||
|
||||
public void Refresh(float durability, float activeDuration, Vector3 localPosition, Vector3 localSize)
|
||||
{
|
||||
if (barrierCollider == null)
|
||||
{
|
||||
barrierCollider = GetComponent<Collider>();
|
||||
}
|
||||
|
||||
if (owner == null)
|
||||
{
|
||||
owner = GetComponentInParent<UnitBase>();
|
||||
}
|
||||
|
||||
transform.localPosition = localPosition;
|
||||
transform.localRotation = Quaternion.identity;
|
||||
|
||||
BoxCollider boxCollider = barrierCollider as BoxCollider;
|
||||
if (boxCollider != null)
|
||||
{
|
||||
boxCollider.center = Vector3.zero;
|
||||
boxCollider.size = localSize;
|
||||
boxCollider.isTrigger = true;
|
||||
}
|
||||
|
||||
maxDurability = Mathf.Max(0.01f, durability);
|
||||
currentDurability = maxDurability;
|
||||
duration = Mathf.Max(0.01f, activeDuration);
|
||||
remainingTime = duration;
|
||||
|
||||
gameObject.SetActive(true);
|
||||
if (barrierCollider != null)
|
||||
{
|
||||
barrierCollider.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryBlock(GameObject attacker, float damage)
|
||||
{
|
||||
if (!IsActive || damage <= 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attacker != null && !IsIncomingFromFront(attacker))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
currentDurability -= damage;
|
||||
if (currentDurability <= 0f)
|
||||
{
|
||||
DisableBarrier();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DisableBarrier()
|
||||
{
|
||||
currentDurability = 0f;
|
||||
remainingTime = 0f;
|
||||
|
||||
if (barrierCollider != null)
|
||||
{
|
||||
barrierCollider.enabled = false;
|
||||
}
|
||||
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
remainingTime -= Time.deltaTime;
|
||||
if (remainingTime <= 0f)
|
||||
{
|
||||
DisableBarrier();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsIncomingFromFront(GameObject attacker)
|
||||
{
|
||||
if (owner == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Vector3 direction = attacker.transform.position - owner.transform.position;
|
||||
direction.y = 0f;
|
||||
if (direction.sqrMagnitude <= 0.0001f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Vector3.Dot(owner.transform.forward, direction.normalized) >= frontDotThreshold;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: By8es3n/Vn8vDFRRNcUcF3DWUozAHLqfoGKYkWDk0WblebrKYNoIno8=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ShieldGuardSkill : UnitSkill
|
||||
{
|
||||
[Header("盾牌参数")]
|
||||
[Min(1f)]
|
||||
public float shieldDurability = 220f;
|
||||
|
||||
[Min(0.1f)]
|
||||
public float shieldDuration = 6f;
|
||||
|
||||
public Vector3 shieldLocalPosition = new Vector3(0f, 1f, 0.7f);
|
||||
|
||||
public Vector3 shieldSize = new Vector3(1.5f, 1.8f, 0.12f);
|
||||
|
||||
private ShieldBarrier shield;
|
||||
|
||||
public ShieldBarrier ActiveShield => shield;
|
||||
|
||||
public override void ReleaseSkill()
|
||||
{
|
||||
EnsureShield();
|
||||
shield.Refresh(shieldDurability, shieldDuration, shieldLocalPosition, shieldSize);
|
||||
}
|
||||
|
||||
private void EnsureShield()
|
||||
{
|
||||
if (shield != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject shieldObject = new GameObject("ShieldBarrier");
|
||||
shieldObject.transform.SetParent(unit.transform, false);
|
||||
|
||||
BoxCollider boxCollider = shieldObject.AddComponent<BoxCollider>();
|
||||
boxCollider.isTrigger = true;
|
||||
boxCollider.size = shieldSize;
|
||||
|
||||
shield = shieldObject.AddComponent<ShieldBarrier>();
|
||||
shield.Configure(unit, boxCollider);
|
||||
shieldObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: XCkWsHz7Wn/4PQyuRdHqOq4UICVTHToo6q2RmJrNHMtmqcPpJ27eVZY=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+102
-2
@@ -290,6 +290,53 @@ public class UnitBase : MonoBehaviour
|
||||
return 0f;
|
||||
}
|
||||
|
||||
// 查找当前 Animator 中使用的技能动画时长。
|
||||
private float GetSkillAnimationDuration()
|
||||
{
|
||||
if (animator == null || animator.runtimeAnimatorController == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var overrideController = animator.runtimeAnimatorController as AnimatorOverrideController;
|
||||
if (overrideController != null)
|
||||
{
|
||||
var overrides = new List<KeyValuePair<AnimationClip, AnimationClip>>();
|
||||
overrideController.GetOverrides(overrides);
|
||||
|
||||
foreach (var pair in overrides)
|
||||
{
|
||||
if (IsSkillAnimation(pair.Key))
|
||||
{
|
||||
return pair.Value != null ? pair.Value.length : pair.Key.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var clip in animator.runtimeAnimatorController.animationClips)
|
||||
{
|
||||
if (IsSkillAnimation(clip))
|
||||
{
|
||||
return clip.length;
|
||||
}
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
// 根据动画名称判断是否为技能动画。
|
||||
private bool IsSkillAnimation(AnimationClip clip)
|
||||
{
|
||||
if (clip == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string clipName = clip.name.ToLowerInvariant();
|
||||
return clipName.Contains("skill")
|
||||
|| clipName.Contains("技能");
|
||||
}
|
||||
|
||||
//根据动画名称判断是否为攻击动画
|
||||
private bool IsAttackAnimation(AnimationClip clip)
|
||||
{
|
||||
@@ -322,6 +369,8 @@ public class UnitBase : MonoBehaviour
|
||||
{
|
||||
target = friend;
|
||||
|
||||
FaceTarget(friend);
|
||||
|
||||
//根据目标攻击时长加速或减速完整攻击动画
|
||||
SetAttackAnimationSpeed();
|
||||
|
||||
@@ -331,6 +380,24 @@ public class UnitBase : MonoBehaviour
|
||||
attackStartTime = Time.time;
|
||||
}
|
||||
|
||||
private void FaceTarget(GameObject opponent)
|
||||
{
|
||||
if (opponent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 direction = opponent.transform.position - transform.position;
|
||||
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);
|
||||
}
|
||||
|
||||
//由攻击动画中最合适的帧通过 Animation Event 调用。
|
||||
public void OnAttackEffect()
|
||||
{
|
||||
@@ -365,6 +432,26 @@ public class UnitBase : MonoBehaviour
|
||||
RegisterAttackHit();
|
||||
}
|
||||
|
||||
public void ApplySkillHit(float damageMultiplier, bool registerEnergy)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WorldHealthBar healthBar = target.GetComponentInParent<WorldHealthBar>();
|
||||
if (healthBar == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
healthBar.TakeDamage(attackPower * Mathf.Max(0f, damageMultiplier));
|
||||
if (registerEnergy)
|
||||
{
|
||||
RegisterAttackHit();
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterAttackHit()
|
||||
{
|
||||
AddAttackEnergy();
|
||||
@@ -432,7 +519,18 @@ public class UnitBase : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public void PlaySkillAnimation(System.Action onFinished)
|
||||
private float GetSkillAnimationSpeed(float targetDuration)
|
||||
{
|
||||
float animationDuration = GetSkillAnimationDuration();
|
||||
if (animationDuration <= 0f || targetDuration <= 0f)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
return animationDuration / targetDuration;
|
||||
}
|
||||
|
||||
public void PlaySkillAnimation(System.Action onFinished, float targetDuration = 0f)
|
||||
{
|
||||
if (isPlayingSkill)
|
||||
{
|
||||
@@ -442,7 +540,7 @@ public class UnitBase : MonoBehaviour
|
||||
skillFinishedCallback = onFinished;
|
||||
isPlayingSkill = true;
|
||||
state = State.Skill;
|
||||
animator.speed = 1f;
|
||||
animator.speed = GetSkillAnimationSpeed(targetDuration);
|
||||
animator.SetInteger("speed", 0);
|
||||
animator.SetTrigger("skill");
|
||||
attackStartTime = Time.time;
|
||||
@@ -520,6 +618,8 @@ public class UnitBase : MonoBehaviour
|
||||
target = FindNearestOpponent();
|
||||
if (target != null && IsWithinAttackReach(target))
|
||||
{
|
||||
// 攻击结束后可能已切换到新的近距离目标,先更新朝向再开始下一次攻击。
|
||||
FaceTarget(target);
|
||||
attack(target);
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user