iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
Modern Web

PixiJS青銅玩家系列 第 27

【LV. 27】PixiJS青銅玩家:讓角色行動但置中於地圖(1)

  • 分享至 

  • xImage
  •  

▉ 前言

這裡將利用教程所提供的"Treasure Hunter Game"來直接學習PixiJS中的語法。"Treasure Hunter Game"的程式碼 ---> Click Here !

系統提示:「A man can succeed at almost anything for which he has unlimited enthusiasm.」,PixiJS青銅玩家完成自定義function階段任務,獲得一把看起來還不錯的劍,從這裡開始將要自行探索未知地圖。

▉ 支線任務:讓角色行動但置中於地圖

▍任務說明

這是一般情況下,當角色移動時,畫面並不會跟著動,這種作法僅限於地圖很小的情況下適用。

但是若是在大地圖的情況下,我會希望他能永遠置中,如google doodle的冠軍島任務一樣。

這裡將會利用PIXI, TypeScript: Character Keyboard Movements + Screen Follow此網站的圖片作為練習,但程式碼僅作為參考,主要是看他的想法(畢竟他是用typeScript,我不會QQ)。

***

▍作法1 基本設置

先把基本設定給設置完成。(順便複習一下之前的內容)
完成的codepen參考。

利用別名

let Application = PIXI.Application,
    Container = PIXI.Container,
    loader = PIXI.loader,
    resources = PIXI.loader.resources,
    Graphics = PIXI.Graphics,
    TextureCache = PIXI.utils.TextureCache,
    Sprite = PIXI.Sprite,
    Text = PIXI.Text,
    TextStyle = PIXI.TextStyle;

定義變數

let state, explorer, treasure, blobs, chimes, exit, player, dungeon,
    door, healthBar, message, gameScene, gameOverScene, enemies, id;

建立畫布

建立一個寬高各為512px的畫布。

let app = new Application({ 
    width: 512, 
    height: 512,                       
    antialiasing: true, 
    transparent: false, 
    resolution: 1
  }
);

將畫布加到網頁之上

document.body.appendChild(app.view);

將圖片先加到Texture Cache,載入完成並執行setUp()

這裡是把圖片用網址的方式載入,當然也可以放到資料夾中,利用檔案路徑的方式。

loader
  .add("map","https://i.imgur.com/Dhw1CcR.png")
  .add("explorer","https://i.imgur.com/FEuTd7c.png")
  .load(setUp);

function setUp()建置

大方向分成幾個部份:

  1. 遊戲場景設置
  2. 角色Sprites建立
  3. 鍵盤設置
  4. 遊戲狀態設為「play()」
  5. 執行遊戲迴圈
function setUp(){
  //1. 遊戲場景設置
  gameScene = new Container();
  app.stage.addChild(gameScene);

  //2. 角色Sprites建立
  map = new Sprite(resources["map"].texture);
  gameScene.addChild(map); 
  
  explorer = new Sprite(resources["explorer"].texture);
  gameScene.addChild(explorer);
  explorer.vx = 0;
  explorer.vy = 0;
  
  //3. 鍵盤設置
  let left = keyboard(37),
    up = keyboard(38),
    right = keyboard(39),
    down = keyboard(40);
  
  left.press = function() {
    explorer.vx = -5;
    explorer.vy = 0;
  };
  left.release = function() {
    if (!right.isDown && explorer.vy === 0) {
      explorer.vx = 0;
    }
  };

  up.press = function() {
    explorer.vy = -5;
    explorer.vx = 0;
  };
  up.release = function() {
    if (!down.isDown && explorer.vx === 0) {
      explorer.vy = 0;
    }
  };

  right.press = function() {
    explorer.vx = 5;
    explorer.vy = 0;
  };
  right.release = function() {
    if (!left.isDown && explorer.vy === 0) {
      explorer.vx = 0;
    }
  };

  down.press = function() {
    explorer.vy = 5;
    explorer.vx = 0;
  };
  down.release = function() {
    if (!up.isDown && explorer.vx === 0) {
      explorer.vy = 0;
    }
  };
  
  //4.遊戲狀態設為「play()」
  state = play;
    
  //5. 執行遊戲迴圈
  app.ticker.add((delta) => gameLoop(delta));
}

