Unity使用新输入系统InputSystem制作飞机大战Demo(实现得分系统)

发布于:2022-11-28 ⋅ 阅读:(571) ⋅ 点赞:(0)

请添加图片描述

@作者 : SYFStrive

@博客首页 : HomePage

📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗

📌:觉得文章不错可以点点关注 👉:专栏连接🔗

💃:程序员每天坚持锻炼💪

请添加图片描述
在这里插入图片描述
相关专栏

👉 飞机大战专栏(🔥)

游戏单例脚本

单例模式是1种设计模式:👉(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

单例使用说明:“单例模式是指在内存中只会创建一次对象的设计模式,并且确保一个类只有实例,而且会自行实例化,并向整个系统提供这个实例。

非持久化泛型单例

using UnityEngine;

//摘要:Base class for everything attached to GameObjects.
//Component中文说明:所有能挂载到游戏对象上的类型基类
public class Singleton<T> : MonoBehaviour where T :Component
{
    public static T Instance { get; private set; }

    protected virtual void Awake()
    {
        Instance = this as T;
    }
}

持久化泛型单例

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

public class PersistentSingleton<T> : MonoBehaviour where T : Component
{
  public static T Instance;

  protected virtual void Awake()
  {
    if(Instance == null)
      Instance = this as T;
    else
      Destroy(this.gameObject);
    DontDestroyOnLoad(this);
  }
}

游戏基类

子弹基类实现子弹移动

实现:子弹生成是就开始移动

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

public class Projectile : MonoBehaviour
{
    //子弹的移动速度
    [SerializeField] float moveSpeed;
    //子弹的移动方向
    [SerializeField] protected Vector3 moveDirection;
    //子弹移动的Obj
    protected GameObject targer;
    
    protected virtual void OnEnable()
    {
        StartCoroutine(ProjectileMoveIE());
    }

    IEnumerator ProjectileMoveIE()
    {
        while (true)
        {
            //子弹移动
            transform.position += moveSpeed * moveDirection * Time.deltaTime;
            yield return null;
        }
    }
}

生命系统的基类

实现:储存人物的血量参数(继承这个脚本的简直爽歪歪)……

代码如 👇

using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Principal;
using UnityEngine;

public class Characters : MonoBehaviour
{
    [Header("---Header---")]
    //最大生命值
    [SerializeField] protected float maxHp;

    //当前生命值
    protected float currentHp;

    //死亡时生成特效
    [SerializeField] GameObject dieSpecialEffects;

    protected virtual void OnEnable()
    {
        currentHp = maxHp;
    }

    /// <summary>
    /// 玩家受伤
    /// </summary>
    /// <param name="injuredValue">伤害值</param>
    protected virtual void Injured(float injuredValue)
    {
        currentHp -= injuredValue;

        if (currentHp <= 0)
            Die();
    }

    /// <summary>
    /// 玩家死亡
    /// </summary>
    public void Die()
    {
        //血量归0
        currentHp=0;    

        //调用对象池
        PoolManager.Release(dieSpecialEffects,transform.position);
		
		隐藏该对象
        this.gameObject.SetActive(false);
    }

    /// <summary>
    /// 恢复生命值
    /// </summary>
    protected virtual void RecoverHP(float value)
    {
        currentHp = Mathf.Clamp(currentHp + value, 0, maxHp);
    }


    /// <summary>
    /// 自动恢复生命值携程
    /// </summary>
    /// <param name="waitForSeconds">恢复的间隔</param>
    /// <param name="value">恢复值</param>
    /// <returns></returns>
    protected virtual IEnumerator SelfRecoverHpIE(WaitForSeconds waitForSeconds,float value)
    {
        while (currentHp < maxHp)
        {
            yield return waitForSeconds;

            RecoverHP(currentHp * value);
        }
    }

    /// <summary>
    /// 持续受伤
    /// </summary>
    /// <param name="waitForSeconds">受伤的间隔</param>
    /// <param name="value">受伤值</param>
    /// <returns></returns>
    protected virtual IEnumerator SelfInjuredIE(WaitForSeconds waitForSeconds, float value)
    {
        while (currentHp >= 0f)
        {
            yield return waitForSeconds;

            Die(currentHp * value);
        }
    }
}

对象池管理器

💡理解:对象池(Object Pool)但从字面理解就是一池子的物体,在我们需要使用的时候就拿出来一个,然后包装成我想用的样子。用完之后清空放回到池子里。

  • 为什么要有对象池呢?
  1. 对象池用于减少内存开销,其原理就是把可能用到到的对象,先存在一个地方(池),要用的时候就调出来,不用就放回去。
  2. 如果我们频繁使用 Instantiate 和 Destroy 来生成后销毁,那么会占用大量的系统内存,甚至导致系统崩溃。
  3. 我们可以利用对象池的设计模式概念,在游戏的一开始就制作若干个数量的物体,将他们设置 SetActive(false) 当我们需要它们的时候,只要把它们设置成 SetActive(true) 就好了,用完了再次设置为false。以此方法来循环使用,以达到优化系统的作用。
  4. 如果不使用对象池,我们不经浪费了很多时间在寻找合适的内存上,更让内存产生了很多的内存碎片。

说明:这里已经添加了这个项目所有的对象池容器

using System.Collections.Generic;
using UnityEngine;

public class PoolManager : MonoBehaviour
{
  //储存不同类准备的对象池
  [SerializeField] Pool[] playerPoolProjectile; //玩家子弹
  [SerializeField] Pool[] enemyPoolProjectile; //敌人子弹
  [SerializeField] Pool[] poolVFX; //特效
  [SerializeField] Pool[] randomCreateEnemy; //随机敌人
  [SerializeField] Pool[] createProp; 敌人掉落的道具

  //使用字典来存储不同的装备
  public static Dictionary<GameObject, Pool> dictionary;

  private void Awake()
  {
    //实例化字典
    dictionary = new Dictionary<GameObject, Pool>();

    //初始化对象池
    InitializeObj(playerPoolProjectile);
    InitializeObj(enemyPoolProjectile);
    InitializeObj(poolVFX);
    InitializeObj(randomCreateEnemy);
    InitializeObj(createProp);
  }


  #region 测试函数
	#if UNITY_EDITOR
	  //停止游戏时执行
	  private void OnDestroy()
	  {
	    CheckPoolSize(playerPoolProjectile);
	    CheckPoolSize(enemyPoolProjectile);
	    CheckPoolSize(poolVFX);
	    CheckPoolSize(randomCreateEnemy);
	    CheckPoolSize(createProp);
	  }
	#endif
  #endregion

  #region 测试需要对象池的容量
	  private void CheckPoolSize(Pool[] pools)
	  {
	    foreach (Pool pool in pools)
	    {
	      if (pool.sumSize > pool.initializeSize)
	      {
	        Debug.LogWarning(string.Format("Pool:{0}初始大小为{1},需要的大小为{2}",
	            pool.prefabeObjProperty.name,
	            pool.initializeSize,
	            pool.sumSize));
	      }
	    }
	  }
  #endregion

  /// <summary>
  /// 初始化子弹
  /// </summary>
  private void InitializeObj(Pool[] pools)
  {
    foreach (var pool in pools)
    {
      #region //条件编译操作 只有在Unity引起运行
		#if UNITY_EDITOR
		      if (dictionary.ContainsKey(pool.prefabeObjProperty))
		      {
		        Debug.Log("字典有相同的名字!"+pool.prefabeObjProperty.name);
		        continue;
		      }
		#endif
      #endregion

      //添加到字典
      dictionary.Add(pool.prefabeObjProperty, pool);
      //给创建的Obj命名
      Transform poolPatent = new GameObject("对象池Poll" + pool.prefabeObjProperty.name).transform;
      //设置父位置
      poolPatent.parent = transform;
      //初始化对象池
      pool.Initialize(poolPatent);
    }
  }

  #region  释放子弹&&重载
  /// <summary>
  /// 释放子弹
  /// </summary>
  /// <param name="prefabe">指定游戏的预制体</param>
  /// <returns></returns>
  public static GameObject Release(GameObject prefabe)
  {
    #region 条件编译操作 只有在Unity引起运行
		#if UNITY_EDITOR
		    if (!dictionary.ContainsKey(prefabe))
		    {
		      Debug.Log("找不到对应的Key");
		      return null;
		    }
		#endif
    #endregion

    return dictionary[prefabe].PrepareQuene();
  }

  /// <summary>
  /// 释放子弹
  /// </summary>
  /// <param name="prefabe">指定游戏的预制体</param>
  /// <param name="position">指定游戏的位置</param>
  /// <returns></returns>
  public static GameObject Release(GameObject prefabe, Vector3 position)
  {
    #region 条件编译操作 只有在Unity引起运行
		#if UNITY_EDITOR
		    if (!dictionary.ContainsKey(prefabe))
		    {
		      Debug.Log("找不到对应的Key");
		      return null;
		    }
		#endif
    #endregion
    return dictionary[prefabe].PrepareQuene(position);
  }


  /// <summary>
  /// 释放子弹
  /// </summary>
  /// <param name="prefabe">指定游戏的预制体</param>
  /// <param name="position">指定游戏的位置</param>
  /// <param name="quaternion">指定游戏的旋转位置</param>
  /// <returns></returns>
  public static GameObject Release(GameObject prefabe, Vector3 position, Quaternion quaternion)
  {
    #region 条件编译操作 只有在Unity引起运行
		#if UNITY_EDITOR
		
		    if (!dictionary.ContainsKey(prefabe))
		    {
		      Debug.Log("找不到对应的Key");
		      return null;
		    }
		#endif
    #endregion

    return dictionary[prefabe].PrepareQuene(position, quaternion);
  }

  /// <summary>
  /// 释放子弹
  /// </summary>
  /// <param name="prefabe">指定游戏的预制体</param>
  /// <param name="position">指定游戏的位置</param>
  /// <param name="quaternion">指定游戏的旋转位置</param>
  /// <param name="localscale">指定游戏的旋转缩放</param>
  /// <returns></returns>
  public static GameObject Release(GameObject prefabe, Vector3 position, Quaternion quaternion, Vector3 localscale)
  {
    #region 条件编译操作 只有在Unity引起运行
		#if UNITY_EDITOR
		
		    if (!dictionary.ContainsKey(prefabe))
		    {
		      Debug.Log("找不到对应的Key");
		      return null;
		    }
		#endif
    #endregion

    return dictionary[prefabe].PrepareQuene(position, quaternion, localscale);
  }
  #endregion
}

得分系统

得分脚本

实现:显示UI得分值同步的得分UI

using UnityEngine;
using UnityEngine.UI;

public class ScoreDisplay : MonoBehaviour
{	
  //得分的文本组件
  public static Text scoreText; 
  private void Awake()
  {
    scoreText = GetComponent<Text>(); 
  }

  private void Start()
  {
    ScoreManager.Instance.ResetScore();
  }

  /// <summary>
  /// 更新分值
  /// </summary>
  /// <param name="scoreValue">更新值</param>
  public static void UpdateScore(int scoreValue)=>scoreText.text = scoreValue.ToString();


  /// <summary>
  /// 文本缩放
  /// </summary>
  /// <param name="TargetScloe">缩放值</param>
  public static void DynamicStateScoleText(Vector3 TargetScloe)=>scoreText.rectTransform.localScale = TargetScloe;
}

得分管理器

实现:实现得分的相关分值

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

public class ScoreManager : PersistentSingleton<ScoreManager>
{

  #region 分数管理
  //属性得分值
  public int Scoring => scoreCount;

  //得分值
  private int scoreCount;
  private int currentCount;

  //缩放目标值
  private Vector3 scoleTargetValue = new Vector3(1.2f,1.2f,1);

  /// <summary>
  /// 初始化分数
  /// </summary>
  public void ResetScore()
  {
    scoreCount = 0;
    currentCount=0;
    ScoreDisplay.UpdateScore(scoreCount);
  }

  /// <summary>
  /// 得分
  /// </summary>
  /// <param name="scoreValue">得分值</param>
  public void AddScore(int scoreValue)
  {
    currentCount += scoreValue;

    StartCoroutine(nameof(dynamicShowTextIE));
  }

  IEnumerator dynamicShowTextIE()
  {
    //文本缩放
    ScoreDisplay.DynamicStateScoleText(scoleTargetValue);

    while (scoreCount<currentCount)
    {
      scoreCount += 5;
      ScoreDisplay.UpdateScore(scoreCount);
      yield return null;  
    }

    ScoreDisplay.DynamicStateScoleText(Vector3.one);
  }
  #endregion
}

效果

在这里插入图片描述

最后

在这里插入图片描述
本文到这里就结束了,大佬们的支持是我持续更新的最大动力,希望这篇文章能帮到大家💪

 

                 相关专栏连接🔗
在这里插入图片描述

下篇文章再见ヾ( ̄▽ ̄)ByeBye

在这里插入图片描述

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到