使用NavMesh实现坦克大战游戏

游戏要求

  1. 使用unity3d的NavMesh实现AI坦克大战
  2. 敌人可以自动找到玩家,并能自动避开障碍物
  3. 玩家可以攻击敌人,敌人也可以攻击玩玩家

视频地址: https://v.youku.com/v_show/id_XMzY3NDY0NjY2NA==.html?spm=a2h0j.11185381.listitem_page1.5~A

资源准备

  1. 到官方商店下载项目Tanks! Tutorial。这里写图片描述
  2. 我们可以只用里面的坦克模型,子弹模型,以及地图(也可以自己制作地图)这里写图片描述
  3. 我们需要对地图进行烘焙,以便敌人可以AI寻路
    这里写图片描述
    我们可以将地图上的一些建筑,或者树设为障碍物,让敌人可以自动避开他们这里写图片描述

  4. 给坦克都加上NavMeshAgent组件
    这里写图片描述

代码部分

Tank.cs

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

public class Tank : MonoBehaviour {
    public float Hp { get; set; }
}

Player.cs

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

public class Player : Tank {

    // Use this for initialization
    void Start () {
        Hp = 10;
    }

    // Update is called once per frame
    void Update () {
        if (Hp <= 0)
        {
            ParticleSystem ps = Singleton<Factory>.Instance.GetTankPs();
            ps.transform.position = transform.position;
            ps.Play();
            this.gameObject.SetActive(false);
            Director.getInstance().currentSceneController.SetGameOver(true);
        }
    }
}

Enemy.cs

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

public class Enemy : Tank {

    // Use this for initialization
    private Vector3 target;
    private NavMeshAgent agent;
    private bool gameover;
    void Start () {
        Hp = 5;
        agent = GetComponent<NavMeshAgent>();
        StartCoroutine(shoot());
    }

    // Update is called once per frame
    void Update () {
        gameover = Director.getInstance().currentSceneController.GetGameOver();
        if (!gameover)
        {
            target = Director.getInstance().currentSceneController.player.transform.position;
            if (Hp <= 0)
            {
                ParticleSystem ps = Singleton<Factory>.Instance.GetTankPs();
                ps.transform.position = transform.position;
                ps.Play();
                Director.getInstance().currentSceneController.Score += 1;
                Singleton<Factory>.Instance.RecycleEnemy(this.gameObject);
            }
            else
            {
                    transform.LookAt(target);
                    agent.SetDestination(target);
            }
        }
        else
        {
            agent.velocity = Vector3.zero;
            agent.ResetPath();
        }

    }
    IEnumerator shoot()
    {//协程实现npc坦克每隔1s进行射击
        while (!gameover)
        {
            for (float i = 1; i > 0; i -= Time.deltaTime)
            {
                yield return 0;
            }
            if (Vector3.Distance(transform.position, target) < 20)
            {//和玩家坦克距离小于20,则射击
                Factory f = Singleton<Factory>.Instance;
                GameObject bullet =f.GetBullet(1);//获取子弹,传入的参数表示发射子弹的坦克类型
                bullet.transform.position = new Vector3(transform.position.x, 1.5f, transform.position.z) +
                    transform.forward * 1.5f;//设置子弹
                bullet.transform.forward = transform.forward;//设置子弹方向
                Rigidbody rb = bullet.GetComponent<Rigidbody>();
                rb.AddForce(bullet.transform.forward * 20, ForceMode.Impulse);//发射子弹
            }
        }
    }

}

Bullet.cs

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

public class Bullet : MonoBehaviour {
    public int type; // 0 表示player的子弹, 1 表示enemy的子弹,用来防止被自己人射死  // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }
    void OnCollisionEnter(Collision collision)
    {
        ParticleSystem ps = Singleton<Factory>.Instance.GetBulletPs();
        ps.transform.position = transform.position;
        if (collision.gameObject.tag == "tank" && this.type == 0)
        {
            collision.gameObject.GetComponent<Tank>().Hp -= 3;
        } else if (collision.gameObject.tag == "Player" && this.type == 1)
        {
            collision.gameObject.GetComponent<Tank>().Hp -= 2;
        }
        ps.Play();
        if (this.gameObject.activeSelf)
        {
            Singleton<Factory>.Instance.RecycleBullet(this.gameObject);
        }
    }

}

Factory.cs

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

