Files
daluandou/Assets/Scripts/投射物和特效/Projectile.cs
T
huadexuan 1a84ea999e 根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
```
docs(readme): 更新文档说明

- 添加项目使用指南
- 完善API接口说明
- 修正错误的配置示例
```

注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
2026-10-14 00:11:54 +08:00

222 lines
6.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
public class Projectile : MonoBehaviour
{
[Header("爆炸效果(可选)")]
public GameObject explosionPrefab; // 爆炸特效预制体,留空则没有爆炸效果
[Header("抛物线飞行(可选)")]
public bool useParabola = false; // 是否启用小抛物线飞行
public float parabolaHeight = 1.0f; // 抛物线最高拱起高度 (不太高的抛物线)
private GameObject target;
private GameObject owner;
private float damage;
private float speed;
private float lifetime;
private bool registerEnergyOnHit;
private bool initialized;
private bool hasHit;
private Vector3 startPosition;
private float totalFlyDistance;
private float horizontalTraveled;
public void Initialize(
GameObject targetObject,
float damageAmount,
GameObject ownerObject,
float moveSpeed,
float lifeTime,
bool registerEnergy = true)
{
target = targetObject;
damage = damageAmount;
owner = ownerObject;
speed = moveSpeed;
lifetime = lifeTime;
registerEnergyOnHit = registerEnergy;
initialized = true;
hasHit = false;
startPosition = transform.position;
horizontalTraveled = 0f;
if (targetObject != null)
{
Vector3 diff = targetObject.transform.position - startPosition;
diff.y = 0f;
totalFlyDistance = Mathf.Max(0.1f, diff.magnitude);
}
else
{
totalFlyDistance = 10f;
}
// The projectile uses its own raycast hit check. Disable the collider so
// it cannot be destroyed or disturbed by the owner's launch socket.
foreach (var collider in GetComponentsInChildren<Collider>(true))
{
collider.enabled = false;
}
if (lifetime > 0f)
{
Destroy(gameObject, lifetime);
}
}
private void Update()
{
if (!initialized)
{
return;
}
if (target == null)
{
Destroy(gameObject);
return;
}
Vector3 targetCurrentPos = target.transform.position;
Vector3 offset = targetCurrentPos - transform.position;
Vector3 flatOffset = new Vector3(offset.x, 0f, offset.z);
if (flatOffset.sqrMagnitude <= 0.04f)
{
HitTarget(target);
return;
}
Vector3 direction = flatOffset.normalized;
float step = speed * Time.deltaTime;
horizontalTraveled += step;
if (step >= flatOffset.magnitude)
{
HitTarget(target);
return;
}
Vector3 nextPos;
if (useParabola && totalFlyDistance > 0.1f)
{
// 水平线性推进
float t = Mathf.Clamp01(horizontalTraveled / totalFlyDistance);
Vector3 groundPos = Vector3.Lerp(new Vector3(startPosition.x, 0.1f, startPosition.z), new Vector3(targetCurrentPos.x, 0.1f, targetCurrentPos.z), t);
// 抛物线高度:基础发射高度渐变 + 4 * h * t * (1 - t) 抛物线弧度
float startY = startPosition.y;
float targetY = targetCurrentPos.y + 0.8f;
float baseY = Mathf.Lerp(startY, targetY, t);
float arcY = 4f * parabolaHeight * t * (1f - t);
nextPos = new Vector3(groundPos.x, baseY + arcY, groundPos.z);
// 矛身旋转:朝向移动切线方向,矛头顺着抛物线弧度自然俯仰
Vector3 velocity = (nextPos - transform.position);
if (velocity.sqrMagnitude > 1e-6f)
{
transform.rotation = Quaternion.LookRotation(velocity.normalized, Vector3.up);
}
transform.position = nextPos;
}
else
{
// 水平直线飞行
transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
transform.position += direction * step;
}
if (Physics.Raycast(transform.position, direction, out RaycastHit hit, step))
{
TryHit(hit.collider.gameObject);
if (hit.collider.transform.root == target.transform.root)
{
return;
}
}
}
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 || target == null || IsOwner(hitObject))
{
return;
}
if (hitObject.transform.root == target.transform.root)
{
HitTarget(hitObject);
}
}
private void HitTarget(GameObject hitObject)
{
if (hasHit)
{
return;
}
UnitBase targetUnit = hitObject.GetComponentInParent<UnitBase>();
WorldHealthBar targetHealthBar = hitObject.GetComponentInParent<WorldHealthBar>();
if (targetUnit == null && targetHealthBar == null)
{
return;
}
hasHit = true;
if (targetUnit != null)
{
targetUnit.ReceiveDamage(damage, owner);
}
else if (targetHealthBar != null)
{
// 召唤师等特殊单位(有 WorldHealthBar 但没有 UnitBase)
targetHealthBar.TakeDamage(damage, owner);
}
if (registerEnergyOnHit && owner != null)
{
UnitBase unit = owner.GetComponent<UnitBase>();
if (unit != null)
{
unit.RegisterAttackHit();
// 远程投射物通用吸血
if (unit.lifestealRate > 0f)
{
WorldHealthBar ownerBar = unit.GetComponent<WorldHealthBar>();
if (ownerBar != null)
{
ownerBar.Heal(damage * unit.lifestealRate);
}
}
}
}
// 生成爆炸效果
if (explosionPrefab != null)
{
Instantiate(explosionPrefab, transform.position, Quaternion.identity);
}
Destroy(gameObject);
}
private bool IsOwner(GameObject hitObject)
{
return owner != null &&
(hitObject == owner || hitObject.transform.root == owner.transform.root);
}
}