iT邦幫忙

2024 iThome 鐵人賽

DAY 23
0
Software Development

六邊形戰士程式設計系列 第 23

D23 - MMORPG事件處理問題 事件建模篇

  • 分享至 

  • xImage
  •  

請參考 D21 - MMORPG事件處理問題 背景介紹篇 以及 D22 - MMORPG事件處理問題 場景建置篇

照例先放上圖片方便對照。

昨天我們把圖片上半部的初始場景建立好了,包含 地圖怪物掉落物玩家。然而這些物件之間並不是靜態的,想要讓他們在地圖上互動,我們就要先定義好相關互動事件的模型

  • 玩家移動
  • 玩家攻擊
  • 玩家撿取
  1. (接續昨天的實作內容) 在 src/models/event.ts 新增以下程式碼定義事件模型

    import { Player } from "./item";
    
    export type Event = MoveEvent | AttackEvent | PickEvent;
    
    export class MoveEvent {
      _tag: "MoveEvent" = "MoveEvent";
      player: Player; // 誰在移動 ?
      direction: "left" | "right"; // 往哪裡移動 ?
      constructor(player: Player, direction: "left" | "right") {
        this.player = player;
        this.direction = direction;
      }
    }
    
    export class AttackEvent {
      _tag: "AttackEvent" = "AttackEvent";
      player: Player; // 誰施放技能 ?
      skill: string; // 技能名稱
      range: { dx: number; dy: number }; // 技能範圍,這邊就不做傷害計算了,一律秒殺
      constructor(
        player: Player,
        skill: string,
        range: { dx: number; dy: number }
      ) {
        this.player = player;
        this.range = range;
        this.skill = skill;
      }
    }
    
    export class PickEvent {
      _tag: "PickEvent" = "PickEvent";
      player: Player;  // 誰在撿取物品
      constructor(player: Player) {
        this.player = player;
      }
    }
    
  2. 有事件模型後,我們就可以在 src/models/map.ts 建立一個事件分配器,把各個事件分別交給不同的事件處理器來處理

      on(event: Event) {
        const { _tag } = event;
        if (_tag === "MoveEvent") this.onMove(event);
        else if (_tag === "AttackEvent") this.onAttack(event);
        else if (_tag === "PickEvent") this.onPick(event);
      }
    
  3. 實作 onMove, onAttack, onPick 三種事件處理方式

      private find(player: Player) {
        return this.grid
          .map((row, y) =>
            row
              .map((cell, x) =>
                cell.some((item) => item.id === player.id)
                  ? ([x, y] as const)
                  : undefined
              )
              .find((cell) => cell !== undefined)
          )
          .find((row) => row !== undefined);
      }
    
      private onMove({ player, direction }: MoveEvent) {
        const position = this.find(player);
        if (!position) return;
        const [x, y] = position;
        this.grid[y][x] = this.grid[y][x].filter((item) => item.id !== player.id);
        if (direction === "left") {
          this.grid[y][x - 1].push(player);
        } else if (direction === "right") {
          this.grid[y][x + 1].push(player);
        }
      }
    
      private onAttack({ player, range, skill }: AttackEvent) {
        const position = this.find(player);
        if (!position) return;
        const [x, y] = position;
        const { dx, dy } = range;
        const xStart = Math.min(x, x + dx);
        const xEnd = Math.max(x, x + dx);
        const yStart = Math.min(y, y + dy);
        const yEnd = Math.max(y, y + dy);
        for (let i = xStart; i < xEnd; i++) {
          for (let j = yStart; j < yEnd; j++) {
            this.grid[j][i] = this.grid[j][i]
              .map((item) => {
                if (item instanceof Monster) {
                  console.log(`${player.name}使用${skill}擊敗了${item.name}`);
                  return item.drop();
                } else {
                  return item;
                }
              })
              .filter((item) => item !== undefined);
          }
        }
      }
    
      private onPick({ player }: PickEvent) {
        const position = this.find(player);
        if (!position) return;
        const [x, y] = position;
        this.grid[y][x] = this.grid[y][x]
          .map((item) => {
            if (item instanceof Dropped) {
              console.log(`${player.name}撿走了${item.name}`);
              return undefined;
            } else {
              return item;
            }
          })
          .filter((item) => item !== undefined);
      }
    
  4. 最後 index.ts 看起來會像是這樣

    const map = new Map("森林小徑1", { colums: 50, rows: 1 });
    const 煞氣a刀賊 = new Player("煞氣a刀賊");
    const 乂正義黑騎乂 = new Player("乂正義黑騎乂");
    const 紅寶 = new Monster("紅寶");
    const 藍寶 = new Monster("藍寶");
    const 刀賊向左走 = new MoveEvent(煞氣a刀賊, "left");
    const 黑騎向右走 = new MoveEvent(乂正義黑騎乂, "right");
    const 刀賊迴旋斬 = new AttackEvent(煞氣a刀賊, "迴旋斬", { dx: -1, dy: 1 });
    const 刀賊撿取 = new PickEvent(煞氣a刀賊);
    
    map.add(煞氣a刀賊, { x: 49, y: 0 });
    map.add(乂正義黑騎乂, { x: 0, y: 0 });
    map.add(紅寶, { x: 25, y: 0 });
    map.add(藍寶, { x: 37, y: 0 });
    for (let i = 0; i < 11; i++) {
      map.on(刀賊向左走);
      map.on(黑騎向右走);
    }
    map.on(刀賊迴旋斬);
    map.on(刀賊向左走);
    map.on(刀賊撿取);
    

    是不是感覺越來越有畫面感了呢 /images/emoticon/emoticon24.gif


上一篇
D22 - MMORPG事件處理問題 場景建置篇
下一篇
D24 - MMORPG事件處理問題 視覺化篇
系列文
六邊形戰士程式設計30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言