• (附源码)HTML+JavaScript+Canvas编写2D小游戏


    Canvas简介 

            Canvas是HTML5中的标签,用以生成图像。这里的生成图像有点类似于我们自己在纸面上绘画,需要指定渲染位置以及大小等参数,同时,这也使得一旦元素被绘制出来,便再也无法编辑,只能擦除后重新绘制。

    Canvas支持的浏览器

            除IE8及更早版本外,其余浏览器如IE9、Edge、Chrome、FireFox、 Safari等支持Canvas。

    预期结果

             其中,(1)为游戏新加载时显示的图像,有两个大小为80px*100px的矩形,一个半径为15px的红色小球;(2)当鼠标左键被按下时,木棍增长,最高不超过Canvas页面外;(3)松开鼠标左键,木棍倒下,如果木棍顶端正好位于另外一个矩形顶部范围内,游戏继续,清除页面内容,开始生成下一个矩形;(4)如果木棍顶端没有到达或超出矩形顶部范围,游戏结束,弹出“重新开始”按钮。

    项目结构 

     

            包含HTML主页面stickGrow.html、css样式文件stick.css和JavaScript文件stick.js。 

    HTML主页面:stickGrow.html

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <title>Horizon Projecttitle>
    6. <link rel="stylesheet" href="./stick.css">
    7. head>
    8. <body>
    9. <div class="con">
    10. <div class="score">0div>
    11. <canvas width="400px" height="600px" id="cvs">canvas>
    12. <button class="restart">重新开始button>
    13. div>
    14. body>
    15. html>
    16. <script src="./stick.js">script>

            Canvas使用标签创建,并直接向其赋值:宽width和高height。创建用来显示得分的div--”score“,其内部赋初始值0。最后是“重新开始”按钮。运行后得到如下结果:

            按照正常使用习惯,我们接下来要将得分和按钮移动进Canvas内,且暂时隐藏“重新开始”按钮,因此需要使用css来完成。

     css样式文件:stick.css

    1. body{
    2. margin: 0;
    3. padding: 0;
    4. background-color: black;
    5. }
    6. .con{
    7. width: 400px;
    8. height: 660px;
    9. margin-top: 50px;
    10. margin-left: auto;
    11. margin-right: auto;
    12. }
    13. .score{
    14. width: 180px;
    15. height: 60px;
    16. text-align: center;
    17. margin-left: auto;
    18. margin-right: auto;
    19. color: #eb4b16;
    20. font-size: 60px;
    21. position: relative;
    22. top: 120px;
    23. text-shadow: 5px 5px 10px #b18253;
    24. }

             首先给Canvas的父级div--“con”赋宽高值。此处使用position: relative;移动了分数的位置,但是HTML还是认为score在Canvas上方占据60px。因此con的高 = Canvas的高 + score的高,为660px。同时将margin-left和margin-right设为auto使其始终位于页面中央。

    1. .restart{
    2. color: #fef7ec;
    3. border: 0;
    4. border-radius: 15px;
    5. background-color: #b16145;
    6. font-size: 25px;
    7. width: 120px;
    8. height: 40px;
    9. position: relative;
    10. top: -300px;
    11. left: 140px;
    12. display: none;
    13. box-shadow: 3px 3px 10px #865746;
    14. }
    15. .restart:hover{
    16. background-color: #8a4831;
    17. cursor: pointer;
    18. }
    19. canvas{
    20. background-color: #fef7ec;
    21. border-radius: 15px;
    22. }

             这里设置“重新开始”按钮display: none;,只有在JavaScript判定游戏结束时才会使它重现。运行后我们可以看到如下结果:

             JavaScript文件stick.js

    1. var cvs = document.getElementById("cvs");
    2. var ctx = cvs.getContext("2d");
    3. var sc = document.getElementsByClassName("score");
    4. var btns = document.getElementsByTagName("button");
    5. var scores = 0;

            首先取出Canvas,Canvas只是一张画布,没有提供绘图能力和相关函数。为了使用JavaScript进行绘制,我们需要对Canvas进行getContext("2d");操作得到ctx对象,这样ctx对象就可以调用getContext()里的属性和方法了。接下来取出得分和按钮,注意他们的取出方法都是getElements,因此即使只有一个元素返回的也是一个列表,我们在使用时需要添加索引如sc[0]、btns[0]。最后定义全局变量scores,scores运算结束后可把值赋给页面上的得分div框中。

    1. function init(){
    2. ctx.fillStyle = "rgb(190, 23, 47)";
    3. ctx.beginPath();
    4. ctx.arc(50, 485, 15, 0, Math.PI * 2, false);
    5. ctx.fill();
    6. ctx.fillStyle = "rgb(38, 54, 69)";
    7. ctx.shadowOffsetX = 5;
    8. ctx.shadowOffsetY = 5;
    9. ctx.shadowBlur = 4;
    10. ctx.shadowColor = "rgba(140, 140, 140, 0.6)"
    11. ctx.fillRect(0, 500, 80, 100); //x, y, wid, hei
    12. }

            定义函数init(),用来生成角色--“红色小球”,其中绘制圆弧函数ctx.arc()中传入参数分别为:(相对于屏幕左上角的原点)横坐标,纵坐标,圆弧的半径,起始角度,终止角度,顺逆时针(true逆时针,false顺时针,不过此处绘制的是正圆,因此无需考虑顺逆时针)。Math.PI表示π,即Math.PI * 2 = 2π = 360度。绘制的圆弧为空心,使用ctx.fill();填充内部。

            绘制角色所在的矩形块,为了使其更立体,我们为其赋上阴影,不过如果想要清除图形,要连带其X轴Y轴上的阴影一同清除,不然就会留下一条明显的阴影。矩形使用ctx.fillRect();函数绘制,传入参数分别为:(相对于屏幕左上角的原点)横坐标,纵坐标,矩形宽,矩形高。

            特别需要注意的一点是,在绘制前需要先指明它的填充颜色ctx.fillStyle();,这就像选择画笔一样,且可在后面修改,定义颜色后可开始绘制。同样的,指定阴影样式后绘制的图形都会带上阴影,在上面的代码中我不想要角色带阴影,所以将角色定义于阴影样式之前。

    1. window.onload = function(){
    2. init();
    3. }

            定义页面加载函数,在页面打开或刷新时调用init(),运行得到以下结果:

    1. function stageGen(){
    2. var x = 80 + 20 + Math.floor(Math.random()*10*22);
    3. ctx.fillStyle = "rgb(38, 54, 69)";
    4. ctx.fillRect(x, 500, 80, 100);
    5. return x;
    6. }

            接下来定义函数stageGen(),以生成新的目标方块,与小球所在方块大小一致,为80px宽、100px高。它在页面X轴的位置需要满足:不能与小球所在方块重叠或太近、不能超出Canvas显示范围。在这里,为了防止两方块距离过近,设置至少间距为20,因此它的X轴位置(左上角的点的位置)在80+20 到 400-80之间取随机数,最终返回其横坐标。

    1. function start(){
    2. init();
    3. res = stageGen();
    4. console.log(res);
    5. }

            新定义start()函数,将stageGen()与init()函数封装,并在控制台输出新生成的方块的X轴坐标。 

    1. window.onload = function(){
    2. start();
    3. }

            直接修改页面加载执行函数window.onload调用封装好的start()函数,运行可看到另一块矩形成功生成,可刷新页面,随着页面刷新,其位置也会发生变化,同时在控制台输出其X轴坐标(不是它相对于左边方块空出来的距离),如下图:

    1. function stickDraw(){
    2. ctx.shadowBlur = 0;
    3. ctx.shadowOffsetX = 0;
    4. ctx.shadowOffsetY = 0;
    5. stickY = 0;
    6. val = setInterval(function(){
    7. if (stickY >= 500 ) {
    8. clearInterval(val);
    9. }else{
    10. stickY++;
    11. ctx.clearRect(70, 500, 50, -500);
    12. ctx.fillStyle = "rgb(73, 36, 21)";
    13. ctx.fillRect(70, 500, 10, -stickY);
    14. }
    15. },10);
    16. }

             定义木棒增长函数stickDraw(),为了防止木棒在绘制中出现错误,我们将它的阴影完全去除,并定义一个全局变量stickY记录棒长。然后使用周期调用函数setInterval();实现每10毫秒让木棒长度自增1px,当木棒长度达到Canvas纵向最高点时,为500px,将木棒旋转也早已超过Canvas横向最大值,因此设定当stickY大于等于500px时停止setInterval()函数。特别的,在下一帧绘制前,我们要将上一帧绘制的木棒清除,所以使用ctx.clearRect();来清除,它传入的参数与ctx.fillRect()函数类似,分别为:(相对于屏幕左上角的原点)横坐标,纵坐标,要清除的矩形宽,要清除的矩形高。这里直接清除了木棒上方全部的内容。

    1. function handleMouseDown() {
    2. stickDraw();
    3. console.log('down' + stickY);
    4. }
    5. cvs.addEventListener('mousedown', handleMouseDown);

            定义函数handleMouseDown(),再为Canvas页面绑定鼠标按下事件,因此当鼠标按下时,木棒会向上增长,同时控制台输出down + 木棒起始长度。此时运行,木棒会持续向上增长,如下:

    1. function rotateBlock(){
    2. ctx.translate(80, 500);
    3. ctx.clearRect(0, 0, -10, -500);
    4. ctx.save();
    5. ctx.rotate(90 * Math.PI / 180);
    6. ctx.fillStyle = "rgb(73, 36, 21)";
    7. ctx.fillRect(0, 0, -10, -stickY);
    8. ctx.restore();
    9. }

            木棒已经出现,现在要当松开鼠标时停止木棒增长并使其顺时针旋转90度。首先处理木棒旋转函数rotateBlock();,这里选择了木棒右下角(即小球所在方块右上角)为旋转原点,因此使用ctx.translate();将原点设为(80, 500),在这之后的坐标均以此为原点(0, 0)。使用ctx.save();保存当前木棒状态,调用旋转函数ctx.rotate();,此处使其顺时针旋转90度即π/2后重新填充木棒,并使用ctx.restore();来还原之前保存的状态。

    1. function handleMouseUp() {
    2. if (val){
    3. clearInterval(val);
    4. }
    5. rotateBlock();
    6. console.log('up'+stickY);
    7. //judgeOver();
    8. }
    9. cvs.addEventListener('mouseup', handleMouseUp);

             定义函数handleMouseUp(),setInterval()函数会有返回值,在前面我们将此返回值存入val全局变量中,当调用函数时,该周期调用函数会被关闭,以此达到木棒长度不再增加,同时调用rotateBlock()函数旋转木棒,并输出此时木棒的长度。向Canvas页面添加鼠标按键抬起触发器,当鼠标抬起,执行以上操作。运行程序,长按鼠标左键,数秒后松开,可得以下结果:

    1. function judgeOver(){
    2. if(stickY < (res-80) || stickY > res){
    3. console.log("游戏结束");
    4. ctx.translate(-80, -500);
    5. cvs.removeEventListener('mousedown', handleMouseDown);
    6. ctx.shadowOffsetX = 5;
    7. ctx.shadowOffsetY = 5;
    8. ctx.shadowBlur = 4;
    9. ctx.shadowColor = "rgba(140, 140, 140, 0.6)"
    10. ctx.fillStyle = "rgb(89, 109, 143)";
    11. ctx.font = '60px Verdana';
    12. ctx.textAlign = 'center';
    13. ctx.fillText("游戏结束", 200, 200);
    14. cvs.removeEventListener('mouseup', handleMouseUp);
    15. btns[0].style.display = "inline";
    16. }else{
    17. console.log("继续");
    18. scores++;
    19. sc[0].innerHTML = scores;
    20. stickY = 0;
    21. setTimeout(function(){
    22. ctx.translate(-80, -500);
    23. ctx.clearRect(0, 0, 400, 600);
    24. start();
    25. },500);
    26. }
    27. }

             最后我们要处理的是游戏是否结束,如果未结束,清除木棒并重新生成方块,分数+1;如果结束,则清除鼠标按下释放事件,弹出“游戏结束”提示和“重新开始”按钮。

            判断的条件是木棒长度小于两矩形间距(即res - 80px)或大于左边矩形右上角到右边矩形右上角的距离(即res本身的值)为游戏失败。此时清除鼠标的所有事件,防止游戏结束后的误操作,并使用ctx.fillText();绘制文字“游戏结束”,通过设置btns[0].style.display属性使得“重新开始”按钮显现。 

            如果木棒落在了矩形上方,游戏继续,令scores自增1后赋值给显示分数的div的innerHTML,分数可实时更新显示出来。再通过延时函数setTimeout();设置0.5秒后还原原点位置至左上角、清除屏幕上所有内容并调用start()函数绘制下一轮的图像。

    1. function handleMouseUp() {
    2. if (val){
    3. clearInterval(val);
    4. }
    5. rotateBlock();
    6. console.log('up'+stickY);
    7. judgeOver();//上面此行被注释掉了,删除注释即可
    8. }

            在handleMouseUp()中封装judgeOver()函数,此时程序也可正常运行了:

    1. btns[0].onclick = function(){
    2. location.reload();
    3. }

             “重新开始”按钮简单粗暴地刷新页面达到重新开始的效果。

    未解决的点

    •  全局无动画。最初设想给木棒旋转、小球移动和新方块生成添加动画,但是能力有限,最终没有完成动画的制作。
    •  少数情况下会有bug存在。在多次点击鼠标或其他极端情况下程序在方块生成、移除上有bug出现,目前未找到原因。

    源码 

            Hori03/HTML+JavaScript+Canvas编写2D小游戏 - 码云 - 开源中国 (gitee.com)

  • 相关阅读:
    算法---在区间范围内统计奇数数目
    8月外贸新规
    能带你起飞的【数据结构】成王第九篇:二叉树2
    openGauss学习笔记-104 openGauss 数据库管理-管理数据库安全-客户端接入之SSL证书管理-证书替换
    欧拉图和哈密顿图
    FPGA——三速自适应以太网设计(1)基本模块
    【物理应用】基于Matlab模拟RANS湍流
    Java练习题-输出二维数组对角线元素和
    机器学习(三):多项式回归
    make: /opt/rh/llvm-toolset-7/root/usr/bin/clang: Command not found
  • 原文地址:https://blog.csdn.net/qq_43059415/article/details/139697874