第八天 AI开发:NavMesh导航系统 对话系统:使用ScriptableObject存储对话数据 存档系统:JSON序列化保存数据

news2025/4/24 7:32:06

一、智能导航系统:NavMesh实战指南

1.1 导航网格基础配置

  1. 在Unity编辑器中:

    • 选择场景中的静态物体
    • 勾选Navigation Static属性
    • 打开Window > AI > Navigation窗口
  2. 烘焙参数设置:

    NavMeshBuildSettings settings = NavMesh.GetSettingsByID(0);
    settings.agentRadius = 0.5f;    // 代理半径
    settings.agentHeight = 2.0f;    // 代理高度
    settings.agentSlope = 45;       // 最大坡度
    NavMeshBuilder.BuildNavMeshAsync(settings);
    

1.2 NavMeshAgent组件控制

public class NPCMovement : MonoBehaviour
{
    public Transform target;
    private NavMeshAgent agent;
    
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        agent.speed = 3.5f;       // 移动速度
        agent.angularSpeed = 360;  // 旋转速度
        agent.stoppingDistance = 1f; // 停止距离
    }

    void Update()
    {
        if(target != null)
        {
            agent.SetDestination(target.position);
            
            // 到达目标后执行动作
            if(!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance)
            {
                OnReachDestination();
            }
        }
    }

    void OnReachDestination()
    {
        // 触发对话或执行其他逻辑
    }
}

1.3 动态障碍物处理

// 动态添加障碍物
NavMeshObstacle obstacle = gameObject.AddComponent<NavMeshObstacle>();
obstacle.shape = NavMeshObstacleShape.Capsule;
obstacle.carving = true;  // 实时更新导航网格
obstacle.size = new Vector3(1f, 2f, 1f);

二、对话系统:ScriptableObject高级应用

2.1 对话数据结构设计

[CreateAssetMenu(fileName = "New Dialogue", menuName = "Dialogue System/Dialogue")]
public class DialogueData : ScriptableObject
{
    [System.Serializable]
    public class DialogueNode
    {
        public string speakerName;
        [TextArea(3,5)] public string content;
        public DialogueOption[] options;
    }

    [System.Serializable]
    public class DialogueOption
    {
        public string optionText;
        public DialogueData nextDialogue;
    }

    public DialogueNode[] dialogueNodes;
}

2.2 对话管理器实现

public class DialogueManager : MonoBehaviour
{
    public static DialogueManager Instance;
    public Text speakerText;
    public Text contentText;
    public GameObject[] optionButtons;

    private DialogueData currentDialogue;
    private int currentNodeIndex;

    void Awake()
    {
        Instance = this;
        HideDialogue();
    }

    public void StartDialogue(DialogueData dialogue)
    {
        currentDialogue = dialogue;
        currentNodeIndex = 0;
        ShowDialogue();
        DisplayNode(currentDialogue.dialogueNodes[currentNodeIndex]);
    }

    void DisplayNode(DialogueData.DialogueNode node)
    {
        speakerText.text = node.speakerName;
        contentText.text = node.content;

        for(int i=0; i<optionButtons.Length; i++)
        {
            if(i < node.options.Length)
            {
                optionButtons[i].SetActive(true);
                optionButtons[i].GetComponentInChildren<Text>().text = node.options[i].optionText;
            }
            else
            {
                optionButtons[i].SetActive(false);
            }
        }
    }

    public void SelectOption(int optionIndex)
    {
        DialogueData.DialogueOption selectedOption = 
            currentDialogue.dialogueNodes[currentNodeIndex].options[optionIndex];
        
        if(selectedOption.nextDialogue != null)
        {
            StartDialogue(selectedOption.nextDialogue);
        }
        else
        {
            HideDialogue();
        }
    }
}

三、游戏存档:JSON序列化实战

3.1 存档数据结构设计

[System.Serializable]
public class SaveData
{
    public Vector3 playerPosition;
    public Quaternion playerRotation;
    public string currentScene;
    public Dictionary<string, bool> completedQuests = new Dictionary<string, bool>();
    
    // JSON序列化辅助方法
    public string ToJson()
    {
        return JsonUtility.ToJson(this);
    }

    public void LoadFromJson(string json)
    {
        JsonUtility.FromJsonOverwrite(json, this);
    }
}

3.2 存档管理器实现

public class SaveSystem : MonoBehaviour
{
    private static string SAVE_PATH => 
        Path.Combine(Application.persistentDataPath, "save.sav");

