Unity3D-塔防游戏项目主要源码(游戏主控器脚本)

using UnityEngine;
using System.Collections;
using System.Collections.Generic;//引入泛型集合的命名空间
using UnityEngine.UI;//UI
using UnityEngine.SceneManagement;//场景管理器
using UnityEngine.EventSystems;//引入事件系统

namespace TowerDefenceTemplate
{
    [System.Serializable]//系统序列化
    public class Wave//敌方炮车波数
    {
        public Transform[] Waypoints;//路点数组
        public Transform SpawnPoint;//产生敌方炮车的位置 
        public GameObject
            EnemyPrefab,//敌方炮车预制体 
            ArrowPoint;//进攻方向预制体
        public Sprite EnemiesIcon;//敌方炮车的图标
        public int
            Speed,//速率
            Amount,//单波敌机总数
            Interval,//时间间隔
            Health,//血量
            Damage,//伤害值
            Award;//奖励金币
        [HideInInspector]
        public int Spawned;//已经产生的敌机数
    }

    public class GameManager : MonoBehaviour
    {
        private EventSystem _eventSystem;//实例化事件系统
        [HideInInspector]
        public UI_Controller _UI_Controller;//实例化UI_Controller
        [HideInInspector]
        public MainMenu mainMenu;
        [HideInInspector]
        public int CurrentWaveIndex;//定义当前波数的索引

        [HideInInspector]
        public List<GameObject>
            SpawnedEnemies,//声明敌方炮车游戏对象的集合
            BuiltTowers;//声明已建炮塔游戏对象的集合

        private GameObject[] TowerPoints;//定义所有炮塔的信息

        private float Timer;//计时器 

        private int BaseHealth = 100;//总血量

        private bool GameEnded;//定义游戏是否结束

        private Tower TowerToEdit;//实例化塔的对象

        [HideInInspector]
        public Dictionary<int, Bullet> BulletsPool;//定义炮弹池的键值对集合
        [HideInInspector]
        public Dictionary<int, Rocket> RocketsPool;//定义导弹池的键值对集合

        private GameObject
            RocketsPoolParent,//父导弹池
            BulletsPoolParent,//父炮弹池
            TempTower,//临时塔
            TowerToBuild;//将要建造的塔

        //UI
        [HideInInspector]
        public RectTransform BaseHealthBar;//矩形血条
        [HideInInspector]
        public Text//文本 
            MoneyMessage, //金币信息
            WaveNumber,//当前波数 
            WaveUpperNumber,//总波数
            MoneyText,//钱币
            EnemiesLeftText,//没被摧毁的敌方炮车数量
            WaveTimer,//波次倒计时
            NameText,//名字
            DamageText,//伤害值
            RangeText,//检测范围
            LevelText,//等级
            UpgradeButtonText,//升级按钮
            SellButtonText;//卖掉按钮

        [HideInInspector]
        public Image //图像
            EnemiesIcon;//敌方炮车图标
        [HideInInspector]
        public Button ToMenuButton,//回到菜单
             RestartButton;//重新开始本关卡

        [HideInInspector]
        public GameObject//定义游戏对象
            RightPanel,//右侧小面板
            PauseMenu,//
            UpgradeButton;//升级按钮

        [Header("Camera boundaries")]//摄相机范围(组件中的小标题) 
        public float X_Min;
        public float X_Max;
        public float Z_Min;
        public float Z_Max;

        public Wave[] Waves;//实例化波数
        public int Money;//定义玩家的金币

