俄罗斯方块源码https://github.com/Legroft/sfml-tetris
大致思路
观察俄罗斯方块的源码,假设以下
a玩家A客户端,b玩家B客户端 C服务器
每个客户端player1是本地玩家 player2是网络玩家
我觉得首先要传两个个地方的信息
一个是直接把每个玩家按下键盘或鼠标的的event事件传输到服务器
把这个event直接传输到各自的客户端进行操作
因为本来是本地同客户端双人游戏,两个玩家对游戏的操作也仅仅是通过键盘的按键,所以我觉得只需要穿这个event就行了
还有一个问题是如果a b正在游戏,然后b方块到底了,这时候A B都要进行生成下一个方块的逻辑,但是它有个生成随机方块的函数,可能会导致两个客户端生成的方块不一样,所以我感觉要在生成方块时把生成方块的信息传过去
比如说B执行了这个函数,生成了绿色L方块,然后A接受信息,A客户端可不可以不执行这个函数,直接让nextShape=xxx
然后加个if判断玩家 player1是本地玩家执行newShapeFunc函数 player2是网络玩家 不执行这个函数
简易的框架
客户端
头文件及变量
#include "SFML/Network.hpp"
#include <iostream>
#include <thread>
using namespace sf;
using namespace std;
int gamePort; //连接端口
bool sendMsg = false; //判断是否发送消息
struct data { //数据结构
bool useable = false; //数据是否可用
int player = -1; //玩家id
int eventType = -1; //事件类型
int eventButton = -1; //按钮类型
int keyCode = -1; //按键类型
}mydata, anotherData; //玩家1,2的数据
float player1_x = 100, player1_y = 100; //玩家一位置
float player2_x = 200, player2_y = 200; //玩家二位置
初次连接服务器
void connect(int port) { //连接服务器并获取玩家的id
TcpSocket socket;
socket.connect("127.0.0.1", port);
Packet packet;
packet << mydata.player;
socket.send(packet);
Packet packet1;
socket.receive(packet1);
packet1 >> mydata.player;
cout << "The server said: " << mydata.player << endl;
}
向服务器发送数据
void sendToService(struct data mydata, int port) { //给服务器传送消息
TcpSocket socket;
socket.connect("127.0.0.1", port);
Packet packet;
packet << mydata.player << mydata.eventType << mydata.eventButton << mydata.keyCode;
socket.send(packet);
}
接受服务器的数据
int reciveService() { //接受服务器的消息
while (true) {
TcpSocket socket;
socket.connect("127.0.0.1", gamePort + 10);
Packet p;
socket.receive(p);
int player, type, button, keycode;
bool useable;
p >> useable >> player >> type >> button >> keycode;
anotherData.useable = useable;
anotherData.player = player;
anotherData.eventType = type;
anotherData.eventButton = button;
anotherData.keyCode = keycode;
if (useable) {
inputJudge();
}
}
return 0;
}
连接服务器及发送消息的线程
int connectService() { //连接服务器及发送消息
cout << "请输入端口" << endl;
cin >> gamePort;
connect(gamePort);
while (true) {
if (sendMsg) {
sendToService(mydata, gamePort);
sendMsg = false;
}
}
return 0;
}
游戏主程序线程
int gameRun() {
RenderWindow window(VideoMode(400, 400), "client");
CircleShape shape1(50.f);
shape1.setFillColor(sf::Color::Green);
CircleShape shape2(50.f);
shape2.setFillColor(sf::Color::Blue);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed)
window.close();
mydata.eventType = event.type;
mydata.eventButton = event.mouseButton.button;
mydata.keyCode = event.key.code;
if (mydata.eventType == Event::KeyReleased) {
if (mydata.keyCode == Keyboard::W) {
player_y[mydata.player] -= 10;
}
if (mydata.keyCode == Keyboard::A) {
player_x[mydata.player] -= 10;
}
if (mydata.keyCode == Keyboard::D) {
player_x[mydata.player] += 10;
}
if (mydata.keyCode == Keyboard::S) {
player_y[mydata.player] += 10;
}
}
sendMsg = true; //窗口发生事件时才传送数据
}
window.clear();
shape2.setPosition(player2_x, player2_y);
window.draw(shape1);
window.draw(shape2);
window.display();
}
return 0;
}
判断接收的信息,让另一个玩家响应操作
void inputJudge() {
if (anotherData.eventType == Event::MouseButtonReleased && anotherData.eventButton == Mouse::Left) {
cout << "另一个玩家单击了左键" << endl;
}
if (anotherData.eventType == Event::KeyReleased) {
if (anotherData.keyCode == Keyboard::W) {
cout << "另一个玩家松开w" << endl;
player_y[anotherData.player] -= 10;
}
if (anotherData.keyCode == Keyboard::A) {
cout << "另一个玩家松开a" << endl;
player_x[anotherData.player] -= 10;
}
if (anotherData.keyCode == Keyboard::D) {
cout << "另一个玩家松开d" << endl;
player_x[anotherData.player] += 10;
}
if (anotherData.keyCode == Keyboard::S) {
cout << "另一个玩家松开s" << endl;
player_y[anotherData.player] += 10;
}
}
}
让发送消息、接受消息、游戏程序并行进行
int main() {
thread t1(connectService);
thread t2(gameRun);
thread t3(reciveService);
t1.join();
t2.join();
t3.join();
return 0;
}
服务端
基本的数据结构同上
#include<iostream>
#include <SFML/Graphics.hpp>
#include "SFML/Network.hpp"
#include <string>
#include<thread>
using namespace std;
using namespace sf;
enum e_player {
player1, player2
};
struct data {
bool useable = false;
int player = -1;
int eventType = -1;
int eventButton = -1;
int keyCode = -1;
}player1Data, player2Data;
首次连接客户端,给客户端分配id
void connect(int id, int port) {
TcpListener listener;
TcpSocket socket;
listener.listen(port);
listener.accept(socket);
cout << "New client connected: " << socket.getRemoteAddress() << endl;
Packet packet1;
socket.receive(packet1);
int x;
packet1 >> x;
if (x == -1) {
Packet packet;
packet << id;
socket.send(packet);
cout << "The client id is: " << id << endl;
}
}
接收消息及发送消息
int reciveClient(int port) {
while (true) {
TcpListener listener;
TcpSocket socket;
Packet packet;
listener.listen(port);
listener.accept(socket);
socket.receive(packet);
int player, eventType, eventButton, keyCode;
packet >> player >> eventType >> eventButton >> keyCode;
if (player == 0) {
player1Data.player = player;
player1Data.eventType = eventType;
player1Data.eventButton = eventButton;
player1Data.keyCode = keyCode;
player1Data.useable = true;
inputJudge(player1Data);
} else if (player == 1) {
player2Data.player = player;
player2Data.eventType = eventType;
player2Data.eventButton = eventButton;
player2Data.keyCode = keyCode;
player2Data.useable = true;
inputJudge(player2Data);
}
}
return 0;
}
int sendToClient(int port) {
while (true) {
TcpListener listener;
TcpSocket socket;
Packet p;
listener.listen(port);
listener.accept(socket);
if (port == 1010) {
p << player2Data.useable << player2Data.player << player2Data.eventType << player2Data.eventButton << player2Data.keyCode;
socket.send(p);
player2Data.useable = false;
} else if (port == 1011) {
p << player1Data.useable << player1Data.player << player1Data.eventType << player1Data.eventButton << player1Data.keyCode;
socket.send(p);
player1Data.useable = false;
}
}
}
对客户端传来的消息进行判断(方便测试)
void inputJudge(struct data inputData) {
int role = inputData.player;
if (inputData.eventType == Event::MouseButtonReleased && inputData.eventButton == Mouse::Left) {
cout << "玩家" << role << "单击了左键" << endl;
}
if (inputData.eventType == Event::KeyPressed) {
if (inputData.keyCode == Keyboard::W)
cout << "玩家" << role << "按下w" << endl;
if (inputData.keyCode == Keyboard::A)
cout << "玩家" << role << "按下a" << endl;
if (inputData.keyCode == Keyboard::D)
cout << "玩家" << role << "按下d" << endl;
if (inputData.keyCode == Keyboard::S)
cout << "玩家" << role << "按下s" << endl;
}
if (inputData.eventType == Event::KeyReleased) {
if (inputData.keyCode == Keyboard::LControl)
cout << "玩家" << role << "松开l ctrl" << endl;
if (inputData.keyCode == Keyboard::Space)
cout << "玩家" << role << "松开space" << endl;
if (inputData.keyCode == Keyboard::A || inputData.keyCode == Keyboard::D)
cout << "玩家" << role << "松开 a或d" << endl;
if (inputData.keyCode == Keyboard::S)
cout << "玩家" << role << "松开s" << endl;
}
}
主函数
int main() {
connect(player1, 1000);
connect(player2, 1001);
thread t1(reciveClient, 1000);
thread t2(reciveClient, 1001);
thread t3(sendToClient, 1010);
thread t4(sendToClient, 1011);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
demo测试
先打开服务器 再打开两个客户端
其中一个客户端输入端口1000
另一个客户端输入端口1001
操作其中一个另一个会有反馈
不知道为什么延迟有点高??
看来还有待优化