Jump to content
  • Advertisement
Sign in to follow this  
Storyyeller

Creating a savestate

This topic is 3266 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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());
}







Share this post


Link to post
Share on other sites
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:

//Globals
scoped_ptr<const kid> lastsavestate;
kid thekid;

//Save the game
if (thekid.Alive())
{
lastsavestate.reset(new kid(thekid));
}

//Restore from last save
if (lastsavestate)
{
thekid = *lastsavestate;
}

Share this post


Link to post
Share on other sites
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

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

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

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



Share this post


Link to post
Share on other sites
Quote:
Original post by Storyyeller
class SaveState
{
//Kid variables
boost::scoped_ptr<const kid> savedkid;

Why would you do that?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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...

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!