第九章 技能系统
与物品系统类似,技能系统也是一个较为复杂的系统,其中UI设计部分涉及到一些新知识,进行重点学习。
9.1 物品信息管理系统
技能管理系统的大致操作与背包系统类似。
9.1.1 技能信息的设计
技能信息的保存格式如下
我们在Atlas中新创建一个记事本存储技能信息,与物品信息类似,命名为SkillInfoInList,内容如下(部分信息进行了修改)
5001,魔法弹,skill-09,伤害 350%,SingleTarget,Attack,350,0,20,10,Magician,1,Enemy,5 5002,治疗,skill-13,治愈30HP,Passive,HP,30,0,10,20,Magician,2,Self,0 5003,冥想,skill-10,魔法恢复20,Passive,MP,20,0,0,30,Magician,5,Self,0 5004,法力涌动,skill-05,攻击力为200%持续15秒,Buff,Attack,200,15,30,30,Magician,7,Self,0 5005,战斗热诚,skill-12,攻击力速度为200%持续30,Buff,AttackSpeed,200,30,30,30,Magician,8,Self,0 5006,究极风暴,skill-11,攻击力400% 所有敌人,MultiTarget,Attack,400,0,50,40,Magician,9,Position,10
为了便于管理物品信息和技能信息,我们新建一个TextInfo来存储这两个txt文档
9.1.2 实现技能信息的读取
与ObjectsInfo类似,我们在GameSetting中添加一个SkillsInfo以访问读取技能信息,在脚本中设定各种属性
public enum ApplyRole{ //适用角色 Swordman, Magician } public enum ApplyType{ //适用类型 Passive, SingleTarget, MultiTarget } public enum ApplyProperty{ //适用属性 Attack, Aefense, AttackSpeed, HP, MP } public enum ReleaseType{ //释放类型,对自身,敌人(需要鼠标指定)和某个位置(鼠标指定) Self, Enemy, Position } public class SkillInfo{ //技能信息表中对应的各条属性 public int id; public string name; public string icon_name; public string des; public ApplyType applyType; public ApplyProperty applyProperty; public int applyValue; public int applyTime; public int mpCost; public int coldTime; public ApplyRole applyRole; public int level; public ReleaseType releaseType; public float releaseDistance; }
在SkillsInfo中,将技能信息存入字典的操作如下
public static SkillsInfo _instance; public TextAsset skillInfoListText; private Dictionary<int,SkillInfo> skillInfoDict = new Dictionary<int,SkillInfo>(); void Awake() { _instance = this; ReadInfo (); }
其中ReadInfo ()的实现可参考物品信息(见背包系统),再提供一个外部的访问接口,通过这个接口即可访问id对应的技能信息
public void GetSkillInfoListText(int id) { SkillInfo skillInfo = null; skillInfoDict.TryGetValue (id, out skillInfo); return skillInfo; }
9.2 UI界面的设计
9.2.1 基础技能界面的创建
在界面中新建一个Scroll View存储所有的技能,以及一组Invisible Widget用来显示单个技能,如下图所示。在Invisible Widget下再创建两个Sprite,一个用来显示技能图标,另一个作为技能描述的背景。
在技能描述中,再新建4个Label,显示其余信息,完成后如下图所示。完成之后,将其保存为Prefab,之后根据不同职业动态添加技能栏信息。
为了实现技能拖拽到快捷栏的效果,在SkillItem中加上Box Collider与Drag Scroll View。
在运行时即可实现上下拖动的效果,这么拖动显然不方便,我们在技能栏的右边加入一个滚动条(可以搜索control),设置一下Depth即可
之后指定一下SkillGrid中ScrollView中的Scroll Bar即可
我们将预先设置好的Grid大小设置为单个技能的大小
可以通过Grid可以直接对技能进行排序,将SkillItem拖放到Prefabs之下
为了管理SkillItem的内容,我们为其添加一个SkillItem的脚本,代码如下
void InitProperty() { iconName = transform.Find ("IconName").GetComponent<UISprite> (); //找到每个Sprite/Label的对应控件 name_label = transform.Find ("Property/Name").GetComponent<UILabel> (); skillType_label = transform.Find ("Property/SkillType").GetComponent<UILabel> (); skillDes_label = transform.Find ("Property/SkillDes").GetComponent<UILabel> (); cost_label = transform.Find ("Property/Cost").GetComponent<UILabel> (); } void SetId(int id) //初始化并在外部调用,进行创建,更新显示 { InitProperty (); this.id = id; info = SkillsInfo._instance.GetSkillInfoFromDict (id); iconName.spriteName = info.icon_name; name_label.text = info.name; switch (info.applyType) { case SkillsInfo.ApplyType.Passive: skillType_label.text = "增益"; break; case SkillsInfo.ApplyType.Buff: skillType_label.text = "增强"; break; case SkillsInfo.ApplyType.SingleTarget: skillType_label.text = "单体"; break; case SkillsInfo.ApplyType.MultiTarget: skillType_label.text = "群体"; break; } skillDes_label.text = info.des; cost_label.text = info.mpCost.ToString(); }
通过判断当前角色是魔法师或者剑士,我们选择不同的技能列表,在Skill脚本中
public int[] MagicianSkillList; public int[] SwordmanSkillList; private PlayerStatus ps; void Start() { ps = GameObject.FindGameObjectWithTag (Tags.player).GetComponent<PlayerStatus> (); int[] idList = null; switch (ps.role) { case playerRole.Magician: idList = MagicianSkillList; break; case playerRole.Swordman: idList = SwordmanSkillList; break; } }
其中MagicianSkillList和SwordmanSkillList的设置根据
SkillInfoInList得到
在确定当前角色后,我们调用SetId方法创建技能面板,在Skill脚本中,添加
public UIGrid grid; //控制所有技能创建的网格,控制所有skillItemPrefab public GameObject skillItemPrefab; //单个技能面板 void Start() { foreach (int id in idList) //遍历idList之中的id信息 { GameObject itemGO = NGUITools.AddChild(grid.gameObject,skillItemPrefab); //对于每一个id,在grid之中添加技能信息的Prefab grid.AddChild(itemGO.transform); itemGO.GetComponent<SkillItem>().SetId(id); //调用SetId方法,更改技能的显示 } }
即可创建出技能栏的内容,如下所示。
9.2.2 技能的拖拽功能
为SkillItem下的IconName添加脚本SkillItemIcon,控制拖动效果,勾选Clone On Drag。将SkillItemIcon脚本继承自UIDragDropItem,以实现拖拽功能。(需删除Start()方法和Update()方法,否则会出错),如下所示,但此时无法拖拽出技能栏。
因此我们需要让克隆的图标按钮能够移出ScrollView,在SkillItemIcon中添加
public class SkillItemIcon : UIDragDropItem{ protected override void OnDragDropStart () { base.OnDragDropStart (); transform.parent = transform.root; //把父类放到UI root下 this.GetComponent<UISprite> ().depth = 40; } }
对6个快捷栏添加box collider,并将它们的标签设置为ShortCut,在SkillItemIcon中,通过如下代码得到id
public int skillId; skillId = transform.parent.GetComponent<SkillItem> ().id;
之后通过OnDragDropRelease函数实现随意拖拽效果
protected override void OnDragDropRelease (GameObject surface) { base.OnDragDropRelease (surface); if (surface != null && surface.tag == Tags.shortcutItem) { surface.GetComponent<Shortcut>().SetSkill(skillId); //实现设置技能的效果,下节实现 } }
9.3 技能的快捷方式
我们创建一个Invisible Widget用以存储技能栏的快捷方式,通过快捷方式我们可以方便地使用技能。在Invisible Widget中新建6个Sprite,命名为Shortcut1~6,用以存储这6个技能,为1~6的快捷方式建立Shortcut脚本,在每个Sprite下创建Child Label作为数字提示(做成Prefab可能更好),如下所示
在拖拽的时候,我们需要创建技能图标,因此在Shortcut1~6中创建Child Sprite作为技能显示图标,并且由于技能快捷栏开始是不显示的, 设置它的active为false。并且我们增加id和info用以读取字典中技能的信息,在SkillItemIcon中通过调用Shortcut中的SetSkill完成,其中SetSkill函数如下
public enum ShortType{ Skill, Null } private ShortType type = ShortType.Null; //默认为Null,即默认情况下按下快捷键不显示 private int id; private SkillsInfo.SkillInfo info; public void SetSkill(int id) //外界进行调用 { type = ShortType.Skill; this.id = id; this.info = SkillsInfo._instance.GetSkillInfoFromDict (id); icon.spriteName = info.icon_name; }
即可将技能拖入快捷方式之中。
9.4 人物等级对技能的限制
接下来考虑任务等级对技能的影响,当人物等级小于技能要求等级时,无法使用拖动技能按钮,因此对SkillItem的Prefab添加Child Sprite,里面储存禁用技能的图标。在SkillItem脚本中添加并在初始化中设置为隐藏。
private GameObject iconMask; iconMask.SetActive (false); void InitProperty() { iconMask = transform.Find ("IconMask").gameObject; }
之后设置一个实时根据等级信息改变技能显示与否的功能函数,当每次点击功能面板的技能图标时调用
public void UpdateItem(int level) //level从别的函数调用,判断当前等级 { if (level < info.level) //info.level对应技能等级,当前等级小于技能等级时,显示iconMask并禁用拖拽功能 { iconMask.SetActive (true); iconName.GetComponent<SkillItemIcon> ().enabled = false; } else { iconMask.SetActive (false); iconName.GetComponent<SkillItemIcon> ().enabled = true; } }
调用位置在Skill脚本中,添加调用UpdateItem(int level)的函数,如下
public void UpdateShow() { SkillItem[] items = this.GetComponentsInChildren<SkillItem> (); foreach (SkillItem item in items) { item.UpdateItem(ps.level); } }
之后在void Show()中调用,即
void Show() { isShow = true; this.gameObject.SetActive (true); UpdateShow (); //调用 UpdateShow (); tween.PlayForward (); }
即可实现该功能