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 21

【Day21】会話が大切である事は自明である【QAC24】

Last updated at Posted at 2024-12-24

いうほど自明なのだろうか...。

sayというクラスを作りましょう。

src/screen/say.ts
import { Screen } from "../lib/screen";

export class Say extends Screen {
  public screenId = "say";

  constructor() {
    super();
  }
}

まぁまずはこんな感じでしょうか。
screenIdについてはabstractなので定義する必要があります。

では少しずつデザインをくみ上げて行きましょう。

メッセージ表示部分

まずはメッセージ表示部分を作ります。

src/screen/say.ts
export class Say extends Screen {
  public screenId = "say";

  constructor() {
    super();
    const screen = super.build();
    this.hide();

    const messageElement = document.createElement("div");
    messageElement.id = "say-message";
    screen.appendChild(messageElement);
  }

  public say(message: string) {
    const messageElement = this.getElementById<HTMLDivElement>("say-message");
    if (!this.showing) this.show();
    if (messageElement) {
      messageElement.textContent = message;
    }
  }
}

sayメソッドをはやしてみました。
これを使うことでメッセージを表示できます。
(メッセージ毎にクラスを作っていると SDGs に反するので使いまわしましょう。)

ですが、これだけだとテキストが表示されるだけです。
もう少しデザインを加えてみましょう。

src/screen/say.ts
export class Say extends Screen {
  ...

  constructor() {
    super();
    const screen = super.build();
    this.hide();

    const messageElement = document.createElement("div");
    messageElement.id = "say-message";
    screen.appendChild(messageElement);

    const style = document.createElement("style");
    style.textContent = `
      #say-message {
        background-color: #000;
        border-radius: 10px;
        box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
        padding: 10px;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        color: #fff;
      }
    `;
    screen.appendChild(style);
  }

  ...
}

このようにするだけで、メッセージ表示部分がデザインされます。

(Copilot がこの CSS を生み出したが Say ではないなこれ。)

メッセージだけ出せばいい?

そう思うあなたは一回なんかしらのゲームをやってきてください。
セリフには、セリフを話している話者の名前が表示されることが多いです。

後は、右下の三角マークみたいなのがあるといいですね。

では、名前を表示する部分を作りましょう。

src/screen/say.ts
export class Say extends Screen {
  ...

  constructor() {
    super();
    const screen = super.build();
    this.hide();

    const whome = document.createElement("div");
    whome.id = "say-whome";
    whome.style.display = "none";
    screen.appendChild(whome);

    const messageElement = document.createElement("div");
    messageElement.id = "say-message";
    screen.appendChild(messageElement);

    const pagenationElement = document.createElement("div");
    pagenationElement.id = "say-pagenation";
    pagenationElement.innerHTML = "";
    screen.appendChild(pagenationElement);

    const style = document.createElement("style");
    style.innerHTML = `
    #say {
      position: absolute;
      bottom: 0%;
      left: 50%;
      width: 80%;
      height: 20%;
      transform: translate(-50%, -100%);
      margin: 0;
      background-color: rgb(0, 0, 0, 0.8);
      border-radius: 10px;
      border: 2px solid #fff;
      display: flex;
      flex-direction: column;
      overflow-y: auto;
      cursor: pointer;
    }

    #say-whome {
      padding: 10px;
      color: #fff;
      border-radius: 10px 10px 0 0;
      border-bottom: 2px solid #fff;
    }

    #say-message {
      padding: 10px;
      color: #fff;
    }

    #say-pagenation {
      position: absolute;
      bottom: 0;
      right: 0;
      padding: 10px;
      color: #fff;
      text-align: right;
    }
    `;
    screen.appendChild(style);
  }

  ...
}

こんなデザインになるでしょうか。
ちゃんと動かすためにsayプロパティを調整しましょう。

src/screen/say.ts
/// 以下は別ファイルにまとめてもいいかもしれない (ex: src/lib/sleep.ts)
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

export class Say extends Screen {
  ...

  public async say(
    message: string,
    name?: string,
    hide: boolean = false,
    speed: number = 50
  ) {
    const messageElement = this.getElementById<HTMLDivElement>("say-message");
    if (!messageElement) {
      throw new Error("Message element not found");
    }
    if (name) {
      const whomeElement = this.getElementById<HTMLDivElement>("say-whome");
      if (!whomeElement) {
        throw new Error("Whome element not found");
      }
      whomeElement.innerHTML = name;
      whomeElement.style.display = "block";
    }
    messageElement.innerHTML = "";
    if (!this.showing) this.show();
    return new Promise<void>(async (resolve) => {
      let skip = false;
      this.element.onclick = () => {
        skip = true;
      };
      for (const char of message) {
        messageElement.innerHTML += char;
        if (speed <= 0) continue;
        if (skip) {
          messageElement.innerHTML = message;
          break;
        } else {
          await sleep(speed);
        }
      }
      this.element.onclick = () => {
        if (hide) {
          this.hide();
          messageElement.innerHTML = "";
          if (name) {
            const whomeElement =
              this.getElementById<HTMLDivElement>("say-whome");
            if (!whomeElement) {
              throw new Error("Whome element not found");
            }
            whomeElement.style.display = "none";
          }
        }
        this.element.onclick = null;
        resolve();
      };
    });
  }

  ...
}

一気に進化させました。
セリフが表示されるときのあの 1 文字ずつ出てくるあれとかです。

これで、セリフを表示する部分は完成です。

sayだけは、よく使われる&使いまわす想定なので、GameMapの中にも埋め込ませておきましょう。
例えばプロパティとしてsayを持たせるとか。

まとめ

これで、セリフを表示する部分が完成しました。
(というかスクリーンの使い方は分かりましたよね!)

でもこれだけだとセリフの表示をそもそもどうやってやるんだって話ですよね。
次回はイベント系の話をしましょうか。

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?