Unity【入门】小项目坦克大战

news2024/9/20 8:42:12

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

文章目录

  • 1、开始场景
    • 1、场景装饰
      • RotateObj
    • 2、开始界面
      • BasePanel
      • BeginPanel
    • 3、设置界面
      • GameDataMgr
      • SettingPanel
    • 4、音效数据逻辑
      • MusicData
    • 5、排行榜界面
      • RankPanel
    • 6、排行榜数据逻辑
      • RankInfo
    • 7、背景音乐
      • BKMusic
  • 2、游戏场景
    • 1、游戏界面
      • GamePanel
    • 2、基础场景搭建
      • CubeObj
      • QuitPanel
      • 奖励 PropReward
      • 武器奖励 WeaponReward
    • 3、坦克基类
      • TankBaseObj
      • 特效销毁 AutoDestroy
      • 子弹 BulletObj
      • 武器 WeaponObj
    • 4、主玩家相关
      • PlayerObj
      • 摄像机跟随 CameraMove
    • 5、敌人相关
      • MonsterObj
      • MonsterTower
    • 6、通关点
      • EndPoint
  • 3、结束界面
    • 1、游戏胜利界面
      • WinPanel
    • 2、游戏失败界面
      • LosePanel
    • 3、游戏打包

1、开始场景

1、场景装饰

RotateObj

using UnityEngine;

public class RotateObj : MonoBehaviour
{
    public float rotateSpeed = 15;
    void Update()
    {
        transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
    }
}

2、开始界面

BasePanel

using UnityEngine;

public class BasePanel<T> : MonoBehaviour where T:class
{
    //两个关键的静态成员
    //私有的静态成员变量(声明) 基础Mono不能new
    private static T instance;
    //公共的静态成员属性或方法(获取)
    //public static T Instance
    //{
    //    get
    //    {
    //        return instance;
    //    }
    //}
    public static T Instance => instance;
    //instance赋值,为确保挂载的唯一性,自己定义一个规范
    private void Awake()
    {
        instance = this as T;
    }
    public virtual void ShowMe()
    {
        this.gameObject.SetActive(true);
    }
    public virtual void HideMe()
    {
        this.gameObject.SetActive(false);
    }
}

BeginPanel

using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// 通过类名快速得到脚本
/// </summary>
public class BeginPanel : BasePanel<BeginPanel>
{
    // 声明公共的成员变量来关联各个控件
    public CustomGUIButton btnBegin;
    public CustomGUIButton btnSetting;
    public CustomGUIButton btnQuit;
    public CustomGUIButton btnRank;
    void Start()
    {
        //锁定鼠标在窗口内
        //Cursor.lockState = CursorLockMode.Confined;
        //监听一次按钮点击过后的内容
        btnBegin.clickEvent += () => 
        {
            SceneManager.LoadScene("GameScene");
        };
        btnSetting.clickEvent += () =>
        {
            SettingPanel.Instance.ShowMe();
            HideMe();
        };
        btnQuit.clickEvent += () =>
        {
            Application.Quit();
        };
        btnRank.clickEvent += () =>
        {
            RankPanel.Instance.ShowMe();
            Instance.HideMe();
        };
    }
}

3、设置界面

GameDataMgr

/// <summary>
/// 游戏数据管理类,是一个单例模式对象
/// </summary>
public class GameDataMgr
{
    private static GameDataMgr instance = new GameDataMgr();
    public static GameDataMgr Instance { get => instance; }
    //设置信息对象
    public MusicData musicData;
    //排行榜数据对象
    public RankList rankData;
    private GameDataMgr()
    {
        //初始化游戏数据
        musicData = PlayerPrefsDataMgr.Instance.LoadData(typeof(MusicData), "Music_Key") as MusicData;
        //第一次进入游戏的默认值
        if (!musicData.notFirst)
        {
            musicData.notFirst = true;
            musicData.isOpenBK = true;
            musicData.isOpenSound = true;
            musicData.bkValue = 1;
            musicData.soundValue = 1;
            PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music_Key");
        }

        //初始化读取排行榜数据
        rankData = PlayerPrefsDataMgr.Instance.LoadData(typeof(RankList),"Rank_key") as RankList;
    }

    //提供一个添加数据到排行榜中的方法
    public void AddRankInfo(string name,int score,float time)
    {
        rankData.list.Add(new RankInfo(name,score,time));
        //排序
        rankData.list.Sort((a, b) => a.time < b.time ? -1 : 1);
        //移除第10条之后的数据
        for (int i = rankData.list.Count-1; i >= 10; i--)
        {
            rankData.list.RemoveAt(i);
        }
        //存储
        PlayerPrefsDataMgr.Instance.SaveData(rankData, "Rank_key");
    }

