PIXI 宝物猎人(7)

介绍 ,本实例来自官网

 

代码结构

 

打开 treasureHunter.html 文件,你将会看到所有的代码都在一个大的文件里。下面是一个关于如何组织所有代码的概览:

 

//Setup Pixi and load the texture atlas files - call the `setup`
//function when they've loaded

function setup() {
  //Initialize the game sprites, set the game `state` to `play`
  //and start the 'gameLoop'
}

function gameLoop(delta) {
  //Runs the current game `state` in a loop and renders the sprites
}

function play(delta) {
  //All the game logic goes here
}

function end() {
  //All the code that should run at the end of the game
}

//The game's helper functions:
//`keyboard`, `hitTestRectangle`, `contain` and `randomInt`

 

把这个当作你游戏代码的蓝图,让我们看看每一部分是如何工作的。

 

创建游戏场景

setup 函数创建了两个被称为gameScene 和 gameOverScene的 Container 分组。他们都被添加到了舞台上。

gameScene = new Container();
app.stage.addChild(gameScene);

gameOverScene = new Container();
app.stage.addChild(gameOverScene);

 

尽管它是在 setup 函数中添加的,但是 gameOverScene不应在游戏一开始的时候显示,所以它的 visible 属性被初始化为 false

gameOverScene.visible = false;

你会在后面看到,为了在游戏结束之后显示文字,当游戏结束gameOverScene 的 visible 属性会被设置为 true 。

 

制造泡泡怪们

六个泡泡怪是被循环创建的。每一个泡泡怪都被赋予了一个随机的初始位置和速度。每个泡泡怪的垂直速度都被交替的乘以 1或者 -1 ,这就是每个怪物和相邻的下一个怪物运动的方向都是相反的原因,每个被创建的怪物都被放进了一个名为 blobs 的数组。

let numberOfBlobs = 6,
    spacing = 48,
    xOffset = 150,
    speed = 2,
    direction = 1;

//An array to store all the blob monsters
blobs = [];

//Make as many blobs as there are `numberOfBlobs`
for (let i = 0; i < numberOfBlobs; i++) {

  //Make a blob
  let blob = new Sprite(id["blob.png"]);

  //Space each blob horizontally according to the `spacing` value.
  //`xOffset` determines the point from the left of the screen
  //at which the first blob should be added
  let x = spacing * i + xOffset;

  //Give the blob a random `y` position
  let y = randomInt(0, stage.height - blob.height);

  //Set the blob's position
  blob.x = x;
  blob.y = y;

  //Set the blob's vertical velocity. `direction` will be either `1` or
  //`-1`. `1` means the enemy will move down and `-1` means the blob will
  //move up. Multiplying `direction` by `speed` determines the blob's
  //vertical direction
  blob.vy = speed * direction;

  //Reverse the direction for the next blob
  direction *= -1;

  //Push the blob into the `blobs` array
  blobs.push(blob);

  //Add the blob to the `gameScene`
  gameScene.addChild(blob);
}

 

制作血条

当你玩儿宝藏猎人的时候,你会发现当猎人碰到其中一个敌人时,场景右上角的血条宽度会减少。这个血条是如何被制作的?他就是两个相同的位置的重叠的矩形:一个黑色的矩形在下面,红色的上面。他们被分组到了一个单独的 healthBar 分组。 healthBar 然后被添加到 gameScene 并在舞台上被定位。

//Create the health bar
healthBar = new PIXI.DisplayObjectContainer();
healthBar.position.set(stage.width - 170, 4)
gameScene.addChild(healthBar);

//Create the black background rectangle
let innerBar = new PIXI.Graphics();
innerBar.beginFill(0x000000);
innerBar.drawRect(0, 0, 128, 8);
innerBar.endFill();
healthBar.addChild(innerBar);

//Create the front red rectangle
let outerBar = new PIXI.Graphics();
outerBar.beginFill(0xFF3300);
outerBar.drawRect(0, 0, 128, 8);
outerBar.endFill();
healthBar.addChild(outerBar);

healthBar.outer = outerBar;

你会看到 healthBar 添加了一个名为 outer 的属性。它仅仅是引用了 outerBar (红色的矩形)以便于过会儿能够被很方便的获取。

healthBar.outer = outerBar;

你可以不这么做,但是为什么不呢?这意味如果你想控制红色 outerBar 的宽度,你可以像这样顺畅的写如下代码:

healthBar.outer.width = 30;

这样的代码相当整齐而且可读性强,所以我们会一直保留它!

 

 

制作消息文字

