Java编写网络打字游戏

《Java语言程序设计》–郭克华

网络打字游戏功能简介

  这里将介绍一个网络对战的打字游戏。首先运行服务器,界面如图1所示。
        这里写图片描述
                图1

  运行客户端,首先显示如图2所示的对话框。
         这里写图片描述
                  图2

  用户能够输入昵称,单击“确定”按钮,则连接到服务器。这里为了简单起见,服务器运行在本机,端口为9999。
  连接成功,显示如图3.
          这里写图片描述
                  图3

  单击“确定”按钮,即可出现打字游戏界面。
  实际上,多人可以加入打字对战。界面如图所示。
  这里写图片描述  这里写图片描述

  规则如下:
  1.初始生命值为10分,字母随机落下。
  2.用户按下键盘,如果输入的字符正确,则加1分,错误,减一分,然后重新落下新字母。
  3.如果用户加1分,将其他所有用户分分数减一分。
  4.字母掉到用户界面底部,用户减1分,重新出现新字母。
  5.如果把生命值变为0分,则游戏退出,如图所示。
        这里写图片描述

  ps:此案例是很多网络对战游戏的基础,如网络打牌、网络赛车、网络五子棋等。

关键技术

  1.如何组织界面

  在这个项目中,服务器端界面比较简单。客户端也只有一个界面,但是最好将游戏的工作写在一个面板内,然后将面板加到一个JFrame中。
  设计出来的类如下:
  1.GamePanel:客户端游戏所在的面板。
  2.GameFrame:客户端游戏面板所在的界面。
  3.Server:服务器界面。

  2.客户端如何掉下字母

  可以通过画图技术在界面上画出字母。不过,在Java GUI中,还有一种更加简单的方法,那就是将面板设置为空布局之后,将字母放在一个JLabel中。
  字母的掉下,实际上相当于调整JLabel的位置,代码如下:

 .
 .
 .
public class GamePanel extends JPanel {
     .
     .
     .
    //掉下的字母Label
    private JLabel lbMoveChar=new JLabel();
    public GamePanel() {  
        this.setLayout(null);
         .
         .
         .
        this.add(lbMoveChar);
        lbMoveChar.setFont(new Font("黑体",Font.BOLD,20));
        lbMoveChar.setForeground(Color.yellow);
        this.init();
         .
         .
         .        
    }

    public void init() { //字母的属性设置
         .
         .
         .
        //出现随机字母
        String str=String.valueOf((char)('A'+rnd.nextInt(26)));
        lbMoveChar.setText(str);
        lbMoveChar.setBounds(rnd.nextInt(this.getWidth()),0,20,20);
    }
     .
     .
     .
    //Timer事件对应的行为:实现掉下一个字母
    @Override
    public void actionPerformed(ActionEvent e) {
        .
        .
        .
       lbMoveChar.setLocation(lbMoveChar.getX(),lbMoveChar.getY()+10);
    }
}
  3.客户端如何实现加减分数

  由于本项目输入网络通信应用,因此,分数的加减可以通过服务器转发。方法如下:
  1.客户端输入正确,将自己加2分,然后讲一个字符串-1发给服务器。
  2.服务器将-1发给所有在线客户端。
  3.所有客户端(包括自己),获取-1之后,将相应的生命值减去1分。
  代码如下:

package game;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import java.util.Random;
import javax.swing.*;

public class GamePanel extends JPanel implements ActionListener,KeyListener,Runnable {
    //生命值
    private int life=10;
    //按键按下的字母
    private char keyChar;
    //掉下的字母Label
    private JLabel lbMoveChar=new JLabel();
    //当前生命值状态显示JLabel
    private JLabel lbLife=new JLabel();

    private Socket s=null;
    private Timer timer=new Timer(100,this);
    private Random rnd=new Random();
    private BufferedReader br=null;
    private PrintStream ps=null;
     .
     .
     .
    ///线程读取网络信息
    @Override
    public void run() {
        try {
            while(canRun) {
                String str=br.readLine(); //读
                int score=Integer.parseInt(str);
                life+=score;
                checkFail();
            }
        }
        catch (Exception ex) {
            canRun=false;
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出");
            System.exit(0);
        }
    }
     .
     .
     .
    //键盘操作事件对应的行为
    @Override
    public void keyPressed(KeyEvent e) {
        keyChar=e.getKeyChar();
        String strChar=String.valueOf(keyChar).toUpperCase();
        try {
            if(strChar.equals(lbMoveChar.getText())) {
                //注意,这里加2分,然后发送-1给所有客户端
                //本客户端又会收到,结果为加1分
                life+=2;
                ps.println("-1");
            }
            else life--;
            checkFail();
        }
        catch (Exception ex) {
            ex.printStackTrace();
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出");
            System.exit(0);
        }
    }
 .
 .
 .
}
  4.客户端如何判断输了

  判断输了很简单,只需判断生命值是否小于等于0即可:

public void checkFail() {
        init();
        if(life<=0) {
            timer.stop();
            javax.swing.JOptionPane.showMessageDialog(this,"生命值耗尽,游戏失败");
            System.exit(0);
        }
    }

代码编写

  1.服务器端

  首先是服务器类,代码如下:

package game;
import java.awt.Color;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import javax.swing.*;

public class Server extends JFrame implements Runnable {
    private Socket s=null;
    private ServerSocket ss=null;
    //保存客户端的线程
    private ArrayList<ChatThread> clients=new ArrayList<ChatThread>();
    public Server() throws Exception {
        this.setTitle("服务器端");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setBackground(Color.yellow);
        this.setSize(200,100);
        this.setVisible(true);
        ss=new ServerSocket(9999);
        new Thread(this).start();
    }

