<canvas id="main"></canvas>
* {padding: 0; margin: 0}
const frames = {
"frames": {
"blob.png": {
"frame": {
"x": 55,
"y": 2,
"w": 32,
"h": 24
},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 24
},
"sourceSize": {
"w": 32,
"h": 24
},
"pivot": {
"x": 0.5,
"y": 0.5
}
},
"door.png": {
"frame": {
"x": 89,
"y": 2,
"w": 32,
"h": 32
},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 32,
"h": 32
},
"sourceSize": {
"w": 32,
"h": 32
},
"pivot": {
"x": 0.5,
"y": 0.5
}
},
"dungeon.png": {
"frame": {
"x": 2,
"y": 36,
"w": 512,
"h": 512
},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 512,
"h": 512
},
"sourceSize": {
"w": 512,
"h": 512
},
"pivot": {
"x": 0.5,
"y": 0.5
}
},
"explorer.png": {
"frame": {
"x": 2,
"y": 2,
"w": 21,
"h": 32
},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 21,
"h": 32
},
"sourceSize": {
"w": 21,
"h": 32
},
"pivot": {
"x": 0.5,
"y": 0.5
}
},
"treasure.png": {
"frame": {
"x": 25,
"y": 2,
"w": 28,
"h": 24
},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {
"x": 0,
"y": 0,
"w": 28,
"h": 24
},
"sourceSize": {
"w": 28,
"h": 24
},
"pivot": {
"x": 0.5,
"y": 0.5
}
}
},
"meta": {
"app": "http://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "https://i.imgur.com/yxmphGW.png",
"format": "RGBA8888",
"size": {
"w": 512,
"h": 512
},
"antialias": true,
"transparent": false,
"backgroundColor": "0x000000",
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:51ede84c7a85e4d6aeb31a6020a20858:3923663e59fb40b578d66a492a2cda2d:9995f8b4db1ac3cb75651b1542df8ee2$"
}
}
const app = new PIXI.Application({
view: document.getElementById('main'),
width: frames.meta.size.w,
height: frames.meta.size.h,
antialias: frames.meta.antialias,
transparent: frames.meta.transparent,
backgroundColor: frames.meta.backgroundColor,
});
const loader = new PIXI.Loader();
loader
.add('gameimg',frames.meta.image)
.load((loader, resource)=> {
init(resource);
})
const gameRun = new PIXI.Container();
gameRun.visible = true;
app.stage.addChild(gameRun);
const gameStart = new PIXI.Container();
gameStart.visible = false;
app.stage.addChild(gameStart);
const gameOver = new PIXI.Container();
gameOver.visible = false;
app.stage.addChild(gameOver);
const gameWin = new PIXI.Container();
gameWin.visible = false;
app.stage.addChild(gameWin);
const hpContainer = new PIXI.Container();
function gameSprite(item, resource) {
const pixiRectangle = new PIXI.Rectangle(frames.frames[item].frame.x, frames.frames[item].frame.y, frames.frames[item].frame.w, frames.frames[item].frame.h);
let newTex = new PIXI.Texture(resource.gameimg.texture, pixiRectangle);
const sprite = new PIXI.Sprite(newTex);
return sprite;
}
let dungeon, blob, treasure, door, explorer,blobOptions;
let gameHP = 100;
let getTreasure = false;
let gamePlay = false;
const blobs = [];
function init(resource) {
// 載入地牢
dungeon = gameSprite('dungeon.png', resource);
gameStart.addChild(dungeon);
// 載入怪物
blobOptions = {
number: 8,
speeds: 5,
};
for(let i = 0; i < blobOptions.number; i += 1) {
blob = gameSprite('blob.png', resource);
blob.x = 120 + (blobOptions.number + 32) * i;
blob.y = frames.meta.size.h / 2;
blob.vy = blobOptions.speeds + randomInt(0, 3);
blobs.push(blob);
gameStart.addChild(blob);
}
// 寶箱
treasure = gameSprite('treasure.png', resource);
treasure.x = 400;
treasure.y = frames.meta.size.h / 2;
gameStart.addChild(treasure);
// 逃出門
door = gameSprite('door.png', resource);
door.x = 50;
door.y = 0;
gameStart.addChild(door);
// 玩家
explorer = gameSprite('explorer.png', resource);
explorer.x = 50;
explorer.y = frames.meta.size.h / 2;
explorer.vx = 0;
explorer.vy = 0;
gameStart.addChild(explorer);
HPstatus();
const run = messages('按下 Enter 開始遊戲');
run.x = frames.meta.size.w / 2 - run.width /2;
run.y = frames.meta.size.h / 2 - run.height /2;
gameRun.addChild(run);
const over = messages('Game Over');
over.x = frames.meta.size.w / 2 - over.width /2;
over.y = frames.meta.size.h / 2 - over.height /2;
gameOver.addChild(over);
const Win = messages('你贏了!');
Win.x = frames.meta.size.w / 2 - Win.width /2;
Win.y = frames.meta.size.h / 2 - Win.height /2;
gameWin.addChild(Win);
// 重新開始按鈕繪畫
const resetBtn = new PIXI.Container();
gameOver.addChild(resetBtn);
let gameGraphics = new PIXI.Graphics();
gameGraphics.beginFill(0x33BBFF);
gameGraphics.drawRoundedRect(0, 0, 120, 50, 25);
gameGraphics.endFill();
gameGraphics.x = frames.meta.size.w / 2 - gameGraphics.width / 2;
gameGraphics.y = frames.meta.size.h / 2 - gameGraphics.height / 2 + 60;
resetBtn.addChild(gameGraphics);
const resetText = new PIXI.Text('重新開始', { // 改用變數傳入
fontFamily: 'Microsoft JhengHei',
fontSize: 16,
fill: [0xFFFFFF],
align: 'center'
});
resetText.x = frames.meta.size.w / 2 - resetText.width / 2;
resetText.y = frames.meta.size.h / 2 - resetText.height / 2 + 60;
resetBtn.addChild(resetText);
// 設置互動
resetBtn.interactive = true;
resetBtn.buttonMode = true;
resetBtn.click = gameReset;
// 遊戲獲勝繪畫
const resetWinBtn = new PIXI.Container();
gameWin.addChild(resetWinBtn);
let gameWinGraphics = new PIXI.Graphics();
gameWinGraphics.beginFill(0x33BBFF);
gameWinGraphics.drawRoundedRect(0, 0, 120, 50, 25);
gameWinGraphics.endFill();
gameWinGraphics.x = frames.meta.size.w / 2 - gameGraphics.width / 2;
gameWinGraphics.y = frames.meta.size.h / 2 - gameGraphics.height / 2 + 60;
resetWinBtn.addChild(gameWinGraphics);
const resetWinText = new PIXI.Text('重新挑戰', {
fontFamily: 'Microsoft JhengHei',
fontSize: 16,
fill: [0xFFFFFF],
align: 'center'
});
resetWinText.x = frames.meta.size.w / 2 - resetWinText.width / 2;
resetWinText.y = frames.meta.size.h / 2 - resetWinText.height / 2 + 60;
resetWinBtn.addChild(resetWinText);
// 設置互動
resetWinBtn.interactive = true;
resetWinBtn.buttonMode = true;
resetWinBtn.click = gameReset;
app.ticker.add((delta) => {
gameLoop(delta);
});
}
function gameReset() {
// HP 回復
gameHP = 100;
// 初始化寶箱
getTreasure = false;
// 玩家回歸初始位置
explorer.x = 50;
explorer.y = frames.meta.size.h / 2;
// 寶箱回歸初始位置
treasure.x = 400;
treasure.y = frames.meta.size.h / 2;
// 重新調整怪物速率
blob.vy = blobOptions.speeds + randomInt(0, 3);
console.log(blob.vy);
// 重新設置 Container 顯示
gameRun.visible = true;
gameStart.visible = false;
gameOver.visible = false;
gameWin.visible = false;
}
function gameLoop() {
if(gameHP <= 0) {
gameStart.visible = false;
gameOver.visible = true;
}
contain(explorer, {x: 28, y: 10, width: 488, height: 480});
let hitStatus = false;
blobs.forEach((item) => {
item.y += item.vy;
const blobsHit = contain(item, {x: 28, y: 10, width: 488, height: 480})
if(blobsHit === 'top' || blobsHit === 'bottom') {
item.vy *= -1;
}
if(boxesIntersect(explorer, item)) {
hitStatus = true;
}
})
if(hitStatus) {
// 碰撞後扣除血量
if(explorer.alpha !== 1) return
const getRandom = randomInt(1, 5);
gameHP -= getRandom;
explorer.alpha = 0.1;
hurt(getRandom);
} else {
explorer.alpha = 1;
}
if(boxesIntersect(explorer, treasure)) {
treasure.x = explorer.x + 8;
treasure.y = explorer.y + 8;
getTreasure = true;
}
if(boxesIntersect(explorer, door) && getTreasure) {
gameWin.visible = true;
gameStart.visible = false;
// 玩家回歸初始位置,避免衝突
explorer.x = 50;
explorer.y = frames.meta.size.h / 2;
}
// 重新調整血量
hpContainer.hpStatus.width = gameHP;
}
function HPstatus() {
let border = new PIXI.Graphics();
border.beginFill(0x000000);
border.drawRect(0, 0, 100, 10, 10);
border.endFill();
hpContainer.addChild(border);
let borderFill = new PIXI.Graphics();
borderFill.beginFill(0xFF0000);
borderFill.drawRect(0, 0, 100, 10, 10);
borderFill.endFill();
hpContainer.addChild(borderFill);
hpContainer.hpStatus = borderFill;
hpContainer.x = 340;
hpContainer.y = 10;
gameStart.addChild(hpContainer);
}
function hurt(item) {
if(gameHP <= 0) return;
const containerEffect = new PIXI.Container();
const graphics = new PIXI.Graphics();
const startText = new PIXI.Text(item ,{
fill: 0xDDD00D,
});
graphics.beginFill(0x9D482E);
graphics.drawStar(0, 0, 10, 30);
graphics.endFill();
graphics.x = explorer.x;
graphics.y = explorer.y - 50;
startText.x = graphics.x - startText.width / 2;
startText.y = graphics.y - startText.height / 2;
containerEffect.addChild(graphics);
containerEffect.addChild(startText);
app.stage.addChild(containerEffect);
removeContainer(containerEffect);
}
function removeContainer(item) {
const s = setInterval(() => {
item.alpha -= 0.05;
},100);
setTimeout(() => {
clearInterval(s);
app.stage.removeChild(item);
},10000);
}
function messages(text) {
const style = new PIXI.TextStyle({
fontFamily: 'Microsoft JhengHei', // 風格
fontSize: 24, // 字體大小
fill: [0xEEEE00,0x00ff99], // 填滿,若是陣列則可以漸層效果
align: 'center', // 對齊
stroke: '#000000', // 外框顏色
});
const messages = new PIXI.Text(text, style);
return messages;
}
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const body = document.querySelector('body');
body.addEventListener('keydown',(e) => {
console.log(e.keyCode)
switch(e.keyCode) {
case 38:
case 87:
if(!gamePlay) return;
explorer.y -= 10;
break;
case 40:
case 83:
if(!gamePlay) return;
explorer.y += 10;
break;
case 37:
case 65:
if(!gamePlay) return;
explorer.x -= 10;
break;
case 39:
case 68:
if(!gamePlay) return;
explorer.x += 10;
break;
case 13:
if(gameHP > 0) {
gameRun.visible = false;
gameStart.visible = true;
gamePlay = true;
}
break;
default:
break;
}
})
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;
}
function boxesIntersect(a, b){
var ab = a.getBounds();
var bb = b.getBounds();
return ab.x + ab.width > bb.x && ab.x < bb.x + bb.width && ab.y + ab.height > bb.y && ab.y < bb.y + bb.height;
}
This Pen doesn't use any external CSS resources.