做这个主要是学习使用一下canvas和websocket,项目地址。

你画我猜大家应该都玩过,一个人画,其他人猜。现在刚刚实现了最基本的功能,以后慢慢修改。

项目截图

完成进度

  • 登录,登录后username存储到了sessionStorage中。
  • 座位,登录后可以选择座位,并通过ws告诉所有人你的座位。
  • 发送内容,登录后可以通过ws将输入内容发布给所有人。
  • 聊天记录,可以接收所有人的聊天打字内容,可以清屏。
  • 开始游戏,一楼可以点击开始游戏按钮,开始游戏后不可再调整座位。
  • 按座位顺序轮流进行画图与看图猜词,画图玩家不可以输入内容与发送内容。
  • 随机在词表中选择词语。用于练习只写了12个词。
  • 发送内容为正确词时特殊显示(游戏开始后有效)。
  • 记分
  • 多房间游戏
  • 移动端

项目架构

客户端

前端从前面的截图可以看到,基本分为四部分,画图部分,选择座位部分,聊天显示部分,发送聊天部分。四部分当然要使用一个websocket连接,所以使用了vuex,同时在vuex中保存用户名等数据。

建立ws连接,存入vuex中。

this.ws = new WebSocket('ws://localhost:3000/');
this.$store.commit('wsStore/connect',ws);

登录部分

这里登录不太重要,就直接把用户名放到sessionStorage里了,同时再存入vuex中供其他部分使用。

saveName(){if(!window.sessionStorage){alert("浏览器不支持sessionStorage!");}else{let storage = window.sessionStorage;storage.setItem("username",this.inputName);}this.username=this.inputName;this.showLogin=0;this.$store.commit('username/setUsername',this.inputName)
}

完整代码可以查看github里的src/components/HomeHeader.vue文件。

画图部分

这部分是最主要的部分,先定义一个canvas

<canvas id="drawBoard" width="700" height="400"></canvas>

然后是一个class draw,这里只是使用画笔的函数,从上面gif可以看出来还可以画直线,矩形,圆,调整颜色等,最后也都是写到这个类里面的。

class canvasDraw{constructor(id,ws){this.ws= ws;this.canvas = document.getElementById(id);this.ctx = this.canvas.getContext('2d');this.stage_info = this.canvas.getBoundingClientRect()this.path = {beginX: 0,beginY: 0,endX: 0,endY: 0}this.isDraw=false}draw(){let that = thisthis.canvas.onmousedown = () => {that.ctx.beginPath()that.path.beginX = event.pageX - that.stage_info.leftthat.path.beginY = event.pageY - that.stage_info.topthat.ctx.moveTo(that.path.beginX,that.path.beginY)that.isDraw=true}this.canvas.onmousemove = () => {if(that.isDraw){that.drawing(event)}}this.canvas.onmouseup = () => {that.isDraw=falsethat.ws.send('stop,')}}drawing(e) {this.path.endX = e.pageX - this.stage_info.leftthis.path.endY = e.pageY - this.stage_info.topthis.ctx.lineTo(this.path.endX,this.path.endY)this.ctx.stroke()this.ws.send(`draw,${this.path.beginX},${this.path.beginY},${this.path.endX},${this.path.endY}`)}clearCanvas(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height); this.ws.send('clear,')}
}

可以看到,构造函数主要是保存起始和终点的坐标,虽然画笔可以随便画,但是也是由一小段一小段线段组成的。

然后draw函数,当监听到mousedown事件后,新建一条路径,将起始坐标保存下来,随着mousemove事件,鼠标每移动一点,就画一条线段,直到监听到mouseup事件,完成一次画线。

画直线、矩形、圆的原理差不多,都是保存鼠标的起始和终点坐标,然后算一下矩形的长和宽,圆的圆心和半径即可。以画圆的代码为例。

drawRound(){this.canvas.onmousedown=()=>{this.ctx.beginPath()this.path.beginX = event.pageX - this.stage_info.leftthis.path.beginY = event.pageY - this.stage_info.top}this.canvas.onmouseup=()=>{this.path.endX = event.pageX - this.stage_info.leftthis.path.endY = event.pageY - this.stage_info.toplet width = this.path.endX-this.path.beginX;let height = this.path.endY-this.path.beginY;this.ctx.arc(this.path.beginX+width/2, this.path.beginY+height/2,Math.sqrt(width*width+height*height)/2,0, Math.PI *2);this.ctx.fill();this.ctx.stroke();}}

改变线条宽度、颜色等就更简单了,只要将选择的数值传到类中的如下函数里即可。

changeColor(color){this.ctx.strokeStyle = color;
}

完整代码可以查看github里的src/components/DrawBoard.vue文件。

座位部分

这一部分就没什么了,就点击button,做一下判断就好了。例如

