游戏物品系统通用架构
游戏物品系统通用架构,物品系统是游戏中最最常用的功能之一,面对各种纷繁复杂的游戏物品系统,在具体项目中如何顺手拈来、驾驭自如?请容我一一到来。在内容前我有必要说明几点,所有内容都是基于我最大的架构思想之下,其中 客户端U3D架构思想的文章地址为 Unity3D游戏架构 ,以及服务器架构:《王图霸业》战争策略游戏服务器架构。
直入主题
首先,我们先归纳一下物品的基本属性。 物品基本属性:名字,描述,数量,最大可叠加数,图标 装备物品扩展属性:装备位置,攻击力,防御力,暴击率,魔力,血量,等等 消耗物品扩展属性:BUFF增加,HP增加,最大HP增加,等等
在游戏项目中,把这些物品的扩展属性,单个提取出来组合成各种游戏中需要的物品,如何做到?
1.把所有属性都写进物品数据配置表中?数据表过大,导致最终项目人员更改数据缓慢,错误不断,甚至连写此系统的程序员自己也吃不消。
2.把每个物品都按单各类进行特殊处理?假设物品有100种,那程序员就得写100各类实例,每增加一个写一个,浪费人力浪费时间。
3.以物品基类的继承的形式,将各种物品分类处理,归类,派生?繁琐,分类时间过长,效率过低,程序员最终会为物品该归到哪一类和策划人员而打起来。
如何才能做到物品系统适应不同的游戏需要,又能使得物品配置方便扩展性强?在这里我引入一个Action概念,Action以动作的概念来理解。当物品使用时,就调用不同的Action来达到不同的效果,例如:加血100加魔法200的物品,点击后,该物品执行加血100的Action实例,接着执行加魔200的实例,完毕后我们就能看到我们需要的效果了。所以物品只需要对应N各Action就可以完全做到你想做的,甚至可以做到你想不到的。
我们能发现,其实物品的很多功能是可以用不同单个功能组合起来的,比如加血,比如加魔,比如加BUFF状态,把这些相同功能的Action都做成一个,然后把不同的Action做成一个集合是多么完美的事,既达到了数据持续化存储和更新能力,又可使得物品功能完全由策划人员自行配置,发挥他们的无限想象力。
现在来看看实际中Action是如何编写的。把加血动作写成 类ActionAddHp,类中参数一个:HP,把加魔法动作写成 类ActionAddMP,类中参数一个:MP,这两个Action从数据表中读取相应的参数数据,变成众多Action实例里的一个,由ActionManager管理存储。到此回头看看,所有继承自Action类的扩展类拥有几个共同的属性:ID(唯一标识,用来区分所有Action),RunType(用来区分不同类型的Action),Read接口(用来读取数据),Excute接口(用来执行Action)。我在此之上加上一个Action类型,把所有Action拆分为条件类Action和执行类Action,条件类Action主要用来判断该物品的使用条件是否满足,执行类Action主要用来执行该动作的效果。因此Action基类就成了这样:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// CAction.cs
// Author: Lu Zexi
// 2013-12-24
//base action
public abstract class CAction : IAction
{
private int m_iID; //id
// id (0-99999) , actionType(0-99) , runType(0-99)
public CAction(int id, int actionType, int runType)
{
this.m_iID = actionType*10000000 + runType*100000 + id;
}
public abstract ActionError Excute(ActionInput input);
//roll back action
public virtual ActionError RollBack(ActionInput input)
{
return ActionError.NoError;
}
//get id
public int ID
{
get
{
return this.m_iID;
}
}
}
有人会问,那如何让物品对应好几个Action呢?我再引入一个Action事件类,把它定义为类EventAction,他里面存储着16个Action ID,前8个ActionID是条件型动作,后8个ActionID为执行型动作。物品的数据中就有一个EventAction的ID,由这个EventAction来判断该执行那些Action,是否满足物品执行条件。当EventAction执行时,首先执行前8个Action,看看是否满足物品执行条件,如果不满足,则不执行后8个效果,如果满足再去执行后8个Action效果。因此EventAction也有很多各实例,由ID来区分不同的EventAction,由EventActionManager来管理所有事件Action。EventAction类结构:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// EventAction.cs
// Author: Lu zexi
// 2013-12-24
/// <summary>
/// Action event
/// </summary>
public class EventAction
{
private int m_iID; //ID
private int[] m_vecActions; //Action ID list
private int[] m_vecReqActions; //Require Action ID list
public EventAction()
{
this.m_vecActions = new int[8];
this.m_vecReqActions = new int[8];
}
// id (0-99999) , actionType(0-99) , runType(0-99)
public EventAction( int id , int eventType , int runType)
{
this.m_iID = eventType * 10000000 + runType*100000 + id;
}
//get id
public int ID
{
get
{
return this.m_iID;
}
}
//set action
public void SetAction( int[] act )
{
this.m_vecActions = act;
}
//set require
public void SetRequire( int[] req )
{
this.m_vecReqActions = req;
}
//excute action
public ActionError Excute( ActionInput input )
{
for (int i = 0; i < this.m_vecActions.Length; i++)
{
CAction action = ActionManager.sInstance.GetAction(this.m_vecActions[i]);
if (action != null)
{
ActionError error = action.Excute(input);
if ( error != null && error.code != ACTION_ERROR_CODE.NONE)
return error;
}
}
return ActionError.NoError;
}
//action require
public ActionError Require( ActionInput input)
{
for (int i = 0; i < this.m_vecReqActions.Length; i++)
{
CAction action = ActionManager.sInstance.GetAction(this.m_vecReqActions[i]);
if (action != null)
{
ActionError error = action.Excute(input);
if ( error != null && error.code != ACTION_ERROR_CODE.NONE)
return error;
}
}
return ActionError.NoError;
}
//action roll back
public ActionError RollBack(ActionInput input)
{
for (int i = 0; i < this.m_vecActions.Length; i++)
{
CAction action = ActionManager.sInstance.GetAction(this.m_vecActions[i]);
if (action != null)
{
ActionError error = action.RollBack(input);
if (error != null && error.code != ACTION_ERROR_CODE.NONE)
return error;
}
}
return ActionError.NoError;
}
}
以上描述解决了物品中消耗品的问题,物品素材本身就是没有功能的消耗品,在这里我就不介绍了。而下面,我们来看看如何解决物品装备。 装备物品在消耗品上多了几样关于英雄角色的属性,这些属性有的明,有的暗,那我们就把那些明的属性提取出来,例如加增攻击力,增加防御力。而那些暗的属性,再拆分两种,一种是直接写进明的配置表中而不显示在界面上,另一个是直接写进动作Action里,当需要时执行相应EventAction实例。
最后,我们看看整个物品系统数据配置和系统源码。
物品类:名字,物品类型(消耗品,装备,素材),描述,数量,最大叠加数量,使用等级,EventActionID,装备位置,攻击力,防御力,HP,MP
Action表: ID , 描述 , ActionType , RunType , 参数1,参数2...
EventAction表: ID,描述,
ReqActionID1,ReqActionID2,...ReqActionID8,
ActionID9,ActionID10,...ActionID16.
源码:https://github.com/luzexi/Unity3DItemSystem
测试案例:https://github.com/luzexi/Unity3DItemSystem-Test
转载请注明出处:http://www.luzexi.com
感谢您的耐心阅读
Thanks for your reading
版权申明
本文为博主原创文章,未经允许不得转载:
Copyright attention
Please don't reprint without authorize.
微信公众号,文章同步推送,致力于分享一个资深程序员在北上广深拼搏中对世界的理解
QQ交流群: 777859752 (高级程序书友会)