C ++ oyunu için nasıl bir kayıt dosyası oluşturabilirim?


33

Bir Video oyunu Programlama kursu için finalimi kodluyorum ve oyunum için bir kayıt dosyası oluşturmayı bilmek istiyorum, böylece bir kullanıcı oynayabilir ve daha sonra geri gelebilir. Bunun nasıl yapıldığına dair herhangi bir fikir, daha önce yaptığım her şey tek çalıştırma programları oldu.



2
Ayrıca SQLite
Nick Shvelidze

1
@Shvelo Bunu yapabilseniz de, zorunlu olarak gerekmeyen birçok karmaşıklığı artıracak gibi görünüyor.
Nate

Yanıtlar:


38

Değişkenlerinizi bellekte sabit diskinize kaydetmek için seri hale getirme kullanmanız gerekir . İkili ve JSON serileştiricileri olmasına rağmen, pek çok seri hale getirme türü vardır. .NET XML'de yaygın bir biçim bulunur. Çok fazla C ++ programcısı değilim, ancak hızlı bir arama C ++ 'da seri hale getirme konusunda bir örnek buldu:

Serileştirme işlevleri sunan kütüphaneler var. Bazıları diğer cevaplarda belirtilmiştir.

İlgilendiğiniz değişkenler muhtemelen oyun durumuyla ilişkili olacak. Örneğin, muhtemelen bu tür bilgileri bilmek isteyeceksiniz

  1. Oyuncu seviye 3 oynuyordu
  2. Oyuncu X, Y dünya koordinatlarındaydı.
  3. Oyuncunun sırt çantasında üç eşya var
    1. Silah
    2. Zırh
    3. Gıda

Hangi dokuların kullanıldığını gerçekten umursamıyorsunuz (oynatıcınız görünümünü değiştiremiyorsa, bu özel bir durum), çünkü genellikle aynılar. Önemli gamestate verilerini kaydetmeye odaklanmanız gerekir.

Oyuna başladığınızda, "yeni" bir oyun için normal olarak başlarsınız (bu dokularınızı, modellerinizi vb. Yükler) ancak uygun bir zamanda, kaydettiğiniz dosyadaki değerleri tekrar "varsayılan" yerine yeni oyun durumu nesnesine yüklersiniz. oyun durumu. Ardından oynatıcının oynamaya devam etmesine izin verirsiniz.

Bunu burada çok basitleştirdim, ancak genel bir fikir edinmelisiniz. Daha spesifik bir sorunuz varsa, burada yeni bir soru sorun; size bu konuda yardımcı olmaya çalışabiliriz.


Ne kaydetmem gerektiğini anlıyorum, ama tam olarak ne olduğunu bilmek istediğim şeyi, bunu projedeki bir .txt dosyasına, değiştirilen değişkenlere veya başka bir yolla mı
kaydediyorsunuz

Evet, oyununuz basitse, bir metin dosyası yeterli olabilir; Bir metin dosyasını düzenleyebileceğini ve böylece kendi kaydetme oyunlarını yapabileceğini aklınızda bulundurmanız gerekir ...
Nate

Metin dosyası kaydeder sadece basit oyunlar için değil. Paradox, amiral gemisi Europa Universalis motoruyla aynı motoru kullanarak oluşturdukları tüm oyunlar için dosyaları kaydetmek için yapılandırılmış bir metin formatı kullandı. Özellikle geç oyun, bu dosyalar muazzam olabilir.
Dan Neely

1
@DanNeely Adil bir nokta, birçok karmaşık veriyi depolamak için bir metin formatı kullanamamanız için hiçbir neden yok, ancak genel olarak konuşursak, verileriniz karmaşık olduğunda, başka bir formatın (ikili, xml vb.) Yararları daha belirgin hale gelir.
Nate

