上一篇提到了人物属性
,并声称没有这个做不下去了。其实我没有说假话。因为接下来就会实现战斗的主要逻辑了,游戏的战斗在代码里其实是一堆数字的四则运算。这堆数字就是以被我们冠以 属性
名头的东西为载体的。比如玩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);
}
好的,至此就算是用上了策划配置的数据了。这个玩家控制类是需要重构最多的地方,现在只是为了测试方便而这样写了,有些不符合流程。
以后还会根据需要不断地往里面添加模板数据,使用流程都如人物属性这般。
有了属性就可以写一下子弹方面的东西了,这是个大模块,也非常复杂,但我不会停止,不管写的好坏,都要写一下。