Unity3D制作塔防类游戏

news2024/10/5 15:33:59

 

演示

功能简介 

制作细节详解


演示

资源包:链接:https://pan.baidu.com/s/15MMtYeKkNk5xChvCx0EckQ?pwd=d1ub 提取码:d1ub 

对应视频教学:01-开始介绍和创建工程_哔哩哔哩_bilibili  

功能简介 

 分为蓝,紫,粉,红四批敌人,每一批的敌人都比前一批的数量要多,并且速度要快,血量要多,当一批敌人死光了,才会出来第二批敌人,一共有三种炮塔每个金额为70,80,90,初始金额为1000,选择炮塔类型,点击Cube,即可以插放,再次点击时候可以选择升级或拆除,由于地图过大,可以一共上下左右键来控制地图前后左右视角,用鼠标滑轮来控制上下视角,把四批敌人杀光才可以通关成功,否则失败。

制作细节详解

Cube创建基本的地图

创建一个空物体记作"MapCube",把与地图map相关的都放进去,创建一个cube,进行ctrl+d复制,要按住ctrl进行拖拽(一米一米的移动否则将随意移动)。



创建敌人行走的路

定位两个位置,起始点/终点,然后随机连起来选择一些个MapCube删除掉,然后在空的路上边还是用Cube(Road纯黑材质)连出这条路来,该长的长,该短的短。

 把上面的敌人走的路径放到一个新建的"RoadCube"

  然后做两个Cube,命名为Start 和 End,调整好大小,材质,放置在起始点和终点正上方,(注意把这两个的Collider取消掉,就不会和下面创建的敌人做碰撞了)

控制游戏的视野(视野移动和放大缩小)

由于地图还是蛮大的,在这里添加地图上下前后移动的功能,给玩家提供便利,具体情况如下所示:

 这里创建一个"Scripts"文档,把脚本都放进去,首先创建一个"ViewController"脚本来控制视野。

代码如下:

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

public class ViewController : MonoBehaviour {

    public float speed = 1;
    public float mouseSpeed = 60;
	
	// Update is called once per frame
	void Update () {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        float mouse = Input.GetAxis("Mouse ScrollWheel");
        transform.Translate(new Vector3(h*speed, mouse*mouseSpeed, v*speed) *Time.deltaTime ,Space.World);
	}
}

敌人的路径管理

让敌人按照我们设计的路线行走,这里我们直接在拐弯的地方添加一些关键点就可以按照这些点一个个向下移动,把这些路径点都放入到"Waypoints中"

 然后在这里添加一个脚本去管理这些路径点,

代码如下

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

public class Waypoints : MonoBehaviour {


    public static Transform[] positions;
    //脚本被载入时调用(最早的执行函数)
    void Awake()
    {
//注意这里如果用transform.GetComponent这种方法,会把自身的组件也带上,所以要用下面的方式0
        positions = new Transform[transform.childCount];//先从孩子点位里获得数组大小
        for (int i = 0; i < positions.Length; i++)
        {
            positions[i] = transform.GetChild(i);
        }
    }
}

创建敌人,控制敌人的移动

这里简单的就拿做不同颜色的小球,当作不同的敌人,然后创建一个预制体"Prefab"文件夹,把不同的敌人放进文件夹中,如下所示:

 为了让敌人之间区分,给小球涂上不同的颜色,给每种敌人创建一个材质:

 控制每个敌人的移动,这里创建一个"Enemy"脚本,代码如下:

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

public class Enemy : MonoBehaviour {

    public float speed = 10;
    public float hp = 150;
    private float totalHp;
    public GameObject explosionEffect;
    private Slider hpSlider;
    private Transform[] positions;
    private int index = 0;


	// Use this for initialization
	void Start () {
        positions = Waypoints.positions;
        totalHp = hp;
        hpSlider = GetComponentInChildren<Slider>();
	}
	
	// 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++;
        }
        if (index > positions.Length - 1)
        {
            ReachDestination();
        }
    }
    //达到终点
    void ReachDestination()
    {
        GameManager.Instance.Failed();
        GameObject.Destroy(this.gameObject);
    }


    void OnDestroy()
    {
        EnemySpawner.CountEnemyAlive--;
    }

    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.5f);
        Destroy(this.gameObject);
    }

}

