【制作100个unity游戏之24】unity制作一个3D动物AI生态系统游戏2(附项目源码)

简介: 【制作100个unity游戏之24】unity制作一个3D动物AI生态系统游戏2(附项目源码)

前言

欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第24篇中,我们将探索如何用unity制作一个3D动物AI生态系统游戏,我会附带项目源码,以便你们更好理解它。

添加捕食者

修改AnimalState,为了方便我就直接贴出全部代码吧,大概就是在原基础上添加了追逐状态和事件处理

using System.Collections;
using UnityEngine;
using UnityEngine.AI;

// 定义动物状态的枚举类型
public enum AnimalState
{
    Idle,   // 空闲状态
    Moving,  // 移动状态
    Chase  // 追逐状态
}

// 必须附加到具有 NavMeshAgent 组件的游戏对象上
[RequireComponent(typeof(NavMeshAgent))]
public class Animal : MonoBehaviour
{
    [Header("动物一次移动的距离"), SerializeField]
    private float wanderDistance = 50f;
    [Header("动物的行走速度"), SerializeField]
    private float walkSpeed = 5f;
    [Header("动物行走的最大时间"), SerializeField]
    private float maxWalkTime = 6f;

    [Header("动物休息的时间"), SerializeField]
    private float idleTime = 5f;

    [Header("奔跑速度"), SerializeField]
    private float runSpeed = 8f;

    [Header("生命值"), SerializeField]
    private int health = 10;

    protected NavMeshAgent navMeshAgent;  // 存储 NavMeshAgent 组件的引用
    protected AnimalState currentState = AnimalState.Idle;  // 存储当前动物的状态,默认为 Idle

    private void Start()
    {
        InitialiseAnimal();  // 初始化动物
    }

    // 初始化动物
    protected virtual void InitialiseAnimal()
    {
        navMeshAgent = GetComponent<NavMeshAgent>();  // 获取 NavMeshAgent 组件的引用
        navMeshAgent.speed = walkSpeed;  // 设置动物的行走速度
        currentState = AnimalState.Idle;  // 将当前状态设置为 Idle
        UpdateState();  // 更新动物的状态
    }

    // 更新动物的状态
    protected virtual void UpdateState()
    {
        switch (currentState)
        {
            case AnimalState.Idle:  // 如果当前状态是 Idle
                HandleIdleState();  // 处理空闲状态
                break;
            case AnimalState.Moving:  // 如果当前状态是 Moving
                HandleMovingState();  // 处理移动状态
                break;
            case AnimalState.Chase:  // 如果当前状态是 Chase
                HandleChaseState();  // 处理追逐状态
                break;
        }
    }

    // 获取距离起点一定距离内的随机 NavMesh 位置
    protected Vector3 GetRandomNavMeshPosition(Vector3 origin, float distance)
    {
        for (int i = 0; i < 5; i++)
        {
            Vector3 randomDirection = Random.insideUnitSphere * distance;  // 在球形区域内生成一个随机方向
            randomDirection += origin;  // 将随机方向与起点相加,计算出随机点的位置
            NavMeshHit navMeshHit;
            if (NavMesh.SamplePosition(randomDirection, out navMeshHit, distance, NavMesh.AllAreas))
            {
                // 如果找到了 NavMesh 上的可行走位置,返回该位置
                return navMeshHit.position;
            }
        }
        return origin;
    }

    protected virtual void HandleChaseState()
    {
        StopAllCoroutines();
    }


    // 处理空闲状态
    protected virtual void HandleIdleState()
    {
        StartCoroutine(WaitToMove());  // 等待一段时间后转换到移动状态
    }

    // 等待一段时间后转换到移动状态
    private IEnumerator WaitToMove()
    {
        float waitTime = Random.Range(idleTime / 2, idleTime * 2);  // 随机生成等待时间
        yield return new WaitForSeconds(waitTime);  // 等待一段时间
        Vector3 randomDestination = GetRandomNavMeshPosition(transform.position, wanderDistance);  // 获取随机位置
        navMeshAgent.SetDestination(randomDestination);  // 设置 NavMeshAgent 的目标位置
        SetState(AnimalState.Moving);  // 将状态设置为 Moving
    }

    // 处理移动状态
    protected virtual void HandleMovingState()
    {
        StartCoroutine(WaitToReachDestination());  // 等待到达目的地后转换到空闲状态
    }