    public void OpenOrCloseBKMusic(bool isOpen)
    {
        musicData.isOpenBK = isOpen; //改变是否开启状态
        //控制开关
        BKMusic.Instance.ChangeOpen(isOpen);
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music_Key"); //存储
    }
    public void OpenOrCloseSound(bool isOpen)
    {
        musicData.isOpenSound = isOpen; //改变
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music_Key"); //存储
    }
    public void ChangeBKValue(float value)
    {
        musicData.bkValue = value;
        BKMusic.Instance.ChangeValue(value);
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music_Key");
    }
    public void ChangeSoundValue(float value)
    {
        musicData.soundValue = value;
        PlayerPrefsDataMgr.Instance.SaveData(musicData, "Music_Key");
    }
}

SettingPanel

using UnityEngine;
using UnityEngine.SceneManagement;

public class SettingPanel : BasePanel<SettingPanel>
{
    //声明成员变量 关联控件
    public CustomGUISlider sliderMusic;
    public CustomGUISlider sliderSound;

    public CustomGUIToggle togMusic;
    public CustomGUIToggle togSound;

    public CustomGUIButton btnClose;

    void Start()
    {
        //监听对应的事件,处理逻辑
        sliderMusic.changeValue += (value) => GameDataMgr.Instance.ChangeBKValue(value);
        sliderSound.changeValue += (value) => GameDataMgr.Instance.ChangeSoundValue(value);
        togMusic.changeValue += (value) => GameDataMgr.Instance.OpenOrCloseBKMusic(value);
        togSound.changeValue += (value) => GameDataMgr.Instance.OpenOrCloseSound(value);

        btnClose.clickEvent += () =>
        {
            HideMe();
            //判断当前所在场景
            if(SceneManager.GetActiveScene().name == "BeginScene")
                //把隐藏的开始界面重新显示
                BeginPanel.Instance.ShowMe();
        };
        HideMe();
        //this.gameObject.SetActive(false);
    }
    public void UpdatePanelInfo()
    {
        //更新设置信息
        MusicData data = GameDataMgr.Instance.musicData;
        sliderMusic.nowValue = data.bkValue;
        sliderSound.nowValue = data.soundValue;
        togMusic.isSel = data.isOpenBK;
        togSound.isSel = data.isOpenSound;
    }
    public override void ShowMe()
    {
        base.ShowMe();
        UpdatePanelInfo(); //更新设置信息
    }
    public override void HideMe()
    {
        base.HideMe();
        Time.timeScale = 1;
    }
}

4、音效数据逻辑

MusicData

/// <summary>
/// 音效数据类,用于存储音乐设置相关的信息
/// </summary>
public class MusicData
{
    //背景音乐
    public bool isOpenBK;
    //音效
    public bool isOpenSound;

    public float bkValue;
    public float soundValue;

    public bool notFirst;
}

5、排行榜界面

RankPanel

using System.Collections.Generic;

public class RankPanel : BasePanel<RankPanel>
{
    //关联public的控件对象
    public CustomGUIButton btnClose;

    private List<CustomGUILable> labRank = new List<CustomGUILable>();
    private List<CustomGUILable> labPlayer = new List<CustomGUILable>();
    private List<CustomGUILable> labScore = new List<CustomGUILable>();
    private List<CustomGUILable> labTime = new List<CustomGUILable>();
    void Start()
    {
        for (int i = 1; i <= 10; i++)
        {
            //通过路径/找到子对象的子对象
            //labRank.Add(transform.Find("labRank/labRank" + i).GetComponent<CustomGUILable>());
            labPlayer.Add(transform.Find("labPlayer/labPlayer" + i).GetComponent<CustomGUILable>());
            labScore.Add(transform.Find("labScore/labScore" + i).GetComponent<CustomGUILable>());
            labTime.Add(transform.Find("labTime/labTime" + i).GetComponent<CustomGUILable>());
        }
        //print("排名" + labRank.Count);
        print("玩家" + labPlayer.Count);
        print("分数" + labScore.Count);
        print("通关时间" + labTime.Count);

        //处理事件监听逻辑
        btnClose.clickEvent += () =>
        {
            HideMe();
            BeginPanel.Instance.ShowMe();
        };
        //GameDataMgr.Instance.AddRankInfo("测试", 100, 7232);
        HideMe();
    }
    public override void ShowMe()
    {
        base.ShowMe();
        UpdatePanelInfo();
    }
    public void UpdatePanelInfo()
    {
        //处理根据排行榜数据更新面板
        //获取GameDataMgr中的排行榜列表,用于更新
        List<RankInfo> list = GameDataMgr.Instance.rankData.list;
        //根据列表更新面板数据
        for (int i = 0; i < list.Count; i++)
        {
            labPlayer[i].content.text = list[i].name;
            labScore[i].content.text = list[i].score.ToString();

            int time = (int)list[i].time;
            labTime[i].content.text = "";

            if (time / 3600 > 0)
            {
                labTime[i].content.text += time / 3600 + "时";
            }
            if (time % 3600 / 60 > 0 || labTime[i].content.text != "")
            {
                labTime[i].content.text += time % 3600 / 60 + "分";
            }
            labTime[i].content.text += time % 60 + "秒";
        }
    }
}