创建敌人孵化器管理敌人的生成

创建四种敌人,每种敌人用不同的颜色表示,每种颜色的敌人血量和移动速度是不一样的,这就需要我们创建单独的脚本来保存每一波敌人的属性,脚本记为"Wave"。

代码如下:

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

//保存每一波敌人生成所需要的属性
[System.Serializable]
public class Wave  {
    public GameObject enemyPrefab;
    public int count;
    public float rate;
}

 接着我们创建一个生成器,管理敌人一波一波的生成,这里创建一个空物体,设置为“GameManager",创建一个"Enemy Spawner"脚本拖入"GameManager"中,

可以设置每种敌人的数量和速度。

代码如下

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

public class EnemySpawner : MonoBehaviour {

    public static int CountEnemyAlive = 0;
    public Wave[] waves;
    public Transform START;
    public float waveRate = 0.2f;
    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);
                CountEnemyAlive++;
                if(i!=wave.count-1)
                    yield return new WaitForSeconds(wave.rate);
            }
            while (CountEnemyAlive > 0)
            {
                yield return 0;
            }
            yield return new WaitForSeconds(waveRate);
        }
        while (CountEnemyAlive > 0)
        {
            yield return 0;
        }
        GameManager.Instance.Win();
    }

}

创建三种炮台Prffab

首先在Prefab文件中创建三种炮台和三种炮台升级后的炮台:将第一个激光炮塔放置到0点上的Cube位置进行调整,j将资源里的材质啥的往合适的地方塞,弄好看了然后命名并设置成LaserTurret预制体,将升级后的命名为LaserTurretUpgraded,同理弄完MissileTurret、MissileTurretUpgraded、StandardTurret、StandardTurretUpgraded。

创建炮塔选择的UI

要在场景中创建炮台,首先要有UI界面,然后才能对炮台进行选择,创建一个新文档"Canvas"然后鼠标右击UI选项,选择"Toggle"开关按钮,名为LaserToggle,将Is On的去掉勾选。来代表三种炮塔的选择。它下面用Label表示介绍,用Text表示价格。ackground里的Image-Source Image设置成资源里的LaserBeamerIcon,可点击Image-Set Native Size将其图片设置为原生大小后再进行调整。将Checkmark大小和Background改成一致(用那个Alt键充满的方式),将里面的Image-Source Image改成一种被遮罩的图片(这里用的一个圆的Knob),修改颜色中的α值透明度,代表着选中后的效果。复制出两个LaserToggle,更换一下背景图片做出MissileToggle和StandardToggle。在Canvas下创建一个空物体,命名为TurretSwitch,在它上创建一ToggleGroup组件来包含这个三开关,位置摆放在Canvas居右,设置好三个炮台开关的分组,都选中后,在Toggle下的Group将TurretSwitch拉过来,这样就可以单选了。

 创建炮台的数据类

 创建炮台数据类,用来保存炮台相关数据,创建脚本TurretData:

System.Serializable]
public class TurretData
{
    public GameObject turretPrefab;//炮塔的模型
    public int cost;//价格
    public GameObject turretUpgradedPrefab;//升级的模型
    public int costUpgraded;//升级的价格
    public TurretType type;
}

public enum TurretType
{
    LaserTurret,//激光炮台
    MissileTurret,//导弹炮台
    StandardTurret,//标准炮台
}

监听炮塔选择的事件

在GameManager中再创建一个BuildManager脚本:(测试的时候可以将selectedTurredData设成public,方便在Inspector面板选择UI炮台时能看出来是否有数据)

检测鼠标点击到了哪个Cube上

在MapCube预制体上添加一个Layer图层,叫MapCube,然后将其图层选择为MapCube。

控制开始按钮1和退出时按钮的点击事件处理

金钱的管理

下边代码有设定钱了,把它显示在UI界面上,Canvas下新建个Text命名为Money,设置一下字体和居右,在BuildManager代码新加入一个方法ChangeMoney和字段moneyText,在Inspector将Money拖入至该字段。

