egret製作猜拳遊戲 fingerGuessing

fingerGuessing


項目介紹

fingerGuessing:一款有趣的猜拳遊戲,基於egret白鷺遊戲引擎開發(ps:遊戲中試玩榜後臺數據均爲隨機虛假)。

演示視頻

演示視頻地址

部分截圖

項目功能結構

項目運行


$ egret build xxx(項目名)

$ egret startserver xxx(項目名) -a

項目搭建教程

該項目主要分爲4個頁面

1. 開始頁面

創建bitmap對象,做背景圖和位圖的繪製,涉及到2個按鈕(開始遊戲按鈕、試玩榜按鈕)的事件監聽(touch_begin、touch_end、touch_release_outside)。

startBtn涉及dispatchEvent,下面篇幅會有詳解

2.遊戲頁面

  • 背景和相關位圖繪製
    • 出拳tween動畫處理 + random出拳
    • 根據所剩時間,動態調整出拳動畫頻率
  • timerPanel.ts 用於控制遊戲倒計時
    • 倒計時欄目UI繪製
    • listen TimerEvent中的timer和timer——complete,做倒計時數字的update
    • 將default_time存入localStorage中,用於game頁面使用
  • 猜況按鈕邏輯處理
    • 根據出拳情況,用戶判斷那邊贏(左邊贏、打平、右邊贏)
    • 爲了精簡代碼,抽象一個commonCallback,用於left、middle和right使用

3.結束頁面

  • 位圖繪製
    • 抽象出commonImgConf函數,來繪製多個按鈕
  • 按鈕事件派發、處理
    • 抽出commonCallback,分別控制3個gameEvent

4.試玩榜頁面

  • 返回按鈕事件派發
  • 文本繪製
  • 抽出commonTxt用於控制多段文本繪製
    private commonTxt(size: number, isCenter: boolean, isAssignment: boolean ,y: number, x?: number) {
        const txt = new egret.TextField();
        txt.width = egret.MainContext.instance.stage.stageWidth;
        this.addChild(txt);
        txt.y = y;
        if (x) txt.x = x;
        txt.textAlign = isCenter == true ? egret.HorizontalAlign.CENTER : egret.HorizontalAlign.LEFT;
        txt.size = size;
        txt.textColor = 0xffffff;
        isAssignment ? this.txt = txt : txt.text = "試玩榜";
    }
  • 請求發送和請求解析
    • onComplete() + onGetComplete()
    private onComplete(): void {
        const url: string = "xxxx";
        const loader: egret.URLLoader = new egret.URLLoader();

        loader.dataFormat = egret.URLLoaderDataFormat.TEXT;
        loader.addEventListener(egret.Event.COMPLETE, this.onGetComplete, this);

        const request: egret.URLRequest = new egret.URLRequest(url);
        request.method = egret.URLRequestMethod.GET;
        loader.load(request);
    }
    private onGetComplete(event: egret.Event):void {
        const loader:egret.URLLoader = <egret.URLLoader> event.target;
        let data:egret.URLVariables = loader.data;
        // 採用js解析方法
        let res = eval("("+data.toString()+")");
        ... // TODO: 其他處理
}
  • easy-mock數據格式:
{
  "data": {
    "array|5-9": [{
      "score": "@integer(1,100)",
      "name": "@cname"
    }]
  }
}

3.發佈遊戲

當整個遊戲製作完成後,我們需要將它進行發佈,這裏只涉及H5方面發佈。

一句命令行而已的事:

參數說明:

egret publish [project_name] [--version [version]] [--runtime html5|native] [--passWorld]


$ egret publish xxx(項目名) --version 0.03(版本號)

發佈項目,如果是在項目文件夾下編譯,可以不加項目名稱

打包成功後,會出現一個bin-release的文件夾

然後把我框出來的1903....這個文件夾裏面的東西,拖到服務器上,然後對應訪問,就可以啦。

敲黑板劃重點

如果說,我們平時在自我測試環節,起一個本地服務,然後手機在同一個網絡下訪問本機ip地址就可以

這樣應該會比較方便,簡單介紹一下這個步驟:

簡單的http服務

首先安裝http-server模塊。


$ npm i -g http-server

運行發佈完成的包


$ cd bin-release/web/190311152632

$ ls

index.html  js  manifest.json  resource

$ hs -p 8088

Starting up http-server, serving ./

Available on:

 http://127.0.0.1:8088

 http://192.168.1.100:8088

Hit CTRL-C to stop the server

最後一級文件夾名字其實是時間戳。

這樣我們便可以在pc瀏覽器或者手機瀏覽器中查看遊戲了。


項目細究

>=2次使用,或有大部分common的,都抽象出來使用,儘量嘗試用新語法來簡化代碼量

今日給自己灌的雞湯:代碼應該是一日寫的比一日優雅纔行,一個需求應思考多種寫法,再找到最優解

精簡繁瑣for循環

