当前位置:   article > 正文

基于unity3d的塔防游戏的设计与实现_unity3d塔防游戏国内外研究现状

unity3d塔防游戏国内外研究现状

博主主页:电子科技大神
博主简介:专注电子技术领域和毕业设计项目实战,远程调试部署、代码讲解、文档指导、ppt制作等技术指导。
主要内容:毕业设计(单片机项目,plc,仿真等)、简历模板、学习资料、技术咨询。



一、摘要

  塔防,即炮塔防御(Tower Defence),也统称TD ,指一类通过在地图上建造炮塔或类似建筑物,以阻止游戏中敌人进攻的策略型游戏,要有阵图。塔防受众很广,游戏模式简单而且可玩性极强,时至今日,塔防在游戏应用中依然是最热门的下载类型之一,比较经典的像《植物大战僵尸》。
  本设计使用当前流行的跨平台游戏引擎Unity3d,编程语言为C#。在游戏中,玩家可以在任何空白的地块上建造各种防御塔用于攻击不同类型的怪物,怪物会按照预定的路线前进,当进入目的地的怪物到达一定数量后游戏会失败;当进入目的地的怪物没有到达指定数量且所有怪物都被击杀后游戏通关。使用Unity3d引擎来设计让UI、游戏地图的布置大幅简化。并通过使用拓展性强的类,使得开发者能快速添加新的怪物、防御塔等,提高开发效率的同时,还降低了游戏的开发成本。
  本文主要从选题背景与意义、可行性与需求分析、游戏设计与功能实现、测试等四个方面,按照制作的流程对本次设计进行论述,对开发的思路、实现以及实现过程中的主要问题都予以描述记录。
关键词:游戏;塔防;Unity3d;PC

二、绪论

  随着智能化电子产品的普及,游戏产业已成为经济市场中增长速度最快的热点,近几年来,Android 平台游戏以及Web的网页游戏迅速发展,已经成为带动游戏产业发展的中坚力量。[10]知识经济的核心内容之一是创意经济,创意经济也是知识经济的重要表现形式,可以说,没有创意,就没有新经济。作为文化创意产业中重要的部分,游戏设计是商品生产与文化艺术创意的一次碰撞下的产物,在中国的经济发展中,游戏产业占据着十分重要的的地位。截至2015年上半年,中国游戏市场实际销售收入达到605.1亿元人民币,同比增长21.9%。[2]
国内的首款自主研发的3d游戏是在2003年,相对于美国最早的第一人称3d射击游戏《重返德军总部》晚了十年左右,虽然目前国内3d技术日益加强,但毕竟起步较晚,与国外存在着不小的差距,只有少数大型的互联网公司能够从设计理念出发并应用到开发制作上,其余大部分游戏公司和游戏开发者依旧使用国外的游戏引擎,比较出名的有:虚幻引擎、寒霜引擎以及Unity3d引擎,要想开发一款优秀的游戏,人工智能必不可少。[1]不过,随着智能化设备的快速普及,现阶段国内的游戏市场竞争很激烈,尤其是移动端,自2017年国内移动游戏市场进入黄金时代以来,游戏APP从种类到数量皆呈现爆发式增长,玩家人数与日俱增,涵盖的玩家年龄段也越来越广。

二、游戏概要设计

3.1总体概述

游戏题材:经典塔防游戏
游戏类别:策略类游戏
游戏玩法:颜色不同的圆球形状的敌人从地图的蓝色正方体中进入地图,经过预设的黑色道路行进,进入地图的粉色正方体(即目的地),玩家需要在这些敌人行进的路线附近建造各种类型的防御塔,用以攻击经过的敌人,在敌人进入粉色正方体前将敌人击杀。敌人有若干进攻波次,在每一波过后敌人都会变得更强,玩家也需要通过建造更多的防御塔或升级已有的防御塔来加强防御力。当进入粉色正方体的敌人到达一定数量后,游戏会失败。要想通关,玩家需顶住敌人的进攻,直到最后一波敌人被歼灭。

3.2游戏功能设计

  本设计的主要功能包括:游戏地图与敌人的行进路线、各种各样的敌人以及敌人的生成器、各种防御塔、资金的显示以及各种特效。