//放置原理:点击的时候在鼠标的位置发射出来一条射线,看一下射线和哪个Node发生了碰撞,发生碰撞后要去检查下这个Node上是否为空,再做处理
//在MapCube添加一个Layer(图层),叫做MapCube,然后选择为它,这样在利用射线做检测时只检测对MapCube的碰撞。
public class BuildManager : MonoBehaviour
{
    public TurretData laserTurretData;//在Inspector面板将预制体拉入,并填写其它相关数据
    public TurretData missileTurretData;//在Inspector面板将预制体拉入,并填写其它相关数据
    public TurretData standardTurretData;//在Inspector面板将预制体拉入,并填写其它相关数据
    //表示当前选择的炮台(要建造的炮台)
    private TurretData selectedTurredData;//UI上显示和选择的炮台,写三个炮台的选择方法,通过注册三个炮台的Toggle事件来识别哪个被选择了
    private int money = 1000;
    public Text moneyText;
    public Animator moneyAnimator;

    void ChangeMoney(int change=0)
    {
        money += change;
        moneyText.text = "$ " + money;
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //如果鼠标在UI上面,则不做处理; EventSystem.current返回的是Hierarchy里EventSystem里EventSystem(Script)组件。
            //IsPointerOverGameObject表示鼠标是否按在了UI上
            if (EventSystem.current.IsPointerOverGameObject() == false)
            {
                //开发炮台的建造,首先判断鼠标点击到了哪个MapCube上,就要使用射线检测了,得到一个射线ray
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//把鼠标的点转化成射线
                RaycastHit hit;
                //Physics.Raycast来进行射线检测,(射线,RaycastHit射线检测跟什么东西做了碰撞的结果,maxDistance最大距离,layerMask和哪一层做射线检测如不指定就是和所有的层)
                bool isCollider = Physics.Raycast(ray,out hit, 1000, LayerMask.GetMask("MapCube"));//得到是否碰撞到MapCube上
                if (isCollider)
                {
                    MapCube mapCube = hit.collider.GetComponent<MapCube>();//得到点击的mapCube
                    if(mapCube.turretGo == null && selectedTurredData != null)//可以创建
                    {
                        if (money > selectedTurredData.cost)
                        {
                            money -= selectedTurretData.cost;
                            mapCube.BuildTurret(selectedTurredData);
                        }
                        else//提示钱不够
                        {
                            moneyAnimator.SetTrigger("Flicker");
                        }
                    }
                    else
                    {
                        //TODO 升级处理
                    }
                }
            }
        }
    }


    //在Canvas里的设备里有On Value Changed里添加GameManager,然后选择对应的下面方法,只要是点击设备值发生改变了,也就是is on发生改变了,都会触发
    public void OnLaserSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurredData = laserTurretData;
        }
    }
    public void OnMissileSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurredData = missileTurretData;
        }
    }
    public void OnStandardSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurredData = standardTurretData;
        }
    }
}

控制子弹跟敌人的碰撞处理,让子弹碰到敌人就爆炸

在Bullet预制体上添加一个Rigidbody刚体,取消勾选Use Gravity,在Bullet中处理触发检测OnTriggerEnter方法,定义好爆炸的特效字段explosionEffectPrefab,在Enemy中添加TakeDamage方法表示受到了伤害,Enemy脚本全部代码如下:

public class Enemy : MonoBehaviour
{
    public float speed = 10;//每秒移动10米
    public float hp = 150;
    private float totalHp;
    private Transform[] positions;
    private int index = 0;//默认的位置
    public GameObject explosionEffect;//爆炸特效
    private Slider hpSlider;//血条

    void Start()
    {
        //获取到小球行走的路径点
        positions = Waypoints.positions;
        totalHp = hp;
        hpSlider = GetComponentInChildren<Slider>();//从子物体中寻找Slider物体装配上
    }
    void Update()
    {
        Move();
    }

