@nqounetです。
「PerlとMooでモンスター軍団を量産してみよう」シリーズの第3回です。
前回の振り返り
前回は、MooX::Cloneを導入してclone()メソッドでスライムを量産する方法を学びました。
10体のスライムが1行で作れるようになりましたね。今回は、clone()で作ったオブジェクトの属性を変更して、バリエーションを作る方法を学びます。
シリーズ全体の目次は以下をご覧ください。
色違いスライムを作りたい!
通常の緑スライムだけでなく、赤スライム、青スライム、金スライムなど、色違いバージョンを作りたいとしましょう。
それぞれをnew()で作ると、こうなります。
1
2
3
4
| my $green = Monster->new(name => 'スライム', hp => 10, attack => 3, defense => 2, color => '緑');
my $red = Monster->new(name => 'スライム', hp => 10, attack => 3, defense => 2, color => '赤');
my $blue = Monster->new(name => 'スライム', hp => 10, attack => 3, defense => 2, color => '青');
my $gold = Monster->new(name => 'スライム', hp => 10, attack => 3, defense => 2, color => '金');
|
ほとんど同じコードの繰り返しです。もっと楽にできないでしょうか?
clone()後に属性を変更する
clone()で作ったオブジェクトは、元のオブジェクトとは独立した別のオブジェクトです。だから、コピー後に属性を変更しても、元のオブジェクトには影響しません。
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
| #!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo, MooX::Clone(cpanmでインストール)
use v5.36;
package Monster {
use Moo;
use MooX::Clone;
has name => (is => 'ro', required => 1);
has hp => (is => 'rw', required => 1);
has attack => (is => 'rw', required => 1);
has defense => (is => 'rw', required => 1);
has color => (is => 'rw', default => '緑');
sub show_status ($self) {
say "【" . $self->name . "】HP:" . $self->hp
. " 攻撃:" . $self->attack
. " 防御:" . $self->defense
. " 色:" . $self->color;
}
}
# ベーススライムを1体作成
my $base_slime = Monster->new(
name => 'スライム',
hp => 10,
attack => 3,
defense => 2,
);
# clone()してから色を変更
my $red_slime = $base_slime->clone;
$red_slime->color('赤'); # 色を赤に変更
my $blue_slime = $base_slime->clone;
$blue_slime->color('青'); # 色を青に変更
my $gold_slime = $base_slime->clone;
$gold_slime->color('金'); # 色を金に変更
# 元のスライムは緑のまま
say "=== 色違いスライム軍団 ===";
$base_slime->show_status; # 色: 緑(元のまま)
$red_slime->show_status; # 色: 赤
$blue_slime->show_status; # 色: 青
$gold_slime->show_status; # 色: 金
|
$base_slime->cloneでコピーを作り、その後->color('赤')で色を変更しています。元の$base_slimeは緑のままです。
5色のスライム軍団を一気に作成
配列とmapを組み合わせて、一気に作ってみましょう。
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, MooX::Clone(cpanmでインストール)
use v5.36;
package Monster {
use Moo;
use MooX::Clone;
has name => (is => 'ro', required => 1);
has hp => (is => 'rw', required => 1);
has attack => (is => 'rw', required => 1);
has defense => (is => 'rw', required => 1);
has color => (is => 'rw', default => '緑');
sub show_status ($self) {
say "【" . $self->name . "】HP:" . $self->hp
. " 攻撃:" . $self->attack
. " 防御:" . $self->defense
. " 色:" . $self->color;
}
}
# ベーススライムを1体作成
my $base_slime = Monster->new(
name => 'スライム',
hp => 10,
attack => 3,
defense => 2,
);
# 5色のスライムを一気に作成
my @colors = qw(緑 赤 青 金 銀);
my @color_slimes = map {
my $slime = $base_slime->clone;
$slime->color($_);
$slime;
} @colors;
say "=== 5色スライム軍団 ===";
for my $slime (@color_slimes) {
$slime->show_status;
}
|
出力結果:
1
2
3
4
5
6
| === 5色スライム軍団 ===
【スライム】HP:10 攻撃:3 防御:2 色:緑
【スライム】HP:10 攻撃:3 防御:2 色:赤
【スライム】HP:10 攻撃:3 防御:2 色:青
【スライム】HP:10 攻撃:3 防御:2 色:金
【スライム】HP:10 攻撃:3 防御:2 色:銀
|
色の配列を@colorsとして定義し、mapで各色のスライムを作成しています。ベースを1体作って、あとは色だけ変えるという発想です。
強化版バリエーションも作れる
色だけでなく、ステータスを少しずつ変えたバリエーションも作れます。
1
2
3
4
5
6
7
8
9
| # 強化版スライムを作成(HP、攻撃力、防御力を少し上げる)
my $strong_slime = $base_slime->clone;
$strong_slime->hp(15);
$strong_slime->attack(5);
$strong_slime->defense(3);
$strong_slime->color('金');
$strong_slime->show_status;
# 【スライム】HP:15 攻撃:5 防御:3 色:金
|
ベースから少しずつパラメータを変えて、強化版モンスターを作れます。これが「テンプレートからのバリエーション生成」です。
今回の完成コード
今回の最終的なコードです。
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
| #!/usr/bin/env perl
# 言語: perl
# バージョン: 5.36以上
# 依存: Moo, MooX::Clone(cpanmでインストール)
#
# clone()でベースを複製し、属性を変更してバリエーションを作成
use v5.36;
package Monster {
use Moo;
use MooX::Clone;
has name => (is => 'ro', required => 1);
has hp => (is => 'rw', required => 1);
has attack => (is => 'rw', required => 1);
has defense => (is => 'rw', required => 1);
has color => (is => 'rw', default => '緑');
sub show_status ($self) {
say "【" . $self->name . "】HP:" . $self->hp
. " 攻撃:" . $self->attack
. " 防御:" . $self->defense
. " 色:" . $self->color;
}
}
# ベーススライムを1体作成
my $base_slime = Monster->new(
name => 'スライム',
hp => 10,
attack => 3,
defense => 2,
);
# 5色のスライム軍団を作成
my @colors = qw(緑 赤 青 金 銀);
my @color_slimes = map {
my $slime = $base_slime->clone;
$slime->color($_);
$slime;
} @colors;
say "=== 5色スライム軍団 ===";
for my $slime (@color_slimes) {
$slime->show_status;
}
# 強化版スライムも作成
say "\n=== 強化版スライム ===";
my $strong_slime = $base_slime->clone;
$strong_slime->hp(15);
$strong_slime->attack(5);
$strong_slime->defense(3);
$strong_slime->color('虹');
$strong_slime->show_status;
|
まとめ
clone()で作ったオブジェクトは、元のオブジェクトとは独立している- コピー後に属性を変更しても、元のオブジェクトには影響しない
- 「ベースを複製→一部を変更」という手順で、バリエーションを効率的に作れる
- 色違いや強化版など、テンプレートからの派生モンスターを量産できる
次回予告
次回は、装備(武器オブジェクト)を持つモンスターをclone()してみます。すると、思わぬ問題が発覚……。「浅いコピー」の罠にハマります。お楽しみに。