6、排行榜数据逻辑

RankInfo

using System.Collections.Generic;

/// <summary>
/// 排行榜单条数据
/// </summary>
public class RankInfo
{
    public string name;
    public int score;
    public float time;
    public RankInfo() { }
    public RankInfo(string name, int score, float time)
    {
        this.name = name;
        this.score = score;
        this.time = time;
    }
}
/// <summary>
/// 排行榜列表
/// </summary>
public class RankList
{
    public List<RankInfo> list;
}

7、背景音乐

BKMusic

using UnityEngine;

public class BKMusic : MonoBehaviour
{
    private static BKMusic instance;
    public static BKMusic Instance => instance;
    public AudioSource audioSource;
    void Awake()
    {
        //初始化 赋值
        instance = this;
        //得到自己依附的游戏对象上挂载的音频源脚本
        audioSource = GetComponent<AudioSource>();
        //初始化已经设置好的信息
        ChangeValue(GameDataMgr.Instance.musicData.bkValue);
        ChangeOpen(GameDataMgr.Instance.musicData.isOpenBK);
    }
    /// <summary>
    /// 改变背景音乐大小
    /// </summary>
    /// <param name="value"></param>
    public void ChangeValue(float value)
    {
        audioSource.volume = value;
    }
    /// <summary>
    /// 开启背景音乐
    /// </summary>
    /// <param name="isOpen"></param>
    public void ChangeOpen(bool isOpen)
    {
        //关闭 mute(静音)
        audioSource.mute = !isOpen;
    }
}

2、游戏场景

1、游戏界面

GamePanel

using UnityEngine;

public class GamePanel : BasePanel<GamePanel>
{
    //获取控件
    public CustomGUILable labScore;
    public CustomGUILable labTime;
    public CustomGUIButton btnQuit;
    public CustomGUIButton btnSetting;
    public CustomGUITexture texHP;
    public float hpW = 300;
    [HideInInspector]
    public int nowScore = 0; //分数记录
    [HideInInspector]
    public float nowTime = 0;
    private int time;

    void Start()
    {
        //锁定鼠标在窗口内
        Cursor.lockState = CursorLockMode.Confined;
        //监听界面上的一些控件操作
        btnQuit.clickEvent += () =>
        {
            QuitPanel.Instance.ShowMe();
            Time.timeScale = 0;
        };
        btnSetting.clickEvent += () =>
        {
            SettingPanel.Instance.ShowMe();
            Time.timeScale = 0;
        };

        //AddScore(100);
        //UpdataHP(300, 30);
    }

    void Update()
    {
        nowTime += Time.deltaTime;

        time = (int)nowTime;
        labTime.content.text = "";

        if (time / 3600 > 0)
        {
            labTime.content.text += time / 3600 + "时";
        }
        if (time % 3600 / 60 > 0 || labTime.content.text != "")
        {
            labTime.content.text += time % 3600 / 60 + "分";
        }
        labTime.content.text += time % 60 + "秒";
    }
    /// <summary>
    /// 更新得分的方法
    /// </summary>
    /// <param name="score"></param>
    public void AddScore(int score)
    {
        nowScore = score;
        //更新界面
        labScore.content.text = score.ToString();
    }
    /// <summary>
    /// 更新血条的方法
    /// </summary>
    /// <param name="maxHP"></param>
    /// <param name="HP"></param>
    public void UpdataHP(int maxHP,int HP)
    {
        texHP.guiPos.width = (float)HP/maxHP * hpW;
    }
}

2、基础场景搭建

CubeObj

using UnityEngine;

public class CubeObj : MonoBehaviour
{
    //奖励预设体关联
    public GameObject[] rewardObjects;
    //死亡特效关联
    public GameObject deadEff;
    private void OnTriggerEnter(Collider other)
    {
        //50%几率创建奖励
        int rangeInt = Random.Range(0, 100);
        if(rangeInt < 70)
        {
            //根据箱子位置随机创建一个奖励预设体
            rangeInt = Random.Range(0,rewardObjects.Length);
            Instantiate(rewardObjects[rangeInt],transform.position,transform.rotation);
        }
        //创建特效预设体
        GameObject effObj = Instantiate(deadEff, transform.position, transform.rotation);
        //控制音效
        AudioSource audioS = effObj.GetComponent<AudioSource>();
        audioS.volume = GameDataMgr.Instance.musicData.soundValue;
        audioS.mute = !GameDataMgr.Instance.musicData.isOpenSound;
        Destroy(gameObject);
    }
}