    void Move()
    {
        //if (index > positions.Length - 1) return;//当到达最后一个位置
        //(目标位置 - 当前位置)得到一个向量.单位化每次移动1,取得单位向量之后再做计算
        transform.Translate((positions[index].position - transform.position).normalized * Time.deltaTime * speed);
        //判断有没有到达目标位置,取得两个点位置是否小于一定距离
        if( Vector3.Distance( positions[index].position,transform.position) < 0.2f)
        {
            index++;
        }
        if (index > positions.Length - 1)//当到达最后一个位置
        {
            ReachDestination();
        }
    }
    //到达目的地,游戏就失败了
    void ReachDestination()
    {
        GameManager.Instance.Faild();
        GameObject.Destroy(this.gameObject);
    }
    //被打掉销毁
    private void OnDestroy()
    {
        EnemySpawner.CountEnemyAlive--;
    }
    //表示受到了伤害
    public void TakeDamage(float damage)
    {
        if (hp <= 0) return;
        hp -= damage;
        hpSlider.value = (float)hp / totalHp;//hpSlider.value是一个0~1的值,所以它以百分比来计算得到
        if(hp <= 0)
        {
            Die();
        }
    }
    void Die()
    {
        GameObject effect = GameObject.Instantiate(explosionEffect, transform.position, transform.rotation);
        Destroy(effect, 1.5f);
        Destroy(this.gameObject);
    }
}

添加爆炸特效

修改检测碰撞方式,创建一个Particle System命名为ExplosionEffect特效,然后将其拉入到Bullet的Bullet字段中,可以将Bullet下Rigidbody下的Collision Detection(碰撞检测)改成Continuous(连续的)或者Continuous Dynamic(动态的)这样对高速移动的物体检测更加准确,把Bullet图层设为Turret。

敌人添加血条显示

创建一个Canvas,把Canvas里的Render Mode修改为World Space,就可以调节画布的大小了,下面创建一个Slider,它不需要交互,所以把Slider(Script)下面Intractable(可交互的)取消勾选,Canvas调整成和Slider差不多大,将Slider下的Handle Slide Area(手柄滑动区)移除,Background取消勾选,Slider是依靠Slider(Script)下面Value来控制条长的,调整Fill Area长度与默认充满,可以把Fill(前置背景)的Image中Color改为绿色,然后把Canvas整体移到各个Enemy预制体下面,调整Canvas的位置和大小。

创建炮塔升级的UI按钮

创建一个Canvas命名为UpgradeCanvas,设置成World Space,下面创建两个Button,命名为ButtonUpgrade和ButtonDestroy,里面Image(Script)去掉,下面Text是升级和拆除,弄好后可拖到在炮塔下面进行编辑,方便定位位置和调整大小,完成后再挪出来。

控制升级面板显示

在BuildManager里进行控制,添加两个引用,public GameObject upgradeCanvas 和 public Button buttonUpgrade,在Inspector面板注册上,脚本里继续添加ShowUpgradeUI 和 HideUpgradeUI 显示/隐藏按钮方法,添加 OnUpgradeButtonDown 和 OnDestroyButtonDown 升级/拆按钮按下方法,然后在Inspector面板把后两个方法注册给两个按钮。

BuildManager脚本全部代码如下:

/放置原理:点击的时候在鼠标的位置发射出来一条射线,看一下射线和哪个Node发生了碰撞,发生碰撞后要去检查下这个Node上是否为空,再做处理
//在MapCube添加一个Layer(图层),叫做MapCube,然后选择为它,这样在利用射线做检测时只检测对MapCube的碰撞。

public class BuildManager : MonoBehaviour
{
    public TurretData laserTurretData;
    public TurretData missileTurretData;
    public TurretData standardTurretData;
    //表示当前选择的炮台(要建造的炮台)
    private TurretData selectedTurredData;//UI上显示和选择的炮台
    public Text moneyText;
    public Animator moneyAnimator;
    private int money = 1000;
    public GameObject upgradeCanvas;//升级UI画板
    public Button buttonUpgrade;
    private MapCube selectedMapCube;//3D场景中选择的炮台
    private Animator upgradeCanvasAnimator;//升级UI显示隐藏的动画转换状态机

