0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Tauriでエンジンからゲームを作ってみるAdvent Calendar 2024

Day 22

【Day22】イベントテーブルとイベントトリガー【QAC24】

Last updated at Posted at 2024-12-24

時間逆行ですみまそん。

ゆるしてくだそん。

当初の計画から変更して、Day23 の内容を Day22 に前倒して書いて、Day23 については別の内容を書くことになりました。

イベントテーブル?

イベントテーブルとは、ゲームにおける様々な発生するイベントをまとめたものになります。

これがあることでイベントを管理して、ゲーム内でイベントを発生させることができます。

今回はJSONという形式を用いてイベントを管理しましょう。
あ、JSON については解説してるか。

Plan

[
  {
    "event_type": "say",
    "event_data": {
      "message": "Hello, World!"
    }
  }
]

こんな感じでevent_typeevent_dataを持つオブジェクトを配列に入れたものをイベントテーブルのデータとしましょう。
(すなわち、テーブル辞書にこれが値として入る)

現在はsayしかありませんが、今後はいろいろ増えていくことでしょう。

よりイベントテーブルっぽくするため、マップ固有イベントとしてファイル名に MapID を付けてsrc/assets/table/map/<MapID>.jsonという形式で保存しましょう。
その際はイベント ID の辞書にしましょう。

src/assets/table/map/sample_map.json
{
  "event_id": {
    "event_type": "say",
    "event_data": {
      "message": "Hello, World!"
    }
  }
}

src以下に配置されたファイルはfetchで取得することが出来ます。

イベントトリガー

まずはイベントトリガーの前に、イベント毎のスクリプトファイルを用意しましょう。

src/lib/events/say.ts
export type SayEventData = {
  name?: string;
  message: string;
  hide?: boolean;
  speed?: number;
};
export type SayEvent = {
  event_type: "say";
  event_data: SayEventData;
};

こんな感じで、イベントの型を定義しておきます。
また、event_typeは複数ある想定なので、イベントをまとめた型も用意しておきます。

src/lib/event.ts
import { SayEvent } from "./events/say";

/// 本来はもっとイベントがある。
export type EventType = SayEvent | ...;

イベントをまとめた型をEventTypeとして定義しておきます。
そうすると今度、EventTypeとして渡されたイベントのデータがどのイベントタイプなのかを判断する必要があります。
EventTypeSayEventか...のどれかなのでそれを確定させる処理が必要という訳ですね。

src/lib/events/say.ts
import { EventType } from "../event";

export type SayEventData = {
  name?: string;
  message: string;
  hide?: boolean;
  speed?: number;
};
export type SayEvent = {
  event_type: "say";
  event_data: SayEventData;
};

export const isSayEvent = (event: EventType): event is SayEvent =>
  event.event_type === "say";

このようにisSayEventという関数を作りました。
これは TypeGuard のような関数で、渡されたeventSayEventであるかを判定します。
なので返り値はevent is SayEventとなります。

実装としては、ただの boolean を返すものになり、trueであればその型の保証がされます。
(つまり、if文とかでこれを使えば、そのスコープ内で型判定が有効になる)

このTypeGuardのようなものはすご~い大事なので覚えておきましょうね。

イベントローダー

実際にファイルを読み込んでみる処理を書いてみましょう。
先にも書いた通り、fetchで取得することが出来ます。

src/lib/event.ts
import { path } from "@tauri-apps/api";

...

const TablePath = "./src/assets/table" as const;

export type EventTable = EventType[];
export type EventTableStore = { [eventId: string]: EventTable };

const loadMapEventTables = async (mapId: string) => {
  if (mapId.includes(".") || mapId.includes("/") || mapId.includes("\\")) {
    throw new Error(
      "Invalid map ID. Map ID must not include '.' '/' '\\' characters."
    );
  }
  const eventTableResponse = await fetch(
    await path.join(TablePath, "map", `${mapId}.json`);
  );
  if (eventTableResponse.status === 200) {
    const eventTable = (await eventTableResponse.json()) as EventTableStore;
    return eventTable;
  } else {
    throw new Error("Failed to load event table.");
  }
}

今回はpathというものを使って、パスの結合をしています。
実際のところは普通に文字列結合でもいいのですが、OS 毎の差異があったら面倒なのでしっかりと使っていきます。

fetchで取得して、HTTP Status Code が 200 であれば JSON として取得して返します。

この HTTP Status Code というのは、皆さんの身近な例でいえば 404 Not Foundとかいうやつですね。
HTTP レスポンスの状態を数字で表します。

200 というのはOKという意味で、リクエストが成功したということを示します。
実際のところ、200 じゃない場合はおおよそファイルが見つからなかったことになるので、エラーを投げています。

これで JSON ファイルを読み込むことは出来ましたね。
じゃあ後はイベントを発生させるだけですよ。

おわりに

今日の課題です。

  1. マップにCollisionと同じようにManualEvents(そのタイルに話しかけたら発生)とAutoEvents(そのタイルを踏んだら発生)を追加しておきましょう。
  2. イベント発生の処理を書きましょう。

Collisionの時に話したかどうかは分かりませんが、StoreAPIを使うことで一応ファイル作成をすることができ、中に JSON を書き込むことができます。
これを上手く使えば、マップエディターの状態をファイルとして保存できるので、開発中はぜひ使ってみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?