3.2.1地图与路线

  游戏地图的构成要素包括:可建造地块、敌人的行进路线、敌人生成位置、敌人进攻目的地。
在这里插入图片描述

图3.1游戏场景图
  此图显示的视角即为游戏内摄像机的位置,其中,白色方格为可建造地块,即防御塔建造的位置,黑色道路为敌人行进路线,蓝色正方体为敌人生成器,游戏内的敌人均从此处生成并以此为行进路线的起点,而红色正方体即为敌人行进的终点,当敌人到达此处时,游戏宣布失败。

四、效果图

交互界面

测试:游戏运行,显示游戏主界面,如下图。
在这里插入图片描述

图5.1游戏开始界面测试图

测试结果:游戏主界面加载成功。
测试:点击“START”按钮开始游戏,加载游戏场景。

在这里插入图片描述

图5.2加载游戏场景测试图

测试结果:游戏场景加载成功,“START”按钮交互成功。
测试:点击“EXIT”按钮退出游戏。
测试结果:成功退出游戏。

游戏过程

测试:敌人生成器生成敌人,敌人开始沿进攻路线从起点出发。

在这里插入图片描述

图5.3敌人生成及行进测试图

测试结果:敌人生成器成功生成敌人,敌人成功按路线行进。
测试:防御塔建造并攻击敌人。
在这里插入图片描述

图5.4防御塔建造并攻击敌人测试图

测试结果:防御塔成功建造并扣除相应的玩家资金,成功攻击范围内的敌人。
测试:防御塔升级及拆除。
在这里插入图片描述

图5.5防御塔升级及拆除测试图

四、游戏具体实现

4.1程序实现

4.1.1建造管理模块

类名:BuildManager
说明:控制游戏内建造防御塔、升级防御塔、拆除防御塔相关的操作。
方法:void ChangeMoney()、void Start()、void Update()、public void OnLaserTurretSelected()、public void OnMissileTurretSelected()、public void OnStandarTurretSelected()、void ShowUpgradeUI()、IEnumerator HideUpgradeUI()、public void OnUpgradeButtonDown()、public void OnDestroyButtonDown()。
分析:void ChangeMoney()记录了玩家资金的变化;void Start()用于开启与防御塔升级界面相关的状态机;void Update()用于控制防御塔的建造、升级、拆操作;public void OnLaserTurretSelected()用于判断激光塔是否被鼠标选择;public void OnMissileTurretSelected()用于判断重炮塔是否被鼠标选择;public void OnStandarTurretSelected()用于判断机枪塔是否被鼠标选择;void ShowUpgradeUI()用于显示防御塔的升级面板;IEnumerator HideUpgradeUI()用携程的方式来读取升级面板的显示与隐藏的变化并控制其隐藏;public void OnUpgradeButtonDown()鼠标监听升级按钮的点击;public void OnDestroyButtonDown()鼠标监听拆除按钮的点击。

4.1.2游戏进程管理模块

类名:GameManager
说明:控制游戏内菜单、场景切换、游戏流程切换。
方法:public void Awake()、public void Win()、public void Failded()、public void OnButtonReplay()、public void OnButtonMenu()。
分析:public void Awake()控制游戏开始时的各种操作如启动敌人生成器;public void Win()控制游戏胜利时的UI显示与文本;public void Failded()控制游戏失败时的UI显示与文本;public void OnButtonReplay()监听游戏结束时“重玩”按钮是否被点击;public void OnButtonMenu()监听游戏结束时“菜单”按钮是否被点击。

4.1.3敌人模块

类名:Enemy
说明:记录敌人各种属性,包括行进行为、承受伤害以及死亡销毁。
方法:void Start ()、void Update ()、void Move()、void ReachDestination()、void OnDestroy()、public void TakeDamage()、void Die()。
分析:void Start ()初始化敌人属性、控制敌人开始从起点移动;void Update ()更新敌人行进状态;void Move()控制敌人移动;void ReachDestination()用于销毁到达终点的敌人,同时宣布游戏失败;public void TakeDamage()控制记录敌人承受的伤害,并作出承伤行为;void Die()敌人血量为0时销毁敌人。

4.1.4敌人生成器模块

