# 小朋友下樓梯 <style> .slide-number { font-size: 75% !important; } </style> --- # 簡報 ## [https://ntust.me/887yu](https://ntust.me/887yu) --- # 講師 - Pikachu ![](https://i.imgur.com/6bOrGl4.png) --- # [:smiling_imp:小朋友下樓梯:smiling_imp: ](http://codepen.io/pikapika/full/Lbdojm/) --- # Phaser ![](https://i.imgur.com/5mXdMEX.png) --- # Fork 程式碼 1. 登入/註冊 [codepen](http://codepen.io/) 2. [打開連結](http://codepen.io/pikapika/pen/bBLMOX?editors=1010) 2. 按右上方的 Fork 按鈕複製到自己的專案下 --- # 創造 Phaser 創造長 ==400== 寬 ==400== 的遊戲畫面,Phaser.AUTO 表示使用預設的繪圖方式,"game" 是告訴 Phaser 遊戲要放置在網頁的那個部分 ```javascript= var game = new Phaser.Game(400, 400, Phaser.AUTO, "game", { preload: preload, create: create, update: update }); ``` --- # 遊戲架構 **preload** 載入遊戲素材,例如圖片、聲音 **create** 遊戲一開始前初始化動作,只會執行一次 **update** 在遊戲進行中,會不斷的被執行 --- # 想想看 1. 創造場景和主角的程式要寫在那個函式? 1. 玩家死掉後會顯示 GameOver 的圖片,這張圖片創造的動作應該寫在? 2. 當我按下左鍵時主角要往左移動,偵測按鍵是否被按下的動作應該寫在? 4. 主角從左移動到右,改變位置的動作應該寫在? 3. 判斷主角是否撞到牆壁,偵測是否碰撞的動作應該放在? 6. 每隔一段時間就創造新的階梯,創造階梯的這個動作應該寫在? --- # preload baseURL 是載入資源的來源 http://s.ntustcoding.club/ns-shaft-workshop/player.png 載入這張圖片並命名為 player ```javascript= game.load.crossOrigin = 'anonymous'; game.load.baseURL = 'http://s.ntustcoding.club/ns-shaft-workshop/'; game.load.spritesheet('player', 'player.png', 32, 32); ``` --- # SpriteSheet spritesheet 和 image 的差異,就是一個 spritesheet 包含很多個分別的圖片,這樣的做法有助於減少儲存空間 [spritesheet](http://s.ntustcoding.club/ns-shaft-workshop/player.png) vs [image](http://s.ntustcoding.club/ns-shaft-workshop/normal.png) ```javascript= game.load.spritesheet('player', '/assets/player.png', 32, 48); game.load.image('normal', '/assets/block.png'); ``` --- # 創造 Sprite 在遊戲中任何的東西都可以稱為 Sprite (遊戲精靈) 試著用以下語法創造出主角 game.add.sprite( x座標, y座標, 圖片 ); ```javascript= player = game.add.sprite(200, 200, 'player'); ``` --- # 掛載物理引擎 啟用 player 的物理特性,例如移動、碰撞等,但如果只是想顯示一張圖片例如背景圖,在遊戲中不需要互動就不用掛載物理引擎 ```javascript= player = game.add.sprite(200, 200, 'player'); game.physics.arcade.enable(player); ``` --- ![](https://i.imgur.com/RxP01sw.jpg) --- # 座標點 sprite座標點在圖片的左上角 ![](https://i.imgur.com/lAfBwyq.jpg) --- # Sprite 座標屬性 取得 sprite 的當前位置 ```javascript= var x = player.body.x; var y = player.body.y; // 印出 x,y console.log(x, y); ``` 設定 sprite 的座標 ```javascript= player.body.x = 100; player.body.y = 100; ``` --- # 移動 每執行一次 update,就讓x座標加上 1 ```javascript= player.body.x += 1; ``` --- # 設定速度 設定角色的速度每秒移動X距離60 ```javascript= player.body.velocity.x = 60; ``` --- # 鍵盤事件 創造鍵盤 ```javascript= keyboard = game.input.keyboard.addKeys({ 'up': Phaser.Keyboard.UP, 'down': Phaser.Keyboard.DOWN, 'left': Phaser.Keyboard.LEFT, 'right': Phaser.Keyboard.RIGHT }); ``` 判斷某個鍵是否被按下 ```javascript= if(keyboard.left.isDown) { // 當左方向鍵按下時執行的動作... } if(keyboard.right.isDown) { // 當右方向鍵按下時執行的動作... } ``` --- # 任務1 * 按上下左右鍵時角色會移動 * 放開按鍵時角色停止 :point_right: [任務目標](http://codepen.io/pikapika/full/GNQdOB/) --- # spritesheet 用法 32, 32 就是裁切的長和寬,編號是從 0 開始 ```javascript= game.load.spritesheet('player', 'player.png', 32, 32); ``` ![](https://i.imgur.com/yQJVLnP.jpg) --- # 影格動畫 幫角色增加動畫 Sprite.animations.add(動畫名字, 影格, 每秒幀數) ```javascript= player.animations.add('left', [0, 1, 2, 3], 6); player.animations.add('right', [9, 10, 11, 12], 6); ``` 播放動畫 Sprite.play(動畫名字) ```javascript= player.animations.play('right'); ``` --- # 任務2 * 向左或向右移動時,播放相對應的動畫 :point_right:[任務目標](http://codepen.io/pikapika/full/pNaVOJ) --- # 設定外觀 設定外觀為圖片編號 1 的部分 ```javascript= player.frame = 1; ``` --- # 任務3 * 不移動時角色要呈現正面站立的樣子 [任務示範](http://codepen.io/pikapika/full/LbQmJe) --- # 作用域 作用域是什麼? ```javascript= var a = 10; function test1 () { var a = 100; console.log(a); } function test2 () { console.log(a); } test1(); // 印出 100 text2(); // 印出 10 ``` --- # 全域變數 ```javascript= var player; var keyboard; create() { player = game.add.sprite(200, 200, 'player'); keyboard = game.input.keyboard.addKeys({ 'left': Phaser.Keyboard.LEFT, 'right': Phaser.Keyboard.RIGHT }); } update() { if(keyboard.left.isDown) { player.body.velocity.x = -250; } if(keyboard.right.isDown) { player.body.velocity.x = 250; } } ``` --- # 任務4 * 增加左右兩邊的牆壁和上排的釘子 :point_right:[任務目標](http://codepen.io/pikapika/full/GNQdXw) --- # 設定牆壁 啟用物理引擎,immovable=true 表示設定牆壁為固定,可以嘗試設為 false 並在碰撞會發生? ```javascript= leftWall = game.add.sprite(0, 0, 'wall'); game.physics.arcade.enable(leftWall); // 設定固定屬性 leftWall.body.immovable = true; ``` --- # 碰撞 ==game.physics.arcade.collide(A, B)== 會判斷 A,B 是否碰撞 接受陣列做為參數,以下程式會檢查玩家是否與左牆或右牆碰撞 ```javascript= game.physics.arcade.collide(player, [leftWall, rightWall]); ``` --- # 任務5 * 讓牆壁有阻擋功能 :point_right:[任務目標](http://codepen.io/pikapika/full/BQYxOE) --- # 掉落 改變角色的速率,實現加速度的方法 ```javascript= player.body.velocity.y += 30; ``` 改用設定玩家的引力,原理和上面的做法相似 ==sprite.body.gravity== 可以設定施加在角色身上的引力 ```javascript= player.body.gravity.y = 500; ``` --- # 任務 6 * 設定角色的引力,會向下墜落 * 創要一個平台,角色踩在平台上會停住 :point_right:[任務目標](http://codepen.io/pikapika/full/ZBroqO) --- # 移動 大!平!台! 平台被設定為固定,因此不能使用 body.velocity 來移動,但可以在 update 裡修改座標 --- # 任務7 * 平台向上移動 :point_right:[任務目標](http://codepen.io/pikapika/full/rWJvqW) --- # 溫習陣列 ```javascript= // 存有 5 個成績的陣列 var scores = [100, 95, 90, 85, 80]; console.log(scores[2]); // 取出第0個成績,印出 90 // 把第0個的成績改成 60 scores[0] = 60; console.log(scores[0]); // 取出第0個成績,印出 60 // 新增新的成績 scores.push(90); console.log(scores[5]); // 取得第5個成績,印出 90 // 取得成績的個數 console.log(scores.length); // 印出 6 ``` --- # 多個平台 遊戲中平台是不斷生成和消失,因此不能用各自的變數去存放平台,必須改用==陣列==來儲存 ```javascript= var platforms = []; function create() { var platform = game.add.sprite(0, 0, 'normal'); platforms.push(platform); } ``` --- # 任務8 * 創造三個平台 * 改用陣列存放平台 * 使用迴圈去更新陣列裡每個平台位置 :point_right:[任務目標](http://codepen.io/pikapika/full/dOdegm) --- # 時間 Phaser 自帶時間計時方便我們取得, 使用 ==game.time.now== 可以取得遊戲開始到現在的時間 (單位毫秒ms) ```javascript= var now = Phaser.time.now; console.log(now); // 印出遊戲開始到現在的時間 ``` --- # 任務9 * 每隔一段時間(600ms)創造新的平台 :point_right:[任務目標](http://codepen.io/pikapika/full/GNQdYw) --- # 移除平台 銷毀 platform 物件 ```javascript= platform.destroy(); ``` 從陣列移除第 i 個平台 ```javascript= platforms.splice(i, 1); ``` --- # 隨機數 執行 ==Math.random()== 會產生0~1的隨機數字, 乘上數字 100 就會取得 0~100 的隨機數字 ```javascript= // rand 是0~100的隨機數字 var rand = Math.random()*100; ``` --- # 任務10 * 隨機產生平台的位置 [任務示範](http://codepen.io/pikapika/full/QGQrJW) --- # 平台種類 1. [normal](http://s.ntustcoding.club/ns-shaft-workshop/normal.png) 藍色平台 2. [nails](http://s.ntustcoding.club/ns-shaft-workshop/nails.png) 釘子 3. [conveyorRight](http://s.ntustcoding.club/ns-shaft-workshop/conveyor_right.png) 往右的傳送帶 4. [conveyorLeft](http://s.ntustcoding.club/ns-shaft-workshop/conveyor_left.png) 往左的傳送帶 5. [trampoline](http://s.ntustcoding.club/ns-shaft-workshop/trampoline.png) 彈簧墊 6. [fake](http://s.ntustcoding.club/ns-shaft-workshop/fake.png) 翻轉的平台 --- # 任務11 1. 隨機產生不同的平台 :point_right:[任務目標](http://codepen.io/pikapika/full/yVKNbg) --- # 碰撞時執行函式 增加第三個參數 effect 為碰撞時執行的函式 ```javascript= game.physics.arcade.collide(player, platforms, effect); ``` effect 函式傳入的參數是碰撞的兩個 Sprite ```javascript= function effect(player, platform) { console.log(platform.key); } ``` ==platform.key== 會是 sprite 的圖片名字 --- # 任務12 當角色碰撞上平台時印出碰撞到的平台種類 console在左下角打開 :point_right:[任務目標](http://codepen.io/pikapika/pen/dOmoWg) --- # 持續播放動畫 第四個參數為 true 時,動畫會不停播放 ```javascript= platform.animations.add('scroll', [0, 1, 2, 3], 16, true); ``` --- # 任務13 * 完成傳送帶的動畫 [圖片1](http://s.ntustcoding.club/ns-shaft-workshop/conveyor_left.png) [圖片2](http://s.ntustcoding.club/ns-shaft-workshop/conveyor_right.png) :point_right:[任務目標](http://codepen.io/pikapika/full/mOxJmg) --- # 任務14 * 實作傳送帶的功能 :point_right:[任務目標](http://codepen.io/pikapika/full/NbYqgG) --- # 任務15 * 完成彈簧墊的彈跳功能 :point_right:[任務目標](http://codepen.io/pikapika/full/mOoLPB/) --- # 任務16 * 完成彈簧墊的動畫 [圖片](http://s.ntustcoding.club/ns-shaft-workshop/trampoline.png) :point_right:[任務目標](http://codepen.io/pikapika/full/xRWGrj) --- # 物件 ```javascript= var pikachu = { color: "yellow", weight: 13.2, height: 40, gender: true } ``` ==pikachu.life== 皮卡丘的生命值 ```javascript= // 印出 13.2公分 console.log(pikachu.height + "公分"); // 皮卡丘增胖 5 公斤 pikachu.weight += 5; ``` --- # 物件 動態增加屬性和賦予值 ```javascript= var pikachu = {} pikachu.color = "yellow"; pikachu.weight = 13.2; pikachu.height = 40; pikachu.gender = true; ``` --- # player 增加屬性 ==life== 存放玩家的生命 ==onTouch== 紀錄碰撞的物體,防止重複觸法事件 ==unbeatableTime== 角色無敵狀態的時間 ```javascript= player.life = 10; player.touchOn = undefined; player.unbeatableTime = 0; ``` --- # 顯示文字 創造文字物件 game.add.text(x座標, y座標, 文字內容); ```javascript= text = game.add.text(10, 10, ""); ``` 動態修改文字的內容 ```javascript= text.setText("這裡放新的文字"); ``` --- # 任務17 * 增加 player 三個屬性 * 顯示玩家血量 :point_right:[任務目標](http://codepen.io/pikapika/full/YpaXQB) --- # 任務18 * 實作釘子功能,玩家踩上去只會一次扣血 * 提示:player.onTouch 紀錄碰撞的平台 :point_right:[任務目標](http://codepen.io/pikapika/full/QGmbME) --- # 平台碰撞邊界 修改圖片碰撞的邊界 ![](https://i.imgur.com/WYfCslk.jpg) sprite.body.setSize(長, 寬, x座標, y座標) ```javascript= game.physics.arcade.enable(platform); platform.body.setSize(96, 15, 0, 15); ``` --- # 任務19 * 修改釘子平台的碰撞邊界 :point_right:[任務目標](http://codepen.io/pikapika/full/XNGqEX) --- # 任務20 * 實作普通平台的功能,玩家踩上去會加血 * 限制血量上限為 10 :point_right:[任務目標](http://codepen.io/pikapika/full/LbdVjJ) --- # 關閉側邊跟下面碰撞 取消左邊、右邊、下邊的碰撞 增加以下語法接到創造 platform 程式後面,角色彈跳時就不會撞到上面的平台 ```javascript= platform.body.checkCollision.down = false; platform.body.checkCollision.left = false; platform.body.checkCollision.right = false; ``` --- # setTimeout 延遲執行函式,setTimeout(執行的函式, 延遲時間ms) ```javascript= setTimeout(function() { // do something... }, 100); ``` --- # 任務21 * 實作翻轉平台的功能 :point_right:[任務目標](http://codepen.io/pikapika/full/aBMGMJ/) --- # 任務22 * 實作翻轉的動畫 [圖片](http://s.ntustcoding.club/ns-shaft-workshop/fake.png) :point_right:[任務目標](http://codepen.io/pikapika/full/bBvdoV) --- # 任務23 天花板扣血 提示:==game.time.now==, ==player.unbeatableTime== :point_right:[任務目標](http://codepen.io/pikapika/full/zoWGEp) --- # 背景閃爍 game.camera.flash(顏色色碼, 時間) ```javascript= game.camera.flash(0xff0000, 100); ``` --- # 任務24 * 判斷玩家是否死亡 * 玩家死亡時停止遊戲,並顯示 gameOver :point_right:[任務目標](http://codepen.io/pikapika/full/oYqXGa) --- # [完成版程式](http://codepen.io/pikapika/pen/MbxPBo) --- # 挑戰1 - 角色動畫 * 完成角色的所有動畫 * 左走、右走、往左掉、往右掉、往下掉 --- # 挑戰2 - 完成 * 設計重新開始 --- # [對戰模式](http://codepen.io/pikapika/pen/ZBPoNW)