게임 상태 저장 /로드?


12

게임 상태 (프로파일)를 저장하기 위해 가장 많이 사용되는 방법 또는 알고리즘, 데이터베이스 텍스트 파일 암호화 방법 및 관련 사항.

Caesar IV가 mySQL을 사용하는 것을 보았습니다.

어떤 제안.

답변:


20

나는 독점적 인 (이진) 형식으로 가장 자주 수행된다는 것을 확신합니다. 사용하는 게임 상태 구조체에서 파일마다 변수를 쓰거나 구조체의 인스턴스로 해당 파일을 다시 읽습니다. 예를 들어 C ++에서는 객체를 바이트 배열처럼 취급하고 fwrite ()를 파일로, 또는 파일에서 fread ()를 객체 형식으로 다시 캐스트 할 수 있습니다.

Java 또는 다른 언어를 사용하는 경우 직렬화 를 사용하여 작업을 정말 쉽게 할 수 있습니다. Java 설명은이 답변의 맨 아래에 있습니다. 다른 언어 (C ++보다 높은 수준의 언어)에는 반드시 자체 직렬화 라이브러리 또는 내장 함수가 있으므로 사용하도록 선택해야합니다. 직렬화 루틴이 아무리 비효율적이든, 오늘날 하드 드라이브 공간은 저렴합니다. 특히 게임 상태가 전혀 크지 않아야하는 경우에는 자체 직렬화 루틴 을 작성하고 유지 하지 못하게 됩니다.

당신은 절대적으로해야 하지 (단지의 첫 번째 문장을 읽어 MySQL을 사용 이 리뷰 ). 어떤 이유로 관계형 데이터베이스 가 실제로 필요한 경우 SQLite를 사용하십시오 . 경량 데이터베이스 시스템이며 단일 데이터베이스 파일에 존재합니다. 그러나 대부분의 게임에서 관계형 데이터베이스는 갈 길이 아니며,이를 사용하려는 회사는 일반적으로 실제 관계형 데이터베이스가 아닌 키-값 조회 테이블로 사용합니다.

로컬 디스크 파일의 모든 종류의 암호화는 난독 화일뿐입니다 . 해커는 프로그램이 해독 한 직후에 데이터를 스니핑합니다. 나는 개인적으로 이런 종류의 일에 반대합니다. 특히 1 인 게임입니다. 내 견해로는 게임 소유자 (고객을 지불하고 마음을 사로 잡는 것)가 원하는 경우 게임을 해킹 할 수 있어야한다는 것입니다. 어떤 경우에는 더 큰 공동체 의식을 만들 수 있으며 더 많은 고객을 유도하는 게임을 위해 "모드"를 개발할 수 있습니다. 가장 최근에 떠오른 예 는 최근에 출시 된 Portal in Minecraft 모드입니다. 인터넷을 통해 게이머 뉴스 사이트에 게시되며 Minecraft의 판매량을 늘릴 수 있습니다.


어떤 이유로 든 실제로 게임 개발에 Java를 사용하기에 충분히 미친 경우 Java의 직렬화에 대한 간단한 설명이 있습니다.

모든 게임 상태 데이터를 하나의 클래스로 유지하고을 구현하여 클래스를 직렬화 가능하게하고 특수 인스턴스화 된 변수를 클래스에 혼합하는 경우 키워드를 Serializable사용하고 transient(일시적 객체는 직렬화되지 않음) 단순히 ObjectOutputStream파일에 쓰고 파일 ObjectInputStream에서 읽는 데 사용합니다 . 그게 다야.


2
Hehe, The Witcher는 데이터를 암호화하지 않았습니다. 16 진수 편집기에서 저장 게임 파일을 열고, 약간 엉망으로하고 벌거 벗은 병아리 카드를 모두 얻는 것이 매우 쉽습니다 ... 오, 정말 죄송합니다!
Anthony

