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

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

前言

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

导入AI导航系统

导航系统基础介绍可以看我之前的文章:【Unity游戏开发教程】零基础带你从小白到超神29——导航系统

如果你用的是unity2022及以上,可能需要先导入AI导航组件

导航烘培

配置地形为静态可行走导航

点击烘培

如果要添加动态障碍物,可以使给物体添加动态导航(NavMeshObstacle组件)

添加羊

代码简单控制羊的随机行为

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

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

    [Header("Idle")]
    public float idleTime = 5f;          // 动物休息的时间

    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;
        }
    }

    // 获取距离起点一定距离内的随机 NavMesh 位置
    protected Vector3 GetRandomNavMeshPosition(Vector3 origin, float distance)
    {
        Vector3 randomDirection = Random.insideUnitSphere * distance;  // 在球形区域内生成一个随机方向
        randomDirection += origin;  // 将随机方向与起点相加,计算出随机点的位置
        NavMeshHit navMeshHit;
        if (NavMesh.SamplePosition(randomDirection, out navMeshHit, distance, NavMesh.AllAreas))
        {
            // 如果找到了 NavMesh 上的可行走位置,返回该位置
            return navMeshHit.position;
        }
        else
        {
            // 否则递归调用函数,继续查找新的随机位置
            return GetRandomNavMeshPosition(origin, distance);
        }
    }

    // 处理空闲状态
    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.remainingDistance > navMeshAgent.stoppingDistance)  // 当还未到达目的地时循环
        {
            if (Time.time - startTime >= maxWalkTime)  // 如果超过了最大行走时间
            {
                navMeshAgent.ResetPath();  // 重置路径
                SetState(AnimalState.Idle);  // 将状态设置为 Idle
                yield break;  // 结束函数的执行
            }
            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)
    {
        UpdateState();  // 更新动物的状态
    }
}

添加羊,配置参数

效果

image.png

源码

源码在最后一期

目录
相关文章
|
10月前
|
图形学
Unity 获取鼠标位置下的UGUI或3D物体
本文总结了两种检测方法,分别用于UGUI和3D物体的检测。第一种方法`GetOverUIobj`专门用于检测鼠标悬停的UGUI元素,通过`GraphicRaycaster`实现。第二种方法`GetOverWordGameObject`则同时适用于UI和3D物体检测,利用`PhysicsRaycaster`进行射线检测。两者均返回悬停对象或null。
|
10月前
|
前端开发 图形学
unity UGUI跟随3D物体的坐标转换
在 Unity 中实现 UGUI 元素跟随 3D 物体,关键是将 3D 物体的世界坐标转换为屏幕或画布坐标。通过 Camera.WorldToScreenPoint 方法,可将 3D 物体位置映射到屏幕上,再更新 UGUI 元素的位置。代码示例展示了如何使用该方法,使 UGUI 图像跟随 3D 模型,并提供文字显示、图像和线条的显示/隐藏功能。
|
编译器 vr&ar 图形学
从零开始的unity3d入门教程(五)---- 基于Vuforia的AR项目
这是一篇Unity3D结合Vuforia实现增强现实(AR)项目的入门教程,涵盖了环境配置、Vuforia账户注册、Target数据集创建、Unity项目设置、AR程序配置、Android环境配置以及最终在手机上测试运行的全过程。
从零开始的unity3d入门教程(五)---- 基于Vuforia的AR项目
|
API 开发工具 vr&ar
PicoVR Unity SDK⭐️一、SDK下载、项目设置与程序初始配置
PicoVR Unity SDK⭐️一、SDK下载、项目设置与程序初始配置
|
图形学 开发者 UED
Unity游戏开发必备技巧:深度解析事件系统运用之道,从生命周期回调到自定义事件,打造高效逻辑与流畅交互的全方位指南
【8月更文挑战第31天】在游戏开发中,事件系统是连接游戏逻辑与用户交互的关键。Unity提供了多种机制处理事件,如MonoBehaviour生命周期回调、事件系统组件及自定义事件。本文介绍如何有效利用这些机制,包括创建自定义事件和使用Unity内置事件系统提升游戏体验。通过合理安排代码执行时机,如在Awake、Start等方法中初始化组件,以及使用委托和事件处理复杂逻辑,可以使游戏更加高效且逻辑清晰。掌握这些技巧有助于开发者更好地应对游戏开发挑战。
730 0
|
图形学 C# 开发者
Unity粒子系统全解析:从基础设置到高级编程技巧,教你轻松玩转绚丽多彩的视觉特效,打造震撼游戏画面的终极指南
【8月更文挑战第31天】粒子系统是Unity引擎的强大功能,可创建动态视觉效果,如火焰、爆炸等。本文介绍如何在Unity中使用粒子系统,并提供示例代码。首先创建粒子系统,然后调整Emission、Shape、Color over Lifetime等模块参数,实现所需效果。此外,还可通过C#脚本实现更复杂的粒子效果,增强游戏视觉冲击力和沉浸感。
1207 0
|
开发者 图形学 前端开发
绝招放送:彻底解锁Unity UI系统奥秘,五大步骤教你如何缔造令人惊叹的沉浸式游戏体验,从Canvas到动画,一步一个脚印走向大师级UI设计
【8月更文挑战第31天】随着游戏开发技术的进步,UI成为提升游戏体验的关键。本文探讨如何利用Unity的UI系统创建美观且功能丰富的界面,包括Canvas、UI元素及Event System的使用,并通过具体示例代码展示按钮点击事件及淡入淡出动画的实现过程,助力开发者打造沉浸式的游戏体验。
714 0
|
2月前
|
人工智能 运维 Kubernetes
Serverless 应用引擎 SAE:为传统应用托底,为 AI 创新加速
在容器技术持续演进与 AI 全面爆发的当下,企业既要稳健托管传统业务,又要高效落地 AI 创新,如何在复杂的基础设施与频繁的版本变化中保持敏捷、稳定与低成本,成了所有技术团队的共同挑战。阿里云 Serverless 应用引擎(SAE)正是为应对这一时代挑战而生的破局者,SAE 以“免运维、强稳定、极致降本”为核心,通过一站式的应用级托管能力,同时支撑传统应用与 AI 应用,让企业把更多精力投入到业务创新。
448 30
|
2月前
|
消息中间件 人工智能 安全
云原生进化论:加速构建 AI 应用
本文将和大家分享过去一年在支持企业构建 AI 应用过程的一些实践和思考。
515 31

热门文章

最新文章