        [Header("Sounds")]//音效
        public AudioSource BuildSound;//建塔音效
        public AudioSource BaseDamageSound;//总血量减少时的音效
        public AudioSource ErrorSound;//错误提示音效
        public AudioSource ExplosionSound;//敌方炮车死亡时音效
        public AudioSource LevelUpSound;//等级提升音效
        public AudioSource LightGunShootSound;//光速炮射击音效
        public AudioSource RicochetSound1;//炮弹弹跳音1
        public AudioSource RicochetSound2;//炮弹弹跳音2
        public AudioSource RicochetSound3;//炮弹弹跳音3
        public AudioSource RocketLaunchSound;//导弹发射器射击音效
        public AudioSource RocketExplosionSound;//导弹爆炸音效
        public AudioSource FlameThrowerSound;//火焰喷射器射击音效
        public AudioSource JeepSound;//吉普车行进音效
        public AudioSource TankSound;//坦克行进音效
        public AudioSource PlaneSound;//飞机行进音效
        public AudioSource SellSound;//卖出炮塔音效
        [Header("Prefabs")]//预制体
        public GameObject RocketLauncherPrefab;//火箭发射器的预制体
        public GameObject LightGunPrefab;//光速炮的预制体
        public GameObject FlameThrowerPrefab;//火焰喷射器的预制体
        public GameObject HealthBarPrefab;//总血量预制体
        public GameObject BulletPrefab;//炮弹预制体
        public GameObject RocketPrefab;//导弹预制体
        private Camera main;
        void Start()
        {
            main = GameObject.Find("Main Camera").GetComponent<Camera>();
            if (Waves.Length > 0)//如果波数大于0
            {
                if (Waves[CurrentWaveIndex].ArrowPoint != null)//如果当前波次的进攻方向不为空
                {
                    Waves[CurrentWaveIndex].ArrowPoint.SetActive(true);//显示当前波次的进攻入口
                }
            }
            else
            {
                Debug.LogError("Set up the GameManager waves!");//设定游戏管理器中的波数
                enabled = false;
            }

            if (_UI_Controller == null)//如果UI控制器为空
            {
                Debug.LogError("UI Canvas not found! GameManager is disabled.");//提示找不到画布
                enabled = false;
                return;
            }
            TowerPoints = GameObject.FindGameObjectsWithTag("TowerPoint");//找出所有炮台的位点物体
            _eventSystem = _UI_Controller._eventSystem;//为事件系统赋值
            CreateBulletsPool();
            CreateRocketsPool();
            Timer = 5;//计时器设置为5
            PauseMenu.SetActive(false);//
            MoneyText.text = Money.ToString() + "$";//改变当前的金币数
            WaveUpperNumber.text = "Wave " + (CurrentWaveIndex + 1).ToString() + "/" + Waves.Length.ToString();//改变上面的波数显示
        }

        void OnDrawGizmos()//画敌方炮车路线图,生命周期函数,只在编辑状态下被调用
        {
            foreach (var wave in Waves)//遍历所有波次
            {
                for (int i = 1; i < wave.Waypoints.Length; i++)//遍历所有路点
                {
                    Gizmos.DrawLine(wave.Waypoints[i].position, wave.Waypoints[i - 1].position);//用直线连接路点
                }
            }
        }

        public void CreateBulletsPool()//创建炮弹池
        {
            BulletsPool = new Dictionary<int, Bullet>();//实例化新对象
            BulletsPoolParent = new GameObject("BulletsPool");//实例化父炮弹池并命名 
            for (int i = 0; i < 30 * TowerPoints.Length; i++)//遍历所有炮塔
            {
                GameObject _newbullet = Instantiate(BulletPrefab);//克隆炮弹预制体
                BulletsPool[i] = _newbullet.GetComponent<Bullet>();//获取克隆炮弹的Bullet脚本
                BulletsPool[i].gameObject.SetActive(false);//让炮弹池所在对象失活
                BulletsPool[i].transform.parent = BulletsPoolParent.transform;//把父炮弹池的位置信息赋值给炮弹池的父亲位置
            }
        }