seatDown(index){if(this.$store.state.wsStore.ws.readyState==3){this.$message('服务器未连接');return ;}if(this.username==null||this.username==''){this.$message('请登陆');}else if(this.seats[index]=='空位'){this.$store.state.wsStore.ws.send(`seats,${index},${this.username}`);}else{this.$message('此位置有人');}
},

如果是一楼的话,可以开始游戏,开始游戏就给后端发一个信号,然后就是后端的一些逻辑了。

begin(){this.$store.state.wsStore.ws.send(`begin,`);
},

还有,如果游戏开始之后,不可以再调整座位或者新用户坐下。

watch:{beginGame(newval,oldval){let buttonEle = document.querySelectorAll('button[component="seat"]');if(this.beginGame){for(let bt of buttonEle){bt.setAttribute('disabled','disabled');}}else{for(let bt of buttonEle){bt.removeAttribute('disabled');}}}
},

完整代码可以查看github里的src/components/TheSeats.vue文件。

聊天显示部分

聊天显示部分只负责将接受到的服务器的消息显示出来,可以清屏。

<div v-for="(record,index) in chatRecords" :key="index" class="record"><span class="blue">{{record.username}}</span>:{{record.content}}
</div>
watch:{msg(newval,oldval){let record = {username: this.msg[1],content: this.msg[2]};this.chatRecords.push(record);let div = document.getElementById('totalBoard')div.scrollTop = div.scrollHeight;console.log(div.scrollTop,div.scrollHeight+100)}},

完整代码可以查看github里的src/components/ChatBoard.vue文件。

聊天输入部分

聊天输入部分只负责将数据发送给服务器,然后服务器再发送给所有用户,上面的聊天显示部分接受到之后显示出来。

submitWord(){if(this.$store.state.wsStore.ws.readyState==3){this.$message('服务器未连接');return ;}if(this.username==null||this.username==''){this.$message('请登陆');return ;}if(this.guassWord!=''){this.$store.state.wsStore.ws.send(`chat,${this.username},${this.guassWord}`);this.guassWord='';}
}

还有如果轮到自己画的话,不可以发送信息。

watch:{drawuser(newval,oldval){let inputEle = document.querySelector('input[component="inputBoard"]');let buttonEle = document.querySelector('button[component="inputBoard"]');if(this.drawuser==this.username){inputEle.setAttribute('disabled','disabled')buttonEle.setAttribute('disabled','disabled');}else{inputEle.removeAttribute('disabled');buttonEle.removeAttribute('disabled');}}
},

完整代码可以查看github里的src/components/InputBoard.vue文件。

服务端

服务端使用了node的ws模块,写的比较简单,只是满足了游戏需要。

主要是将用户画图时的起始和终点坐标做一个转发。

有用户坐下或调整座位后发送一下最终的座位信息。

游戏开始后,按座位顺序,轮流给每个人发送画图人和要画的词。

用户输入后判断是否为正确词,如果不对,就发送原词,如果猜对了,就发送“猜对了”

// 导入WebSocket模块:
const WebSocket = require('ws');// 引用Server类:
const WebSocketServer = WebSocket.Server;// 实例化:
const wss = new WebSocketServer({port: 3000
});let seats=['空位','空位','空位','空位','空位','空位']
let dict=['老鼠','牛','老虎','兔子','龙','蛇','马','羊','猴子','鸡','狗','猪']let gameBegin = false;
let guassWord = '';
wss.on('connection', function (ws) {console.log(`[SERVER] connection()`);ws.on('message', function (message) {console.log(`[SERVER] Received: ${message}`);let msg = message.split(',');if(msg[0]=="seats"&&msg.length==3){let i = seats.indexOf(msg[2])if(i>-1){seats[i]='空位';}seats[msg[1]]=msg[2];wss.clients.forEach((client) => {client.send(`seats,${seats}`)})}else if(msg[0]=='begin'){function settime(username,ms){let i = Math.floor(Math.random()*dict.length);setTimeout(()=>{guassWord=dict[i];wss.clients.forEach((client) => {client.send(`begin,${username},${dict[i]}`)})},ms)}gameBegin=true;let ms = 0;for(let username of seats){if(username!='空位'){settime(username,ms)ms+=10000;}}setTimeout(()=>{wss.clients.forEach((client) => {client.send(`end,`)})gameBegin=false;},ms)}else if(gameBegin&&msg[0]=='chat'&&msg[2]==guassWord){wss.clients.forEach((client) => {client.send(`chat,${msg[1]},猜对啦!`)})}else{wss.clients.forEach((client) => {client.send(message)})}})
});

