HTML5应用——欢乐老虎机

来源:互联网 发布:必达家淘宝是正品吗 编辑:程序博客网 时间:2024/04/28 21:31

                         在上面一篇博文中,我介绍了HTML5应用的简易播放器,这篇博文中介绍一个比较复杂的HTML5游戏-老虎机。 寒假在家玩老虎机输了些许钱,所以自己就萌生了写个老虎机的游戏。开始打算用Visual C++编写的,但是考虑到HTML5的<canvas>对象的简便性,就打算用HTML5编写了。 同时还用ASP语言编写了个服务器,如果游戏可以推广的话,自己可以做庄家,让大家玩哦! 但千万不要聚众赌博哦!

      

             在线试玩:http://101.225.71.60/game/game.asp,注意需要用支持HTML5的浏览器,最好是谷歌浏览器,请利用全屏模式体验最佳。(游戏存在一些bug,不好意思)

           言归正传,游戏的界面如下:




















           其实游戏本身应该是挺简单的,以下根据几个模块解说一下:

      一:用户界面

       游戏中有图片和<canvas>绘制的矩形、圆形。在开始编写前应该准备好游戏所需要的图片。我自己用PS简单画了几个,然后用VC写个小程序将图片进行反色、放缩处理就得到了全部图片:例如自己画了几个:

                                 

               其次关于界面押分、启动、投币、得分、积分等,首先需要<canvas>标签:

<canvas id="mycanvas" width="900" height="650" onclick="dealclick(con)" onmousemove="dealmove(con)" style="border:1px solid yellow;">Your brower does not support canvas of HTML5!</canvas>
*****创建显示线条框架的函数***************/function showFrame(con, x, y, space) {     con.beginPath();     con.lineWidth = 1;     con.moveTo(x, y-1); con.lineTo(x + space * 7, y-1);     con.moveTo(x, y + space); con.lineTo(x + space * 7, y + space);     con.moveTo(x, y + space * 6-1); con.lineTo(x + space * 7-1, y + space * 6);     con.moveTo(x, y + space * 7); con.lineTo(x + space * 7, y + space * 7);     con.moveTo(x, y); con.lineTo(x, y + space * 7);     con.moveTo(x+space , y); con.lineTo(x + space, y + space * 7);     con.moveTo(x + space * 6, y); con.lineTo(x + space * 6, y + space * 7);     con.moveTo(x + space * 7, y); con.lineTo(x + space * 7, y + space * 7);         con.closePath();     con.stroke();}/********************************************//****创建显示某个特定图片的函数*************/function showImage(i, flag,x,y,con) //1为西瓜,0为小西瓜,2为黑西瓜,3为黑小西瓜 {     var img = new Image();    switch (i)   {      case 1: if (flag == 1)              img.src = "苹果.png";          else              if (flag == 0)                  img.src = "小苹果.png";              else                  if (flag == 3)                      img.src = "黑苹果.bmp";                  else                      img.src = "黑小苹果.bmp";          break;      case 2:          if (flag == 1)              img.src = "橙子.png";          else              if (flag == 0)                  img.src = "小橙子.png";              else                  if (flag == 3)                      img.src = "黑橙子.bmp";                  else                      img.src = "黑小橙子.bmp";          break;      case 3:          if (flag == 1)              img.src = "芒果.png";          else              if (flag == 0)                  img.src = "小芒果.png";              else                  if (flag == 3)                      img.src = "黑芒果.bmp";                  else                      img.src = "黑小芒果.bmp";          break;      case 4:          if (flag == 1)              img.src = "铃铛.png";          else              if (flag == 0)                  img.src = "小铃铛.png";              else                  if (flag == 3)                      img.src = "黑铃铛.bmp";                  else                      img.src = "黑小铃铛.bmp";          break;      case 5:          if (flag == 1)              img.src = "西瓜.png";          else              if (flag == 0)                  img.src = "小西瓜.png";              else                  if (flag == 3)                      img.src = "黑西瓜.bmp";                  else                      img.src = "黑小西瓜.bmp";          break;      case 6:          if (flag == 1)              img.src = "双星.png";          else              if (flag == 0)                  img.src = "小双星.png";              else                  if (flag == 3)                      img.src = "黑双星.bmp";                  else                      img.src = "黑小双星.bmp";          break;      case 7:          if (flag == 1)              img.src = "双七.png";          else              if (flag == 0)                  img.src = "小双七.png";              else                  if (flag == 3)                      img.src = "黑双七.bmp";                  else                      img.src = "黑小双七.bmp";          break;      case 8:          if (flag == 1)              img.src = "大王.png";          else              if (flag == 0)                  img.src = "小王.png";              else                  if (flag == 3)                      img.src = "黑大王.bmp";                  else                      img.src = "黑小王.bmp";          break;      case 9:          if (flag == 1)              img.src = "机会.png";          else              img.src = "黑机会.bmp";      default: break;  }  img.onload = function () {      con.drawImage(img, x, y);  }}/********************************************************************//**************显示得分与积分***********************/function showScore(con, win_score, score) {    con.save();    con.font = "40px impact";    con.fillStyle = "red";    con.fillText("得分", 160, 35);    con.fillText("积分", 400, 35);    con.fillStyle = "#3DD369";    con.fillRect(160, 42, 74, 35);    con.fillRect(400, 42, 74, 35);    var str = win_score.toString();    con.font = "30 宋体";    con.fillStyle = "red";    con.fillText(str, 160, 70);    str = score.toString();    con.fillText(str, 400, 70);    con.restore();}/**************创建显示大小的函数**************/function showBigSmall(con)  //大小{    con.save();    con.beginPath();    var g = con.createRadialGradient(230, 320, 0, 230, 320, 20); //创建渐变颜色    g.addColorStop(0.2, "#C8EE2B"); //黄    g.addColorStop(0.8, "#BCC1AC"); //    con.fillStyle = g;    con.arc(230, 320, 20, 0, Math.PI * 2, true);    con.fill();    con.closePath();    con.restore();    con.save();    con.beginPath();    var g1 = con.createRadialGradient(370, 320, 0, 370, 320, 20); //创建渐变颜色    g1.addColorStop(0.2, "#C8EE2B"); //黄    g1.addColorStop(0.8, "#BCC1AC"); //    con.fillStyle = g1;    con.arc(370, 320, 20, 0, Math.PI * 2, true);    con.fillStyle = "";    con.fill();    con.closePath();    con.fillStyle = "black"; con.font = "30px 楷体";    con.fillText("大", 215, 330);    con.fillText("小", 355, 330);    con.restore();}/*******创建显示投币与退币的函数****************/function showGiveQuit(con) //投与退{    con.save();    con.beginPath();    var g = con.createRadialGradient(200, 260, 0, 200, 260, 15); //创建渐变颜色    g.addColorStop(0.2, "#C8EE2B"); //黄    g.addColorStop(0.8, "#BCC1AC"); //    con.fillStyle = g;    con.arc(200, 260, 15, 0, Math.PI * 2, true);    con.fill();    con.closePath();    con.restore();    con.save();    con.beginPath();    var g1 = con.createRadialGradient(400, 260, 0, 400, 260, 15); //创建渐变颜色    g1.addColorStop(0.2, "#C8EE2B"); //黄    g1.addColorStop(0.8, "#BCC1AC"); //    con.fillStyle = g1;    con.arc(400, 260, 15, 0, Math.PI * 2, true);    con.fill();    con.closePath();    con.restore();    con.save();    con.beginPath();    g1 = con.createRadialGradient(300, 235, 0, 300, 235, 15); //创建渐变颜色    g1.addColorStop(0.2, "#C8EE2B"); //黄    g1.addColorStop(0.8, "#BCC1AC"); //    con.fillStyle = g1;    con.arc(300, 235, 15, 0, Math.PI * 2, true);    con.fill();    con.closePath();    con.fillStyle = "black"; con.font = "30px 楷体";    con.fillText("退币", 170, 235); con.fillText("投币", 370, 235);con.fillText("启动", 270, 210);    con.restore();}/********创建显示界面的函数**************/function showSurface(con, x, y, space) {     // showFrame(con, x, y, space); //显示主框架     con.save();     showBeilv(con, y - 80, space);     con.restore();     con.save();     showImage(2, 1, x, y, con); showImage(4, 1, x + space, y, con); showImage(8, 0, x + 2 * space, y, con); showImage(8, 1, x + 3 * space, y, con);     showImage(1, 1, x + 4 * space, y,con); showImage(1, 0, x + 5 * space, y, con); showImage(3, 1, x + 6 * space, y, con);     showImage(4,0,x,y+space,con);showImage(1,1,x,y+2*space,con);showImage(6,0,x,y+4*space,con);showImage(6,1,x,y+5*space,con); showImage(3,1,x,y+6*space,con);     showImage(5, 1, x + 6 * space, y + space, con); showImage(5, 0, x + 6 * space, y + 2 * space, con); showImage(1, 1, x + 6 * space, y + 4 * space, con);     showImage(2, 0, x + 6 * space, y + 5 * space, con); showImage(2, 1, x + 6 * space, y + 6 * space, con);     showImage(3, 0, x + space, y + 6 * space, con); showImage(1, 1, x + 2 * space, y + 6 * space, con); showImage(7, 1, x + 3 * space, y + 6 * space, con);     showImage(7, 0, x + 4 * space, y + 6 * space, con); showImage(4, 1, x + 5 * space, y + 6 * space, con);     showImage(9, 1, x, y + 3 * space, con); showImage(9, 1, x + 6 * space, y + 3 * space, con);     con.restore();     con.save();     showFrame(con, x, y, space);     con.restore();     con.save();     showLight(con, 0);     con.restore();     con.save();     showGiveQuit(con);     con.restore();     init_param();    // gain_score = 200; score = 20;     con.save();     showScore(con, gain_score, score);     con.restore();     con.save();     showYafen(yafen, con, y, space);     con.restore();     con.save();     showBigSmall(con);     con.restore();}/*************************************************//***********创建显示说明和倍率的函数*******************/function showBeilv(con,y,space) {     con.save();     var str;     con.fillStyle = "black"; con.lineWidth = 3; con.font = "40px 汉仪丫丫体简";     str = "*100"; showImage(8, 1, 620, y, con);con.fillText(str, 700, y + 50);     str = "*40"; showImage(7, 1, 620, y + space, con);con.fillText(str, 700, y + 50 + space);     str = "*30"; showImage(6, 1, 620, y + 2 * space, con); con.fillText(str, 700, y + 50 + 2*space);     str = "*20"; showImage(5, 1, 620, y + 3 * space, con); con.fillText(str, 700, y + 50 + 3*space);     str = "*20"; showImage(4, 1, 620, y + 4 * space, con); con.fillText(str, 700, y + 50 + 4*space);     str = "*15"; showImage(3, 1, 620, y + 5 * space, con); con.fillText(str, 700, y + 50 + 5*space);     str = "*10"; showImage(2, 1, 620, y + 6 * space, con); con.fillText(str, 700, y + 50 + 6*space);     str = "*5"; showImage(1, 1, 620, y + 7 * space, con); con.fillText(str, 700, y + 50 + 7*space);     con.restore();}/*********************************************//******创建显示各个压分大小的函数**************/function showYafen(yafen,con,y,space) {    con.save();    con.fillStyle = "#3DD369";    for (var i = 0; i < 8; i++)        con.fillRect(780, y + i * space - 60, 60, 40);    con.fillStyle = "red"; con.font = "40px 楷体";    for (var j = 0; j < 8; j++) {        var str = yafen[7-j].toString();        con.fillText(str, 780, y + j * space - 28);    }    //其次创建8个按钮    con.restore();    con.save();    for (var m = 0; m < 8; m++)     {        con.beginPath();        var g = con.createRadialGradient(860, y+m*space-40, 0, 860, y+m*space-40, 15); //创建渐变颜色        g.addColorStop(0.2, "#C8EE2B"); //黄        g.addColorStop(0.8, "#BCC1AC"); //        con.fillStyle = g;        con.arc(860, y + m * space - 40, 15, 0, Math.PI * 2, true); con.fill(); con.closePath();    }    con.restore();}/***********************************************//***************显示指示灯的亮灭*************/function showLight(con, flag) //等{   con.save();   if (flag == 0) //等灭   {       var g = con.createRadialGradient(300, 50, 0, 300, 50, 25); //创建渐变颜色       g.addColorStop(0.2, "black"); //黄       g.addColorStop(0.8, "white"); //       con.fillStyle = g;       con.arc(300, 50, 25, 0, Math.PI * 2, true); con.fill();   }   else //等亮   {       var g1 = con.createRadialGradient(300, 50, 0, 300, 50, 25); //创建渐变颜色       g1.addColorStop(0.2, "red"); //黄       g1.addColorStop(0.8, "white"); //       con.fillStyle = g1;       con.arc(300, 50, 25, 0, Math.PI * 2, true); con.fill();   }   con.restore();}/*************************

   二:事件处理

    在<canvas>标签中加入了onclick="dealclick(con)"代码,这是整个事件处理。

    其实事件处理包括几个步骤: 根据坐标找出对应的事件(因为游戏中有押分、启动、投币等多个事件),然后再处理对应的事件。

       dealclick(con)函数如下:

    