        public void CreateRocketsPool()//创建导弹池
        {
            RocketsPool = new Dictionary<int, Rocket>();
            RocketsPoolParent = new GameObject("RocketsPool");
            for (int i = 0; i < 5 * TowerPoints.Length; i++)
            {
                GameObject _newRocket = Instantiate(RocketPrefab);
                RocketsPool[i] = _newRocket.GetComponent<Rocket>();
                RocketsPool[i].gameObject.SetActive(false);
                RocketsPool[i].transform.parent = RocketsPoolParent.transform;
            }
        }

        public void SpendMoney(int moneyAmount)//花费金币
        {
            Money -= moneyAmount;//当前钱币减去花费的金币数赋值给当前金币数
            MoneyText.text = Money.ToString() + "$";//改变UGUI显示内容
        }

        public void AddMoney(int moneyAmount)//增加金币
        {
            Money += moneyAmount;//当前钱币加上奖励的金币数赋值给当前金币数
            MoneyText.text = Money.ToString() + "$";//改变UGUI显示内容
        }

        void Update()
        {
            if (GameEnded)//如果游戏结束
            {
                return;//结束当前方法
            }
            Timer = Mathf.MoveTowards(Timer, 0, Time.deltaTime);//倒计时
            DoNavigation();//地图拖动
            ControlMoneyMessageColor(false);//控制提示钱币信息的颜色
            CountEnemiesLeft();//计数每波敌人存活的个数
            CountTimeTillWaveStart();//下一波等待时间
            CountEnemiesSpawnerTimer();//计数敌方炮车总数
            PositioningTower();//给炮塔定位
            CreatingTower();//创建塔
            if (Input.GetKeyDown("mouse 0") && !_eventSystem.IsPointerOverGameObject() && TowerToEdit != null)//如果按下鼠标左键并且事件系统的对象的指针存在,并且实例化的塔对象不为空
            {
                DeactivateTowerControl();//使塔控制器失效
            }         
        }

        void DoNavigation()//地图拖动
        {
            if (_eventSystem.IsPointerOverGameObject())
            //事件系统的对象的指针不存在
            {
                return;//结束当前方法
            }
            if (Input.GetKey("mouse 0") && TempTower == null)//如果按着鼠标左键并且临时塔为空 
            {
                Camera.main.transform.position += new Vector3(-Input.GetAxis("Mouse X"), 0, -Input.GetAxis("Mouse Y"));//移动主摄像机的位置
            }
            Camera.main.transform.position = new Vector3(Mathf.Clamp(Camera.main.transform.position.x, X_Min, X_Max), Camera.main.transform.position.y, Mathf.Clamp(Camera.main.transform.position.z, Z_Min, Z_Max));//限制相机的移动范围
            float autoScale = Input.GetAxis("Mouse ScrollWheel");
            main.orthographicSize *= 1 - autoScale;
            main.orthographicSize = Mathf.Clamp(main.orthographicSize, 10, 55); 
        }

        void CountEnemiesSpawnerTimer()//计数敌方炮车
        {
            if (Timer == 0)//如果本波次敌人产生完
            {
                WaveNumber.gameObject.SetActive(false);//当前波次设置失活
                if (Waves[CurrentWaveIndex].ArrowPoint != null)//如果当前波次的方向箭头不为空
                {
                    Waves[CurrentWaveIndex].ArrowPoint.SetActive(false);//让它失活
                }
                Timer = Waves[CurrentWaveIndex].Interval;//等待一段时间
                EnemiesIcon.overrideSprite = Waves[CurrentWaveIndex].EnemiesIcon;//修改上面敌方炮车类型的图标为当前波次的敌方炮车图标
                int _spawned = Waves[CurrentWaveIndex].Spawned;//本波次当前的敌机数量
                int _amount = Waves[CurrentWaveIndex].Amount;//奔波次应产生的敌机总数
                if (_spawned < _amount)//没产够
                {
                    SpawnEnemy();//生产敌方炮车
                }
                else if (SpawnedEnemies.Count == 0)//如果产完了本波次的所有炮车,但都已被摧毁
                {
                    JeepSound.Stop();//停止音效
                    TankSound.Stop();
                    PlaneSound.Stop();
                    Timer = 5;//重新赋值为5
                    WaveNumber.gameObject.SetActive(true);//当前波次激活
                    if (CurrentWaveIndex < (Waves.Length - 1))//如果当前波次不是最后一波
                    {
                        CurrentWaveIndex += 1;//已生产的波次索引+1
                        WaveUpperNumber.text = "Wave " + (CurrentWaveIndex + 1).ToString() + "/" + Waves.Length.ToString();//改变面板提示信息
                        Waves[CurrentWaveIndex].ArrowPoint.SetActive(true);//设置当前波次的行进箭头为激活状态
                        WaveNumber.text = "Wave " + (CurrentWaveIndex + 1).ToString();//屏幕中间提示第几波
                    }
                    else
                    {
                        YouWon();//胜利
                    }
                }
            }
        }

