将设计模式运用于游戏设计:适配器模式

目前休闲类游戏平台众多,竞争激烈,很多有创意的小游戏是靠某个人、或者某群人灵感突然爆发和艰苦的工作后而产生。嗯——哈哈,于是我摇身一变登上了历史舞台。Myself小组设计的游戏由于大红大紫被联众相中(幻想中),联众想直接把这套游戏收购置于自己的平台上!但是,现实问题来了——Myself小组的这款游戏和联众游戏的接口并不相同,咋办?神阿~9958~~一把年纪了还在啃烧饼。请神之前先让我们来看看代码。

 

#include <stdio.h>
#include <map>
#include <string>

// 我方平台游戏的接口
class IMyGame
{
public:
    // 我方游戏平台用的是账号名登陆
    virtual bool LogIn(const char* const pName) = 0;
    // 我方游戏平台用的是账号名登出
    virtual bool LogOut(const char* const pName) = 0;
    // 我方游戏平台把游戏命令和数据分开,传入游戏逻辑
    virtual bool ExecGameLogic(int nCmd, void* pVoid) = 0;
};


// 我方平台的麻将游戏
class CMyMajiang:public IMyGame
{
public:
    bool LogIn(const char* const pName)
    {
        printf( "log in 麻将 ");
        return true;
    }

    bool LogOut(const char* const pName)
    {
        printf( "log out 麻将 ");
        return true;
    }

    bool ExecGameLogic(int nCmd, void* pVoid)
    {
        // 将命令和数据丢给游戏逻辑,这里做个模拟
        switch( nCmd )
        {
        case 1: // 假定nCmd==1是出牌
            {
                printf( "打出代号为%d的牌 ", *(int*)(pVoid) );
                break;
            }
        default:
            {
                printf( "其他逻辑 " );
            }

        }
        return true;
    }
};


// 对方平台的游戏接口
class ILZGame
{
public:
    // 对方游戏平台用的是账号名、和账号ID登陆
    virtual bool UserIn(int nUserID, const char* const pName) = 0;
    // 对方游戏平台用的是账号ID登出
    virtual bool UserOut(int nUserID) = 0;
    // 对方游戏平台把命令和逻辑数据捆绑在一起丢给游戏逻辑
    virtual bool ExecLogic(void* pVoid) = 0;
protected:
    int GetCommand(void* pVoid)
    {
        // pVoid 的前四个字节存储命令
        int nCmd;
        memcpy( &nCmd, pVoid, 4 );
        return nCmd;
    }

    std::map<int, std::string> m_vUserMap;
};

 

对比一下 IMyGame, ILZGame。这是两个游戏平台的游戏接口,不论接口名还是接口参数他们都是不一样的。毕竟Myself的team leader没想过会让联众收购这款游戏。但是这两个接口类表达了同一组行为,登陆、登出、执行游戏逻辑,在这点上两个接口类是相似的。所以能不能把ImyGame的接口转化为ILZGame所期望的接口呢?答案是能~~我们请到了财神的邻居——适配器模式。在这个例子中ILZGame就是适配目标,因为我们想把ImyGame变成——至少看起来是ILZGame,这样联众平台就认识我们的游戏了。想到如何遗传接口了吗?是的,使用继承,用继承的方式把ILZGame的躯壳(接口)继承下来,但内部组合了一个IMyGame这样就做到了形式上统一,但内部还是IMyGame在干活。代码如下:

 

// 适配器
// 把ILZGame的躯壳(接口)继承下来,但内部使用的是IMyGame
// 这样就做到了形式上统一,但内部还是我干我的
class CGameAdapter:public ILZGame
{
public:
    CGameAdapter(IMyGame* pGame)
    {
        m_pGame = pGame;
    }

    bool UserIn(int nUserID, const char* const pName)
    {
        m_vUserMap.insert( std::make_pair(nUserID, pName) );
        m_pGame->LogIn( pName );

        return true;
    }

    bool UserOut(int nUserID)
    {
        std::map<int, std::string>::iterator iter = m_vUserMap.find( nUserID );
        if( iter == m_vUserMap.end() )
            return false;

        m_pGame->LogOut( iter->second.c_str() );
        m_vUserMap.erase( iter );

        return true;
    }

    bool ExecLogic(void* pVoid)
    {
        int nCmd = GetCommand( pVoid );
        char* p = (char*)pVoid;
        p += 4;
        return m_pGame->ExecGameLogic( nCmd, p );
    }

private:
    IMyGame* m_pGame;
};

如此我们解决了不同平台之间的接口相容问题。J

类结构图如下:

 

将设计模式运用于游戏设计:适配器模式_第1张图片

执行代码如下:

// 平台
class CGameManager
{
public:
    bool Init()
    {
        m_pGame2 = new CGameAdapter( new CMyMajiang );
        return true;
    }
    ILZGame* m_pGame2;
};


typedef struct _INPUTGAME
{
    int nCmd;
    int nCode;
}INPUTGAME;

int main()
{
    CGameManager GameManager;
    GameManager.Init();


    GameManager.m_pGame2->UserIn(1, "zqy");


    // 模拟一个客户的操作发送给服务器
    INPUTGAME data;
    data.nCmd = 1;
    data.nCode = 1000;

    GameManager.m_pGame2->ExecLogic( (void*)&data );


    GameManager.m_pGame2->UserOut(1);

    getchar();
    return 0;
}

你可能感兴趣的:(设计模式,游戏,String,login,平台,pair)