1、任务描述:
用JavaSocket编程开发英语六级词汇学习游戏系统
(1) 在网上自行下载英语六级词汇表。存到文件中,格式自定。
(2)系统支持两人连到服务器,测试学生对英语六级词汇的熟悉程度。
(3)首先运行服务器。服务器运行之后,两个客户可以加入。
(4)运行客户端。用户能够输入昵称,确定,则连接到服务器。连接成功,即可出现游戏界面。
(5)客户端游戏界面如下:用户点击开始之后,系统随机选择一个六级英文词汇(5个字母以上),空缺2个字母,用下划线,从客户端顶部落下,底部有其中文释义。

规则如下:
(1)每人初始生命值为20分。
(2)英文词汇(黑色)从界面顶部落下时,用户按下键盘,输入字母。字母(红色)快速射向单词。如果字母正确,则字母填入单词,该空字母下划线消失;错误,则该单词考试失败,出现下一个单词。用户输入正确字母,自己加1分;如果错误,自己减1分,对方加1分。
(3)用户正确将单词填写完毕,或者输入错误字母导致填写错误,该单词下落过程结束。重新出现新的单词从顶部落下,中文释义更新。
(4)词汇掉到用户界面底部,如果用户还没将单词填完,用户减1分,重新出现新词汇掉下来,中文释义更新。
(5)某用户如果生命值率先变为0分,则判输,游戏退出。
(6)游戏退出后,每个用户保存3个文件,分别存储输入正确的单词表、输入错误的单词表、未输入完即掉到底部的单词表。便于用户复习。
2、所用知识
总的来说,这次需要编写三个大类
①Server 作为服务器处理数据
②GamePanel 游戏面板,主要的游戏逻辑由他实现
③GameFrame 游戏界面,主要负责把GamePanel加上去,设置界面不可调整等微小工作

所用知识我们可以对照任务描述分条来看
①下载六次词汇表并做一定的处理:java.io.,为了方便我们后续处理,必须对文件中单词和中文释义有一定的格式要求,我的预期格式是一对单词和汉语解释占单独一行,中间有空格分开,这样方便我们用BufferedReader类提供的readLine函数,一次读取一对,同时要提前把小于5个字母的单词删掉
②连接服务器需要用java.net.
③系统随机选择单词落下,空缺两个字母,从顶端落下:由于这个任务我们要运行多次,所以我选择写一个Init函数,用来完成这个任务和其他任务。
(1)随机选择单词:随机选单词如果在读的阶段就随机很麻烦,所以我选择读入后再打乱顺序,我把读取的一行数据存入一个vector(java.util.Vector)中(注意这里一定不能用java.util.
后面说为什么),然后用经典的洗牌算法:按顺序迭代,如果到了第i个,就产生一个从i—n-1的随机数(是n-1而不是n,因为这里是索引,java String里面也是从0开始计数),代码如下:

// 打乱
		for (int i = 0; i < words.size(); i++) {
			String tmp = words.get(i);
			int randIndex = (int) (Math.random() * (words.size() - i) + i);
			words.set(i, words.get(randIndex));
			words.set(randIndex, tmp);
		}

(2)任意选择位置空缺:直接使用随机数生成两个位置,把这两个位置用int变量 lackPos1, lackPos2保存下来(之后判断是否填入正确单词要用),要注意,二者一定不能相等,而且要保证lackPos1<lackPos2,有一个小于的关系是因为之后要判断用户是否输入了正确单词,填写顺序肯定是从前到后的,为了判断方便,我们之间在这里让lackPos1<lackPos2,至于空缺,可以直接把lackPos1、lackPos2对应位置的字母换成下划线,但是因为String类对于改变操作并不是很方便,所以新建一个临时变量StringBuffer类,这样方便我们修改对应位置的字母

dic = words.get(wordIndex++ % words.size()).split(" ");
		sb = new StringBuffer(dic[0]);

		// 保证二者不相等
		lackPos2 = lackPos1 = (int) (Math.random() * sb.length());
		while (lackPos2 == lackPos1) {
			lackPos2 = (int) (Math.random() * sb.length());
		}
		if (lackPos2 < lackPos1) {
			int tmp = lackPos2;
			lackPos2 = lackPos1;
			lackPos1 = tmp;
		}
		sb.setCharAt(lackPos1, '_');
		sb.setCharAt(lackPos2, '_');

