游戏物品系统通用架构

游戏物品系统通用架构,物品系统是游戏中最最常用的功能之一,面对各种纷繁复杂的游戏物品系统,在具体项目中如何顺手拈来、驾驭自如?请容我一一到来。在内容前我有必要说明几点,所有内容都是基于我最大的架构思想之下,其中 客户端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

· Unity3D, 游戏架构, 游戏通用模块

感谢您的耐心阅读

Thanks for your reading

  • 版权申明

    本文为博主原创文章,未经允许不得转载:

    游戏物品系统通用架构

    Copyright attention

    Please don't reprint without authorize.

  • 微信公众号,文章同步推送,致力于分享一个资深程序员在北上广深拼搏中对世界的理解

    QQ交流群: 777859752 (高级程序书友会)