遊戲迴圈(每秒60次更新遊戲狀態)

function gameLoop(delta){
  state(delta);
}

function play()建置

放置遊戲邏輯的地方,大方向分為以下部份:

  1. 更新玩家的位置(速度vx,vy只有在方向鍵按下時才會增加減少)
  2. 範圍限制的設置
function play(delta){
  //1. 更新玩家的位置
  explorer.x += explorer.vx;
  explorer.y += explorer.vy;
  
  //2. 範圍限制的設置
  contain(explorer, {x: 28, y: 10, width: 488, height: 480});
}

helper function contain()建置

用來限制角色活動範圍。

function contain(sprite, container) {

  let collision = undefined;

  if (sprite.x < container.x) {
    sprite.x = container.x;
    collision = "left";
  }

  if (sprite.y < container.y) {
    sprite.y = container.y;
    collision = "top";
  }

  if (sprite.x + sprite.width > container.width) {
    sprite.x = container.width - sprite.width;
    collision = "right";
  }

  if (sprite.y + sprite.height > container.height) {
    sprite.y = container.height - sprite.height;
    collision = "bottom";
  }
  return collision;
}

helper function keyboard()建置

function keyboard(keyCode) {
  var key = {};
  key.code = keyCode;
  key.isDown = false;
  key.isUp = true;
  key.press = undefined;
  key.release = undefined;
    
  key.downHandler = function(event) {
    if (event.keyCode === key.code) {
      if (key.isUp && key.press) key.press();
      key.isDown = true;
      key.isUp = false;
    }
    event.preventDefault();
  };

  key.upHandler = function(event) {
    if (event.keyCode === key.code) {
      if (key.isDown && key.release) key.release();
      key.isDown = false;
      key.isUp = true;
    }
    event.preventDefault();
  };

  window.addEventListener(
    "keydown", key.downHandler.bind(key), false
  );
  window.addEventListener(
    "keyup", key.upHandler.bind(key), false
  );
  return key;
}

▍作法2 角色置中設置

先想想看他需要更改什麼?首先,我們希望角色可以置中於畫布之上,除了當角色移動到地圖的邊邊(這裡的地圖指的是「底圖」,以此處為例,即如下那張綠色的地圖)

基本上地圖比我們的畫布(畫布即顯示在網頁之上的可視範圍,等等說明會用「screen」取代)還要大,所以我們使地圖位置可以移動,讓他的位置以角色做相對的移動。

***

第一件事情就是先將角色的錨點(anchor)從預設在左上角,改到中心(也就是該explorer的原點改到中心):

explorer.anchor.set(0.5,0.5);

接著,來看看地圖如何與角色相對的改變位置,下圖為初始狀態:

我們把地圖移動到位置(-elorer.x,-explorer.y):

為了讓角色explorer可以置中於screen,所以地圖又挪動了半個screen的寬以及高:

至於程式碼如下:

explorer.anchor.set(0.5,0.5);
explorer.x = app.screen.width * 0.5;
explorer.y = app.screen.height * 0.5;

screenCenterX = app.screen.width * 0.5;
screenCenterY = app.screen.height * 0.5;
newMapPosX = -explorer.x + screenCenterX;
newMapPosY = -explorer.y + screenCenterY;

map.x = newMapPosX;
map.y = newMapPosY;

附上codepen的示範,不過目前還有些問題找不到,決定明天處理!


▉ 結語

又是一個寫了5個小時的一篇,然而寫出來的東西還是有bug,甚至不知道問題出在哪邊,但是等等還要趕打工,回來十點多了大概也沒時間改,所以...就留到明天來處理吧(目前猜測可能是改了explorer的anchor、限制範圍沒處理好,就是當screen的邊界撞到map的邊界處理)


參考資料


上一篇
【LV. 26】PixiJS青銅玩家:helper function - keyboard()(3)abc?
下一篇
【LV. 28】PixiJS青銅玩家:讓角色行動但置中於地圖(2)邊界限制
系列文
PixiJS青銅玩家30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言