QuitPanel

using UnityEngine;
using UnityEngine.SceneManagement;

public class QuitPanel : BasePanel<QuitPanel>
{
    public CustomGUIButton btnQuit;
    public CustomGUIButton btnGoOn;
    public CustomGUIButton btnClose;
    void Start()
    {
        HideMe();

        btnQuit.clickEvent += () =>
        {
            SceneManager.LoadScene("BeginScene");
        };
        btnGoOn.clickEvent += () =>
        {
            HideMe();
        };
        btnClose.clickEvent += () =>
        {
            HideMe();
        };
    }
    public override void HideMe()
    {
        base.HideMe();
        Time.timeScale = 1.0f;
    }
}

奖励 PropReward

using UnityEngine;
public enum E_PropType
{
    Atk, Def, MaxHp, Hp,
}
public class PropReward : MonoBehaviour
{
    public E_PropType type = E_PropType.Atk;
    public int changeVaule = 2;
    public GameObject getEff;
    private void OnTriggerEnter(Collider other)
    {
        //玩家才能获取属性奖励
        if (other.CompareTag("Player"))
        {
            //得到玩家脚本
            PlayerObj player = other.GetComponent<PlayerObj>();
            switch (type)
            {
                case E_PropType.Atk:
                    player.atk += changeVaule;
                    break;
                case E_PropType.Def:
                    player.def += changeVaule;
                    break;
                case E_PropType.MaxHp:
                    player.maxHp += changeVaule;
                    GamePanel.Instance.UpdataHP(player.maxHp, player.hp);
                    break;
                case E_PropType.Hp:
                    player.hp += changeVaule;
                    if (player.hp > player.maxHp)
                        player.hp = player.maxHp;
                    GamePanel.Instance.UpdataHP(player.maxHp, player.hp);
                    break;
            }
            //创建特效
            //获得武器的特效
            GameObject eff = Instantiate(getEff, transform.position, transform.rotation);
            AudioSource audioS = eff.GetComponent<AudioSource>();
            audioS.volume = GameDataMgr.Instance.musicData.soundValue;
            audioS.mute = !GameDataMgr.Instance.musicData.isOpenSound;
            Destroy(gameObject);
        }
    }
}

武器奖励 WeaponReward

using UnityEngine;

public class WeaponReward : MonoBehaviour
{
    //随机武器
    public GameObject[] weaponObj;
    //武器特效
    public GameObject getEff;
    
    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            //获取武器
            int index = Random.Range(0, weaponObj.Length);
            //得到PlayerObj脚本
            PlayerObj player = other.GetComponent<PlayerObj>();
            //切换武器
            player.ChangeWeapon(weaponObj[index]);

            //获得武器的特效
            GameObject eff = Instantiate(getEff, transform.position, transform.rotation);
            AudioSource audioS = eff.GetComponent<AudioSource>();
            audioS.volume = GameDataMgr.Instance.musicData.soundValue;
            audioS.mute = !GameDataMgr.Instance.musicData.isOpenSound;

            Destroy(gameObject);
        }
    }
}

3、坦克基类

TankBaseObj

using UnityEngine;

public abstract class TankBaseObj : MonoBehaviour
{
    public int atk;
    public int def;
    public int maxHp;
    public int hp;

    public Transform tankHead;

    public float moveSpeed = 10;
    public float roundSpeed = 100;
    public float headRoundSpeed = 100;

    //死亡特效
    public GameObject deadEff;

    public abstract void Fire();
    public virtual void Wound(TankBaseObj other)
    {
        //伤害计算
        int dmg = other.atk - this.def;
        if (dmg <= 0)
            return;
        this.hp -= dmg;
        if (this.hp <= 0)
        {
            hp = 0;
            this.Dead();
        }
            
    }
    public virtual void Dead()
    {
        Destroy(gameObject);
        if (deadEff != null)
        {
            //实例化对象(特效)的位置和角度
            GameObject effObj = Instantiate(deadEff, transform.position, transform.rotation);
            //获取该对象的音频
            AudioSource audioSource = effObj.GetComponent<AudioSource>();
            audioSource.volume = GameDataMgr.Instance.musicData.soundValue;
            audioSource.mute = !GameDataMgr.Instance.musicData.isOpenSound;
            audioSource.Play();
        }
    }
}

特效销毁 AutoDestroy

using UnityEngine;

public class AutoDestroy : MonoBehaviour
{
    public float time = 2;
    void Start()
    {
        Destroy(gameObject, time);
    }
}

子弹 BulletObj

using UnityEngine;

