《HTML5游戏编程核心技术与实战》——2.7 案例:《你画我猜》

    xiaoxiao2024-03-28  11

    本节书摘来自异步社区《HTML5游戏编程核心技术与实战》一书中的第2章,第2.7节,作者: 向峰 更多章节内容可以访问云栖社区“异步社区”公众号查看。

    2.7 案例:《你画我猜》

    在这一小节中,我们将利用前面介绍的知识,来创作一个《你画我猜》游戏中的主要功能。《你画我猜》是一款老少皆宜的多人在线的网络游戏,2012年风靡一时,玩法其实也来源于生活当中,经常在娱乐节目中出现。通常在节目中是这样玩的,主持人写出一个词语,然后由一个参与者根据这个词语画出相应的图案,由另一个参与者来根据这个图案猜出这个词语,而《你画我猜》就把现实生活中的这个玩法转到了电脑上,玩法就是这么简单。当然,本章还无法开始做出一个网络游戏,哪怕是一个简单的单机游戏,本书第10章有《你画我猜》这个游戏完整的实现,本章将会完成《你画我猜》中的画板部分。没玩过《你画我猜》游戏的,也应该用过Windows的画板吧?好了,下面我们来实现这个画板。

    2.7.1 UI界面设计首先,我们来设计一个画板的主页面,关于使用div布局游戏的UI不是本书的重点,我们只关注游戏逻辑的实现,以下代码只是一个参考实现:

    <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> <title>你画我猜</title> <link href="images/CSS.css" rel="stylesheet" type="text/css"> </head> <body> <!--主容器层--> <div id="main" class="main"> <div>  <div class="tx"></div>  <div class="wz">   <div id="dround"></div>   <!--显示问题区域-->   <span id="question"></span>  </div>  <!--显示时间-->  <div id="qTime" class="time">60</div>  <div class="tc">离开房间<img src="images/edit_undo.png" align="absmiddle"></div> </div> <div class="qc"></div> <div> <!--绘图主层-->  <div class="hbbox">  <!--绘图区域-->   <div id="hb" class="hb">    <canvas id="paintArea" width="525" height="370" ></canvas>   </div>  </div> <!--工具选项区--> <div class="hbr"> <div id="operDiv">   <!--颜色选取区-->   <div id="ys" class="ys"></div>   <!--画笔大小选择区-->   <div id="bc" class="bc"></div>   <!--定义橡皮擦工具-->   <div id="ssx" class="tb">    <div id="btnRub" class="tb2"><img src="images/01945.png"></div>    <div id="btnClear" class="tb3"><img src="images/053753321.gif"></div>   </div> </div> <!--消息发布主层--> <div id="msgArea" class="msgArea"></div> <div class="bd">  <input type="button" id="btnSendMsg" value="发送"><input type="text" size="13"     id="txtMsg">  </div> </div> </div> </div> </body>

    设计好以上的主页面后,得到如图2-23所示的图,当然,样式部分不是重点,所以忽略,我们需要了解这个画板中和程序相关的部分。

    好了,定义好图2-23所示的UI界面后,开始实现功能。

    https://yqfile.alicdn.com/64544f968884da39f1f6c0a2473b636843de9b86.png" >

    2.7.2 定义画板对象首先,我们把整个画板看成一个画板对象,这个画板对象中有一些基本的属性,如画板的大小,当前画笔使用的颜色、宽度等,于是就有了以下的Paint类:

    var Painter = {   //绑定的环境上下文   ctx:null,   //宽度   w:0,   //高度   h:0,     //当前画笔颜色   bColor:null,   //当前画笔大小   bWidth:null }

    画板定义好了以后,我们需要逐渐往里面增加相应的功能代码。

    2.7.3 初始化画笔选项区接下来,我们需要动态产生颜色区域以及画笔区域,因为在前面设计UI的时候,只给这两个区域留下了空白容器,需要我们通过代码来产生。于是,可以给Painter定义一个初始化画笔的方法,具体的代码如下:

    //初始化画笔 initBrush:function()   {   //定义画板颜色   var bColor = ["#000000", "#999999", "#FFFFFF", "#FF0000", "#FF9900", "#FFFF00",   "#008000", "#00CCFF", "#0099FF", "#FF33CC", "#CC66FF", "#FFCCCC", "#6633FF", "#CCFFCC"];   var bDiv = $("#ys"),     self = this;  //产生颜色层  for(var i=0;i<bColor.length;i++)  {    var b = $("<div class='bys'></div>").css("background-color", bColor[i]);     //修改颜色     b.on("click", function(){     //触发更新画板状态事件     self.fire("onPaintUpdate", {"color":$(this).css("background-color")});    });    bDiv.append(b);  }  //绑定画笔大小  var bWidth = [2, 8, 16, 24];  var bcDiv = $("#bc");  for(i = 0;i<bWidth.length;i++)  {    var bw = $("<div class='bwid' data-bidx='"+(i)+"'></div>");    bw.css("background-image", "url(images/bc"+(i+1)+".png)");      //修改画笔大小    bw.on("click", function(){     //触发更新画板状态事件     self.fire("onPaintUpdate", {"width":bWidth[this.getAttribute("data-bidx")]});    });    bcDiv.append(bw);  } }

    以上函数中,我们创建了颜色区域和画笔区域中的选择层部分,当用户鼠标点击颜色选择区域的时候,就会使用fire方法触发一个onPaintUpdate事件,修改当前用户画笔的颜色。同样,当用户鼠标点击画笔选择区域时,也会触发onPaintUpdate事件,修改当前用户画笔的大小。fire方法是Paint类中自定义的一个方法,用于处理画板中的各种自定义事件的触发,具体实现如下:

    //触发画板事件 fire:function(eventName, param) {  if(this[eventName])  {    this[eventName](param);  }    }

    本质上,fire方法就是调用相应的事件处理程序,相应的,我们需要添加一个onPatintUpdate方法,相关代码如下:

    //画板更新事件,当画板的参数比如画笔颜色,大小改变时触发 onPaintUpdate:function(data) {  var w = data.width||this.bWidth,     c = data.color||this.bColor;  var param = {"width":w, "color":c};  //设置画笔大小  this.setBrushWidth(w);  //设置画笔颜色  this.setBrushColor(c);       }

    在这个更新画笔的方法中,需要根据传入的参数调用setBrushWidth()和setBrushColor()方法更新当前画笔的色彩和大小,这两个方法的具体代码如下:

    //设置画笔颜色 setBrushColor:function(color) {  this.bColor = color||"black";  this.ctx.strokeStyle = this.bColor;    }, //设置画笔宽度 setBrushWidth:function(width) {  this.bWidth = width||1;  this.ctx.lineWidth = this.bWidth; }

    到此为止,关于画笔颜色和大小的选择已经完成,接下来,我们看看如何实现用户在画板上用画笔绘画。**2.7.4 实现画板绘制**使用画笔在画板上绘制,从原理上来说,canvas上是可以画图形的,所以我们只要在鼠标的移动事件上面做文章就可以了。鼠标在canvas上面移动的时候,可以跟踪当前鼠标的坐标,然后在这些坐标点上绘制点就可以了,这是我们最初的想法,但实际上这种效果并不理想。因为,首先,canvas并没有提供画点的方法,当然,可以通过绘制一个极小的矩形实现,但这不是主要问题。主要问题在于,虽然我们移动鼠标是连续移动,但电脑没有足够高的灵敏度捕捉鼠标的移动轨迹。事实上,如果采用这种方法,将会在canvas上留下断断续续的点的轨迹,这不是我们要的结果,我们需要的是把这些点用线连起来。所以最终的算法是,捕捉鼠标移动的坐标,然后把当前点和上一个点用线连接起来,最终,就可以达到一个比较好的效果。

    根据以上的原理部分,我们在Paint类中增加一个initCanvas()方法,用于初始化画板,绑定鼠标在canvas上面的相关事件,其实现代码如下:

    //初始化画板 initCanvas:function() {  //绑定绘图canvas  var can = $("#paintArea"), self = this;  //绑定鼠标按下时间  can.on("mousedown", function(e){        e.preventDefault();    this.x = e.offsetX,    this.y = e.offsetY;    self.fire("onStartDraw", {"x":this.x, "y":this.y});    //绑定鼠标移动事件    can.on("mousemove", function(e){     var nx = event.offsetX, ny = event.offsetY;     self.fire("onDrawing", {"x":nx, "y":ny});     this.x = nx;     this.y = ny;    });    //绑定鼠标抬起事件    can.on("mouseup", function(){     //取消鼠标移动事件     can.off("mousemove");    });  }) }

    这里,我们在canvas中绑定了鼠标按下、移动和抬起事件,在按下和移动的时候,触发了onStartDraw和onDrawing方法,这两个方法主要用于绘制线条,代码如下:

    //开始画画事件 onStartDraw:function(data){   //开始路径   this.ctx.beginPath();   this.ctx.moveTo(data.x, data.y);    }, //画画事件 onDrawing:function(data) {   this.ctx.lineTo(data.x, data.y);   this.ctx.stroke();    }

    主要绘制部分的工作,基本都完成了。噢,别忘了,还有一个擦除区域。当我们绘制错误的时候,需要擦除图像,于是,我们可以完成初始化擦除区域的方法如下:

    //初始化橡皮擦 initEraser:function() {  var self = this;  //绑定清除屏幕事件  $("#btnClear").click(function(){   self.clear();  });  //擦除  $("#btnRub").click(function(){   self.setBrushColor("white");   self.setBrushWidth(32);  }); }

    好了,大功告成,为了让Paint类开始工作,我们最好写一个init()方法,完成以上全部的初始化工作,以便让用户方便使用,init()方法可参考完整代码部分。

    2.7.5 整合代码好了,整个Painter对象就完成了,最终,我们得到的Paint对象的完整代码如下:

    (function(){   //定义画板对象   var Painter = {    //绑定的环境上下文    ctx:null,    //宽度    w:0,    //高度    h:0,      //当前画笔颜色    bColor:null,    //当前画笔大小    bWidth:null,    //初始化   init:function()   {    var can = $("#paintArea")[0];    this.ctx = can.getContext("2d");    this.w = can.width;    this.h = can.height;    this.setBGColor();    this.setBrushColor();    this.setBrushWidth();    this.ctx.lineCap = "round";    this.ctx.lineJoin = "round";    //初始化画板事件    this.initCanvas();    //初始化画笔颜色    this.initBrush();    //初始化橡皮擦    this.initEraser();   },   //初始化画笔   initBrush:function()   {    //定义画板颜色    var bColor = ["#000000", "#999999", "#FFFFFF", "#FF0000", "#FF9900", "#FFFF00",              "#008000", "#00CCFF", "#0099FF", "#FF33CC", "#CC66FF", "#FFCCCC",              "#6633FF", "#CCFFCC"];    var bDiv = $("#ys"),       self = this;    //产生颜色层    for(var i=0;i<bColor.length;i++)    {      var b = $("<div class='bys'></div>").css("background-color", bColor[i]);      //修改颜色      b.on("click", function(){       //触发更新画板状态事件       self.fire("onPaintUpdate", {"color":$(this).css("background-color")});      });     bDiv.append(b);    }    //绑定画笔大小    var bWidth = [2, 8, 16, 24];    var bcDiv = $("#bc");    for(i = 0;i<bWidth.length;i++)    {      var bw = $("<div class='bwid' data-bidx='"+(i)+"'></div>");      bw.css("background-image", "url(images/bc"+(i+1)+".png)");        //修改画笔大小      bw.on("click", function(){       //触发更新画板状态事件       self.fire("onPaintUpdate", {"width":bWidth[this.getAttribute("data-bidx")]});      });      bcDiv.append(bw);    }   },   //初始化橡皮擦   initEraser:function()   {    var self = this;    //绑定清除屏幕事件    $("#btnClear").click(function(){     self.clear();    });    //擦除    $("#btnRub").click(function(){     self.setBrushColor("white");     self.setBrushWidth(32);    });   },   //设置背景颜色   setBGColor:function(color){    this.ctx.fillStyle = color||"white";    this.ctx.fillRect(0, 0, this.w, this.h);   },   //设置画笔颜色   setBrushColor:function(color)   {    this.bColor = color||"black";    this.ctx.strokeStyle = this.bColor;      },   //设置画笔宽度   setBrushWidth:function(width)   {    this.bWidth = width||1;    this.ctx.lineWidth = this.bWidth;   },   //初始化画板   initCanvas:function()   {    //绑定绘图canvas    var can = $("#paintArea"), self = this;    //绑定鼠标按下时间    can.on("mousedown", function(e){         e.preventDefault();     this.x = e.offsetX,     this.y = e.offsetY;     self.fire("onStartDraw", {"x":this.x, "y":this.y});     //绑定鼠标移动事件     can.on("mousemove", function(e){      var nx = event.offsetX, ny = event.offsetY;      self.fire("onDrawing", {"x":nx, "y":ny});      this.x = nx;      this.y = ny;     });     //绑定鼠标抬起事件     can.on("mouseup", function(){     //取消鼠标移动事件     can.off("mousemove");     });    })   },   //清除canvas   clear:function()   {    this.ctx.clearRect(0, 0, this.w, this.h);   },   //触发画板事件   fire:function(eventName, param)   {    if(this[eventName])    {     this[eventName](param);    }      },   //开始画画事件   onStartDraw:function(data){    //开始路径    this.ctx.beginPath();    this.ctx.moveTo(data.x, data.y);      },   //画画事件   onDrawing:function(data)   {    this.ctx.lineTo(data.x, data.y);    this.ctx.stroke();      },   //画板更新事件,当画板的参数比如画笔颜色,大小改变时触发   onPaintUpdate:function(data)   {    var w = data.width||this.bWidth, c = data.color||this.bColor;    var param = {"width":w,"color":c};    //设置画笔大小    this.setBrushWidth(w);    //设置画笔颜色    this.setBrushColor(c);         }  }   //画板初始化  Painter.init();  window.Painter = Painter; }())

    以上的Paint类在painter.js文件中,最后,完整的主页面代码index.htm如下:

    <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> <title>你画我猜</title> <link href="images/CSS.css" rel="stylesheet" type="text/css"> </head> <body> <!--主容器层--> <div id="main" class="main"> <div>  <div class="tx"></div>  <div class="wz">   <div id="dround"></div>   <!--显示问题区域-->   <span id="question"></span>  </div>  <!--显示时间-->  <div id="qTime" class="time">60</div>  <div class="tc">离开房间<img src="images/edit_undo.png" align="absmiddle"></div> </div> <div class="qc"></div> <div> <!--绘图主层-->  <div class="hbbox">  <!--绘图区域-->   <div id="hb" class="hb">    <canvas id="paintArea" width="525" height="370" ></canvas>   </div>  </div> <!--工具选项区--> <div class="hbr"> <div id="operDiv">   <!--颜色选取区-->    <div id="ys" class="ys"></div>    <!--画笔大小选择区-->    <div id="bc" class="bc"></div>    <!--定义橡皮擦工具-->    <div id="ssx" class="tb">     <div id="btnRub" class="tb2"><img src="images/01945.png"></div>     <div id="btnClear" class="tb3"><img src="images/053753321.gif"></div>    </div> </div> <!--消息发布主层--> <div id="msgArea" class="msgArea"></div> <div class="bd">  <input type="button" id="btnSendMsg" value="发送"><input type="text" size="13"     id="txtMsg">  </div> </div> </div> </div> </body> <script src="../js/jquery.js" charset="utf-8"></script> <script src="../js/painter.js" charset="utf-8"></script> <script> //检测是否支持HTML5 function isSupportHTML5() {  return (typeof(Worker) !== "undefined"); } //初始化 $(document).ready(function(){  if(!isSupportHTML5())  {   alert("您的浏览器不支持HTML5,请更换浏览器!");  }  else  {   $("#main").show();   }  }); </script> </html>

    至此,《你画我猜》的绘制部分已经完成,至于多人游戏的部分,在学习了后面的网络编程部分后,我们会得到一个完整的案例。

    相关资源:网络应用编程大作业--《你画我猜》.zip
    最新回复(0)