H5 Flappy Bird 游戲制作 (3)

前言

上期捷绑,我們實現(xiàn)了 canvas, Background 模塊淳玩,這期我們將實現(xiàn) BirdStickManager 模塊抵拘。

Bird

Bird 也是一個可移動的模塊陵珍,但是和之前的 Background 的移動模式不同寝杖,Bird 的移動是有加速度的。

另外的不同的一點是互纯,Bird 是一個需要考慮碰撞的模型:

  • 邊界碰撞瑟幕。碰到頂部和底部,游戲結(jié)束留潦。
  • 柱子碰撞只盹。碰到柱子,游戲結(jié)束兔院。

我們把 Bird 簡化成一個橢圓模型殖卑,這樣計算碰撞會簡單很多。

// 依然引入畫布
import canvas, { ctx } from './canvas';
// 向前飛的圖片坊萝,在速度為 0 的時候用
import forwardImage from './bird-forward.png';
// 向上飛的圖片孵稽,在速度小于 0 的時候用
import upImage from './bird-up.png';
// 向下飛的圖片许起,在速度大于 0 的時候用
import downImage from './bird-down.png';
// 移動模型的基礎(chǔ)類
import Movable from './Movable';

export default class Bird extends Movable {
    constructor() {
        // 調(diào)用父類的構(gòu)造函數(shù),初始化基礎(chǔ)的位置和速度屬性
        super();
        const { width, height } = canvas.getSize();
        // 圓心菩鲜,我們把 `Bird` 固定在這個位置园细,移動背景來讓玩家覺得鳥在向前飛行
        this.x = 200;
        // 初始的高度位置在屏幕中心
        this.y = height / 2;
        // 圖片素材和真實展示的像素的比例
        const ratio = 5;
        // 鳥的模型
        // 橢圓的橫軸長度
        this.rx = 17 * ratio; // 17 * 12
        // 橢圓的縱軸長度
        this.ry = 12 * ratio;
        // 初始的縱向速度,會在每次 `move` 時疊加差值
        this.vy = 100;
        // 重力加速度接校,不變
        this.g = 2000;

        // 加載圖像素材
        const forward = new Image();
        forward.src = forwardImage;
        const up = new Image();
        up.src = upImage;
        const down = new Image();
        down.src = downImage;
        this.image = { forward, up, down };
    }

    move() {
        // 調(diào)用父類的移動函數(shù)猛频,并且拿到時間差,用來計算新的縱向速度
        const { diff } = super.move();
        this.vy = this.vy + (this.g * diff / 1000);
    }

    moveUp() {
        // 當(dāng)用戶點擊屏幕時馅笙,將鳥的縱向速度設(shè)置成向上飛的一個速度
        this.vy = -500;
    }

    getYState() {
        // 根據(jù)縱向速度伦乔,判斷這時需要使用什么圖片
        if (this.vy > 0) {
            return 'up';
        }
        if (this.vy < 0) {
            return 'down';
        }
        return 'forward';
    }

    getImage() {
        // 獲取展示的圖片
        const state = this.getYState();
        return this.image[state];
    }

    paint() {
        // 畫個鳥厉亏,鳥的坐標(biāo) x 和 y 表示的是中心點
        ctx.drawImage(this.getImage(), this.x - (this.rx / 2), this.y - (this.ry / 2), this.rx, this.ry);
    }

}

修改 GameManagerpaint 函數(shù)董习,添加 Bird 的應(yīng)用:

paint() {
    canvas.clear();
    this.background.move();
    this.background.paint();
    this.bird.move();
    this.bird.paint();
}

這里需要注意繪制的順序,先畫 Background爱只,再畫 Bird皿淋,否則 Bird 會被 Background 覆蓋掉。

可以看到這個 Bird 直接掉了下去恬试。之后我們會添加用戶的交互窝趣,來讓它重新飛起來。

StickManager

這個類會管理所有的柱子训柴,所以我們還需要添加一個類:Stick 來表示一組柱子哑舒,包括一上一下的兩個柱子:

import canvas, { ctx } from './canvas';
// 上部分的那根柱子的圖片素材
import stickUpImageSrc from './stick-up.png';
// 下部分的那根柱子的圖片素材
import stickDownImageSrc from './stick-down.png';

import Movable from './Movable';

export default class Stick extends Movable {

    constructor(x) {
        // 在使用柱子的時候,我們會從外面?zhèn)魅胫拥臋M坐標(biāo)
        super();
        // 加載圖片素材
        const stickUpImage = new Image();
        stickUpImage.src = stickUpImageSrc;
        const stickDownImage = new Image();
        stickDownImage.src = stickDownImageSrc;
        this.image = { up: stickUpImage, down: stickDownImage };

        // 簡單地設(shè)置上下兩根柱子之間的空隙的高度
        this.gap = 400;
        // 設(shè)置柱子的橫向速度
        this.vx = -500;
        const { width, height } = canvas.getSize();

        // 圖片素材和實際展示的像素大小的比例關(guān)系
        const ratio = 5;
        this.width = {
            up: 26 * ratio, // 26 * 135
            down: 26 * ratio, // 26 * 121
        };
        this.height = {
            up: 135 * ratio,
            down: 121 * ratio,
        };
        // 初始的柱子的位置幻馁。橫坐標(biāo)和縱坐標(biāo)表示上下兩根柱子之間的空隙的中心位置
        this.x = x;
        this.y = height / 2 + (Math.random() * 400 - 200);
    }