    public static void SaveGame()
    {
        SaveData data = new SaveData();
        
        // 收集需要保存的数据
        GameObject player = GameObject.FindGameObjectWithTag("Player");
        data.playerPosition = player.transform.position;
        data.playerRotation = player.transform.rotation;
        data.currentScene = SceneManager.GetActiveScene().name;

        // 序列化并加密存储
        string json = data.ToJson();
        byte[] encryptedData = Encrypt(json);
        File.WriteAllBytes(SAVE_PATH, encryptedData);
    }

    public static bool LoadGame()
    {
        if(File.Exists(SAVE_PATH))
        {
            byte[] encryptedData = File.ReadAllBytes(SAVE_PATH);
            string json = Decrypt(encryptedData);
            
            SaveData data = new SaveData();
            data.LoadFromJson(json);

            // 应用加载的数据
            SceneManager.LoadScene(data.currentScene);
            GameObject player = GameObject.FindGameObjectWithTag("Player");
            player.transform.position = data.playerPosition;
            player.transform.rotation = data.playerRotation;

            return true;
        }
        return false;
    }

    private static byte[] Encrypt(string data)
    {
        // 实现AES加密逻辑
        return Encoding.UTF8.GetBytes(data);
    }

    private static string Decrypt(byte[] data)
    {
        // 实现AES解密逻辑
        return Encoding.UTF8.GetString(data);
    }
}

四、综合实践:完整游戏系统搭建

4.1 第三人称角色控制升级版

public class AdvancedThirdPersonController : MonoBehaviour
{
    [Header("Movement")]
    public float moveSpeed = 5f;
    public float sprintMultiplier = 1.5f;
    public float jumpForce = 5f;
    
    [Header("Camera")]
    public CinemachineVirtualCamera followCamera;
    
    private CharacterController controller;
    private Vector3 moveDirection;
    private float verticalVelocity;
    
    void Start()
    {
        controller = GetComponent<CharacterController>();
        Cursor.lockState = CursorLockMode.Locked;
    }
    
    void Update()
    {
        HandleMovement();
        HandleCamera();
    }

    void HandleMovement()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        bool isSprinting = Input.GetKey(KeyCode.LeftShift);
        
        Vector3 forward = followCamera.transform.forward;
        Vector3 right = followCamera.transform.right;
        forward.y = 0f;
        right.y = 0f;
        
        Vector3 move = forward.normalized * vertical + right.normalized * horizontal;
        float speed = isSprinting ? moveSpeed * sprintMultiplier : moveSpeed;
        
        if(controller.isGrounded)
        {
            verticalVelocity = -0.5f;
            if(Input.GetButtonDown("Jump"))
            {
                verticalVelocity = jumpForce;
            }
        }
        else
        {
            verticalVelocity += Physics.gravity.y * Time.deltaTime;
        }
        
        move.y = verticalVelocity;
        controller.Move(move * speed * Time.deltaTime);
    }

    void HandleCamera()
    {
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");
        
        transform.Rotate(Vector3.up * mouseX * 2f);
        followCamera.transform.RotateAround(transform.position, transform.right, -mouseY * 2f);
    }
}

4.2 系统集成示例

public class GameManager : MonoBehaviour
{
    public DialogueData startDialogue;
    public Transform npcDestination;
    
    void Start()
    {
        // 加载存档
        if(SaveSystem.LoadGame())
        {
            Debug.Log("存档加载成功");
        }
        else
        {
            // 新游戏初始化
            StartCoroutine(StartNewGame());
        }
    }
    
    IEnumerator StartNewGame()
    {
        yield return new WaitForSeconds(1f);
        DialogueManager.Instance.StartDialogue(startDialogue);
        
        // NPC开始移动
        NPCMovement npc = FindObjectOfType<NPCMovement>();
        npc.target = npcDestination;
    }
    
    void OnApplicationQuit()
    {
        SaveSystem.SaveGame();
    }
}

五、调试与优化技巧

5.1 NavMesh调试工具

void OnDrawGizmos()
{
    if(Application.isPlaying)
    {
        NavMeshAgent agent = GetComponent<NavMeshAgent>();
        Gizmos.color = Color.red;
        Gizmos.DrawLine(transform.position, agent.destination);
        
        // 显示路径
        NavMeshPath path = agent.path;
        for(int i=0; i<path.corners.Length-1; i++)
        {
            Gizmos.DrawLine(path.corners[i], path.corners[i+1]);
        }
    }
}

