Unity3D

2023-11-04

Cheer Up

游戏说明:

除了音效,游戏地图上的元素有:

草丛(玩家可以躲进去,敌人攻击不到) 
河流(双方都过不去,但是子弹可以穿过) 
铁墙(坦克和子弹都过不去) 
砖墙(一发子弹摧毁后坦克可以过去)
空气墙(围在地图周围,防止出界) 
敌方大坦克(打两下才死)
敌方小坦克(打一下就死)
有玩家1和玩家2,可以选择单人/双人模式

地图是随机生成的,每次打开都不一样,丰富了游戏的多样性和可玩性。敌方坦克(包括大小坦克)一次最多存在3个,被摧毁后间隔3s在地图的左上/右上/上面中间 随机生成相应数量的。

❥输赢的判断:

敌方坦克数目一定,每消灭一个获得1分,消灭满20个,也就就是得分20分获得胜利,玩家自己有3生命值(即3条生命),被击中一次损失1条生命并且在出生点复活,获得3s的无敌效果。生命耗尽或者老家被摧毁,就game over。

可以理解为有10个类:


☀️项目效果展示

  • 这里的gif是用了一款ScreenToGif的软件录制的,处理的比较短
  • 右侧图标分别表示消灭的敌人数量(上) 和 当前的生命值(下),被击中一次生命值-1,变为0的时候玩家死亡,游戏结束

目录

☀️项目效果展示

☀️前言

⭐️相关背景

⭐️相关知识

⭐️版本说明

⭐️许可证激活

⭐️及时保存

☀️项目概况

⭐️项目整体布局

⭐️猜想验证

⭐️分步介绍(结合代码)

❀Audio.cs

❀Born.cs

❀Bullet.cs

❀Enemy.cs

❀Explosion.cs

❀Heart.cs

❀MapCreation.cs

❀Option.cs

❀Player.cs

❀PlayerManager.cs


☀️前言

⭐️相关背景

  • 博主在大二暑期接触了unity,借鉴实战视频上手花两天复刻了 “坦克大战” 这一经典小游戏,发现做游戏这种过程真的比玩儿游戏还要爽啊,hh,看着一些功能逐渐被实现,游戏越来越完整,真的是挺令人舒适和爽快。由于那时候零基础直接上手,对于一些基础操作真的是不太熟练,很不灵活,幸好有一些前辈的帮助(比如CSDN小伙伴们熟悉的y哥),解决了一些自己难以解决的问题,帮助小虾在迷茫的夜海上拨开神秘的迷雾。

⭐️相关知识

  • 在学习的过程中,几个比较重要的知识点就是:预制体、克隆体、精灵渲染器、渲染层级、脚本、碰撞检测、触发检测、AI设计、UI设计、固定物理帧...

⭐️版本说明

  • 小虾采用的是Unity一个较新的版本-2021.1.16,这里建议小伙伴们下载和教程相同版本的,不然真的会有可能遇到卡点消耗心情哈。
  • 可以和我一样在Unity Hub里面安装,Hub感觉挺好用的。

⭐️许可证激活

  • 这个...有段时间不登好像就要重新激活许可证,遇到同样问题的小伙伴可以进入下图的网址:Unity - Activation 按提示进行操作即可

⭐️及时保存

  • 打开的时候总是激动又煎熬,U3D启动有点慢并且操作过程中很可能就会报废,所以项目过程中一定要及时保存,不然一不小心可能就要重新来过啦!


☀️项目概况

⭐️项目整体布局

  • 整个项目工程的一个概况就在下面了,Hierarchy菜单下是放 预制体(Prefab) 的 克隆体(Clone) 的,这些克隆体可以拖到左上方的大界面中,通过点击播放按钮进行效果检验。左下方的就是整体的动画效果显示了。
  • 做一个小游戏项目,我们首先需要素材,这里推荐初学的小伙伴去siki学院学习,里面会有相应的完整初级案例教程。

