上一节中我们了解了ScriptableObject的用法,这一节让我们来实现一个卡牌存储器。
使用了我很喜欢的游戏杀戮尖塔的设计和素材


设计理念

不知道大家有没有玩过杀戮尖塔,示例卡牌如下。

我们可以看到,一张卡牌包括名称消耗类型描述卡牌背景卡牌底图等部分构成,而且在游戏中,玩家的属性可以影响到卡牌的强度,如玩家的力量可以提升卡牌的伤害,玩家的敏捷可以提升玩家获得护甲的多少。
我遵从杀戮尖塔的设计,制作了如下的卡牌编辑器,实现了,数据可视化存储,并且让卡牌强度与玩家挂钩,使用这个存储器可以创建任意数据的卡牌。

设计思考

卡牌应当具有可加成的数值和不可加成的数值,在设计时我将用大写字母占位代表可加成数值,小写字母占位代表不可加成数值。
可加成数值需要从玩家Asset来提取数值。
不论是可加成还是不可加成占位,都将在使用时被替换成具体的数值。

效果预览


卡牌存储器从上自下可以分成材质,数据,处理三个部分。

  • 材质:存储卡牌的卡面,类型,品质等。
  • 数据:存储卡牌的名称,能力消耗,表达式,描述,基础数据,玩家加成数据等。
  • 处理:读入数据和加载入玩家数据。

代码实现

在项目中我试用了NaughtyAttributes来对检视面板数据做进一步处理,详细NaughtyAttributes可查看github

PlayerAsset.cs

using System.Collections.Generic;
using System.Linq;
using NaughtyAttributes;
using UnityEngine;

namespace Asset
{
    [CreateAssetMenu(fileName = "NEW PlayerAsset", menuName = "MyAsset/PlayerAsset", order = 0)]
    public class PlayerAsset : ScriptableObject
    {

        public int maxHp;
        public int currentHp;
        public int strength;
        public int agility;
        private static PlayerAsset _instance;
        public static PlayerAsset Instance
        {
            get
            {
                if (!_instance)
                {
                   _instance = Resources.FindObjectsOfTypeAll<PlayerAsset>().FirstOrDefault();;
                }
                if (_instance) return _instance;
                _instance= CreateInstance<PlayerAsset>();
                _instance.hideFlags = HideFlags.DontSave;

                return _instance;
            }
            private set => _instance = value;
        }

    }
}

CardAsset.cs

using System;
using System.Collections.Generic;
using System.Linq;
using NaughtyAttributes;
using UnityEngine;
using UnityEngine.Serialization;

namespace Asset
{
    /// <summary>
    /// 卡牌类型
    /// </summary>
    public enum CardType
    {
        Attack,
        Skill,
    }

    public enum CardQuality
    {
        Write,
        Blue,
        Yellow
    }

    [Serializable]
    public class DictData
    {
        public char key;
        public int data;

        public DictData(char key,int data)
        {
            this.key = key;
            this.data = data;
        }
    }

    [CreateAssetMenu(fileName = "NewCard", menuName = "MyAsset/CardAsset", order = 0)]
    public class CardAsset : ScriptableObject
    {
        [BoxGroup("材质")] public CardType cardType;
        [BoxGroup("材质")] public CardQuality cardQuality;
        [BoxGroup("材质")] [ShowAssetPreview()] public Sprite cardSprite;

        [BoxGroup("数据")] [MinValue(0), MaxValue(5), Tooltip("消耗")]
        public int expend;

        [BoxGroup("数据")] public string title;

        [BoxGroup("数据")] [ResizableTextArea] public string express;
        [BoxGroup("数据")] [ResizableTextArea] public string desc;

        [BoxGroup("数据"),Label("基础数据")] [ReorderableList] public DictData[] dictData;
        [BoxGroup("数据"),Label("玩家加成数据")] [ReorderableList] public List<DictData> dictAddData;
        /// <summary>
        /// 写入配置
        /// </summary>
        [Button("写入配置")]
        public void InitCardAsset()
        {
            desc = string.Empty;
            //字典方式
            foreach (var i in express)
            {
                var currChar = i + "";
                foreach (var t in dictData)
                {
                    if (!t.key.Equals(i)) continue;

                    var addValue = 0;    //加成值
                    //判断该数据是否需要被加成
                    if (t.key>='A'&&t.key<='Z')
                    {
                        addValue = dictAddData.FirstOrDefault(m => m.key == t.key).data;
                    }
                    currChar = t.data+addValue + "";
                    break;
                }

                desc += currChar;
            }
        }
        /// <summary>
        /// 更新卡牌
        /// </summary>
        [Button("写入玩家加成数据")]
        public void UpdateCard()
        {
            dictAddData.Clear();
            dictAddData.Add(new DictData('X',PlayerAsset.Instance.strength));
            dictAddData.Add(new DictData('Y',PlayerAsset.Instance.agility));
            //X-玩家力量 Y-玩家敏捷
 Debug.Log(PlayerAsset.Instance.maxHp+"\t"+PlayerAsset.Instance.currentHp+"\t"+PlayerAsset.Instance.strength);
            //重写
            InitCardAsset();
        }
    }
}