ホームへ戻る

 3.11章 セーブデータの作り方

 本章ではセーブデータの作り方を学んでいきましょう。
セキュリティなどの話はとりあえず置いておき、一番簡単なセーブデータの作り方をまずご紹介します。

C言語の入門サイトや参考書でファイル入出力を学ぶと、最初にテキストファイルへの出力を学ぶかもしれません。

FILE *fp = fopen( "***.txt", "w" );

このようにしてファイルを開き、例えば

int money = 1000;
fprintf( fp, "現在の所持金 = %d\n", money );

このように書けば、ファイルには



このように出力されます。
しかし、このように出力すると、誰にでもあっさりと改竄出来てしまいますし、データが何百、何千になった時、非常に面倒なことになります。
ですから、テキストファイルではなく「バイナリファイル」に出力してみましょう。

fopen関数 の仕様を確認して下さい。

FILE *fp = fopen( "***.dat", "w
b" );

このように「b」を付けることでバイナリファイルの出力が出来ます。
文字コードに従ってデータを出力していたのがテキストファイルだったのに対して、
バイナリファイルとはデータの値をそのまま出力したファイルです。

バイナリファイルにデータを出力する関数は fwrite関数 です。仕様を確認して下さい。

第一引数が、セーブデータを持った変数のアドレス。
第二引数が、出力するバイト数。
第三引数が、出力する個数。
第四引数が、出力するファイルポインタ。

となりますから、上のように money変数 にはいったint型の値を出力したい時は

fwrite( &money, sizeof(int), 1, fp );

となります。
実行できる形にプログラムを書くと以下のようになります。


#include <stdio.h>

int main(){
        int money = 1000;
        FILE *fp = fopen( "セーブデータ.dat", "wb" ); // ファイルを開く
        if( fp == NULL ){ // NULLが返ってきたらエラー発生
                return 0;
        }
        fwrite( &money, sizeof(int), 1, fp ); // ファイルにmoneyの値を出力する
        fclose( fp ); //ファイルを閉じる
        return 0;
}

実行結果

 今回はDXライブラリを必要としないので、コンソールプログラムを実行できるプロジェクトを使っています。
必要があればこちらからプロジェクトをダウンロードして使ってください。

 プログラムを実行すると、「セーブデータ.dat」という何にも関連付けされていないファイルが生成されます。
ダブルクリックしてもファイルが開けず改竄されにくくなりました。

→ しかしバイナリエディタで開くといとも簡単に編集出来ます。詳しく知りたい人はページ最下部をご覧下さい。

 セーブデータは一つずつ出力すると面倒なので、構造体に入れて一気に出力すると便利です。
例えばセーブしたいデータに「HP、MP、所持金、経験値」の4種類のデータがあるとしたら、これを一つの構造体にして、
fwrite関数で一気に出力できます。


#include <stdio.h>

typedef struct{
        int HP;         //HP
        int MP;         //MP
        int Money;      //所持金
        int Exp;        //経験値
}SaveData_t;

int main(){
        SaveData_t Data = { 200, 100, 1000, 1000 };
        FILE *fp = fopen( "セーブデータ.dat", "wb" );//バイナリファイルを開く
        if( fp == NULL ){//エラーが起きたらNULLを返す
                return 0;
        }
        fwrite( &Data, sizeof(Data), 1, fp ); // SaveData_t構造体の中身を出力
        fclose( fp );//ファイルを閉じる
        return 0;
}


生成したファイルの中身を見てみると



16バイトのデータが保存されています。
16バイトかどうかは環境によって異なりますが、int型の値が4つちゃんと出力されていそうです。
では、出力したデータを読み込むときのプログラムです。
fwrite関数で出力したのに対して、読み込みは fread関数 を使います。
引数の仕様は fwrite関数 と同じです。先ほど出力したファイルを読み込むサンプルプログラムで確認してみましょう。


#include <stdio.h>

typedef struct{
        int HP;         //HP
        int MP;         //MP
        int Money;      //所持金
        int Exp;        //経験値
}SaveData_t;

int main(){
        SaveData_t Data;
        FILE *fp = fopen( "セーブデータ.dat", "rb" );
        if( fp == NULL ){
                return 0;
        }
        fread( &Data, sizeof(Data), 1, fp );
        fclose( fp );

        printf("HP=%d\nMP=%d\n所持金=%d\n経験値=%d\n",
                Data.HP, Data.MP, Data.Money, Data.Exp );

        return 0;
}

実行結果


 変更しているのは、fopenに指定するファイルオープンの方法を、書き込み形式である w から読込形式である r に変えただけで、
後は、fwriteをfreadにしただけです。
ファイルに出力した時に代入した値が fread関数 によってきちんと読み込むことが出来ましたね。
このように構造体に出力したいデータを詰め込んで、fwriteで出力、freadで読み込むと楽にセーブデータを作ることが出来ます。




※ もっと詳しく知りたい人の為に ※

しかし生成したセーブデータをバイナリエディタで開くと・・。



ファイルに記録されたデータの内容が確認出来ます。
E8 03 00 00
というのが、変数moneyに入っていた「1000」の値です。
何故これが1000になるかというと・・。

まず、バイナリエディタは16進数表記になっています。
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F までの16種類を一つの位で表現しますから、

10 は 16 を表しますし、FF は 255 を表します。
2進数、10進数、16進数はプログラムを学んでいると必ず出てきますので、良く分からない人はこの辺でよく確認しておいてください。

それから、「E8 03 00 00」は位の順番が逆になっていることに注意して下さい。
これはリトルエンディアンと呼ばれるデータの並びだからです。
普段私たちは、「左にあるものの方が位が上」だと思っていますが、CPUの都合上、これを反対に記憶した方が都合がよいことがあります。
つまり「00 00 03 E8」が10進数で表すところの「1000」になります。
電卓で計算してみましょう。

windowsに標準で付いてくる電卓は・・



実は「表示」をクリックするとプログラマー用の電卓に変更できます。



「00 00 03 E8」が10進数でいくつになるか確認したいので、「16 進」にチェックを入れ、「3E8」と入力



その後「10 進」にチェックを移すと 10 進数でいくつになるかが分かります。



1000であることが確認出来ました。

→分からないことがあれば掲示板で質問して下さい


- Remical Soft -