在虚拟仿真项目开发中,经常需要实现类似 天敌捕食猎物 的交互实验功能。本文将介绍如何用 Unity 实现一个完整的 猎物生成 + 捕食控制 + 点击反馈 的系统,并结合对象池、事件回调等常见开发技巧,最终实现高效可复用的实验功能。
🎯 功能需求
-
猎物批量生成:按照配置数量生成猎物,避免重复 Instantiate 带来的性能消耗。
-
点击反馈:用户点击猎物时,触发缩放动画等视觉反馈。
-
猎物回收:猎物被捕食后自动回收,复用到对象池中。
-
天敌控制:不同的天敌(Predator)可通过按钮触发捕食行为。
-
实验流程演示:支持点击交互、提示信息清除与重新开始。
🧩 核心脚本实现
下面展示几个主要的脚本(均已加上详细备注,便于理解和学习)。
1. IHuntable 接口(猎物通用规范)
/// <summary>
/// 可捕食对象接口(猎物基类接口)
/// 用于解耦 PredatorController 与具体的猎物实现。
/// 所有猎物(如 PreyController)必须实现该接口,才能被天敌识别和捕食。
/// </summary>
public interface IHuntable
{
/// <summary>
/// 当猎物被点击时调用
/// - 负责触发点击反馈(例如高亮、闪烁、缩放等视觉效果)。
/// - 由猎物自身实现,保证不同猎物可以有不同反馈。
/// </summary>
void OnClicked();
/// <summary>
/// 重置猎物状态(对象池复用时调用)
/// - 用于恢复初始状态,例如位置、颜色、动画状态等。
/// - 避免频繁销毁/实例化造成性能浪费。
/// </summary>
void ResetPrey();
/// <summary>
/// 第一次被点击的时间(秒)
/// - 用于统计和记录捕食数据。
/// - 由实现类维护(通常在第一次 OnClicked 时赋值)。
/// - PredatorController 在 ConsumePrey 时可以读取该值,用于分析。
/// </summary>
float FirstClickTime { get; }
}
👉 这样可以保证所有“猎物”类都有统一的点击与回收逻辑。
2. Prey.cs(猎物类,负责点击与反馈)
using UnityEngine;
using UnityEngine.EventSystems;
using System;
using System.Collections;
/// <summary>
/// Prey 脚本:
/// - 实现了 IHuntable(可捕猎接口)和 IPointerClickHandler(UI点击接口)
/// - 提供点击反馈(缩放动画)
/// - 支持对象池回收(ResetPrey)
/// - 提供点击时间记录(FirstClickTime)
/// </summary>
[RequireComponent(typeof(Collider))] // 确保物体身上有 Collider,用于鼠标/点击检测
public class Prey : MonoBehaviour, IHuntable, IPointerClickHandler
{
/// <summary>
/// 静态事件:当任意 Prey 被点击时触发
/// 外部可以通过订阅 OnPreyClicked 来获取点击的 Prey 对象
/// </summary>
public static event Action<Prey> OnPreyClicked;
/// <summary>
/// 第一次被点击的时间(记录点击时间点)
/// -1 表示未点击过
/// </summary>
public float FirstClickTime { get; private set; } = -1f;
[Header("点击视觉反馈")]
[Tooltip("点击时缩放比例")]
public float clickScale = 1.3f;
[Tooltip("反馈持续时间(秒)")]
public float feedbackDuration = 0.15f;
/// <summary>
/// 原始缩放,用于恢复大小
/// </summary>
private Vector3 _originalScale;
/// <summary>
/// 是否已经被点击过
/// </summary>
private bool _isClicked = false;
private void Awake()
{
// 确保物体带有 Collider(否则点击检测无效)
if (GetComponent<Collider>() == null)
gameObject.AddComponent<BoxCollider>();
// 记录初始缩放
_originalScale = transform.localScale;
}
#region IHuntable 实现
/// <summary>
/// 当猎物被点击时调用
/// - 记录点击时间
/// - 标记为已点击
/// - 播放点击反馈动画
/// </summary>
public void OnClicked()
{
if (!_isClicked)
{
FirstClickTime = Time.time; // 记录点击时间
_isClicked = true; // 防止重复记录
}
// 停止所有可能的点击反馈协程(避免叠加)
StopAllCoroutines();
StartCoroutine(ClickFeedback());
}
/// <summary>
/// 点击视觉反馈协程
/// - 等待一段时间后恢复原始缩放
/// </summary>
private IEnumerator ClickFeedback()
{
// 点击瞬间放大
transform.localScale = _originalScale * clickScale;
// 等待指定时间
yield return new WaitForSeconds(feedbackDuration);
// 恢复原始缩放
transform.localScale = _originalScale;
}
/// <summary>
/// 对象池回收时重置猎物状态
/// - 恢复缩放
/// - 重置点击状态
/// - 重新激活对象
/// </summary>
public void ResetPrey()
{
_isClicked = false;
FirstClickTime = -1f;
transform.localScale = _originalScale;
gameObject.SetActive(true);
}
#endregion
#region 点击事件
/// <summary>
/// UI 点击接口实现(支持 EventSystem 点击)
/// </summary>
public void OnPointerClick(PointerEventData eventData) => TriggerClick();
/// <summary>
/// 鼠标点击事件(非 UI 点击,也能触发)
/// </summary>
private void OnMouseDown() => TriggerClick();
/// <summary>
/// 触发点击逻辑:
/// - 先广播事件(通知外部)
/// - 再执行自身的点击反馈
/// </summary>
private void TriggerClick()
{
OnPreyClicked?.Invoke(this); // 通知外部有 Prey 被点击
OnClicked(); // 播放反馈效果
}
#endregion
}
👉 该脚本负责猎物点击后的反馈(缩放效果)和回收逻辑。
3. InsertAnimation.cs(捕食动画配置)
using UnityEngine;
using System;
/// <summary>
/// InsertAnimation
/// —— 用于配置“天敌”与其相关的动画、猎物生成信息
/// —— 作为数据容器供 PredatorController 使用
/// </summary>
[Serializable] // 可序列化,能在 Inspector 中显示
public class InsertAnimation
{
[Header("配置名称(唯一标识)")]
[Tooltip("该配置的唯一名称标识,例如:'七星瓢虫配置',用于区分不同天敌的设置")]
public string name;
[Header("天敌对象")]
[Tooltip("天敌在场景中的 GameObject,例如:七星瓢虫的模型对象")]
public GameObject mainHunter;
[Header("猎物生成的父物体")]
[Tooltip("猎物生成时的父级 Transform,方便统一管理猎物位置与层级")]
public Transform root;
[Header("猎物预制体")]
[Tooltip("猎物的预制体(Prefab),用于实例化新的猎物对象")]
public Prey preyPrefab;
[Header("天敌爬行动画")]
[Tooltip("天敌的 Animation 组件,用于播放移动、捕食等动画")]
public Animation predatorAni;
}
👉 通过 ScriptableObject 或 Inspector 配置不同天敌及其动画效果。
4.PredatorController.cs(天敌控制器)
using UnityEngine;
using System.Collections.Generic;
using TMPro;
using System.Globalization;
using Random = UnityEngine.Random;
/// <summary>
/// 天敌控制器(PredatorController)
/// 功能:
/// 1. 控制天敌捕食猎物的过程(包含点击触发、移动路径、捕食逻辑)
/// 2. 支持对象池,减少频繁创建/销毁 Prey 对象
/// 3. 提供捕食关系表(可扩展,不同天敌对应不同猎物)
/// 4. 显示提示信息(如耗时)
/// </summary>
public class PredatorController : MonoBehaviour
{
[Header("天敌配置")]
[Tooltip("插入的动画和猎物配置(Prefab、动画、根节点等)")]
public InsertAnimation config;
[Header("移动参数")]
[Tooltip("天敌最小移动速度")]
public float minSpeed = 0.1f;
[Tooltip("天敌最大移动速度")]
public float maxSpeed = 0.2f;
private float _speed; // 当前速度
public bool isMove; // 是否正在移动
private int _moveStep; // 当前移动阶段
private Vector3 _targetPos; // 当前目标位置
private Vector3[] _targetPath; // 天敌移动的路径点数组
private int _maxPathPoints; // 路径点数量
private int _nowOpt; // 当前路径点索引
private Prey _clickPrey; // 被点击的猎物(天敌追逐目标)
private float _timeUsed; // 捕食过程耗时
// 管理 Prey 的对象池
private readonly List<Prey> _preyList = new(); // 当前活跃的猎物列表
private readonly Queue<Prey> _preyPool = new(); // 可复用的猎物对象池
/// <summary>
/// 捕食关系表(可扩展:天敌种类 -> 可捕食的猎物列表)
/// 示例: predatorPreyTable["七星瓢虫"] = new List<string> { "蚜虫", "白粉虱" };
/// </summary>
public Dictionary<string, List<string>> predatorPreyTable = new();
private void OnEnable()
{
// 订阅 Prey 点击事件
Prey.OnPreyClicked += HandlePreyClicked;
}
private void OnDisable()
{
// 取消订阅,避免内存泄漏
Prey.OnPreyClicked -= HandlePreyClicked;
}
/// <summary>
/// 开始狩猎,生成指定数量的猎物
/// </summary>
public void BeginHunt(int count)
{
_preyList.Clear();
_speed = Random.Range(minSpeed, maxSpeed); // 随机速度
_maxPathPoints = Random.Range(2, 6); // 随机路径点数量
// 对象池生成猎物
for (var i = 0; i < count; i++)
{
Prey prey;
if (_preyPool.Count > 0)
{
// 从池中取出已有的猎物
prey = _preyPool.Dequeue();
prey.ResetPrey();
}
else
{
// 池中没有,则实例化新的猎物
prey = Instantiate(config.preyPrefab, config.root);
}
// 随机放置猎物位置
prey.transform.localPosition = new Vector3(
Random.Range(-0.2f, 0.2f),
0,
Random.Range(-0.2f, 0.2f)
);
_preyList.Add(prey);
prey.gameObject.SetActive(true);
}
// 重置状态
_timeUsed = 0;
_moveStep = 0;
isMove = false;
TipManager.instance.ClearTip();
}
/// <summary>
/// 处理猎物点击事件
/// </summary>
private void HandlePreyClicked(Prey prey)
{
if (isMove) return; // 如果天敌正在移动,不响应
if (!_preyList.Contains(prey)) return; // 必须是当前活跃猎物
_clickPrey = prey;
_targetPos = prey.transform.localPosition;
// 生成移动路径
_targetPath = GetPath(_maxPathPoints, config.mainHunter.transform.localPosition, _targetPos);
// 进入移动状态
isMove = true;
_nowOpt = 0;
_moveStep = 0;
// 播放天敌移动动画
config.predatorAni["七星瓢虫-爬"].speed = 5;
config.predatorAni.Play("七星瓢虫-爬");
}
private void Update()
{
if (config != null)
MainFlow();
}
/// <summary>
/// 天敌移动与捕食主流程
/// </summary>
private void MainFlow()
{
if (!isMove) return;
var frameMove = Time.deltaTime * _speed; // 当前帧的移动步长
Vector3 nextPos;
switch (_moveStep)
{
// 阶段 0:天敌沿路径移动,追逐点击的猎物
case 0:
{
_timeUsed += Time.deltaTime;
nextPos = _targetPath[_nowOpt];
// 移动天敌
MoveHunter(nextPos, frameMove);
// 如果到达路径点
if (config.mainHunter.transform.localPosition == nextPos)
{
_nowOpt++;
// 到达终点,进入捕食阶段
if (_nowOpt >= _targetPath.Length)
{
_moveStep = 1;
ConsumePrey(_clickPrey);
return;
}
}
// 显示用时(取两位小数)
TipManager.instance.ShowTip((Mathf.Round(_timeUsed * 100f) / 100f).ToString());
break;
}
// 阶段 1:没有猎物了,停止
case 1 when _preyList.Count == 0:
isMove = false;
config.predatorAni.Stop();
return;
// 阶段 1:继续追逐下一个猎物
case 1:
{
nextPos = _preyList[0].transform.localPosition;
MoveHunter(nextPos, frameMove);
if (config.mainHunter.transform.localPosition == nextPos)
{
ConsumePrey(_preyList[0]);
}
break;
}
}
}
/// <summary>
/// 控制天敌移动(位置 + 朝向)
/// </summary>
private void MoveHunter(Vector3 target, float moveStep)
{
var pos = config.mainHunter.transform.localPosition;
// 移动到目标位置
config.mainHunter.transform.localPosition = Vector3.MoveTowards(pos, target, moveStep);
// 平滑旋转,朝向目标
config.mainHunter.transform.localRotation = Quaternion.Lerp(
config.mainHunter.transform.localRotation,
Quaternion.LookRotation(target - pos),
5f * Time.deltaTime
);
}
/// <summary>
/// 捕食猎物:从列表移除并回收到对象池
/// </summary>
private void ConsumePrey(Prey prey)
{
if (_preyList.Contains(prey))
_preyList.Remove(prey);
prey.gameObject.SetActive(false); // 隐藏
_preyPool.Enqueue(prey); // 回收进对象池
// 输出调试信息
Debug.Log($"捕食 {prey.name},首次捕食时间 {prey.FirstClickTime}");
}
/// <summary>
/// 生成路径点(起点 → 终点,中间有随机扰动)
/// </summary>
private static Vector3[] GetPath(int count, Vector3 start, Vector3 end)
{
var path = new Vector3[count];
var dx = Mathf.Abs(start.x - end.x) / (count + 1);
var dy = Mathf.Abs(start.y - end.y) / (count + 1);
var dz = Mathf.Abs(start.z - end.z) / (count + 1);
// 逐点生成路径
for (var i = 0; i < path.Length - 1; i++)
{
path[i].x = start.x >= end.x ? start.x - (i + 1) * dx : start.x + (i + 1) * dx;
path[i].y = start.y >= end.y ? start.y - (i + 1) * dy : start.y + (i + 1) * dy;
path[i].z = start.z >= end.z ? start.z - (i + 1) * dz : start.z + (i + 1) * dz;
}
// 最后一个点是终点
path[^1] = end;
// 给中间点加一些随机偏移(避免路径完全直线)
for (var i = 0; i < path.Length - 1; i++)
path[i].x += Random.Range(-0.3f, 0.3f);
return path;
}
}
👉 PredatorController 是 天敌的行为控制器,负责处理猎物点击后的捕食流程,包括移动路径规划、动画驱动、猎物消耗与回收。
5. HuntManager.cs(整体捕食控制)
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 天敌信息数据类
/// —— 绑定 UI 启动按钮 和 对应的天敌控制器
/// —— 用于在 HuntManager 中统一管理多个天敌
/// </summary>
[Serializable] // 可序列化,能在 Inspector 中显示
public class PredatorInfo
{
[Header("开始按钮")]
[Tooltip("点击该按钮后,启动对应的天敌捕食流程")]
public Button startButton;
[Header("天敌控制器")]
[Tooltip("该按钮绑定的天敌控制器对象(PredatorController 脚本)")]
public PredatorController predatorController;
}
/// <summary>
/// HuntManager
/// —— 捕食管理器
/// —— 主要负责:
/// 1. 绑定按钮点击事件与天敌的捕食逻辑
/// 2. 控制猎物数量
/// 3. 调用 PredatorController 来启动捕食流程
/// </summary>
public class HuntManager : MonoBehaviour
{
[Header("猎物数量")]
[Tooltip("每次点击按钮后,生成的猎物数量")]
public int preyCount = 10;
[Header("天敌信息列表")]
[Tooltip("所有需要控制的天敌信息(按钮 + 天敌控制器的绑定关系)")]
public List<PredatorInfo> predators;
/// <summary>
/// 初始化时执行
/// —— 遍历所有天敌信息,将按钮点击事件与对应的 StartHunt() 绑定
/// </summary>
private void Start()
{
foreach (var info in predators.Where(info => info.startButton != null && info.predatorController != null))
{
// 给每个按钮添加点击事件,启动对应天敌的捕食逻辑
info.startButton.onClick.AddListener(() =>
{
StartHunt(info.predatorController);
});
}
}
/// <summary>
/// 启动某个天敌的捕食流程
/// —— 会重置其状态,并生成猎物
/// </summary>
/// <param name="predator">指定的天敌控制器</param>
private void StartHunt(PredatorController predator)
{
// 1. 停止天敌的移动与动画
predator.isMove = false;
if (predator.config != null && predator.config.predatorAni != null)
{
predator.config.predatorAni.Stop();
}
// 2. 清空提示信息
TipManager.instance.ClearTip();
// 3. 启动捕食逻辑(生成猎物并进入捕食状态)
predator.BeginHunt(preyCount);
}
}
👉 HuntManager 是核心调度器,负责启动猎物生成和捕食逻辑。
6.TipManager.cs(提示管理器)
using TMPro;
using UnityEngine;
/// <summary>
/// TipManager —— 全局提示文本管理器
/// 用于在实验过程中显示或清空提示信息(例如捕食计时、提示文字)。
/// 采用单例模式,方便在其他脚本中通过 TipManager.instance 调用。
/// </summary>
public class TipManager : MonoBehaviour
{
/// <summary>
/// 静态单例引用,保证全局只有一个 TipManager。
/// 其他脚本可通过 TipManager.instance 快速访问。
/// </summary>
public static TipManager instance;
[Header("共用提示文本")]
/// <summary>
/// UI 上用于显示提示信息的 TMP_Text 组件。
/// 可以在 Inspector 中绑定,例如场景中的 TextMeshProUGUI。
/// </summary>
public TMP_Text tipText;
/// <summary>
/// Unity 生命周期方法 —— 脚本实例化时调用。
/// 设置单例引用,并初始化提示文本为空。
/// </summary>
private void Awake()
{
instance = this;
if (tipText != null)
tipText.text = "";
}
/// <summary>
/// 显示提示信息。
/// 在运行时调用该方法可更新提示文本内容。
/// </summary>
/// <param name="msg">需要显示的提示内容</param>
public void ShowTip(string msg)
{
if (tipText != null)
tipText.text = msg;
}
/// <summary>
/// 清空提示文本。
/// 例如在重新开始实验或捕食流程结束时调用。
/// </summary>
public void ClearTip()
{
if (tipText != null)
tipText.text = "";
}
}
👉 TipManager负责界面提示(第一次捕食时间)。
⚙️脚本挂载:
1.创建一个名为【天敌捕食管理器】的空物体,挂载【HuntManager.cs】和【TipManager.cs】
在天敌信息列表中配置,创建一个按钮启动捕食功能,将创建好的【Predator】拖给按钮对应的天敌控制器,实现按钮与天敌的一对一控制。确保天敌上有爬行动画,否则注释对应的爬行动画功能。

