PIXI 太空玉兔游戏(6)
想法来源
出于练习看到这篇文章 没有什么难度 效果如下,接下来会用pixijs讲解如何实现
创建应用及舞台
HTML部分只创建标签引入 pixi.min.js 即可:
<script src="pixi.min.js"></script>
javascript:
let app = new PIXI.Application({ width: 350, height: 526 }) document.body.appendChild(app.view);
运行后,页面是一个黑色区域,没错这是正常的。接下来添加精灵
创建精灵
首先加载图片使用PIXI自带loader,来实现图片预加载,图片全部加载完毕后再执行其它操作
定义图片列表:
var imgList = [ "img/bg.jpg", "img/bg1.jpg", "img/food21.png", "img/food2.png", "img/icon.png", "img/player.png", "img/heart.png", ]
是用loader预加载图片,也可以监听加载进度onprogress:
PIXI.loader .add(imgList) .load(function () {
// 图片素在记载完毕后,创建精灵 bgimg = new PIXI.Sprite(PIXI.loader.resources["img/bg.jpg"].texture); })
把精灵添加到舞台
app.stage.addChild(bgimg);
背景图片滚动效果
原理如图,创建两个精灵,一个在可见区域,一个在可见区域上边或者下边,操作背景移动 ,当背景移动区域大于背景图片高度,重新绘制。
可以见图,方向是从上往下运动的
通过中间变量:
var bgDistance = 0; // 获取移动y值大小
var bgHeight = 背景图片高度; // 获取背景图片高度来 判断bgDistance移动大小是否,超过背景图片高度,超过重新渲染
背景图片移动向下飞船向前行驶,使用PIXI ticker
tips:bgDistance 变量在上边已声明默认:0
app.ticker.add(function (delta) { // 背景图片1位置 bgimg.x = 0; bgimg.y = bgDistance - bgHeight; // 背景图片2位置 bgimg1.x = 0; bgimg1.y = bgDistance; if(bgDistance >= bgHeight){ bgloop = 0; } bgDistance = ++bgloop; })
会看到背景在移动,接下来在页面添加飞船,操作跟添加背景图片一样,创建一个精灵添加到舞台中:
这时会看到飞船出现在太空中,飞船有些怪怪的,接下来大小及位置修改
player.scale.x = 0.5; player.scale.y = 0.5; player.x = (app.view.width - player.width)/2; player.y = app.view.height - player.height -100;
scale 进行缩放
app.view.width/height 获取舞台的宽高,
player.width/height 获取当前容器或是精灵所占的大小
接下来,让飞船移动,通过键盘 上下左右来控制 及监听事件 keydown
document.addEventListener("keydown", function (event) { // alert(event.keyCode) switch (event.keyCode) { case 37: player.vx = -5; break; case 38: player.vy = -5; break; case 39: player.vx = 5; break; case 40: player.vy = 5; break; } }, false) document.addEventListener("keyup", function () { player.vx = 0; player.vy = 0; })
键盘事件写完后,在ticker控制改变飞船位置
player.x += player.vx;
player.y += player.vy;
这时会发现,飞船 飞出舞台之外了 ,接下来,检测是否到舞台边缘 有两种方式可以检测
1、自定义一个容器的宽高,来判断是否到该容器边缘
2、直接判该该精灵是否到舞台边缘
直接就用第二种了,
// 判断是否到舞台边缘 if(player.x<=0){ player.x = 0; }else if( player.x >= bgimg.width- player.width){ player.x = bgimg.width- player.width; } // 同理, y的判断 也是一样的。。。
靠近边缘,不会出舞台外边;
接下来考虑,随机掉月饼有两种类型的月饼
function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
每个随机玉兔位置应该为:
var randomType = Math.floor(Math.random()*2 +1); return { rabbitType : randomInt(1, 2), x: randomInt(1,app.view.width), y: 0,
speed:randomInt(1,4), // 向下移动速度
isHas: false // 是否存在舞台中
}
}
在舞台中,显示玉兔,创建精灵并把精灵放到舞台上 ,让精灵从上往下移动使用ticker 对y ++操作 ,当玉兔跟月饼相碰是,
精灵销毁 ,销毁,销毁! 当精灵到屏幕最下方是,也要做销毁处理。
月饼下实际上就是对数组操作,当到底部在数组中移除操作
定义存放数组
var ybArry = []; // 存放月饼
var imgOjbArry = []; // 存放图片对象,把每个创建的精灵添加的舞台上
在ticker随机调用数组函数 ,数组里边最多存放五个元素。
// 处理月饼下路 假定 一直值显示五个 月饼
if(Math.random() > 0.9 && ybArry && ybArry.length < 5){
var randPoint = randomRabbit();
ybArry.push(randPoint);
}
对每个随机生成的坐标y ,进行++ 操作,数组中每个元素都会有下路效果
for(var i = 0; i < ybArry.length; i++){ ybArry[i].y += ybArry[i].speed; }
跟随机月饼数,创建图片精灵,让图片运动
// 根据随机数组创建精灵 for(var j = 0; j< ybArry.length; j++){ if(ybArry[j].isHas == false){ var sprite = new PIXI.Sprite( PIXI.loader.resources["img/food"+ybArry[j].rabbitType+".png"].texture ); ybArry[j].isHas = true; imgOjbArry.push(sprite); app.stage.addChild(sprite); } }
把数组月饼中的y 赋值给图片精灵坐标
for(var k = 0; k < imgOjbArry.length; k++){ imgOjbArry[k].x = ybArry[k].x; imgOjbArry[k].y = ybArry[k].y; if(imgOjbArry[k].y >= app.view.height){ imgOjbArry[k].destroy(); ybArry.splice(k, 1); imgOjbArry.splice(k, 1); } }
舞台就会显示精灵运动,细心会看到,随机产生不一样的图。根据不同图片,碰撞那个加分那个结束。自己定义;
碰撞检测实现:(官网的函数copy)
//The `hitTestRectangle` function function hitTestRectangle(r1, r2) { //Define the variables we'll need to calculate let hit, combinedHalfWidths, combinedHalfHeights, vx, vy; //hit will determine whether there's a collision hit = false; //Find the center points of each sprite 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; //Find the half-widths and half-heights of each sprite r1.halfWidth = r1.width / 2; r1.halfHeight = r1.height / 2; r2.halfWidth = r2.width / 2; r2.halfHeight = r2.height / 2; //Calculate the distance vector between the sprites vx = r1.centerX - r2.centerX; vy = r1.centerY - r2.centerY; //Figure out the combined half-widths and half-heights combinedHalfWidths = r1.halfWidth + r2.halfWidth; combinedHalfHeights = r1.halfHeight + r2.halfHeight; //Check for a collision on the x axis if (Math.abs(vx) < combinedHalfWidths) { //A collision might be occuring. Check for a collision on the y axis if (Math.abs(vy) < combinedHalfHeights) { //There's definitely a collision happening hit = true; } else { //There's no collision on the y axis hit = false; } } else { //There's no collision on the x axis hit = false; } //`hit` will be either `true` or `false` return hit; };
碰撞后的月饼透明度降低,是带花的,五仁的无变化。
for(var k = 0; k < imgOjbArry.length; k++){ imgOjbArry[k].x = ybArry[k].x; imgOjbArry[k].y = ybArry[k].y; imgOjbArry[k].rabbitType = ybArry[k].rabbitType; // 碰撞检测 var hitStatus = hitTestRectangle(player, imgOjbArry[k]); // console.log(imgOjbArry[k].rabbitType ) if(hitStatus){ if(imgOjbArry[k].rabbitType == 2){ imgOjbArry[k].alpha = 0.5; } } if(imgOjbArry[k].y >= app.view.height){ imgOjbArry[k].destroy(); ybArry.splice(k, 1); imgOjbArry.splice(k, 1); } }
其它操作根据需要添加,记录分数或或是关数。
代码如下:勉强看吧,没整理。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>天空</title> <script src="lib/pixi.min.js"></script> </head> <body> <script> var bgDistance = 0; var bgHeight; // 背景图片高度 var bgloop = 0; var ybArry = []; // 存放月饼 var imgOjbArry = []; // 存放图片对象,把每个创建的精灵添加的舞台上 let app = new PIXI.Application({ width: 350, height: 526 }); document.body.appendChild(app.view); // 随机创建玉兔(有两种根据类型来区分) //randomRabbit(); function randomRabbit(){ var randomType = Math.floor(Math.random()*2 +1); return { rabbitType : randomInt(1, 2), x: randomInt(1,app.view.width), y: 0, speed:randomInt(1,4), // 向下移动速度 isHas: false // 是否存在舞台中 } } console.log(randomRabbit()) function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } var imgList = [ "img/bg.jpg", "img/food1.png", "img/food2.png", "img/icon.png", "img/player.png", ] PIXI.loader .add(imgList) .load(function () { // 图片素在记载完毕后,创建精灵 bgimg = new PIXI.Sprite(PIXI.loader.resources["img/bg.jpg"].texture); bgimg1 = new PIXI.Sprite(PIXI.loader.resources["img/bg.jpg"].texture); player = new PIXI.Sprite(PIXI.loader.resources["img/player.png"].texture); player.scale.x = 0.5; player.scale.y = 0.5; player.x = (app.view.width - player.width) / 2; player.y = app.view.height - player.height - 100; player.vx = 0; player.vy = 0; bgHeight = bgimg.height; app.stage.addChild(bgimg); app.stage.addChild(bgimg1); app.stage.addChild(player); app.ticker.add(function (delta) { bgimg.x = 0; bgimg.y = bgDistance - bgHeight; bgimg1.x = 0; bgimg1.y = bgDistance; if (bgDistance >= bgHeight) { bgloop = 0; } bgDistance = ++bgloop; player.x += player.vx; player.y += player.vy; // 判断是否到舞台边缘 if(player.x<=0){ player.x = 0; }else if( player.x >= bgimg.width- player.width){ player.x = bgimg.width- player.width; } // 同理, y的判断 也是一样的。。。 // 处理月饼下路 假定 一直值显示五个 月饼 if(Math.random() > 0.9 && ybArry && ybArry.length < 5){ var randPoint = randomRabbit(); ybArry.push(randPoint); } for(var i = 0; i < ybArry.length; i++){ ybArry[i].y += ybArry[i].speed; } // 根据随机数组创建精灵 for(var j = 0; j< ybArry.length; j++){ if(ybArry[j].isHas == false){ var sprite = new PIXI.Sprite( PIXI.loader.resources["img/food"+ybArry[j].rabbitType+".png"].texture ); ybArry[j].isHas = true; imgOjbArry.push(sprite); app.stage.addChild(sprite); } } for(var k = 0; k < imgOjbArry.length; k++){ imgOjbArry[k].x = ybArry[k].x; imgOjbArry[k].y = ybArry[k].y; imgOjbArry[k].rabbitType = ybArry[k].rabbitType; // 碰撞检测 var hitStatus = hitTestRectangle(player, imgOjbArry[k]); // console.log(imgOjbArry[k].rabbitType ) if(hitStatus){ if(imgOjbArry[k].rabbitType == 2){ imgOjbArry[k].alpha = 0.5; } } if(imgOjbArry[k].y >= app.view.height){ imgOjbArry[k].destroy(); ybArry.splice(k, 1); imgOjbArry.splice(k, 1); } } }) }); document.addEventListener("keydown", function (event) { // alert(event.keyCode) switch (event.keyCode) { case 37: player.vx = -5; break; case 38: player.vy = -5; break; case 39: player.vx = 5; break; case 40: player.vy = 5; break; } }, false) document.addEventListener("keyup", function () { player.vx = 0; player.vy = 0; }) //The `hitTestRectangle` function function hitTestRectangle(r1, r2) { //Define the variables we'll need to calculate let hit, combinedHalfWidths, combinedHalfHeights, vx, vy; //hit will determine whether there's a collision hit = false; //Find the center points of each sprite 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; //Find the half-widths and half-heights of each sprite r1.halfWidth = r1.width / 2; r1.halfHeight = r1.height / 2; r2.halfWidth = r2.width / 2; r2.halfHeight = r2.height / 2; //Calculate the distance vector between the sprites vx = r1.centerX - r2.centerX; vy = r1.centerY - r2.centerY; //Figure out the combined half-widths and half-heights combinedHalfWidths = r1.halfWidth + r2.halfWidth; combinedHalfHeights = r1.halfHeight + r2.halfHeight; //Check for a collision on the x axis if (Math.abs(vx) < combinedHalfWidths) { //A collision might be occuring. Check for a collision on the y axis if (Math.abs(vy) < combinedHalfHeights) { //There's definitely a collision happening hit = true; } else { //There's no collision on the y axis hit = false; } } else { //There's no collision on the x axis hit = false; } //`hit` will be either `true` or `false` return hit; }; </script> </body> </html>