⭐️猜想验证

  • 学习的过程就是不断验证猜想并接收新知识的过程,我本来想坦克大战里面的地图就是把预先处理好的 “砖墙、水泥墙、草坪、河流、空气墙(防止Object越过边界的)” 按照一定规律和自己的喜好摆放开来作为整张地图的,但事实打脸了(觉得自己真傻)。
  •  地图实例化(MapCreation) 中,我们使用了 “产生随机位置” 的方法,在避开已有位置的基础上,自动生成其它的地图元素。

代码如下:

 // 产生随机位置的方法
    private Vector3 CreateRandomPosition() // 列表中没有这个位置,才能产生
    {
        while(true)
        {
            // 不生成x=-13,13(变为10)这两列,y=-8,8这两行的位置
            Vector3 createPosition = new Vector3( Random.Range(-9,10), Random.Range(-7,8), 0); // x y z,Random.Range(a,b)中b的实际值是b-1
            if(!HasThePosition(createPosition))
            { 
                return createPosition; // 列表中没有的位置就返回 
            }
            // false 则继续循环
        }
    }

⭐️分步介绍(结合代码)

Audio.cs

  • 目的:调用音效
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Audio : MonoBehaviour
{
    public AudioClip hitAudio;

    public void PlayAudio()
    {
        AudioSource.PlayClipAtPoint(hitAudio, transform.position);
    }
}

Born.cs

  • 目的:让特效播放一段时间后自我销毁,产生玩家
  • 思路:首先引用玩家playerPrefab,新建一个敌人的数组作为敌人预制体的列表,并设置bool变量判断子弹是谁产生的。游戏一开始时,调用延时Born方法,经过延时销毁出生特效。判断是否是玩家,是则产生玩家,不是就定义一个随机数,随机生成2种敌人中的1种。
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Born : MonoBehaviour
{
    public GameObject playerPrefab; // 先拿一下玩家的引用
    public GameObject[] enemyPrefabList; // 新建一个敌人的数组:敌人预制体的列表
    public bool createPlayer; // 为了判断子弹是谁产生的(初始默认为敌人的)

    // Start is called before the first frame update
    void Start()
    {
        Invoke("BornTank", 1.0f); // 让游戏一开始调用一下延时Born方法
        Destroy(gameObject, 1.0f); // 经过0.8f延时0.8f销毁出生特效
    }

    private void BornTank()
    {
        if(createPlayer) // ture则产生玩家
        {
            Instantiate(playerPrefab, transform.position, Quaternion.identity);
        }
        else // 敌人有两种,定义一个随机数
        {
            int num = Random.Range(0,2); // 0 1(整型情况包含前两个)
            Instantiate(enemyPrefabList[num], transform.position, Quaternion.identity);
        }
    }
}

Bullet.cs

  • 目的:模拟子弹和相关物体的碰撞效果
  • 思路:首先给子弹一个速度,这个速度要适中设置,建议和玩家移动速度相同。设置一个bool变量 isPlayerBullet(默认false,不是敌人的子弹),再分6类完善子弹碰撞检测的方法
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class bullet : MonoBehaviour
{
    public float moveSpeed = 10;
    public bool isPlayerBullet; // 默认false,不是敌人的子弹
    
    void Update()
    {
        transform.Translate(transform.up * moveSpeed * Time.deltaTime, Space.World); //让子弹沿着自身所在Y轴移动
    }

    //子弹碰撞检测的方法
    private void OnTriggerEnter2D(Collider2D collision) 
    {
        switch (collision.tag)
        {
            case "Tank":
                if (!isPlayerBullet) // 玩家碰到敌人的子弹
                {
                    collision.SendMessage("Die");  // 玩家死亡一次
                    Destroy(gameObject); // 玩家死亡,自身子弹销毁
                }
                break;
            case "Heart":
                collision.SendMessage("Die"); // 老家死亡
                Destroy(gameObject); // 子弹碰老家,子弹销毁
                break;
            case "Enemy":
                if(isPlayerBullet) // 敌人碰到玩家的子弹
                {
                    collision.SendMessage("Die");  // 敌人死亡一次
                    Destroy(gameObject); // 敌人死亡,自身子弹销毁
                }
                break;
            case "Wall":
                Destroy(collision.gameObject); // 子弹碰到,墙就销毁
                Destroy(gameObject); // 子弹碰墙,子弹也销毁
                break;
            case "Barrier":
                if(isPlayerBullet) // 玩家的子弹才有音效
                {
                    collision.SendMessage("PlayAudio"); 
                }
                Destroy(gameObject); // 子弹碰到不可销毁的障碍(铁墙和空气墙),子弹自身销毁
                break;
            default:
                break;
        }

    }
}

