[Unity C++] 前端+后端商城和背包系统

[Unity C++] 前端+后端商城和背包系统

基于Unity前端和C++后端实现的联网商城和背包系统,玩家登录后从服务器拉取自身背包和金钱数据,能够从商城购买物品并将物品加入自身背包,玩家退出后,背包信息存储在服务器中

  • 前端:使用Unity实现商城和背包UI,采用MVC模式组织数据的获取、存储和展现
  • 后端:基于C++ Libuv实现网络通信,运行于Windows平台,使用ProtoBuf传输数据
  • 实现玩家登录、玩家数据拉取、商城购买逻辑,利用版本号实现商城的限购

网络通信

在执行业务逻辑前,需要建立客户端和服务器的底层网络链接(TCP)

自定义的协议内容:“TC”(两字节)+包体长度(两字节)+操作码CMD(两字节)+包体内容

其中包体内容通过ProtoBuf编解码

基本的连接建立和收发消息流程如下

网络通信

业务逻辑

建立底层网络连接后便可处理业务逻辑了。业务逻辑主要有登录逻辑和商城逻辑两大部分。

玩家登录逻辑

玩家登录逻辑

登录请求中有玩家ID和密码,登录回复中有登录结果,成功/失败原因,玩家数据

商城逻辑

在这里插入图片描述

通过使用版本号来减少商城加载操作的同步量。

服务器每次接受到一次成功的购买操作就会递增服务器的版本号,客户端在加载商城时若发送的版本号与客户端相同则无需发送商城完整信息;同样,当客户端接收到购买成功回复时,若服务器版本号之比自身版本号大一表明这段时间内只有自己进行了购买,因此只需自己更新商店信息而无需重新想服务器请求,否则应想服务器重新请求商城信息

服务器实现

PlayerMgr

服务器的PlayerMgr类负责处理玩家相关业务逻辑,包括玩家的创建、登录和玩家信息的存取与维护

struct Player {
    string PlayerID;
    string Password;
    string Name;
    int money;
    map<int, ShopItem*> bagItems;
};

class PlayerMgr {
public:
    PlayerMgr();
    ~PlayerMgr();

    bool init();
    bool un_init();

    // 处理用户请求
    bool player_login(uv_tcp_t* client, const PlayerLoginReq* req);
    bool player_create(uv_tcp_t* client, const PlayerCreateReq* req);
    
    Player* find_player(string playerID);

    // 有链接断开
    bool on_client_close(uv_tcp_t* client);

    // 背包
    bool changeBagItems(uv_tcp_t* client, const ShopItem* Item, int num = 1);
private:
    // 从文件中读取和存储玩家信息
    int _load_player(string playerID, PlayerSaveData* playerData);
    bool _save_player(const Player* player);
public:
    map<uv_tcp_t*, Player*> m_playerMap;
};

Player结构体用于存储玩家信息,包括玩家ID,昵称,密码和背包数据

PlayerMgr类中有player_loginplayer_create负责处理玩家登录和创建请求,m_playerMap维护了当前登录的玩家信息,用于防止玩家重复登录和更新玩家背包金钱等业务逻辑需要

Shop

Shop类负责处理商城业务逻辑,包括加载和购买请求等

class Shop
{
public:
	Shop();
	~Shop();

	// 初始化,从json文件加载商品到map
	bool init();
	// 存储商品信息到json文件,清理map内存
	bool un_init();

	//处理用户请求
	bool shop_load(uv_tcp_t* client, const ShopLoadReq* req);
	bool shop_buy(uv_tcp_t* client, const ShopBuyReq* req);
private:

	map<int, ShopItem*> _shopItemsMap;

	int _buySequence;

	// 解析json文件,并写入map
	int _parseShopCfg(const char* const monitor);
	// 从json文件中加载商品到map
	bool _load_items();
	// 存储商品map到json文件
	bool _save_items();
};

_shopItemsMap维护了当前商城列表,_buySequence即商城版本号

客户端实现

客户端采用MVC模式组织商城数据的获取、存储和展现

客户端MVC

  • View层由UI和挂载在UI上的脚本组成

  • Controller为单例负责解耦数据存储和数据展示,其中方法有的注册在UI组件中,有的注册在网络组件中

    public class ShopController : MonoBehaviour
    {
        private Shop _shop;
        private Bag _bag;
    
        private ShopPanel _shopView;
        private BackpackPanel _bagView;
    
        private Network _network;
    	
        ...
    }
    

    可以看到Controller中包含View层和Module层的引用,解耦数据展示和数据存储,Network用于网络通信

  • Module分为Shop和Bag负责存储商城和背包数据以及处理业务逻辑

商城和背包业务逻辑流程均由上面七步操作组成

背包逻辑

背包的业务逻辑不牵扯网络通信,因此较为简单

  1. 用户点击背包按钮
  2. 回调注册在按钮中的Controller.UpdateBagView()
  3. Controller调用Bag.getBagItems()
  4. 无需处理,直接返回背包数据
  5. Bag返回背包数据
  6. Controller调用View.Update()显示背包信息
// 注册在背包按钮中
public void UpdateBagView()
{
	_bagView.ShowItems(GetBagItems());
}
// 从Model层获得数据
public List<ShopItem> GetBagItems()
{
	return _bag.GetBagItems();
}

商城加载逻辑

商城加载过程分为发送请求和处理回包两部分

  • 请求商店

    1. 点击购买按钮
    2. 回调注册在按钮中的Controller.LoadShopItems()
    3. Controller调用Shop.LoadShop()获取请求包
    4. 构造请求包并返回
    5. 返回请求包
    6. 调用网络组件发送商城加载请求
    // 注册在商店按钮中
    public void LoadShopItems()
    {
    	ShopLoadReq req = _shop.LoadShop();
    	_network.SendMsg((int)CLIENT_CMD.ClientShopLoadReq, req);
    }
    
  • 处理回包

    1. 收到商城加载回包
    2. 回调注册在网络组件中的Controller.OnShopLoadRsp()
    3. Controller调用Shop.LoadShopRsp()处理请求包
    4. 根据回包更新本地商城数据
    5. Controller调用GetShopItems(), _bag.GetPlayerMoney()获得商店数据和玩家金钱
    6. 更新View
    public void OnShopLoadRsp(int cmd, IMessage msg)
    {
    	// 处理返回包
    	_shop.LoadShopRsp((ShopLoadRsp)msg);
    	// 更新View
    	UpdateShopView();
    }
    public void UpdateShopView()
    {
    	_shopView.ShowItems(GetShopItems(), _bag.GetPlayerMoney());
    }
    

购买逻辑

购买逻辑同样分为网络请求和处理回包两部分

  • 请求商店
    1. 点击购买按钮
    2. 回调注册在按钮中的Controller.Buy()
    3. Controller调用Shop.Buy()获取请求包
    4. 自检成功后则构造并返回请求包,否则返回null
    5. 返回请求包
    6. 调用网络组件发送商城购买请求
  • 处理回包
    1. 收到商城购买回包
    2. 回调注册在网络组件中的Controller.OnShopBuyRsp()
    3. Controller调用Bag.BuyRsp()处理请求包
    4. 根据回包更新本地商城数据和背包数据
    5. Controller调用GetShopItems(), _bag.GetPlayerMoney()获得商店数据和玩家金钱
    6. 更新View

效果展示

https://www.bilibili.com/video/BV1pY4y1M7RQ

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值