Creating a savestate

Started by
7 comments, last by Sneftel 14 years, 6 months ago
Here's my attempt to create a save feature for the 2d platformer game I am working on. I decided to only save the state of the character for now. Unfortunately, it only works once. Whenever I try to restore from a savestate more then once, it crashes due to an assertion failure somewhere in boost/shared_ptr. What am I doing wrong? Is there a better way to do this? SaveState class

#ifndef SAVESTATE_H
#define SAVESTATE_H

#include "thekid.h"
#include <cassert>

class SaveState
{
    //Kid variables
    const kid savedkid;

    public:
    SaveState(const kid& KidToSave): savedkid(KidToSave)
    {   assert(savedkid.Alive());   }

    kid RestoreFromSave() const
    {   return savedkid;    }
};

#endif







kid class header

#ifndef THEKID_H
#define THEKID_H

#include <vector>
#include "BOOST/shared_ptr.hpp"
#include "imagemanager.h"
#include "brush.h"

typedef std::vector<boost::shared_ptr<Brush> >* brushlist;

    //Jump height is approximately 95 pixels
    //95 = h = (V0*V0)/(2 * G)

    //Jump duration about .8 seconds
    // .8 = t = (2 * V0)/G
    // h = V0 * t/4 <- initial jump speed is 475 pixels/second

    //Horizontal move speed about 150 pixels/sec

    //Remember to update all these values if the framerate is changed!
    const double GRAVITY = .475;
    const double JUMPV0 = -9.5;
    const double XSPEED = 3;

    const Uint32 NUMJUMPS = 2;

    const double ANIMRATE = .03;

enum Animation{ STAND, WALK, JUMP, FALL};
enum Orientation{ RIGHT, LEFT};

class kid
{
    //stuff to check collisions against
    //Pointer not owned by the kid
    brushlist worldbrushes;

    img_sptr texture; //shared_ptr to an image class
    double animcycle; //goes from 0 to 1 then repeats
    Animation animstate;
    Orientation facing;

    double x,y;
    double vx,vy; //used in case of being airborne
    bool airborne;
    boost::shared_ptr<const Brush> platform; //Brush the kid is currently standing on if not airborne

    Uint32 jumpsleft;

    bool isdead;

    /////////////////////////////////////////

    Uint32 GetAnimFrame() const;


    public:
    kid(double initialx, double initialy,
    brushlist thebrushes);

    void Update( int inputx, bool jump=false); //Inputx: -1=left, 0 = normal, 1=right
    //void move( double dx, double dy);

    void draw(SDL_Surface *screen, double xoff, double yoff) const;

    double X() const {return x;}
    double Y() const {return y;}
    double Left() const {return x-15;}
    double Top() const {return y-4;}
    double Right() const {return x+7;}
    double Bottom() const {return y+17;}
    bool Alive() const {return !isdead;}
};

#endif







Save and loading code

//Globals
boost::shared_ptr<SaveState> lastsavestate;
boost::shared_ptr<kid> thekid;

//Save the game
if (thekid->Alive())
{
    lastsavestate.reset(new SaveState(*thekid));
}

//Restore from last save
if (lastsavestate)
{
    thekid.reset(& lastsavestate->RestoreFromSave());
}







I trust exceptions about as far as I can throw them.
Advertisement
My totally blind guess is that you're setting platform from a raw pointer already managed by a shared pointer. BTW, what is the functional difference between a SavedState and a const kid?

EDIT: actually, it looks like it's that you're setting thekid from a member variable of an object which you then allow to die. Your code is too clever by half. What's wrong with just:
//Globalsscoped_ptr<const kid> lastsavestate;kid thekid;//Save the gameif (thekid.Alive()){    lastsavestate.reset(new kid(thekid));}//Restore from last saveif (lastsavestate){    thekid = *lastsavestate;}
Ok here's what I changed it to. It appears to work now.
Also, I can't just use a pointer because I am planning on storing other data in the save later.

SaveState class
#ifndef SAVESTATE_H#define SAVESTATE_H#include "thekid.h"#include "BOOST/scoped_ptr.hpp"#include <cassert>class SaveState{    //Kid variables    boost::scoped_ptr<const kid> savedkid;    public:    SaveState( const boost::scoped_ptr<kid>& KidToSave)    {        assert(KidToSave->Alive());        savedkid.reset(new kid(*KidToSave));    }    void RestoreFromSave(boost::scoped_ptr<kid>& KidRestoreTarget) const    {        KidRestoreTarget.reset(new kid(*savedkid));    }};#endif



Save and loading code
//Globalsboost::shared_ptr<SaveState> lastsavestate;boost::scoped_ptr<kid> thekid;//Save the gameif (thekid->Alive()){    lastsavestate.reset(new SaveState(thekid));}//Restore from last saveif (lastsavestate){    lastsavestate->RestoreFromSave(thekid);}
I trust exceptions about as far as I can throw them.
Quote:Original post by Storyyeller
class SaveState
{
//Kid variables
boost::scoped_ptr<const kid> savedkid;

Why would you do that?
How else should I do it?
I trust exceptions about as far as I can throw them.
I mean, why are you storing a scoped_ptr to a kid, rather than just storing a kid in your savedstate? You're not doing any dynamic binding, and you don't need to manage the lifetime of the object.
Because that's what I was doing before, and it didn't work.
I trust exceptions about as far as I can throw them.
Sneftel's suggestion is that you don't need the shared_ptr for the global "thekid" variable, though. Since it's global, there's no question over that particular object's lifetime/ownership. You only need shared_ptr to manage object lifetime and ownership, so when that is already established, there's no need for shared_ptr's anymore.

The other option would be to use shared_ptr's everywhere. The difficulty comes when, as in your first example, you mix and match use of shared_ptr with raw pointers and temporary objects...
The only reason to have a pointer anywhere at all is because you obviously want to have a "no save state" situation. As codeka said, you might choose the "shared_ptrs for everything" route, but you aren't doing that right now, and those halfway measures are what caused this bug, and three others you haven't found, or perhaps even written, yet. Overuse of the reset function is emblematic of fragile use of smart pointers, as is mixing different types of smart pointers without a full understanding of the differences between them. Stick to the simple stuff. It's what works.

This topic is closed to new replies.

Advertisement