俄罗斯方块源码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
操作其中一个另一个会有反馈

不知道为什么延迟有点高??

看来还有待优化

Last modification:July 17th, 2020 at 10:32 pm