1
@NateBross Anlaşıldı. Paradox oyunları çok mod dostu idi ve senaryo verileri için benzer (özdeş?) Bir format kullandı. Verilerinin çoğunu metin olarak saklamak, kamu kullanımı için senaryo editörü araçlarına yatırım yapmaları gerekmediği anlamına geliyordu.
Dan Neely

19

Genelde bu, oyununuza özgüdür. Şu ana kadar sınıflarındaki dosyalara yazmayı ve kitaplardan okumayı öğrendiğine eminim. Temel fikir şudur:

  1. Oyundan çıkarken, kaydetmek istediğiniz değerleri bir dosyaya yazın.
  2. Oyunu yüklerken, kaydedilmiş bir dosyanın var olup olmadığını kontrol edin, eğer öyleyse, okumaları programınıza yükleyin. Dosya yoksa, şimdi yaptığınız gibi devam edin ve değerleri başlangıç ​​/ varsayılan değerlerine ayarlayın.

Yazdıkların sana kalmış, oyununa bağlı. Yazmanın bir yolu, istediğiniz değişkenleri belirli bir sıradaki bayt akışı olarak yazmaktır. Ardından, yüklerken bunları programınıza aynı sırada okuyun.

Örneğin (hızlı sözde kodda):

SaveGame(FileInput file) {
    file.writeInt(playerLevel);
    file.writeInt(playerHealth);
    file.writeInt(gameProgress);
}

LoadGame(FileInput file) {
    if(file.exists()) {
        playerLevel= file.readInt();
        playerHealth = file.readInt();
        gameProgress = file.readInt();
    } else {
        playerLevel = 1;
        playerHealth = 100;
        gameProgress = 0;
    }
}

1
Bu yöntem hoş ve küçüktür, ancak veri parçaları için bazı basit etiketler eklemenizi öneririm. Böylece, daha sonra normalde dosyanın ortasında olan bir şeyi değiştirmeniz gerekirse, bunu yapabilirsiniz ve yapmanız gereken tek "eskiden dönüşüm" bu blok içindedir. Bir defalık görev için bu kadar önemli değil, ancak insanlar dosyaları kurtarmaya başladıktan sonra çalışmaya devam ederseniz, sadece tanımlayıcı olan konumdaki düz baytları kullanmak kabusun bir parçası.
Lunin

1
Evet, bu geleceğe dönük kaydetme dosyaları oluşturmaz. Ayrıca, dizge gibi değişken bayt boyutlarına sahip veriler için de çalışmaz. İkincisi, önce yazılacak verinin boyutunu yazarak, ardından doğru bayt sayısını okumak için yüklerken bunu kullanmak kolaydır.
MichaelHouse

6

Muhtemelen bunu yapmanın çok fazla yolu vardır, ancak hem kişisel hem de profesyonel olarak daima bulup kullandığım en basit şey, kaydedilmesini istediğim tüm değerleri içeren bir yapı oluşturmaktır.

struct SaveGameData
{
    int              characterLevel; // Any straight up values from the player
    int              inventoryCount; // Number of items the player has on them or stored or what not
    int[STAT_COUNT]  statistics;     // This is usually a constant size (I am tracking X number of stats)
    // etc
}

struct Item
{
    int itemTypeId;
    int Durability; // also used as a 'uses' count for potions and the like
    int strength;   // damage of a weapon, protection of armor, effectiveness of a potion
    // etc
}

Daha sonra sadece temel Dosya GÇ değerlerini kullanarak bir dosyaya veri girip fwrite / fread. İnventoryCount, dosyadaki ana SaveGameData yapısından sonra kaydedilen Öğe yapılarının sayısıdır, bu yüzden bu verilerin alınmasından sonra kaçının okunacağını biliyorum. Buradaki anahtar, yeni bir şey kaydetmek istediğimde, bir öğe listesi veya benzeri olmadıkça yapmam gereken tek şey, bazı yerlere yapıya değer katmak. Eğer bu bir öğe listesi ise, daha önce Eşya nesneleri için ima ettiğim gibi, ana başlıktaki bir sayaç ve ardından girişler gibi bir okuma geçişi eklemek zorunda kalacağım.