类名:EnemySpawner
说明:控制敌人在起点生成、每一波次的生成时间、生成的敌人类型和数量。
方法:void Start()、public void Stop()、IEnumerator SpawnEnemy()。
分析:void Start()用于启动敌人生成器;public void Stop()停止敌人的生成(当游戏失败时);IEnumerator SpawnEnemy()用携程控制每一波生成的敌人,判断条件以作出是否进行下一波敌人的进攻,敌人全部被消灭时宣布游戏胜利。

4.1.5游戏菜单模块

类名:GameMenu
说明:控制游戏的菜单、场景加载以及其相关事件。
方法:public void OnstartGame()、public void OnExitGame()。
分析:public void OnstartGame()用于加载游戏开始时进入游戏内的场景;public void OnExitGame()控制游戏暂停与退出。

4.1.6可建造地块模块

类名:MapCube
说明:控制游戏的菜单、场景加载以及其相关事件。
方法:void Start()、public void BuildTurret ()、public void UpgradeTurret()、public void DestroyTurret()、void OnMouseOver()、void OnMouseExit()。
分析:void Start()初始化地块;public void BuildTurret ()将建造防御塔时的特效实例化并控制其显示时间;public void UpgradeTurret()用于判断当前地块能否升级防御塔,并控制其升级行为和相关特效;public void DestroyTurret()用于判断当前地块能否拆除防御塔,并控制其拆除行为和相关特效;void OnMouseOver()监听鼠标悬停于地块上;void OnMouseExit()监听鼠标悬停离开地块。

4.1.7防御塔模块

类名:StandardTurret
说明:控制防御塔的攻击行为。
方法:void OnTriggerEnter()、void OnTriggerExit()、void Start()、void Update()、void Attack()、void UpdateEnemies()。
分析:void OnTriggerEnter()用于判断敌人是否进入防御塔的射程之内;void OnTriggerExit()用于判断敌人是否离开防御塔的射程之内;void Start()用于启动防御塔攻击计时器;void Update()控制防御塔炮口的朝向,发动攻击指令,以及决定使用何种武器系统进行攻击;void Attack()记录攻击指令;void UpdateEnemies()更新防御塔发现的敌人数量。

4.1.8防御塔数据模块

类名:TurretData
说明:记录防御塔的类型和各项属性。
方法:public enum TurretType。
分析:public enum TurretType用于记录激光塔、重炮塔、机枪塔的类型。

4.1.9游戏视角控制模块

类名:ViewController
说明:控制显示游戏场景的摄像机。
方法:void Update ()。
分析:void Update ()控制键盘上“A”、“W”、“S”、“D”四个键对应的镜头移动,赋予鼠标滚轮放大缩小的功能。

4.1.10敌人进攻波次模块

类名:Wave
说明:记录每波敌人的类型、数量和生成速度。
方法:无,非主类。
分析:记录每波敌人的类型、数量和生成速度。

4.1.8敌人行进路线模块

类名:WayPoints
说明:控制防御塔的攻击行为。
方法:void Awake()。
分析:void Awake()用于获取路径点,对每一段行进路线进行赋值操作。

4.2程序代码