    paint() {
        // 畫出上下兩根柱子
        ctx.drawImage(this.image.up, this.x - this.width.up / 2, this.y - this.gap / 2 - this.height.up, this.width.up, this.height.up);
        ctx.drawImage(this.image.down, this.x - this.width.down / 2, this.y + this.gap / 2, this.width.down, this.height.down);
    }

    isOutOfScreen() {
        // 提供方法來判斷柱子是否已經(jīng)移出了屏幕
        return this.x + this.width.up / 2 < 0 && this.x + this.width.down / 2 < 0;
    }

}

然后在 StickManager 中使用:

import canvas from './canvas';
import Stick from './Stick';

export default class StickManager {

    constructor() {
        // 當(dāng)前屏幕內(nèi)的 `Stick` 列表
        this.sticks = [];
        const { width } = canvas.getSize();
        // 初始時添加三根柱子洗鸵,分別在屏幕最右側(cè),最右側(cè)的 1.5 倍位置和最右側(cè)的 2 倍位置
        this.create(width);
        this.create(width + width / 2);
        this.create(width * 2);
    }

    getSticks() {
        // 對外提供獲取 sticks 的方法
        return this.sticks;
    }

    create(x) {
        // 創(chuàng)建 `Stick`仗嗦,并記錄到列表中
        this.sticks.push(new Stick(x));
    }

    move() {
        // 移動每根柱子膘滨,并且判斷是否柱子已經(jīng)移動到屏幕外面了,如果移出了屏幕稀拐,則從列表中去除火邓。這里是用了在 `Stick` 中定義的 `isOutOfScreen` 方法
        this.sticks = this.sticks.filter((stick) => {
            stick.move();
            return !stick.isOutOfScreen();
        });
        // 保持游戲中有三根柱子,如果列表長度小于 3德撬,則新增一根柱子
        if (this.sticks.length < 3) {
            const { width } = canvas.getSize();
            this.create(width * 3 / 2);
        }
    }

    paint() {
        // 畫出列表中的每根柱子
        return this.sticks.map((stick) => {
            return stick.paint();
        });
    }

}

修改 GameManagerpaint 函數(shù)铲咨,添加 StickManager 的應(yīng)用:

paint() {
    canvas.clear();
    this.background.move();
    this.background.paint();
    this.sticks.move();
    this.sticks.paint();
    this.bird.move();
    this.bird.paint();
}

可以看到柱子能正確地移動和生成。

TBC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜓洪,一起剝皮案震驚了整個濱河市鸣驱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝠咆,老刑警劉巖踊东,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件北滥,死亡現(xiàn)場離奇詭異,居然都是意外死亡闸翅,警方通過查閱死者的電腦和手機(jī)措近,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門兼蜈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事扭勉。” “怎么了痊夭?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵旱捧,是天一觀的道長。 經(jīng)常有香客問我液南,道長壳猜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任滑凉,我火速辦了婚禮统扳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘畅姊。我一直安慰自己咒钟,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布若未。 她就那樣靜靜地躺著朱嘴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪粗合。 梳的紋絲不亂的頭發(fā)上萍嬉,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音舌劳,去河邊找鬼帚湘。 笑死,一個胖子當(dāng)著我的面吹牛甚淡,可吹牛的內(nèi)容都是我干的大诸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼贯卦,長吁一口氣:“原來是場噩夢啊……” “哼资柔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起撵割,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤贿堰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后啡彬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羹与,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡故硅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纵搁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吃衅。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖腾誉,靈堂內(nèi)的尸體忽然破棺而出徘层,到底是詐尸還是另有隱情,我是刑警寧澤利职,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布趣效,位于F島的核電站,受9級特大地震影響猪贪,放射性物質(zhì)發(fā)生泄漏跷敬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一哮伟、第九天 我趴在偏房一處隱蔽的房頂上張望干花。 院中可真熱鬧妄帘,春花似錦楞黄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至致盟,卻和暖如春碎税,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背馏锡。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工雷蹂, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杯道。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓匪煌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親党巾。 傳聞我的和親對象是個殘疾皇子萎庭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • 前言 上期,我們介紹了 canvas, GameManager 模塊齿拂,這期我們實現(xiàn)一下上次拆分的 canvas 和...
    vivaxy閱讀 734評論 0 1
  • 前言 上期驳规,我們實現(xiàn)了 Bird, StickManager 模塊,這期我們將實現(xiàn) Input署海,碰撞檢測和完成整個...
    vivaxy閱讀 721評論 0 0
  • 傍晚的晚霞映襯 卻只在水的波紋蕩漾 此水無舟 唯有月相照 裙擺的...
    琉璃盞中顏如玉閱讀 310評論 0 8
  • 人簡單就快樂吗购,但快樂的人寥寥無幾医男,是哭是笑只有你自己知道,該哭該笑只有你自己明了捻勉,有些事昨登,發(fā)生了就只能接受,有些人...
    Mr歐先生閱讀 287評論 1 0