Bu, bir kaydetme dosyasının farklı sürümlerini, özel işlemesi dışında birbiriyle uyumlu hale getirmenin dezavantajı vardır (ana yapıdaki her giriş için yalnızca varsayılan değerler olsa bile). Ancak genel olarak bu, sistemin yeni bir veri değeri ekleyerek ve gerektiğinde ona bir değer koyarak kolayca genişletilmesini sağlar.

Yine, bunu yapmanın epeyce bir yolu ve bu durum C ++ 'a C ++' dan daha fazla yol açabilir, ancak işi tamamladı!


1
Bunun platformdan bağımsız olmadığını, C ++ dizgileri için veya referanslar veya işaretçilerden bahseden nesneler veya yukarıdakilerden herhangi birini içeren nesneler için işe yaramayacağını da belirtmek gerekir!
Kylotan

Bu platform neden bağımsız değil? PC, PS * sistemleri ve 360 ​​.. fwrite (pToDataBuffer, sizeof (datatype), countOfElements, pToFile) üzerinde iyi çalıştı; verilerini ve nesnenin boyutunu ve sonra yazmak istediğiniz sayıyı işaretleyebileceğini
James

Bu ise bir platform üzerinde kaydedilmiş dosyaları başka birinde yüklenebilir bu sadece garantisi yok, platformdan bağımsız. Bu, örneğin oyun verisi tasarrufu için oldukça önemsizdir. İşaretçi-veri-ve-boyut-memcpy şeyler açıkça biraz garip olabilir, ama işe yarıyor.
leftaroundabout'ta

3
Aslında sonsuza dek sizin için çalışmaya devam edeceğinin garantisi yok - yeni bir derleyici ile oluşturulmuş yeni bir sürüm veya yapı dolgusunu değiştiren yeni bir derleme seçenekleri bile çıkarırsanız ne olur? Sadece bu nedenle raw-struct fwrite () yönteminin kullanılmamasını şiddetle rahatsız ederdim (bu konuda deneyimden bahsediyorum, bu arada).
kabarık

1
'32 bit veri 'ile ilgili değil. Orijinal poster sadece "değişkenlerimi nasıl kaydederim" diye soruyor. Değişkeni doğrudan yazarsanız, platformlar arasında bilgi kaybedersiniz. Eğer fwrite önce önişlemeniz gerekiyorsa, cevabın en önemli kısmını, yani. Verilerin doğru bir şekilde kaydedilmesi için nasıl işlendiği ve sadece önemsiz bit içerdiği, yani. diske bir şey koymak için fwrite çağrılması.
Kylotan

3

İlk önce hangi verilerin kaydedilmesi gerektiğine karar vermeniz gerekir. Örneğin, bu karakterin yeri, puanı ve madeni paraların sayısı olabilir. Tabii ki, oyununuz muhtemelen çok daha karmaşık olacak ve bu yüzden seviye numarası ve düşman listesi gibi ek verileri kaydetmeniz gerekecek.

Ardından, bunu bir dosyaya kaydetmek için kod yazın (akış kullanımı). Kullanabileceğiniz nispeten basit bir format aşağıdaki gibidir:

x y score coins

Ve böylece dosya şöyle görünür:

14 96 4200 100

Bu da 4200 ve 100 jetonlu bir pozisyonda (14, 96) olduğu anlamına gelir.

Ayrıca bu dosyayı yüklemek için kod yazmanız gerekir (ifstream kullanın).


Düşmanları kurtarmak, dosyadaki konumlarını dahil ederek yapılabilir. Bu formatı kullanabiliriz:

number_of_enemies x1 y1 x2 y2 ...

Önce önce number_of_enemiesokunur, sonra her pozisyon basit bir döngü ile okunur.


1

