1

ソケットを使用して Java でマルチプレイヤー スネーク ゲームを作成しています。すべての送信は、接続されているすべてのクライアントに対してサーバーを介して行われます。同じコードはまだ完全には完成していませんが、特定のクライアントが食べ物を食べた場合にヘビを動かし、スコアを上げるという基本的な仕事をしています。

サーバー側からフード座標の乱数を生成し、すべてのクライアントに中継します。クライアントがキーを押すと、要求された移動が計算され、移動の方向がサーバーに送信され、サーバーは移動をすべてのクライアント (送信したクライアントを含む) に中継し、移動情報を受信した場合にのみ、クライアントは変更を行います。動いたヘビに。したがって、すべての動きはネットワーク上で追跡され、クライアント 'player1' が移動を要求したとします。

私が直面している問題は、2 人のプレイヤーでも、ヘビを少し動かした後の座標に違いがあるように見えることです。

ヘビの位置間のこの明らかな遅れを取り除くために、コードにどのような救済策を適用できますか?

これはクライアント コードです。

package mycode;

import java.awt.Point;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.Map;

import javax.swing.JOptionPane;

public class ConnectionManager implements Runnable {
    Socket socket;
    boolean start = false;
    DataInputStream in;
    DataOutputStream out;
    Map<String, Snake> map;

