角色系统

角色系统

大部分的编程工作都是围绕角色系统展开的。


角色系统框架

角色系统是一个类型,它挂在Role下面作为其成员。它保存着游戏中各种各样的数据,比如坐骑系统保存了玩家已经获得的坐骑,坐骑的等级,坐骑的装备等等。

像这样的系统的框架是十分类似的,其类型定义如:

class Mount
{
public:
    Mount() : m_role(0) {}
    void SetRole(Role *role) { m_role = role; }
    void InitParam(const MountParam &param) { m_param = param; }
    void GetInitParam(MountParam *param) { *param = m_param; }

private:
    Role *m_role;
    CharIntAttrs m_attrs_add;
    MountParam m_param;
};

Mount类型保存一个Role指针,绑定到拥有它的角色。一定要在Role的构造函数执行完毕前调用SetRole方法初始化m_role,否则就是一场灾难(服务器随时准备崩溃)。参见方法Role::SetSubSystemRole

InitParam用于初始化系统数据。GetInitParam用于保存系统数据。如果忘记调用,数据将存取失败。

m_attrs_add是系统的属性,这个属性是通过系统的其他数据根据配置计算出来的,比如3级的坐骑可以加成1000的血量,100的速度,100的防御。

m_param则是系统的具体数据,需要存储到数据库中的。一个典型的SomeParam定义文件如:

// mountdef.h
#ifndef __MOUNT_DEF_H__
#define __MOUNT_DEF_H__

#pragma pack(push, 4)

struct MountParam
{
    MountParam() { this->Reset(); }
    void Reset()
    {
        // members reset
    }

    // members
};

typedef char MountParamHex[sizeof(MountParam) * 2 + 1];
UNSTD_STATIC_CHECK(sizeof(MountParamHex) < 64);

#pragma pack(pop)

#endif // __MOUNT_DEF_H__

由于这个数据会在进程间通信,甚至用于协议通讯,所以要确定字节对齐大小,并使成员紧密排列。且要进行序列化,因此绝不能存储指针、迭代器这种东西。

  1. 也不要存储unsigned long这样的在32/64位机器上长度不固定的类型,对于使用位运算的数据成员,要使用unsigned类型。对于数组类型,考虑给数组元素预留长度。
  2. 项目中需要字节对齐的地方只有两处,一处是协议类型,一处是存储类型。

MountParamHex将存储到数据表中的一个字段里,它是MountParam的十六进制字符串形式。

协议文件

我们把与Mount系统相关的协议存放到一个文件中管理。文件内容大致如下:

// msgmount.h
#ifndef __MSG_MOUNT_H__
#define __MSG_MOUNT_H__

#include "servercommon/mountdef.hpp"

#pragma pack(push, 4)

namespace Protocol
{
    class CSMountOperaReq
    {
    public:
        CSMountOperaReq();
        MessageHeader header;
        // some info
    };

    class SCMountInfo
    {
    public:
        SCMountInfo();
        MessageHeader header;
        // some info
    };
}

#pragma pack(pop)

#endif // __MSG_MOUNT_H__

这里面CS代表Client To Server,SC代表Server To Client,对于CS协议,我们要注册一个回调函数,绑定到其协议号上,这个回调函数定义在MessageHandler中。

客户端的协议必须和服务端保持一致,不然就会出现各种奇怪的问题。

当开发中出现问题的时候,应当第一时间从协议入手,首先检查一下协议有没有对齐,然后检查协议处理函数有没有正确注册,然后再看看客户端发过来的参数是否正确。

配置文件

我们设计好配置文件后,还必须自己编写对应的代码。目前这份工作都是由服务端完成的,当系统开发完毕后,才把配置交给策划去修改。

一份配置文件大致如:

#ifndef __MOUNT_CONFIG_H__
#define __MOUNT_CONFIG_H__

#include "servercommon/configcommon.h"
#include "servercommon/mountdef.hpp"

class MountConfig
{
public:
    bool Init(const std::string &configname, std::string *err);

private:
    // some config
};

#endif // __MOUNT_CONFIG_H__

这个配置类型挂在LogicConfigManager下面,它是一个单例对象,因此可以在任何地方获取系统的配置。

角色系统是Role的从属

在游戏中,当玩家上下坐骑后,速度属性就会发生变化,这是怎么实现的呢?我们看一下代码中的调用过程:


MountManager::GoonMount
    Role::ReCalcAttr
        ...
            MountManager::ReCalcAttr

我们通过调用Role::ReCalcAttr来重算玩家的属性,这个函数会“通知”MountManager::ReCalcAttr重算坐骑的属性。我们没有直接调用MountManager::ReCalcAttr是因为这个方法只会重新计算坐骑系统的属性,它还需要被加成到Role的总属性上面。

把角色系统当成Role的从属,从属提供事件处理接口供外部调用,当Role触发了某一个事件时,它就可以通知其子系统来更新自身的状态。

猜你喜欢

转载自blog.csdn.net/jonWei/article/details/82319535
今日推荐