Enemy.cs

  • 目的:实现敌方坦克的攻击、移动、死亡操作并优化 敌人(Enemy) 的AI
  • 思路:这里要用到精灵渲染器,我们要考虑子弹的旋转角度、敌方坦克上下左右的移动操作,攻击的CD、改变方向的时间计时器以及攻击、移动和死亡的具体方法的实现。最后,为了使玩家获得更加流畅舒适的体验,我们要优化 敌人(Enemy) 的AI。
  • Tips:我们要用到 void FixedUpdate() 这个生命周期函数,它也叫固定物理帧,保证在每一秒都匀称的情况下执行...
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    //属性值
    public float moveSpeed = 3;
    private Vector3 bulletEulerAngles; // 子弹应该旋转的角度
    private float v = -1;
    private float h;

    //引用
    private SpriteRenderer sr;
    public Sprite[] tankSprite; // 表示(上 右 下 左)方向;因为要改变精灵的值,所以设一个精灵数组(也就是拿一个和精灵值同类型的属性)
    public GameObject bulletPrefab;
    public GameObject explosionPrefab;

    //计时器
    private float timeVal; // 攻击CD(计时器)
    private float timeValChangeDirection; // 改变方向的时间计时器(一开始若设为4是为了使其已出现就移动)


    private void Awake() // 一般的引用在awake中赋值比较好,因为所有属性的确定和引用都应该在游戏物体一开始的时候就拿到
    {
        sr = GetComponent<SpriteRenderer>(); // 拿到了精灵渲染的组件
       
        // 接下来去控制它的图片属性就可以了
    }

    void Update()
    {
        // 攻击的时间间隔
        if (timeVal >= 3f)
        {
            
        }
        else
        {
            
        }
    }

    void FixedUpdate() // 生命周期函数,也叫固定物理帧,是在每一秒都匀称的情况下执行的
    {
        Move();
    }

    //坦克的攻击方法
    public void Attack()
    {
        //子弹产生的角度:当前坦克的角度+子弹应该旋转的角度(rotation中的欧拉角要转为四元数的形式表示角度)
        //子弹预制体 坦克位置 旋转角
        timeVal = 0;
    }

    // 坦克的移动方法
    private void Move() // 把移动代码封装成 Move()函数
    {
        
        // 下面的敌人AI只是为了控制h和v的值,下面和player一样根据值选择图片
        if(timeValChangeDirection >= 4) // 攻击一次后改变方向
        {
            int num = Random.Range(0, 8); // 多定义4个,为了让朝下的概率变大
            if(num > 5) // 向下(确保概率较大)
            {

            }
            else if(num == 0) // 向上(确保概率较小)
            {

            }
            else if (num > 0 && num <=2) // 向左
            {

            }
            else if (num > 2 && num <= 4) // 向右
            {

            }

            timeValChangeDirection = 0; // 归零防止鬼畜运动
        }
        else
        {
            // 因为move()的方法是放在void FixedUpdate()里面的 
        }

       
        //控制玩家的移动,Vector3.right 表示X轴方向的移动,Y轴是up,Z轴是forward
        if (v < 0)
        {
            sr.sprite = tankSprite[2];
            bulletEulerAngles = new Vector3(0, 0, -180);
        }
        else if (v > 0)
        {
            sr.sprite = tankSprite[0];
            bulletEulerAngles = new Vector3(0, 0, 0);
        }
        if (v != 0) // 确保按照上下移动的同时不能进行左右移动
        { 
            return;
        }


        transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World); //Time.deltaTime:按每秒移动;逗号右边的第二个方向是以世界坐标轴的方向
        if (h < 0)
        {

        }
        else if (h > 0)
        {

        }
    }

    // 坦克的死亡方法
    private void Die()
    {
        // 敌人每次死亡,就让得分+1
        PlayerManager.Instance.playerScore++;
        // 产生爆炸特效
        Instantiate(explosionPrefab, transform.position, transform.rotation);
        //死亡
        Destroy(gameObject);
    }

    //优化Enemy的AI:若敌人相碰,顺时针旋转一个角度,这样能避免扎堆
    private void OnCollisionEnter2D(Collision2D collision) // 坦克是碰撞检测
    {
        if(collision.gameObject.tag=="Enemy")
        {
            // 时间瞬间达到4,即刻旋转
        }
    }
}

