Featured image of post 第3回-状態をまとめて保存しよう(スナップショット) - Mooを使ってゲームのセーブ機能を作ってみよう

第3回-状態をまとめて保存しよう(スナップショット) - Mooを使ってゲームのセーブ機能を作ってみよう

プレイヤーの状態を1つのオブジェクトにまとめて保存。PlayerSnapshotクラスを作成し、is=>'ro'で不変(イミュータブル)なスナップショットを実装します。

@nqounetです。

前回の振り返り

前回は、ゲームの状態を保存しようとして、単純な変数コピーを試みました。しかし、参照コピーの罠やカプセル化の破壊といった問題に直面しました。

前回見つかった問題点

単純な変数コピーによる状態保存には、以下の問題がありました。

問題1:参照コピーの罠

配列やハッシュなどの参照は浅いコピーになってしまい、保存後に元のオブジェクトを変更すると保存した状態も変わってしまう問題がありました。

問題2:カプセル化の破壊

Playerクラスの内部実装(属性の構造)を外部に露出しており、カプセル化の原則に反していました。属性が増えるたびに、保存・復元のコードも修正が必要になります。

今回は、これらの問題を解決する方法として、スナップショットという考え方を導入します。

今回のゴール

今回のゴールは、プレイヤーの状態を1つのオブジェクトにまとめて保存することです。

  • PlayerSnapshotクラスを作成する
  • is => 'ro'で不変(イミュータブル)なスナップショットを作る
  • Playerクラスにsave_snapshotメソッドを追加する
  • スナップショットの動作を確認する

スナップショットを使うことで、前回の問題(参照コピーの罠、カプセル化の破壊)を解決していきます。

スナップショットとは

スナップショットとは、ある時点でのオブジェクトの状態を丸ごと保存したものです。

