得意黑字体安装

This commit is contained in:
tianmo committed 2026-10-14 14:46:24 +08:00
1 parent d0144205c4
commit 3ae43d1297
39 files changed
+8628 -817

No files matched your search

@@ -0,0 +1,157 @@
using UnityEngine;
/// <summary>
/// 敌人追踪:朝目标(玩家)移动,进入销毁半径后自我销毁。
///
/// 目标引用在 Inspector / 预制体实例里接好——预制体资产本身存不下场景对象,
/// 所以每个场景实例各自接一次;代码里不按名字 / 标签查找节点。
/// 移动直接改 transform,不依赖 CharacterController / Rigidbody,只在水平面内走,
/// 高度保持不变。
///
/// 死亡流程:一进入销毁半径就立刻在 dieFxPos 节点处生成死亡特效预制体(die_fx,预制体引用,
/// 允许留空)、用 Animator 播死亡动画(把 dieParamName 这个 bool 置为 true,都允许留空),
/// 再等「延迟死亡」的时间,到点销毁自己;延迟期间停止移动和转身。
/// 也就是说 die_delay 延迟的是「身体消失」,不是特效/动画的播放时机——
/// 想看清死亡动画,把 die_delay 设成动画时长即可(三个变量互不关联)。
/// </summary>
[DisallowMultipleComponent]
public class EnemyChaseController : MonoBehaviour
{
[Header("引用(必须在 Inspector / 预制体实例里指定)")]
[Tooltip("追踪目标,拖玩家根节点进来;留空敌人不会移动")]
[SerializeField] private Transform target;
[Header("移动")]
[Tooltip("最大移动速度(单位/秒)")]
[SerializeField] private float moveSpeed = 3f;
[Tooltip("加速度(单位/秒²),决定从静止加到最大速度的快慢;0 = 立刻满速")]
[SerializeField] private float acceleration = 10f;
[Tooltip("转身速度,越大转得越快;0 = 不转身,保持初始朝向")]
[SerializeField] private float turnSpeed = 10f;
[Tooltip("销毁半径:与目标的水平距离小于它时销毁自己(0 = 贴身才销毁)")]
[SerializeField] private float destroyDistance = 0.8f;
[Header("死亡")]
[Tooltip("死亡特效预制体,允许留空。在 dieFxPos 处生成一份,按自身时长兜底回收")]
[SerializeField] private GameObject dieFx;
[Tooltip("死亡特效的生成位置,拖敌人下的定位节点(例如 fx_die_pos);留空则在敌人自身位置生成")]
[SerializeField] private Transform dieFxPos;
[Tooltip("敌人身上的 Animator,死亡动画由它播;留空则不播死亡动画")]
[SerializeField] private Animator animator;
[Tooltip("死亡动画的 bool 参数名,对应 Animator 里的参数;置 true 触发死亡动画,留空则不播")]
[SerializeField] private string dieParamName = "is_die";
[Tooltip("延迟死亡:进入销毁半径后再等这么久才销毁自己(0 = 立刻消失,特效和动画也就看不到)")]
[SerializeField] private float dieDelay = 0f;
private float _currentSpeed;
private bool _dying;
private float _dieTimer;
private void Awake()
{
if (target == null)
Debug.LogError("[EnemyChaseController] 没有指定追踪目标(target),请在 Inspector / 预制体实例里拖入玩家。", this);
if (animator == null)
Debug.LogError("[EnemyChaseController] 没有指定 Animator(animator),死亡动画不会播。", this);
}
private void Update()
{
if (_dying)
{
_dieTimer -= Time.deltaTime;
if (_dieTimer <= 0f)
Destroy(gameObject);
return;
}
if (target == null)
return;
Vector3 toTarget = target.position - transform.position;
toTarget.y = 0f; // 只在水平面内追踪,高度不变
float distance = toTarget.magnitude;
if (distance <= destroyDistance)
{
StartDying();
return;
}
Vector3 direction = toTarget / Mathf.Max(distance, 0.0001f);
_currentSpeed = acceleration > 0f
? Mathf.MoveTowards(_currentSpeed, moveSpeed, acceleration * Time.deltaTime)
: moveSpeed;
transform.position += direction * (_currentSpeed * Time.deltaTime);
if (turnSpeed > 0f)
transform.rotation = Quaternion.Slerp(
transform.rotation,
Quaternion.LookRotation(direction),
turnSpeed * Time.deltaTime);
}
/// <summary>进入销毁半径:立刻生成死亡特效、播死亡动画,然后开始倒计时。</summary>
private void StartDying()
{
_dying = true;
_dieTimer = dieDelay;
SpawnDeathPrefab(dieFx);
if (animator != null && !string.IsNullOrEmpty(dieParamName))
animator.SetBool(dieParamName, true);
if (_dieTimer <= 0f)
Destroy(gameObject);
}
/// <summary>在敌人当前位置生成死亡用的预制体,并按它自己的时长兜底回收。</summary>
private void SpawnDeathPrefab(GameObject prefab)
{
if (prefab == null)
return;
Transform spawn = dieFxPos != null ? dieFxPos : transform;
GameObject instance = Instantiate(prefab, spawn.position, spawn.rotation);
Destroy(instance, Lifespan(instance));
}
/// <summary>
/// 估这个实例能播多久:粒子取「时长 + 最长粒子寿命」,旧版动画取 Clip 长度,取最长的一个。
/// 只是兜底回收,预制体自己销毁也可以(重复销毁无害)。
/// </summary>
private static float Lifespan(GameObject instance)
{
float longest = 0f;
foreach (ParticleSystem ps in instance.GetComponentsInChildren<ParticleSystem>(true))
{
ParticleSystem.MainModule main = ps.main;
longest = Mathf.Max(longest, main.duration + main.startLifetime.constantMax);
}
foreach (Animation animation in instance.GetComponentsInChildren<Animation>(true))
foreach (AnimationState state in animation)
longest = Mathf.Max(longest, state.length);
return longest > 0f ? longest : 1f; // 认不出时长时给个兜底
}
/// <summary>在场景视图里画出销毁半径,方便对着调数值。</summary>
private void OnDrawGizmosSelected()
{
Gizmos.color = new Color(1f, 0.3f, 0.3f, 0.6f);
Gizmos.DrawWireSphere(transform.position, destroyDistance);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 6cfa95cf7b0a943c4904a13297c823e3
@@ -4,14 +4,14 @@ using UnityEngine;
/// 用 UI 虚拟摇杆(移动轮盘)驱动角色移动,替代原来的点击地面移动。
///
/// 用法:挂在带 CharacterController 的角色上,把场景里的 <see cref="VirtualJoystick"/>
/// 拖到 joystick 字段(留空会在运行时自动查找)。
/// 拖到 joystick 字段(在 Inspector / 预制体里接线,代码里不查找节点)。
/// 摇杆方向默认按摄像机朝向换算成世界方向,推杆越满速度越快;只做水平移动,y 不变。
/// </summary>
[RequireComponent(typeof(CharacterController))]
public class PlayerMoveController : MonoBehaviour
{
[Header("输入")]
[Tooltip("移动轮盘;留空则在运行时自动查找场景中的 VirtualJoystick")]
[Tooltip("移动轮盘;必须在 Inspector / 预制体里指定,留空角色不会移动")]
[SerializeField] private VirtualJoystick joystick;
[Tooltip("是否按摄像机朝向移动(关掉则按世界坐标轴移动)")]
@@ -37,16 +37,9 @@ public class PlayerMoveController : MonoBehaviour
private void Awake()
{
_controller = GetComponent<CharacterController>();
}
private void Start()
{
// 场景里只有一个轮盘时自动接管,省得手动拖引用
if (joystick == null)
joystick = FindFirstObjectByType<VirtualJoystick>(FindObjectsInactive.Include);
if (joystick == null)
Debug.LogWarning("[PlayerMoveController] 场景里没找到 VirtualJoystick(移动轮盘),角色不会移动。");
Debug.LogError("[PlayerMoveController] 没有指定移动轮盘(joystick),请在 Inspector / 预制体里拖入。", this);
}
private void Update()