好家伙, 代碼已開源 Git: https://gitee.com/tang-and-han-dynasties/panghu-planebattle-esm.git NPM: panghu-planebattle-esm - npm (npmjs.com) 現在,比如說,我用Vue寫好了個人博客主 ...
好家伙,
代碼已開源
Git:
https://gitee.com/tang-and-han-dynasties/panghu-planebattle-esm.git
NPM:
panghu-planebattle-esm - npm (npmjs.com)
現在,比如說,我用Vue寫好了個人博客主頁的前端
我想在這個主頁裡面加點東西,讓我的博客更繽紛多彩一點
我想在他的主頁裡面塞個小游戲,他會怎麼做
1.思考步驟
如下:
第一步:去網上找個小游戲的資源,將這個包下載到本地,
誒,正好發現有個飛機大戰 panghu-planebattle-modular 小游戲開發好了
我可以直接下載,或者通過npm安裝
npm install panghu-planebattle-modular
第二步:導入到某個.vue文件或html文件
通過import導入
第三步:劃一個區域<div>給這個包去渲染游戲
剩下的他就不用管了
大概是這麼個過程,然後我們按著這個思路,反向去分我們這個包
先來看看原先的完整代碼:
完整代碼
<template>
<div>
<h1>歡迎來到主頁面</h1>
<div ref="stage"></div>
</div>
</template>
<script>
export default {
mounted() {
//canvas初始化
console.log("我被執行啦")
let canvas = document.createElement('canvas');
this.$refs.stage.appendChild(canvas);
canvas.width = 480;
canvas.height = 650;
canvas.ref = canvas;
canvas.style = "border: 1px solid red;"
const context = canvas.getContext("2d");
//圖片初始化方法
function createImage(src) {
let img;
if (typeof src === "string") {
img = new Image();
img.src = require('./img/' + src);
} else {
img = [];
for (let i = 0; i < src.length; i++) {
img[i] = new Image();
img[i].src = require('./img/' + src[i]);
}
}
return img;
}
//createImage()方法測試樣例
// let bg = createImage("4.jpg")
// bg.onload = function () {
// console.log("img載入完畢")
// context.drawImage(bg, 0, 0, 480, 650)
// }
const IMAGES = {
b: "bullet1.png",
bg: "4.png",
copyright: "shoot_copyright.png",
pause: "game_pause.png",
loading_frame: ["game_loading1.png", "game_loading2.png", "game_loading3.png",
"game_loading4.png"
],
hero_frame_live: ["hero1.png", "hero2.png"],
hero_frame_death: ["hero_blowup_n1.png", "hero_blowup_n2.png", "hero_blowup_n3.png",
"hero_blowup_n4.png"
],
e1_live: ["enemy1.png"],
e1_death: ["enemy1_down1.png", "enemy1_down2.png", "enemy1_down3.png", "enemy1_down4.png"],
e2_live: ["enemy2.png"],
e2_death: ["enemy2_down1.png", "enemy2_down2.png", "enemy2_down3.png", "enemy2_down4.png"],
e3_live: ["enemy3_n1.png", "enemy3_n2.png"],
e3_death: ["enemy3_down1.png", "enemy3_down2.png", "enemy3_down3.png", "enemy3_down4.png",
"enemy3_down5.png", "enemy3_down6.png"
],
c1: "lanqiu.png"
};
//初始化各個圖片
const b = createImage(IMAGES.b);
const bg = createImage(IMAGES.bg);
const copyright = createImage(IMAGES.copyright);
const pause = createImage(IMAGES.pause);
const loading_frame = createImage(IMAGES.loading_frame);
const hero_frame = {
live: createImage(IMAGES.hero_frame_live),
death: createImage(IMAGES.hero_frame_death),
};
const e1 = {
live: createImage(IMAGES.e1_live),
death: createImage(IMAGES.e1_death),
};
const e2 = {
live: createImage(IMAGES.e2_live),
death: createImage(IMAGES.e2_death),
};
const e3 = {
live: createImage(IMAGES.e3_live),
death: createImage(IMAGES.e3_death),
};
const c1 = createImage(IMAGES.c1);
//配置項:
// 定義游戲的狀態
// 開始
const START = 0;
// 開始時
const STARTING = 1;
// 運行時
const RUNNING = 2;
// 暫停時
const PAUSE = 3;
// 結束時
const END = 4;
// 載入中
const LOADINGING = 5;
//state表示游戲的狀態 取值必須是以上的五種狀態
let state = LOADINGING;
// hero_frame.addEventListener("load", () => {
// state = START;
// })
pause.onload = function () {
state = START;
console.log(state)
}
//score 分數變數 life 變數
let score = 0;
let life = 3;
//天空類的配置項
const SKY = {
bg: bg,
width: 480,
height: 650,
speed: 10,
};
// 飛機載入界面的配置項
const LOADING = {
frame: loading_frame,
width: 186,
height: 38,
x: 0,
y: 650 - 38,
speed: 400,
};
// 英雄配置項
const HERO = {
frame: hero_frame,
width: 99,
height: 124,
speed: 100,
};
// 子彈配置項
const BULLET = {
img: b,
width: 9,
height: 21,
};
//小敵機配置項
const E1 = {
type: 1,
width: 57,
height: 51,
life: 10,
score: 1,
frame: e1,
minSpeed: 20,
maxSpeed: 10
};
//中敵機配置項
const E2 = {
type: 2,
width: 69,
height: 95,
life: 50,
score: 5,
frame: e2,
minSpeed: 50,
maxSpeed: 20
};
//打敵機配置項
const E3 = {
type: 3,
width: 169,
height: 258,
life: 100,
score: 20,
frame: e3,
minSpeed: 100,
maxSpeed: 100
};
//獎勵類配置項
const C1 = {
type: 4,
width: 75,
height: 75,
life: 1,
score: 1,
img: c1,
minSpeed: 5,
maxSpeed: 10
};
//正式代碼
//初始化獎勵類
class Award {
constructor(config) {
this.type = config.type;
this.width = config.width;
this.height = config.height;
this.x = Math.floor(Math.random() * (480 - config.width));
this.y = -config.height;
this.life = config.life;
this.score = config.score;
this.img = config.img;
this.live = true;
this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;
this.lastTime = new Date().getTime();
this.deathIndex = 0;
this.destory = false;
}
move() {
const currentTime = new Date().getTime();
if (currentTime - this.lastTime >= this.speed) {
if (this.live) {
this.y = this.y + 6;
this.lastTime = currentTime;
} else {
this.destory = true;
}
}
}
paint(context) {
context.drawImage(this.img, this.x, this.y, this.width, this.height);
}
outOfBounds() {
if (this.y > 650) {
return true;
}
}
hit(o) {
let ol = o.x;
let or = o.x + o.width;
let ot = o.y;
let ob = o.y + o.height;
let el = this.x;
let er = this.x + this.width;
let et = this.y;
let eb = this.y + this.height;
if (ol > er || or < el || ot > eb || ob < et) {
return false;
} else {
return true;
}
}
// collide() {
// this.life--;
// if (this.life === 0) {
// this.live = false;
// score += this.score;
// }
// }
}
//
//初始化一個子彈類
class Bullet {
constructor(config, x, y) {
this.img = config.img;
this.width = config.width;
this.height = config.height;
this.x = x;
this.y = y;
this.destory = false;
}
//子彈繪製方法
paint(context) {
context.drawImage(this.img, this.x, this.y);
}
//移動子彈 this.y--
move() {
this.y -= 8;
}
outOfBounds() {
//如果返回的是真的話 那麼我們應該銷毀掉這個子彈
return this.y < -this.height;
}
collide() {
//讓這顆子彈變成可銷毀狀態
this.destory = true;
}
}
//
// 初始化一個敵機類
class Enemy {
constructor(config) {
this.type = config.type;
this.width = config.width;
this.height = config.height;
this.x = Math.floor(Math.random() * (480 - config.width));
this.y = -config.height;
this.life = config.life;
this.score = config.score;
this.frame = config.frame;
this.img = this.frame.live[0];
this.live = true;
this.speed = Math.floor(Math.random() * (config.minSpeed - config.maxSpeed + 1)) + config.maxSpeed;
this.lastTime = new Date().getTime();
this.deathIndex = 0;
this.destory = false;
}
move() {
const currentTime = new Date().getTime();
if (currentTime - this.lastTime >= this.speed) {
if (this.live) {
this.img = this.frame.live[0];
this.y++;
this.lastTime = currentTime;
} else {
this.img = this.frame.death[this.deathIndex++];
if (this.deathIndex === this.frame.death.length) {
this.destory = true;
}
}
}
}
paint(context) {
context.drawImage(this.img, this.x, this.y);
}
outOfBounds() {
if (this.y > 650) {
return true;
}
}
hit(o) {
let ol = o.x;
let or = o.x + o.width;
let ot = o.y;
let ob = o.y + o.height;
let el = this.x;
let er = this.x + this.width;
let et = this.y;
let eb = this.y + this.height;
if (ol > er || or < el || ot > eb || ob < et) {
return false;
} else {
return true;
}
}
collide() {
this.life--;
if (this.life === 0) {
this.live = false;
score += this.score;
}
}
}
//
// 初始化一個英雄類
class Hero {
constructor(config) {
this.width = config.width;
this.height = config.height;
this.x = (480 - config.width) / 2;
this.y = 650 - config.height;
this.frame = config.frame;
this.frameLiveIndex = 0;
this.frameDeathIndex = 0;
this.lastTime = new Date().getTime();
this.speed = config.speed;
//當前展示的圖片
this.img = null;
this.live = true;
//子彈上次射擊的時間
this.lastShootTime = new Date().getTime();
//子彈射擊的間隔
this.shootInterval = 50;
//子彈夾數組
this.bulletList = [];
this.destory = false;
}
judge() {
const currentTime = new Date().getTime();
if (currentTime - this.lastTime > this.speed) {
if (this.live) {
this.img = this.frame.live[this.frameLiveIndex++ % this.frame.live.length];
} else {
//0 1 2 3 4
this.img = this.frame.death[this.frameDeathIndex++];
//到4的時候英雄死了
if (this.frameDeathIndex === this.frame.death.length) {
this.destory = true;
}
}
this.lastTime = currentTime;
}
}
paint(context) {
context.drawImage(this.img, this.x, this.y, this.width, this.height);
}
//英雄可以射擊子彈
shoot() {
//獲取當前時間
const currentTime = new Date().getTime();
//飛機的位置
if (currentTime - this.lastShootTime > this.shootInterval) {
//在飛機的頭部初始化一個子彈對象
let bullet = new Bullet(BULLET, this.x + this.width / 2 - BULLET.width / 2, this.y - BULLET.height);
//英雄飛機要認領這個子彈
this.bulletList.push(bullet);
//在網頁上繪製一個子彈對象
bullet.paint(context);
//更新英雄射擊時間
this.lastShootTime = currentTime;
}
}
collide() {
//將活著標識符切換為false
//活著 -> 爆炸中 -> 死亡(銷毀)
this.live = false;
}
}
//
// 初始化一個飛機界面載入類
class Loading {
constructor(config) {
this.frame = config.frame;
this.frameIndex = 0;
this.width = config.width;
this.height = config.height;
this.x = config.x;
this.y = config.y;
this.speed = config.speed;
this.lastTime = new Date().getTime();
}
judge() {
const currentTime = new Date().getTime();
if (currentTime - this.lastTime > this.speed) {
this.frameIndex++;
if (this.frameIndex === 4) {
state = RUNNING;
}
this.lastTime = currentTime;
}
}
paint(context) {
context.drawImage(this.frame[this.frameIndex], this.x, this.y);
}
}
class Main {
//一下全為全局變數或方法 (全局的!!)
//初始化一個天空實例
//主啟動方法
maingame() {
const sky = new Sky(SKY);
//初始化一個飛機界面載入實例
const loading = new Loading(LOADING);
//初始化一個英雄實例 英雄是會變的
let hero = new Hero(HERO);
//該變數中有所有的敵機實例
let enemies = [];
//該變數中存放所有的獎勵實例
let awards = [];
//敵機產生的速率
let ENEMY_CREATE_INTERVAL = 800;
let ENEMY_LASTTIME = new Date().getTime();
function stateControl() {
//為canvas綁定一個點擊事件 且他如果是START狀態的時候需要修改成STARTING狀態
canvas.addEventListener("click", () => {
if (state === START) {
state = STARTING;
}
});
// 為canvas綁定一個滑鼠移動事件 滑鼠正好在飛機圖片的正中心
canvas.addEventListener("mousemove", (e) => {
let x = e.offsetX;
let y = e.offsetY;
hero.x = x - hero.width / 2;
hero.y = y - hero.height / 2;
});
// 為canvas綁定一個滑鼠離開事件 滑鼠離開時 RUNNING -> PAUSE
canvas.addEventListener("mouseleave", () => {
if (state === RUNNING) {
state = PAUSE;
}
});
// 為canvas綁定一個滑鼠進入事件 滑鼠進入時 PAUSE => RUNNING
canvas.addEventListener("mouseenter", () => {
if (state === PAUSE) {
state = RUNNING;
}
});
//為canvas綁定一個屏幕移動觸摸點事件 觸碰點正好在飛機圖片的正中心
canvas.addEventListener("touchmove", (e) => {
// let x = e.pageX;
// let y = e.pageY;
console.log(e);
// let x = e.touches[0].clientX;
// let y = e.touches[0].clinetY;
let x = e.touches[0].pageX;
let y = e.touches[0].pageY;
// let x = e.touches[0].screenX;
// let y = e.touches[0].screenY;
let write1 = (document.body.clientWidth - 480) / 2;
let write2 = (document.body.clientHeight - 650) / 2;
hero.x = x - write1 - hero.width / 2;
hero.y = y - write2 - hero.height / 2;
// hero.x = x - hero.width / 2;
// hero.y = y - hero.height / 2;
console.log(x, y);
console.log(document.body.clientWidth, document.body.clientHeight);
e.preventDefault(); // 阻止屏幕滾動的預設行為
})
}
stateControl();
// 碰撞檢測函數
//此處的碰撞檢測包括
//1.子彈與敵機的碰撞
//2.英雄與敵機的碰撞
//3.英雄與隨機獎勵的碰撞
function checkHit() {
// 遍歷所有的敵機
for (let i = 0; i < awards.length; i++) {
//檢測英雄是否碰到獎勵類
if (awards[i].hit(hero)) {
//當然了,這個隨機獎勵的樣式也要刪了
awards.splice(i, 1);
//清除所有的敵機
// for (let i = 0; i < enemies.length; i++) {
// enemies.splice(i, 1);
// }
enemies.length = 0;
}
}
for (let i = 0; i < enemies.length; i++) {
//檢測英雄是否撞到敵機
if (enemies[i].hit(hero)) {
//將敵機和英雄的destory屬性改為true
enemies[i].collide();
hero.collide();
}
for (let j = 0; j < hero.bulletList.length; j++) {
enemies[i].hit(hero.bulletList[j]);
//檢測子彈是否撞到敵機
if (enemies[i].hit(hero.bulletList[j])) {
//將敵機和子彈的destory屬性改為true
enemies[i].collide();
hero.bulletList[j].collide();
}
}
}
}
// 全局函數 隔一段時間就來初始化一架敵機/獎勵
function createComponent() {
const currentTime = new Date().getTime();
if (currentTime - ENEMY_LASTTIME >= ENEMY_CREATE_INTERVAL) {
let ran = Math.floor(Math.random() * 100);
if (ran < 55) {
enemies.push(new Enemy(E1));
} else if (ran < 85 && ran > 55) {
enemies.push(new Enemy(E2));
} else if (ran < 95 && ran > 85) {
enemies.push(new Enemy(E3));
} else if (ran > 95) {
awards.push(new award(C1));
}
ENEMY_LASTTIME = currentTime;
}
}
// 全局函數 來判斷所有的子彈/敵人組件 "負責移動"
function judgeComponent() {
for (let i = 0; i < hero.bulletList.length; i++) {
hero.bulletList[i].move();
}
for (let i = 0; i < enemies.length; i++) {
enemies[i].move();
}
for (let i = 0; i < awards.length; i++) {
awards[i].move();
}
}
// 全局函數 來繪製所有的子彈/敵人組件 繪製score&life面板
function paintComponent() {
for (let i = 0; i < hero.bulletList.length; i++) {
hero.bulletList[i].paint(context);
}
for (let i = 0; i < enemies.length; i++) {
enemies[i].paint(context);
}
for (let i = 0; i < awards.length; i++) {
awards[i].paint(context);
}
context.font = "20px 微軟雅黑";
context.fillStyle = "green";
context.textAlign = "left";
context.fillText("score: " + score, 10, 20);
context.textAlign = "right";
context.fillText("life: " + life, 480 - 10, 20);
//重置樣式
context.fillStyle = "black