程序源代码
BuildManager建造管理模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class BuildManager : MonoBehaviour {

    //各种类型炮塔的数据;
    public TurretData laserTurretData;//激光炮台数据;
    public TurretData missileTurretData;//导弹炮塔数据;
    public TurretData standardTurretData;//机枪炮塔数据;

    private TurretData selectedTurretData;//当前选择的炮塔的数据;

    private MapCube selectedMapCube;//当前选择的场景中的地块;

    public Text moneyText;//资金的文本;

    public GameObject upgradeCanvas;//升级的UI;

    public Button buttonUpgrade;

    public Animator moneyAnimator;//资金的动画;

    private Animator upgradeCanvasAnimator;//升级UI的状态机;

    private int money = 10000;//玩家拥有的资金;
    
    //玩家拥有的资金的变化;
    void ChangeMoney(int change = 0)
    {
            money += change;
        moneyText.text = "拥有资金:¥" + money;
    }

    void Start()
    {
        upgradeCanvasAnimator = upgradeCanvas.GetComponent<Animator>();
    }

    void Update()
    {
        if(Input.GetMouseButtonDown(0))//判断鼠标是否按下;
        {
            if(EventSystem.current.IsPointerOverGameObject() == false)//判断鼠标按下的的位置是否在UI上;
            {
                //新炮台的建造;
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//将鼠标点的位置转化为射线;
                RaycastHit hit;//射线预检测;
                bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));//射线碰撞检测;
                
                //判断是否发碰撞;
                if (isCollider)
                {
                    MapCube mapCube = hit.collider.GetComponent<MapCube>();//得到了点击的mapCube;
                    if (selectedTurretData != null && mapCube.turretGo == null)
                    {
                        //可以建造;
                        if (money >= selectedTurretData.cost)
                        {
                            //钱足够;
                            ChangeMoney(-selectedTurretData.cost);
                            mapCube.BuildTurret(selectedTurretData);
                        }
                        else
                        {
                            //钱不够;
                            moneyAnimator.SetTrigger("Flicker");
                        }
                    }
                    else if(mapCube.turretGo != null)
                    {
                        //TODO升级处理;
                        //if (mapCube.isUpgraded)
                        //{
                        //    //如果已升级,则禁用升级;
                        //    ShowUpgradeUI(mapCube.transform.position, true);
                        //}
                        //else
                        //{
                        //    //如果未升级,则不禁用升级;
                        //    ShowUpgradeUI(mapCube.transform.position, false);
                        //}
                        //以上代码可简化为如下代码:
                        //ShowUpgradeUI(mapCube.transform.position, mapCube.isUpgraded);

                        //判断当前地块炮台是否为选择的炮台,并且升级UI是否已经显示;
                        if (mapCube == selectedMapCube && upgradeCanvas.activeInHierarchy)
                        {
                            StartCoroutine(HideUpgradeUI());
                        }
                        else
                        {
                            ShowUpgradeUI(mapCube.transform.position, mapCube.isUpgraded);
                        }
                        selectedMapCube = mapCube;//更新所选择的地块为当前地块;
                    }
                }
            }
        }
    }

    public void OnLaserTurretSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurretData = laserTurretData;//激光塔被选择;
        }
    }

    public void OnMissileTurretSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurretData = missileTurretData;//导弹塔被选择;
        }
    }

    public void OnStandarTurretSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurretData = standardTurretData;//机枪塔被选择;
        }
    }

    //显示升级UI;
    void ShowUpgradeUI(Vector3 pos,bool isDisableUpgrade = false)//读取即将升级的炮塔位置,升级是否禁用设置初始值为不可用;
    {
        StopCoroutine("HideUpgradeUI");//暂停携程;
        upgradeCanvas.SetActive(false);//初始化
        upgradeCanvas.SetActive(true);//升级UI的显示;
        upgradeCanvas.transform.position = pos;
        buttonUpgrade.interactable = !isDisableUpgrade;//升级是否禁用,如果禁用,则不显示升级UI;
    }

    //隐藏升级UI;
    IEnumerator HideUpgradeUI()
    {
        upgradeCanvasAnimator.SetTrigger("Hide");
        //upgradeCanvas.SetActive(false);//隐藏UI的显示;
        yield return new WaitForSeconds(0.8f);
        upgradeCanvas.SetActive(false);
    }



    public void OnUpgradeButtonDown()
    {
        if (money >= selectedMapCube.turretData.costUpgraded)
        {
            ChangeMoney(-selectedMapCube.turretData.costUpgraded);
            selectedMapCube.UpgradeTurret();
        }
        else
        {
            moneyAnimator.SetTrigger("Flicker");
        }
        StartCoroutine(HideUpgradeUI());
        
        
    }

    public void OnDestroyButtonDown()
    {
        selectedMapCube.DestroyTurret();
        StartCoroutine(HideUpgradeUI());
    }

}

GameManager游戏进程管理模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour {

    public GameObject endUI;//游戏结束画面;
    public Text endMessage;//游戏结束时显示的文本;
    public static GameManager Instance;
    public EnemySpawner enemySpawner;

    public void Awake()
    {
        Instance = this;
        enemySpawner = GetComponent<EnemySpawner>();
    }

    public void Win()
    {
        endUI.SetActive(true);
        endMessage.text = "VICTORY";
    }
	
    public void Failded()
    {
        enemySpawner.Stop();
        endUI.SetActive(true);
        endMessage.text = "GAME OVER";
    }

    public void OnButtonReplay()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    public void OnButtonMenu()
    {
        SceneManager.LoadScene(0);
    }
}