5.2 存档系统安全增强

  1. 数据校验机制:
    public class SaveData
    {
        public string checksum;
        
        public void GenerateChecksum()
        {
            checksum = CalculateMD5(JsonUtility.ToJson(this));
        }
        
        public bool Validate()
        {
            string temp = checksum;
            checksum = "";
            string current = CalculateMD5(JsonUtility.ToJson(this));
            checksum = temp;
            return current == temp;
        }
    }
    

5.3 对话系统性能优化

  1. 对象池技术实现:
    public class DialoguePool : MonoBehaviour
    {
        public GameObject optionPrefab;
        private Queue<GameObject> pool = new Queue<GameObject>();
        
        public GameObject GetOption()
        {
            if(pool.Count > 0)
            {
                return pool.Dequeue();
            }
            return Instantiate(optionPrefab);
        }
        
        public void ReturnOption(GameObject option)
        {
            option.SetActive(false);
            pool.Enqueue(option);
        }
    }
    

结语

通过本教程的学习,我们实现了从角色控制到AI行为、从交互系统到数据管理的完整开发流程。建议进行以下扩展练习:

  1. 实现多语言对话系统
  2. 添加NPC巡逻路径功能
  3. 开发云存档系统
  4. 制作任务进度追踪界面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2341277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring AI Alibaba-02-多轮对话记忆、持久化消息记录

Spring AI Alibaba-02-多轮对话记忆、持久化消息记录 Lison <dreamlison163.com>, v1.0.0, 2025.04.19 文章目录 Spring AI Alibaba-02-多轮对话记忆、持久化消息记录多轮对话对话持久-Redis 本次主要聚焦于多轮对话功能的实现&#xff0c;后续会逐步增加更多实用内容&…

联邦元学习实现个性化物联网的框架

随着数据安全和隐私保护相关法律法规的出台&#xff0c;需要直接在中央服务器上收集和处理数据的集中式解决方案&#xff0c;对于个性化物联网而言&#xff0c;训练各种特定领域场景的人工智能模型已变得不切实际。基于此&#xff0c;中山大学&#xff0c;南洋理工大学&#xf…

实验1 温度转换与输入输出强化

知识点&#xff1a;input()/print()、分支语句、字符串处理&#xff08;教材2.1-2.2&#xff09; 实验任务&#xff1a; 1. 实现摄氏温度与华氏温度互转&#xff08;保留两位小数&#xff09; 2. 扩展功能&#xff1a;输入错误处理&#xff08;如非数字输入提示重新输入&#x…

【AI】SpringAI 第五弹:接入千帆大模型

1. 添加依赖 <dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-qianfan</artifactId> </dependency> 2. 编写 yml 配置文件 spring:ai:qianfan:api-key: 你的api-keysecret-key: 你的secr…

[Godot] C#2D平台游戏基础移动和进阶跳跃代码

本文章给大家分享一下如何实现基本的移动和进阶的跳跃&#xff08;跳跃缓冲、可变跳跃、土狼时间&#xff09;以及相对应的重力代码&#xff0c;大家可以根据自己的需要自行修改 实现效果 场景搭建 因为Godot不像Unity&#xff0c;一个节点只能绑定一个脚本&#xff0c;所以我…

【Unity笔记】Unity + OpenXR项目无法启动SteamVR的排查与解决全指南

图片为AI生成 一、前言 随着Unity在XR领域全面转向OpenXR标准&#xff0c;越来越多的开发者选择使用OpenXR来构建跨平台的VR应用。但在项目实际部署中发现&#xff1a;打包成的EXE程序无法正常启动SteamVR&#xff0c;或者SteamVR未能识别到该应用。本文将以“Unity OpenXR …

使用 rebase 轻松管理主干分支

前言 最近遇到一个技术团队的 dev 环境分支错乱&#xff0c;因为是多人合作大家各自提交信息&#xff0c;导致出现很多交叉合并记录&#xff0c;让对应 log 看起来非常混乱&#xff0c;难以阅读。 举例说明 假设我们有一个项目&#xff0c;最初develop分支有 3 个提交记录&a…

【愚公系列】《Python网络爬虫从入门到精通》063-项目实战电商数据侦探(主窗体的数据展示)

&#x1f31f;【技术大咖愚公搬代码&#xff1a;全栈专家的成长之路&#xff0c;你关注的宝藏博主在这里&#xff01;】&#x1f31f; &#x1f4e3;开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主&#xff01; &#x1f…

HttpSessionListener 的用法笔记250417

HttpSessionListener 的用法笔记250417 以下是关于 HttpSessionListener 的用法详解&#xff0c;涵盖核心方法、实现步骤、典型应用场景及注意事项&#xff0c;帮助您全面掌握会话&#xff08;Session&#xff09;生命周期的监听与管理&#xff1a; 1. 核心功能 HttpSessionLi…