public class BulletObj : MonoBehaviour
{
    //移动速度
    public float moveSpeed = 50;
    //子弹所有者
    public TankBaseObj fatherObj;
    //特效对象
    public GameObject effObj;

    void Update()
    {
        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
    }

    private void OnTriggerEnter(Collider other)
    {
        //子弹打到Cube
        if (other.CompareTag("Cube") ||
            //Monster发出的子弹打到玩家
            other.CompareTag("Player") && fatherObj.CompareTag("Monster") ||
            //Player发出的子弹打到Monster
            other.CompareTag("Monster") && fatherObj.CompareTag("Player"))
        {
            //伤害判断
            //通过里氏判断让父类获取脚本
            TankBaseObj obj = other.GetComponent<TankBaseObj>();
            if (obj != null)
                obj.Wound(fatherObj);

            //子弹爆炸特效
            if (effObj != null)
            {
                //创建爆炸特效
                GameObject eff = Instantiate(effObj, transform.position, transform.rotation);
                //修改音效的音量
                //获取音效对象
                AudioSource audioSource = eff.GetComponent<AudioSource>();
                audioSource.volume = GameDataMgr.Instance.musicData.soundValue;
                audioSource.mute = !GameDataMgr.Instance.musicData.isOpenSound;
                Destroy(gameObject);
            }
        }
    }
    //设置子弹所有者
    public void SetFather(TankBaseObj obj)
    {
        fatherObj = obj;
    }
}

武器 WeaponObj

using UnityEngine;

public class WeaponObj : MonoBehaviour
{
    //实例化子弹对象
    public GameObject bullet;
    //发射位置
    public Transform[] shootPos;
    //武器的拥有者
    public TankBaseObj fatherObj;
    
    //设置子弹所有者
    public void SetFather(TankBaseObj obj)
    {
        fatherObj = obj;
    }
    public void Fire()
    {
        //根据位置创建子弹
        for (int i = 0; i < shootPos.Length; i++)
        {
            //创建子弹
            GameObject obj = Instantiate(bullet, shootPos[i].position, shootPos[i].rotation); ;
            //获取子弹所有者
            BulletObj bulletObj = obj.GetComponent<BulletObj>();
            bulletObj.SetFather(fatherObj);

            //补 刚体里的Constraints为轴向约束
        }
    }
}

4、主玩家相关

PlayerObj

using UnityEngine;

public class PlayerObj : TankBaseObj
{
    //当前装备的武器
    public WeaponObj nowWeapon;
    //武器安装位置
    public Transform weaponPos;

    void Update()
    {
        //控制前进后退 1.Transform 位移 2.Input 轴向输入检测
        transform.Translate(Input.GetAxis("Vertical") * Vector3.forward * moveSpeed * Time.deltaTime);
        //控制旋转 1.Transform 旋转 2.Input 轴向输入检测
        transform.Rotate(Input.GetAxis("Horizontal") * Vector3.up * roundSpeed * Time.deltaTime);
        //鼠标左右移动 控制炮台旋转
        tankHead.transform.Rotate(Input.GetAxis("Mouse X") * Vector3.up * headRoundSpeed * Time.deltaTime);
        //开火
        if (Input.GetMouseButtonDown(0))
        {
            Fire();
        }
    }
    public override void Fire()
    {
        if (nowWeapon != null)
            nowWeapon.Fire();
    }
    public override void Dead()
    {
        //base.Dead();
        Time.timeScale = 0;
        LostPanel.Instance.ShowMe();
    }
    public override void Wound(TankBaseObj other)
    {
        base.Wound(other);
        //更新血条
        GamePanel.Instance.UpdataHP(maxHp, hp);
    }
    /// <summary>
    /// 切换武器
    /// </summary>
    /// <param name="obj">Weapon</param>
    public void ChangeWeapon(GameObject weapon)
    {
        if(nowWeapon != null)
        {
            Destroy(nowWeapon.gameObject);
            nowWeapon = null;
        }
        //实例化随机到的武器       将weaponPos设置为weapon的父对象,false保持缩放大小
        GameObject weaponObj = Instantiate(weapon, weaponPos, false);
        //挂载武器脚本
        nowWeapon = weaponObj.GetComponent<WeaponObj>();
        //设置武器拥有者为Player
        nowWeapon.SetFather(this);
    }
}

摄像机跟随 CameraMove

using UnityEngine;

public class CameraMove : MonoBehaviour
{
    //摄像机目标位置
    public Transform targetPlayer;
    public float H = 17;

    private Vector3 pos;

    void LateUpdate()
    {
        if (targetPlayer == null)
            return;
        //x/z和玩家一样
        pos.x = targetPlayer.position.x;
        pos.z = targetPlayer.position.z;
        //通过外部调整摄像机高度
        pos.y = H;
        transform.position = pos;
    }
}

