1. 开始场景搭建

创建场景,自己去搭建,没有技术难点。

丰富场景视觉效果

可以设置一些脚本,让场景中的物品来回移动。

比如,让金币上下移动和旋转。

using UnityEngine;
public class GoldUpDown : MonoBehaviour
{
    [SerializeField]float upDownSpeed = 0.05f;
    [SerializeField] float rotateSpeed = 130f;
    void Update()
    {
        transform.Translate(0, upDownSpeed * Time.deltaTime, 0, Space.Self);
        transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
        
        if(transform.position.y > 3.2)
        {
            upDownSpeed = -upDownSpeed;
        }else if(transform.position.y < 2.8)
        {
            upDownSpeed = -upDownSpeed;
        }
    }
}

2. UI面板框架搭建

主要是面板的显示与隐藏,还有多个按钮的点击事件,执行不同的方法。

BasePanel类

SettingPanel类

  • 点击保存,将当前的设置保存到本地

  • 滑动音量时,改变它的大小

BeginPanel类

RankSlot类

卡槽,排行榜的一条显示UI信息。可以修改这条信息的排行、名字、分数、通关时间。

RankPanel类

控制排行榜面板的一些UI信息。

  • 控制关闭按钮

  • 控制实时排行显示:读取数据类拿到信息,实例化排行卡槽

GamePanel类

  • 点击设置

  • 设置血条

QuitPanel类

  • 关闭该面板

  • 继续游戏按钮

  • 确定退出按钮

  • 计时器

3. 数据框架搭建

PlayerPrefsDataMgr类

这是一个工具类,用于将数据对象类保存在本地。提供两个方法。

注意;这个类我们只将属性字段保存到本地。

  • public object Load(Type type, string keyName)

    • 通过名字从本地加载数据到object里面,如果使用的话,可以进行强转即可

    • type就是你要保存的数据类的类型,可以通过 typeof(类名)

  • public void Save(object obj, string keyName)

    • 将传过来的对象保存到数据中

using System;
using System.Collections;
using System.Reflection;
using UnityEngine;

namespace DY.Tools
{
    /// <summary>
    /// 数据持久化管理类,对数据进行保存和加载
    /// 定义key的规则:keyName_类名_属性类型_属性名
    /// 只适用于:int、float、bool、string、List、Direction、自定义类型
    /// </summary>
    public class PlayerPrefsDataMgr
    {
        private static PlayerPrefsDataMgr instace = new PlayerPrefsDataMgr();

        public static PlayerPrefsDataMgr Instance {
            get {
                return instace;
            }
        }

        private PlayerPrefsDataMgr() { }

        /// <summary>
        /// 对传入的数据进行保存
        /// </summary>
        /// <param name="obj">传入的对象</param>
        /// <param name="keyName">唯一的标识</param>
        public void Save(object obj, string keyName)
        {
            // 获得传入对象的属性
            if (obj == null)
            {
                throw new ArgumentNullException("传入的obj参数为空");
            }
            Type type = obj.GetType();
            // 1. 获得类中所有的公共成员属性
            // 2. 定义key的规则:keyName_类名_属性类型_属性名
            string savekeyName = "";
            PropertyInfo[] info = type.GetProperties();
            for (int i = 0; i < info.Length; i++)
            {
                PropertyInfo member = info[i];
                savekeyName = keyName + "_" + type.Name + "_" + member.PropertyType.Name + "_" + member.Name;
                SaveValue(member.GetValue(obj), savekeyName);
            }
        }

        private void SaveValue(object value, string savekeyName)
        {
            Type type = value.GetType();
            if(type == typeof(int))
            {
                PlayerPrefs.SetInt(savekeyName, (int)value);
            } else if(type == typeof(float))
            {
                PlayerPrefs.SetFloat(savekeyName, (float)value);
            }else if(type == typeof(string))
            {
                PlayerPrefs.SetString(savekeyName, (string)value);
            }else if(type == typeof(bool))
            {
                // 自定义bool类型的存储规则
                PlayerPrefs.SetInt(savekeyName, (bool)value ? 1 : 0);
            }else if (typeof(IList).IsAssignableFrom(type))  // 存储泛型
            {
                IList lists = value as IList;
                PlayerPrefs.SetInt(savekeyName, lists.Count);
                for(int i = 0; i < lists.Count; i++)
                {
                    SaveValue(lists[i], savekeyName + i);
                }
            }else if (typeof(IDictionary).IsAssignableFrom(type)) // 存储字典
            {
                IDictionary dictionary = value as IDictionary;
                PlayerPrefs.SetInt(savekeyName, dictionary.Count);
                int index = 0;
                foreach (var key in dictionary.Keys)
                {
                    SaveValue(key, savekeyName + "_key_" + index);
                    SaveValue(dictionary[key], savekeyName + "_value_" + index);
                    ++index;
                }
            }else // 不是基本数据类型,而是自定义类型
            {
                Save(value, savekeyName);
            }
        }