(3)落下:首先设置GamePanel类的布局为空,单词我们把他加到一个JLabel上,落下这件事通过setLocation函数实现,由于落下要间隔一定的时间,但是程序并不能卡在这里,所以要用到javax.swing.Timer类,同时这个类要实现ActionListener接口,通过重写actionPerformed函数,告诉Timer间隔一定时间要做什么

//第一个参数是间隔时间,第二个参数是实现了ActionListener接口的对象
Timer timer = new Timer(250, this);

// 每隔100毫秒下落
	public void actionPerformed(ActionEvent e) {
		jlbChinese.setSize(dic[1].length() * 20, 20);
		jlbChinese.setLocation((this.getWidth() - jlbChinese.getWidth()) / 2,
				this.getHeight() - jlbChinese.getHeight());
		jlbWord.setLocation(jlbWord.getX(), jlbWord.getY() + 10);
		checkFail();
	}

④字母射向单词:因为字母射向单词的时候别的程序也要运行,单词也不可能呆滞在空中,所以这里还要用到多线程的知识,但是原先的GamePanel类已经实现了Runnable接口,处理的是接受服务器信息的任务,所以我们再新建一个类,jlbFollow,用它实现Runnable接口,需要做的就是判断字母有没有飞上去,如果飞上去就结束,如果是第二个字母飞上去了,就调用Init函数

private class jlbFollow extends JLabel implements Runnable {

		JLabel jlbWord;
		int pos;

		public jlbFollow(JLabel jlbWord, char ch, JPanel jpl, int pos) {
			this.setFont(new Font("黑体", Font.BOLD, 20));
			this.setForeground(Color.red);
			this.setText(String.valueOf(ch));
			this.setBounds((int) ((jpl.getWidth() - 20) * Math.random()), jpl.getHeight(), 20, 20);
			this.jlbWord = jlbWord;
			this.pos = pos;
		}

		public void run() {

			while (Math.pow(jlbWord.getX() - this.getX(), 2) + Math.pow(jlbWord.getY() - this.getY(), 2) > 100) {
				int x = (jlbWord.getX() - this.getX()) / 2, y = (jlbWord.getY() - this.getY()) / 2;
				this.setLocation(x + this.getX(), y + this.getY());
				try {
					Thread.sleep(50);
				} catch (Exception e) {

				}
			}
			this.setVisible(false);
			sb.setCharAt(pos, this.getText().charAt(0));
			jlbWord.setText(sb.toString());

			try {
				Thread.sleep(100);
			} catch (Exception e) {

			}

			if (lackPos1 == lackPos2)
				Init();
		}

	}

⑤如果错误,自己生命-1,对方生命+1:如果单词错误,那就给自己生命-2,然后给服务器发送+1.服务器再给加入的每一个玩家发送+1即可

**注意:**如果想要成功运行,必须要修改GamePanel类里面的一个String类的对象ip,还要保证路径下有图片和文本文档。修改是因为要匹配你所在的网络环境(想要查看首先打开CMD窗口,然后输入ipconfig 敲回车),在“ IPv4 地址”那一行查看(或者直接改成127.0.0.1 表示本机)。图片是我用来设置背景和图标的,文本文档是读取单词所用。虽然我只用了四张图,但是存了很多图片,这是因为我原本想实现一个每次登陆随机背景图的功能,但是现在处于答辩前夕,不想更改代码了。(●’◡’●)

运行情况:

作业管理系统javaee大作业 java期末大作业系统代码_服务器


作业管理系统javaee大作业 java期末大作业系统代码_作业管理系统javaee大作业_02


作业管理系统javaee大作业 java期末大作业系统代码_gui_03


作业管理系统javaee大作业 java期末大作业系统代码_服务器_04


作业管理系统javaee大作业 java期末大作业系统代码_socket_05

路径内容如下图:

3、代码
①Server类

package Game;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.*;
import javax.swing.*;
import java.util.*;

public class Server extends JFrame implements Runnable, ActionListener {

	private ServerSocket ss = null;
	private Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
	private Vector<ChatThread> client = new Vector<ChatThread>();
	private MyJPanel jpl = new MyJPanel();
	private JComboBox jcb = new JComboBox();
	private JLabel jlb = new JLabel();

	public Server() throws Exception {

		Image img = Toolkit.getDefaultToolkit().getImage("serverIcon.jpg");

		ss = new ServerSocket(9999);
		this.setSize(350, 400);
		jpl.setLayout(null);
		jpl.add(jcb);
		jpl.add(jlb);
		jcb.setLocation(130, 100);
		jcb.setSize(70, 20);

		jlb.setSize(200, 20);
		jlb.setFont(new Font("楷体", Font.BOLD, 20));
		jlb.setForeground(Color.red);
		jlb.setText("选择用户,请他离开");
		jlb.setLocation(80, 80);
		this.add(jpl);
		this.setIconImage(img);
		this.setLocation((int) ((screenSize.getWidth() - 350) / 2), (int) ((screenSize.getHeight() - 400) / 2));
		this.setTitle("服务器端启动,未见客户加入");
		this.setVisible(true);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setResizable(false);
		jcb.addItem(" ");
		jcb.setVisible(false);
		jlb.setVisible(false);
		jcb.addActionListener(this);

		new Thread(this).start();
	}

	public void run() {
		while (true) {
			try {
				Socket s = ss.accept();
				this.setTitle("客户加入");
				ChatThread ct = new ChatThread(s);
				client.add(ct);
				this.setTitle("用户" + ct.nickName + "连接成功");
				jcb.addItem(ct.nickName);
				jcb.setVisible(true);
				jlb.setVisible(true);
				ct.start();

			} catch (IOException e) {

				e.printStackTrace();
			}
		}
	}

	private class ChatThread extends Thread {

		BufferedReader br = null;
		PrintStream ps = null;
		boolean canRun = true;
		String nickName;

		public ChatThread(Socket s) throws IOException {
			br = new BufferedReader(new InputStreamReader(s.getInputStream()));
			ps = new PrintStream(s.getOutputStream());
			nickName = br.readLine();

		}

		public void run() {

			while (canRun) {
				try {
					String str = br.readLine();
					sentMessage(str);
				} catch (Exception e) {
					canRun = false;
					client.remove(this);
					jcb.removeItem(nickName);
				}

			}
		}

		private void sentMessage(String str) {
			for (ChatThread ct : client)
				ct.ps.println(str);
		}

	}

	class MyJPanel extends JPanel {
		public void paintComponent(Graphics g) {
			Image img = new ImageIcon("gra4.jpg").getImage();
			g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), jpl);
		}
	}

	public static void main(String[] args) throws Exception {
		new Server();
	}

	@Override
	public void actionPerformed(ActionEvent e) {

		Object name = jcb.getSelectedItem();
		if (null == name || name.equals(" "))
			return;
		int choice = JOptionPane.showConfirmDialog(this, "您确定要踢出" + name + "吗?");
		if (JOptionPane.YES_OPTION == choice) {

			for (ChatThread c : client) {
				if (name.equals(c.nickName)) {
					c.ps.println("-9999");
					this.setTitle("用户" + name + "踢出成功");
					jcb.removeItem(name);
					JOptionPane.showMessageDialog(null, "踢除成功!");
				}

			}
		}
	}
}

②GamePanel类

package Game;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import sun.security.util.math.ImmutableIntegerModuloP;

import java.io.*;
import java.net.*;
import java.util.Vector;

public class GamePanel extends JPanel implements ActionListener, KeyListener, Runnable {

