【3D游戏编程与设计】十 游戏智能

坦克对战游戏 AI 设计

游戏设计要求:

从商店下载游戏:“Kawaii” Tank 或 其他坦克模型,构建 AI 对战坦克。具体要求

  1. 使用“感知-思考-行为”模型,建模 AI 坦克
  2. 场景中要放置一些障碍阻挡对手视线
  3. 坦克需要放置一个矩阵包围盒触发器,以保证 AI 坦克能使用射线探测对手方位
  4. AI 坦克必须在有目标条件下使用导航,并能绕过障碍。(失去目标时策略自己思考)
  5. 实现人机对战

项目架构

软件版本

项目使用的开发软件为Unity 3D 2020.1.4f1c1。

文件组织

在这里插入图片描述
项目的资源文件夹包括Assets和Packages两个子文件夹。其中,Packages子文件夹存储了系统自带的一些包,在这个项目中并没有特别使用。而Assets文件夹则存储了这次游戏项目使用的资源,场景,脚本,动画,预制等。其中,_TerrainAutoUpgrade,Kawaii_Tanks_Project和Standard Assets文件夹是游戏引用的一些资源包。而Scenes文件夹存储了游戏的场景(这个游戏中只有一个场景)。AI文件夹存储了游戏中使用的NavMesh导航网格。

项目地址

由于整个游戏文件夹过大,这里按照实验要求仅将Assets文件夹传到了公开的仓库上。仓库的链接为https://github.com/alphabstc/3D-AI-Tank。新建一个Unity 3D项目,按照下面的指引将仓库内容导入,将脚本拖到对应的对象上,应该可以创建出一个可以正常运行的游戏。

感知-思考-行动模型设计思路

感知

感知是 agent 接收世界信息的行为,其获取的数据将是思考的输入。在游戏中,程序是可以获得游戏世界任意信息的,设计一个“干死”玩家的算法通常是比较容易的,因此如何限制信息获取是设计不同级别 agent 的核心问题。

在游戏中,定义获取信息能力通常可以从视觉、听觉和嗅觉等渠道去考虑。在这个游戏中,坦克AI获取信息主要是通过视觉获得的。每个AI坦克获取的信息是当前摄像机所能感受到的信息,AI坦克如果可以直接看到对手坦克,则可以从视觉方面获得对手坦克的位置信息。由于有房子,树木等障碍物的阻挡,AI坦克获得的对手信息不一定完全一致,有的AI坦克甚至暂时不能发现玩家。本游戏默认一方的多个坦克都是独立行动的,不会共享对手的信息来围攻对方。这一方面也符合实际坦克对战的情况,使得坦克对战更加真实,另一方面这也避免了AI坦克“群殴”玩家,增强了游戏的可玩性。

思考

本游戏中,坦克的思考主要体现在利用NavMesh进行寻路。同时,思考也体现在坦克的进攻防御策略上。AI坦克根据感知到的对手位置与前进方向,根据当前自己的位置,前进方向与血量,做出决策是进攻(是直线前进还是迂回前进),还是防御(直接逃跑还是躲在房子等掩体后面)。

行动

每个AI坦克都会有前进,发射子弹的行为。AI坦克的子弹不可以连续不断地发射,其也需要有一个准备和反应的时间,这也符合人来操纵AI坦克的特点。同时,AI坦克的发射子弹,转向,移动等行动的切换速度不能太快,行动速度也不能太快,其需要具有渐变性,如同人操纵AI坦克一样,AI坦克的行动要有一个缓冲的时间。同样,AI坦克的子弹发射不能百发百中,其需要像真人操纵坦克一样,要允许存在一定的误差。

游戏设计实现

首先下载与导入Kawaii Tank 的 package。
在这里插入图片描述
然后导入标准素材库的如下内容:
在这里插入图片描述
然后可以观察Kawaii Tank中的场景,体验一下该场景。
在这里插入图片描述
类似地,可以制作出一个新的场景如下:
在这里插入图片描述
该场景增加了一些凹凸不平的沙地,增加了一些丘陵地形,以及一些森林,还有风场。使得场景更复杂,增加可玩性。同时也使得场景中障碍物更多,更需要玩家操作的技巧性。玩家的移动与设计都需要更加仔细地考虑与设计。同时,也调整了玩家和敌人的血量,以及调整了敌人的数量。
在这里插入图片描述
选中场景中的地形并在Navigation窗口选择Object,设置如下的属性:
在这里插入图片描述
再选择Bake,设置如下属性:
在这里插入图片描述
然后给所有的AI坦克的MainBody添加Nav Mesh Agent组件。
在这里插入图片描述
然后在Bake选项卡中点击"Bake":
在这里插入图片描述
之后可以生成导航网格图如下:
在这里插入图片描述
然后给坦克添加AI脚本,使得坦克可以追踪玩家:

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