    // 等待到达目的地后转换到空闲状态
    private IEnumerator WaitToReachDestination()
    {
        float startTime = Time.time;  // 记录开始移动的时间
        while (navMeshAgent.pathPending || navMeshAgent.remainingDistance > navMeshAgent.stoppingDistance && navMeshAgent.isActiveAndEnabled)  // 当还未到达目的地时循环
        {
            if (Time.time - startTime >= maxWalkTime)  // 如果超过了最大行走时间
            {
                navMeshAgent.ResetPath();  // 重置路径
                SetState(AnimalState.Idle);  // 将状态设置为 Idle
                yield break;  // 结束函数的执行
            }
            CheckChaseConditions();  // 检查是否满足追逐条件
            yield return null;  // 等待下一帧
        }
        // 到达目的地后将状态设置为 Idle
        SetState(AnimalState.Idle);
    }

    // 将动物的状态设置为指定的状态
    protected void SetState(AnimalState newState)
    {
        if (currentState == newState)  // 如果新状态与当前状态相同,直接返回
        {
            return;
        }
        currentState = newState;  // 更新当前状态
        OnStateChanged(newState);  // 触发状态改变事件
    }

    // 状态改变事件的处理函数
    protected virtual void OnStateChanged(AnimalState newState)
    {
        if (newState == AnimalState.Moving)
            navMeshAgent.speed = walkSpeed;
        if (newState == AnimalState.Chase)
            navMeshAgent.speed = runSpeed;
        UpdateState();
    }

    // 接收到伤害时的处理函数
    public virtual void RecieveDamage(int damage)
    {
        health -= damage;
        if (health <= 0)
            Die();
    }

    // 死亡时的处理函数
    protected virtual void Die()
    {
        StopAllCoroutines();
        Destroy(gameObject);
    }

    // 检查是否满足追逐条件的函数
    protected virtual void CheckChaseConditions() { }
}

新增Prey,基础Animal,定义猎物的行为脚本

using System.Collections;
using UnityEngine;
// 表示一种猎物动物
public class Prey : Animal
{
    [SerializeField, Header("检测范围")] private float detectionRange = 10f;
    [SerializeField, Header("逃离的最大距离")] private float escapeMaxDistance = 80f;
    private Predator currentPredator = null;   // 当前追逐的捕食者

    // 警报猎物,传入捕食者对象
    public void AlertPrey(Predator predator)
    {
        SetState(AnimalState.Chase);   // 设置状态为追逐
        currentPredator = predator;   // 设置当前捕食者
        StartCoroutine(RunFromPredator());   // 开始逃离捕食者
    }

    // 逃离捕食者的协程
    private IEnumerator RunFromPredator()
    {
        // 等待捕食者进入检测范围
        while (currentPredator == null || Vector3.Distance(transform.position, currentPredator.transform.position) > detectionRange)
        {
            yield return null;
        }

        // 捕食者进入检测范围,开始逃离
        while (currentPredator != null && Vector3.Distance(transform.position, currentPredator.transform.position) <= detectionRange)
        {
            RunAwayFromPredator();   // 逃离捕食者
            yield return null;
        }

        // 捕食者超出范围,向最终位置逃离并回到空闲状态 
        if (!navMeshAgent.pathPending && navMeshAgent.remainingDistance > navMeshAgent.stoppingDistance)
        {
            yield return null;
        }
        SetState(AnimalState.Idle);   // 设置状态为空闲
    }

    // 逃离捕食者的方法
    private void RunAwayFromPredator()
    {
        if (navMeshAgent != null && navMeshAgent.isActiveAndEnabled)
        {
            if (!navMeshAgent.pathPending && navMeshAgent.remainingDistance < navMeshAgent.stoppingDistance)
            {
                // 计算逃离方向,即当前位置与捕食者位置的反向向量
                Vector3 runDirection = transform.position - currentPredator.transform.position;

                // 根据逃离方向和逃离的最大距离,计算逃离的目标位置
                Vector3 escapeDestination = transform.position + runDirection.normalized * (escapeMaxDistance * 2);

                // 设置导航代理的目标位置为随机的逃离目标位置
                navMeshAgent.SetDestination(GetRandomNavMeshPosition(escapeDestination, escapeMaxDistance));
            }
        }
    }

    // 处理猎物死亡的方法
    protected override void Die()
    {
        StopAllCoroutines();
        base.Die();
    }

    // 在场景视图中绘制检测范围的方法
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(transform.position, detectionRange);
    }
}

