Featured image of post 第1回-ダンジョン探索ログを作ろう - Perlでローグライク通知システムを作ろう

第1回-ダンジョン探索ログを作ろう - Perlでローグライク通知システムを作ろう

ローグライクゲームで敵を倒したらログに記録する簡単な仕組みを作成。Perlのsayで直接出力する素朴な実装から始めます。Mooでのクラス設計入門。

@nqounetです。

新シリーズ「Perlでローグライク通知システムを作ろう」を始めます。このシリーズでは、テキストベースのローグライクゲームを題材に、プレイヤーのあらゆる行動(敵討伐、レベルアップ、アイテム取得など)を複数の独立したシステムに通知する仕組みを作っていきます。

このシリーズについて

シリーズ全体の目次は以下をご覧ください。

前提知識

このシリーズは「Mooで覚えるオブジェクト指向プログラミング」シリーズを読了していることを前提にしています。まだ読んでいない方は、先にそちらをご覧ください。

対象読者

  • Perl入学式を卒業したばかりの入門者
  • オブジェクト指向プログラミングを身に付けたい方
  • モダンなPerlを使ってみたい方
  • ゲームを作ってみたい方

技術スタック

  • Perl v5.36以降(signaturesを使用)
  • Moo(オブジェクト指向フレームワーク)

今回のゴール

プレイヤーが敵を倒したらログに記録する、シンプルな仕組みを作成します。まずはsayで直接出力する素朴な実装から始めましょう。

ゲームイベントを表現するクラス

ローグライクゲームでは、様々なイベントが発生します。

  • 敵を倒した
  • アイテムを取得した
  • レベルアップした
  • トラップにかかった

これらのイベントを1つのクラスで表現してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo(cpanmでインストール)

use v5.36;
use Moo;

# ゲームイベントを表現するクラス
package GameEvent {
    use Moo;
    use v5.36;

    # イベントの種類(例: enemy_defeated, item_acquired, level_up)
    has 'type' => (
        is       => 'ro',
        required => 1,
    );

    # イベントの詳細情報
    has 'message' => (
        is       => 'ro',
        required => 1,
    );

    # イベント発生時刻(デフォルトは現在時刻)
    has 'timestamp' => (
        is      => 'ro',
        default => sub { time() },
    );
}

# 使用例
package main {
    use v5.36;

    my $event = GameEvent->new(
        type    => 'enemy_defeated',
        message => 'スライムを倒した!',
    );

    say "イベント種類: " . $event->type;
    say "メッセージ: " . $event->message;
    say "タイムスタンプ: " . $event->timestamp;
}

実行結果は以下のようになります。

1
2
3
イベント種類: enemy_defeated
メッセージ: スライムを倒した!
タイムスタンプ: 1768928440

ログ出力機能を追加する

イベントが発生したら、ログに記録するようにしてみましょう。今回は素朴にsayで出力します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo(cpanmでインストール)

use v5.36;

# ゲームイベントを表現するクラス
package GameEvent {
    use Moo;
    use v5.36;

    has 'type' => (
        is       => 'ro',
        required => 1,
    );

    has 'message' => (
        is       => 'ro',
        required => 1,
    );

    has 'timestamp' => (
        is      => 'ro',
        default => sub { time() },
    );
}

# ゲームのメインロジック
package Game {
    use Moo;
    use v5.36;

    # プレイヤー名
    has 'player_name' => (
        is       => 'ro',
        required => 1,
    );

    # 敵を倒したときの処理
    sub defeat_enemy ($self, $enemy_name) {
        my $event = GameEvent->new(
            type    => 'enemy_defeated',
            message => "${enemy_name}を倒した!",
        );

        # ログに出力
        say "[LOG] " . $self->player_name . ": " . $event->message;
    }

    # アイテムを取得したときの処理
    sub acquire_item ($self, $item_name) {
        my $event = GameEvent->new(
            type    => 'item_acquired',
            message => "${item_name}を手に入れた!",
        );

        # ログに出力
        say "[LOG] " . $self->player_name . ": " . $event->message;
    }

    # レベルアップしたときの処理
    sub level_up ($self, $new_level) {
        my $event = GameEvent->new(
            type    => 'level_up',
            message => "レベルが${new_level}になった!",
        );

        # ログに出力
        say "[LOG] " . $self->player_name . ": " . $event->message;
    }
}

# ダンジョン探索シミュレーション
package main {
    use v5.36;

    my $game = Game->new(player_name => '勇者');

    say "=== ダンジョン探索開始 ===";
    say "";

    $game->defeat_enemy('スライム');
    $game->acquire_item('薬草');
    $game->defeat_enemy('ゴブリン');
    $game->level_up(2);
    $game->defeat_enemy('オーク');

    say "";
    say "=== ダンジョン探索終了 ===";
}

実行結果は以下のようになります。

1
2
3
4
5
6
7
8
9
=== ダンジョン探索開始 ===

[LOG] 勇者: スライムを倒した!
[LOG] 勇者: 薬草を手に入れた!
[LOG] 勇者: ゴブリンを倒した!
[LOG] 勇者: レベルが2になった!
[LOG] 勇者: オークを倒した!

=== ダンジョン探索終了 ===

ログがしっかり出力されていますね。

今回のまとめ

今回は、ローグライクゲームの基礎として以下を実装しました。

  • ゲームイベントを表現するGameEventクラス
  • イベント発生時にログを出力するGameクラス

現時点では、ログ出力は各メソッド内で直接sayを呼び出しています。これはシンプルで分かりやすいですが、次回以降で「このやり方の問題点」が見えてきます。

完成コード

今回の完成コードは以下の通りです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo(cpanmでインストール)

use v5.36;

package GameEvent {
    use Moo;
    use v5.36;

    has 'type' => (
        is       => 'ro',
        required => 1,
    );

    has 'message' => (
        is       => 'ro',
        required => 1,
    );

    has 'timestamp' => (
        is      => 'ro',
        default => sub { time() },
    );
}

package Game {
    use Moo;
    use v5.36;

    has 'player_name' => (
        is       => 'ro',
        required => 1,
    );

    sub defeat_enemy ($self, $enemy_name) {
        my $event = GameEvent->new(
            type    => 'enemy_defeated',
            message => "${enemy_name}を倒した!",
        );
        say "[LOG] " . $self->player_name . ": " . $event->message;
    }

    sub acquire_item ($self, $item_name) {
        my $event = GameEvent->new(
            type    => 'item_acquired',
            message => "${item_name}を手に入れた!",
        );
        say "[LOG] " . $self->player_name . ": " . $event->message;
    }

    sub level_up ($self, $new_level) {
        my $event = GameEvent->new(
            type    => 'level_up',
            message => "レベルが${new_level}になった!",
        );
        say "[LOG] " . $self->player_name . ": " . $event->message;
    }
}

package main {
    use v5.36;

    my $game = Game->new(player_name => '勇者');

    say "=== ダンジョン探索開始 ===";
    say "";

    $game->defeat_enemy('スライム');
    $game->acquire_item('薬草');
    $game->defeat_enemy('ゴブリン');
    $game->level_up(2);
    $game->defeat_enemy('オーク');

    say "";
    say "=== ダンジョン探索終了 ===";
}

次回予告

次回は「実績システムも追加したい!」です。ログだけでなく、「初めて敵を倒した」などの実績を記録する機能を追加したくなったとき、どんな問題が起きるかを見ていきます。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。