public class Factory : MonoBehaviour {
    public GameObject player;
    public GameObject enemy;
    public GameObject bullet;
    public ParticleSystem bulletPs;
    public ParticleSystem tankPs;
    // Use this for initialization
    private Queue<GameObject> enemys = new Queue<GameObject>();
    private Queue<GameObject> bullets = new Queue<GameObject>();
    private List<ParticleSystem> bulletPses = new List<ParticleSystem>();
    private List<ParticleSystem> tankPses = new List<ParticleSystem>();
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }

    public GameObject GetPlayer()
    {
        return Instantiate<GameObject>(player);
    }
    public GameObject GetEnemy()
    {
        GameObject e = null;
        Vector3 pos;
        if (enemys.Count == 0)
        {
            e = Instantiate<GameObject>(enemy);
             pos = new Vector3(Random.Range(-90, 90), 0, Random.Range(-90, 90));
            while (Vector3.Distance(Director.getInstance().currentSceneController.player.transform.position, pos) < 20)
            {
                pos = new Vector3(Random.Range(-90, 90), 0, Random.Range(-90, 90));
            }
            e.transform.position = pos;
            return e;
        }
        e = enemys.Dequeue();
        pos = new Vector3(Random.Range(-90, 90), 0, Random.Range(-90, 90));
        while (Vector3.Distance(Director.getInstance().currentSceneController.player.transform.position, pos) < 20)
        {
            pos = new Vector3(Random.Range(-90, 90), 0, Random.Range(-90, 90));
        }
        e.transform.position = pos;
        return e;
    }
    public GameObject GetBullet(int type)
    {
        GameObject b = null;
        if (bullets.Count == 0)
        {
            b = Instantiate<GameObject>(bullet);
            b.GetComponent<Bullet>().type = type;
            return b;
        }
        b = bullets.Dequeue();
        b.GetComponent<Bullet>().type = type;
        return b;
    }

    public void RecycleEnemy(GameObject e)
    {
        e.SetActive(false);
        enemys.Enqueue(e);
    }

    public void RecycleBullet(GameObject b)
    {
        b.SetActive(false);
        bullets.Enqueue(b);
    }

    public ParticleSystem GetBulletPs()
    {
        for (int i = 0; i < bulletPses.Count; ++i)
        {
            if (!bulletPses[i].isPlaying)
            {
                return bulletPses[i];
            }
        }
        ParticleSystem p = Instantiate<ParticleSystem>(bulletPs);
        bulletPses.Add(p);
        return p;
    }
    public ParticleSystem GetTankPs()
    {
        for (int i = 0; i < tankPses.Count; ++i)
        {
            if (!tankPses[i].isPlaying)
            {
                return tankPses[i];
            }
        }
        ParticleSystem p = Instantiate<ParticleSystem>(tankPs);
        tankPses.Add(p);
        return p;
    }
}

IUserAction

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

public interface IUserAction{

    void move();
    void shoot();
}

SceneControll.cs

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

public class SceneController : MonoBehaviour, IUserAction {
    public GameObject player;
    public float moveSpeed = 10.0f;     //玩家移动速度
    public float rotateSpeed = 60.0f;   //玩家旋转速度
    public int enemyNum = 6;
    public GameObject BustedTank;
    public int Score { get; set; }
    private bool isGameOver = false;
    private Factory f;
    void Awake()
    {
        f = Singleton<Factory>.Instance;
        player = f.GetPlayer();
        Director director = Director.getInstance();
        director.currentSceneController = this;
        Score = 0;
    }
    public void move()
    {
        float h = Input.GetAxisRaw("Horizontal");   //获取玩家水平轴上的输入
        float v = Input.GetAxisRaw("Vertical"); //获取玩家在垂直方向的输入
        player.transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime * v);
        //v<0表示获取玩家向后的输入,玩家以moveSpeed的速度向后运动
        player.transform.Rotate(Vector3.up * h * rotateSpeed * Time.deltaTime);
    }

    public void shoot()
    {
       if (Input.GetKeyDown(KeyCode.Space))
        {
            GameObject b = f.GetBullet(0);
            b.transform.position = new Vector3(player.transform.position.x, 1.5f, player.transform.position.z) + player.transform.forward * 1.5f;
            b.transform.forward = player.transform.forward;//设置子弹方向
            Rigidbody rb = b.GetComponent<Rigidbody>();
            rb.AddForce(b.transform.forward * 20, ForceMode.Impulse);//发射子弹
        }
    }

    // Use this for initialization
    void Start () {
       for (int i = 0; i < enemyNum; ++i)
        {
            f.GetEnemy();
        }
    }

    // Update is called once per frame
    void Update () {
        if (!isGameOver)
        {
            Camera.main.transform.position = new Vector3(player.transform.position.x, 15, player.transform.position.z);
            move();
            shoot();
            if(Score == enemyNum)
            {
                isGameOver = true;
            }
        }

    }
    public void SetGameOver(bool gameover)
    {
        isGameOver = gameover;
        if(gameover)
        {
            GameObject b = Instantiate<GameObject>(BustedTank);
            b.transform.position = player.transform.position;
        }

    }
    public bool GetGameOver()
    {
        return isGameOver;
    }
}

Director.cs

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

public class Director : System.Object {
    private static Director _instance;
    public SceneController currentSceneController { get; set; }
    public static Director getInstance()
    {
        if (_instance == null)
        {
            _instance = new Director();
        }
        return _instance;
    }
}

UserGUI.cs

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

public class UserGUI : MonoBehaviour {

    // Use this for initialization
    GUIStyle style;
    SceneController scene;
    void Start () {
        style = new GUIStyle();
        style.normal.textColor = Color.red;
        style.fontSize = 20;
        scene = Director.getInstance().currentSceneController;
    }

    // Update is called once per frame
    void Update () {

    }
    void OnGUI()
    {
        if(scene.GetGameOver())
        {
            GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 25, 200, 50), "GameOver, Your Score is " + scene.Score.ToString(), style);
        } else
        {
            GUI.Label(new Rect(Screen.width / 2 - 50, 10, 100, 50), Director.getInstance().currentSceneController.Score.ToString(), style);
        }

    }
}

Singleton.cs

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

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    protected static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = (T)FindObjectOfType(typeof(T));
                if (instance == null)
                {
                    Debug.LogError("An instance of " + typeof(T) + " is needed in the scene, but there is none.");
                }
            }
            return instance;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_36297981/article/details/80740505