火山RTC 5 转推CDN 布局合成规则

实时音视频房间&#xff0c;转推CDN&#xff0c;文档&#xff1a; 转推直播--实时音视频-火山引擎 一、转推CDN 0、前提 * 在调用该接口前&#xff0c;你需要在[控制台](https://console.volcengine.com/rtc/workplaceRTC)开启转推直播功能。<br> * 调…

Spark两种运行模式与部署

1. Spark 的运行模式 部署Spark集群就两种方式&#xff0c;单机模式与集群模式 单机模式就是为了方便开发者调试框架的运行环境。但是生产环境中&#xff0c;一般都是集群部署。 现在Spark目前支持的部署模式&#xff1a; &#xff08;1&#xff09;Local模式&#xff1a;在本地…

qt画一朵花

希望大家的生活都更加美好&#xff0c;画一朵花送给大家 效果图 void FloatingArrowPubshButton::paintEvent(QPaintEvent *event) {QPainter painter(this);painter.setRenderHints(QPainter::Antialiasing);QPen pen;pen.setColor("green");pen.setWidth(5);QBrush…

服务器上安装maven

1.安装 下载安装包 https://maven.apache.org/download.cgi 解压安装包 cd /opt/software tar -xzvf apache-maven-3.9.9-bin.tar.gz 安装目录(/opt/maven/) mv /opt/software/apache-maven-3.9.9 /opt/ 3.权限设置 把/opt/software/apache-maven-3.9.9 文件夹重命名为ma…

UOS+N 卡 + CUDA 环境下 X86 架构 DeepSeek 基于 vLLM 部署与 Dify 平台搭建指南

一、文档说明 本文档是一份关于 DeepSeek 在X86架构下通vLLM工具部署的操作指南&#xff0c;主要面向需要在UOSN卡CUDA环境中部署DeepSeek的技术人员&#xff0c;旨在指导文档使用者完成从 Python 环境升级、vLLM 库安装、模型部署到 Dify 平台搭建的全流程操作。 二、安装Pyt…

MySQL终章(8)JDBC

目录 1.前言 2.正文 2.1JDBC概念 2.2三种编码方式 2.2.1第一种 2.2.2第二种&#xff08;优化版&#xff09; 2.2.3第三种&#xff08;更优化版&#xff09; 3.小结 1.前言 哈喽大家好吖&#xff0c;今天来给大家带来Java中的JDBC的讲解&#xff0c;之前学习的都是操作…

Python 爬虫如何伪装 Referer?从随机生成到动态匹配

一、Referer 的作用与重要性 Referer 是 HTTP 请求头中的一个字段&#xff0c;用于标识请求的来源页面。它在网站的正常运行中扮演着重要角色&#xff0c;例如用于统计流量来源、防止恶意链接等。然而&#xff0c;对于爬虫来说&#xff0c;Referer 也可能成为被识别为爬虫的关…

【MySQL】表的约束(主键、唯一键、外键等约束类型详解)、表的设计

目录 1.数据库约束 1.1 约束类型 1.2 null约束 — not null 1.3 unique — 唯一约束 1.4 default — 设置默认值 1.5 primary key — 主键约束 自增主键 自增主键的局限性&#xff1a;经典面试问题&#xff08;进阶问题&#xff09; 1.6 foreign key — 外键约束 1.7…

基于STC89C52RC和8X8点阵屏、独立按键的小游戏《打砖块》

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、8X8点阵屏2、独立按键3、定时器04、定时器1 四、主函数总结 系列文章目录 前言 用的是普中A2开发板&#xff0c;外设有&#xff1a;8X8LED点阵屏、独立按键。 【单片机】STC89C52RC 【频率】12T11.0592MHz 效…

数字电子技术基础(五十)——硬件描述语言简介

目录 1 硬件描述语言简介 1.1 硬件描述语言简介 1.2 硬件编程语言的发展历史 1.3 两种硬件描述的比较 1.4 硬件描述语言的应用场景 1.5 基本程序结构 1.5.1 基本程序结构 1.5.2 基本语句和描述方法 1.5.3 仿真 1 硬件描述语言简介 1.1 硬件描述语言简介 硬件描述语…

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3核心文件common.py解读

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3核心文件common.py解读 文章目录 【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3核心文件common.py解读前言autopad函数Conv类__init__成员函数forward成员函数forward_fuse成员函数 Bottleneck类__init__成员…