C++实现的手机游戏局域网联机对战库

前言

  我一直想做一个可以局域网联机对战的游戏,但是无论是使用还是蓝牙的方式进行开发都要学习相关平台的知识。其实我去年了解过这方面的知识,据不可靠的资料显示我会遇到这些问题:

  1. IOS的蓝牙不可连接其它厂商的蓝牙设备;
  2. WIFI好像也是和第一条一样;
  3. 如何知道某个蓝牙或者wifi设备所在的手机上启动了我的游戏?

  由于涉及到不同平台,问题还有很多,我也懒得去解决这些问题。于是我就选择了一个懒办法,使用TCP协议来进行局域网联机对战,这样有一个前提就是多台设备必须要在一个局域网内。但是可以做到跨平台,而且也不用那么麻烦。于是 Buddy 库诞生了,这个名字用于纪念童年一起玩小霸王游戏机的小伙伴们。

实现原理

  实现原理很简单,首先主机玩家开一个游戏房间,向局域网内广播主机玩家的地址端口等信息,并启动游戏服务器。其它玩家收到主机玩家广播的消息后(广播信息里面包含了游戏服务器的地址和端口信息),就连接到游戏服务器,从而达到联机对战的目的。这些步骤 Buddy 库已经封装好啦,我们可以直接使用。

使用说明

  使用上也是比较简单的,这里把服务端和客户端的使用分开来讲解,其实它们是非常相似的。

服务端

示例代码:

 1 #include <iostream>
 2 #include "Classes/Buddy.h"
 3 
 4 enum eMessageDelegate
 5 {
 6     BuddyServiceID = (int)ReservedDelegateID::UserCustom + 1,
 7 };
 8 
 9 class SessionManage : public ServiceDelegate
10 {
11 public:
12     virtual void OnPlayerJoin(SOCKET session, const Address &address) override
13     {
14         std::cout << "OnPlayerJoin" << std::endl;
15 
16         char buffer[] = "hello";
17         SendData(session, buffer, sizeof(buffer));
18     }
19 
20     virtual void OnPlayerLeave(SOCKET session, const Address &address) override
21     {
22         std::cout << "OnPlayerLeave" << std::endl;
23     }
24 
25     virtual void OnMessageReceive(SOCKET session, void *data, size_t size) override
26     {
27         std::cout << "OnMessageReceive" << std::endl;
28     }
29 };
30 
31 int main(int argc, char **argv)
32 {
33     InitBuddy();
34 
35     SessionManage manage;
36     BuddyService server(Address(kHostPort), 10, BuddyServiceID, &manage);
37     server.Accept();
38 
39     while (true)
40     {
41         std::this_thread::sleep_for(std::chrono::milliseconds(kFrameDeltaTime));
42         MessageManager::GetInstance()->Update(0);
43         server.Broadcast();
44     }
45 
46     return 0;
47 }

  首先,第4行声明一个枚举类型,用来表示消息代理的ID。Buddy 库内部实现了一个消息队列,需要游戏主循环来更新它。我们可以为一个对象(对象的类类型必须继承自MessageDelegate)指定一个ID,这样向这个ID发送消息后,这个对象的OnMessageReceive函数就会被调用。

  在第9行实现一个类SessionManage,继承自ServiceDelegate。实际上这个类是对所有会话的管理类,当玩家进入或离开或收到玩家发送的数据时,ServiceDelegate类的虚函数将被调用。另外ServiceDelegate类还有提供了其它几个函数来管理玩家。

  第33行是对库进行初始化,这个必须在主线程中调用。

  第36行创建了一个游戏服务器,构造函数的参数分别是服务器的地址、消息代理的ID和服务代理的指针。服务代理其实就是前面说的会话管理类的实例。

  第37行调用Accept后,将会接受局域网内的连接请求,并不会阻塞线程。

  第39行其实是在模拟游戏的主循环,一般是每秒60帧,一帧耗时就是16毫秒。

  第42行是在游戏主循环中更新消息队列。

  第43行是在局域网内广播消息,这样局域网里的玩家才能够发现服务器。

客户端

示例代码:

 1 1 #include <iostream>
 2 2 #include "Classes/Buddy.h"
 3 
 4 enum eMessageDelegate
 5 {
 6     BuddyClientID = (int)ReservedDelegateID::UserCustom + 1,
 7 };
 8 
 9 class ConnectManage : public ClientDelegate
10 {
11 public:
12     virtual void OnFoundHost(const Address &address, int number)
13     {
14         std::cout << "FoundHost: " << address.ToString() << std::endl;
15         std::cout << "在线人数: " << number << std::endl;
16         client_->Connect(address);
17     }
18 
19     virtual void OnNotFoundHost()
20     {
21         std::cout << "OnNotFoundHost" << std::endl;
22     }
23 
24     virtual void OnConnectSuccess()
25     {
26         std::cout << "OnConnectSuccess" << std::endl;
27     }
28 
29     virtual void OnConnectFail()
30     {
31         std::cout << "OnConnectFail" << std::endl;
32     }
33 
34     virtual void OnDisconnect()
35     {
36         std::cout << "OnDisconnect" << std::endl;
37     }
38 
39     virtual void OnMessageReceive(void *data, size_t size)
40     {
41         std::cout << "OnMessageReceive" << std::endl;
42         std::cout << (char *)data << std::endl;
43     }
44 
45 public:
46     void SetBuddyClient(BuddyClient *client)
47     {
48         client_ = client;
49     }
50 
51 public:
52     BuddyClient* client_ = { nullptr };
53 };
54 
55 int main(int argc, char **argv)
56 {
57     init_sockets();
58     MessageManager::GetInstance();
59 
60     ConnectManage manage;
61     BuddyClient client(BuddyClientID, &manage);
62     manage.SetBuddyClient(&client);
63     client.SearchHost(60);
64 
65     while (true)
66     {
67         std::this_thread::sleep_for(std::chrono::milliseconds(16));
68         MessageManager::GetInstance()->Update(0);
69     }
70 
71     return 0;
72 }

  你会发现跟服务端的用法很相似对吧?下面来说说不同的地方。

  在第9行实现一个类ConnectManage,这是一个连接管理类(永远只有一个连接)。

  第61行跟服务端的用法类似,创建了一个客户端,构造函数传入消息代理的ID和管理类的指针。

  第63行调用SearchHost函数将会在局域网内搜索有无游戏服务端,参数是超时时间,单位为秒。这个操作不会阻塞主线程。

  第65行也是在模拟游戏主线程,在主线程中更新消息队列。

 

  值得注意的是,游戏的逻辑通常在会话管理类或者连接管理类里面实现。游戏主线程中每帧只需更新一次消息队列。消息的代理ID必须每个对象都是唯一的,意思就是说两个不同的对象不能拥有同一个消息代理ID。

源码下载

https://github.com/zhangpanyi/Buddy

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。