5、敌人相关

MonsterObj

using UnityEngine;

public class MonsterObj : TankBaseObj
{
    #region 变量
    //当前目标点
    private Transform targetPos;
    //随机用的点,外面关联
    public Transform[] randomPos;
    //敌方坦克盯着的目标
    public Transform lookAtTarget;
    //靠近一定范围自动开火
    public float fireDis = 5;
    //间隔时间
    public float fireOffsetTime = 2;
    private float nowTime = 0;

    //开火位置
    public Transform[] shootPos;
    //子弹预设体位置
    public GameObject bulletObj;
    //血条显示时间
    private float showTime;

    public Texture maxHpBK;
    public Texture hpBK;

    private Rect maxHpRect;
    private Rect hpRect;
    #endregion



    void Start()
    {
        RandomPos();

    }

    void Update()
    {
        //看向自己的目标点
        transform.LookAt(targetPos);
        //不停的往自己的面朝向移动
        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
        //判断距离
        if (Vector3.Distance(transform.position, targetPos.position) < 0.1f)
        {
            RandomPos();
        }

        if (lookAtTarget != null)
        {
            tankHead.LookAt(lookAtTarget);
            if (Vector3.Distance(transform.position, lookAtTarget.position) <= fireDis)
            {
                nowTime += Time.deltaTime;
                if (nowTime >= fireOffsetTime)
                {
                    Fire();
                    nowTime = 0;
                }
            }
        }
    }
    private void RandomPos()
    {
        if (randomPos == null)
            return;
        targetPos = randomPos[Random.Range(0, randomPos.Length)];
    }
    public override void Fire()
    {
        for (int i = 0; i < shootPos.Length; i++)
        {
            GameObject obj = Instantiate(bulletObj, shootPos[i].position, shootPos[i].rotation);
            BulletObj bullet = obj.GetComponent<BulletObj>();
            bullet.SetFather(this);
        }
    }
    public override void Dead()
    {
        base.Dead();
        GamePanel.Instance.AddScore(10);
    }
    //血条UI
    private void OnGUI()
    {
        if (showTime > 0)
        {
            showTime -= Time.deltaTime;
            //血条位置:先把世界位置转换成屏幕位置,再转换成GUI位置
            Vector3 screenPos = Camera.main.WorldToScreenPoint(transform.position);
            //screenPos.y -= Screen.height;
            //screenPos.y =screenPos.y - Screen.height;

            screenPos.y = Screen.height - screenPos.y;
            //绘制GUI
            maxHpRect.x = screenPos.x - 50;
            maxHpRect.y = screenPos.y - 70;

            maxHpRect.width = 100;
            maxHpRect.height = 15;
            GUI.DrawTexture(maxHpRect, maxHpBK);

            hpRect.x = screenPos.x - 50;
            hpRect.y = screenPos.y - 70;

            hpRect.width = (float)hp / maxHp * 100f;
            hpRect.height = 15;
            GUI.DrawTexture(hpRect, hpBK);
        }
    }
    public override void Wound(TankBaseObj other)
    {
        base.Wound(other);
        showTime = 2;
    }
}

MonsterTower

using UnityEngine;

public class MonsterTower : TankBaseObj
{
    //间隔开火
    //开火间隔时间
    public float fireOffsetTime = 1;
    private float nowtTime = 0;
    //子弹发射位置
    public Transform[] shootPos;
    //关联子弹预设体
    public GameObject bulletObj;

    void Update()
    {
        nowtTime += Time.deltaTime;
        if (nowtTime >= fireOffsetTime)
        {
            Fire();
            nowtTime = 0;
        }
    }
    public override void Fire()
    {
        for (int i = 0; i < shootPos.Length; i++)
        {
            //根据发射位置个数实例化子弹
            GameObject obj = Instantiate(bulletObj, shootPos[i].position, shootPos[i].rotation);
            //获取子弹脚本
            BulletObj bullet = obj.GetComponent<BulletObj>();
            //获取子弹所有者
            bullet.SetFather(this);
        }
    }
    public override void Wound(TankBaseObj other)
    {
        //base.Wound(other); 不用父类的Wound伤害计算方法
    }
}

6、通关点

EndPoint

using UnityEngine;

public class EndPoint : MonoBehaviour
{
    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            //暂停游戏
            Time.timeScale = 0;
            //通过逻辑
            WinPanel.Instance.ShowMe();
        }
    }
}

3、结束界面

1、游戏胜利界面

WinPanel

using UnityEngine;
using UnityEngine.SceneManagement;

public class WinPanel : BasePanel<WinPanel>
{
    public CustomGUIInput inputInfo;
    public CustomGUIButton btnSure;