public class PlaceTarget : MonoBehaviour
{
    public GameObject target;  //获取目标点,注意在面板中赋值
    NavMeshAgent mr;   //声明变量初始化
    void Start()
    {
        //获取到自身的NavMeshAgent组件
        mr = GetComponent<NavMeshAgent>();
    }

    // Update is called once per frame
    void Update()
    {
        //使用方法获取目标点坐标,,和前一行代码作用相同
        mr.SetDestination(target.transform.position);
    }
}

修改AI坦克发射炮弹的代码如下:

		void Update ()
		{
			if (idScript.isPlayer) {
				#if UNITY_ANDROID || UNITY_IPHONE
				Mobile_Input ();
				#else
				Desktop_Input ();
				#endif
			} else
            {
                count = count + Time.deltaTime;
                if(count > 6.0f)
                {
                    Fire();
                    count = 0;
                }
            }
		}

使得AI坦克每6s发射一次炮弹,这样也和人类玩家尽力瞄准再发射炮弹的特性更相似。避免AI坦克发射炮弹速度过快,使得玩家很快被AI坦克消灭。

人类通常需要看到进度条,才更能有决心与动力去做一件事情。为了增加游戏的可玩性,这里使得玩家的血量始终显示,而敌人(AI坦克)在被玩家击中时显示血量:

		void Update ()
		{
            // 游戏玩家一直显示血条,AI坦克受到攻击后显示血条
            if(idScript.isPlayer)
            {
                displayScript.Get_Damage(durability, initialDurability);
            }
            // Destruct
            if (idScript.isPlayer) {
				#if UNITY_ANDROID || UNITY_IPHONE
				if (CrossPlatformInputManager.GetButtonDown ("Destruct")) {
				#else
				if (Input.GetKeyDown (KeyCode.Return)) {
				#endif
					Start_Destroying ();
				}
			}
		}

同时,如果敌人血量降到0以下,则让敌人消失:

		void Start_Destroying ()
		{
            if(idScript.isPlayer == false)
            {
                this.gameObject.SetActive(false);
            }
			// Send message to all the parts.
			BroadcastMessage ("Destroy", SendMessageOptions.DontRequireReceiver);
			// Create destroyedPrefab.
			if (destroyedPrefab) {
				GameObject tempObject = Instantiate (destroyedPrefab, bodyTransform.position, Quaternion.identity) as GameObject;
				tempObject.transform.parent = bodyTransform;
			}
			// Remove the Damage text.
			if (displayScript) {
				Destroy (displayScript.gameObject);
			}
			// Destroy this script.
			Destroy (this);
		}

同时,降低攻击力与敌人攻击速度如下:

		[ Header ("Firing settings")]
		[ Tooltip ("Prefab of muzzle fire.")] public GameObject firePrefab;
		[ Tooltip ("Prefab of bullet.")] public GameObject bulletPrefab;
		[ Tooltip ("Attack force of the bullet.")] public float attackForce = 80.0f;
		[ Tooltip ("Speed of the bullet. (Meter per Second)")] public float bulletVelocity = 230.0f;
		[ Tooltip ("Offset distance for spawning the bullet. (Meter)")] public float spawnOffset = 1.0f;

游戏可以通过减少敌人的攻击力与速度,增加敌人的数量,来增加游戏的可玩性。玩家一开始面对较多敌人的夹击,丢血较多,可能只剩下一点血,让情况非常紧急。而随着玩家消灭更多的敌人,玩家受到敌人攻击频率降低,可能可以以较低的血量消灭掉所有敌人。这也增加了游戏的可玩性:
在这里插入图片描述

游戏效果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

玩家被消灭在这里插入图片描述
在这里插入图片描述

敌人的视角:
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值