게임을 저장하기 위해 .NET 바이너리 직렬화를 사용했으며 C #은 게임을 개발하는 Java보다 약간 더 합리적인 플랫폼 일 것입니다. 전체 객체 그래프를 자동으로 자동 저장하는 방법이 마음에 듭니다. 이것은 훨씬 더 어려웠습니다. 물론 이제는 텍스처와 같은 불필요한 부분을 배제해야하는 어려움이 있지만, 달성해야 할 큰 문제는 아닙니다.
BlueMonkMN

C #이 게임 개발에 Java보다 약간 더 인기 가 있다는 데 동의하지 않습니다 . 좀 더 합리적이라는 것은 주관성을 의미하며, XNA와 Java 개발자 모두 Java를 선호합니다. 원하는 경우 C #으로 직렬화에 대한 지침을 자유롭게 나열하십시오. 한 적이 없지만 내 답변으로 편집 할 수 있습니다. Java의 직렬화는 전체 객체 그래프를 저장하고 변수의 'transient'키워드는 텍스처와 같은 불필요한 부분을 제외합니다. 임시 멤버가 아닌 모든 멤버는 직렬화 가능해야하며,이를 해결하기 위해 사용자 정의 writeObject 메소드를 작성할 수 있습니다.
Ricket

파이썬의 피클은 전체 객체 그래프를 저장하고 사용하기 쉽습니다. deserialize 할 때 파이처럼 쉽게 수화 된 개체를 돌려 받습니다
drhayes

내장 이진 포맷터를 사용하는 경우 C # /. NET의 직렬화에서 특정 필드를 제외하는 것은 [NonSerialized] 속성으로 필드를 장식하는 것만 큼 간단합니다. 그러나 간과하기 쉬운 것은 컴파일러 생성 이벤트 (예 : 사용자 정의 추가 / 제거 연산자가없는 모든 이벤트)는 구독자를 저장할 백업 필드를 생성한다는 것입니다. 따라서 실수로 (의도없이) 이벤트 처리기를 직렬화 된 개체 그래프에 포함시키는 것이 쉽습니다. 이 문제를 해결하려면 이벤트 선언에 NonSerialized 속성을 추가해야하지만 "field :"한정자를 추가해야합니다.[field: NonSerialized]
Mike Strobel

3

C ++에서와 같이 자신의 코드를 작성하는 경우 이진 파일을 사용할 수 있습니다 . 이진 파일은 난독 화를 통해 암호화 형식을 제공합니다. 그러나 인터넷 전체에 기록되어 있듯이 보안은 매우 약한 형태입니다.

또는 사람이 읽을 수있는 솔루션을 원할 경우 RapidXML 과 같은 것을 사용할 수 있습니다.

어떤 종류의 프레임 워크를 사용하는 경우 파일 지원 기능을 살펴보십시오. HTH


0

내가 본 대부분의 게임은 손으로 작성한 코드를 사용하여 이진 파일에서 읽고 쓸 수 있습니다. 종종 온 디스크 형식은 객체 메모리 레이아웃의 정확한 복제본이므로로드는 다음과 같습니다.

  1. 파일을 메모리로 읽습니다.
  2. 버퍼를 객체 유형으로 캐스트 포인터.

빠르지 만 유지 관리, 플랫폼 별 및 융통성없는 작업입니다.

최근에 몇 가지 게임에서 SQLite를 사용하는 것을 보았습니다. 내가 얘기 한 사람들이 좋아하는 것 같습니다.


0

직렬화는 다른 답변 중 일부에서 언급되었으며 합리적인 해결책이라는 데 동의합니다. 그래도 실패하는 한 가지 방법은 버전 관리입니다.

게임을 출시하면 사람들이 게임을하고 일부 저장 게임을 만듭니다. 그런 다음 이후 패치에서 버그를 수정하거나 일부 기능을 추가하려고합니다. 간단한 바이너리 직렬화 방법을 사용하고 클래스에 멤버를 추가하면 고객이 패치를 설치할 때 직렬화가 이전 저장 게임과 호환되지 않을 수 있습니다.