canvas+websocket+vue做一个你画我猜小游戏相关推荐

  1. canvas+websocket+vue做一个完整的你画我猜小游戏

    第一次在掘金发文章,瑟瑟发抖. 这个主要是为了学习使用一下canvas和websocket,项目地址.求star- 你画我猜大家应该都玩过,一个人画,其他人猜.目前实现了最基本的功能,慢慢修改. 项目 ...

  2. 用pygame做一个简单的python小游戏---七彩同心圆

    用pygame做一个简单的python小游戏-七彩同心圆 这个小游戏原是我同学python课的课后作业,并不是很难,就简单实现了一下,顺便加强一下pygame库的学习. 玩法:每次点击鼠标时,会以鼠标 ...

  3. python七彩同心圆_用pygame做一个简单的python小游戏---七彩同心圆

    用pygame做一个简单的python小游戏---七彩同心圆 用pygame做一个简单的python小游戏-七彩同心圆 这个小游戏原是我同学python课的课后作业,并不是很难,就简单实现了一下,顺便 ...

  4. 用pygame做一个简单的python小游戏---贪吃蛇

    用pygame做一个简单的python小游戏-贪吃蛇 贪吃蛇游戏博客链接:(方法一样,语言不一样) c++贪吃蛇:https://blog.csdn.net/weixin_46791942/artic ...

  5. 用pygame做一个简单的python小游戏---生命游戏

    用pygame做一个简单的python小游戏-生命游戏 生命游戏(Game of Life) 生命游戏(Game of Life)是剑桥大学约翰·何顿·康威(John Horton Conway)教授 ...

  6. 做一个简单的java小游戏--单机版五子棋

    做一个简单的java小游戏–单机版五子棋 学了java有一段时间了,今天就来搞一个简单的单机版五子棋游戏. 实现功能:那必须能进行基础的输赢判断.还有重新开始的功能,悔棋的功能,先手设置的功能和退出的 ...

  7. 手把手教你做一个Java贪吃蛇小游戏

    大家好,我是孙不坚1208,这篇博客给大家分享一下:如何做一个贪吃蛇小游戏(Java版)的exe应用程序,希望能给需要帮助的朋友带来方便. 手把手教你做一个Java贪吃蛇小游戏的exe应用程序 一.J ...

  8. 做一个简单的java小游戏--贪吃蛇

    做一个简单的java小游戏–贪吃蛇 贪吃蛇游戏博客链接:(方法一样,语言不一样) c++贪吃蛇:https://blog.csdn.net/weixin_46791942/article/detail ...

  9. python井字棋_用Python做一个井字棋小游戏

    井字棋是一个经典的小游戏,在九宫格上玩家轮流画OXO,当每列或每行或是两个对角成一线时便是获胜. 今天就用Python编写一个井字棋小游戏,与电脑对战. 程序执行画面如下图所示: 程序提供了两种人工智 ...

  10. python井字棋游戏人机对战_用Python做一个井字棋小游戏

    井字棋是一个经典的小游戏,在九宫格上玩家轮流画OXO,当每列或每行或是两个对角成一线时便是获胜. 今天就用Python编写一个井字棋小游戏,与电脑对战. 程序执行画面如下图所示: 程序提供了两种人工智 ...

最新文章

  1. Linux CNTOS7 修改网络配置
  2. 背包模型dp2之二维费用背包
  3. 福利 | 大数据新媒体平台面向清华校内师生开放!
  4. 03-----数据类型
  5. System.arraycopy和Arrays.copyOf的原理解剖
  6. RIPv1与RIPv2互通
  7. 前端学习 -- 内联框架iframe
  8. 编码器的一点微小认识
  9. 分析器错误信息: 未能加载类型命名空间.类...
  10. MATLAB中的柱面与球面
  11. 获取列表的最后一个元素
  12. 翻译: 3.3. 线性回归的简明实现 pytorch
  13. html展开图片,Html 显示图片的三种方式
  14. 全球首个大规模虐童图像数据库!标记15万图像、20类信息,自动判断图片是否违法​...
  15. [转载]Meta Learning单排小教学
  16. xcode13 swift语言 ios开发 快捷代码优化方式(代码重构)例子
  17. 方舟自建服务器500人,方舟等级上限mod500 | 手游网游页游攻略大全
  18. 最新最全的免费股票数据接口--沪深A股基本面数据API接口(四)
  19. Stream流之简介
  20. 通宵教室(ACM水题)

热门文章

  1. CentOS 6 x86里交叉编译libxml2
  2. 卸载 Oracle 19c
  3. 【方太】顺利通过CMMI3认证
  4. PeckShield旗下数字资产反洗钱系统CoinHolmes亮相北京网络安全大会!
  5. [Fondar]手机屏幕驱动板HDMI接口USB触摸使用步骤
  6. 单双面打印价格一样吗_正式合同应该打印单面还是双面?
  7. 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式
  8. 论文翻译|疾病自动编码|Clinical-Coder: Assigning Interpretable ICD-10 Codes to Chinese Clinical Notes
  9. 别催更啦!手淘全链路性能优化下篇--容器极速之路
  10. 手机qq旋风.android,QQ旋风安卓版2020,QQ旋风手机版安卓版2020最新版本预约 v1.0 - 浏览器家园...