Enemy敌人模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Enemy : MonoBehaviour {

    private Transform[] positions;//子路径点;
    private int index = 0;//子路径点索引数字;
    public GameObject explosionEffect;//爆炸效果节点;
    private Slider hpSlider;//敌人的血条显示UI;
    private float totalHp;//敌人的生命值上限;
    public float speed = 10;//移动速度预设值;
    public float hp = 100;//敌人的实时生命值;

	// Use this for initialization
	void Start () {
        positions = WayPoints.positions;//从WayPoints脚本中获取子路径点;
        totalHp = hp;
        hpSlider = GetComponentInChildren<Slider>();//获取所有类型的敌人的血条显示UI;
	}
	
	// Update is called once per frame
	void Update () {
        Move();
	}

    void Move()//构建敌人移动的方法;
    {
        //判断是否还有下一个子路径;
        if (index>positions.Length - 1)
        {
            return;//若没有则返回;
        }

        //设置移动路径与速度,敌人参考系本身就是世界坐标系,因此无需重新设置参考系;
        transform.Translate((positions[index].position - transform.position).normalized * Time.deltaTime * speed);

        //到达目标子路径点后搜索下一子路径点;
        if (Vector3.Distance(positions[index].position,transform.position) < 0.2f)
        {
            index++;//索引值+1;
        }

        //判断敌人是否到达目的地;
        if (index > positions.Length - 1)
        {
            ReachDestination();//调用销毁方法;
        }
    }

    void ReachDestination()//销毁敌人;
    {
        GameManager.Instance.Failded();
        GameObject.Destroy(this.gameObject);//销毁当前敌人;
    }

    void OnDestroy()//记录已被销毁的敌人;
    {
        EnemySpawner.EnemyAliveCount--;//敌人存活数量-1;
    }

    public void TakeDamage(float damage)
    {
        //先判断敌人是否还能继续承受伤害;
        if (hp <= 0)
        {
            return;
        }

        //敌人承受伤害;
        hp -= damage;
        hpSlider.value = (float)hp / totalHp;//敌人生命值的百分比;

        //判断敌人承受伤害后是否死亡;
        if (hp <= 0)
        {
            Die();//调用死亡方法;
        }
    }

    //死亡方法;
    void Die()
    {
        GameObject effect = GameObject.Instantiate(explosionEffect, transform.position, transform.rotation);//敌人的死亡特效;
        Destroy(effect, 1);
        Destroy(this.gameObject);
    }
}

EnemySpawner敌人生成器模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemySpawner : MonoBehaviour {

    public Wave[] waves;//敌人的进攻波数;
    public Transform START;//敌人初始位置;
    public float waveRate = 0.2f;//每一波的间隔;
    public static int EnemyAliveCount = 0;//敌人存活数量;
    private Coroutine coroutine;

    void Start()
    {
        coroutine = StartCoroutine(SpawnEnemy());//启动敌人生成器;
    }

    public void Stop()
    {
        StopCoroutine(coroutine);
    }
    //通过携程来创建敌人生成器;
    IEnumerator SpawnEnemy()
    {
        //设置每一波敌人;
        foreach (Wave wave in waves)
        {
            //每一波敌人的数量和位置;
            for (int i = 0; i < wave.count; i++)
            {
                GameObject.Instantiate(wave.enemyPrefab, START.position, Quaternion.identity);//生成敌人;

                EnemyAliveCount++;//敌人存活数量+1;

                //判断当前生成的敌人是否为最后一个,依次生成当前波数的所有敌人;
                if (i!=wave.count - 1)
                {
                    yield return new WaitForSeconds(wave.rate);//暂停生成间隔时间,生成当前波数的下一个敌人;
                }
            }

            //当有敌人存活时,暂停下一波的生成;
            while (EnemyAliveCount > 0)
            {
                yield return 0;//暂停;
            }

            yield return new WaitForSeconds(waveRate);//暂停每波的间隔时间,开始下一波;
        }
        while (EnemyAliveCount > 0)
        {
            yield return 0;
        }
        GameManager.Instance.Win();
    }
}