        void CountTimeTillWaveStart()//开始下一波次前的等待时间
        {
            if (SpawnedEnemies.Count == 0)//如果本波次的敌方炮车都已被摧毁
            {
                WaveTimer.text = "0:0" + ((int)Timer).ToString();//重置等待时间
            }
        }

        void CountEnemiesLeft()//计数存活的敌人
        {
            int Killed, EnemiesLeft;
            Killed = Waves[CurrentWaveIndex].Spawned - SpawnedEnemies.Count;//已经被摧毁的敌方炮车数量
            EnemiesLeft = Waves[CurrentWaveIndex].Amount - Killed;//没有被摧毁的炮车数量
            EnemiesLeftText.text = EnemiesLeft.ToString();//显示在画板上
        }

        void ControlMoneyMessageColor(bool ShowMessage)//控制钱币信息的颜色
        {
            Color _color = MoneyMessage.color;
            _color.a = Mathf.MoveTowards(_color.a, 0, Time.deltaTime);
            if (ShowMessage)
            {
                _color.a = 1;
            }
            MoneyMessage.color = _color;
        }

        public void ThrowMoneyMessage()//抛出钱币信息
        {
            ControlMoneyMessageColor(true);//改变钱币数颜色
            ErrorSound.Play();//播放错误音效
        }

        public void UpgradeTower()//防御塔升级
        {
            if (TowerToEdit.UpgradeCost > Money)//如果当前金币不够升级的花销
            {
                ThrowMoneyMessage();//提示不够
                return;//结束当前方法
            }
            TowerToEdit.Upgrade();//调用实例对象自身的升级方法
            SpendMoney(TowerToEdit.UpgradeCost/2);//花费金币
            if (TowerToEdit.Level == TowerToEdit.Damage.Length - 1)//如果塔对象的等级等于最后的伤害值
            {
                UpgradeButton.SetActive(false);//设置升级按钮失活
            }
            else
            {
                UpgradeButton.SetActive(true);//一直可以升级
            }
            LevelUpSound.Play();//升级音效播放
            UpdateTowerInfo();//更新防御塔的信息
        }

        public void SellTower()//卖塔
        {
            AddMoney(TowerToEdit.UpgradeCost / 2);//加钱
            Destroy(TowerToEdit.gameObject);//摧毁这个塔
            SellSound.Play();//卖出音效播放
            DeactivateTowerControl();//这个塔的控制器失效
        }

        void UpdateTowerInfo()//更新塔的信息
        {
            UpgradeButtonText.text = "Upgrade " + TowerToEdit.UpgradeCost.ToString() + "$";//UGUI面板升级花费
            SellButtonText.text = "Sell " + (TowerToEdit.Cost / 2).ToString() + "$";//卖塔价钱
            NameText.text = TowerToEdit.TowerName;//塔的名字
            LevelText.text = "Level " + (TowerToEdit.Level + 1).ToString();//塔的等级
            DamageText.text = "Damage " + TowerToEdit.Damage[TowerToEdit.Level].ToString();//伤害值
            RangeText.text = "Range " + TowerToEdit.Range[TowerToEdit.Level].ToString();//检测范围
        }

