`

你画我猜

    博客分类:
  • Java
 
阅读更多

根据之前的画图板加上网络改造一下成一个网络的。

 

画图板中,有许多形状,都是包装为对象的,例如,直线对象:

public class Line extends Shape{

	public Line(int x1,int y1,int x2,int y2,Color c){
		this.x1 = x1;
		this.y1 = y1;
		this.x2 = x2;
		this.y2 = y2;
		this.c = c;
	}
	void draw(Graphics g) {
	
	 g.setColor(c);
		 g.drawLine(x1,y1,x2,y2);
	}
}

 

所有的形状都有相同父类Shape。

 

在服务器上,接收消息并转发给其它猜的客户端。

在客户端上,第一个连上的客户端为画家。其他的都不能画只能猜答案。

 

 

关于网络协议:定义了消息类:

其中会有7种不同的消息,分别在服务器或者客户端进行处理。在这里,只有发答案在服务端进行处理。

public class Message implements Serializable {
	// 0表示画形状,1表示撤销形状,2表示格式,3表示打开的文件路径,4表示发答案,5表示玩家形象:画者P 1,猜者C 2  6表示将答案正确与否等消息由服务器发给每个客户端

	// 6表示消息
	int count = 0;
	Shape sp = null;
	String str = null;

	/**
	 * count为0画形状,1撤销
	 * 
	 * @param count
	 * @param sp
	 */
	public Message(int count, Shape sp) {
		this.count = count;
		this.sp = sp;
	}

	/**
	 * count为2为定义保存格式,3文件打开路径,4发答案
	 * 
	 * @param count
	 * @param answer
	 */
	public Message(int count, String str) {
		this.count = count;
		this.str = str;
	}
}

 

服务端与客户端具有许多相同的类,如形状类,消息类等。

不同的只有服务器类以及处理线程。

 

步骤:

首先,启动服务器,然后,启动一个客户端链接服务器。由服务器发送角色的消息给他。之后连的客户端类似。如果收到消息是画家,则将输入答案框变色突出显示提示其先输入正确答案。确认完毕之后在center上加上鼠标监听器开始画画。其余客户端同步接收到画的或者撤销的形状,并且可以猜测答案,如果猜对了,服务器将给出提示,并且清空画布重新开始。

 

运行效果图:(只实现了直线,矩形,椭圆,棱形,橡皮擦,画笔,以及调色板(最后一个白色按钮))

开始(没确定答案不能开始画)

 

猜错了:

 

猜对了



 

具体的实现:

/**
 * 服务器
 * 
 * @author Huangbin
 * @date 2014年7月31日
 */
public class PaintServer {
	public static ArrayList<PaintThread> slist = new ArrayList<PaintThread>();
	public static int num = 1;// 保存有多少个人连上了
	public static ArrayList<Integer> roleList = new ArrayList<Integer>();
	public static String theAnswer;

	public static void main(String[] args) {
		// 套接字,指定端口
		try {
			ServerSocket ss = new ServerSocket(9999);// 不要使用80等其他程序常用端口
			System.out.println("服务器已启动,等待连接。。。");
			while (true) {
				// 必须等到有客户端连上,该方法才能执行完毕(阻塞方法)。接收到一个客户端对象
				Socket st = ss.accept();
				while (roleList.contains(num)) {
					num++;// 连上一个就加一
				}
				roleList.add(num);
				System.out.println(st.getInetAddress().getHostName() + "已连接上");
				PaintThread pt = new PaintThread(st);
				pt.start();
				slist.add(pt);
			}
		} catch (BindException e) {
			System.out.println("端口使用中,关闭服务器程序重新开始");
			System.exit(0);
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}

}

 

/**
 * 转发消息的线程
 * 
 * @author Huangbin
 * @date 2014年7月31日
 */
public class PaintThread extends Thread {

	Socket st;
	PaintThread pt;
	String name;
	ObjectOutputStream oos;
	ObjectInputStream ois;
	boolean bConnected = true;
	Integer num;

	public PaintThread(Socket st) {
		this.st = st;
		this.num = PaintServer.num;
		name = "player" + num;
	}

	public void run() {
		try {
			oos = new ObjectOutputStream(st.getOutputStream());
			ois = new ObjectInputStream(st.getInputStream());
			// 连上了马上将角色消息发给客户端
			if (num == 1) {
				// 只允许第一个人画,其他人猜
				this.sent(new Message(5, "P"));
				System.out.println("Player1");
			} else {
				this.sent(new Message(5, "C"));
				System.out.println("Player" + num);
			}
			while (bConnected) {
				Message msg = (Message) ois.readObject();
				if (msg.count == 4) {
					// 如果是发答案,在服务器进行处理
					if (this.num == 1) {
						// 设置答案
						PaintServer.theAnswer = msg.str;
						this.sent(new Message(5, "P"));
					} else {
						if (msg.str.equals(PaintServer.theAnswer)) {
							// 猜对了,给每个人发一条消息告诉别人this.name猜对了
							// 结束游戏,开始新的,给每个客户端发送清空屏幕消息
							Message mes = new Message(6, this.name + "猜答案是:"
									+ msg.str + "  恭喜回答正确。");
							Message clear = new Message(6, "#clear");
							for (int i = 0; i < PaintServer.slist.size(); i++) {
								pt = PaintServer.slist.get(i);
								pt.sent(mes);
								pt.sent(clear);
							}
						} else {
							System.out.println(PaintServer.theAnswer);
							// 猜错了,给每个人发一条消息告诉别人this.name猜错了
							Message mes = new Message(6, this.name + "猜答案是:"
									+ msg.str + "  还差一点点。");
							for (int i = 0; i < PaintServer.slist.size(); i++) {
								pt = PaintServer.slist.get(i);
								pt.sent(mes);
							}
						}
					}
				} else {
					for (int i = 0; i < PaintServer.slist.size(); i++) {
						pt = PaintServer.slist.get(i);
						if (pt != this) {
							pt.sent(msg);
						}
					}
				}
			}
		} catch (SocketException se) {
			this.bConnected = false;
			PaintServer.roleList.remove(this.num);
			PaintServer.num = 1;
			System.out.println(PaintServer.num);
			PaintServer.slist.remove(this);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 发消息的方法
	 * 
	 * @param msg
	 */
	public void sent(Message msg) {
		try {
			oos.writeObject(msg);
			oos.flush();
		} catch (SocketException se) {
			this.bConnected = false;
			PaintServer.slist.remove(this);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

这个类许多代码是以前画图板的,随便修改了一下在监听器那里加了发消息,所以有些乱。

/**
 * 客户端连接
 * 
 * @author Huangbin
 * @date 2014年7月31日
 */
public class PaintClient {
	Socket st = null;
	ObjectOutputStream oos;
	ObjectInputStream ois;
	String name;
	boolean bConnected = false;
	DrawUI du;
	ArrayList<Shape> list = new ArrayList<Shape>();// static 可实现类名调用
	ArrayList<Shape> list1 = new ArrayList<Shape>();// 保存被撤销的对象
	static int colors[][];
	public static boolean isHb = true;
	static Point p = new Point();// 用來保存center左上角的座标
	static Dimension dim;
	DrawListener lis = new DrawListener();

	public static void main(String[] args) {
		PaintClient pc = new PaintClient();
		pc.connect();

	}

	public void connect() {
		try {
			// 链接服务器
			st = new Socket("127.0.0.1", 9999);
			// 获得客户端名字
			name = st.getInetAddress().getHostName();
			// 获得流
			oos = new ObjectOutputStream(st.getOutputStream());
			ois = new ObjectInputStream(st.getInputStream());
			// 创建画图窗体
			// ********************************************************//
			du = new DrawUI(oos, list, list1, lis);
			// 开启接收线程
			Receive rc = new Receive();
			rc.start();
			bConnected = true;
		} catch (ConnectException ce) {
			System.out.println("检查服务器");
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void disConnected() {
		try {
			ois.close();
			oos.close();
			st.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	class Receive extends Thread {

		public void run() {
			try {
				while (bConnected) {
					Message msg = (Message) ois.readObject();

					// 如果是1表示要添加,0为撤销,接收到了对象就保存起来
					if (msg.count == 1) {
						list.add(msg.sp);
					} else if (msg.count == 0) {
						list.remove(msg.sp);
					} else if (msg.count == 2) {
						if (msg.str.equals("hb")) {
							// 新建对象文件
							isHb = true;
						} else {
							// 新建bmp文件
							isHb = false;
						}
						du.newPaint();
					} else if (msg.count == 3) {
						// 打开文件,路径为string
						File f = new File(msg.str);
						// 判断是bmp还是hb文件
						if (msg.str.endsWith("bmp")) {
							du.repaintData(f);
						} else {
							list.clear();
							list = FileSave.readHb(f);
							du.repaint1();
						}
					} else if (msg.count == 4) {
						// 原则上是不会从服务器接收到答案的,到了这里说明有错误
						System.out.println("逻辑错误,接收到了未处理的答案。");
					} else if (msg.count == 5) {
						// System.out.println("接收到角色分配的消息");
						if (du.player == 0 && msg.str.equals("C")) {
							du.player = 2;
						} else if (msg.str.equals("P")) {
							if (du.player == 0) {
								// 画画的角色
								du.player = 1;
								du.answer.setBackground(Color.white);
								du.answer.setForeground(Color.black);
							} else {
								du.answer.setBackground(Color.gray);
								du.answer.setForeground(Color.white);
								System.out.println("添加监听器");
								DrawUI.center.addMouseListener(lis);
								DrawUI.center.addMouseMotionListener(lis);
							}
						}
					} else if (msg.count == 6) {
						// 接收到了答案处理消息
						if (!msg.str.equals("#clear")) {
							// 不是重来消息
							// 将消息显示在DrawUI上
							du.tip.setText(msg.str);
							System.out.println(msg.str);
						} else {
							// 接收到重新开始的消息,清空画布,即清空队列,重新开始猜测答案
							System.out.println("重新开始游戏");
							list.clear();
							list1.clear();
							if (du.player == 1) {
								du.answer.setEditable(true);
								du.answer.setBackground(Color.white);
								du.answer.setForeground(Color.black);
								DrawUI.center.removeMouseListener(lis);
								DrawUI.center.removeMouseMotionListener(lis);
							}
						}
					}
					DrawUI.center.repaint();
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SocketException ee) {
				String str = "服务器连接失败\r\n";
				System.out.print(str);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 继承鼠标适配器抽象类,只需要重写需要用的方法,不需要全部实现.
	 * 
	 * @author Huangbin
	 * @date 2014年7月31日
	 */
	public class DrawListener extends MouseAdapter implements ActionListener,
			WindowListener {
		File f = new File("D:\\Pic.hb");
		FileSave fs = new FileSave();
		File fAuto = new File("D:\\PicAutoBmp.hb");
		private int x1, x2, x3, x4, y1, y2, y3, y4;
		Color c = Color.black;
		JColorChooser chooser = new JColorChooser();
		ArrayList<Line> drag = new ArrayList<Line>();
		// 声明单选按钮组
		private String form;// 颜色,形状的标记.
		private Graphics g;
		JFileChooser jfc = new JFileChooser();
		boolean chexiaobiaoji = true;

		/**
		 * MouseEvent 鼠标事件对象,可以得到触发事件的对象
		 */
		public void mousePressed(MouseEvent e) {
			// 获得事件源对象
			DrawUI.center = (JPanel) e.getSource();

			// 得到按钮组中被选中的按钮的动作命令,可以用来区分同名字的按钮.

			form = du.group.getSelection().getActionCommand();
			System.out.println(form);
			g = DrawUI.center.getGraphics();
			x1 = e.getX();
			y1 = e.getY();
			drag.clear();
		}

		public void mouseReleased(MouseEvent e) {

			x2 = e.getX();
			y2 = e.getY();
			x3 = (x1 > x2) ? x2 : x1;// x3=Math.min(x1,x2):
			y3 = (y1 > y2) ? y2 : y1;
			x4 = (x1 < x2) ? x2 : x1;// x4=Math.max(x1,x2);
			y4 = (y1 < y2) ? y2 : y1;

			// 获得center相对于屏幕的绝对坐标
			// 相对于窗体的用getLocation

			p = DrawUI.center.getLocationOnScreen();
			dim = DrawUI.center.getSize();// 获得center(JPanel)的大小,为Dimension的对象,宽度和高度
			try {
				Robot robot = new Robot();
				// 定义矩形区域
				Rectangle rect = new Rectangle(p, dim);

				// 调用截取屏幕的方法
				BufferedImage img = robot.createScreenCapture(rect);

				// 创建二维数组保存每个点的颜色。先高度后宽度(二维数组和屏幕x,y轴相反)。

				// 每种颜色由ARGB四个byte组成,每种颜色都可以用int来表示。总共2^32-1种颜色。(32位)
				// Color[][] colors=new Color[dim.height][dim.width];
				colors = new int[dim.height][img.getWidth()];// 此处可以用dim获得高度,也可以用img获得。
				for (int i = 0; i < colors.length; i++) {
					for (int j = 0; j < colors[i].length; j++) {
						colors[i][j] = img.getRGB(j, i);// img获得的坐标和数组下标正好相反

					}
				}
			} catch (AWTException e1) {
				e1.printStackTrace();
			}

			try {
				if (form.equals("line")) {
					Line line = new Line(x1, y1, x2, y2, c);
					list.add(line);

					oos.writeObject(new Message(1, line));
					oos.flush();

					line.draw(g);
				} else if (form.equals("rect")) {
					Rect rect = new Rect(x3, y3, x4, y4, c);
					list.add(rect);

					oos.writeObject(new Message(1, rect));
					oos.flush();

					rect.draw(g);
				} else if (form.equals("oval")) {
					Oval oval = new Oval(x3, y3, x4, y4, c);
					list.add(oval);

					oos.writeObject(new Message(1, oval));
					oos.flush();

					oval.draw(g);
				} else if (form.equals("round")) {
					Round round = new Round(x3, y3, x4, y4, c);
					list.add(round);

					oos.writeObject(new Message(1, round));
					oos.flush();

					round.draw(g);
				} else if (form.equals("prismatic")) {
					Prismatic prismatic = new Prismatic(x3, y3, x4, y4, c);
					list.add(prismatic);

					oos.writeObject(new Message(1, prismatic));
					oos.flush();
					prismatic.draw(g);
				} else if (form.equals("eraser")) {
					Eraser es = new Eraser(x1, y1, Color.white);
					list.add(es);
					oos.writeObject(new Message(1, es));
					oos.flush();
					es.draw(g);
				}
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}

		/**
		 * 用于画点的方法
		 */
		public void mouseDragged(MouseEvent e) {
			x2 = e.getX();
			y2 = e.getY();

			if (form.equals("pen")) {
				Line line = new Line(x1, y1, x2, y2, c);
				line.draw(g);
				list.add(line);
				try {
					oos.writeObject(new Message(1, line));
					oos.flush();
				} catch (IOException ee) {
					ee.printStackTrace();
				}
				x1 = x2;
				y1 = y2;
				// 此处撤销办法
				// 增加一个属性记录line是不是画的点,将这一次加进去的line的下标保存起来,点击撤销的时候将他们全部移到list1;
			} else if (form.equals("eraser")) {
				Eraser es = new Eraser(x1, y1, Color.white);
				list.add(es);
				try {
					oos.writeObject(new Message(1, es));
					oos.flush();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				es.draw(g);
				x1 = x2;
				y1 = y2;
			}
			chexiaobiaoji = true;
		}

		public void actionPerformed(ActionEvent e) {
			String str = e.getActionCommand();// 获得产生动作的按钮
			System.out.println(str);
			g = DrawUI.center.getGraphics();// 重新得到g.
			try {
				if (str.equals("对象格式")) {
					du.newPaint();
					oos.writeObject(new Message(2, "hb"));
					oos.flush();
				} else if (str.equals("bmp格式")) {
					isHb = false;
					du.newPaint();
					oos.writeObject(new Message(2, "bmp"));
					oos.flush();
				} else if (str.equals("退出")) {
					System.exit(0);
				} else if (str.equals("撤销")) {
					if (drag.size() > 0 && chexiaobiaoji) {
						chexiaobiaoji = false;
						System.out.println(drag.size());
						for (Line line : drag) {
							list.remove(line);
							oos.writeObject(new Message(0, line));
							oos.flush();
							// list1.add(line);
						}
						du.repaint1();
					} else if (list.size() != 0) {
						Shape sp = list.remove(list.size() - 1);
						list1.add(sp);
						oos.writeObject(new Message(0, sp));
						oos.flush();
						du.repaint1();
					}

				} else if (str.equals("恢复")) {
					if (drag.size() > 0 && chexiaobiaoji) {
						for (Line line : drag) {
							list.add(line);
							oos.writeObject(new Message(1, line));
							oos.flush();
						}
						// drag.clear();
						du.repaint1();
					} else if (list1.size() > 0) {
						Shape sp = list1.remove(list1.size() - 1);
						list.add(sp);

						oos.writeObject(new Message(1, sp));
						oos.flush();

						du.repaint1();
					} else {
						chexiaobiaoji = true;
					}

				} else if (str.equals("关于")) {
					JOptionPane
							.showMessageDialog(
									null,
									"版本号:Bmp\r\n功能:\r\n画点,直线,矩形,椭圆\r\n新建撤销前进操作\r\n更改颜色\r\n保存bmp,hb格式图片\r\n增加保存打开bmp和hb格式图片功能,暂时不能自定义打开路径.",
									"关于", JOptionPane.CLOSED_OPTION);
				} else if (str.equals("保存")) {
					// 判断是不是新建的对象文件
					if (isHb) {
						FileSave.saveHb(f, list);
					} else {
						// 否则保存为bmp格式
						bmpsave(f);
					}
				} else if (str.equals("hb")) {
					FileSave.saveHb(f, list);
				} else if (str.equals("bmp")) {
					int state = jfc.showOpenDialog(du);
					if (state == JFileChooser.APPROVE_OPTION) {// 点击了打开按钮
						File f = new File(jfc.getSelectedFile()
								.getAbsolutePath());
						bmpsave(f);
					}
				} else if (str.equals("打开")) {
					int state = jfc.showOpenDialog(du);
					if (state == 0) {// 点击了打开按钮
						String st = jfc.getSelectedFile().getAbsolutePath();
						File f = new File(st);
						oos.writeObject(new Message(3, st));
						oos.flush();
						// 判断是bmp还是hb文件
						if (st.endsWith("bmp")) {
							du.repaintData(f);
						} else {
							list.clear();
							list = FileSave.readHb(f);
							du.repaint1();
						}
					}
				} else if (str.equals("确定")) {
					if (du.player == 1) {
						System.out.println("aaaaa");
						oos.writeObject(new Message(4, du.answer.getText()
								.trim()));
						// 画家定义答案完成禁止编辑
						du.answer.setEditable(false);
					} else if (du.player == 2) {
						// 猜的发答案
						oos.writeObject(new Message(4, du.answer.getText()
								.trim()));
						oos.flush();
					} else {
						System.out.println("未分配到角色");
					}
				} else if (str.equals("其它")) {
					c = chooser.showDialog(du, "颜色", Color.BLACK);
					JButton jbt = (JButton) e.getSource();
					jbt.setBackground(c);
				} else {
					judgeColor(str);
				}
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}

		public void judgeColor(String str) {
			if (str.equals("黑色")) {
				c = Color.black;
			} else if (str.equals("深灰")) {
				c = Color.darkGray;
			} else if (str.equals("红色")) {
				c = Color.red;
			} else if (str.equals("黄色")) {
				c = Color.yellow;
			} else if (str.equals("绿色")) {
				c = Color.green;
			} else if (str.equals("蓝色")) {
				c = Color.blue;
			} else if (str.equals("白色")) {
				c = Color.white;
			} else if (str.equals("浅灰")) {
				c = Color.lightGray;
			} else if (str.equals("粉红")) {
				c = Color.pink;
			} else if (str.equals("橙色")) {
				c = Color.orange;
			} else if (str.equals("天蓝")) {
				c = Color.cyan;
			}
		}

		public void bmpsave(File f) {
			fs.write24bmp(f);
		}

		public void windowClosing(WindowEvent e) {
			FileSave.saveHb(fAuto, list);
			System.out.println("保存成功");
		}

		public void windowDeactivated(WindowEvent e) {
		}

		public void windowDeiconified(WindowEvent e) {
		}

		public void windowIconified(WindowEvent e) {
		}

		public void windowOpened(WindowEvent e) {
		}

		public void windowActivated(WindowEvent e) {
		}

		public void windowClosed(WindowEvent e) {
		}
	}
}

 

 

剩余文件:

 

  • 大小: 154.4 KB
  • 大小: 177 KB
  • 大小: 142 KB
1
1
分享到:
评论
1 楼 okcd00 2014-11-28  
很棒的代码呢,学习了很多^_^

相关推荐

Global site tag (gtag.js) - Google Analytics