新增Predator,同样继承Animal,定义捕食者的行为脚本

using System.Collections;
using UnityEngine;

// 表示一种捕食者动物
public class Predator : Animal
{
    [SerializeField, Header("检测范围,用于检测猎物")] private float detectionRange = 20f;
    [SerializeField, Header("追逐的最长时间")] private float maxChaseTime = 10f;
    [SerializeField, Header("咬伤伤害值")] private int biteDamage = 3;
    [SerializeField, Header("咬伤冷却时间")] private float biteCooldown = 1f;
    private Prey currentChaseTarget;   // 当前追逐的猎物

    // 检查追逐条件
    protected override void CheckChaseConditions()
    {
        if (currentChaseTarget)
            return;

        // 获取检测范围内的所有 collider,存储到数组中
        Collider[] colliders = new Collider[10];
        int numColliders = Physics.OverlapSphereNonAlloc(transform.position, detectionRange, colliders);
        for (int i = 0; i < numColliders; i++)
        {
            // 如果该 collider 绑定的游戏物体上有 Prey 组件,则该游戏物体为猎物
            Prey prey = colliders[i].GetComponent<Prey>();
            if (prey != null)
            {
                StartChase(prey);   // 开始追逐该猎物
                return;
            }
        }

        currentChaseTarget = null;
    }

    // 开始追逐指定的猎物
    private void StartChase(Prey prey)
    {
        currentChaseTarget = prey;
        SetState(AnimalState.Chase);   // 设置状态为追逐状态
    }

    // 处理追逐状态
    protected override void HandleChaseState()
    {
        if (currentChaseTarget != null)
        {
            currentChaseTarget.AlertPrey(this);   // 提醒猎物有捕食者正在追逐它
            StartCoroutine(ChasePrey());   // 开始追逐猎物的协程
        }
        else
        {
            SetState(AnimalState.Idle);   // 如果没有猎物,则回到空闲状态
        }
    }

    // 追逐猎物的协程
    private IEnumerator ChasePrey()
    {
        float startTime = Time.time;
        while (currentChaseTarget != null && Vector3.Distance(transform.position, currentChaseTarget.transform.position) > navMeshAgent.stoppingDistance)
        {
            if (Time.time - startTime >= maxChaseTime || currentChaseTarget == null)
            {
                StopChase();   // 如果超时或者目标不存在,则停止追逐
                yield break;
            }
            SetState(AnimalState.Chase);   // 设置状态为追逐状态
            navMeshAgent.SetDestination(currentChaseTarget.transform.position);   // 设置目标位置为猎物的位置
            yield return null;
        }

        // 如果猎物还存在,则对猎物造成伤害
        if (currentChaseTarget)
        {
            currentChaseTarget.RecieveDamage(biteDamage);
        }

        yield return new WaitForSeconds(biteCooldown);   // 等待咬伤冷却时间

        currentChaseTarget = null;
        HandleChaseState();   // 继续处理追逐状态
        CheckChaseConditions();   // 检查是否可以继续追逐其他猎物
    }

    // 停止追逐
    private void StopChase()
    {
        navMeshAgent.ResetPath();
        currentChaseTarget = null;
        SetState(AnimalState.Moving);   // 设置状态为移动状态
    }

    // 绘制检测范围的 gizmos
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, detectionRange);
    }
}

挂载新脚本,并配置参数,这里可以设置捕猎者速度大于动物,确保可以狩猎成功

记得给动物添加碰撞体,不然捕猎者无法检测到动物发起攻击

同样修改动物和捕猎者导航网格停止距离的值,这里我设置为3

注意:导航网格停止距离的值默认为0,如果不设置会影响动物的逃跑功能和捕猎者的咬伤功能

羊参数配置

狼参数配置

效果

image.png

动画控制

修改Animal

private Animator animator;

animator = GetComponentInChildren<Animator>();

// 状态改变事件的处理函数
protected virtual void OnStateChanged(AnimalState newState)
{
    animator?.CrossFadeInFixedTime(newState.ToString(), 0.5f);

    //...
}

看看CrossFadeInFixedTime官方文档解释,实现一个淡入淡出的动画效果

效果

image.png

源码

源码在最后一期

