Vue3+Canvas實現(xiàn)坦克大戰(zhàn)游戲(二)
前言
接著上篇講,本篇主要給大家講解一下子彈擊中物體、物體銷毀、敵方坦克構(gòu)建生成、運(yùn)動算法、爆炸效果、以及障礙物的生成;了解了這些我相信你可以不依賴游戲引擎實現(xiàn)大部分小游戲的開發(fā)。
W/上 S/下 A/左 D/右 F/射擊
讓我們開始吧!
敵方坦克的生成
我們可以使用 for 循環(huán)和Tank 構(gòu)造函數(shù),批量制造多個敵方坦克,x,y 為其在畫布中的坐標(biāo),direction 為坦克當(dāng)前方向,type 為精靈圖中截取坦克圖片的信息,包含了坐標(biāo),尺寸,類型等
for(let t=0; t<20; t++) {
let tank = new Tank(new TankConfig({
x: 100 + t*30,
y: 100,
direction: DIRECTION.DOWN,
type: TANK_TYPE.ENEMY0,
is_player: 0
}))
tank.star();
}
ENEMY0: {
type: 'ENEMY1',
dimension: [30, 30],
image_coordinates: [129, 1, 129, 33]
}坦克移動的算法
emove 函數(shù)就是就是物體移動狀態(tài)下每幀都會執(zhí)行的函數(shù),將根據(jù)當(dāng)前方向修改下幀的坐標(biāo),當(dāng)下幀坐標(biāo)到達(dá)地圖邊緣時將執(zhí)行 dir 函數(shù)更新坦克方向,如果是子彈則將銷毀。
this.emove = function (d, obstacle_sprites) {
this.direction = d
switch (d) {
case DIRECTION.UP:
if (
(obstacle_sprites && !this.checkRangeOverlap(obstacle_sprites)) ||
!obstacle_sprites
) {
this.y -= this.speed
if (this.y <= 10) {
this.dir()
}
} else {
this.dir()
}
break
...dir 函數(shù)用來隨機(jī)修改移動的方向,當(dāng)然這個函數(shù)不能每幀都調(diào)用,否則坦克將像無頭蒼蠅一樣了;那么什么時候需要修改方向呢,我們認(rèn)為當(dāng)坦克和物體相撞的時候或者到達(dá)地圖邊緣時修改方向是合理的;子彈也可以認(rèn)為是一個物體,所以子彈到達(dá)坦克周邊一定距離時也將引起坦克規(guī)避動作,也就是將觸發(fā)dir 函數(shù)。
this.dir = function () {
if(Math.floor(Math.random()*10) === 0 || Math.floor(Math.random()*10) === 1) {
this.direction = DIRECTION.UP;
};
if(Math.floor(Math.random()*10) === 2 || Math.floor(Math.random()*10) === 3) {
this.direction = DIRECTION.DOWN;
};
if(Math.floor(Math.random()*10) === 4 || Math.floor(Math.random()*10) === 5) {
this.direction = DIRECTION.LEFT;
};
if(Math.floor(Math.random()*10) === 6 || Math.floor(Math.random()*10) === 7) {
this.direction = DIRECTION.RIGHT;
};
if(Math.floor(Math.random()*10) === 8 || Math.floor(Math.random()*10) === 9) {
this.dir();
}
}
子彈擊中物體的算法
我們定義 checkRangeOverlap 函數(shù)來判斷兩個物體是否相撞,uiobjs 為畫布中所有的元素對象列表,包含 坦克、子彈、障礙物等;
this.getFrontPoints() 函數(shù)將根據(jù)當(dāng)前方向返回包含三個頂端點坐標(biāo)數(shù)組,形成判斷區(qū)域;
uiobjs[o].getCornerPoints() 函數(shù)返回當(dāng)前元素的四個邊角坐標(biāo)數(shù)組,形成判斷區(qū)域;
然后getFrontPoints() 的三個點坐標(biāo) 將分別和 uiobjs[o].getCornerPoints() 的四邊坐標(biāo)進(jìn)行點對點判斷,根據(jù)x, y 數(shù)值滿足下方四個條件時則認(rèn)為此點坐標(biāo)在元素內(nèi)部。
// 判斷點坐標(biāo)是否在區(qū)域內(nèi)部,以識別是否相撞
CanvasSprite.prototype.checkRangeOverlap = function (uiobjs) {
for (let o in uiobjs) {
let obstacle_cp = uiobjs[o].getCornerPoints()
let fp = this.getFrontPoints()
for (let idx = 0; idx < fp.length; idx++) {
if (
fp[idx].x > obstacle_cp[0].x &&
fp[idx].x < obstacle_cp[1].x &&
fp[idx].y > obstacle_cp[0].y &&
fp[idx].y < obstacle_cp[2].y
) {
return true
}
}
}
return false
}
// 返回當(dāng)前物體頂端三坐標(biāo)
CanvasSprite.prototype.getFrontPoints = function () {
let front_point
switch (this.direction) {
case DIRECTION.UP:
front_point = [
new Point(this.x, this.y),
new Point((2 * this.x + this.width) / 2, this.y),
new Point(this.x + this.width, this.y),
]
break
... 縮略,下左右方向
}
return front_point
}// 返回元素四邊坐標(biāo)形成的矩形區(qū)域范圍
CanvasSprite.prototype.getCornerPoints = function () {
var corner_points
switch (this.direction) {
case DIRECTION.UP:
corner_points = [
new Point(this.x, this.y),
new Point(this.x + this.width, this.y),
new Point(this.x + this.width, this.y + this.height),
new Point(this.x, this.y + this.height),
]
break
... 縮略,下左右方向
}
return corner_points
}
啊 坦克搞多了!但是可以看到子彈擊中效果是成功的,你發(fā)現(xiàn)了沒,擊中后有一個爆炸效果,這個是怎么實現(xiàn)的呢?
爆炸效果的實現(xiàn)
當(dāng)識別到擊中后將此坦克將觸發(fā)explode 爆炸效果,然后每幀 判斷 isDestroied 是否銷毀,后續(xù)每幀將 explosion_count++ 從 0 到 5,然后更改alive 狀態(tài)為0 代表已銷毀。
if (s instanceof Tank && s.alive && s.isDestroied()) {
s.explode()
}
this.isDestroied = function () {
return explosion_count > 0
}
this.explode = function () {
if (explosion_count++ === 5) {
this.alive = 0
}
}然后 explosion_count 的數(shù)值,從0 到 5 代表爆炸效果圖的5幀
this.getImg = function () {
if (explosion_count > 0) {
return {
width: TANK_EXPLOSION_FRAME[explosion_count].dimension[0],
height: TANK_EXPLOSION_FRAME[explosion_count].dimension[1],
offset_x: TANK_EXPLOSION_FRAME[explosion_count].image_coordinates[0],
offset_y: TANK_EXPLOSION_FRAME[explosion_count].image_coordinates[1],
}
} else {
return {
width: width,
height: height,
offset_x: this.type.image_coordinates[0],
offset_y: this.type.image_coordinates[1],
}
}
}
到現(xiàn)在我們的坦克游戲已經(jīng)基本可玩了,只不過現(xiàn)在是一片大平原,毫無遮攔,我們該為畫布增加一些障礙物如墻體,石頭等,增加游戲可玩性。
生成障礙物(石墻、磚墻等)
有了之前tanke 和 子彈的構(gòu)建函數(shù),現(xiàn)在這個 Block 就簡單多了,只需要定義好基本的信息,如坐標(biāo),寬高、狀態(tài)、方向,然后借用 apply 來使用 CanvasSprite 上的通用方法。
let Block = function (x, y, direction, type) {
type = type || BLOCK_TYPE.BLOCK_BRICK
let alive = 1
let width = type.dimension[0]
let height = type.dimension[1]
let explosion_count = 0
this.type = type
this.x = x
this.y = y
this.genre = 'block'
this.direction = direction || DIRECTION.UP
CanvasSprite.apply(this, [
{
alive: 1,
border_y: HEIGHT,
border_x: WIDTH,
speed: 0,
direction: direction,
x: x,
y: y,
width: width,
height: height,
},
])
this.isDestroied = function () {
return explosion_count > 0
}
this.explode = function () {
if (explosion_count++ === 5) {
this.alive = 0
}
}
this.getImg = function () {
if (explosion_count > 0) {
return {
width: TANK_EXPLOSION_FRAME[explosion_count].dimension[0],
height: TANK_EXPLOSION_FRAME[explosion_count].dimension[1],
offset_x: TANK_EXPLOSION_FRAME[explosion_count].image_coordinates[0],
offset_y: TANK_EXPLOSION_FRAME[explosion_count].image_coordinates[1],
}
} else {
return {
width: width,
height: height,
offset_x: type.image_coordinates[0],
offset_y: type.image_coordinates[1],
}
}
}
this._generateId = function () {
return uuidv4()
}
sprites[this._generateId()] = this
}定義好后,使用時只需批量生成Block 的實例,將坐標(biāo),類型等信息傳遞進(jìn)去就可以在下一幀渲染出來。
for(let i=0; i<=2; i++) {
for(let j=0; j<=2; j++) {
let block = new Block(j*16, 140 + i*16, DIRECTION.UP, BLOCK_TYPE.BLOCK_STONE)
}
}好了我們看看最終的效果吧!

總結(jié)
ok 坦克大戰(zhàn)基本上完成了,你可以修改子彈發(fā)射速度,敵方坦克數(shù)量,障礙物也可以自己隨意畫,可玩性還是挺好的,當(dāng)然還有很多細(xì)節(jié)可以完善,如 預(yù)制幾種不同的地圖,做成通關(guān)類,預(yù)制幾條生命等。如果你想試一下,可以 star 下 github 倉庫自己來改造自己的坦克大戰(zhàn)吧。
以上就是Vue3+Canvas實現(xiàn)坦克大戰(zhàn)游戲(二)的詳細(xì)內(nèi)容,更多關(guān)于Vue3 Canvas坦克大戰(zhàn)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue elementUI table表格數(shù)據(jù) 滾動懶加載的實現(xiàn)方法
這篇文章主要介紹了vue elementUI table表格數(shù)據(jù)滾動懶加載的實現(xiàn)方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04
vue2?web多標(biāo)簽輸入框elinput是否當(dāng)前焦點詳解
這篇文章主要介紹了vue2?web多標(biāo)簽輸入框elinput是否當(dāng)前焦點的相關(guān)資料,講解了如何在產(chǎn)品中實現(xiàn)用戶輸入文字后按下回車鍵生成標(biāo)簽并顯示在頁面上的功能,通過組件的使用和改造,解決了輸入不連續(xù)的問題,需要的朋友可以參考下2025-01-01
Vue中實現(xiàn)深度監(jiān)聽的方法小結(jié)
在Vue中,深度監(jiān)聽是一種非常常見且重要的功能,它可以讓我們監(jiān)聽對象內(nèi)部的所有屬性,并對這些屬性的變化做出相應(yīng)的處理,在本篇博客中,我將為大家介紹Vue中如何實現(xiàn)深度監(jiān)聽的方法,需要的朋友可以參考下2024-09-09

