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

- 添加项目使用指南
- 完善API接口说明
- 修正错误的配置示例
```
2026-10-01 16:01:43 +08:00

178 lines
4.8 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; // 爆炸特效预制体,留空则没有爆炸效果
private GameObject target;
private GameObject owner;
private float damage;
private float speed;
private float lifetime;
private bool registerEnergyOnHit;
private bool initialized;
private bool hasHit;
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;
// 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 offset = target.transform.position - transform.position;
offset.y = 0f;
if (offset.sqrMagnitude <= 0.01f)
{
HitTarget(target);
return;
}
Vector3 direction = offset.normalized;
float distance = speed * Time.deltaTime;
if (distance >= offset.magnitude)
{
HitTarget(target);
return;
}
// 投射物保持水平飞行,并让根节点前方始终指向当前目标。
transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
if (Physics.Raycast(transform.position, direction, out RaycastHit hit, distance))
{
TryHit(hit.collider.gameObject);
if (hit.collider.transform.root == target.transform.root)
{
return;
}
}
// 火球保持 Prefab 固定角度,不随飞行方向旋转
// 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 || 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>();
CrystalMineCapture crystalMine = hitObject.GetComponentInParent<CrystalMineCapture>();
BuildingHealth targetBuilding = hitObject.GetComponentInParent<BuildingHealth>();
WorldHealthBar targetHealthBar = hitObject.GetComponentInParent<WorldHealthBar>();
if (targetUnit == null && crystalMine == null && targetBuilding == null && targetHealthBar == null)
{
return;
}
hasHit = true;
if (targetUnit != null)
{
targetUnit.ReceiveDamage(damage, owner);
}
else if (crystalMine != null)
{
crystalMine.ReceiveCaptureDamage(damage, owner);
}
else if (targetBuilding != null)
{
targetBuilding.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 (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);
}
}