zoukankan      html  css  js  c++  java
  • 模仿开发H5游戏,看你有多色

    开发记录

    前言

    之前跟着慕课网学习开发H5小游戏开心鱼,勾起我的兴趣。
    在写代码的过程中,不怎么会遇到问题。虽然代码是亲手敲出来的,但是由于并没有对游戏的整体思路,所以并不知道开发与优化的过程。
    为了巩固知识,和了解优化的过程。我用了半天时间写出《看你有多色》游戏,(在网上找了一张游戏截图,之后自己写)
    由于是第一次自己构思、自己动手的游戏,所以还有很多地方有待完善。

    基本界面的实现的过程

    就像切图一样,在我找到截图的时候,先利用HTML搭出框架,再用CSS填充样式。
    随后是加入JS去实现不同的逻辑与活动。

    HTML

    我把控制输出信息(得分、倒计时、按钮)与画板(Canvas)分开,这样方便用JS控制。

    <div class="message">
        <div class="score" id="score">
            Score:0
        </div>
        <div class="timer">
            <strong id="timer">
                &nbsp;60&nbsp;
            </strong>
        </div>
        <div class="stop">
            <button>暂停</button>
        </div>
    </div>
            
    <div class="colorGame">
        <canvas id="canvas" width="500" height="500"></canvas>
    </div>
    

    CSS

    CSS的实现没什么难度,只是在写的时候发现一个问题:
    如果我想把倒计时写成div,并且用display:inline-block;控制其显示为有长宽的矩形时,文字好像脱离了div。原因暂时还没想到,目前的解决方式是改变显示方式。

    *{padding: 0;margin: 0;}
    .gameView{margin: 0 auto;width: 500px;height: 100px;position: relative;}
    .message{margin: 0 auto;width: 500px;height: 100px;background: #D1FFA2;
        margin-top: 20px;}
    .score,.timer,.stop{width: 160px;height: 100px;display: inline-block;
        text-align: center;font: 32px/100px "Tahoma";margin-right: -8px;}
    .score{background: #00CF95;}
    .timer{width: 180px;background: #0098EF;}
    .timer strong{color: #fff;margin: 0px auto;background: red;border-radius: 10px;
        font-weight: normal;}
    .stop{background: #6D0AD3;}
    .stop button{display: inline-block;height: 50px;width: 70px;border: none;
        border-radius: 10px;background: red;color: #fff;
        font: 24px/50px "微软雅黑";box-shadow: 5px 3px #ccc;}
    
    .colorGame{margin: 0 auto;width: 500px;height: 500px;background: #D1FFA2;}
    

    JS

    JS的实现比较复杂,我的思路是先通过Canvas画出游戏刚开始的界面。
    这里用到了Canvas绘图的函数。我封装了一个绘制圆角矩形的函数,drawRoundRect(),参数依次是绘图环境名称、圆角矩形的XY坐标、圆角矩形的半径、填充色。

    function drawRoundRect(cxt,x,y,r,radius,fillColor){
        cxt.save();
        cxt.translate(x,y);
        
        cxt.beginPath();
        cxt.arc(r-radius,r-radius,radius,0,Math.PI/2);
        cxt.lineTo(radius,r);
        cxt.arc(radius,r-radius,radius,Math.PI/2,Math.PI);
        cxt.lineTo(0,radius);
        cxt.arc(radius,radius,radius,Math.PI,Math.PI*3/2);
        cxt.lineTo(r-radius,0);
        cxt.arc(r-radius,radius,radius,Math.PI*3/2,Math.PI*2);
        cxt.lineTo(r,r-radius);
        cxt.closePath();
        
        cxt.fillStyle = fillColor?fillColor:"black";
        cxt.fill();
        cxt.restore();
    }
    

    然后绘制游戏界面,drawGame()。
    由于不同等级下的界面并不相同,所以我需要提前设置变量:level[ ](游戏等级分布)、
    spacing[ ](不同等级下的方块间距)、squareW[ ](不同等级下的方块边长)。
    又因为绘制时需要的参数是XY轴坐标,因为可以通过这三个变量算出来,所以我又封装了一个函数rectXY(),传入的参数是方块的位置值。

    function rectXY(a){
        return (a+1)*spacing[levelNum]+a*squareW[levelNum];
    }
    
    function init(){
        /*MaxLevel : 136*/
        level = [
            2,2,
            3,3,3,
            4,4,4,4,
            5,5,5,5,5,5,5,5,5,5,
            6,6,6,6,6,6,6,6,6,6,6,6,
            7,7,7,7,7,7,7,7,7,7,7,7,7,7,
            8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
            9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
            10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
            10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10];
        for (var i = 0; i < level.length; i++) {
            spacing[i] = level[i]*(-2)+24;
        }
        for (var i = 0; i < level.length; i++) {
            squareW[i] = Math.floor((w-(level[i]+1)*(level[i]*(-2)+24))/level[i]);
        }   
    }
    
    function drawGame(levelNum){
        //画背景
        drawRoundRect(context,0,0,500,8,"#D1FFA2");
        //画方块
        for (var i = 0; i < level[levelNum]; i++){
            for (var j = 0; j < level[levelNum]; j++){
                drawRoundRect(context,rectXY(i),rectXY(j),squareW[levelNum],6,formerColor);
            }
        }
        drawRoundRect(context,rectXY(differentX),rectXY(differentY),squareW[levelNum],6,differentColor);
        canvas.addEventListener('click',detect);
    }
    

    这样基本的界面就已经画好了。

    交互逻辑的实现

    这个游戏的交互活动很简单,仅仅是需要添加点击的监听事件。
    难点在于,Canvas是DOM对象,监听时获取到的结果是对整个Canvas的点击效果(Canvas内部的图样是没有办法独立的被监听)。
    解决的逻辑是:先获取鼠标的坐标值,然后用isPointInPath()判断当前坐标是否在当前绘制路径内部。

    • canvas对象有一个方法getBoundClientRect(),返回的是canvas元素的边界框对象, 此对象的left与top属性即为canvas左上角点相对窗口的坐标,将事件坐标与canvas坐标相减,得到相对于canvas的坐标。

    var x = event.clientX - canvas.getBoundingClientRect().left;
    var y = event.clientY - canvas.getBoundingClientRect().top;
    
    • 获取后再进行判断是否在图形内。

    if(context.isPointInPath(x,y)){}
    
    • 由于绘制差异方块时还需要注意颜色相近,所以我设置了一个数组,用于计算RGB模式下的颜色差值,差值越来越小是因为关卡难度上升导致颜色更加相近。

    badNum = [38,28,18,12,10,8,6,4,3,2];
    

    具体的实现方法,包含之前的监听与判断,还有随机色与之相近色的获取和统计分数并更新。

    function scoreNum(){
        var scoreObj = document.getElementById("score");
        score++;
        scoreObj.innerHTML = "Score:"+score;
    }
    
     function detect(event){
        var x = event.clientX - canvas.getBoundingClientRect().left;
        var y = event.clientY - canvas.getBoundingClientRect().top;
    
        drawRoundRect(context,rectXY(differentX),rectXY(differentY),squareW[levelNum],6,differentColor);
    
        if(context.isPointInPath(x,y)){
            levelNum++;
            differentX = Math.floor(Math.random()*level[levelNum]);
            differentY = Math.floor(Math.random()*level[levelNum]);
            R = Math.floor(Math.random()*255);
            G = Math.floor(Math.random()*255);
            B = Math.floor(Math.random()*255);
            formerColor = "rgb("+R+","+G+","+B+")";
            nR = R+badNum[0];
            nG = G+badNum[0];
            nB = B+badNum[0];
            differentColor = "rgb("+nR+","+nG+","+nB+")";
            context.clearRect(0,0,500,500);
            drawGame(levelNum);
            scoreNum();
        }
    }
    

    汇总

    • 总的来说这个游戏并不复杂,稍微有一些不一样的地方就是对Canvas添加监听事件时,与对DOM添加事件相比更麻烦一点。

    • 暂时来说没有卡住的地方,但是存在一个很大的问题:这个程序并没有体现面向对象的思想,二次浏览的时候会感到逻辑混乱。

    • 还有一些小功能并没有添加进去:
      1.没有暂停功能。
      2.没有GameOver后的提示功能。

  • 相关阅读:
    一款非常好用的范围滑动插件
    设置滚动条样式
    Qml 定义 constant
    qml 中 使用 shader
    Qt ImageProvider 的使用
    qt 汉化 国际化
    qt rcc 使用
    CentOS7/RHEL7 pacemaker+corosync高可用集群搭建
    Ubunt平台Qt出现:-1: error: cannot find -lgl
    排序-堆排序
  • 原文地址:https://www.cnblogs.com/BigTail/p/5827890.html
Copyright © 2011-2022 走看看