Explosion.cs

  • 目的:实现爆炸效果
  • 思路:这里的爆炸效果是刚出生的爆炸特效,持续短暂时间后消失就好,也就是只作用一小段时间
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Explosion : MonoBehaviour
{
    void Start()
    {
        Destroy(gameObject, 0.167f);
    }
}

Heart.cs

  • 目的:模拟 老家(Heart)
  • 思路:开始的时候就获取一下精灵渲染器组件,一旦触发老家死亡效果,就更换为老家被破坏时的图片,并附加爆炸特效。被击中的时候要调用一下 dieAudio音效。
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Heart : MonoBehaviour
{
    private SpriteRenderer sr;
    public Sprite BrokenSprite; // 为了获取老家(Heart)被破坏时的图片
    public GameObject explosionPrefab; // 拿一下爆炸效果的引用
    public AudioClip dieAudio;

    void Start()
    {
        sr = GetComponent<SpriteRenderer>(); // 开始的时候就获取一下精灵渲染器组件
    }

    public void Die()
    {
        // 更换图片:一旦触发老家死亡效果,就更换为老家被破坏时的图片
        // 产生爆炸特效
        PlayerManager.Instance.isDefeat = true; // 被击中
        // 在某个点调用一下dieAudio
    }
}

MapCreation.cs

  • 目的:地图实例化
  • 思路:我们在已知固定位置的地方手动生成,并将它们放在一个列表中,其他的位置用随机生成位置的方法生成地图,这样每次玩家进入游戏的时候,就是一张崭新的地图了,这就丰富了游戏的多样性和可玩性。我们将敌人位置的生成也放在这部分代码中。
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MapCreation : MonoBehaviour // 地图实例化
{
    // 用来装饰初始化地图所需物体的数组
    // 0.老家 1.墙 2.障碍 3.出生效果 4.河流 5.草 6.空气墙
    public GameObject[] item;

    // 已经有东西的位置列表
    private List<Vector3> itemPositionList = new List<Vector3>();

    private void Awake()
    {
        InitMap();
    }

    private void InitMap()
    {
        // 实例化老家(CreateItem其实还是用的Instantiate的方法)
        CreateItem(item[0], new Vector3(0, -8, 0), Quaternion.identity); // Quaternion.identity:无旋转

        //用墙把老家围起来
        CreateItem(item[1], new Vector3(-1, -8, 0), Quaternion.identity);
        CreateItem(item[1], new Vector3(1, -8, 0), Quaternion.identity);
        for (int i = -1; i < 2; i++)
        {
            CreateItem(item[1], new Vector3(i, -7, 0), Quaternion.identity);
        }

        // 实例化外围墙 x=-14,14(变为11),y=-9,9
        for (int i = -11; i < 12; i++) //最上面一行
        {
            CreateItem(item[6], new Vector3(i, 9, 0), Quaternion.identity);
        }
        for (int i = -11; i < 12; i++) //最下面一行
        {
            CreateItem(item[6], new Vector3(i, -9, 0), Quaternion.identity);
        }
        for (int i = -8; i < 9; i++) //最左面一行
        {
            CreateItem(item[6], new Vector3(-11, i, 0), Quaternion.identity);
        }
        for (int i = -8; i < 9; i++) //最右面一行
        {
            CreateItem(item[6], new Vector3(11, i, 0), Quaternion.identity);
        }

        // 初始化玩家(通过实例化出生特效实例化玩家)
        GameObject go = Instantiate(item[3], new Vector3(-2, -8, 0), Quaternion.identity); // 产生出生特效
        go.GetComponent<Born>().createPlayer = true;// 特效上的bool值设置为true:产生玩家

        // 产生敌人(在固定的三个位置先产生,之后每隔一段时间在三个位置随机产生)
        CreateItem(item[3], new Vector3(-10, 8, 0), Quaternion.identity);
        CreateItem(item[3], new Vector3(0, 8, 0), Quaternion.identity);
        CreateItem(item[3], new Vector3(10, 8, 0), Quaternion.identity);

        InvokeRepeating("CreateEnemy", 4, 5);

        // 实例化地图
        for (int i = 0; i < 60; i++)
        {
            CreateItem(item[1], CreateRandomPosition(), Quaternion.identity);
        }
        for (int i = 0; i < 20; i++)
        {
            CreateItem(item[2], CreateRandomPosition(), Quaternion.identity);
        }
        for (int i = 0; i < 20; i++)
        {
            CreateItem(item[4], CreateRandomPosition(), Quaternion.identity);
        }
        for (int i = 0; i < 20; i++)
        {
            CreateItem(item[5], CreateRandomPosition(), Quaternion.identity);
        }

    }


    private void CreateItem(GameObject createGameObject,Vector3 createPosition,Quaternion createRotation) // 封装一个方法:使播放时Hierarchy菜单下的东西不散落
    {
        // 根据上面的参数实例化游戏物体
        // Parent是当前的游戏物体
        // 把所有出现过的物体位置存进位置列表
    }
    

    // 产生随机位置的方法
    private Vector3 CreateRandomPosition() // 列表中没有这个位置,才能产生
    {
        while(true)
        {
            // 不生成x=-13,13(变为10)这两列,y=-8,8这两行的位置
            Vector3 createPosition = new Vector3( Random.Range(-9,10), Random.Range(-7,8), 0); // x y z,Random.Range(a,b)中b的实际值是b-1
            if(!HasThePosition(createPosition))
            { 
                return createPosition; // 列表中没有的位置就返回 
            }
            // false 则继续循环
        }
    }

    
    // 用来判断位置列表中是否有这个位置
    private bool HasThePosition(Vector3 createPos)
    {
        for (int i = 0; i < itemPositionList.Count; i++) // 遍历当前的位置列表
        {
            if(createPos == itemPositionList[i]) // 如果随机产生位置的值等于当前列表某个位置的值
            {
                return true;
            }
        }
        return false;
    }


    // 产生敌人的方法
    private void CreateEnemy()
    {
        int num = Random.Range(0, 3); // 0 1 2
        Vector3 EnemyPos = new Vector3(); // 装随机产生的位置:向量/坐标
        if (num == 0)
        {

        }
        else if (num == 1)
        {

        }
        else 
        {

        }
        CreateItem(item[3], EnemyPos, Quaternion.identity);
    }
}