    @Override
    public void run() {
        try {
            while(true) {
                s=ss.accept();
                ChatThread ct=new ChatThread(s);
                clients.add(ct);
                ct.start();
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出!");
            System.exit(0);
        }
    }

    class ChatThread extends Thread { //为某个Socket负责接收信息
        private Socket s=null;
        private BufferedReader br=null;
        private PrintStream ps=null;
        private boolean canRun=true;
        public ChatThread(Socket s) throws Exception {
            this.s=s;
            br=new BufferedReader(new InputStreamReader(s.getInputStream()));
            ps=new PrintStream(s.getOutputStream());
        }

        @Override
        public void run() {
            try {
                while(canRun) {
                    String str=br.readLine(); //读取该Socket传来的信息
                    sendMessage(str);         //将str转发给所有客户端
                }
            }
            catch (Exception ex) {
                //此处可以解决客户异常下线问题
                canRun=false;
                clients.remove(this);
            }
        }
    }

    //将信息发给其他客户端
    public void sendMessage(String msg) {
        for(ChatThread ct: clients) ct.ps.println(msg);
    }

    public static void main(String[] args) throws Exception {
        new Server();
    }
}
  2.客户端

  首先是游戏面板类,代码如下:

package game;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import java.util.Random;
import javax.swing.*;

public class GamePanel extends JPanel implements ActionListener,KeyListener,Runnable {
    //生命值
    private int life=10;
    //按键按下的字母
    private char keyChar;
    //掉下的字母Label
    private JLabel lbMoveChar=new JLabel();
    //当前生命值状态显示JLabel
    private JLabel lbLife=new JLabel();
    private Socket s=null;
    private Timer timer=new Timer(100,this);
    private Random rnd=new Random();
    private BufferedReader br=null;
    private PrintStream ps=null;
    private boolean canRun=true;
    public GamePanel() {  //构造器
        this.setLayout(null);
        this.setBackground(Color.DARK_GRAY);
        this.setSize(240,320);

        this.add(lbLife);
        lbLife.setFont(new Font("黑体",Font.BOLD,20));
        lbLife.setBackground(Color.yellow);
        lbLife.setForeground(Color.PINK);
        lbLife.setBounds(0,0,this.getWidth(),20);

        this.add(lbMoveChar);
        lbMoveChar.setFont(new Font("黑体",Font.BOLD,20));
        lbMoveChar.setForeground(Color.yellow);
        this.init();
        this.addKeyListener(this);
        try {
            s=new Socket("127.0.0.1",9999);
            JOptionPane.showMessageDialog(this,"连接成功");
            InputStream is=s.getInputStream();
            br=new BufferedReader(new InputStreamReader(is));
            OutputStream os=s.getOutputStream();
            ps=new PrintStream(os);
            new Thread(this).start();
        }
        catch (Exception ex) {
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出");
            System.exit(0);
        }
        timer.start();
    }

    public void init() { //字母的属性设置
        lbLife.setText("当前生命值:"+life);
        //出现随机字母
        String str=String.valueOf((char)('A'+rnd.nextInt(26)));
        lbMoveChar.setText(str);
        lbMoveChar.setBounds(rnd.nextInt(this.getWidth()),0,20,20);
    }

    @Override
    public void run() {
        try {
            while(canRun) {
                String str=br.readLine(); //读
                int score=Integer.parseInt(str);
                life+=score;
                checkFail();
            }
        }
        catch (Exception ex) {
            canRun=false;
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出");
            System.exit(0);
        }
    }

    //Timer事件对应的行为:实现掉下一个字母
    @Override
    public void actionPerformed(ActionEvent e) {
        if(lbMoveChar.getY()>=this.getHeight()) {
            life--;
            checkFail();
        }
        lbMoveChar.setLocation(lbMoveChar.getX(),lbMoveChar.getY()+10);
    }

    public void checkFail() {
        init();
        if(life<=0) {
            timer.stop();
            javax.swing.JOptionPane.showMessageDialog(this,"生命值耗尽,游戏失败");
            System.exit(0);
        }
    }

    //键盘操作事件对应的行为
    @Override
    public void keyPressed(KeyEvent e) {
        keyChar=e.getKeyChar();
        String strChar=String.valueOf(keyChar).toUpperCase();
        try {
            if(strChar.equals(lbMoveChar.getText())) {
                //注意,这里加2分,然后发送-1给所有客户端
                //本客户端又会收到,结果为加1分
                life+=2;
                ps.println("-1");
            }
            else life--;
            checkFail();
        }
        catch (Exception ex) {
            ex.printStackTrace();
            javax.swing.JOptionPane.showMessageDialog(this,"游戏异常退出");
            System.exit(0);
        }
    }

    public void keyTyped(KeyEvent e) {}
    public void keyReleased(KeyEvent e) {}
}

  接下来是面板所在的界面类,代码如下:

package game;
import javax.swing.JOptionPane;
import javax.swing.JFrame;

public class GameFrame extends JFrame {
    private GamePanel gp;
    public GameFrame() {//构造器
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        String nickName=JOptionPane.showInputDialog("输入昵称");
        this.setTitle(nickName);
        gp=new GamePanel();
        this.add(gp);
        //获取焦点
        gp.setFocusable(true);
        this.setSize(gp.getWidth(),gp.getHeight());
        this.setResizable(false);
        this.setVisible(true);
    }

    //主函数入口
    public static void main(String[] args) {
        new GameFrame();
    }
}

  运行该服务器,在运行客户端类,则可以进行网络对战游戏。

  • 5
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值