在判斷猜況的時候,需要使用到多重嵌套for循環,看着特別噁心,後面採用了這種方法用於簡化:

        const actions = () => {
            const answerFalse = () => { this.answer_type = false };
            const addScore = () => { this.score++; this.answer_type = true };
            return new Map([
                [{ left_type: 0, right_type: 0 }, answerFalse],
                [{ left_type: 0, right_type: 1 }, answerFalse],
                [{ left_type: 0, right_type: 2 }, addScore],
                [{ left_type: 1, right_type: 0 }, addScore],
                [{ left_type: 1, right_type: 1 }, answerFalse],
                [{ left_type: 1, right_type: 2 }, answerFalse],
                [{ left_type: 2, right_type: 0 }, answerFalse],
                [{ left_type: 2, right_type: 1 }, addScore],
                [{ left_type: 2, right_type: 2 }, answerFalse]
            ])
        }
            let action = [...actions()].filter(([key, value]) => (
                key.left_type == this.left_type && key.right_type == this.right_type
            ));
            action.forEach(([key, value]) => value.call(this));

Egret核心顯示類

需要重新梳理一下這些類,以便下次寫的時候,有點混

描述
DisplObject 顯示對象基類,所有顯示對象均繼承自此類
DisplObjectContainer 顯示對象容器接口,所有顯示對象容器均實現此接口
Bitmap 位圖,用來顯示圖片
Shape 用來顯示矢量圖,可以用裏面的方法繪製矢量圖形
TextField 文本類
BitmapText 位圖文本類
Sprite 帶有矢量繪製功能的顯示容器
Stage 舞臺類

這裏面的startscene,使用繼承sprite,而沒用displaObjeContainer,在這裏簡單列出它們之間的繼承關係:
egret.Sprite -> egret.DisplayObjectContainer -> egret.DisplayObject

同時,提一點:shape與sprite的diff:

  • shape:顯示對象,一般用於繪製圖形
  • sprite:顯示容器,在此基礎上可以添加子容器 + 顯示對象,一般用於create可以裝載content的遊戲層或者遊戲對象

http://blog.sina.com.cn/s/blog_6b80d2ca0102vjx4.html

Egret中事件處理

egret中的事件機制是一套比較標準的事件處理架構。

簡單來說,數據的提供者只需要在意發出數據對象,以及確保數據對象是egret.event類或者子類的實例即可,這種數據對象,也就是事件event

我們在遊戲中涉及到按鈕的一些交互效果,比如:點擊開始遊戲,進入遊戲頁面;點擊Rank icon,彈出rank的彈框。

以上這些都會涉及倒egret的事件處理,我先簡單羅列事件處理的一個基本流程:

  • step1: 註冊偵聽器listener
  • step2:發送事件
  • step3:偵聽事件
  • step4:移除事件偵聽器

這裏我用startScene中點擊開始遊戲的事件處理,做一個簡單事例:

startscene .ts:

// init fn
private init(){
  ...
  // 註冊listener 
  rank_btn.addEventListener(egret.TouchEvent.TOUCH_BEGIN, this.rank_btnCallback, this);
  ...
}

// start_btnCallback fn
private start_btnCallback(evt:egret.TouchEvent):void {
  ...
  //  派發事件
  let event:GameEvent = new GameEvent(GameEvent.GAME_GO);
  this.dispatchEvent(event);
  ...
}

main.ts:

private createGameScene() {
  ....   
  //偵聽事件
  startScene.addEventListener(GameEvent.GAME_GO, this.go, this);
  startScene.addEventListener(GameEvent.GAME_BLEED, this.startrank, this);
}
private go(){
  // 手動回收偵聽器
  this.removeEventListener(GameEvent.GAME_GO, this.go, this);
  this.removeChildren();
  ...
}

注意:這裏存在一個問題:

在顯示對象容器裏的子顯示對象上增加了事件偵聽器(addEventListener),如果通過父級顯示對象的removeChild移除了這個容器,那麼有必要在egret.Event.REMOVED_FROM_STAGE事件裏手動刪除所有子對象註冊的事件偵聽器嗎(removeEventListener)?還是說引擎會自動處理?

答案是:如果沒有引用的話就會回收,但是爲了保證一定回收最好手動移除,不然可能造成內存泄露

Egret支持ES6

在egret中默認設置是es5,所有很多es6的語法是不支持的,比如map這些...
yiwen那麼如何更改配置項,讓其支持es6語法呢?

step1:

找到根目錄下的tsconfig.json文件,這是默認生成的:

{
    "compilerOptions": {
        "target": "es5",
        "outDir": "bin-debug",
        "experimentalDecorators": true,
        "lib": [
            "es5",
            "dom",
            "es2015.promise"
        ],
        "types": []
    },
    "include": [
        "src",
        "libs"
    ]
}

step2:

  • 將lib下的es5改成es2015,也就是es6
  • 在compilerOptions下加一行"downlevelIteration": true,,用於降級迭代器

更改後是這樣:

{
    "compilerOptions": {
        "target": "es5",
        "outDir": "bin-debug",
        "experimentalDecorators": true,
        "downlevelIteration": true,
        "lib": [
            "es2015",
            "dom",
            "es2015.promise"
        ],
        "types":[]
    },
    "exclude": [
        "node_modules"
    ]
}

step 3:

由於配置項更改了,需要重新build和run,再次執行:

> $ egret build xxx
> $ egret startserver xxx -a

之前...由於一些原因,本地的lib和遠程github上的lib衝突了,導致項目跑不起來,然而各種...奇怪的方法嘗試,最後找到問題,本地解決了,發現...無法把本地的lib推到遠程的lib上...然和各種奇怪,反正那啥最後就強制了一把:git push -f ... 唉唉 真是粗暴

項目源碼

代碼中寫了很多註釋,基本都能看懂就不再贅述啦~

源碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章