GameMenu游戏菜单模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameMenu : MonoBehaviour {

	public void OnstartGame()
    {
        SceneManager.LoadScene(1);
    }

    public void OnExitGame()
    {
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#else
        Application.Quit();
#endif
    }
}

MapCube可建造地块模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

public class MapCube : MonoBehaviour {
    [HideInInspector]
    public GameObject turretGo;//保存地块当前已有的炮台;
    [HideInInspector]
    public TurretData turretData;
    [HideInInspector]
    public bool isUpgraded = false;//默认炮台还未升级过;

    public GameObject buildEffect;//建造特效;

    private Renderer render;
    void Start()
    {
        render = GetComponent<Renderer>();
    }

    public void BuildTurret (TurretData turretData)
    {
        //特效实例化;
        this.turretData = turretData;
        isUpgraded = false;//炮台还未升级过;
        //turretGo = Instantiate(turretData.turretPrefab, transform.position, Quaternion.identity);
        turretGo = GameObject.Instantiate(turretData.turretPrefab, transform.position, Quaternion.identity);
        GameObject effect = GameObject.Instantiate(buildEffect, transform.position, Quaternion.identity);
        Destroy(effect, 1.5f);//特效持续一秒后销毁;
    }

    public void UpgradeTurret()//防御塔升级;
    {
        if(isUpgraded==true)
        {
            return;
        }
        Destroy(turretGo);
        isUpgraded = true;
        turretGo = GameObject.Instantiate(turretData.turretUpgradedPrefab, transform.position, Quaternion.identity);
        GameObject effect = GameObject.Instantiate(buildEffect, transform.position, Quaternion.identity);
        Destroy(effect, 1.5f);//特效持续一秒后销毁;
    }

    public void DestroyTurret()
    {
        Destroy(turretGo);
        isUpgraded = false;
        turretGo = null;
        turretData = null;
        GameObject effect = GameObject.Instantiate(buildEffect, transform.position, Quaternion.identity);
        Destroy(effect, 1.5f);//特效持续一秒后销毁;
    }

    //鼠标悬停方法;
    void OnMouseOver()
    {
        if (turretGo == null && EventSystem.current.IsPointerOverGameObject() == false)
        {
            render.material.color = Color.gray;
        }
    }

    //鼠标悬停离开方法;
    void OnMouseExit()
    {
        render.material.color = Color.white;
    }
}

StandardBullet武器模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StandardBullet : MonoBehaviour {

    public int damage = 10;//子弹伤害;

    public float speed = 100;//子弹飞行速度;

    public GameObject explosionEffectPrefab;//爆炸效果;

    private Transform target;//子弹攻击目标;

    //子弹当前攻击的目标;
    public void SetTarget(Transform _target)
    {
        this.target = _target;
    }

    //更新目标位置,子弹攻击目标;
    void Update()
    {
        //如果子弹追击途中失去目标;
        if(target == null)
        {
            Die();//子弹自行销毁;
            return;//不在向下执行;
        }
        transform.LookAt(target.position);
        transform.Translate(Vector3.forward * speed * Time.deltaTime);
    }

    void OnTriggerEnter(Collider col)
    {
        if(col.tag == "Enemy")
        {
            col.GetComponent<Enemy>().TakeDamage(damage);
            Die();
        }
    }

    void Die()
    {
        GameObject effect = GameObject.Instantiate(explosionEffectPrefab, transform.position, transform.rotation);
        Destroy(this.gameObject);//销毁子弹;
        Destroy(effect, 0.2f);//销毁特效;
    }
}