    ConnectionManager(String name, String IP, Map<String, Snake> m) {
        this.map = m;
        try {
            socket = new Socket(IP, 9977);
            in = new DataInputStream(new BufferedInputStream(
                    socket.getInputStream()));
            out = new DataOutputStream(new BufferedOutputStream(
                    socket.getOutputStream()));
            out.writeUTF(name);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "Could Not Find Server",
                    "ERROR", JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
    }

    void populateMap() {
        try {
            String name = in.readUTF();
            System.out.println("Name received: " + name);
            if (name.equals("start_game_9977")) {
                start = true;
                System.out.println("Game Started");
                return;
            } else if (name.equals("food_coord")) {
                Game.foodx = in.readInt();
                Game.foody = in.readInt();
                return;
            }
            map.put(name, new Snake(5));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    boolean start() {
        return start;
    }

    void increaseSnakeLength(String thisname){
        Snake temp = map.get(thisname);
        Point temp1=new Point(0,0);
        temp.length++;
        switch (temp.move) {
        case DOWN:
             temp1= new Point(temp.p[temp.length - 2].x,
                    temp.p[temp.length - 2].y+6);
             break;
        case LEFT:
            temp1= new Point(temp.p[temp.length - 2].x-6,
                    temp.p[temp.length - 2].y);
            break;
        case RIGHT:
            temp1= new Point(temp.p[temp.length - 2].x+6,
                    temp.p[temp.length - 2].y);
            break;
        case UP:
            temp1= new Point(temp.p[temp.length - 2].x,
                    temp.p[temp.length - 2].y-6);
            break;
        default:
            break;
        }
        if(temp1.y>Game.max)
            temp1.y=Game.min;
        if(temp1.x>Game.max)
            temp1.x=Game.min;
        if(temp1.y<Game.min)
            temp1.y=Game.max;
        if(temp1.x<Game.min)
            temp1.x=Game.max;
        temp.p[temp.length-1]=temp1;
    }

    void readMotion() {
        try {
            while (true) {
                if (Game.changedirection) {
                    String mov = "";
                    mov = Game.move.name();
                    // System.out.println(Game.move);
                    out.writeUTF(mov);
                    out.flush();
                    Game.changedirection = false;
                }
                if (Game.foodeaten) {
                    out.writeUTF("food_eaten");
                    out.flush();
                    Game.foodeaten = false;
                }
                Thread.sleep(50);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void otherRunMethod() {
        try {
            while (true) {
                String mname = in.readUTF();
                String mov = in.readUTF();
                if (mov.equals("Resigned")) {
                    map.remove(mname);
                } else if (mov.length() >= 10) {
                    if (mov.substring(0, 10).equals("food_eaten")) {
                        String[] s = mov.split(",");
                        Game.foodx = Integer.parseInt(s[1]);
                        Game.foody = Integer.parseInt(s[2]);
                        int score = ++map.get(mname).score;
                        increaseSnakeLength(mname);
                        System.out.println(mname + ":" + score+" Length:"+map.get(mname).length);
                    }
                } else {
                    Game.move = Direction.valueOf(mov);
                    map.get(mname).move = Game.move;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (true) {
            if (!start) {
                populateMap();
            } else if (start) {
                new Thread(new Runnable() {
                    public void run() {
                        otherRunMethod();
                    }
                }).start();
                readMotion();
                break;
            }
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

コードはかなり長いので、接続を管理するサーバー側のコードを載せるだけです。

package mycode;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.Map;

public class Playerhandler implements Runnable {
    Socket player;
    String thisname;
    Map<String, Socket> map;
    DataInputStream in = null;
    DataOutputStream out = null;
    ObjectInputStream ob;
    Snake snake;

    Playerhandler(Socket player, Map<String, Socket> m) {
        this.player = player;
        this.map = m;
        try {
            in = new DataInputStream(new BufferedInputStream(
                    player.getInputStream()));
            thisname = in.readUTF();
            map.put(thisname, this.player);
            populatePlayers();
            System.out.println("Connected Client " + thisname);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    void populatePlayers() {
        try {
            out = new DataOutputStream(new BufferedOutputStream(
                    player.getOutputStream()));
            for (String name : map.keySet()) {
                out.writeUTF(name);
                out.flush();
            }

            for (String name : map.keySet()) {
                out = new DataOutputStream(new BufferedOutputStream(map.get(
                        name).getOutputStream()));
                out.writeUTF(thisname);
                out.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void relay(String move) {
        try {
            if (move.equals("food_eaten")) {
                move = move + ","
                        + (Snakeserver.randomGenerator.nextInt(100) * 6) + ","
                        + (Snakeserver.randomGenerator.nextInt(100) * 6);

            }
            for (String name : map.keySet()) {
                out = new DataOutputStream(new BufferedOutputStream(map.get(
                        name).getOutputStream()));
                out.writeUTF(thisname);
                out.flush();
                out.writeUTF(move);
                // System.out.println(Direction.valueOf(move));
                out.flush();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void run() {
        while (true) {
            try {
                relay(in.readUTF());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                System.out.println("Player " + thisname + " Resigned");
                map.remove(thisname);
                relay("Resigned");
                return;
            }
        }
    }

}
4

3 に答える 3

0

setTcpNoDelay(true) の明示的な呼び出しを追加する傾向があります。これにより、 http://en.wikipedia.org/wiki/Nagle%27s_algorithmが確実にオフになり、通常はわずかな遅延の増加で効率を上げる最適化が無効になります。

于 2013-03-31T12:52:58.930 に答える
0

これまでネットワーク マルチプレイヤー ゲームを実装したことはありませんが、ここで最も広く使用されている「解決策」はチートだと思います。

とにかくヘビはこれとまったく同じように機能しますが、それは「推測航法」と呼ばれると思います。

http://www.gamasutra.com/view/feature/3230/dead_reckoning_latency_hiding_for_.php

基本的に、ゲーム ループをネットワーク アップデートから分離します。各クライアントに独自の状態を保持させ、各フレームで対戦相手がどこにいるのかを単純に予測します。その後、サーバーからの更新が到着したら、対戦相手を実際の位置に調整できます。この矛盾を隠すために、現在の状態ではなく、数ミリ秒前のゲームの状態をレンダリングするのが一般的だと思います。そうすれば、ネットワークの更新がゲーム ループに追いつく可能性がより現実的になるため、途切れが少なくなります。

私が言ったように、私は実際にこれを自分で実装したことがないので、YMMV. これは、ゲーム開発における難しい問題の 1 つです。

于 2013-03-31T12:33:56.747 に答える