    void Start()
    {
        //取消游戏暂停
        Time.timeScale = 1;
        //监听将数据记录到排行榜中
        btnSure.clickEvent += () =>
        {
            GameDataMgr.Instance.AddRankInfo(inputInfo.content.text, 
                GamePanel.Instance.nowScore, 
                GamePanel.Instance.nowTime);
            //返回开始界面
            SceneManager.LoadScene("BeginScene");
        };
        HideMe();
    }
}

2、游戏失败界面

LosePanel

using UnityEngine;
using UnityEngine.SceneManagement;

public class LostPanel : BasePanel<LostPanel>
{
    public CustomGUIButton btnBack;
    public CustomGUIButton btnGoOn;
    void Start()
    {
        btnBack.clickEvent += () =>
        {
            Time.timeScale = 1;
            SceneManager.LoadScene("BeginScene");

        };
        btnGoOn.clickEvent += () =>
        {
            Time.timeScale = 1;
            SceneManager.LoadScene("GameScene");
        };
        HideMe();
    }
}

3、游戏打包

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

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

相关文章

如何使用极狐GitLab CI/CD Component Catalog?【上】

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门面向中国程序员和企业提供企业级一体化 DevOps 平台&#xff0c;用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规&#xff0c;而且所有的操作都是在一个平台上进行&#xff0c;省事省心省钱。可以一键安装极狐GitL…

SQL进阶技巧:Hive如何巧解和差计算的递归问题?【应用案例2】