写真を撮るように、その瞬間の状態を「パシャッ」と記録するイメージです。

	graph LR
    A["Player
    HP:70
    所持金:50G
    位置:森
    所持品:[薬草]"] -->|"save_snapshot()"| B["PlayerSnapshot
    HP:70
    所持金:50G
    位置:森
    所持品:[薬草]"]
    B -.->|"読み取り専用"| C["保存された状態は
    変更できない"]
    
    style B fill:#9f9
    style C fill:#ff9

スナップショットの重要なポイントは、保存した状態が後から変更されないことです。

これを実現するために、Mooのis => 'ro'(読み取り専用)属性を使います。

PlayerSnapshotクラスを作る

まず、プレイヤーの状態を保存するためのPlayerSnapshotクラスを作成しましょう。

	classDiagram
    class PlayerSnapshot {
        -hp: Int
        -gold: Int
        -position: Str
        -items: ArrayRef
    }
    note for PlayerSnapshot "すべての属性が is => 'ro'
    作成後は変更できない
    不変(イミュータブル)"

コード例1:PlayerSnapshotクラス

 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
# Perl v5.36 以降
# 外部依存: Moo

package PlayerSnapshot {
    use Moo;
    use v5.36;

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

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

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

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

このクラスは、4つの属性を持っています。

属性:

  • hp — プレイヤーの体力
  • gold — 所持金
  • position — 現在位置
  • items — 所持品(配列リファレンス)

すべての属性はis => 'ro'で読み取り専用に設定されています。これは、一度作成したスナップショットは後から変更できないことを意味します。

また、すべての属性にrequired => 1を指定しています。スナップショットを作成する際、必ずすべての値を渡す必要があります。

is => 'ro'の重要性

is => 'ro'(read-only)は、属性を読み取り専用にするオプションです。

前シリーズ「Mooで覚えるオブジェクト指向プログラミング」の第4回で学んだ内容ですね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
my $snapshot = PlayerSnapshot->new(
    hp       => 70,
    gold     => 50,
    position => '森',
    items    => [],
);

say $snapshot->hp;  # 70(読み取りはOK)

$snapshot->hp(100); # エラー!書き込みはできない

スナップショットは、ある時点での状態を記録したものです。後から「あの時のHPは実は100だった」と変更されてしまったら、記録の意味がありません。

is => 'ro'を使うことで、スナップショットの不変性(イミュータブル)を保証します。

Playerクラスと比較

前回までに作成したPlayerクラスと比較してみましょう。

項目PlayerPlayerSnapshot
属性のアクセスis => 'rw'is => 'ro'
目的ゲーム中に状態が変化する状態を記録して保存する
変更可能性いつでも変更できる一度作成したら変更できない
メソッド状態を変更するメソッドありメソッドなし(読み取りのみ)

Playerは「今遊んでいるゲーム」、PlayerSnapshotは「セーブデータ」と考えるとわかりやすいでしょう。

Playerクラスにsave_snapshotメソッドを追加する

次に、Playerクラスに、自分の状態からスナップショットを作成するメソッドを追加します。

	sequenceDiagram
    participant P as Player
    participant S as PlayerSnapshot
    
    Note over P: HP:70, 所持金:50G, 位置:森
    P->>P: save_snapshot()
    P->>S: new(hp:70, gold:50, position:'森')
    S-->>P: PlayerSnapshotオブジェクト
    Note over S: 保存された状態

コード例2:save_snapshotメソッド

 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
# Perl v5.36 以降
# 外部依存: Moo

package Player {
    use Moo;
    use v5.36;

    has hp => (
        is      => 'rw',
        default => 100,
    );

    has gold => (
        is      => 'rw',
        default => 0,
    );

    has position => (
        is      => 'rw',
        default => '町',
    );

    has items => (
        is      => 'rw',
        default => sub { [] },
    );

    sub take_damage ($self, $amount) {
        $self->hp($self->hp - $amount);
        if ($self->hp < 0) {
            $self->hp(0);
        }
    }

    sub earn_gold ($self, $amount) {
        $self->gold($self->gold + $amount);
    }

    sub move_to ($self, $location) {
        $self->position($location);
    }

    sub is_alive ($self) {
        return $self->hp > 0;
    }

    sub show_status ($self) {
        say "HP: " . $self->hp;
        say "所持金: " . $self->gold . "G";
        say "位置: " . $self->position;
        my $items = $self->items;
        my $items_str = ($items->@*) ? join(', ', $items->@*) : 'なし';
        say "所持品: " . $items_str;
        say "";
    }

    sub save_snapshot ($self) {
        return PlayerSnapshot->new(
            hp       => $self->hp,
            gold     => $self->gold,
            position => $self->position,
            items    => [$self->items->@*], # 配列の中身を展開して新しい配列リファレンスを作成(コピー)
        );
    }
};

save_snapshotメソッドは、現在の自分の状態からPlayerSnapshotオブジェクトを作成して返します。

このメソッドの重要なポイントは、Playerクラスの内部実装を隠蔽していることです。

外部のコードは、Playerがどんな属性を持っているかを知る必要がありません。ただsave_snapshotメソッドを呼ぶだけで、正しくスナップショットが作成されます。

カプセル化の維持

前回の問題点だった「内部状態の露出」が、この設計で解決されています。

1
2
3
4
5
6
7
# 前回の方法(内部状態を露出)
my $saved_hp       = $player->hp;
my $saved_gold     = $player->gold;
my $saved_position = $player->position;

# 今回の方法(カプセル化を維持)
my $snapshot = $player->save_snapshot;

save_snapshotメソッドを使うことで、Playerクラスの内部構造を知らなくても状態を保存できます。

コピーの問題についても、Playerクラスがメソッドの中で解決することができます。

もし将来、Playerクラスに新しい属性(例えばlevelexperience)を追加したとしても、save_snapshotメソッドの中を修正するだけで済みます。外部のコードは一切変更不要です。

これが、カプセル化を維持することのメリットです。

動作確認

それでは、作成したPlayerSnapshotsave_snapshotメソッドを使って、実際に動作を確認してみましょう。

  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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# Perl v5.36 以降
# 外部依存: Moo

use v5.36;

package PlayerSnapshot {
    use Moo;
    use v5.36;

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

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

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

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

package Player {
    use Moo;
    use v5.36;

    has hp => (
        is      => 'rw',
        default => 100,
    );

    has gold => (
        is      => 'rw',
        default => 0,
    );

    has position => (
        is      => 'rw',
        default => '町',
    );

    has items => (
        is      => 'rw',
        default => sub { [] },
    );

    sub take_damage ($self, $amount) {
        $self->hp($self->hp - $amount);
        if ($self->hp < 0) {
            $self->hp(0);
        }
    }

    sub earn_gold ($self, $amount) {
        $self->gold($self->gold + $amount);
    }

    sub move_to ($self, $location) {
        $self->position($location);
    }

    sub is_alive ($self) {
        return $self->hp > 0;
    }

    sub add_item ($self, $item) {
        push $self->items->@*, $item;
    }

    sub remove_item ($self, $item) {
        my @new = grep { $_ ne $item } $self->items->@*;
        $self->items(\@new);
    }

    sub show_status ($self) {
        say "HP: " . $self->hp;
        say "所持金: " . $self->gold . "G";
        say "位置: " . $self->position;
        my $items = $self->items;
        my $items_str = ($items->@*) ? join(', ', $items->@*) : 'なし';
        say "所持品: " . $items_str;
        say "";
    }

    sub save_snapshot ($self) {
        return PlayerSnapshot->new(
            hp       => $self->hp,
            gold     => $self->gold,
            position => $self->position,
            items    => [$self->items->@*],
        );
    }
};

# ゲームループのデモ
my $player = Player->new;

say "=== ゲーム開始 ===";
$player->show_status;

say "森へ移動...";
$player->move_to('森');
$player->show_status;

say "スライムと戦闘!";
$player->take_damage(30);
say "30のダメージを受けた!";
$player->show_status;

if ($player->is_alive) {
    say "スライムを倒した!";
    $player->earn_gold(50);
    say "50Gを手に入れた!";
    $player->add_item("薬草");
    say "薬草を手に入れた!";
    $player->show_status;
}

# スナップショットを保存
say "=== セーブポイント ===";
my $snapshot = $player->save_snapshot;
say "状態を保存しました";
say "保存された状態:";
{
    say "HP: " . $snapshot->hp;
    say "所持金: " . $snapshot->gold . "G";
    say "位置: " . $snapshot->position;
    my $items = $snapshot->items;
    my $items_str = ($items->@*) ? join(', ', $items->@*) : 'なし';
    say "所持品: " . $items_str;
    say "";
}
say "洞窟へ移動...";
$player->move_to('洞窟');
$player->add_item("毒消し草");
say "毒消し草を手に入れた!";
$player->show_status;

say "ドラゴンと戦闘!";
$player->take_damage(80);
say "80のダメージを受けた!";
$player->show_status;

# スナップショットの内容は変わっていないことを確認
say "=== スナップショットの確認 ===";
say "保存された状態は変わっていない:";
{
    say "HP: " . $snapshot->hp;
    say "所持金: " . $snapshot->gold . "G";
    say "位置: " . $snapshot->position;
    my $items = $snapshot->items;
    my $items_str = ($items->@*) ? join(', ', $items->@*) : 'なし';
    say "所持品: " . $items_str;
    say "";
}

# スナップショットを変更しようとするとエラーになる
say "スナップショットを変更しようとすると...";
eval {
    $snapshot->hp(999);
};
if ($@) {
    say "エラー: スナップショットは変更できません";
    say "(is => 'ro'で保護されています)";
}

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

 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
=== ゲーム開始 ===
HP: 100
所持金: 0G
位置: 町
所持品: なし

森へ移動...
HP: 100
所持金: 0G
位置: 森
所持品: なし

スライムと戦闘!
30のダメージを受けた!
HP: 70
所持金: 0G
位置: 森
所持品: なし

スライムを倒した!
50Gを手に入れた!
薬草を手に入れた!
HP: 70
所持金: 50G
位置: 森
所持品: 薬草

=== セーブポイント ===
状態を保存しました
保存された状態:
HP: 70
所持金: 50G
位置: 森
所持品: 薬草

洞窟へ移動...
毒消し草を手に入れた!
HP: 70
所持金: 50G
位置: 洞窟
所持品: 薬草, 毒消し草

ドラゴンと戦闘!
80のダメージを受けた!
HP: 0
所持金: 50G
位置: 洞窟
所持品: 薬草, 毒消し草

=== スナップショットの確認 ===
保存された状態は変わっていない:
HP: 70
所持金: 50G
位置: 森
所持品: 薬草

スナップショットを変更しようとすると...
エラー: スナップショットは変更できません
(is => 'ro'で保護されています)

重要なポイントは以下の通りです。

  1. スナップショット作成時の状態が保存される — HP:70、所持金:50G、位置:森
  2. プレイヤーの状態が変わっても、スナップショットは変わらない — ドラゴンとの戦闘後もスナップショットはHP:70のまま
  3. スナップショットを変更しようとするとエラーになる — is => 'ro'で保護されている

これで、前回の問題点だった「参照コピーの罠」も解決されています。スナップショットは完全に独立したオブジェクトであり、元のPlayerオブジェクトとは別の値を持っています。

不変性(イミュータブル)の重要性

今回、スナップショットのすべての属性をis => 'ro'(読み取り専用)にしました。これが不変性(イミュータブル)です。

不変性とは、一度作成したオブジェクトの状態を変更できないようにすることです。

なぜ不変性が重要なのか

セーブデータを考えてみてください。「2024年12月1日 15:30のセーブデータ」というのは、その時点での状態を記録したものですよね。

もしこのセーブデータが後から勝手に書き換えられたら、それはもう「その時点のセーブデータ」ではありません。

スナップショットも同じです。ある時点での状態を記録したものなので、後から変更されてはいけません。

不変性のメリット

不変性には、以下のような重要なメリットがあります。

1. 予期しない変更を防ぐ

セーブデータが勝手に書き換えられたら、ゲームが壊れてしまいます。

1
2
# もし is => 'rw' だったら...
$snapshot->hp(999);  # セーブデータを改ざんできてしまう!

is => 'ro'にすることで、このような改ざんを防ぎます。

2. コードの安全性が高まる

スナップショットを受け取った関数が、その中身を変更しないことが保証されます。

1
2
3
4
sub display_save_data ($snapshot) {
    # この関数内でスナップショットを変更することはできない
    say "HP: " . $snapshot->hp;
}

3. デバッグがしやすい

スナップショットの値が変わらないことがわかっているので、バグの原因を探しやすくなります。

	graph TB
    A["スナップショット作成
    HP:70"] --> B["関数Aに渡す
    HP:70"]
    B --> C["関数Bに渡す
    HP:70"]
    C --> D["後で確認
    HP:70"]
    
    style A fill:#9f9
    style D fill:#9f9
    
    E["変更可能なオブジェクト
    HP:70"] --> F["関数Aに渡す
    HP:?"]
    F --> G["関数Bに渡す
    HP:?"]
    G --> H["後で確認
    HP:?"]
    
    style H fill:#f99

不変オブジェクトは、どこに渡しても値が変わらないため、予測しやすく安全です。

Perlでのイミュータブル

Perlには、JavaのfinalやJavaScriptのconstのような言語組み込みの不変性機能はありません。しかし、Mooのis => 'ro'を使うことで、オブジェクトの属性を不変にできます。

1
2
3
4
5
6
7
# Java での例(参考)
# public final int hp = 70;

# Perl + Moo での例
has hp => (
    is => 'ro',  # 読み取り専用 = 不変
);

is => 'ro'は単なる「読み取り専用」ではなく、オブジェクト指向設計における「不変オブジェクト」を実現する重要なテクニックです。

前シリーズで学んだis => 'ro'の使い方が、ここで活きてきましたね!

解決できたこと、できていないこと

今回のスナップショット導入で、前回の問題がどこまで解決できたか整理しましょう。

解決できたこと ✓

  1. 参照コピーの罠 → スナップショットは完全に独立したオブジェクト
  2. カプセル化の破壊 → save_snapshotメソッドで内部実装を隠蔽
  3. 不変性の保証 → is => 'ro'でスナップショットの改ざんを防止

まだできていないこと ✗

  1. スナップショットから復元する機能 — 保存はできるが、復元はまだ実装していない
  2. 複数のスナップショット管理 — 1つしか保存できない(複数のセーブスロットがない)
  3. 自動的な保存 — 手動でsave_snapshotを呼ぶ必要がある

次回は、「スナップショットから復元する」機能を実装します。

今回作成した完成コード

以下が今回作成した完成コードです。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
 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
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#!/usr/bin/env perl
# Perl v5.36 以降
# 外部依存: Moo
use v5.36;

package PlayerSnapshot {
    use Moo;
    use v5.36;

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

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

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

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

package Player {
    use Moo;
    use v5.36;

    has hp => (
        is      => 'rw',
        default => 100,
    );

    has gold => (
        is      => 'rw',
        default => 0,
    );

    has position => (
        is      => 'rw',
        default => '町',
    );

    has items => (
        is      => 'rw',
        default => sub { [] },
    );

    sub take_damage ($self, $amount) {
        $self->hp($self->hp - $amount);
        if ($self->hp < 0) {
            $self->hp(0);
        }
    }

    sub earn_gold ($self, $amount) {
        $self->gold($self->gold + $amount);
    }

    sub move_to ($self, $location) {
        $self->position($location);
    }

    sub is_alive ($self) {
        return $self->hp > 0;
    }

    sub add_item ($self, $item) {
        push $self->items->@*, $item;
    }

    sub remove_item ($self, $item) {
        my @new = grep { $_ ne $item } $self->items->@*;
        $self->items(\@new);
    }

    sub show_status ($self) {
        say "HP: " . $self->hp;
        say "所持金: " . $self->gold . "G";
        say "位置: " . $self->position;
        my $items = $self->items;
        my $items_str = ($items->@*) ? join(', ', $items->@*) : 'なし';
        say "所持品: " . $items_str;
        say "";
    }

    sub save_snapshot ($self) {
        return PlayerSnapshot->new(
            hp       => $self->hp,
            gold     => $self->gold,
            position => $self->position,
            items    => [$self->items->@*],
        );
    }
};

# ゲームループのデモ
my $player = Player->new;

say "=== ゲーム開始 ===";
$player->show_status;

say "森へ移動...";
$player->move_to('森');
$player->show_status;

say "スライムと戦闘!";
$player->take_damage(30);
say "30のダメージを受けた!";
$player->show_status;

if ($player->is_alive) {
    say "スライムを倒した!";
    $player->earn_gold(50);
    say "50Gを手に入れた!";
    $player->add_item("薬草");
    say "薬草を手に入れた!";
    $player->show_status;
}

# スナップショットを保存
say "=== セーブポイント ===";
my $snapshot = $player->save_snapshot;
say "状態を保存しました";
say "保存された状態:";
{
    say "HP: " . $snapshot->hp;
    say "所持金: " . $snapshot->gold . "G";
    say "位置: " . $snapshot->position;
    my $items = $snapshot->items;
    my $items_str = ($items->@*) ? join(', ', $items->@*) : 'なし';
    say "所持品: " . $items_str;
    say "";
}
say "洞窟へ移動...";
$player->move_to('洞窟');
$player->add_item("毒消し草");
say "毒消し草を手に入れた!";
$player->show_status;

say "ドラゴンと戦闘!";
$player->take_damage(80);
say "80のダメージを受けた!";
$player->show_status;

# スナップショットの内容は変わっていないことを確認
say "=== スナップショットの確認 ===";
say "保存された状態は変わっていない:";
{
    say "HP: " . $snapshot->hp;
    say "所持金: " . $snapshot->gold . "G";
    say "位置: " . $snapshot->position;
    my $items = $snapshot->items;
    my $items_str = ($items->@*) ? join(', ', $items->@*) : 'なし';
    say "所持品: " . $items_str;
    say "";
}

# スナップショットを変更しようとするとエラーになる
say "スナップショットを変更しようとすると...";
eval {
    $snapshot->hp(999);
};
if ($@) {
    say "エラー: スナップショットは変更できません";
    say "(is => 'ro'で保護されています)";
}

まとめ

今回は、プレイヤーの状態を1つのオブジェクトにまとめて保存するPlayerSnapshotクラスを作成しました。

作成したもの:

  • PlayerSnapshotクラスで、プレイヤーの状態を保存する構造を作った
  • すべての属性をis => 'ro'で読み取り専用にし、不変性を保証した
  • Playerクラスにsave_snapshotメソッドを追加し、カプセル化を維持した
  • スナップショットが元のオブジェクトから独立していることを確認した

解決できたこと:

  • 参照コピーの罠 → スナップショット作成時に「深いコピー」(deep copy)を行う。作成したスナップショットはPlayerとは独立したオブジェクトなので、元のPlayerを変更してもスナップショットは影響を受けない
  • カプセル化の破壊 → save_snapshotメソッドで内部実装を隠蔽し、属性を直接触らなくて済む
  • 不変性の保証 → is => 'ro'でスナップショットの改ざんを防止し、保存した状態が変わらないことを保証

学んだこと:

  • is => 'ro'で不変(イミュータブル)なオブジェクトを作れる
  • 不変オブジェクトは、予期しない変更を防ぎ、コードの安全性を高める
  • スナップショットという考え方で、状態を安全に保存できる
  • カプセル化を維持することで、将来の変更に強い設計になる

前回の問題点をすべて解決できました!次回は、保存したスナップショットから、プレイヤーの状態を復元する機能を実装します。

次回予告

今回、スナップショットを作成して状態を保存する機能ができました。

しかし、まだ「復元」する機能がありません。ゲームオーバーになっても、保存した状態に戻ることができないのです。

次回は、スナップショットからPlayerの状態を復元するrestore_from_snapshotメソッドを実装します。

セーブ→ダメージを受ける→ロードという一連の流れを完成させ、ゲームのセーブ・ロード機能の基本を実現します。

第4回のテーマ: 保存した状態から復元しよう

お楽しみに。

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