	final private String ip = "127.0.0.1";
	String nickName = null;
	Socket s = null;
	JLabel jlbWord = new JLabel();
	JLabel jlbChinese = new JLabel();
	JLabel jlbLife = new JLabel();
	BufferedReader br = new BufferedReader(new FileReader("word.txt"));
	Vector<String> words = new Vector<String>();
	String[] dic; // 单词及其中文释义
	int wordIndex = 0; // 下一个该读取的单词的索引
	int lackPos1, lackPos2;// 空出字母在单词内的索引
	int life = 20;
	PrintStream ps = null;
	BufferedReader brServer = null;
	StringBuffer sb = null;
	StringBuffer filePath = new StringBuffer("C:\\Users\\86132\\Desktop\\java\\clientInfo\\");
	File file = null;
	PrintStream psCorrect = null;
	PrintStream psFalse = null;
	PrintStream psForget = null;

	Timer timer = new Timer(250, this);

	public GamePanel() throws Exception {

		String str = null;

		// 读取单词
		while (null != (str = br.readLine())) {
			words.add(str);
		}
		br.close();

		// 打乱
		for (int i = 0; i < words.size(); i++) {
			String tmp = words.get(i);
			int randIndex = (int) (Math.random() * (words.size() - i) + i);
			words.set(i, words.get(randIndex));
			words.set(randIndex, tmp);
		}

		nickName = JOptionPane.showInputDialog("请输入昵称");

		if (null == nickName)
			System.exit(0);

		this.setLayout(null);
		this.setSize(500, 550);
		s = new Socket(ip, 9999);
		this.add(jlbLife);
		this.add(jlbWord);
		this.add(jlbChinese);
		this.setFocusable(true);
		this.addKeyListener(this);

		jlbLife.setFont(new Font("楷体", Font.BOLD, 20));
		jlbWord.setFont(new Font("黑体", Font.BOLD, 20));
		jlbChinese.setFont(new Font("楷体", Font.BOLD, 20));

		ps = new PrintStream(s.getOutputStream());
		brServer = new BufferedReader(new InputStreamReader(s.getInputStream()));

		JOptionPane.showMessageDialog(null, "连接成功,游戏即将开始");
		ps.println(nickName);

		filePath.append(nickName);
		file = new File(filePath.toString());
		if (!file.exists())
			file.mkdir();

		filePath.append("\\");
		psCorrect = new PrintStream(new FileOutputStream(new File(filePath.toString() + "CorrectWord.txt"), true));
		psForget = new PrintStream(new FileOutputStream(new File(filePath.toString() + "ForgetWord.txt"), true));
		psFalse = new PrintStream(new FileOutputStream(new File(filePath.toString() + "FalseWord.txt"), true));

		Init();
		timer.start();
		new Thread(this).start();
	}

	public void paintComponent(Graphics g) {
		Image img = new ImageIcon("gra1.jpg").getImage();
		g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
	}

	private void Init() {

		dic = words.get(wordIndex++ % words.size()).split(" ");
		sb = new StringBuffer(dic[0]);

		// 保证二者不相等
		lackPos2 = lackPos1 = (int) (Math.random() * sb.length());
		while (lackPos2 == lackPos1) {
			lackPos2 = (int) (Math.random() * sb.length());
		}
		if (lackPos2 < lackPos1) {
			int tmp = lackPos2;
			lackPos2 = lackPos1;
			lackPos1 = tmp;
		}
		sb.setCharAt(lackPos1, '_');
		sb.setCharAt(lackPos2, '_');

		jlbLife.setText("剩余生命值:" + life);
		jlbLife.setBounds(0, 0, 200, 20);
		jlbWord.setText(sb.toString());
		jlbWord.setBounds((int) ((this.getWidth() - 20 * dic[0].length()) * Math.random()), 20, 20 * dic[0].length(),
				40);
		jlbChinese.setText(dic[1]);

		jlbChinese.setVisible(true);
		jlbChinese.setSize(dic[1].length() * 20, 20);
		jlbChinese.setLocation((this.getWidth() - jlbChinese.getWidth()) / 2,
				this.getHeight() - jlbChinese.getHeight());

	}

	private void checkFail() {
		if (jlbWord.getY() >= jlbChinese.getY() - 20) {
			life--;
			psForget.println(words.get(wordIndex - 1));
			Init();
		}
		if (0 == life) {
			JOptionPane.showMessageDialog(null, "游戏失败");
			System.exit(0);
		} else if (life <= -100) {
			psCorrect.close();
			psFalse.close();
			psForget.close();
			JOptionPane.showMessageDialog(null, "您被房主请出房间");
			System.exit(0);
		}

	}