StandardTurret防御塔模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StandardTurret : MonoBehaviour {

    private List<GameObject> enemies = new List<GameObject>();
	void OnTriggerEnter(Collider col)
    {
        if(col.tag == "Enemy")
        {
            enemies.Add(col.gameObject);
        }
    }

    void OnTriggerExit(Collider col)
    {
        if (col.tag == "Enemy")
        {
            enemies.Remove(col.gameObject);
        }
    }

    public float attackRateTime = 0.1f;//攻击冷却;
    private float timer = 0;//计时器;
    public GameObject bulletPrefab;//子弹;
    public Transform firePosition;//开火口位置;
    public Transform head;//炮塔顶部;

    public bool useLaser = false;//是否使用激光,默认不使用;
    public float damageRate = 30;

    public LineRenderer laserRenderer;//激光系统;
    public GameObject laserEffect;//调用激光特效;

    void Start()
    {
        timer = attackRateTime;//炮塔在遇到敌人前就已准备就绪,无需再等待冷却;
    }
    void Update()
    {
        //调转炮口方向;
        if (enemies.Count > 0 && enemies[0] != null)
        {
            Vector3 targetPosition = enemies[0].transform.position;//读取当前敌人的位置;
            targetPosition.y = head.position.y;//将敌人的垂直方向位置与炮塔顶部保持一致;
            head.LookAt(targetPosition);//炮塔顶部的炮口指向当前敌人;
        }
        if (useLaser == false)
        {
            //计时器开始计时;
            timer += Time.deltaTime;

            //攻击;
            if (enemies.Count > 0 && timer >= attackRateTime)
            {
                timer = 0;
                Attack();
            }
        }

        else if (enemies.Count > 0)
        {
            //发现敌人,启动激光武器以及它的特效;
            if(laserRenderer.enabled==false)
            {
                laserRenderer.enabled = true;
                laserEffect.SetActive(true);
            }
            if (enemies[0] == null)
            {
                UpdateEnemies();
            }
            if (enemies.Count > 0)
            {
                //设置激光的起点为激光塔炮口,激光的终点为激光塔此时锁定的敌人;
                laserRenderer.SetPositions(new Vector3[] { firePosition.position, enemies[0].transform.position });
                //对敌人造成伤害;
                enemies[0].GetComponent<Enemy>().TakeDamage(damageRate * Time.deltaTime);
                //激光特效产生的位置就是敌人的位置;
                laserEffect.transform.position = enemies[0].transform.position;
                Vector3 pos = transform.position;//设置一个位置;
                pos.y = enemies[0].transform.position.y;//让这个位置的Y轴与敌人的Y轴保持一致;
                laserEffect.transform.LookAt(pos);//特效朝向这个位置;
            }
            else
            {
                //没有敌人时不启动激光武器和特效;
                laserRenderer.enabled = false;
                laserEffect.SetActive(false);
            }
        }
    }

    void Attack()
    {
        //判断敌人是否存在;
        if (enemies[0] == null)
        {
            UpdateEnemies();//更新敌人集合元素;
        }
        if(enemies.Count > 0)
        {
            GameObject bullet = GameObject.Instantiate(bulletPrefab, firePosition.position, firePosition.rotation);
            bullet.GetComponent<StandardBullet>().SetTarget(enemies[0].transform);
        }
        else
        {
            timer = attackRateTime;
        }
    }

    void UpdateEnemies()//更新敌人状态
    {
        // enemies.RemoveAll(null);//移除所有空元素;
        List<int> emptyIndex = new List<int>();
        for (int index = 0; index < enemies.Count; index++)
        {
            if(enemies [index] == null)
            {
                emptyIndex.Add(index);
            }
        }

        for (int i = 0; i < emptyIndex.Count; i++)
        {
            enemies.RemoveAt(emptyIndex[i] - i);
        }
    }
}

TurretData防御塔数据模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class TurretData {

    public GameObject turretPrefab;//炮塔预制体;
    public int cost;//炮塔花费;
    public GameObject turretUpgradedPrefab;//高级炮塔预制体;
    public int costUpgraded;//高级炮塔花费;
    public TurretType type;//炮塔类型;

    //炮塔所有类型;
    public enum TurretType
    {
        LaserTurret,//激光炮塔;
        MisslieTurret,//导弹炮塔;
        StandardTurret//机枪炮塔;
    }
    
}