    void ChangeMoney(int change=0)
    {
        money += change;
        moneyText.text = "$ " + money;
    }
    private void Start()
    {
        upgradeCanvasAnimator = upgradeCanvas.GetComponent<Animator>();//得到状态机
    }
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //如果鼠标在UI上面,则不做处理; EventSystem.current得到的是EventSystem模块里EventSystem那个组件。
            if (EventSystem.current.IsPointerOverGameObject() == false)//IsPointerOverGameObject表示鼠标是否按在了UI上
            {
                //开发炮台的建造,首先判断鼠标点击到了哪个MapCube上,就要使用射线检测了,得到一个射线ray
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//把鼠标的点转化成射线
                RaycastHit hit;
                //Physics.Raycast来进行射线检测,(射线,RaycastHit射线检测跟什么东西做了碰撞的结果,maxDistance最大距离,layerMask和哪一层做射线检测如不指定就是和所有的层)
                bool isCollider = Physics.Raycast(ray,out hit, 1000, LayerMask.GetMask("MapCube"));//得到是否碰撞到MapCube上
                if (isCollider)
                {
                    MapCube mapCube = hit.collider.GetComponent<MapCube>();//得到点击的mapCube
                    if(mapCube.turretGo == null && selectedTurredData != null)//可以创建
                    {
                        if (money > selectedTurredData.cost)
                        {
                            ChangeMoney(-selectedTurredData.cost);
                            mapCube.BuildTurret(selectedTurredData);
                        }
                        else//提示钱不够
                        {
                            //TODO
                            moneyAnimator.SetTrigger("Flicker");
                        }
                    }
                    else if(mapCube.turretGo != null)//如果上边有炮台,那么判断是否做升级处理
                    {
                        if(mapCube.turretGo == selectedMapCube && upgradeCanvas.activeInHierarchy)//如果第二次点击此炮台了并且UI的激活属性是true
                        {
                            StartCoroutine("HideUpgradeUI");//将UI隐藏,用协程的方式
                        }
                        else
                        {
                            //否则显示升级/拆除UI面板,第二个参数的bool值与是否有炮台判断相符,所以不再if判断直接传即可
                            ShowUpgradeUI(mapCube.transform.position, mapCube.isUpgraded);
                        }
                        selectedMapCube = mapCube;//把点击的炮台赋给点击的炮台
                    }
                }
            }
        }
    }
    //在Canvas里的设备里有On Value Changed里添加GameManager,然后选择对应的下面方法,只要是点击设备值发生改变了,就会触发
    public void OnLaserSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurredData = laserTurretData;
        }
    }
    public void OnMissileSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurredData = missileTurretData;
        }
    }
    public void OnStandardSelected(bool isOn)
    {
        if (isOn)
        {
            selectedTurredData = standardTurretData;
        }
    }
    void ShowUpgradeUI(Vector3 pos, bool isDisableUpgrade=false)
    {
        StopCoroutine("HideUpgradeUI");//搜索下面的HideUpgradeUI协程方法有没有在运行,有的话先给暂停掉,没有也不会影响。
        //设置画布禁用,为的是切换到新的炮台时候,状态机会初始化一下,能有一个激活弹出UI的效果,这里调用状态机里show的时候,可能HideUpgradeUI还在播放,
        //为了防止冲突故在上面加上一个暂停的协程方法。
        upgradeCanvas.SetActive(false);
        upgradeCanvas.SetActive(true);//设置画布显示
        pos.y = pos.y + 4;
        upgradeCanvas.transform.position = pos;//设置画布位置
        buttonUpgrade.interactable = !isDisableUpgrade;//开启或者禁用升级按钮
    }
    IEnumerator HideUpgradeUI()
    {
        upgradeCanvasAnimator.SetTrigger("Hide");
        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");//把UI隐藏掉
    }
    public void OnDestroyButtonDown()//按下拆除触发的方法
    {
        selectedMapCube.DestroyTurret();
        StartCoroutine("HideUpgradeUI");
    }
}