Bir ekleme / öneri, serileştirme işleminize şifreleme düzeyi ekleyecektir, böylece kullanıcılar değerlerini "9999999999999999999" olarak değiştiremezler. Bunu yapmanın iyi bir nedeni, tamsayı taşmalarını önlemektir (örneğin).



0

Bütünlüğü uğruna, ben şahsen kullandığım ve henüz bahsetmediğim bir c ++ serileştirme kütüphanesinden bahsetmek istiyorum: tahıl .
Kullanımı kolaydır ve serileştirme için güzel ve temiz bir sözdizimine sahiptir. Aynı zamanda kaydedebileceğiniz birçok format sunar (XML, Json, Binary (endianess ile ilgili taşınabilir bir sürüm dahil)). Kalıtım destekler ve yalnızca üstbilgidir,


0

Oyununuz, baytlara dönüştürmeniz gereken veri yapılarını (umarım?) Tehlikeye atacaktır, böylece bunları saklayabilirsiniz. Gelecekte, bu baytları geri yükleyebilir ve tekrar orijinal yapınıza dönüştürebilirsiniz (seri kaldırma). C ++ 'ta yansıma çok sınırlı olduğu için çok zor değil. Ancak yine de bazı kütüphaneler burada size yardımcı olabilir. Bununla ilgili bir makale yazdım: https://rubentorresbonet.wordpress.com/2014/08/25/an-overview-of-data-serialization-techniques-in-c/ Temel olarak, bir göz atmanızı öneririm Tahıl kütüphanesi C ++ 11 derleyicileri hedefleyebilirseniz. Protobuf gibi ara dosyalar oluşturmanıza gerek yok, bu nedenle hızlı sonuç almak istiyorsanız orada biraz zaman kazanırsınız. İkili ve JSON arasında da seçim yapabilirsiniz, bu nedenle burada hata ayıklamaya oldukça yardımcı olabilir.

Bunun da ötesinde, güvenlik kaygısı varsa, özellikle JSON gibi okunabilir formatlar kullanıyorsanız, sakladığınız verileri şifrelemek / şifresini çözmek isteyebilirsiniz. AES gibi algoritmalar burada faydalıdır.


-5

fstreamGiriş / çıkış dosyaları için kullanmanız gerekir . Sözdizimi basit EX:

#include <fstream>
// ...
std::ofstream flux ; // to open a file in ouput mode
flux.open("myfile.whatever") ; 

Veya

#include <fstream>
// ...
std::ifstream flux ; // open a file in input mode
flux.open("myfile.whatever") ;

Dosyanızda başka eylemler de mümkündür: append , binary , trunc , vs. Yukarıdaki gibi aynı sözdizimini kullanırsınız, bunun yerine std :: ios: :( flags) koyarız, örneğin:

  • ios::out çıkış işlemi için
  • ios::in giriş işlemi için
  • ios::binary İkili (ham bayt) GÇ işlemi için karakter tabanlı yerine
  • ios::app dosyanın sonuna yazmaya başlamak için
  • ios::trunc Çünkü eğer dosya zaten mevcutsa, eski içeriği silin ve yenisiyle değiştirin.
  • ios::ate - giriş / çıkış için dosya işaretçisini "sonunda" konumlandırın

Ör:

#include <fstream>
// ...
std::ifstream flux ;
flux.open("myfile.whatever" , ios::binary) ;

İşte daha eksiksiz ama basit bir örnek.

#include <iostream>
#include <fstream>

using namespace std ;

int input ;
int New_Apple ;
int Apple_Instock ;
int Eat_Apple ;
int Apple ;