目录 0 问题描述 1 数据准备 2 问题分析 3 小结 0 问题描述 有如下数据:反应了每月的页面浏览量 现需要按照如下规则计算每月的累计阅读量,具体计算规则如下: 最终结果如下: 1 数据准备 with data as( select 2024-01 as month ,2 as pv union all select 2024-02 …

使用MongoDB构建AI:Jina AI将突破性开源嵌入模型变为现实

Jina AI创立于2020年&#xff0c;总部位于德国柏林&#xff0c;主要从事提示工程和嵌入模型业务&#xff0c;已迅速成长为多模态AI领导者。Jina AI积极推动开源和开放研究&#xff0c;致力于弥合先进AI理论与开发者及数据科学家构建的AI驱动型真实世界应用程序之间的差距。目前…

卷积神经网络 - 池化(Pooling)篇

序言 在深度学习的广阔领域中&#xff0c;卷积神经网络&#xff08; CNN \text{CNN} CNN&#xff09;以其卓越的特征提取能力&#xff0c;在图像识别、视频处理及自然语言处理等多个领域展现出非凡的潜力。而池化&#xff08; Pooling \text{Pooling} Pooling&#xff09;作为…

智慧水务项目(四)django(drf)+angular 18 配置REST_FRAMEWORK

一、说明 建立了几个文件 二、一步一步来 1、建立json_response.py 继承了 Response&#xff0c; 一共三个函数&#xff0c;成功、详情&#xff0c;错误 from rest_framework.response import Responseclass SuccessResponse(Response):"""标准响应成功的返回…

springboot共享汽车租赁管理系统-计算机毕业设计源码99204

目 录 第 1 章 引 言 1.1 选题背景及意义 1.2 研究前期调研 1.3 论文结构安排 第 2 章 系统的需求分析 2.1 系统可行性分析 2.1.1 技术方面可行性分析 2.1.2 经济方面可行性分析 2.1.3 法律方面可行性分析 2.1.4 操作方面可行性分析 2.2 系统功能需求分析 2.3 系统…

机械学习—零基础学习日志(高数18——无穷小与无穷大)

零基础为了学人工智能&#xff0c;真的开始复习高数 学习速度加快&#xff01; 无穷小定义 这里可以记住&#xff0c;无穷小有一个特殊&#xff0c;那就是零。 零是最高阶的无穷小&#xff0c;且零是唯一一个常数无穷小。 张宇老师还是使用了超实数概念来讲解无穷小。其实是…

《马拉松名将手记:42.195公里的孤独之旅》大迫杰之舞

《马拉松名将手记&#xff1a;42.195公里的孤独之旅》大迫杰之舞 大迫杰&#xff0c;日本田径长跑选手。2020年3月1日&#xff0c;在东京马拉松赛上&#xff0c;以2小时5分29秒获得日本本土冠军&#xff0c;刷新自己保持的日本国家记录&#xff0c;并拿下东京奥运会马拉松项目入…

UE5 从零开始制作跟随的鸭子

文章目录 二、绑定骨骼三、创建 ControlRig四、创建动画五、创建动画蓝图六、自动寻路七、生成 goose八、碰撞 和 Physics Asset缺点 # 一、下载模型 首先我们需要下载一个静态网格体&#xff0c;这里我们可以从 Sketchfab 中下载&#xff1a;Goose Low Poly - Download Free …

黑暗之魂和艾尔登法环有什么联系吗 黑暗之魂和艾尔登法环哪一个好玩 苹果电脑怎么玩Windows游戏 apple电脑可以玩游戏吗

有不少游戏爱好者对于艾尔登法环与经典游戏黑魂之间是否存在关系产生了疑问。在新旧元素的融合中&#xff0c;艾尔登法环注定成为一场别具匠心的冒险之旅。在实机演示中类魂的玩法以及黑魂相似的画风让不少玩家想要了解本作与黑魂是否有联系&#xff0c;今天&#xff0c;我们将…

SAP生产版本维护以及注意事项

事务代码&#xff1a;C223/MM02&#xff0c;CS01,CA01 步骤&#xff1a; CS01创建BOM CA01创建工艺路线 C223/MM02创建生产版本 选择BOM清单 注意&#xff1a; 1、该生产版本与BOM清单绑定在一起&#xff0c;后续如果BOM有多个&#xff0c;需要添加或修改这个生产版本 2、…

进化版:一个C++模板工厂的编译问题的解决。针对第三方库的构造函数以及追加了的对象构造函数。牵扯到重载、特化等

原始版本在这里 一个C模板工厂的编译问题的解决。针对第三方库的构造函数以及追加了的对象构造函数。牵扯到重载、特化等-CSDN博客 问题 1、关于类型的判断&#xff0c;适应性不强 比如template <typename T>IsFarElementId<>&#xff0c;目前只能判断FarElemen…

达梦数据库的系统视图v$cacheitem

达梦数据库的系统视图v$cacheitem 达梦数据库的系统视图V$CACHEITEM的作用是显示缓存中的项信息&#xff0c;在 ini 参数 USE_PLN_POOL !0 时才统计。这个视图帮助数据库管理员监控和分析缓存的使用情况&#xff0c;优化数据库性能。通过查询V$CACHEITEM视图&#xff0c;可以获…

ai web 1.0靶机漏洞渗透详解

一、导入靶机 解压下载好的靶机&#xff0c;然后打开VMware&#xff0c;点击文件》打开》找到刚刚解压的靶机点击下面的文件》打开 确认是靶机的网络连接模式是NAT模式 二、信息收集 1、主机发现 在本机的命令窗口输入ipconfig查看VMnet8这块网卡&#xff0c;这块网卡就是虚…

案例分享—国外优秀ui设计作品赏析

国外UI设计创意迭出&#xff0c;融合多元文化元素&#xff0c;以极简风搭配动态交互&#xff0c;打造沉浸式体验&#xff0c;色彩运用大胆前卫&#xff0c;引领界面设计新风尚 同时注重用户体验的深度挖掘&#xff0c;通过个性化定制与智能算法结合&#xff0c;让界面不仅美观且…

代码随想录第十八天|动态规划(2)

目录 01背包问题——二维数组 01背包问题——一维数组 LeetCode 416. 分割等和子集 LeetCode 1049. 最后一块石头的重量 II LeetCode 494. 目标和 LeetCode 474. 一和零 总结 01背包问题——二维数组 有n件物品和一个最多能放入重量为w的背包。第i件物品的重量是weight…

web基础之CSS

web基础之CSS 文章目录 web基础之CSS一、CSS简介二、基本用法2、CSS应用方式2.1 行内样式2.2内部样式2.3外部样式 三、选择器1、标签选择器2、类选择器3、ID选择器4、选择器的优先级 四、常见的CSS属性1、字体属性2、文本属性3、背景属性4、表格属性5、盒子模型的属性6、定位 总…

LabVIEW激光主动探测系统

开发了一种基于LabVIEW的高性能激光主动探测控制与处理系统的设计与实现。该系统充分利用了LabVIEW的多线程和模块化设计优势&#xff0c;提供了一套功能完整、运行高效且稳定的解决方案&#xff0c;适用于高精度激光探测领域。 项目背景 激光主动探测技术利用激光作为主动光源…

dp专题(二)

洛谷&#xff1a;B3626 跳跃机器人 题目描述 地上有一排格子&#xff0c;共 nn 个位置。机器猫站在第一个格子上&#xff0c;需要取第 nn 个格子里的东西。 机器猫当然不愿意自己跑过去&#xff0c;所以机器猫从口袋里掏出了一个机器人&#xff01;这个机器人的行动遵循下面…

【面试题】【C语言】寻找两个正序数组的中位数

寻找两个正序数组的中位数 仅供学习 题目 算法时间复杂度 二分查找算法&#xff0c;时间复杂度为 O(log(min(m, n)))&#xff0c;其中 m 和 n 分别是两个数组的长度。 子函数 查找两个数字的最大值 int max(int a, int b) {return a > b ? a : b; }查找两个数字的最小…