欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

你画我猜 博客分类: Java  

程序员文章站 2024-03-11 21:28:19
...

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

 

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

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上加上鼠标监听器开始画画。其余客户端同步接收到画的或者撤销的形状,并且可以猜测答案,如果猜对了,服务器将给出提示,并且清空画布重新开始。

 

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

开始(没确定答案不能开始画)
你画我猜
            
    
    博客分类: Java  
 

猜错了:
你画我猜
            
    
    博客分类: Java  
 

猜对了


你画我猜
            
    
    博客分类: Java  
 

具体的实现:

/**
 * 服务器
 * 
 * @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) {
		}
	}
}

 

 

剩余文件:

 

  • 你画我猜
            
    
    博客分类: Java  
  • 大小: 154.4 KB
  • 你画我猜
            
    
    博客分类: Java  
  • 大小: 177 KB
  • 你画我猜
            
    
    博客分类: Java  
  • 大小: 142 KB