仿arrow.io之人物属性

上一篇提到了人物属性,并声称没有这个做不下去了。其实我没有说假话。因为接下来就会实现战斗的主要逻辑了,游戏的战斗在代码里其实是一堆数字的四则运算。这堆数字就是以被我们冠以 属性 名头的东西为载体的。比如玩dota的就常说:“拍拍吃了一个急速,下路小心了!”、“你这小脆皮,PA能一刀把你暴死!”等等这些术语其实都在描述了一个属性在游戏里的作用。好了,话不多说,屁不多放,进入正题。

arrow.io里的人物属性其实不多,也不复杂。数了数大概有以下这些属性:

  • 血量值hp
  • 移动速度speed
  • 攻击力attack
  • 防御值deffence
  • 暴击值critical
  • 吸血值vampair

按照策划的思路就是用Excel来配数据,假设我们有了N个角色可以供玩家选择。当然有些角色就像arrow.io一样是需要金币来购买,因为他们有一些与众不同的特性。然后我就啪啪啪地弄了下面的表:

其中第二行是每个字段的数据类型。
有了这张excel表怎么能为我所用呢?这需要:

  • 将Excel导出用TAB隔开的数据(全选复制后粘贴到一个.txt文本文件里,最好是写一个VBA程序自动导出)
  • 写一个脚本能把这个数据文件生成项目能用的.cs代码文件


生成的脚本:

using System.Collections.Generic;
public class roleTemplateContainer
{
    public class roleTemplate
    {
        public string id;
        public string name;
        public int hp;
        public float speed;
        public int def;
        public int atk;
        public float critical;
        public float dodge;
        public string res_path;
    }


