3D游戏:AI Tank
要求
- 使用“感知-思考-行为”模型,建模AI坦克
AI坦克可以在一定范围内感知玩家,并移动过来发起攻击。AI每隔3秒钟就发射一次攻击。 - 场景中要放置一些障碍阻碍对手视线
使用房屋、或者路障阻碍视线 - 坦克需要放置一个矩阵包围盒触发器,以保证 AI 坦克能使用射线探测对手方位
- AI 坦克必须在有目标条件下使用导航,并能绕过障碍。(失去目标时策略自己思考)
游戏实现:
1:导入资源:
本次实验使用了资源商店的资源Kawaii Tank
需要导入使用:
这时发现出现了报错,资源不完整,还需要导入标准资源的CrossPlatformInput:
2:导入场景:
- 使用资源包中的场景Test_field;该场景已经实现了很丰富的画面和功能;
- 有一辆可以驾驶的坦克SD_tiger;可以驾驶它对敌机发起攻击;还有一辆敌机SD_firely;
- 我们在场景中添加多点障碍物和敌机
3:设置navigation:
- 打开Navigation设置窗口:
window->AI->Navigation - 并进行如下设置:
Navigation Static : true
Walkable;
- 给每个SD_firely的MainBody添加组件 Nav Mesh Agent:
Component->Navigation->Nav Mesh Agent
- 生成导航图:
在Navigation窗口选择Bake,然后点击bake;就会在场景的目录下生成导航图:
4:脚本:
4.1 PlaceTarget
- 为SD_firely AI坦克的组件Mainbody添加脚本;用来跟踪玩家;
- 将玩家即SD_tiger的组件Mainbody添加到脚本成员Target那里;使得AI坦克可以跟踪玩家:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class PlaceTarget : MonoBehaviour
{
public GameObject target; //获取目标点,注意在面板中赋值
NavMeshAgent mr; //声明变量
// Use this for initialization
void Start()
{
//获取到自身的NavMeshAgent组件
mr = GetComponent<NavMeshAgent>();
}
// Update is called once per frame
void Update()
{
//使用属性将目标点的坐标进行传递
//mr.destination = target.transform.position;
//使用方法获取目标点坐标,,和前一行代码作用相同
mr.SetDestination(target.transform.position);
}
}
4.2:Fire_Control_CS
在原先玩家可以可控制主坦克发起攻击的基础上,为AI敌机添加了每三秒钟就发射一次炮弹的功能。
/*
void Update ()
{
if (idScript.isPlayer) {
#if UNITY_ANDROID || UNITY_IPHONE
Mobile_Input ();
#else
Desktop_Input ();
#endif
}
}
*/
void Update ()
{
if (idScript.isPlayer) {
#if UNITY_ANDROID || UNITY_IPHONE
Mobile_Input ();
#else
Desktop_Input ();
#endif
} else
{
count = count + Time.deltaTime;
if(count > 3.0f)
{
Fire();
count = 0;
}
}
}
4.3:Damge_Control_CS
- 修改玩家坦克的血条一直显示
- 当AI坦克的血量耗光时,会消失:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
#if UNITY_ANDROID || UNITY_IPHONE
using UnityStandardAssets.CrossPlatformInput;
#endif
namespace ChobiAssets.KTP
{
public class Damage_Control_CS : MonoBehaviour
{
[Header ("Damage settings")]
[Tooltip ("Durability of this tank.")] public float durability = 300.0f;
[Tooltip ("Prefab used for destroyed effects.")] public GameObject destroyedPrefab;
[Tooltip ("Prefab of Damage Text.")] public GameObject textPrefab;
[Tooltip ("Name of the Canvas used for Damage Text.")] public string canvasName = "Canvas_Texts";
Transform bodyTransform;
Damage_Display_CS displayScript;
float initialDurability;
ID_Control_CS idScript;
void Start ()
{ // Do not change to "Awake()".
initialDurability = durability;
}
void Set_DamageText ()
{
if (textPrefab == null || string.IsNullOrEmpty (canvasName) || durability == Mathf.Infinity) {
return;
}
// Instantiate Damage Text, and set it to the Canvas.
GameObject textObject = Instantiate (textPrefab, Vector3.zero, Quaternion.identity) as GameObject;
displayScript = textObject.GetComponent <Damage_Display_CS> ();
displayScript.targetTransform = bodyTransform;
GameObject canvasObject = GameObject.Find (canvasName);
if (canvasObject) {
displayScript.transform.SetParent (canvasObject.transform);
displayScript.transform.localScale = Vector3.one;
} else {
Debug.LogWarning ("Canvas for Damage Text cannot be found.");
}
}
/*
void Update ()
{
// Destruct
if (idScript.isPlayer) {
#if UNITY_ANDROID || UNITY_IPHONE
if (CrossPlatformInputManager.GetButtonDown ("Destruct")) {
#else
if (Input.GetKeyDown (KeyCode.Return)) {
#endif
Start_Destroying ();
}
}
}
*/
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 ();
}
}
}
public void Get_Damage (float damageValue)
{ // Called from "Bullet_Nav_CS".
durability -= damageValue;
if (durability > 0.0f) { // Still alive.
// Display Text
if (displayScript) {
displayScript.Get_Damage (durability, initialDurability);
}
} else { // Dead
Start_Destroying ();
}
}
/*
void Start_Destroying ()
{
// 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);
}
*/
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);
}
void Get_ID_Script (ID_Control_CS tempScript)
{
idScript = tempScript;
bodyTransform = idScript.storedTankProp.bodyTransform;
Set_DamageText ();
}
void Pause (bool isPaused)
{ // Called from "Game_Controller_CS".
this.enabled = !isPaused;
}
}
}
游戏运行:
- 场景已经保存在Assets/Scene/AI Tank,点击运行即可:
- 游戏运行视频:
Tank