	// 每隔250毫秒下落
	public void actionPerformed(ActionEvent e) {
		jlbChinese.setSize(dic[1].length() * 20, 20);
		jlbChinese.setLocation((this.getWidth() - jlbChinese.getWidth()) / 2,
				this.getHeight() - jlbChinese.getHeight());
		jlbWord.setLocation(jlbWord.getX(), jlbWord.getY() + 10);
		checkFail();
	}

	@Override
	public void keyTyped(KeyEvent e) {

		if (-1 != lackPos1) {

			if (e.getKeyChar() == dic[0].charAt(lackPos1)) {

				jlbFollow jlbfollow = new jlbFollow(jlbWord, e.getKeyChar(), this, lackPos1);
				this.add(jlbfollow);
				new Thread(jlbfollow).start();

				lackPos1 = -1;
			} else {
				life -= 2;
				ps.println("+1");
				psFalse.println(words.get(wordIndex - 1));
				Init();
			}
		} else {

			//Jiayige防误触
			if (e.getKeyChar() == dic[0].charAt(lackPos2)) {
				life++;
				psCorrect.println(words.get(wordIndex - 1));
				jlbFollow jlbfollow = new jlbFollow(jlbWord, e.getKeyChar(), this, lackPos2);
				this.add(jlbfollow);
				lackPos2 = -1;
				new Thread(jlbfollow).start();

			} else {
				life -= 2;
				ps.println("+1");
				psFalse.println(words.get(wordIndex - 1));
				Init();
			}
		}
		checkFail();
	}

	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void keyReleased(KeyEvent e) {
		// TODO Auto-generated method stub

	}

	@Override
	public void run() {
		boolean canRun = true;

		while (canRun) {

			try {
				String str = brServer.readLine();
				life += Integer.parseInt(str);
				jlbLife.setText("剩余生命值:" + life);
			} catch (Exception e) {
				canRun = false;
				psCorrect.close();
				psFalse.close();
				psForget.close();
			}
		}

	}

	private class jlbFollow extends JLabel implements Runnable {

		JLabel jlbWord;
		int pos;

		public jlbFollow(JLabel jlbWord, char ch, JPanel jpl, int pos) {
			this.setFont(new Font("黑体", Font.BOLD, 20));
			this.setForeground(Color.red);
			this.setText(String.valueOf(ch));
			this.setBounds((int) ((jpl.getWidth() - 20) * Math.random()), jpl.getHeight(), 20, 20);
			this.jlbWord = jlbWord;
			this.pos = pos;
		}

		public void run() {

			while (Math.pow(jlbWord.getX() - this.getX(), 2) + Math.pow(jlbWord.getY() - this.getY(), 2) > 100) {
				int x = (jlbWord.getX() - this.getX()) / 2, y = (jlbWord.getY() - this.getY()) / 2;
				this.setLocation(x + this.getX(), y + this.getY());
				try {
					Thread.sleep(50);
				} catch (Exception e) {

				}
			}
			this.setVisible(false);
			sb.setCharAt(pos, this.getText().charAt(0));
			jlbWord.setText(sb.toString());

			try {
				Thread.sleep(100);
			} catch (Exception e) {

			}

			if (lackPos1 == lackPos2)
				Init();
		}

	}
}

③GameFrame类

package Game;

import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.net.*;

public class GameFrame extends JFrame{
	
	public GameFrame() throws Exception{
		
		Image img = Toolkit.getDefaultToolkit().getImage("clientIcon.jpeg");
		GamePanel gpl = new GamePanel();
		this.setSize(gpl.getSize());
		this.setTitle(gpl.nickName);
		this.add(gpl);
		
		this.setIconImage(img);
		this.setTitle(gpl.nickName);
		this.setVisible(true);
		this.setResizable(false);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	public static void main(String[] args) throws Exception {
		new GameFrame();
	}
	
	
	
}