[GML]게임메이커 스튜디오2 파일저장 및 불러오기

게임메이커 스튜디오2 파일저장 및 불러오기

 

파일 저장과 불러오기
파일 저장과 불러오기

 

게임 제작에서 기초적으로 사용되는 파일관리 및 세이브파일을 만들거나 불러오는 기능이

필요한 이유는 항상 메모리에 저장을 하고 있을 수 없으며 게임이 끝이나면 메모리에서도 해제되기에

그동안 플레이를 했던 귀중한 데이터를 어디엔가 저장을 해야 할 필요가 있었습니다.

항상 저장이 가능한 매개체가 HDD인 하드 디스크이며 그곳에 파일로 보관을 한다면 언제든지

다시 불러와 게임의 데이터를 이어서 플레이 할 수 있었습니다.

오늘은 기본적인 파일관리 저장, 불러오기등에 대해서 이야기를 하며 게임메이커에서 제공하는 함수들을 보며

학습해보는 시간을 가져볼까 합니다.

 

파일은 어떻게 만들고 저장해야 하나?

 

먼저 어떤 파일을 어떻게 만들지 막연하겠지만 기본적인 것 부터 예제를 보며 알아볼까 합니다.

파일을 만들어 무언가를 저장하는 함수와 사용법은 아래의 예제 파일과 같습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. 텍스트 파일 열기 (새로 생성하거나 기존 파일을 덮어씀)
var file = file_text_open_write(“example.txt”);
// 2. 텍스트 파일에 쓰기
file_text_write_string(file, “안녕하세요, 게임메이커!”);
file_text_writeln(file); // 줄 바꿈
file_text_write_string(file, “텍스트 파일에 데이터를 저장합니다.”);
// 3. 파일 닫기 (파일 저장 및 종료)
file_text_close(file);
// 1. 텍스트 파일 열기 (읽기 전용)
var file = file_text_open_read(“example.txt”);
// 2. 텍스트 파일에서 데이터 읽기
while (!file_text_eof(file)) {  // 파일 끝까지 읽음
    var line = file_text_read_string(file);  // 한 줄 읽기
    show_debug_message(line);  // 읽은 줄을 콘솔에 출력
    file_text_readln(file);  // 다음 줄로 이동
}
// 3. 파일 닫기
file_text_close(file);
cs

 

여기서 중요하게 봐야 하는 핵심 함수는 file_text_open_write라는 함수로

파일이 있다면 열고 쓰기를 하며 없다면 생성을 합니다.

만약 폴더를 만들고 싶다면 예제: “data/example.txt” 라고 쓰면 폴더와 함께 생성되고

생성이 제대로 되었는지 확인하기 위해서는, 보통 c:\user\username\Appdata\Local\프로젝트 이름

이곳에 저장됩니다.

불러오기 역시 마찬가지이며 위의 코드를 확인하여 불러오도록 합니다.

 

이미지 파일을 저장할 수는 없는가?

 

이미지 파일을 만들려면 화면 자체를 저장해야 하기에 surface를 사용해야 하며, 아래와 같은

방식으로 생성, 그리기 그리고 파일로 저장하고 메모리 해제를 해야 합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 1. 서피스 생성
var surf = surface_create(640480);
// 2. 서피스에 그리기
surface_set_target(surf);
draw_clear(c_white);  // 서피스를 흰색으로 채움
draw_sprite(spr_player, 0100100);  // 플레이어 스프라이트 그리기
surface_reset_target();
// 3. 서피스를 파일로 저장
surface_save(surf, “saved_image.png”);
// 4. 서피스 삭제 (메모리 해제)
surface_free(surf);
// 1. 이미지 불러오기
var loaded_sprite = sprite_add(“saved_image.png”1false00);
// 2. 스프라이트로 그리기
draw_sprite(loaded_sprite, 0200200);
// 3. 스프라이트 삭제 (필요 없을 때 메모리 해제)
sprite_delete(loaded_sprite);
cs

 