    private Dictionary<string,roleTemplate> tplData = new Dictionary<string,roleTemplate>()
    {
        {"10001",new roleTemplate{id="10001",name="傻逼",hp=4,speed=1,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10002",new roleTemplate{id="10002",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10003",new roleTemplate{id="10003",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10004",new roleTemplate{id="10004",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10005",new roleTemplate{id="10005",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10006",new roleTemplate{id="10006",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10007",new roleTemplate{id="10007",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10008",new roleTemplate{id="10008",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10009",new roleTemplate{id="10009",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}},
        {"10010",new roleTemplate{id="10010",name="傻逼",hp=4,speed=4,def=0,atk=0,critical=0,dodge=0,res_path="prefabs/role"}}

    };
    public Dictionary<string,roleTemplate> TplData 
    {
        get{return tplData;}
    }
}

这个工具脚本我就不粘出来了,可以在我github项目里找。

有了这个属性模板类,还需要经他实例化并赋给玩家才算是真的有了属性。于是我又做了一个属性的接口:

using UnityEngine;
using System.Collections;

public enum AttributeName
{
    ATTR_EXP,
    ATTR_HP,
    ATTR_DEF,
    ATTR_ATK,
    ATTR_SPD,
    ATTR_CRT,
    ATTR_DDG,
}

public interface IArrowAttributes {
    /// <summary>
    /// 经验值
    /// </summary>
    int Exp { get; set; }
    /// <summary>
    /// 血量值
    /// </summary>
    int Hp { get; set; }
    /// <summary>
    /// 防御值
    /// </summary>
    int Deffence { get; set; }
    /// <summary>
    /// 攻击值
    /// </summary>
    int Attack { get; set; }
    /// <summary>
    /// 移速
    /// </summary>
    float Speed { get; set; }
    /// <summary>
    /// 暴击概率
    /// </summary>
    float Critical { get; set; }
    /// <summary>
    /// 闪避概率
    /// </summary>
    float Dodge { get; set; }
    /// <summary>
    /// 属性变化事件
    /// </summary>
    event ArrowAttributeChange attributeChgEvent;

    /// <summary>
    /// 设置属性的值为某值
    /// </summary>
    /// <param name="attrName">属性名称</param>
    /// <param name="value">属性新的值</param>
    void SetAttribute(AttributeName attrName, object value);
    /// <summary>
    /// 设置属性变化值
    /// </summary>
    /// <param name="attrName">属性名称</param>
    /// <param name="value">变化值,正数是加,负数是减</param>
    void ChangeAttribute(AttributeName attrName, object value);
}

这个接口其实就是MVC里的M层,即数据逻辑层,一个好的数据Model不仅能方便存取数据,还至少要有通知外界数据变化的能力,这个很重要,这样就可以让多个用了这个model数据的界面能够及时地刷新界面。用户也可以得到及时且正确的反馈,非常棒的一个机制。
玩家的属性类实现了上面的接口:

using System;
using System.Collections.Generic;

public class RoleAttribute : IArrowAttributes
{
    private int _attack;
    public int Attack
    {
        get
        {
            return _attack;
        }

        set
        {
            _attack = value;
            Notify(AttributeName.ATTR_ATK, _attack);
        }
    }

    private float _critical;
    public float Critical
    {
        get
        {
            return _critical;
        }

        set
        {
            _critical = value;
            Notify(AttributeName.ATTR_CRT, _critical);
        }
    }

    private int _deffence;
    public int Deffence
    {
        get
        {
            return _deffence;
        }

        set
        {
            _deffence = value;
            Notify(AttributeName.ATTR_DEF, _deffence);
        }
    }

    private float _dodge;
    public float Dodge
    {
        get
        {
            return _dodge;
        }

        set
        {
            _dodge = value;
            Notify(AttributeName.ATTR_DDG, _dodge);
        }
    }

    private int _hp;
    public int Hp
    {
        get
        {
            return _hp;
        }

        set
        {
            _hp = value;
            Notify(AttributeName.ATTR_HP, _hp);
        }
    }

    private float _speed;
    public float Speed
    {
        get
        {
            return _speed;
        }

        set
        {
            _speed = value;
            Notify(AttributeName.ATTR_SPD, _speed);
        }
    }

    private int _exp;
    public int Exp
    {
        get
        {
            return _exp;
        }

        set
        {
            _exp = value;
            Notify(AttributeName.ATTR_EXP, _exp);
        }
    }

    public event ArrowAttributeChange attributeChgEvent;
    public RoleAttribute(string roleId)
    {
        roleTemplateContainer tpl = new roleTemplateContainer();
        _hp = tpl.TplData[roleId].hp;
        _attack = tpl.TplData[roleId].atk;
        _deffence = tpl.TplData[roleId].def;
        _critical = tpl.TplData[roleId].critical;
        _speed = tpl.TplData[roleId].speed;
        _dodge = tpl.TplData[roleId].dodge;
    }

    public void ChangeAttribute(AttributeName attrName, object value)
    {
        switch(attrName)
        {
            case AttributeName.ATTR_ATK:
                Attack += (int)value;
                break;
            case AttributeName.ATTR_CRT:
                Critical += (float)value;
                break;
            case AttributeName.ATTR_DDG:
                Dodge += (int)value;
                break;
            case AttributeName.ATTR_DEF:
                Deffence += (int)value;
                break;
            case AttributeName.ATTR_EXP:
                Exp += (int)value;
                break;
            case AttributeName.ATTR_HP:
                Hp += (int)value;
                break;
            case AttributeName.ATTR_SPD:
                Speed += (int)value;
                break;
        }
        Notify(attrName, value);
    }

    public void SetAttribute(AttributeName attrName, object value)
    {
        switch (attrName)
        {
            case AttributeName.ATTR_ATK:
                Attack = (int)value;
                break;
            case AttributeName.ATTR_CRT:
                Critical = (float)value;
                break;
            case AttributeName.ATTR_DDG:
                Dodge = (int)value;
                break;
            case AttributeName.ATTR_DEF:
                Deffence = (int)value;
                break;
            case AttributeName.ATTR_EXP:
                Exp = (int)value;
                break;
            case AttributeName.ATTR_HP:
                Hp = (int)value;
                break;
            case AttributeName.ATTR_SPD:
                Speed = (int)value;
                break;
        }
        Notify(attrName, value);
    }

    private void Notify(AttributeName attrName, object value)
    {
        if (value is int)
        {
            if ((int)value == 0)
            {
                return;
            }
        }
        else if (value is float)
        {
            if ((float)value == 0)
            {
                return;
            }
        }
        if (null != attributeChgEvent)
        {
            attributeChgEvent.Invoke(attrName, value);
        }
    }
}

有了这个属性类,就可以用了。
玩家控制类:

using UnityEngine;
using System.Collections;

public class RoleController : MonoBehaviour
{
    IInputControl inputCtrl;
    CharacterController chCtrl;

    RoleAttribute attributes;
    public RoleAttribute Attributes
    {
        get { return attributes; }
    }

    // Use this for initialization
    void Start()
    {
        inputCtrl = Camera.main.GetComponent<IInputControl>();
        inputCtrl.onMove += OnMove;
        inputCtrl.onRotate += OnRotate;
        inputCtrl.onFire += OnFire;
        chCtrl = GetComponent<CharacterController>();
        attributes = new RoleAttribute("10001");
    }

    void OnDisable()
    {
        inputCtrl.onMove -= OnMove;
        inputCtrl.onFire -= OnFire;
    }

    // Update is called once per frame
    void Update()
    {
    }

    void OnMove(Vector3 dir)
    {
        chCtrl.SimpleMove(dir.normalized * attributes.Speed);
    }

    void OnFire()
    {
        Debug.Log("Fire In The Hole!");
    }

    void OnRotate(Vector3 screenPoint)
    {
        Ray ray = Camera.main.ScreenPointToRay(screenPoint);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 1 << LayerMask.NameToLayer("Ground")))
        {
            transform.LookAt2D(hit.point);
        }
    }
}

先不管流程性的东西,这里用了10001号角色,这样就可以在控制人物移动的地方用上属性里的 speed属性

    void OnMove(Vector3 dir)
    {
        chCtrl.SimpleMove(dir.normalized * attributes.Speed);
    }

而不是以前的这样了:

    void OnMove(Vector3 dir)
    {
        chCtrl.SimpleMove(dir.normalized * 5.0F);
    }

好的,至此就算是用上了策划配置的数据了。这个玩家控制类是需要重构最多的地方,现在只是为了测试方便而这样写了,有些不符合流程。
以后还会根据需要不断地往里面添加模板数据,使用流程都如人物属性这般。
有了属性就可以写一下子弹方面的东西了,这是个大模块,也非常复杂,但我不会停止,不管写的好坏,都要写一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值