ViewController游戏视角控制模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ViewController : MonoBehaviour {
    
    //设置镜头移动和视角放大缩小的速度调节系数;
    public float speedVariable = 1;//设置镜头移动速度的调节系数;
    public float mouseSpeedVariable = 1;//设置鼠标滚轮的放大缩小的调节系数; 

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

        //记录移动方向的输入值,垂直方向由鼠标滚轮的放大缩小来代替;
        float h = Input.GetAxis("Horizontal");//将水平方向的按键“A”、“D”的输入值赋予 h ;
        float m = Input.GetAxis("Mouse ScrollWheel");//将视角放大缩小的鼠标滚轮的输入值赋予 m ;
        float v = Input.GetAxis("Vertical");//将竖直方向的按键“W”、“S”的输入值赋予 v ;

        Debug.Log(m);//运行中查看 m 的具体数值,调试用;

        //设置镜头移动和视角放大缩小的速度,设置移动参考系为世界坐标系;
        transform.Translate(new Vector3(h * speedVariable, m * mouseSpeedVariable, v * speedVariable) * Time.deltaTime,Space.World);
	}
}

Wave敌人进攻波次模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//保存每一波敌人生成所需要的的属性,Wave不作为主类,因此无需继承;
[System.Serializable]//可被序列化,这样属性能显示在面板中;
public class Wave {
    public GameObject enemyPrefab;//敌人的类型;
    public int count;//敌人的数量;
    public float rate;//敌人的生成速度;

}

WayPoints敌人行进路线模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WayPoints : MonoBehaviour {

    public static Transform[] positions;//构造路径点数组;
    
    void Awake()
    {
        positions = new Transform[transform.childCount];//得到路径点孩子的个数;
        
        //对每个路径点孩子进行赋值;
        for (int i = 0; i < positions.Length; i++)
        {
            positions[i] = transform.GetChild(i);//赋值;

        }
    } 
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726
  • 727
  • 728
  • 729
  • 730
  • 731

总结

   这此的毕业设计是大学阶段的最后一个大作业。对我来说,这更像是在完成这项任务的过程中对我四年大学生涯的一种成果查验。在这之前,我对unity3d引擎的理解比较浅,这次的设计让我更加熟练去使用unity3d,对这些方面的认识也比以前更加深入。
   对于我来说,这次一个人完成整个游戏确实是很大的挑战。很多的功能、实现我在一开始都没有很好的考虑到,在制作的过程中发现很多的问题,比如说特效、动画的制作,以及携程的使用等等,常常为制作过程一个小小的BUG而费时很久,也由于是第一次独立开发项目,在整个设计的过程中对于开发进度的掌握还是太过稚嫩,导致许多创意只能停留在计划上,而根本没有去实施,过程中加强对unity3d的使用的同时,也学习C#编程语言,目前,Unity引擎的API有好几千个,并且随着Unity版本的更新,API的数量还会不断增长。对API的熟悉程度直接影响着程序的开发效率,熟悉API也成了新手进阶的必经之路。[3]为此查阅了很多的资料,也得益于老师不厌其烦的帮助。其中让我觉得受益最大的地方在于,我对游戏开发,或者说独立游戏开发的过程有了一个最初级却比较清晰的了解。
   我在这个游戏开发的过程中得到了很大的成长,很大程度的提高了自己的开发能力。但是在这过程中对于时间的掌控还是没有做到十分的充分合理,这一点我会牢牢记在心里好好改进。

目 录

目 录
1前言 1
1.1论文结构安排 1
1.2研究的目的与意义 1
2 可行性研究 3
2.1技术可行性 3
2.2经济可行性 3
2.3操作可行性 4
2.4总结 4
3 游戏概要设计 5
3.1总体概述 5
3.2游戏功能设计 5
3.2.1地图与路线 5
3.2.2敌人 6
3.2.3防御塔 6
3.2.4武器系统 8
3.2.5特效设计 8
3.2.6各种UI 9
4 游戏具体实现 11
4.1程序实现 11
4.1.1建造管理模块 11
4.1.2游戏进程管理模块 11
4.1.3敌人模块 12
4.1.4敌人生成器模块 12
4.1.5游戏菜单模块 12
4.1.6可建造地块模块 13
4.1.7防御塔模块 13
4.1.8防御塔数据模块 13
4.1.9游戏视角控制模块 14
4.1.10敌人进攻波次模块 14
4.1.8敌人行进路线模块 14
5 测试 14
5.1交互界面 14
5.2游戏过程 15
5.3测试总结 17
6 总结 18
参考文献 19
谢 辞 20
附 录 20

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/672029
推荐阅读
相关标签
  

闽ICP备14008679号