Cet article présente principalement l'utilisation de HTML5 Canvas pour créer un jeu de masturbation simple. L'auteur fournit également du code Javascript pertinent. Les amis qui en ont besoin peuvent s'y référer
Plus tôt dans la DEMO de Dang Knight, j'ai vu une masturbation. jeu et a retiré ses images et son audio. . . . Je l'ai réécrit juste pour m'amuser. Juste pour le divertissement. . . . . . Je n'utilise pas de framework, j'écris moi-même tous les js. . . . . . Il s'agit donc d'un didacticiel simple. Il peut être utile à ceux qui débutent sur Canvas. L'auteur ne joue pas à Canvas depuis longtemps et ses compétences ne sont pas très bonnes.
Sans plus tarder, commençons par la DEMO : Airplane Game. L'affiche originale a écrit ceci juste pour le plaisir, et n'avait pas l'intention de l'écrire dans un jeu sérieux.
Entrons dans le vif du sujet : Le fichier du jeu de masturbation comprend le fichier d'entrée index.html, le fichier de traitement logique des sprites allSprite.js, le fichier de traitement de chargement chargement.js et data.js (certaines données initialisées).
Tout d'abord, les jeux normaux nécessitent essentiellement un chargement. La page de chargement est utilisée pour précharger les données, y compris les images des feuilles de sprite, l'audio, etc. Comme il s'agit d'un petit jeu, les seules choses qui doivent être chargées. sont quelques audio et image. Les codes de chargement à l'intérieur sont principalement les suivants. Les autres servent à réaliser des animations de chargement. Celui-ci est relativement simple, donc je ne le publierai pas. Si vous êtes intéressé, regardez simplement la console dans la DÉMO :
Code XML/ HTMLCopiez le contenu dans le presse-papiers
loadImg:function(datas){ var _this = this; var dataIndex = 0; li(); function li(){ if(datas[dataIndex].indexOf("mp3")>=0){ var audio = document.createElement("audio"); document.body.appendChild(audio); audio.preload = "auto"; audio.src = datas[dataIndex]; audio.oncanplaythrough = function(){ this.oncanplaythrough = null; dataIndex++; if(dataIndex===datas.length){ _this.percent = 100; }else { _this.percent = parseInt(dataIndex/datas.length*100); li.call(_this); } } }else { preLoadImg(datas[dataIndex] , function(){ dataIndex++; if(dataIndex===datas.length){ _this.percent = 100; } else { _this.percent = parseInt(dataIndex/datas.length*100); li.call(_this); } }) } } }, //再贴出preLoadImg的方法 function preLoadImg(src , callback){ var img = new Image(); img.src = src; if(img.complete){ callback.call(img); }else { img.onload = function(){ callback.call(img); } } }
Code XML/HTMLCopiez le contenu dans le presse-papiers
W.Sprite = function(name , painter , behaviors , args){ if(name !== undefined) this.name = name; if(painter !== undefined) this.painter = painter; this.top = 0; this.left = 0; this.width = 0; this.height = 0; this.velocityX = 3; this.velocityY = 2; this.visible = true; this.animating = false; this.behaviors = behaviors; this.rotateAngle = 0; this.blood = 50; this.fullBlood = 50; if(name==="plan"){ this.rotateSpeed = 0.05; this.rotateLeft = false; this.rotateRight = false; this.fire = false; this.firePerFrame = 10; this.fireLevel = 1; }else if(name==="star"){ this.width = Math.random()*2; this.speed = 1*this.width/2; this.lightLength = 5; this.cacheCanvas = document.createElement("canvas"); thisthis.cacheCtx = this.cacheCanvas.getContext('2d'); thisthis.cacheCanvas.width = this.width+this.lightLength*2; thisthis.cacheCanvas.height = this.width+this.lightLength*2; this.painter.cache(this); }else if(name==="badPlan"){ this.badKind = 1; this.speed = 2; this.rotateAngle = Math.PI; }else if(name==="missle"){ this.width = missleWidth; }else if(name==="boom"){ this.width = boomWidth; }else if(name==="food"){ this.width = 40; this.speed = 3; this.kind = "LevelUP" } this.toLeft = false; this.toTop = false; this.toRight = false; this.toBottom = false; this.outArcRadius = Math.sqrt((this.width/2*this.width/2)*2); if(args){ for(var arg in args){ this[arg] = args[arg]; } } } Sprite.prototype = { constructor:Sprite, paint:function(){ if(this.name==="badPlan"){this.update();} if(this.painter !== undefined && this.visible){ if(this.name!=="badPlan") { this.update(); } if(this.name==="plan"||this.name==="missle"||this.name==="badPlan"){ ctx.save(); ctx.translate(this.left , this.top); ctx.rotate(this.rotateAngle); this.painter.paint(this); ctx.restore(); }else { this.painter.paint(this); } } }, update:function(time){ if(this.behaviors){ for(var i=0;i<this.behaviors.length;i++){ this.behaviors[i].execute(this,time); } } } }
Code XML/HTMLCopiez le contenu dans le Presse-papiers
var SpriteSheetPainter = function(cells){ this.cells = cells || []; this.cellIndex = 0; } SpriteSheetPainter.prototype = { advance:function(){ if(this.cellIndex === this.cells.length-1){ this.cellIndex = 0; } else this.cellIndex++; }, paint:function(sprite){ var cell = this.cells[this.cellIndex]; context.drawImage(spritesheet , cell.x , cell.y , cell.w , cell.h , sprite.left , sprite.top , cell.w , cell.h); } }
Code JavaScriptCopier le contenu dans le presse-papiers
(function(W){ "use strict" var planWidth = 24, planHeight = 24, missleWidth = 70, missleHeight = 70, boomWidth = 60; //精灵类 W.Sprite = function(name , painter , behaviors , args){ if(name !== undefined) this.name = name; if(painter !== undefined) this.painter = painter; this.top = 0; this.left = 0; this.width = 0; this.height = 0; this.velocityX = 3; this.velocityY = 2; this.visible = true; this.animating = false; this.behaviors = behaviors; this.rotateAngle = 0; this.blood = 50; this.fullBlood = 50; if(name==="plan"){ this.rotateSpeed = 0.05; this.rotateLeft = false; this.rotateRight = false; this.fire = false; this.firePerFrame = 10; this.fireLevel = 1; }else if(name==="star"){ this.width = Math.random()*2; this.speed = 1*this.width/2; this.lightLength = 5; this.cacheCanvas = document.createElement("canvas"); this.cacheCtx = this.cacheCanvas.getContext('2d'); this.cacheCanvas.width = this.width+this.lightLength*2; this.cacheCanvas.height = this.width+this.lightLength*2; this.painter.cache(this); }else if(name==="badPlan"){ this.badKind = 1; this.speed = 2; this.rotateAngle = Math.PI; }else if(name==="missle"){ this.width = missleWidth; }else if(name==="boom"){ this.width = boomWidth; }else if(name==="food"){ this.width = 40; this.speed = 3; this.kind = "LevelUP" } this.toLeft = false; this.toTop = false; this.toRight = false; this.toBottom = false; this.outArcRadius = Math.sqrt((this.width/2*this.width/2)*2); if(args){ for(var arg in args){ this[arg] = args[arg]; } } } Sprite.prototype = { constructor:Sprite, paint:function(){ if(this.name==="badPlan"){this.update();} if(this.painter !== undefined && this.visible){ if(this.name!=="badPlan") { this.update(); } if(this.name==="plan"||this.name==="missle"||this.name==="badPlan"){ ctx.save(); ctx.translate(this.left , this.top); ctx.rotate(this.rotateAngle); this.painter.paint(this); ctx.restore(); }else { this.painter.paint(this); } } }, update:function(time){ if(this.behaviors){ for(var i=0;i<this.behaviors.length;i++){ this.behaviors[i].execute(this,time); } } } } // 精灵表绘制器 W.SpriteSheetPainter = function(cells , isloop , endCallback , spritesheet){ this.cells = cells || []; this.cellIndex = 0; this.dateCount = null; this.isloop = isloop; this.endCallback = endCallback; this.spritesheet = spritesheet; } SpriteSheetPainter.prototype = { advance:function(){ this.cellIndex = this.isloop?(this.cellIndex===this.cells.length-1?0:this.cellIndex+1):(this.cellIndex+1); }, paint:function(sprite){ if(this.dateCount===null){ this.dateCount = new Date(); }else { var newd = new Date(); var tc = newd-this.dateCount; if(tc>40){ this.advance(); this.dateCount = newd; } } if(this.cellIndex<this.cells.length || this.isloop){ var cell = this.cells[this.cellIndex]; ctx.drawImage(this.spritesheet , cell.x , cell.y , cell.w , cell.h , sprite.left-sprite.width/2 , sprite.top-sprite.width/2 , cell.w , cell.h); } else if(this.endCallback){ this.endCallback.call(sprite); this.cellIndex = 0; } } } //特制飞机精灵表绘制器 W.controllSpriteSheetPainter = function(cells , spritesheet){ this.cells = cells || []; this.cellIndex = 0; this.dateCount = null; this.isActive = false; this.derection = true; this.spritesheet = spritesheet; } controllSpriteSheetPainter.prototype = { advance:function(){ if(this.isActive){ this.cellIndex++; if(this.cellIndex === this.cells.length){ this.cellIndex = 0; this.isActive = false; } } }, paint:function(sprite){ if(this.dateCount===null){ this.dateCount = new Date(); }else { var newd = new Date(); var tc = newd-this.dateCount; if(tc>sprite.firePerFrame){ this.advance(); this.dateCount = newd; } } var cell = this.cells[this.cellIndex]; ctx.drawImage(this.spritesheet , cell.x , cell.y , cell.w , cell.h , -planWidth/2 , -planHeight/2 , cell.w , cell.h); } } W.planBehavior = [ {execute:function(sprite,time){ if(sprite.toTop){ sprite.top = sprite.top<planHeight/2? sprite.top : sprite.top-sprite.velocityY; } if(sprite.toLeft){ sprite.left = sprite.left<planWidth/2? sprite.left : sprite.left-sprite.velocityX; } if(sprite.toRight){ sprite.left = sprite.left>canvas.width-planWidth/2? sprite.left : sprite.left+sprite.velocityX; } if(sprite.toBottom){ sprite.top = sprite.top>canvas.height-planHeight/2? sprite.top : sprite.top+sprite.velocityY; } if(sprite.rotateLeft){ sprite.rotateAngle -= sprite.rotateSpeed; } if(sprite.rotateRight){ sprite.rotateAngle += sprite.rotateSpeed; } if(sprite.fire&&!sprite.painter.isActive){ sprite.painter.isActive = true; this.shot(sprite); } }, shot:function(sprite){ this.addMissle(sprite , sprite.rotateAngle); var missleAngle = 0.1 for(var i=1;i<sprite.fireLevel;i++){ this.addMissle(sprite , sprite.rotateAngle-i*missleAngle); this.addMissle(sprite , sprite.rotateAngle+i*missleAngle); } var audio = document.getElementsByTagName("audio"); for(var i=0;i<audio.length;i++){ console.log(audio[i].paused) if(audio[i].src.indexOf("shot")>=0&&audio[i].paused){ audio[i].play(); break; } } }, addMissle:function(sprite , angle){ for(var j=0;j<missles.length;j++){ if(!missles[j].visible){ missles[j].left = sprite.left; missles[j].top = sprite.top; missles[j].rotateAngle = angle; var missleSpeed = 20; missles[j].velocityX = missleSpeed*Math.sin(-missles[j].rotateAngle); missles[j].velocityY = missleSpeed*Math.cos(-missles[j].rotateAngle); missles[j].visible = true; break; } } } } ] W.starBehavior = [ {execute:function(sprite,time){ if(sprite.top > canvas.height){ sprite.left = Math.random()*canvas.width; sprite.top = Math.random()*canvas.height - canvas.height; } sprite.top += sprite.speed; }} ] W.starPainter = { paint:function(sprite){ ctx.drawImage(sprite.cacheCanvas , sprite.left-sprite.width/2-sprite.lightLength , sprite.top-sprite.width/2-sprite.lightLength) }, cache:function(sprite){ sprite.cacheCtx.save(); var opacity = 0.5,addopa = 1/sprite.lightLength; sprite.cacheCtx.fillStyle = "rgba(255,255,255,0.8)"; sprite.cacheCtx.beginPath(); sprite.cacheCtx.arc(sprite.width/2+sprite.lightLength , sprite.width/2+sprite.lightLength , sprite.width/2 , 0 , 2*Math.PI); sprite.cacheCtx.fill(); for(var i=1;i<=sprite.lightLength;i+=2){ opacity-=addopa; sprite.cacheCtx.fillStyle = "rgba(255,255,255,"+opacity+")"; sprite.cacheCtx.beginPath(); sprite.cacheCtx.arc(sprite.width/2+sprite.lightLength , sprite.width/2+sprite.lightLength , sprite.width/2+i , 0 , 2*Math.PI); sprite.cacheCtx.fill(); } } } W.foodBehavior = [ {execute:function(sprite,time){ sprite.top += sprite.speed; if(sprite.top > canvas.height+sprite.width){ sprite.visible = false; } }} ] W.foodPainter = { paint:function(sprite){ ctx.fillStyle = "rgba("+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+",1)" ctx.font="15px 微软雅黑" ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(sprite.kind , sprite.left , sprite.top); } } W.missleBehavior = [{ execute:function(sprite,time){ sprite.left -= sprite.velocityX; sprite.top -= sprite.velocityY; if(sprite.left<-missleWidth/2||sprite.top<-missleHeight/2||sprite.left>canvas.width+missleWidth/2||sprite.top<-missleHeight/2){ sprite.visible = false; } } }]; W.misslePainter = { paint:function(sprite){ var img = new Image(); img.src="../planGame/image/plasma.png" ctx.drawImage(img , -missleWidth/2+1 , -missleHeight/2+1 , missleWidth , missleHeight); } } W.badPlanBehavior = [{ execute:function(sprite,time){ if(sprite.top > canvas.height || !sprite.visible){ var random = Math.random(); if(point>=200&&point<400){ sprite.fullBlood = 150; if(random<0.1){ sprite.badKind = 2; sprite.fullBlood = 250; } }else if(point>=400&&point<600){ sprite.fullBlood = 250; if(random<0.2){ sprite.badKind = 2; sprite.fullBlood = 400; } if(random<0.1){ sprite.badKind = 3; sprite.fullBlood = 600; } }else if(point>=600){ sprite.fullBlood = 500; if(random<0.4){ sprite.badKind = 2; sprite.fullBlood = 700; } if(random<0.2){ sprite.badKind = 3; sprite.fullBlood = 1000; } } sprite.visible = true; sprite.blood = sprite.fullBlood; sprite.left = Math.random()*(canvas.width-2*planWidth)+planWidth; sprite.top = Math.random()*canvas.height - canvas.height; } sprite.top += sprite.speed; }, shot:function(sprite){ this.addMissle(sprite , sprite.rotateAngle); var missleAngle = 0.1 for(var i=1;i<sprite.fireLevel;i++){ this.addMissle(sprite , sprite.rotateAngle-i*missleAngle); this.addMissle(sprite , sprite.rotateAngle+i*missleAngle); } }, addMissle:function(sprite , angle){ for(var j=0;j<missles.length;j++){ if(!missles[j].visible){ missles[j].left = sprite.left; missles[j].top = sprite.top; missles[j].rotateAngle = angle; var missleSpeed = 20; missles[j].velocityX = missleSpeed*Math.sin(-missles[j].rotateAngle); missles[j].velocityY = missleSpeed*Math.cos(-missles[j].rotateAngle); missles[j].visible = true; break; } } } }]; W.badPlanPainter = { paint:function(sprite){ var img = new Image(); img.src="../planGame/image/ship.png" switch(sprite.badKind){ case 1:ctx.drawImage(img , 96 , 0 , planWidth , planWidth , -planWidth/2 , -planHeight/2 , planWidth , planWidth); break; case 2:ctx.drawImage(img , 120 , 0 , planWidth , planWidth , -planWidth/2 , -planHeight/2 , planWidth , planWidth); break; case 3:ctx.drawImage(img , 144 , 0 , planWidth , planWidth , -planWidth/2 , -planHeight/2 , planWidth , planWidth); break; } ctx.strokeStyle = "#FFF"; ctx.fillStyle = "#F00"; var bloodHeight = 1; ctx.strokeRect(-planWidth/2-1 , planHeight+bloodHeight+3 , planWidth+2 , bloodHeight+2); ctx.fillRect(planWidth/2-planWidth*sprite.blood/sprite.fullBlood , planHeight+bloodHeight+3 , planWidth*sprite.blood/sprite.fullBlood , bloodHeight); } } W.planSize = function(){ return { w:planWidth, h:planHeight } } })(window);
所以,我们要通过keydown和keyup来实现飞机的运动,原理很容易理解:当我们按下往左的方向键时,我们给飞机一个往左的状态,也就是让飞机的toLeft属性为true,而在动画循环中,判断飞机的状态,如果toLeft为true则飞机的x值不停地减少,飞机也就会不停地往左移动,然后当我们抬起手指时触发keyup事件,我们就再keyup事件中解除飞机往左的状态。飞机也就停止往左移动了。其他状态也一样的原理,这样写的话,就能够让飞机多种状态于一生了。可以同时开枪同时到处跑了。
实现的代码如下:
XML/HTML Code复制内容到剪贴板
//keydown/keyup事件的绑定 window.onkeydown = function(event){ switch(event.keyCode){ case 88:myplan.fire = true; break; case 90:myplan.rotateLeft=true; break; case 67:myplan.rotateRight=true; break; case 37:myplan.toLeft = true; break; case 38:myplan.toTop = true; break; case 39:myplan.toRight = true; break; case 40:myplan.toBottom = true; break; } } window.onkeyup = function(event){ switch(event.keyCode){ case 88:myplan.fire = false; break; case 90:myplan.rotateLeft=false; break; case 67:myplan.rotateRight=false; break; case 37:myplan.toLeft = false; break; case 38:myplan.toTop = false; break; case 39:myplan.toRight = false; break; case 40:myplan.toBottom = false; break; } } //飞机每一帧的状态更新处理代码 execute:function(sprite,time){ if(sprite.toTop){ spritesprite.top = sprite.top<planHeight/2? sprite.top : sprite.top-sprite.velocityY; } if(sprite.toLeft){ spritesprite.left = sprite.left<planWidth/2? sprite.left : sprite.left-sprite.velocityX; } if(sprite.toRight){ spritesprite.left = sprite.left>canvas.width-planWidth/2? sprite.left : sprite.left+sprite.velocityX; } if(sprite.toBottom){ spritesprite.top = sprite.top>canvas.height-planHeight/2? sprite.top : sprite.top+sprite.velocityY; } if(sprite.rotateLeft){ sprite.rotateAngle -= sprite.rotateSpeed; } if(sprite.rotateRight){ sprite.rotateAngle += sprite.rotateSpeed; } if(sprite.fire&&!sprite.painter.isActive){ sprite.painter.isActive = true; this.shot(sprite); }
就是如此简单。
然后说下对象控制,打飞机游戏,会发射大量子弹,产生大量对象,包括爆炸啊,飞机啊,子弹等,如果不停地进行对象的生成和销毁,会让浏览器的负荷变得很大,运行了一段时间后就会卡出翔了。所以,我们要用可以循环利用的对象来解决这个问题,不进行对象的销毁,对所有对象进行保存,循环利用。
我的做法就是,在游戏初始化的时候,直接生成一定数量的对象,存放在数组里面。当我们需要一个对象的时候,就从里面取,当用完后,再放回数组里面。数组里的所有对象都有一个属性,visible,代表对象当前是否可用。
举个例子,当我的飞机发射一发炮弹,我需要一发炮弹,所以我就到炮弹数组里遍历,如果遍历到的炮弹visible为true,也就说明该对象正在使用着,不能拿来用,所以继续遍历,直到遍历到visible为false的炮弹对象,说明这个对象暂时没人用。然后就可以拿过来重新设置属性,投入使用了。当炮弹击中敌人或者打出画布外的时候,把炮弹的visible设成false,又成了一个没人用的炮弹在数组里存放起来等待下一次调用。
所以,我们要预算算好页面大概要用到多少个对象,然后就预先准备好对象,这样,在游戏进行中,不会有对象进行生成和销毁,对游戏性能方面就有了提升了。
最后再说下音频,游戏里面要用到多个同样的audio才能保证音效的不间断性:
复制代码
XML/HTML Code复制内容到剪贴板
var audio = document.getElementsByTagName("audio"); for(var i=0;i<audio.length;i++){ console.log(audio[i].paused) if(audio[i].src.indexOf("boom")>=0&&audio[i].paused){ audio[i].play(); break; } }
好吧,基本上就这样了。技术或许还不够好,纯碎做个记录,如果代码有不当正处,欢迎指出,共同学习。
相关推荐:
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!