广树
2024-11-11 14:21
@hikari:是的,水比较深
作者:广树时间:2018-09-16 18:16:46分类:JavaScript/jQuery/Vue
早在去年十月,我写过一篇题为《【H5小游戏】制作一个简单的赛车游戏》的文章。基于最基本的canvas功能,而且是以格子的形式去刷新游戏界面,游戏原理类似上世纪的game&watch。
然后最近学了些pixi.js的东西(相关文章可以从《2D WebGL renderer Pixi.js v4 入门【第一回】》开始看),于是就把游戏换成了基于pixi.js的游戏。
游戏质量也从game&watch模式提升到了逐帧模式了。不过总体还是很垃圾,毕竟是四个小时瞎搞出来的。
那么废话不多说,先上预览效果。(感觉游戏难度更难了……)
然后是代码部分,原理基本上都注释了。这次代码基于pixi.js v4.8.2。相关内容也都是之前pixi入门中提到过的内容。
//别名 let Application = PIXI.Application, Container = PIXI.Container, loader = PIXI.loader, resources = PIXI.loader.resources, Graphics = PIXI.Graphics, TextureCache = PIXI.utils.TextureCache, Sprite = PIXI.Sprite, Text = PIXI.Text, TextStyle = PIXI.TextStyle; //创建一个pixi应用 let app = new Application({ width: 240, height: 120, antialiasing: true, transparent: false, resolution: 2 } ); //将pixi插入文档 document.body.appendChild(app.view); //加载素材图片 loader .add("img/bg.png") .add("img/boom.png") .add("img/emcar.png") .add("img/mycar.png") .load(setup); //设置要用到的变量 let state,gameScene,gameOverScene,message,bg,mycarSprite,blobs,numberOfBlobs,score,hp; function setup() { numberOfBlobs = 2;//初始化最大敌车数量 score = 0;//初始化分数 hp = 5;//初始化HP document.getElementById('hp').innerHTML = String(hp);//将HP写入元素 //设置游戏场景容器 gameScene = new Container(); app.stage.addChild(gameScene); //设置背景 bg = new Sprite(resources["img/bg.png"].texture); bg.width = 480; bg.height = 120; gameScene.addChild(bg); //设置自己的车 mycarSprite=new Sprite(resources["img/mycar.png"].texture); mycarSprite.position.set(2,5);//自己的车的初始位置 mycarSprite.width = 18;//自己的车宽度 mycarSprite.height = 11;//高度 mycarSprite.vy = 0;//y轴加速度 mycarSprite.vx = 0;//x轴加速度 mycarSprite.invl = 0;//初始化无敌时间 gameScene.addChild(mycarSprite); //一个存地方车辆的数组 blobs = []; //创建一个游戏结束的场景 gameOverScene = new Container(); app.stage.addChild(gameOverScene); //先让游戏结束场景隐藏 gameOverScene.visible = false; //设置一个默认字体 let style = new TextStyle({ "wordWrap": true, "align": "center", "fill": "white", "fontSize": 14 }); message = new Text("", style); message.y = app.stage.height / 2 - 32; message.x = app.stage.width / 4; //字体中心点用于居中 message.anchor.set(0.5,0.5) gameOverScene.addChild(message); //设置按键代码 let left = keyboard(37), up = keyboard(38), right = keyboard(39), down = keyboard(40); //左按下去之后 left.press = function() { //改变自己车子的加速度 mycarSprite.vx = -5; mycarSprite.vy = 0; }; //松开左键 left.release = function() { //防止键位冲突 if (!right.isDown && mycarSprite.vy === 0) { mycarSprite.vx = 0; } }; //Up up.press = function() { mycarSprite.vy = -5; mycarSprite.vx = 0; }; up.release = function() { if (!down.isDown && mycarSprite.vx === 0) { mycarSprite.vy = 0; } }; //Right right.press = function() { mycarSprite.vx = 5; mycarSprite.vy = 0; }; right.release = function() { if (!left.isDown && mycarSprite.vy === 0) { mycarSprite.vx = 0; } }; //Down down.press = function() { mycarSprite.vy = 5; mycarSprite.vx = 0; }; down.release = function() { if (!up.isDown && mycarSprite.vx === 0) { mycarSprite.vy = 0; } }; state = play; app.ticker.add(delta => gameLoop(delta)); } function creatEMCar(){ //设置敌人 //根据最大敌车数创建车子 for (let i = 0; i < numberOfBlobs-blobs.length; i++) { //创建敌车 let blob = new Sprite(resources["img/emcar.png"].texture); blob.width = 18; blob.height = 11; let EMCarIsHit = true; while(EMCarIsHit){ //随机车子x坐标在画布外 let x = randomInt(250, 500); //随机y坐标在画布范围内 let y = randomInt(2, app.stage.height - 2 - blob.height); //设置车辆位置 blob.x = x; blob.y = y; //检测车辆位置是否有重叠 EMCarIsHit = checkEMCarPositionHit(blob); } //将车子加入数组 blobs.push(blob); //渲染 gameScene.addChild(blob); } } //检查生成的地方车辆是否有重叠 function checkEMCarPositionHit(blob){ for(var i=0;i<blobs.length;i++){ if(hitTestRectangle(blob,blobs[i])){ return true; break; } } } function gameLoop(delta){ //更新游戏场景: state(delta); } function play(delta) { //背景移动 bg.x -= 10; if(bg.x<-230){ bg.x = 0; } //移动赛车 mycarSprite.x += mycarSprite.vx; mycarSprite.y += mycarSprite.vy; if(mycarSprite.invl>0){ mycarSprite.invl --; } //判断车辆是否超出画布 contain(mycarSprite, {x: 0, y: 2, width: 240, height: 118}); //设置敌人 creatEMCar(); let explorerHit = false; //移动敌人 blobs.forEach(function(blob) { //移动地方车辆 blob.x -= 5; //如果和我方车辆发生碰撞 if(hitTestRectangle(mycarSprite, blob)) { explorerHit = true; } }); //去除已经行驶到画布外的敌军车辆 removeEmCar(); //加分 score += 1; document.getElementById('score').innerHTML = String(score); //如果分数大于2000则每1000分场景车辆+1,最多八辆 if(score>2000){ numberOfBlobs = Math.floor(score/1000); if(numberOfBlobs>8){ numberOfBlobs = 8; } } //如果发生碰撞且在无敌时间之外 if(explorerHit&&mycarSprite.invl===0) { //车子图片变更为爆炸 mycarSprite.texture = resources["img/boom.png"].texture; //减血 hp --; document.getElementById('hp').innerHTML = String(hp); //判断是否血量归零 if(hp<1){ //抛出游戏结束 state = end; message.text = "你的车炸了!刷新重来"; } mycarSprite.invl = 30//设置无敌时间 }else if(mycarSprite.invl<=0){//如果不在无敌时间内 //车辆贴图变回去 mycarSprite.texture = resources["img/mycar.png"].texture; } else {//其他情况 mycarSprite.texture = resources["img/mycar.png"].texture; } } //移除已经行驶到画布外围的敌方车辆 function removeEmCar(){ for(var i=0;i<blobs.length;i++){ if(blobs[i].x<-50){ //从数组删除 blobs.splice(i, 1); //从场景删除 gameScene.removeChildAt(i+2); } } } //按键方法 function keyboard(keyCode) { var key = {}; key.code = keyCode; key.isDown = false; key.isUp = true; key.press = undefined; key.release = undefined; key.downHandler = function(event) { if (event.keyCode === key.code) { if (key.isUp && key.press) key.press(); key.isDown = true; key.isUp = false; } event.preventDefault(); }; key.upHandler = function(event) { if (event.keyCode === key.code) { if (key.isDown && key.release) key.release(); key.isDown = false; key.isUp = true; } event.preventDefault(); }; window.addEventListener( "keydown", key.downHandler.bind(key), false ); window.addEventListener( "keyup", key.upHandler.bind(key), false ); return key; } //判断是否超出画布 function contain(sprite, container) { let collision = undefined; //Left if (sprite.x < container.x) { sprite.x = container.x; collision = "left"; } //Top if (sprite.y < container.y) { sprite.y = container.y; collision = "top"; } //Right if (sprite.x + sprite.width > container.width) { sprite.x = container.width - sprite.width; collision = "right"; } //Bottom if (sprite.y + sprite.height > container.height) { sprite.y = container.height - sprite.height; collision = "bottom"; } return collision; } function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } //碰撞检测 function hitTestRectangle(r1, r2) { //设置需要的变量 let hit, combinedHalfWidths, combinedHalfHeights, vx, vy; //初始化是否碰撞 hit = false; //寻找精灵中心点 r1.centerX = r1.x + r1.width / 2; r1.centerY = r1.y + r1.height / 2; r2.centerX = r2.x + r2.width / 2; r2.centerY = r2.y + r2.height / 2; //计算精灵一半的宽度和高度 r1.halfWidth = r1.width / 2; r1.halfHeight = r1.height / 2; r2.halfWidth = r2.width / 2; r2.halfHeight = r2.height / 2; //计算相互之间的距离 vx = r1.centerX - r2.centerX; vy = r1.centerY - r2.centerY; //计算xy重叠的数值 combinedHalfWidths = r1.halfWidth + r2.halfWidth; combinedHalfHeights = r1.halfHeight + r2.halfHeight; //检查x轴是否充电 if (Math.abs(vx) < combinedHalfWidths) { //再检查y轴 if (Math.abs(vy) < combinedHalfHeights) { //如果是则判断为碰撞 hit = true; } else { //否则为没碰撞 hit = false; } } else { //x轴没有碰撞 hit = false; } //传是否碰撞 return hit; }; //游戏结束显示游戏结束场景,隐藏游戏场景 function end() { gameScene.visible = false; gameOverScene.visible = true; }