根据提供的code differences信息,由于没有具体的代码变更内容,我将生成一个通用的commit message模板:
``` docs(readme): 更新文档说明 - 添加项目使用指南 - 完善API接口说明 - 修正错误的配置示例 ``` 注意:由于您提供的code differences部分为空,以上是一个示例模板。当您有具体的代码变更时,请提供详细的变更内容以便生成更准确的commit message。
This commit is contained in:
1 parent
8d58c2223c
commit
11f1af357d
191 files changed
+2315
-1539
No files matched your search
@@ -0,0 +1,177 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: BykWtHz4ACmy6F8Kvn3Hf5OfB+YboFb74aULp8lFAVCZxth46GKpB9g=
|
||||
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: DSxN5iutVn7vYWZihot5eHq3WxYgtPA7YmvQEIefFazDgNVA+Ol4fxA=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,120 @@
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// 控制冲击波扩散效果
|
||||
/// 可通过代码动态调整颜色、大小、速度等参数,实现不同技能的复用
|
||||
/// </summary>
|
||||
public class ShockwaveEffect : MonoBehaviour
|
||||
{
|
||||
[Header("扩散参数")]
|
||||
[Tooltip("扩散持续时间(秒)")]
|
||||
public float duration = 1f;
|
||||
|
||||
[Tooltip("扩散完成后是否自动销毁")]
|
||||
public bool autoDestroy = true;
|
||||
|
||||
[Header("运行时可配置参数(可选)")]
|
||||
[Tooltip("覆盖材质颜色(Alpha=0时不覆盖)")]
|
||||
public Color overrideColor = new Color(0, 0, 0, 0);
|
||||
|
||||
[Tooltip("覆盖扩散半径(<=0时使用Transform.localScale)")]
|
||||
public float overrideRadius = 0f;
|
||||
|
||||
[Tooltip("覆盖圆环粗细(<=0时使用材质默认值)")]
|
||||
public float overrideRingWidth = 0f;
|
||||
|
||||
private Material material;
|
||||
private float elapsed;
|
||||
private bool isPlaying;
|
||||
private Vector3 initialScale;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// 获取材质实例(不影响原材质)
|
||||
var renderer = GetComponent<MeshRenderer>();
|
||||
if (renderer != null)
|
||||
{
|
||||
material = renderer.material; // 自动创建材质实例
|
||||
|
||||
// 应用覆盖参数
|
||||
if (overrideColor.a > 0f)
|
||||
{
|
||||
material.SetColor("_RingColor", overrideColor);
|
||||
}
|
||||
|
||||
if (overrideRingWidth > 0f)
|
||||
{
|
||||
material.SetFloat("_RingWidth", overrideRingWidth);
|
||||
}
|
||||
|
||||
if (overrideRadius > 0f)
|
||||
{
|
||||
// 根据半径计算所需的Scale
|
||||
// Quad默认1x1,半径0.5,所以 Scale = radius * 2
|
||||
float scale = overrideRadius * 2f;
|
||||
transform.localScale = new Vector3(scale, scale, scale);
|
||||
}
|
||||
|
||||
// 从0开始扩散
|
||||
material.SetFloat("_Progress", 0f);
|
||||
isPlaying = true;
|
||||
elapsed = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("ShockwaveEffect: 找不到MeshRenderer组件");
|
||||
if (autoDestroy)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!isPlaying || material == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
elapsed += Time.deltaTime;
|
||||
float progress = Mathf.Clamp01(elapsed / duration);
|
||||
|
||||
// 更新Progress参数(0到1)
|
||||
material.SetFloat("_Progress", progress);
|
||||
|
||||
// 扩散完成
|
||||
if (progress >= 1f)
|
||||
{
|
||||
isPlaying = false;
|
||||
|
||||
if (autoDestroy)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// 清理材质实例
|
||||
if (material != null)
|
||||
{
|
||||
Destroy(material);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快捷方法:设置扩散参数
|
||||
/// </summary>
|
||||
public void SetupEffect(float radius, float duration, Color color, float ringWidth = 0f)
|
||||
{
|
||||
this.overrideRadius = radius;
|
||||
this.duration = duration;
|
||||
this.overrideColor = color;
|
||||
if (ringWidth > 0f)
|
||||
{
|
||||
this.overrideRingWidth = ringWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: BixO4yurUijnu64p8+3f1u2vjWk9JK3rjKwwJMBeodVwKJbnJRniptQ=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user