function dealclick(con)     {        var posx = event.clientX;        var posy = event.clientY;        var flag = getPos(posx, posy); //获得对应的事件序号        //alert(posx.toString()+" "+posy.toString()+"    "+flag.toString());        if (flag == 0)   //表示不是兴趣区域            return;        if (flag >= 1 && flag <= 8) //对应的事件是压分        {            deal_yafen_event(flag,con);            return;        }        if (flag >= 9) //对应事件        {            switch (flag) //            {                case 9: deal_quit_event(con); break; //退币事件                case 10: deal_give_event(con); break;  //投币事件                case 11: begin_event(con); break; //启动事件                case 12: choseBig(); break;    //选大                case 13: choseSmall(); break; ;     //选小                case 14: LRevent(1); break;                case 15: LRevent(0); break;            }        }    }
      其中getPos事件是找出对应事件代号的

   

/****根据坐标寻找对应位置的函数************/function getPos(posx, posy) {    if (posx < 200 || posx > 1100 || posy < 20 || posy > 670)        return 0;  //返回零,不在canvas范围中    if (posx >= 1035 && posx <= 1075)   //表示在yafen的横坐标范围中    {        for (var i = 1; i <= 8; i++)  //1-8也分别是序号        {            if (posy > Y + (i - 1) * 80 - 35 && posy < Y + (i - 1) * 80 - 5)                return 9 - i;    //注意1-8分别对应苹果-大王的位置        }        return 0;    }    if (posx >= 385 && posx <= 615)  //在事件启动范围中    {        if (posx >= 385 && posx <= 415 && posy >= 265 && posy <= 295)            return 9; //退币对应事件9        if (posx >= 585 && posx <= 615 && posy >= 265 && posy <= 295)            return 10; //投币对应事件10        if (posx >= 485 && posx <= 515 && posy >= 240 && posy <= 270)            return 11; //启动对应事件11        if (posx >= 410 && posx <= 250+200 && posy >= 320 && posy <= 360)            return 12; //选大        if (posx >= 550 && posx <= 590 && posy >= 320 && posy <= 360)            return 13; //选小        if (posx >= 235+200 && posx <= 270+200 && posy >= 410 && posy <= 430)            return 14; //左移        if (posx >= 340 + 200 && posx <= 375 + 200 && posy >= 410 && posy <= 430)            return 15; //右移                 }    return 0;}
      关于对应的押分,选大、选小事件其实都是比较简单的, 可以简单说一下选大选小中关于控制概率的算法, 例如控制庄家赢的概率是70%;可以利用var num=Math.random(); if(num<0.7)就算庄家赢,return true. 这都是比较简单的。

      最复杂的其实是启动事件。 玩过老虎机的朋友都知道,灯的闪烁、以及闪烁有快慢。 js中只有setInterval() 函数处理定时问题,所以编写时比较复杂

     

/********************处理起动事件***************/    function begin_event(con) //启动事件    {        if (isOK == false)            return; //表示上一次的启动还没有完成        var g=0;        for (var e = 0; e < 8; e++)            g += yafen[e];        if (g == 0)            return;  //表示没有压分        if (gain_score != 0) //如果右边有钱先将钱转到右边        {            score += gain_score;            gain_score = 0;            showScore(con, gain_score, score);            return;        }        if (repeatYafen == true) //表示重复上次的压分不变        {            showYafen(yafen, con, Y, 80); //显示压分            var totalyafen=0;            for (var a = 0; a < 8; a++)                totalyafen += yafen[a];            if (totalyafen > score) //表示余额不足                return;            else                score -= totalyafen; //扣分;            showScore(con, gain_score, score);        }         for (n = 0; n < 6; n++)            time[n] = 0;        n = count = 0;         var id = getTheOne(0);  //获取本次中奖的号码        // var id = 17; //用于实验         var id2, id3;         isOK = false;//表示进入闪烁模式        if (id >= 0 && id <= 15)//没有中机会        {            id2 = getOnlyOne(id);  //得到在屏幕中的序号            aim_pos1 = id2;            light_flash(id,id2, 0, con);  //灯闪烁        }        if (id == 16)  //中了左机会        {            id2 = getTheOne(1); //再次获得序号            id3 = getOnlyOne(id2); //得到在屏幕中的序号            aim_pos1 = id3; //记录新的启示序号            light_flash(id, 0, 1, con);           // alert(aim_pos1.toString());        }        if (id == 17)  //中了右机会        {            id2 = getTheOne(1);  //在次获得序号            id3 = getOnlyOne(id2); //得到在屏幕中的位置            aim_pos1 = id3;            //alert(id3.toString());            light_flash(id, 12, 2, con);        }    }    /******************************************/
       主要介绍一下利用setInterval()介绍一下如何实现递增的延时,如老虎机灯快停时闪烁的越来越慢。可能我的方法不太好,大家可以参考一下

/*****************闪烁函数1*********************/    function flash1() //闪烁    {       var k=0;        if (n <count - 5) //这段时间内正常闪烁        {            if (n == 0)                showImageId(aim_pos, con, 0);            else {                 k = (aim_pos - 1 + n) % 24;                showImageId(k, con, 1);                k = (k + 1) % 24;                showImageId(k, con, 0);            }            showLight(con, n % 2); flashSound();            flashSound();            n++;        }        else  //延时闪烁        {            switch(time[0]) //位置            {                case 0: time[0]++; k = (aim_pos - 1 + n) % 24; showImageId(k, con, 1); k = (k + 1) % 24; showImageId(k, con, 0); flashSound(); showLight(con, n % 2); n++; break;             case 1: time[1]++;                     if(time[1]==2) //时间到                     {                         k = (aim_pos - 1 + n) % 24; showImageId(k, con, 1); k = (k + 1) % 24; showImageId(k, con, 0); showLight(con, n % 2); flashSound();                        time[0]++; n++;                     }break;             case 2:time[2]++;                    if(time[2]==3) //时间到                    {                        k = (aim_pos - 1 + n) % 24; showImageId(k, con, 1); k = (k + 1) % 24; showImageId(k, con, 0); showLight(con, n % 2); flashSound();                        time[0]++; n++;                    }break;             case 3:time[3]++;                    if(time[3]==4) //时间到                    {                        k = (aim_pos - 1 + n) % 24; showImageId(k, con, 1); k = (k + 1) % 24; showImageId(k, con, 0); showLight(con, n % 2); flashSound();                        time[0]++; n++;                    }break;             case 4:                    time[4]++;                    if(time[4]==5) //时间到                    {                        k = (aim_pos - 1 + n) % 24; showImageId(k, con, 1); k = (k + 1) % 24; showImageId(k, con, 0); showLight(con, n % 2); flashSound();                        time[0]++; n++;                    } break;                case 5: time[5]++;                    if (time[5] == 6) //时间到                    {                        k = (aim_pos - 1 + n) % 24; showImageId(k, con, 1); k = (k + 1) % 24; showImageId(k, con, 0); showLight(con, n % 2); flashSound();                        clearInterval(jsq); showLight(con, 1); flashSound();                        aim_pos = aim_pos1; //更新起始位置                        countGainScore(0, con, aim_pos1); repeatYafen = true; //设置默认是重复压分模式                        isOK = true;                    }            }        }    }

     三:背景音乐

        游戏中需要游戏背景音乐。 这需要用到html5中的<audio>标签,然后通过js来控制声音的播放与暂停

     在游戏界面中先载入:

   

 <audio src="sound\HP.wav" id="win"></audio><br />   <audio src="sound\pig.wav" id="lose"></audio><br /><audio src="sound\CLICK.WAV" id="flash"></audio>
   然后利用js来控制, 例如介绍一下选大选小时赢时候的背景音乐

 var win = document.getElementById("win");function winSound() //    {        win.currentTime = 0; //置于当前        win.play();    }
  然后在自己需要控制的地方加入播放函数即可。

     

  四:游戏bug

    由于HTML5还不够成熟,因此浏览器对其支持能力不够好。例如背景音乐,长期使用定时器播放后浏览器会失去对<audio>的支持能力。 由于游戏是在浏览器中运行的,所以效率相比vc++的程序比较低。 如何脚本过于复杂会直接崩溃。

  

   

   

    

         

原创粉丝点击