3D游戏编程与设计作业10
3D游戏编程与设计作业10
- 环境说明
- Unity3D 导航与寻路
- Agent 和 Navmesh 练习
- Obstacle和Off-Mesh-Link练习
- P&D 过河游戏智能帮助实现
- 状态图
- AI算法
- 其他设计修改
- 演示视频
- 其他参考资料
环境说明
Unity2019.2.3f1
Win10 x64
Unity3D 导航与寻路
寻路是游戏中经常使用到的一项技术,自动寻路就是AI中的一个十分重要的分支,其算法异常复杂。Unity中提供的一套非常成熟的组件来为我们解决这一难题:Unity 导航系统允许创建给游戏角色导航的游戏世界。它将游戏场景中复杂的结构组织关系简化为带有一定信息的网格 ,在这些网格的基础上通过一系列相应的计算来实现自动寻路。
- NavMesh (Navigation Mesh) :一种数据结构,它描述了游戏对象可行走的表面。通过三角网格,计算其中任意两点之间的最短路径,用于游戏对象的导航。它是根据场景几何结构自动创建或烘焙构建的
更多有关NavMesh的介绍更参考Unity 基础寻路-NavMesh - NavMesh Agent:寻路导航组件,它创建具有寻路能力的角色
有关NavMeshAgent组件面板属性的说明可参考NavMeshAgent 寻路导航组件,NavMeshAgent以及Unity官方文档说明 - Off-Mesh Link:这一组件允许将不连接的块之间建立“传送门”。例如,跳过沟渠或围栏,或在穿过它之前打开门,都可以被描述为 Off-Mesh Link
- Nav Mesh Obstacle:这一组件允许您描述 agent 在移动时应避免的移动障碍。例如,由物理系统控制的桶或箱子就是很典型的障碍。在障碍物移动的过程中,Agent 尽力避开它,但一旦障碍物变得静止,它将在导航网格上开一个洞,以便 Agent 可以改变他们的路径以绕过它,或者静止的障碍物阻塞路径,使得 Agent 找到其他路线
有关这一组件的更多详细说明可参考NavMesh Obstacle组件及Unity官方文档说明
Agent 和 Navmesh 练习
- 创建一个新Unity3D项目
NavPractive
- 创建一个
Plane
对象:GameObject
->3D Object
->Plane
- 分别创建蓝色
Material
和粉色Material
,命名为blue
和pink
- 创建一个
Cube
对象,赋上blue
,制作成wall
预制
- 创建一个
Sphere
对象,赋上pink
,制作成target
预制
- 从
Assets Store
中下载并导入标准资源Standard Assets
- 添加AI角色:将资源包
Standard Assets/Characters/ThirdPersonCharacter/Prefabs/
下的AIThirdPersonController
拖入场景中 - 布局:
对象 | position | rotation | scale |
---|---|---|---|
Main Camera | (0,5,-10) | (30,0,0) | (1,1,1) |
AIThirdPersonController | (0,0,-4) | (0,0,0) | (1,1,1) |
target | (0,0,-4) | (0,0,0) | (0.5,0.1,0.5) |
wall0 | (0,0,0) | (0,0,0) | (3,1,1) |
wall1 | (4,0,0) | (0,0,0) | (2,1,1) |
wall2 | (-4,0,0) | (0,0,0) | (2,1,1) |
完成布局后,将
target
拖入AIThirdPersonController
对象的AICharacterControl
脚本组件的Target
插槽(这里注意是将target
对象赋值过去,而非预制)
打开
Navigation
编辑面板窗口:Window
->AI
->Navigation
配置
Plane
和target
对象的Navigation
->Object
:- 勾选
Navigation Static
- 设置
Navigation Area
为Walkable
- 勾选
配置
wall0
,wall1
和wall2
的Navigation
->Object
:- 勾选
Navigation Static
- 设置
Navigation Area
为Not Walkable
- 勾选
点击
Navigation
/Bake
面板中的Bake
,可得到如图所示的结果:
图中,水蓝色凸多边形构成的网格就是寻路算法的数据结构,agent 将用它来导航。创建脚本
PlaceTarget
,代码内容如下:using System.Collections; using System.Collections.Generic; using UnityEngine;public class PlaceTarget : MonoBehaviour {// Update is called once per framevoid Update () {if (Input.GetMouseButtonDown (0)) { Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition); RaycastHit hit; if (Physics.Raycast (ray, out hit)) { Debug.Log (hit.collider.name); if (hit.collider.name == "Plane") {this.transform.position = hit.point;}}}} }
将这一脚本挂载到
target
对象下运行:
Obstacle和Off-Mesh-Link练习
- 将当前场景另存为
scene2
- 创建一个
Cylinder
对象,Scale=(0.5,0.01,0.5)
,黄色,制作成mark
预制 - 将
mark
预制拖入场景,创建两个对象,分别命名为mark0
(position=(0,0,-1)
)和mark1
(position=(0,0,1)
),两者均勾选Navigation Static
,并设置Navigation Area
为Walkable
Creat Empty
创建一个empty对象,命名为meshlink
,并添加组件Off Mesh Link
- 将
mark0
和mark1
分别拖入meshlink
的Off Mesh Link
组件的start
和end
插槽,并设置Cost Override
为-1
- 创建一个
Cube
对象,命名为Obstacle
,并为其添加组件Nav Mesh Obstacle
,在该组件中勾选Carve
,它将自动切割导航网格
- 重新布置场景如下:
- 运行:
本部分完整项目代码见NavPractice
P&D 过河游戏智能帮助实现
程序具体要求:
- 实现状态图的自动生成
- 讲解图数据在程序中的表示方法
- 利用算法实现下一步的计算
状态图
参考Unity学习之P&D 过河游戏智能帮助实现,以游戏过程中的开始陆地的情况为例,其状态图如下:
其中,P代表牧师,D代表魔鬼,B代表船,数字代表角色个数,例如:3P3DB表示开始陆地有3个牧师和3个魔鬼,同时船也停在开始陆地。箭头指向即为状态的转化方向。
从而我们可以很容易想到这样设计算法:点击Tip
之后,分析当前场景的状态,读取可行路径上的下一个状态(可通过随机选择函数进行选择/亦可设计算法选择具有最短路径者/或直接自行指定,我这里采用的是直接指定),将这一可行的状态返回给用户。
从而我们可以设计如下数据结构:
//表示下一状态要用船运走的角色
public enum BoatAction { P, D, PP, DD, PD }
/* P:船运载一个牧师* D:船运载一个恶魔* PP:船运载两个牧师* DD:船运载两个恶魔* PD:船运载一个牧师一个恶魔*///船将要承载的船员的情况及船的状态
public struct nextPassenger
{public int boat_sign;//船在左岸(end)为-1,右岸(start)为1public BoatAction boat_action;
}
AI算法
首先获取当前状态
next.boat_sign = boat.GetBoatSign();
根据状态图,计算下一状态:
if (next.boat_sign == 1 && start_priest == 3 && start_devil == 3) {next.boat_action = BoatAction.DD; } else if (next.boat_sign == -1 && start_priest == 3 && start_devil == 1) {next.boat_action = BoatAction.D; } else if (next.boat_sign == -1 && start_priest == 3 && start_devil == 2) {next.boat_action = BoatAction.D; } else if (next.boat_sign == -1 && start_priest == 2 && start_devil == 2) {next.boat_action = BoatAction.P; } else if (next.boat_sign == 1 && start_priest == 3 && start_devil == 2) {next.boat_action = BoatAction.DD; } else if (next.boat_sign == -1 && start_priest == 3 && start_devil == 0) {next.boat_action = BoatAction.D; } else if (next.boat_sign == 1 && start_priest == 3 && start_devil == 1) {next.boat_action = BoatAction.PP; } else if (next.boat_sign == -1 && start_priest == 1 && start_devil == 1) {next.boat_action = BoatAction.PD; } else if (next.boat_sign == 1 && start_priest == 2 && start_devil == 2) {next.boat_action = BoatAction.PP; } else if (next.boat_sign == -1 && start_priest == 0 && start_devil == 2) {next.boat_action = BoatAction.D; } else if (next.boat_sign == 1 && start_priest == 0 && start_devil == 3) {next.boat_action = BoatAction.DD; } else if (next.boat_sign == -1 && start_priest == 0 && start_devil == 1) {next.boat_action = BoatAction.D; } else if (next.boat_sign == 1 && start_priest == 0 && start_devil == 2) {next.boat_action = BoatAction.DD; } else if (next.boat_sign == 1 && start_priest == 1 && start_devil == 1) {next.boat_action = BoatAction.PD; }
其他设计修改
IUserAction
:根据分析我们可以知道,用户动作列表应多一个“获取帮助”的操作//IUserAction.cs using System.Collections; using System.Collections.Generic; using UnityEngine;public interface IUserAction //用户动作“列表”接口 {void MoveBoat();//移动船void MoveRole(RoleModel role);//移动角色void Restart();//重新开始string getTips();//获取帮助 }
此外,在
Controller.cs
中,getTips()
的实现如下:public string getTips() {if (user_gui.sign != 0) return "";getNextPassenger();//计算得到下一状态值//get helping contentsstring text = "";if (next.boat_action == BoatAction.D){text = "运载一个魔鬼到对岸";}else if (next.boat_action == BoatAction.P){text = "运载一个牧师到对岸";}else if (next.boat_action == BoatAction.DD){text = "运载两个魔鬼到对岸";}else if (next.boat_action == BoatAction.PP){text = "运载两个牧师到对岸";}else if (next.boat_action == BoatAction.PD){text = "运载一个牧师、一个魔鬼到对岸";}return text; }
UserGUI
:添加Tips
的按钮及其响应处理代码if (GUI.Button(new Rect(80,10,60,30), "Tips", button_style)) {helping_text = action.getTips();if (isTip)isTip = false;elseisTip = true; } if (isTip) {GUI.Label(new Rect(10, 50, 200, 50), helping_text); }
本部分完整项目代码见PriestsAndDevils2
演示视频
前往b站观看演示视频
其他参考资料
潘老师的课程网站 第十章 游戏智能
3D游戏编程与设计作业10相关推荐
- 3D游戏编程与设计作业09
3D游戏编程与设计作业09 UGUI基础 画布 基础概念 测试渲染模式 UI布局基础 基本概念 锚点练习 UI组件与元素 基本概念 Mask练习 动画练习 富文本测试 简单血条 血条(Health B ...
- 3D游戏编程与设计作业六
改进飞碟(Hit UFO)游戏 要求 按 adapter模式 设计图修改飞碟游戏 使它同时支持物理运动与运动学(变换)运动 实现 原项目:3D编程与游戏设计作业五 仅仅对其中的一些类进行改动就能实现. ...
- 3D游戏编程与设计作业6-Unity实现打飞碟游戏改进版(Hit UFO)
改进飞碟(Hit UFO)游戏 游戏内容要求 按adapter模式设计图修改飞碟游戏 使它同时支持物理运动与运动学(交换)运动 编程实践 本次作业直接在上一次打飞碟游戏的基础上增加adapter设计模 ...
- 3D游戏编程与设计作业4-Skybox_牧师与魔鬼进阶版
基本操作演练 下载Fantasy Skybbox FREE,构建自己的游戏场景 直接在上一次作业的Priests and Devils游戏场景中添加天空盒和地形构建场景.首先在Unity Assets ...
- 3D游戏编程与设计作业四
第四次3D编程作业 本次作业源代码链接:点击此处进行跳转 一. 基本操作演练 下载Fantasy Skybox Free,构建自己的游戏场景 场景总览图: 游玩截图: 写一个简单的总结,总结游戏对象的 ...
- 3D游戏编程与设计作业三
第三次3D编程作业 本次作业源代码链接:点击此处进行跳转 1. 简答并用程序验证 游戏对象运动的本质是什么 游戏对象空间属性(坐标.旋转度.大小)的变化 请用三种以上方法,实现物体的抛物线运动 此处我 ...
- 3D游戏编程与设计作业2-太阳系-Priests and Devils
简答题 一.游戏对象运动的本质是什么? 游戏对象的本质是游戏对象每一帧的由空间属性决定的坐标随时间发生改变,其中空间属性包括对象transform属性中的空间位置Position属性.旋转角度Ro ...
- 3D游戏编程与设计作业一
游戏分类与热点探索 使用思维导图描述游戏的分类.(游戏分类方法特别多) 所使用的思维导图绘图工具为mindmaster. 结合手机游戏市场的下载量与排名等数据,结合游戏分类图,描述游戏市场的热点. 华 ...
- 3D游戏编程与设计作业4——使用skybox构建游戏场景
步骤1: 首先下载支持使用Fantacy Skybox FREE 的Unity版本(2021.3) 步骤2:打开unity store, 搜索Fantacy Skybox FREE 并进行下载 步骤3 ...
最新文章
- 使用OneFlow搭建神经网络
- 偏最小二乘法回归(Partial Least Squares Regression)
- mysql 优化rand_mysql优化--巧用rand(),with rollup,help__update2014.1.13
- Web Application Projects的一个问题
- Android Service 的重启
- sql server book
- django的admin
- 读取QQ ClientKey失败分析
- 虚拟机vmware的完全卸载
- 云服务器bat文件在哪里,云服务器bat文件在哪里
- PS关于打开图片或者直接拖入图片结果显示程序错误
- 大数据如何助力“驯服”火灾?
- 北京开始整治互联网加班了!附 955 不加班公司名单(2022版)
- 【】评价指标 mrr accuracy recall
- quadro显卡好在哪
- java.sql.SQLException: 批处理中出现错误: batch must be either executed or cleared
- 什么是银行测试业务?银行测试业务包括哪些?
- 解决页面可以左右滑动但无法上下滑动(有坑))
- [转]智慧小故事集精
- 循环卷积的时域DFT性质推导