        void YouLose()//游戏失败
        {
            ToMenuButton.gameObject.SetActive(true);//返回开始菜单按钮被激活
            RestartButton.gameObject.SetActive(true);//重新开始本关卡按钮被激活
            GameEnded = true;//游戏结束
            if (_UI_Controller != null)//如果UI控制器的对象不为空
            {
                _UI_Controller.GameEnd();//执行结束方法
            }
            WaveNumber.gameObject.SetActive(true);//当前波次对象被激活
            WaveNumber.text = "You lose!";//被激活的对象内容显示你失败了
        }

        void YouWon()//游戏胜利 
        {
            ToMenuButton.gameObject.SetActive(true);//返回开始菜单按钮被激活
            RestartButton.gameObject.SetActive(true);//重新开始本关卡按钮被激活
            GameEnded = true;//游戏结束
            if (_UI_Controller != null)//如果UI控制器的对象不为空
            {
                _UI_Controller.GameEnd();//执行结束方法
            }
            WaveNumber.gameObject.SetActive(true);//当前波次对象被激活
            WaveTimer.gameObject.SetActive(false);//波次倒计时器设置失活
            WaveNumber.text = "You won!";//被激活的对象内容显示你胜利了
        }

        public void ActivateTowerControl(Tower tower)//激活炮塔控制器
        {
            RightPanel.SetActive(true);//右侧菜单激活
            TowerToEdit = tower;//控制塔的实例化对象
            if (TowerToEdit.Level == TowerToEdit.Damage.Length - 1)
            {
                UpgradeButton.SetActive(false);
            }
            else
            {
                UpgradeButton.SetActive(true);
            }
            UpdateTowerInfo();
        }

        public void DeactivateTowerControl()//失活炮塔控制器
        {
            RightPanel.SetActive(false);//右侧菜单失活
            if (TowerToEdit != null)//如果防御塔对象不为空
            {
                TowerToEdit.DetectCircle.gameObject.SetActive(false);//设置防御塔的侦测圆失活
            }
            TowerToEdit = null;//让对象为空
        }

        public void DamageBase()//玩家血量的伤害
        {
            BaseHealth -= Waves[CurrentWaveIndex].Damage;//血量减少
            BaseDamageSound.Play();//血量受到伤害的音效
            BaseHealthBar.localScale = new Vector3(BaseHealth / 100f, 1, 1);//对血条预制体实行x方向的缩放
            if (BaseHealth <= 0)//如果血量非正
            {
                YouLose();//失败方法
                BaseHealth = 100;//重新赋值为100
            }
        }

        public void BackToMenu()//返回主菜单
        {
            SceneManager.LoadScene("Menu");//加载场景Menu
        }

        public void RestartLevel()//重新开始本关卡
        {
            SceneManager.LoadScene(SceneManager.GetActiveScene().name);//按当前场景名字加载当前场景
        }

        public void BuildingModeOn(string type)//建造模型
        {
            if (type == "lightgun")//如果类型为ligthgun,并且按钮可按
            {
                TowerToBuild = LightGunPrefab;//将要建造的塔的游戏对象赋值为光速炮的预制体
            }
            if (type == "rocketlauncher")
            {
                TowerToBuild = RocketLauncherPrefab;
            }
            if (type == "flamethrower")
            {
                TowerToBuild = FlameThrowerPrefab;
            }
            TempTower = Instantiate(TowerToBuild);//克隆即将要建的塔赋值给临时塔
        }

        void PositioningTower()//确定新建塔的位置
        {
            if (TempTower == null)//如果临时塔为空
            {
                return;
            }
            RaycastHit hit;//射线碰撞体
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//鼠标位置发射一条射线
            if (Physics.Raycast(ray, out hit))//射线检测
            {
                TempTower.transform.position = hit.point;//临时炮塔放在鼠标位置
            }
        }