目录
相关文章
|
7月前
|
人工智能 监控 安全
提效40%?揭秘AI驱动的支付方式“一键接入”系统
本项目构建AI驱动的研发提效系统,通过Qwen Coder与MCP工具链协同,实现跨境支付渠道接入的自动化闭环。采用多智能体协作模式,结合结构化Prompt、任务拆解、流程管控与安全约束,显著提升研发效率与交付质量,探索大模型在复杂业务场景下的高采纳率编码实践。
763 26
提效40%?揭秘AI驱动的支付方式“一键接入”系统
|
7月前
|
人工智能 自然语言处理 前端开发
最佳实践2:用通义灵码以自然语言交互实现 AI 高考志愿填报系统
本项目旨在通过自然语言交互,结合通义千问AI模型,构建一个智能高考志愿填报系统。利用Vue3与Python,实现信息采集、AI推荐、专业详情展示及数据存储功能,支持响应式设计与Supabase数据库集成,助力考生精准择校选专业。(239字)
713 12
|
7月前
|
存储 人工智能 搜索推荐
LangGraph 记忆系统实战:反馈循环 + 动态 Prompt 让 AI 持续学习
本文介绍基于LangGraph构建的双层记忆系统,通过短期与长期记忆协同,实现AI代理的持续学习。短期记忆管理会话内上下文,长期记忆跨会话存储用户偏好与决策,结合人机协作反馈循环,动态更新提示词,使代理具备个性化响应与行为进化能力。
1393 10
LangGraph 记忆系统实战:反馈循环 + 动态 Prompt 让 AI 持续学习
|
7月前
|
机器学习/深度学习 人工智能 JSON
PHP从0到1实现 AI 智能体系统并且训练知识库资料
本文详解如何用PHP从0到1构建AI智能体,涵盖提示词设计、记忆管理、知识库集成与反馈优化四大核心训练维度,结合实战案例与系统架构,助你打造懂业务、会进化的专属AI助手。
1110 6
|
7月前
|
人工智能 JSON 安全
Claude Code插件系统:重塑AI辅助编程的工作流
Anthropic为Claude Code推出插件系统与市场,支持斜杠命令、子代理、MCP服务器等功能模块,实现工作流自动化与团队协作标准化。开发者可封装常用工具或知识为插件,一键共享复用,构建个性化AI编程环境,推动AI助手从工具迈向生态化平台。
2065 1
|
7月前
|
存储 人工智能 自然语言处理
拔俗AI产投公司档案管理系统:让数据资产 “活” 起来的智能助手
AI产投档案管理系统通过NLP、知识图谱与加密技术,实现档案智能分类、秒级检索与数据关联分析,破解传统人工管理效率低、数据孤岛难题,助力投资决策提效与数据资产化,推动AI产投数字化转型。
335 0
|
7月前
|
人工智能 算法 数据安全/隐私保护
拔俗AI多模态心理风险预警系统:用科技守护心理健康的第一道防线
AI多模态心理风险预警系统通过语音、文本、表情与行为数据,智能识别抑郁、焦虑等心理风险,实现早期干预。融合多源信息,提升准确率,广泛应用于校园、企业,助力心理健康服务从“被动响应”转向“主动预防”,为心灵筑起智能防线。(238字)
833 0
|
7月前
|
人工智能 搜索推荐 Cloud Native
拔俗AI助教系统:教师的"超级教学秘书",让每堂课都精准高效
备课到深夜、批改作业如山?阿里云原生AI助教系统,化身“超级教学秘书”,智能备课、实时学情分析、自动批改、精准辅导,为教师减负增效。让课堂从经验驱动转向数据驱动,每位学生都被看见,教育更有温度。
745 0
|
7月前
|
机器学习/深度学习 人工智能 监控
拔俗AI智能营运分析助手软件系统:企业决策的"数据军师",让经营从"拍脑袋"变"精准导航"
AI智能营运分析助手打破数据孤岛,实时整合ERP、CRM等系统数据,自动生成报表、智能预警与可视化决策建议,助力企业从“经验驱动”迈向“数据驱动”,提升决策效率,降低运营成本,精准把握市场先机。(238字)
228 0
|
7月前
|
存储 人工智能 自然语言处理
拔俗AI自动化评价分析系统:让数据说话,让决策更智能
在用户体验为核心的时代,传统评价分析面临效率低、洞察浅等痛点。本文基于阿里云AI与大数据技术,构建“数据-算法-应用”三层智能分析体系,实现多源数据实时接入、情感与主题精准识别、跨模态融合分析及实时预警,助力企业提升运营效率、加速产品迭代、优化服务质量,并已在头部电商平台成功落地,显著提升用户满意度与商业转化。
651 0