你画我猜 博客分类: 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上加上鼠标监听器开始画画。其余客户端同步接收到画的或者撤销的形状,并且可以猜测答案,如果猜对了,服务器将给出提示,并且清空画布重新开始。
运行效果图:(只实现了直线,矩形,椭圆,棱形,橡皮擦,画笔,以及调色板(最后一个白色按钮))
开始(没确定答案不能开始画)
猜错了:
猜对了
具体的实现:
/** * 服务器 * * @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
-
java String你知道少 博客分类: java 语言 java String
-
Cassandra重启报错 java.lang.ClassCastException 博客分类: Cassandra我的Java CassandraClassCastExceptionByteBuffer
-
使用Zookeeper来为你的程序加上Leader Election的功能。 博客分类: java基础 zkHadoopmavenApache.net
-
记我的第一次spring aop项目实践 博客分类: Java aopspringaspectjspring aop日志记录
-
Java保证程序结束时调用释放资源函数 博客分类: 我的笔记 java程序结束释放资源虚拟机
-
Java保证程序结束时调用释放资源函数 博客分类: 我的笔记 java程序结束释放资源虚拟机
-
抖音直播怎么进行你画我猜?抖音直播你画我猜教程
-
抖音直播怎么进行你画我猜?抖音直播你画我猜教程