        void CreatingTower()//创建塔
        {
            if (TempTower == null || TowerPoints.Length == 0)//如果临时塔为空或者没有地方放置塔
            {
                return;
            }
            GameObject ClosestTowerPoint = TowerPoints[0];//设置第一个塔点为最靠近的鼠标位置的塔点
            Vector3 ClosestTowerPointPosition = TowerPoints[0].transform.position;//它的位置为第一个塔点的位置
            foreach (var towerpoint in TowerPoints)//遍历所有的塔点
            {
                if (Vector3.Distance(TempTower.transform.position, towerpoint.transform.position) < Vector3.Distance(ClosestTowerPointPosition, TempTower.transform.position))//当临时塔的位置与塔点位置的距离小于最近塔与临时塔的距离
                {
                    ClosestTowerPoint = towerpoint;//把这个塔点赋值给最近的塔点
                    ClosestTowerPointPosition = towerpoint.transform.position;//赋值相应的位置信息
                }
            }
            if (Input.GetKeyUp("mouse 0"))//当抬起鼠标左键时
            {
                if (Money < TempTower.GetComponent<Tower>().Cost)//如果当前的金币数小于建塔要花费的金币
                {
                    ThrowMoneyMessage();//提示钱不够
                    Destroy(TempTower);//销毁临时塔
                    return;//结束当前方法
                }
                if (Vector3.Distance(TempTower.transform.position, ClosestTowerPointPosition) > 5
                    || ClosestTowerPoint.transform.childCount > 0)//如果临时塔和最塔点的距离大于5或者最近塔点没有子塔点
                {
                    Destroy(TempTower);//销毁临时塔
                    ErrorSound.Play();//错误音效
                    return;//结束当前方法
                }
                SpendMoney(TempTower.GetComponent<Tower>().Cost);//建塔花费金币
                BuiltTowers.Add(TempTower);//添加到已建造的集合
                float _verticalOffset = ClosestTowerPoint.AddComponent<BoxCollider>().bounds.size.y;//获取最近塔点的盒子碰撞器的长度,作为炮台高
                Destroy(ClosestTowerPoint.GetComponent<BoxCollider>());//销毁盒子碰撞器
                SphereCollider _tempCollider = TempTower.AddComponent<SphereCollider>();//给临时塔添加球形碰撞体
                _tempCollider.radius = 5;//半径为5
                TempTower.transform.position = ClosestTowerPointPosition + new Vector3(0, _verticalOffset, 0);//把临时塔放在最近塔点位置,并以炮台的高度作为底面高
                TempTower.transform.parent = ClosestTowerPoint.transform;//把最近塔点的位置赋值给临时塔的父物体位置
                TempTower = null;//临时塔设置为空
                BuildSound.Play();//建塔音效播放
            }
        }

        public void Pause()//暂停
        {
            Time.timeScale = 0;
        }

        public void UnPause()//播放
        {
            Time.timeScale = 1;
        }

        public void Fast()//加快
        {
            Time.timeScale = 3;
        }

        void SpawnEnemy()//生产敌方炮车
        {
            Waves[CurrentWaveIndex].Spawned += 1;//已造出的炮车数量增加1
            GameObject _newEnemy = Instantiate(Waves[CurrentWaveIndex].EnemyPrefab, Waves[CurrentWaveIndex].SpawnPoint.position, Waves[CurrentWaveIndex].SpawnPoint.rotation) as GameObject;//克隆新的敌方炮车
            GameObject _healthBar = _newEnemy.GetComponent<Enemy>().HealthBar = Instantiate(HealthBarPrefab) as GameObject;//克隆炮车的血条
            _healthBar.transform.parent = _UI_Controller.transform;//定义血条的父物体为_UI_Controller
            SpawnedEnemies.Add(_newEnemy);//把新炮车添加到敌方炮车集合中
        }
    }
}

版权声明:本文为qq_23680543原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。