根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加项目使用指南 - 完善API接口描述 - 修正错误的配置示例 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。请提供具体的代码变更信息以便生成更准确的commit message。
This commit is contained in:
1 parent
e532a6c8ce
commit
fe3877dedb
16 files changed
+951
-113
No files matched your search
@@ -0,0 +1,122 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class Projectile : MonoBehaviour
|
||||
{
|
||||
private GameObject target;
|
||||
private GameObject owner;
|
||||
private float damage;
|
||||
private float speed;
|
||||
private float lifetime;
|
||||
private bool initialized;
|
||||
|
||||
public void Initialize(
|
||||
GameObject targetObject,
|
||||
float damageAmount,
|
||||
GameObject ownerObject,
|
||||
float moveSpeed,
|
||||
float lifeTime)
|
||||
{
|
||||
target = targetObject;
|
||||
damage = damageAmount;
|
||||
owner = ownerObject;
|
||||
speed = moveSpeed;
|
||||
lifetime = lifeTime;
|
||||
initialized = true;
|
||||
|
||||
if (lifetime > 0f)
|
||||
{
|
||||
Destroy(gameObject, lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 offset = target.transform.position - transform.position;
|
||||
if (offset.sqrMagnitude <= 0.01f)
|
||||
{
|
||||
HitTarget(target);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 direction = offset.normalized;
|
||||
float distance = speed * Time.deltaTime;
|
||||
if (distance >= offset.magnitude)
|
||||
{
|
||||
HitTarget(target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Physics.Raycast(transform.position, direction, out RaycastHit hit, distance))
|
||||
{
|
||||
TryHit(hit.collider.gameObject);
|
||||
if (hit.collider.transform.root == target.transform.root)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
transform.forward = direction;
|
||||
transform.position += direction * distance;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
TryHit(other.gameObject);
|
||||
}
|
||||
|
||||
private void OnCollisionEnter(Collision collision)
|
||||
{
|
||||
TryHit(collision.gameObject);
|
||||
}
|
||||
|
||||
private void TryHit(GameObject hitObject)
|
||||
{
|
||||
if (!initialized || hitObject == null || IsOwner(hitObject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (hitObject.transform.root == target.transform.root)
|
||||
{
|
||||
HitTarget(hitObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void HitTarget(GameObject hitObject)
|
||||
{
|
||||
WorldHealthBar healthBar = hitObject.GetComponentInParent<WorldHealthBar>();
|
||||
if (healthBar == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
healthBar.TakeDamage(damage);
|
||||
if (owner != null)
|
||||
{
|
||||
UnitBase unit = owner.GetComponent<UnitBase>();
|
||||
if (unit != null)
|
||||
{
|
||||
unit.RegisterAttackHit();
|
||||
}
|
||||
}
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
private bool IsOwner(GameObject hitObject)
|
||||
{
|
||||
return owner != null &&
|
||||
(hitObject == owner || hitObject.transform.root == owner.transform.root);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: BygavHz+By46EKJGDuv5Bnn4/4HcaL5YCS0EUQ96cZEDuWogq0Yq0gM=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+123
-36
@@ -1,4 +1,3 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -28,6 +27,32 @@ public class UnitBase : MonoBehaviour
|
||||
[Min(0f)]
|
||||
public float attackDuration = 0f;
|
||||
|
||||
public enum AttackType
|
||||
{
|
||||
//近战锁定目标,攻击事件触发时直接扣血。
|
||||
MeleeDirect,
|
||||
|
||||
//近战范围伤害,暂未实现。
|
||||
MeleeArea,
|
||||
|
||||
//远程锁定目标,攻击事件触发时生成飞行物。
|
||||
RangedProjectile,
|
||||
|
||||
//远程抛物线,不锁定目标,暂未实现。
|
||||
RangedArc
|
||||
}
|
||||
|
||||
[Header("攻击类型")]
|
||||
public AttackType attackType = AttackType.MeleeDirect;
|
||||
|
||||
[Header("远程飞行物")]
|
||||
public GameObject projectilePrefab;
|
||||
public Transform projectileSpawnPoint;
|
||||
[Min(0f)]
|
||||
public float projectileSpeed = 20f;
|
||||
[Min(0f)]
|
||||
public float projectileLifetime = 5f;
|
||||
|
||||
//每次普通攻击命中后增加的技能能量。
|
||||
[Header("技能能量")]
|
||||
[Min(0f)]
|
||||
@@ -57,8 +82,9 @@ public class UnitBase : MonoBehaviour
|
||||
//垂直速度
|
||||
private float verticalVelocity;
|
||||
|
||||
//当前攻击的伤害结算协程。
|
||||
private Coroutine attackDamageCoroutine;
|
||||
//当前攻击开始时间,用于在没有结束 Event 时释放攻击状态。
|
||||
private float attackStartTime;
|
||||
|
||||
|
||||
//状态枚举
|
||||
public enum State
|
||||
@@ -95,11 +121,12 @@ public class UnitBase : MonoBehaviour
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
//攻击状态直接返回
|
||||
//攻击状态下等待Animator报告攻击动画播放完成。
|
||||
if (state == State.Attack)
|
||||
{
|
||||
animator.SetInteger("speed", 0);
|
||||
applyGravity();
|
||||
CheckAttackAnimationFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,36 +302,64 @@ public class UnitBase : MonoBehaviour
|
||||
animator.SetInteger("speed", 0);
|
||||
animator.SetTrigger("attack");
|
||||
state = State.Attack;
|
||||
|
||||
//在攻击动画中段结算伤害,而不是在动画开始瞬间扣血。
|
||||
if (attackDamageCoroutine != null)
|
||||
{
|
||||
StopCoroutine(attackDamageCoroutine);
|
||||
}
|
||||
|
||||
attackDamageCoroutine = StartCoroutine(ApplyAttackDamageInMiddle(friend));
|
||||
attackStartTime = Time.time;
|
||||
}
|
||||
|
||||
//等待半个完整攻击时长后,对目标扣除攻击力。
|
||||
private IEnumerator ApplyAttackDamageInMiddle(GameObject friend)
|
||||
//由攻击动画中最合适的帧通过 Animation Event 调用。
|
||||
public void OnAttackEffect()
|
||||
{
|
||||
float fullAttackDuration = attackDuration > 0f
|
||||
? attackDuration
|
||||
: Mathf.Max(GetAttackAnimationDuration(), 0.01f);
|
||||
|
||||
yield return new WaitForSeconds(fullAttackDuration * 0.5f);
|
||||
|
||||
if (friend != null)
|
||||
if (state != State.Attack || target == null)
|
||||
{
|
||||
WorldHealthBar healthBar = friend.GetComponentInParent<WorldHealthBar>();
|
||||
if (healthBar != null)
|
||||
{
|
||||
healthBar.TakeDamage(attackPower);
|
||||
AddAttackEnergy();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
attackDamageCoroutine = null;
|
||||
switch (attackType)
|
||||
{
|
||||
case AttackType.MeleeDirect:
|
||||
ApplyDirectDamage(target);
|
||||
break;
|
||||
case AttackType.RangedProjectile:
|
||||
FireProjectile(target);
|
||||
break;
|
||||
case AttackType.MeleeArea:
|
||||
case AttackType.RangedArc:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyDirectDamage(GameObject opponent)
|
||||
{
|
||||
WorldHealthBar healthBar = opponent.GetComponentInParent<WorldHealthBar>();
|
||||
if (healthBar == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
healthBar.TakeDamage(attackPower);
|
||||
RegisterAttackHit();
|
||||
}
|
||||
|
||||
public void RegisterAttackHit()
|
||||
{
|
||||
AddAttackEnergy();
|
||||
}
|
||||
|
||||
private void FireProjectile(GameObject opponent)
|
||||
{
|
||||
if (projectilePrefab == null || projectileSpawnPoint == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject projectileObject = Instantiate(
|
||||
projectilePrefab,
|
||||
projectileSpawnPoint.position,
|
||||
projectileSpawnPoint.rotation);
|
||||
Projectile projectile = projectileObject.GetComponent<Projectile>();
|
||||
if (projectile != null)
|
||||
{
|
||||
projectile.Initialize(opponent, attackPower, gameObject, projectileSpeed, projectileLifetime);
|
||||
}
|
||||
}
|
||||
|
||||
// 普通攻击命中后积攒能量,满能量时清空能量并调用技能占位方法。
|
||||
@@ -330,19 +385,51 @@ public class UnitBase : MonoBehaviour
|
||||
}
|
||||
|
||||
|
||||
// 在 Animator 的 Attack 动画结束时调用此方法
|
||||
public void OnAttackAnimationEnd()
|
||||
// 根据当前攻击动画的播放进度自动结束攻击,不依赖 Animation Event。
|
||||
private void CheckAttackAnimationFinished()
|
||||
{
|
||||
if (attackDamageCoroutine != null)
|
||||
float animationDuration = attackDuration > 0f
|
||||
? attackDuration
|
||||
: GetAttackAnimationDuration();
|
||||
|
||||
if (animationDuration > 0f && Time.time - attackStartTime >= animationDuration)
|
||||
{
|
||||
StopCoroutine(attackDamageCoroutine);
|
||||
attackDamageCoroutine = null;
|
||||
FinishAttack();
|
||||
return;
|
||||
}
|
||||
|
||||
//攻击动画结束后恢复Animator速度
|
||||
if (!IsPlayingAttackAnimation())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
|
||||
if (stateInfo.normalizedTime >= 1f)
|
||||
{
|
||||
FinishAttack();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPlayingAttackAnimation()
|
||||
{
|
||||
AnimatorClipInfo[] clipInfos = animator.GetCurrentAnimatorClipInfo(0);
|
||||
foreach (AnimatorClipInfo clipInfo in clipInfos)
|
||||
{
|
||||
if (IsAttackAnimation(clipInfo.clip))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void FinishAttack()
|
||||
{
|
||||
//攻击动画结束后恢复Animator速度。
|
||||
animator.speed = 1f;
|
||||
|
||||
//攻击动画结束后,目标仍在攻击范围内则立即进行下一次攻击
|
||||
//攻击动画结束后,目标仍在攻击范围内则立即进行下一次攻击。
|
||||
target = FindNearestOpponent();
|
||||
if (target != null && IsWithinAttackReach(target))
|
||||
{
|
||||
@@ -350,7 +437,7 @@ public class UnitBase : MonoBehaviour
|
||||
return;
|
||||
}
|
||||
|
||||
//目标离开攻击范围,回到待机逻辑
|
||||
//目标离开攻击范围,回到待机逻辑。
|
||||
state = State.Alert;
|
||||
animator.SetInteger("speed", 0);
|
||||
applyGravity();
|
||||
|
||||
Reference in new issue
Block a user