Option.cs

  • 目的:选项监听实现
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Option : MonoBehaviour
{
    private int choice = 1;
    public Transform posOne;
    public Transform posTwo;

    // Update is called once per frame
    void Update()
    {
        // 监听开始界面上下移动的选项
        if (Input.GetKeyDown(KeyCode.W))
        {
            choice = 1;
            transform.position = posOne.position;
        }
        else if (Input.GetKeyDown(KeyCode.S))
        {
            choice = 2;
            transform.position = posTwo.position;
        }
        if (choice==1 && Input.GetKeyDown(KeyCode.Space))
        {
            SceneManager.LoadScene(1); // 加载 Main 场景
        }
    }
}

Player.cs

  • 目的:模拟玩家坦克Player
  • 思路:玩家坦克Player 的实现可以借鉴 敌方坦克Enemy
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour {
    
    //属性值
    public float moveSpeed = 3;
    private Vector3 bulletEulerAngles; // 子弹应该旋转的角度
    private float timeVal; // 攻击CD(计时器)
    private float defendTimeVal = 3; // 无敌CD(计时器)
    private bool isDefended = true; // 无敌状态

    //引用
    // 拿到渲染器
    // 拿到需要用到的图片,(上 右 下 左)方向;因为要改变精灵的值,所以设一个精灵数组(也就是拿一个和精灵值同类型的属性)
    public GameObject bulletPrefab;
    public GameObject explosionPrefab;
    public GameObject defendEffectPrefab;
    // 控制音效的播放
    // 拿到音效播放的资源

    private void Awake() // 一般的引用在awake中赋值比较好,因为所有属性的确定和引用都应该在游戏物体一开始的时候就拿到
    {
        // 拿到了精灵渲染的组件
        
        // 接下来去控制它的图片属性就可以了
    }

    void Update()
    {
        // 无敌CD
        if(isDefended)
        {
            defendEffectPrefab.SetActive(true); // 控制无敌状态时护盾打开
            defendTimeVal -= Time.deltaTime;
            if(defendTimeVal <= 0)
            {
                isDefended = false;
                // 控制无敌状态时护盾关闭
            }
        }
    }

    private void FixedUpdate() {// 生命周期函数,也叫固定物理帧,是在每一秒都匀称的情况下执行的
        
        if(PlayerManager.Instance.isDefeat)
        {
            return; // true则return:游戏失败,禁止玩家所有操作
        }
        Move();

        // 攻击CD
        if (timeVal >= 0.4f)
        {
            Attack();
        }
        else
        {
            timeVal += Time.fixedDeltaTime;
        }
    }


    //坦克的攻击方法
    public void Attack()
    {
        if() //若检测到玩家输入为空格键
        {
            //子弹产生的角度:当前坦克的角度+子弹应该旋转的角度(rotation中的欧拉角要转为四元数的形式表示角度)
            //子弹预制体 坦克位置 旋转角
            timeVal = 0;
        }
    }


    // 坦克的移动方法
    private void Move() // 把移动代码封装成 Move()函数
    {
        // 监听玩家垂直轴值的输入
        transform.Translate(Vector3.up * v * moveSpeed * Time.fixedDeltaTime, Space.World);

        if (v < 0) {
            
        }
        else if (v > 0) {
            
        }

        // v和h不为0,播放...音效 (在判断v是否为0之前判断是考虑了它的优先级)
        if(Mathf.Abs(v)>0.05f)
        {
            // 若音效播放时又调用,声音会很杂,所以要判断...
            if(!moveAudio.isPlaying)
            {
                moveAudio.Play();
            }
        }

        if (v != 0) { // 确保按照上下移动的同时不能进行左右移动
            return;
        }

        //监听玩家水平轴值的输入h来控制在X轴上的位移的方向,按右方向为1,左为-1

        //控制玩家的移动,Vector3.right 表示X轴方向的移动,Y轴是up,Z轴是forward
        transform.Translate(Vector3.right * h * moveSpeed * Time.fixedDeltaTime, Space.World); //Time.deltaTime:按每秒移动;逗号右边的第二个方向是以世界坐标轴的方向
        if (h < 0) {

        }
        else if (h > 0) {

        }

        if (Mathf.Abs(h) > 0.05f)
        {
            moveAudio.clip = tankAudio[1]; 
            if (!moveAudio.isPlaying)
            {
                moveAudio.Play();
            }
        }
        else
        {
            moveAudio.clip = tankAudio[0];
            if (!moveAudio.isPlaying)
            {
                moveAudio.Play();
            }
        }

    }

    // 坦克的死亡方法
    private void Die()
    {
        // 是否处于无敌状态
        if (isDefended)
        {
            return;
        }
        // 击中的话,就让玩家管理里面的属性变为true
        PlayerManager.Instance.isDead = true;

        // 产生爆炸特效
        Instantiate(explosionPrefab, transform.position, transform.rotation);
        //死亡
        Destroy(gameObject);
    }

}

