トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

TagJ

鬼ごっこ

本当のマルチプレイヤーゲームへの第一歩。

3人でやる疑似鬼ごっこのサンプルを示す。サーバはデータを集中管理し、クライアントはユーザに対応する。スケルトンのみ示すから、諸君は、鬼役をきちんと導入したり、衝突などの処理をきちんとできるよう改善してみてはどうか。

サーバのサンプルコードは以下の通り。クライアントからの接続要求を受け付けるServerSocketと実際のセッションの通信に使うSocketの区別をしっかりつける必要がある。このサンプルではSocketを生に扱うことはせず、BufferedReaderやPrintWriterにラップして受け渡すようにしている。Reader, Writerでやるということは、あまり速度性能を重視していないということだが、性能評価は上級の課題としてとっておく。デバッグ用のコードがそのまま残っているが、動作の確認のため、利用してほしい。クライアントのサンプルも同様。

package tag3;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

public class Tag3Server {
    final int PORT = 11000;
    // Model Part
    Player[] players;
    int num; // Number of players

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        (new Tag3Server(3)).start();
    }

    Tag3Server(int num) {
        this.num = num;
        players = new Player[num];
    }

    void start() {
        registerPlayers(); // players 登録
        kickOff(); // 開始
    }

    // players 登録
    void registerPlayers() {
        ServerSocket soc0=null;
        try {
            soc0 = new ServerSocket(PORT);
            for (int i = 0; i < num; i++) {
                Socket soc = soc0.accept();
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(soc.getInputStream()));
                PrintWriter writer = new PrintWriter(new OutputStreamWriter(
                        soc.getOutputStream()));

                String name = reader.readLine();
                players[i] = new Player(this, i, name, reader, writer, i, i);
                writer.println("Hello, " + name + "!");
                writer.println("Your ID is " + i + ". Please wait a second.");
                writer.flush();
            }
            soc0.close(); // ServerSocket は用済み
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            try{if(soc0!=null) soc0.close();}catch(IOException ie){/* do nothing */}
            System.exit(-1);
        }
    }

    // 開始の合図
    void kickOff() {
        System.out.println("kickOff is called.");
        for (int i = 0; i < num; i++) {
            System.out.printf("kickOff: %d/%d\n", i,num);
            players[i].writer.println("Go ahead!");
            players[i].writer.flush();
        }
        for(int i=0; i<num; i++){
            (new Thread(players[i])).start(); // client 通信の監視スレッド
            System.out.printf("Player thread %d/%d\n", i, num);
        }
    }

    // client への更新通知
    void update(int id){
        System.out.println("server.update is called");
        for(int i=0; i<num; i++){
            System.out.printf("message: id = %d\n", i);
            players[i].writer.printf("%d %d %d\n", id, players[id].x, players[id].y);
            players[i].writer.flush();
        }
    }
}

class Player implements Runnable {
    Tag3Server server;
    int id;
    String name;
    BufferedReader reader; // client からの reader
    PrintWriter writer; // client への writer
    int x;
    int y;

    Player(Tag3Server server, int id, String name, BufferedReader reader, PrintWriter writer, int x, int y) {
        this.server = server;
        this.id = id;
        this.name = name;
        this.reader = reader;
        this.writer = writer;
        this.x = x;
        this.y = y;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

        while (true) {
            try {
                String line = reader.readLine();
                Scanner scan = new Scanner(line);
                x = scan.nextInt();
                y = scan.nextInt();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            server.update(id);
            System.out.printf("id = %d, x = %d, y = %d\n",id, x, y);
        }
    }
}

以下はクライアントのサンプルである。

package tag3;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

import javax.swing.JButton;
import javax.swing.JFrame;

public class TagClient {
    final int PORT = 11000;
    String name;
    int id;
    String hostname = "localhost";
    Socket soc;
    BufferedReader reader;
    PrintWriter writer;
    TagPanel panel;

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        // args[0] should be player's nickname
        // The second argument to the constructor is
        // the number of clients, which must be more flexible
        // in a final version.
        (new TagClient(args[0], 3)).start();
    }