따라서 어떤 접근 방식을 사용하든 게임을 처음 출시하기 전에이 문제를 고려해야합니다! 패치를 적용한 후 다시 시작해야하는 고객은 만족하지 않습니다.

이를 피하는 한 가지 방법은 각 기본 데이터 유형이 각 버전의 게임에 대해 저장 및로드 할 데이터를 알 수있을 정도로 똑똑한 Save (version) 및 Load (version) 메소드를 구현하도록하는 것입니다. 이렇게하면 저장 게임의 이전 버전과의 호환성을 지원하고 사용자가 실행중인 것보다 최신 버전의 게임에서 저장 게임을로드하려고하면 정상적으로 실패 할 수 있습니다.


1
간단히 말해 : 각 버전의 게임에 대해 "저장"및 "로드"기능을 구현하면 유지 관리의 악몽으로 빠르게 변합니다. 더 나은 해결책은 버전 번호를 포함하고, 현재 버전의 게임에 대해 "저장 /로드"기능을 작성하고, 최신 비 현재 버전에서 현재 버전으로 "변환"기능을 작성하는 것입니다. 그런 다음 모든 변환 기능을 유지하십시오. 열 버전의 오래된 게임을로드하려고하면 열 개의 변환 함수가 직렬로 실행 된 다음 기본 "현재 게임 버전로드"기능이 실행됩니다.
ZorbaTHut

장기적인 유지 관리가 훨씬 쉬워졌습니다. 일련의 "이전 게임 파일 모두로드"기능을 유지하면 다항식 시간으로 빠르게 전환됩니다.
ZorbaTHut

필자가 작업 한 게임에서로드 / 저장 방법의 여러 복사본을 유지하는 대신 하나의 단일 방법이 버전 번호를 사용하여 읽고 쓸 바이트를 결정합니다. 구조를 작성하는 것이 아니라 원시 데이터 유형 레벨 (예 : ReadByte / ReadFload / etc)에서 오퍼레이션으로로드 및 저장을 구현하는 경우 훨씬 쉬워집니다. 그런 다음 새 멤버를 추가하면 if (version> 10) {someNewByte = ReadByte (); }이 방법으로 구현하면 다른 엔디안 플랫폼도 더 쉽게 지원할 수 있습니다.
kevin42

버전 관리 지원을 피하는 또 다른 방법은 객체의 직렬화 / 직렬화 메소드가 스트림에서 개별 값을 직접 쓰거나 읽지 않도록하는 것입니다. 대신, 객체는 키 / 값 쌍을 사용하여 소유 한 데이터를 속성 백에 쓴 다음 백을 스트림에 쓸 수 있습니다. 역 직렬화 중에 객체는 이름으로 백에서 값을 읽을 수 있습니다. 그러나 kevin42가 제안한 것과 같은 특정 버전 번호 검사 대신 누락 된 값을 처리하는 방법에 대한 규칙을 가질 수 있습니다 (예 : 필요한 값이 가방에없는 경우 표준 기본값 사용).
Mike Strobel

0

레벨을 저장하는 데 사용하는 것과 동일한 형식을 사용할 수 있다면 보너스입니다.

예를 들어, Peggle과 같은 게임은 초기 보드 레이아웃, 볼 수, 현재 점수 (초기 보드의 경우 0) 등을 가질 수 있습니다. 그러면 저장된 게임은 정확히 동일한 형식을 사용할 수 있습니다.

동일한 형식을 사용하면 게임 저장 및 레벨로드로 작업을보다 쉽게하는 코드를 공유 할 수 있습니다!

맵 파일이 정말 큰 게임의 경우 저장 게임은 맵 파일을 참조로 포함 할 수 있습니다. 초기 레벨 파일도 같은 일을 할 수 있습니다. 어떤 이유로 NPC 중 일부가 죽었고 시체가 모두 누워있는 것을 제외하고 어떤 이유로 이야기가 같은 장소로 돌아왔다면 맵으로 쉽게 돌아올 수 있습니다 위에.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.