예를 들어서 스크린샷을 제공하는 게임을 만들려면, 함수로 간단하게 표현할 수 있습니다.

 

function save_screenshot()

{

// 1. 서피스 생성
var surf = surface_create(640, 480);
// 2. 서피스에 그리기
surface_set_target(surf);
draw_clear(c_white);  // 서피스를 흰색으로 채움
draw_sprite(spr_player, 0, 100, 100);  // 플레이어 스프라이트 그리기
surface_reset_target();
// 3. 서피스를 파일로 저장
surface_save(surf, “saved_image.png”);
// 4. 서피스 삭제 (메모리 해제)
surface_free(surf);

}

 

이렇게 스크립트 함수를 만들고 사용시 save_screenshot();

 

INI파일로 옵션같은 성격의 파일을 만들고 싶다

 

위의 코드들이 이해가 갔다면 ini파일도 함수의 이름이 다를뿐 같은 형태이지만 주의깊게 봐야 할 부분은,

섹션 헤더를 사용할 수 있다는 것입니다. 섹션 헤더가 무엇인가 하면 제목이라고 생각해도 되고 구분을 짓기 위해서

사용할 수 있습니다. 키값으로 들어가는 부분은 변수의 이름으로 사용되고 Values는 변수의 실제 값을 의미합니다.

 

ini 파일 구조
ini 파일 구조

 

그렇다면 저장하고 게임에서 흔히 사용되는 옵션을 만든다고 예를 들면 아래와 같이 사용할 수 있습니다.

 

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
function save_options()
{
    ini_open(“datafiles/gameoption.ini”)
    ini_write_real(“RESOLUTION”,“WIDTH”,global.res_w);
    ini_write_real(“RESOLUTION”,“HEIGHT”,global.res_h);
    ini_write_real(“RESOLUTION”,“MODE”,global.screen_mode);        //풀 스크린 0 /윈도우 1;
    ini_write_real(“DIFFICUTY”,“DIFFICULTY”,global.difficult);
    ini_write_real(“KEYSETTINGS”,“KEYUP”,global.key_up);
    ini_write_real(“KEYSETTINGS”,“KEYDOWN”,global.key_down);
    ini_write_real(“KEYSETTINGS”,“KEYLEFT”,global.key_left);
    ini_write_real(“KEYSETTINGS”,“KEYRIGHT”,global.key_right);
    ini_write_real(“KEYSETTINGS”,“KEYSHOOT”,global.key_shoot);
    ini_write_real(“KEYSETTINGS”,“KEYSKILL”,global.key_skill);
    ini_write_real(“KEYSETTINGS”,“KEYCONFORM”,global.key_conform);
    ini_write_real(“KEYSETTINGS”,“KEYCANCEL”,global.key_cancel);
    ini_write_real(“LANGUAGES”,“LANGUAGE”,global.language);
    ini_close();
    show_message(“options save”);
}
function load_options()
{
    ini_open(“datafiles/gameoption.ini”)
    global.res_w=ini_read_string(“RESOLUTION”,“WIDTH”,1280);
    global.res_h=ini_read_string(“RESOLUTION”,“WIDTH”,720);
    global.screen_mode=ini_read_string(“RESOLUTION”,“MODE”,1);
    global.difficult=ini_read_string(“DIFFICUTY”,“DIFFICUTY”,1);
    global.key_up=ini_read_string(“KEYSETTINGS”,“KEYUP”,38);
    global.key_down=ini_read_string(“KEYSETTINGS”,“KEYDOWN”,40);
    global.key_left=ini_read_string(“KEYSETTINGS”,“KEYLEFT”,37);
    global.key_right=ini_read_string(“KEYSETTINGS”,“KEYRIGHT”,39);
    global.key_shoot=ini_read_string(“KEYSETTINGS”,“KEYSHOOT”,32);
    global.key_skill=ini_read_string(“KEYSETTINGS”,“KEYSKILL”,17);
    global.key_conform=ini_read_string(“KEYSETTINGS”,“KEYCONFORM”,13);
    global.key_cancel=ini_read_string(“KEYSETTINGS”,“KEYCANCEL”,27);
    global.languge=ini_read_string(“LANGUAGES”,“LANGUAGE”,1);
    ini_close()
    show_message(“options load”);
}
cs

 

