@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 "=== ダンジョン探索終了 ===";
}
|
次回予告
次回は「実績システムも追加したい!」です。ログだけでなく、「初めて敵を倒した」などの実績を記録する機能を追加したくなったとき、どんな問題が起きるかを見ていきます。