        /// <summary>
        /// 通过唯一标识,加载type对应的类型
        /// </summary>
        /// <param name="type">对象类型</param>
        /// <param name="keyName">唯一表示</param>
        /// <returns></returns>
        public object Load(Type type, string keyName)
        {
            // 创建实例
            object obj = Activator.CreateInstance(type);

            // 通过反射获得属性
            PropertyInfo[] propertyInfos = type.GetProperties();

            string loadkeyname = "";
            for(int i = 0;i<propertyInfos.Length; i++)
            {
                PropertyInfo memberProperty = propertyInfos[i];
                // key的规则:keyName_类名_属性类型_属性名
                loadkeyname = keyName + "_" + type.Name + "_" + memberProperty.PropertyType.Name + "_" + memberProperty.Name;
                memberProperty.SetValue(obj, LoadValue(memberProperty.PropertyType, loadkeyname));
            }
            return obj;
        }

        private object LoadValue(Type type, string loadkeyname)
        {
            object value = null;
            if(type == typeof(int))
            {
                value = PlayerPrefs.GetInt(loadkeyname);
                return value;
            }else if(type == typeof(float))
            {
                value = PlayerPrefs.GetFloat(loadkeyname);
                return value;
            }else if(type == typeof(string))
            {
                value = PlayerPrefs.GetString(loadkeyname);
            }else if(type == typeof(bool))
            {
                value = PlayerPrefs.GetInt(loadkeyname) == 1;
            }else if(typeof(IList).IsAssignableFrom(type))
            {
                IList lists = Activator.CreateInstance(type) as IList;
                int listLen = PlayerPrefs.GetInt(loadkeyname);
                for(int i = 0; i < listLen; i++)
                {
                    lists.Add(LoadValue(type.GenericTypeArguments[0], loadkeyname + i));
                }
                return lists;
            }else if (typeof(IDictionary).IsAssignableFrom(type))
            {
                IDictionary dictionary = Activator.CreateInstance(type) as IDictionary;
                int dicLen = PlayerPrefs.GetInt(loadkeyname);
                for(int i = 0; i < dicLen; i++)
                {
                    dictionary.Add(LoadValue(type.GenericTypeArguments[0], loadkeyname + "_key_" + i),
                        LoadValue(type.GenericTypeArguments[1], loadkeyname + "_value_" + i));
                }
                return dictionary;
            }
            else
            {
                value = Load(type, loadkeyname);
            }

            return value;
        }
    }
}

GameDataManager类

用于管理各种数据的

  • 项目一但启动,就会从本地加载数据

  • 如果某个数据改变了,就会调用这个类里面的方法,将数据保存到本地,所以说这个类也是单例的。

MusicData类

与声音有关的数据,必须保证里面的数据必须是属性字段。

RankInfo类和RankListData类

RankInfo类:一条排行榜信息

RankListData类:存储所有的排行信息

4. 音频框架搭建

BackGroundMusic类

控制背景音乐的声音

  • 一开始时,从本地读取音量,从而设置背景音乐

5. 游戏场景搭建

没有什么技术难点。就是耗时间,自己去搭建。

6. 坦克行为

TankBase类

玩家和敌人坦克的一些公共属性,如攻击力、防御力、血量、最大血量等。

玩家和敌人坦克的一些公共行为,如受伤,死亡 等。

PlayerTank类

  • ws 前进

  • ad 旋转

  • 鼠标控制炮台方向

  • 开火

7. 地图

创建另外一个摄像机。俯视看着坦克。

  • 跟随坦克