불러오는 경우에는 값이 저장이 되지 않았을 경우를 대비하여 기본 값을 작성해주고

대입할 변수 이름 = ini_read_string(“섹션이름”, “변수 이름”, 값)으로 불러오기가 가능해집니다.

 

JSON과 같이 쉽게 열어볼 수 없는 파일을 만들고 싶다

 

파일의 저장방식은 기본적으로 비슷하지만 이번에는 ds_map_create()로 배열을 이용하여 저장후

JSON으로 문자열로 변환, 그리고 메모리에서 해제하면서 저장을 해야 합니다.

아래의 코드는 실제 개인적으로 사용하는 게임의 코드 중 일부이며, 위에 설명한 내용을 복습하며 살펴봅시다.

 

function save_system()
{
    // save_variables_to_json
    var _file_name = “datafiles/saved_data.json”;
    global.save_data = ds_map_create();
    // 변수들을 ds_map에 추가
    ds_map_add(global.save_data, “screen_width”, global.res_w);
    ds_map_add(global.save_data, “screen_height”, global.res_h);
    ds_map_add(global.save_data, “screen_scale”, global.res_scale);
    ds_map_add(global.save_data, “screen_mode”, global.screen_mode);
    ds_map_add(global.save_data, “difficulty”, global.difficult);
    ds_map_add(global.save_data, “coin”, global.coinmoney);
    ds_map_add(global.save_data, “level”, global.level);
    ds_map_add(global.save_data, “level_max”, global.level_max);
    ds_map_add(global.save_data, “playerhp”, global.playerhp);
    ds_map_add(global.save_data, “playerhpmax”, global.playerhp_max);
    ds_map_add(global.save_data, “bulletdamage”, global.bulletdamage);
    ds_map_add(global.save_data, “rocketdamage”, global.rocketdamage);
    ds_map_add(global.save_data, “beamdamage”, global.beamdamage);
    ds_map_add(global.save_data, “lightingdamage”, global.lightingdamage);
    ds_map_add(global.save_data, “skillpower”, global.subpilot_skillpower);
    ds_map_add(global.save_data, “gageup”, global.subpilot_gageup);
    ds_map_add(global.save_data, “skillgage”, global.skillgage);
    ds_map_add(global.save_data, “skillgage_max”, global.skillgage_max);
    ds_map_add(global.save_data, “shootrate”, global.shoot_rate);
    ds_map_add(global.save_data, “gamepad”, global.gamepad);
    ds_map_add(global.save_data, “gotoroom”, global.gotoroom);
    ds_map_add(global.save_data, “transition”, global.transition);
    ds_map_add(global.save_data, “targetroom”, global.targetroom);
    ds_map_add(global.save_data, “pause”, global.pause);
    ds_map_add(global.save_data, “language”, global.language);
    ds_map_add(global.save_data, “hitcount”, global.hitcount);
    ds_map_add(global.save_data, “gamepad”, global.gamepad);
    ds_map_add(global.save_data, “key_up”, global.key_up);
    ds_map_add(global.save_data, “key_down”, global.key_down);
    ds_map_add(global.save_data, “key_left”, global.key_left);
    ds_map_add(global.save_data, “key_right”, global.key_right);
    ds_map_add(global.save_data, “key_shoot”, global.key_shoot);
    ds_map_add(global.save_data, “key_skill”, global.key_skill);
    ds_map_add(global.save_data, “key_conform”, global.key_conform);
    ds_map_add(global.save_data, “key_cancel”, global.key_cancel);
    // JSON 문자열로 변환
    var _json_string = json_encode(global.save_data);
    // JSON 문자열이 비어있지 않은지 확인
    if (_json_string != “” && _json_string != undefined) {
        var _file = file_text_open_write(_file_name);
        if (_file != 1) {
            file_text_write_string(_file, _json_string);
            file_text_close(_file);
        } else {
            show_message(“파일을 열 수 없습니다.”);
        }
    } else {
        show_message(“JSON 데이터가 비어 있거나 인코딩 오류 발생.”);
    }
    // 데이터 구조 메모리 해제
    ds_map_destroy(global.save_data);
}
// Load system data from a JSON file
function load_system()
{
    var _file_name = “datafiles/saved_data.json”// 읽어올 파일 이름
    // 파일에서 JSON 문자열 읽기
    if (file_exists(_file_name))
    {
        show_debug_message(“JSON 파일이 존재합니다.”);
        var _file = file_text_open_read(_file_name);
        if (_file == 1) {
            show_debug_message(“파일을 열 수 없습니다.”);
            return;
        }
        var _json_string = “”;
        while (!file_text_eof(_file)) {
            _json_string += file_text_read_string(_file);
        }
        file_text_close(_file);
        show_debug_message(“JSON 문자열을 성공적으로 읽었습니다.”);
        // JSON 문자열을 ds_map으로 변환
        var _loaded_data = json_decode(_json_string);
        if (is_undefined(_loaded_data)) {
            show_debug_message(“JSON 디코딩에 실패했습니다.”);
            return;
        }
        show_debug_message(“JSON 디코딩 성공.”);
        // ds_map에서 변수 복원
        global.save_data = _loaded_data; // Ensure save_data is initialized
        if (ds_map_exists(_loaded_data, “screen_width”)) { global.res_w = ds_map_find_value(_loaded_data, “screen_width”); }
        if (ds_map_exists(_loaded_data, “screen_height”)) { global.res_h = ds_map_find_value(_loaded_data, “screen_height”); }
        if (ds_map_exists(_loaded_data, “screen_scale”)) { global.res_scale = ds_map_find_value(_loaded_data, “screen_scale”); }
        if (ds_map_exists(_loaded_data, “screen_mode”)) { global.screen_mode = ds_map_find_value(_loaded_data, “screen_mode”); }
        if (ds_map_exists(_loaded_data, “difficulty”)) { global.difficult = ds_map_find_value(_loaded_data, “difficulty”); }
        if (ds_map_exists(_loaded_data, “coin”)) { global.coinmoney = ds_map_find_value(_loaded_data, “coin”); }
        if (ds_map_exists(_loaded_data, “level”)) { global.level = ds_map_find_value(_loaded_data, “level”); }
        if (ds_map_exists(_loaded_data, “level_max”)) { global.level_max = ds_map_find_value(_loaded_data, “level_max”); }
        if (ds_map_exists(_loaded_data, “playerhp”)) { global.playerhp = ds_map_find_value(_loaded_data, “playerhp”); }
        if (ds_map_exists(_loaded_data, “playerhpmax”)) { global.playerhp_max = ds_map_find_value(_loaded_data, “playerhpmax”); }
        if (ds_map_exists(_loaded_data, “bulletdamage”)) { global.bulletdamage = ds_map_find_value(_loaded_data, “bulletdamage”); }
        if (ds_map_exists(_loaded_data, “rocketdamage”)) { global.rocketdamage = ds_map_find_value(_loaded_data, “rocketdamage”); }
        if (ds_map_exists(_loaded_data, “beamdamage”)) { global.beamdamage = ds_map_find_value(_loaded_data, “beamdamage”); }
        if (ds_map_exists(_loaded_data, “lightingdamage”)) { global.lightingdamage = ds_map_find_value(_loaded_data, “lightingdamage”); }
        if (ds_map_exists(_loaded_data, “skillpower”)) { global.subpilot_skillpower = ds_map_find_value(_loaded_data, “skillpower”); }
        if (ds_map_exists(_loaded_data, “gageup”)) { global.subpilot_gageup = ds_map_find_value(_loaded_data, “gageup”); }
        if (ds_map_exists(_loaded_data, “skillgage”)) { global.skillgage = ds_map_find_value(_loaded_data, “skillgage”); }
        if (ds_map_exists(_loaded_data, “skillgage_max”)) { global.skillgage_max = ds_map_find_value(_loaded_data, “skillgage_max”); }
        if (ds_map_exists(_loaded_data, “shootrate”)) { global.shoot_rate = ds_map_find_value(_loaded_data, “shootrate”); }
        if (ds_map_exists(_loaded_data, “gamepad”)) { global.gamepad = ds_map_find_value(_loaded_data, “gamepad”); }
        if (ds_map_exists(_loaded_data, “gotoroom”)) { global.gotoroom = ds_map_find_value(_loaded_data, “gotoroom”); }
        if (ds_map_exists(_loaded_data, “transition”)) { global.transition = ds_map_find_value(_loaded_data, “transition”); }
        if (ds_map_exists(_loaded_data, “targetroom”)) { global.targetroom = ds_map_find_value(_loaded_data, “targetroom”); }
        if (ds_map_exists(_loaded_data, “pause”)) { global.pause = ds_map_find_value(_loaded_data, “pause”); }
        if (ds_map_exists(_loaded_data, “language”)) { global.language = ds_map_find_value(_loaded_data, “language”); }
        if (ds_map_exists(_loaded_data, “hitcount”)) { global.hitcount = ds_map_find_value(_loaded_data, “hitcount”); }
        if (ds_map_exists(_loaded_data, “gamepad”)) { global.gamepad = ds_map_find_value(_loaded_data, “gamepad”); }
        if (ds_map_exists(_loaded_data, “key_up”)) { global.key_up = ds_map_find_value(_loaded_data, “key_up”); }
        if (ds_map_exists(_loaded_data, “key_down”)) { global.key_down = ds_map_find_value(_loaded_data, “key_down”); }
        if (ds_map_exists(_loaded_data, “key_left”)) { global.key_left = ds_map_find_value(_loaded_data, “key_left”); }
        if (ds_map_exists(_loaded_data, “key_right”)) { global.key_right = ds_map_find_value(_loaded_data, “key_right”); }
        if (ds_map_exists(_loaded_data, “key_shoot”)) { global.key_shoot = ds_map_find_value(_loaded_data, “key_shoot”); }
        if (ds_map_exists(_loaded_data, “key_skill”)) { global.key_skill = ds_map_find_value(_loaded_data, “key_skill”); }
        if (ds_map_exists(_loaded_data, “key_conform”)) { global.key_conform = ds_map_find_value(_loaded_data, “key_conform”); }
        if (ds_map_exists(_loaded_data, “key_cancel”)) { global.key_cancel = ds_map_find_value(_loaded_data, “key_cancel”); }
        global.keybindings =
        [
            [“Key Up”, global.key_up],
            [“Key Down”, global.key_down],
            [“Key Left”, global.key_left],
            [“Key Right”, global.key_right],
            [“Key Shoot”, global.key_shoot],
            [“Key Skill”, global.key_skill],
            [“Key Conform”, global.key_conform],
            [“Key Cancel”, global.key_cancel]
        ];
        show_debug_message(“모든 변수를 성공적으로 로드했습니다.”);
        // 데이터 구조 메모리 해제
        ds_map_destroy(_loaded_data);
        show_debug_message(“세이브 파일을 로드했습니다.”);
    } else {
        show_debug_message(“파일을 찾을 수 없습니다.”);
    }
}
cs

 

불러오기에서도 JSON파일을 디코딩을 하고 다시 변수에 대입하는 과정을 거친 후 메모리에서 해제를 하는 것으로

로드가 가능해집니다. 복잡하거나 이해가 가지 않는다면 txt파일의 저장과 불러오기 과정을 연습하면

구조 자체가 크게 다른 부분이 없기 때문에 위의 예제 코드들을 살펴보며 이해를 하도록 해야 합니다.

Leave a Comment