    TagClient(String name, int num) {
        this.name = name;
        panel = new TagPanel(name, num);
    }

    void start() {
        register(); // players 登録
        kickOff(); // 開始
    }

    void register() {
        try {
            soc = new Socket(hostname, PORT);

            reader = new BufferedReader(new InputStreamReader(
                    soc.getInputStream()));
            writer = new PrintWriter(new OutputStreamWriter(
                    soc.getOutputStream()));

            writer.println(name);
            writer.flush();

            // expect "Hello, ....\n"
            String line = reader.readLine();
            System.out.println(line); // for debug
            // expect "Your ID is #. Please wait a second.\n"
            line = reader.readLine();
            System.out.println(line); // for debug
            id = Integer.parseInt(line.substring(11, 12));
            panel.id = id;
            panel.reader = this.reader;
            panel.writer = this.writer;
            // expect "Go ahead!\n"
            line = reader.readLine();
            System.out.println(line);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    void kickOff() {
        new Thread((new SocketListener(reader, panel))).start();
    }
}

class TagPanel {
    JFrame frame;
    JButton[][] buttons;
    int[] x; // i-th player's horizontal position -- left to right
    int[] y; // i-th player's vertical position -- up to bottom
    int id;
    BufferedReader reader;
    PrintWriter writer;

    TagPanel(String name, int num) {
        frame = new JFrame(name);
        frame.setSize(400, 400);
        buttons = new JButton[8][8];
        x = new int[num];
        y = new int[num];

        frame.getContentPane().setLayout(new GridLayout(8, 8));
        for (int j = 0; j < 8; j++) {
            for (int i = 0; i < 8; i++) {
                buttons[j][i] = new JButton();
                buttons[j][i].addActionListener(new PositionGetter(this, i, j));
                frame.getContentPane().add(buttons[j][i]);
            }
        }

        frame.setVisible(true);
    }

    // myUpdate is called by the action listener of a button.
    void myUpdate(int newX, int newY) {
        /*
        buttons[y[this.id]][x[this.id]].setText(" ");
        x[this.id] = newX;
        y[this.id] = newY;
        buttons[newY][newX].setText(String.valueOf(this.id));
        */
        writer.printf("%d %d\n", newX, newY);
        writer.flush();
        System.out.printf("myUpdate is called. %d %d\n", newX, newY);
    }

    // update is called by the socket listener.
    void update(int id, int newX, int newY) {
        buttons[y[id]][x[id]].setText(" ");
        x[id] = newX;
        y[id] = newY;
        buttons[newY][newX].setText(String.valueOf(id));
    }
}

class SocketListener implements Runnable {
    BufferedReader reader;
    TagPanel panel;

    SocketListener(BufferedReader reader, TagPanel panel) {
        this.reader = reader;
        this.panel = panel;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            try {
                String line = reader.readLine();
                Scanner scan = new Scanner(line);
                int id = scan.nextInt();
                int x = scan.nextInt();
                int y = scan.nextInt();
                panel.update(id, x, y);		
            } catch (IOException e) {
                System.out.println("read error");
                System.exit(-1);
            }
        }
    }
}

class PositionGetter implements ActionListener {
    TagPanel panel;
    int x;
    int y;

    PositionGetter(TagPanel panel, int x, int y) {
        this.panel = panel;
        this.x = x;
        this.y = y;
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub
        panel.myUpdate(x, y);
    }
}

実行例

4つのコマンドプロンプト(シェルのウィンドウ)を立ち上げておく。いずれも

...\workspace\prog3\bin

にカレントディレクトリを移動しておく。

java tag3/Tag3Server
java tag3/TagClient betty
java tag3/TagClient jack
java tag3/TagCleint tom

のように実行すると以下の図のようにゲームが始まる。