int  main()
{
  bool shouldQuit = false;
  New_Apple = 0 ;
  Apple_Instock = 0 ;
  Eat_Apple = 0 ;

  while( !shouldQuit )
  {
    cout << "------------------------------------- /n";
    cout << "1) add some apple " << endl ;
    cout << "2) check apple in stock " << endl ;
    cout << "3) eat some apple " << endl ;
    cout << "4) quit " << endl ;
    cout << "------------------------------------- /n";
    cin >> input ;

    switch (input)
    {
      case 1 :
      {
        system("cls") ;

        cout << "------------------------------------ /n";
        cout << " how much apple do you want to add /n";
        cout << "------------------------------------ /n";      
        cin >> New_Apple ;

        ifstream apple_checker ;
        apple_checker.open("apple.apl") ;
        apple_checker >> Apple_Instock ;
        apple_checker.close() ; 

        Apple = New_Apple + Apple_Instock ;

        ofstream apple_adder ;
        apple_adder.open("apple.apl") ;
        apple_adder << Apple ;
        apple_adder.close() ;

        cout << "------------------------------------ /n";
        cout << New_Apple << " Apple has been added ! /n";
        cout << "------------------------------------ /n";
        break;
      }

      case 2 :  
      {
        system("cls") ;

        ifstream apple_checker ;
        apple_checker.open("apple.apl") ;
        apple_checker >> Apple_Instock ;
        apple_checker.close() ;

        cout << "------------------------------------ /n";
        cout << " there is " << Apple_Instock ;
        cout << "apple in stock /n" ;
        cout << "------------------------------------ /n";
        break;
      }

      case 3 :
      {
        system("cls") ;

        cout << "------------------------------------ /n";
        cout << "How many apple do you want to eat /n" ;
        cout << "------------------------------------ /n";
        cin >> Eat_Apple ;

        ifstream apple_checker ;
        apple_checker.open("apple.apl") ;
        apple_checker >> Apple_Instock ;
        apple_checker.close() ;

        Apple = Apple_Instock - Eat_Apple ; 

        ofstream apple_eater ;
        apple_eater.open("apple.apl") ;
        apple_eater << Apple ;
        apple_eater.close() ;

        cout << "----------------------------------- /n";
        cout << Eat_Apple ;
        cout << " Apple has been eated! /n";
        cout << "----------------------------------- /n";
        cout << Apple << " Apple left in stock /n";
        cout << "----------------------------------- /n";
        break;
      }

      case 4 :
      {
        shouldQuit = true;
        break;
      }

      default :
      {
        system("cls") ;

        cout << "------------------------------------ /n";
        cout << " invalide choice ! /n";
        cout << "------------------------------------ /n"; 
        break;
      }
    }
  }
  return 0;
}

4
-1 Bu çok kötü bir cevap. Kodu doğru şekilde biçimlendirmeli ve görüntülemeli ve ne yaptığınızı açıklamalısınız , hiç kimse bir kod parçasını deşifre etmek istemez.
Vaillancourt

Eğer doğru ben sana o bu tür şeyleri yeni am neden ben web sitesinden kaynağımı biçimlendirmek nasıl bana söyleyebilir daha iyi kodumu açıklamalıdır olan yorum için katu ederiz
Francisco Forcier

Gönderen bu site ya yönelik bu sitede? Gönderileri biçimlendirme konusunda yardım için, biçimlendirme yardım sayfasını ziyaret edebilirsiniz . Size yardımcı olmak için gönderdiğiniz metin alanı başlığının yanında da bir ünlem işareti var.
Vaillancourt

Sorulanı belgelemeye çalışın; Her şeyi yorumlamana gerek yok. Ve ne yaptığınızı açıklamamakla, yorumuma göre, genel olarak en azından kısa bir paragrafla önerdiğiniz stratejiyi tanıtmak istedim. (örneğin, "Tekniklerden biri, bir akış işleci ile bir ikili dosya formatı kullanmaktır. Aynı sırada okuma ve yazma konusunda dikkatli olmalısınız, bla bla lba").
Vaillancourt

2
Ve gotos kullanarak , halka açık bir yerde linç alacaksınız. gotoS kullanmayın .
Vaillancourt
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.