PlayerManager.cs

  • 目的:玩法管理和UI设计
  • 代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI; // 为了更新 UI,拿一下它的引用,使用 UI,就要拿一下Unity的运营空间
using UnityEngine.SceneManagement;

public class PlayerManager : MonoBehaviour
{
    //属性值
    public int lifeValue = 3;
    public int playerScore = 0;
    public bool isDead;
    public bool isDefeat;

    //引用
    public GameObject born;
    public Text playerScoreText;
    public Text playerLifeValueText;
    public GameObject isDefeatUI; // 拿一下游戏物体的引用

    //单例
    private static PlayerManager instance;
    public static PlayerManager Instance { get => instance; set => instance = value; }

    private void Awake()
    {
        Instance = this;
    }

    // Update is called once per frame
    void Update() // 在其中实时监听一些状态
    {
        if(isDefeat) // 失败的话
        {
            isDefeatUI.SetActive(true); // 让失败的图片显示
            return;
        }
        
        if(isDead) // 如果死亡
        {
            Recover();
        }

        // 实时更新消灭敌人的显示和生命值
        playerScoreText.text = playerScore.ToString(); // .ToString():把int转化为字符串
        playerLifeValueText.text = lifeValue.ToString();
    }

    private void Recover()
    {
        if (lifeValue<=0)
        {
            // 游戏失败,返回主界面
            isDefeat = true;
            Invoke("ReturnToTheMainMenu", 3); // 延时3s调用这个方法
        }
        else
        {
            lifeValue --;
            GameObject go = Instantiate(born, new Vector3(-2, -7.9f, 0), Quaternion.identity); // 接收born的脚本
            go.GetComponent<Born>().createPlayer = true;
            isDead = false;
        }
    }

