得意黑字体安装
This commit is contained in:
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()
|
||||
|
||||
@@ -3,9 +3,8 @@ using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// 屏幕虚拟摇杆(移动轮盘)。挂在预制体根节点上,子物体约定按名字查找:
|
||||
/// Background —— 底盘(RectTransform + Image)
|
||||
/// Handle —— 摇杆头(RectTransform + Image)
|
||||
/// 屏幕虚拟摇杆(移动轮盘)。挂在预制体根节点上,底盘和摇杆头两个引用
|
||||
/// (background / handle)在 Inspector / 预制体里接好,代码里不按名字查找节点。
|
||||
///
|
||||
/// 鼠标按住拖拽或手指触摸都可以操作,对外输出归一化方向 <see cref="Value"/>:
|
||||
/// x = 左右(右为正),y = 前后(上为正),模长 0~1 表示推杆幅度。
|
||||
@@ -14,8 +13,11 @@ using UnityEngine.UI;
|
||||
[DisallowMultipleComponent]
|
||||
public class VirtualJoystick : MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
|
||||
{
|
||||
[Header("引用(留空则按子物体名自动查找)")]
|
||||
[Header("引用(必须在 Inspector / 预制体里指定)")]
|
||||
[Tooltip("摇杆底盘(RectTransform)。拖拽位置以它的轴心为圆心换算,必须是捕获点击的那个节点")]
|
||||
[SerializeField] private RectTransform background;
|
||||
|
||||
[Tooltip("摇杆头(RectTransform),跟着拖拽移动")]
|
||||
[SerializeField] private RectTransform handle;
|
||||
|
||||
[Header("手感(距离单位是 UI 像素,以底盘轴心为圆心)")]
|
||||
@@ -45,12 +47,9 @@ public class VirtualJoystick : MonoBehaviour, IPointerDownHandler, IDragHandler,
|
||||
private void Awake()
|
||||
{
|
||||
if (background == null)
|
||||
background = FindChildRect("Background");
|
||||
// 没有名为 Background 的子物体时,就把挂脚本的这个节点本身当作底盘
|
||||
if (background == null)
|
||||
background = transform as RectTransform;
|
||||
Debug.LogError("[VirtualJoystick] 没有指定摇杆底盘(background),请在 Inspector / 预制体里拖入。", this);
|
||||
if (handle == null)
|
||||
handle = FindChildRect("Handle");
|
||||
Debug.LogError("[VirtualJoystick] 没有指定摇杆头(handle),请在 Inspector / 预制体里拖入。", this);
|
||||
|
||||
_canvas = GetComponentInParent<Canvas>();
|
||||
if (handle != null)
|
||||
@@ -125,28 +124,4 @@ public class VirtualJoystick : MonoBehaviour, IPointerDownHandler, IDragHandler,
|
||||
if (handle != null)
|
||||
handle.anchoredPosition = _handleHomePosition + direction * (magnitude * radius);
|
||||
}
|
||||
|
||||
private RectTransform FindChildRect(string childName)
|
||||
{
|
||||
Transform child = transform.Find(childName);
|
||||
if (child == null)
|
||||
{
|
||||
// 允许更深层级,比如 Root/Art/Background
|
||||
foreach (RectTransform rt in GetComponentsInChildren<RectTransform>(true))
|
||||
{
|
||||
if (rt != null && rt != transform && rt.name == childName)
|
||||
return rt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return child as RectTransform;
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (handle == null)
|
||||
handle = FindChildRect("Handle");
|
||||
if (background == null)
|
||||
background = FindChildRect("Background") ?? transform as RectTransform;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user