2.创建一个名为【Predator】的空物体,挂载【PredatorController.cs】

3.猎物预制体上需要挂载【Prey.cs】,确保预制体上有【BoxCollider】组件用于交互。
🐞天敌捕食流程图:展示了 天敌启动 → 猎物生成 → 点击反馈 → 动画捕食 → 回收复用 的完整流程
HuntManager(按钮启动)
│
▼
PredatorController.BeginHunt()
│
▼
生成猎物(Prey对象池)
│
点击猎物 → Prey.OnClicked()
│ ▲
▼ │
PredatorController.HandlePreyClicked()
│
动画捕食(config.predatorAni)
│
▼
ConsumePrey() → 回收到对象池
│
▼
TipManager(提示更新)
🖼️ 效果演示
-
点击“开始捕食”按钮,指定天敌出动。
-
在场景中生成多个猎物(对象池管理)。
-
玩家可点击生成的猎物,触发缩放反馈及捕食行为。
-
天敌执行捕食动画,会记录第一次捕食所用的时间,待全部捕食完成。
💡 技术要点总结
-
对象池复用:避免频繁销毁/实例化,提高性能。
-
接口抽象:通过
IHuntable保证所有猎物都有统一的交互逻辑。 -
事件驱动:使用
OnPreyClicked,避免直接耦合逻辑,方便扩展。 -
UI 控制 + 场景交互结合:按钮启动天敌 → 猎物生成 → 点击反馈 → 动画播放,完整闭环。
🚀 应用场景
该功能可以应用于:
-
虚拟仿真教学(如食物链、生态实验)。
-
游戏开发(捕猎玩法、反应力测试)。
-
交互式科普展示。
📌 总结
通过本文,我们实现了一个完整的 天敌捕食猎物实验功能,涉及 UI 控制、对象池、点击反馈、动画控制 等多个常见开发要素。这不仅能直接应用到虚拟仿真实验中,也能为其他类似交互玩法提供参考。

被折叠的 条评论
为什么被折叠?