    private void ReturnToTheMainMenu()
    {
        SceneManager.LoadScene(0); // 返回开始界面
    }

}

完结,撒花❀~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity3D 的相关文章

随机推荐

  • Ubuntu安装Tango Control教程

    文章目录 环境 步骤 其他指令 参考 环境 虚拟机 VMware Ubuntun 20 04LTS Tango 9 3 4 步骤 为tango controls安装 sudo apt get install g openjdk 8 jdk
  • 中国C-V2X SPDU格式解读

    1 SPDU简介 SPDU即Secure Protocol Data Unit 是在V2X设备之间进行传输的消息结构 结构如下 SecuredMessage SEQUENCE version Uint8 版本号 当前为2 payload P
  • Highcharts+NodeJS搭建数据可视化平台

    前一段时间完成了一个数据可视化项目 由后台NodeJS Highcharts框架进行搭建 下面分享一下整个开发过程的流程 以及使用Highcharts框架的经验 一 数据的读取 由于数据库使用的是Mysql数据库 在NodeJS中 可以使用
  • 最新类ChatPDF及AutoGPT开源18大功能平台——闻达手把手超详细环境部署与安装——如何在低显存单显卡上面安装私有ChatGPT GPT-4大语言模型LLM调用平台

    目录 前言 闻达框架简介 前期准备 电脑要求 安装anaconda 安装相应版本的CUDA 配置ChatGLM 6B Conda环境 安装pytorch 类ChatPDF及AutoGPT开源模型 闻达环境部署及安装 相关代码及模型下载 解压
  • 【狂神说】Spring学习笔记(全)

    狂神说 Spring5最新完整教程IDEA版参考链接 https www bilibili com video BV1WE411d7Dv 狂神说 Java学习完整路线https www bilibili com read cv5702420
  • 【OpenGL进阶】05.绘制3D模型

    这篇文章来绘制3D模型 添加了model h和model cpp文件 model h pragma once include ggl h include vertexbuffer h include shader h class Model
  • ES6学习

    阮一峰讲解非常清晰 通俗易懂 https es6 ruanyifeng com https wangdoc com es6
  • h5跳转小程序页面url_微信小程序页面跳转方法

    我所知道的 微信小程序页面跳转有以下方法 下面分情况说明下 一 利用小程序提供的 API 跳转 1 简单page页面之间跳转 保留当前页面 跳转到应用内的某个页面 使用wx navigateBack可以返回到原页面 注意 调用 naviga
  • 数字模拟转换DAC

    数字模拟转换DAC 1 局限性 只有大容量的STM32F10x才具有DAC功能 2 数模转换原理 STM32的DAC模块 数字 模拟转换模块 是12位数字输入 电压输出型的DAC DAC可以配置为8位或12位模式 也可以与DMA控制器配合使
  • 【计算机网络】我与张三的 DNS 解析过程,浏览器中输入URL 回车后发生了什么

    视频解析 方便大家理解 我在 b 站发布了一期视频 欢迎大家查收 计网 浏览器输入url按下回车后发生了什么 计算机网络DNS工作流程详解 解析 hello 家好 我是 up主黎明 菜 今早我正打开b站刷剧 突然想到了 个问题 我们在浏览器
  • 一名系统研究者的攀登之路-陈海波-

    陈海波 原复旦大学Pa ra lle l Proc e s s ing Institute实验室的牛人 在sosp EuroSys等世界最顶级会议上发表过论文的大牛人 不过 现在被上交软件学院给挖走了 哈哈 1 引言 写好计算机系统领域的研
  • Mysql用同一张表查询的结果删除此表的数据报错

    DELETE FROM study name WHERE name id IN SELECT name id FROM study name WHERE name id 20221209 执行会报错如下 DELETE 0 row s 0 0
  • LaTex学习笔记(三):矩阵的输入

    矩阵的输入类似于表格 在latex中输入矩阵有多种方式 1 left begin array clr 4343 434 235 45 3232 34 56 232 3467 end array right 2 begin bmatrix 不
  • Excel 两列数据中相同的数据进行同行显示

    一 要求 假设您有两个列 分别是A列和B列 需要在C列中找出A列对应的B列的值 二 方案 方法1 寻常思路 凸显重复项 对A列单独进行筛选 按颜色进行排序 然后升序 对B列重复上述操作即可 方法2 两个公式 VLOOKUP 纵向查找函数 语
  • HDFS操作

    1 使用oiv命令查看hadoop 的镜像文件 hadoop s201 hadoop dfs name current hdfs oiv Usage bin hdfs oiv OPTIONS i INPUTFILE o OUTPUTFILE
  • Python处理缺失数据

    目录 1 缺失原因 2 缺失类型 3 处理方法 3 1 删除 3 1 1 统计每列缺失值的个数 3 1 2 直接删除含有缺失值的行 3 1 3 直接删除含有缺失值的列 3 1 4 只删除全是缺失值的行 3 1 5 保留至少有4个非缺失值的行
  • 51单片机(STC)串口无阻塞发送函数

    目录 一 简介 1 1 开发环境 1 2 功能描述 二 串口程序 2 1 串口配置 2 2 变量定义 2 3 中断函数 2 4 发送函数 一 简介 1 1 开发环境 KeilC51 单片机型号STC15F2K60S2 1 2 功能描述 使用
  • Hutool导出Excel,导多个Sheet页

    重要方法 指定要写出的 Sheet 页 bigWriter setSheet sheet getSheetName 工具类 public class HuExcelUtils 导出多个 Sheet 页 param response para
  • 零售业未来如何破局?抓住数智化经营的两把利刃!

    导语 数字化转型浪潮席卷了千行百业 有人从中看出了汹涌的挑战 也有人从中嗅出了美妙的商机 对于零售企业而言 当前数智经营进入了哪个阶段 未来的破局之道又在何方 我们邀请到了广东省 CIO 协会消费品与零售行业分会会长 腾讯云 TVP 行业大
  • Unity3D

    Cheer Up 游戏说明 除了音效 游戏地图上的元素有 草丛 玩家可以躲进去 敌人攻击不到 河流 双方都过不去 但是子弹可以穿过 铁墙 坦克和子弹都过不去 砖墙 一发子弹摧毁后坦克可以过去 空气墙 围在地图周围 防止出界 敌方大坦克 打两