给升级面板添加的动画。
让升级和拆按钮”弹”出来,”缩”回去,创建一个Upgrade文件夹,右键UpgradeCanvas创建动画,创建面积从小到大的show动画,和从大到小的hide动画,存在Upgrade里,做完后会自动有一个UpgradeCanvas状态机,设置从Entry指向show,show—>hide,注意取消各自的Loop Time(从Project里点击动画能看到此菜单),show—>hide的连接线取消勾选Has Exit Time ,左上角添加一个Trigger命名为Hide来取触发这个连接。
在BuildManager里需要用状态机来控制,创建字段 private Animator upgradeCanvasAnimator ,从Start方法中由之前定义好的upgradeCanvas物体直接获得此控制器,然后在隐藏方法HideUpgradeUI里用upgradeCanvasAnimator.SetTrigger(“Hide”) 来设置播放隐藏动画,隐藏之后再禁用这个upgradeGcanvas,因为播放需要一下等待时间,所以需将HideUpgradeUI改成协程方法。
为了每一次点击不同炮台都会有show效果,在ShowUpgradeUI方法里先暂停一下HideUpgradeUI协程,再禁用一下upgradeCanvas再开启。
(暂停协程方法使用时是去搜索该方法名,有就暂停,没有也不会受影响)

设计游戏结束时候的UI界面

在主Canvas下创建一个空物体命名为End,让其与Canvas画布大小保持一致(用Alt填充方法),在它下面创建一个Image命名为Bg,修改颜色和透明度,创建一个Text命名为Message居中,创建两个Button命名为ButtonRetry、ButtonMenu,修改这俩下面的Text内容为重玩和菜单,选择End创建动画show,做一个背景慢慢显示,Text和俩Button从外进来的效果,

控制失败界面的显示

在GameManager下创建GameManager脚本,添加Win胜利和Failed失败方法,创建字段public GameObject endUI 和 public Text endMessage ,把End和它下面的Message拖入,创建字段public static GameManager Instance ,做成单例模式方便外界调用,在Enemy脚本就可以调用到GameManager脚本方法了,在Enemy脚本ReachDestination到达终点方法中调用GameManager的Faild方法。
因为EnemySpawner和GameManager在同一物体GameManager上的,所以在GameManager中控制敌人的生成,
GameManager脚本全部代码如下:
public class GameManager : MonoBehaviour
{
    public GameObject endUI;//结束的UI画面
    public Text endMessage;
    public static GameManager Instance;
    private EnemySpawner enemySpawner;

    private void Awake()
    {
        Instance = this;
        enemySpawner = GetComponent<EnemySpawner>();
    }
    public void Win()
    {
        endUI.SetActive(true);//设置为true后,它下面的Animator动画已经勾上了,会自动播放
        endMessage.text = "胜 利";
    }
    public void Faild()
    {
        enemySpawner.Stop();//停止生成敌人。
        endUI.SetActive(true);
        endMessage.text = "失 败";
    }
    public void OnButtonRetry()
    {
        endUI.SetActive(false);
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);//重新加载当前场景
    }
    public void OnButtonMenu()
    {
        SceneManager.LoadScene("MainMenu");
    }
}

添加胜利界面和重玩,菜单按钮的点击

游戏胜利的条件是所有的敌人都生成了并且都死亡了,在EnemySpawner脚本中的SpawnEnemy协程方法中写一个对敌人数量的while循环,如果还有,就截止到此返回,如果没了就往下执行GameManager.Instance.Win() 方法。

GameManager脚本中,添加OnButtonRetry、OnButtonMenu 重玩和菜单方法,并注册到End对应的两个Button下。
可以给两个Button添加一下之前的放大的动画,在其组件下添加Animator,然后Controller处选择ButtonUpgrade动画,注意Button(Script)处的Transition选择Animation。

开发菜单场景