当游戏结束的时候, “You won!” 或者 “You lost!” 的文字会显示出来。这使用文字纹理制作的,并添加到了 gameOverScene。因为 gameOverScene 的 visible 属性设为了 false ,当游戏开始的时候,你看不到这些文字。这段代码来自 setup 函数,它创建了消息文字,而且被添加到了 gameOverScene

let style = new TextStyle({
    fontFamily: "Futura",
    fontSize: 64,
    fill: "white"
  });
message = new Text("The End!", style);
message.x = 120;
message.y = app.stage.height / 2 - 32;
gameOverScene.addChild(message);

 

控制运动的范围

一个新的地方的是,探险者的运动是被包裹在地牢的墙体之内的。绿色的轮廓表明了探险者运动的边界。

Displaying text

通过一个名为 contain 的自定义函数可以帮助实现。

contain(explorer, {x: 28, y: 10, width: 488, height: 480});

contain 接收两个参数。第一个是你想控制的精灵。第二个是包含了 xywidth 和height属性的任何一个对象。在这个例子中,控制对象定义了一个区域,它稍微比舞台小了一点,和地牢的尺寸一样。

这里是实现了上述功能的 contain 函数。函数检查了精灵是否跨越了控制对象的边界。如果超出,代码会把精灵继续放在那个边界上。 contain 函数也返回了一个值可能为"top", "right", "bottom" 或者 "left" 的 collision 变量,取决于精灵碰到了哪一个边界。(如果精灵没有碰到任何边界,collision 将返回 undefined 。)

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 the `collision` value
  return collision;
}

你会在接下来看到 collision 的返回值在代码里是如何让怪物在地牢的顶部和底部之间来回反弹的。

 

移动怪物

play 函数也能够移动怪物,保持它们在地牢的墙体之内,并检测每个怪物是否和玩家发生了碰撞。如果一只怪物撞到了地牢的顶部或者底部的墙,它就会被设置为反向运动。完成所有这些功能都是通过一个 forEach循环,它每一帧都会遍历在 blobs 数组里的每一个怪物。

blobs.forEach(function(blob) {

  //Move the blob
  blob.y += blob.vy;

  //Check the blob's screen boundaries
  let blobHitsWall = contain(blob, {x: 28, y: 10, width: 488, height: 480});

  //If the blob hits the top or bottom of the stage, reverse
  //its direction
  if (blobHitsWall === "top" || blobHitsWall === "bottom") {
    blob.vy *= -1;
  }

  //Test for a collision. If any of the enemies are touching
  //the explorer, set `explorerHit` to `true`
  if(hitTestRectangle(explorer, blob)) {
    explorerHit = true;
  }
});

你可以在上面这段代码中看到, contain 函数的返回值是如何被用来让怪物在墙体之间来回反弹的。一个名为 blobHitsWall的变量被用来捕获返回值:

let blobHitsWall = contain(blob, {x: 28, y: 10, width: 488, height: 480});

blobHitsWall 通常应该是 undefined。但是如果怪物碰到了顶部的墙,blobHitsWall 将会变成 "top"。如果碰到了底部的墙,blobHitsWall 会变为 "bottom"。如果它们其中任何一种情况为 true,你就可以通过给怪物的速度取反来让它反向运动。这是实现它的代码:

if (blobHitsWall === "top" || blobHitsWall === "bottom") {
  blob.vy *= -1;
}

把怪物的 vy (垂直速度)乘以 -1 就会反转它的运动方向。

 

检测碰撞

在上面的循环代码里用了 hitTestRectangle 来指明是否有敌人碰到了猎人。

if(hitTestRectangle(explorer, blob)) {
  explorerHit = true;
}

如果 hitTestRectangle 返回 true,意味着发生了一次碰撞,名为 explorerHit 的变量被设置为了 true。如果 explorerHit为 true, play 函数让猎人变为半透明,然后把 health 条减少1像素的宽度。

if(explorerHit) {

  //Make the explorer semi-transparent
  explorer.alpha = 0.5;

  //Reduce the width of the health bar's inner rectangle by 1 pixel
  healthBar.outer.width -= 1;

} else {

  //Make the explorer fully opaque (non-transparent) if it hasn't been hit
  explorer.alpha = 1;
}

如果 explorerHit 是 false,猎人的 alpha 属性将保持1,完全不透明。

 

Displaying text

这段代码实现了上述效果:

if (hitTestRectangle(explorer, treasure)) {
  treasure.x = explorer.x + 8;
  treasure.y = explorer.y + 8;
}

 

 

 

posted on 2018-07-30 00:06  爱拼才有钱  阅读(512)  评论(0编辑  收藏  举报

导航