Project里新建一个场景命名为MainMenu,做一个Plane当做地面,Scale面积设置大一些能遮住视野,把StandardTurret拿过来,把里面有用的零件拿出来即可,弄一个空物体命名为Pviot,把炮塔拖到它下面,调整Pviot位置,创建一个Canvas的Button,因为要放在Pviot物体上随着运动所以要使用World Space,调整Button字体、大小等等,放在炮台下面再调整好位置,做成开始游戏,调好一个了再复制一个Canvas出来旋转一下做成退出,可以给俩Button添加之前的button动画(调成Animation和添加Animator设置Controller),可以在Button里添加一个Shadow组件,设置Effect Distance的值。 调整相机位置。

控制开始按钮和退出按钮的点击事件处理

控制开始按钮和退出按钮的点击事件处理。通过控制炮台Y轴旋转做一个旋转动画rotate。
创建一个空物体命名为GameMenu,在它身上添加脚本GameMenu,添加OnStartGame和OnExitGame方法,注册到两个Button下,在Build Setting里把两个场景添加进去。

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
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/20650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[iOS]UI分析工具Reveal

Reveal是ITTY BITTY发布的UI分析工具&#xff0c;可以很直观的查看App的UI布局。 去官网下载安装Reveal&#xff0c;启动后样式如下。 下面介绍一下如何在非越狱设备和越狱设备上用Reveal工具分析UI 一、非越狱设备用Reveal分析UI 1.安装MonkeyDev 安装流程可查看前文[iOS]…

2. Java线程模型

1. 操作系统线程 无论使用何种编程语言编写多线程程序&#xff0c;最终都是通过调用操作系统的线程来执行任务。线程是CPU调度的最小执行单元。 线程有多种实现方式&#xff0c;常见的有&#xff1a;内核线程、用户线程、混合线程。 不同线程模型的主要区别在于线程的调度方不…

bat脚本启动Java服务

bat脚本启动Java服务1.终端cmd窗口运行jar2. bat脚本启动jar包3.后台启动bat脚本4. 运行bat只启动一次jar服务及停止脚本5.注意事项6.所用资源1.终端cmd窗口运行jar Java项目一般会被打包成jar后启动&#xff0c;在windows系统中可以通过终端窗口cmd启动jar包&#xff0c;即在…

5G无线技术基础自学系列 | 移动通信网络的架构

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 第五代(5thGeneraton&#xff0c; 5G) …

计算机中的第一个伟大发明(IR/IAR)

计算机工具的普及极大的推动了科技领域的发展以及提高人们生活的便捷性&#xff0c;那么在其中有哪些举足轻重的发明呢&#xff1f;本节我们来了解一下计算机中第一个伟大的发明&#xff1a;指令寄存器和指令地址寄存器 回顾在步进器加持下的两数两加 前面提到两个数相加的执行…

PyCharm运行PyQT6 (四) 百篇文章学PyQT

本文章是百篇文章学PyQT的第四篇&#xff0c;本文讲述如何使用PyCharm成功运行PyQT6&#xff0c;PyCharm在配置过程中会遇到很多问题&#xff0c;博主在本篇文章中将遇到和踩过的坑总结出来&#xff0c;可以供大家参考&#xff0c;希望大家安装顺利。包括 安装、遇到问题的解决…

Intel芯片、AMD显卡有多强?M1系列呢?

我们知道英特尔是半导体行业和计算创新领域的全球领先厂商 &#xff0c;创始于1968年。如今&#xff0c;英特尔正转型为一家以数据为中心的公司 。英特尔与合作伙伴一起&#xff0c;推动人工智能、5G、智能边缘等转折性技术的创新和应用突破 &#xff0c;驱动智能互联世界。…

【由浅入深 - Java笔记】玩转List:List过滤和筛选

文章目录List过滤&#xff1a;Comparator、Comparable 和 Stream1. 使用list的Stream进行筛选2. 增强for循环遍历元素&#xff0c;进行筛选3. 使用list.foreach遍历4. 使用iterator遍历5. 视频笔记List过滤&#xff1a;Comparator、Comparable 和 Stream 花时间精力去凿许多浅井…

5G无线技术基础自学系列 | 移动通信网络的演进

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 随着移动用户数量的不断增加&#xff0…

mac系统安装搭载Windows系统虚拟机方法教程

我们都知道macOS系统虽然相对windows系统而言更稳定&#xff0c;但macOS系统中可使用的软件数量较windows系统而言要少很多。对于macOS系统应用少的问题&#xff0c;我们可以使用虚拟机来解决。那么&#xff0c;苹果虚拟机好用吗&#xff1f;整体而言是可以的。苹果虚拟机怎么装…

0-搭建nodejs环境

1) 安装 nvm nvm 即 (node version manager)&#xff0c;好处是方便切换 node.js 版本 安装注意事项 要卸载掉现有的 nodejs提示选择 nvm 和 nodejs 目录时&#xff0c;一定要避免目录中出现空格选用【以管理员身份运行】cmd 程序来执行 nvm 命令首次运行前设置好国内镜像地…

小马哥的CSS驿站

目录 第一章 CSS概述 1.1语法 1.2注释 1.3CSS的创建 第二章 CSS选择器 1.id选择器 2.class选择器 3.标签选择器 4.子代选择器 5.后代选择器 6.相邻兄弟选择器 7.后续兄弟选择器 8.交集选择器 9.并集选择器 第三章 CSS样式 1.文本与文字样式 &#xff08;1&…

G2O学习 - 曲线拟合实例

学习使用G2O进行曲线拟合使用 1. 拟合目的&#xff0c;如何建立拟合的图模型 大概就是曲线拟合&#xff0c;求曲线 y ax^2 bx c 中 abc 的值。 但是我一直不明白如何获得下面图的。 我觉得应该是这样的 首先把abc视为一个变量&#xff0c;这个变量应该是一个矩阵【这里还…

Python和Pycharm安装教程

一、Python安装 1.进入官网下载Python 官网地址&#xff1a;Download Python | Python.org 官网下载可能会有些慢&#xff0c;请耐心等待 我这里把3.11的安装包和后面的Pycharm安装包都放到网盘里面了 链接&#xff1a;https://pan.baidu.com/s/1spxNUbH4trWBox1SJeBjkQ?pwdh…

Linux CentOS 系统安装

VMware 下载地址 Ctrl F 搜索【下载试用版】VMware 下载地址 Ctrl F 搜索【DOWNLOAD NOW】VMware-workstation-full-16.2.4-20089737.exe 文件下载地址CentOS 下载地址 Ubuntu 下载地址 阿里巴巴开源镜像站-OPSX镜像站 1、下载安装VMware虚拟机 2、下载Linux系统镜像&…

TCP/UDP/Socket 通俗讲解

1.封包和拆包 封包&#xff0c;就是发送数据前把自己本地需要发送的数据包装一下&#xff0c;即把要发送的原始数据附加上接受者可以辨识到自己身份等一些额外信息。有点像寄一封信&#xff0c;信封上填写的寄件人和收件人以及地址。 拆包&#xff0c;是接收到对方封包后发送来…

强化学习领域值得关注的国际顶级会议

导读&#xff1a; 强化学习&#xff08;Reinforcement Learning, RL&#xff09;&#xff0c;又称再励学习、评价学习或增强学习&#xff0c;是机器学习的范式和方法论之一&#xff0c;用于描述和解决智能体&#xff08;agent&#xff09;在与环境的交互过程中通过学习策略以达…

目标检测论文解读复现之十三:改进YOLOv5s的遥感图像目标检测

前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c…

LeetCode 0799. 香槟塔

【LetMeFly】799.香槟塔 力扣题目链接&#xff1a;https://leetcode.cn/problems/champagne-tower/ 我们把玻璃杯摆成金字塔的形状&#xff0c;其中 第一层 有 1 个玻璃杯&#xff0c; 第二层 有 2 个&#xff0c;依次类推到第 100 层&#xff0c;每个玻璃杯 (250ml) 将盛有香…

几何算法——介绍

几何算法——介绍介绍1 关于几何引擎介绍2 关于模型的表达2.1 CSG (Constructive Solid Geometry )表示2.2 Brep (Boundary Representation)表示3 关于翼边结构和半边结构3.1 翼边结构3.2 半边结构4 关于边界表示法4.1 ACIS中的Brep表示很早之前就想写一些几何算法相关的文章&a…