I am trying to modify the variable "done" from the class "Explosion" through the "sequenceEnded" function(see classes below)
The problem is after "sequenceEnded", "ifDone()" still returns false.
"sequenceEnded" is called from "updateTick()" in the "ImagesPlayer" class
"ifDone()" is called from the "drawSprite" method in the "Meteor" class from an iterator.
I'm now learning C++ so I might be making a silly mistake or maybe not i don't know.
include <string>
using namespace std;
class ImagesPlayerWatcher
{
public:
virtual void sequenceEnded(string imageName) =0;
};
class Explosion: public Sprite, public ImagesPlayerWatcher{
bool done;
public:
Explosion (int x, int y, int pw, int ph, ImagesManager* il):Sprite(x, y, pw, ph, il, "explosion"){
done = false;
Sprite::setStep(0, 0);
player.setWatcher(this);
Sprite::playImage(33);
}
bool ifDone(){return done;}
void sequenceEnded(string imageName){
done = true;
}
};
class Meteor: public Sprite{
list<Explosion> explosions;
list<Shot> shots;
Sprite ship;
public:
Meteor (int x, int y, int pw, int ph, ImagesManager il, string s, list<Shot> shots, Sprite ship):Sprite(x, y, pw, ph, il, s){
this->shots = shots;
this->ship = ship;
}
bool intersect(Rectangle rec){
bool result = false;
Rectangle bdrec = getMyRectangle();
if ((bdrec->x <= rec->x)&&(bdrec->x+bdrec->w >= rec->x)&&(rec->y <= bdrec->y+bdrec->h)&&(rec->y+rec->h > bdrec->y)){
explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
result = true;
}
else if ((rec->x <= bdrec->x)&&(rec->x+rec->w >= bdrec->x)&&(bdrec->y <= rec->y+rec->h)&&(bdrec->y+bdrec->h > rec->y)){
explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
result = true;
}
delete rec;
delete bdrec;
return result;
}
bool blownUp(){
list<Shot>::iterator it = shots->begin();
for (; it != shots->end(); it++){
if (intersect((it).getMyRectangle())){
shots->erase(it);
return true;
}
}
if (intersect(ship->getMyRectangle()))
return true;
return false;
}
void updateSprite(){
Sprite::updateSprite();
if ((locx < 0)||(blownUp())){
locx = 480;
}
list<Explosion>::iterator it = explosions.begin();
for (; it != explosions.end(); it++)
(*it).updateSprite();
}
void drawSprite(Image screen){
Sprite::drawSprite(screen);
list<Explosion>::iterator it = explosions.begin();
for (; it != explosions.end(); it++){
it->drawSprite(screen);
cout << explosions.size() <<"\n";
if (it->ifDone()){
cout << "to erase\n";
it = explosions.erase(it);
}
}
}
};
class ImagesPlayer
{
string imName;
bool isRepeating, ticksIgnored;
ImagesManager *imsLoader;
int animPeriod;
// period used by animation loop (in ms)
unsigned long animTotalTime;
unsigned long animLastTime;
int showPeriod;
// period the current image is shown (in ms)
unsigned long seqDuration;
// total duration of the entire image sequence (in secs)
int numImages;
int imPosition; // position of current displayable image
ImagesPlayerWatcher *watcher;
void _ImagesPlayer(string , int , bool , ImagesManager* );
public:
ImagesPlayer(){};
ImagesPlayer(string s, int i, bool b, ImagesManager* im){
_ImagesPlayer(s, i, b, im);
}
void modify(string s, int i, bool b, ImagesManager *im){
_ImagesPlayer(s, i, b, im);
}
void updateTick();
int getCurrentImage();
int getCurrentPosition(){ return imPosition; };
void setWatcher(ImagesPlayerWatcher* w){ watcher = w; };
/ updateTick() calls will no longer update the
total animation time or imPosition. /
void stop(){ ticksIgnored = true; };
bool isStopped(){ return ticksIgnored; };
// are we at the last image and not cycling through them?
bool atSequenceEnd(){ return ((imPosition == numImages-1) && (!isRepeating)); };
void restartAt(int );
void resume()
// start at previous image position
{
if (numImages != 0)
ticksIgnored = false;
};
}; // end of ImagesPlayer class
animTotalTime = 0L;
struct timeval tv;
sceKernelLibcGettimeofday(&tv, NULL);
animLastTime = tv.tv_sec*1000 + tv.tv_usec/1000;
if (!imsLoader->isLoaded(imName)) {
cout << imName << " is not known by the ImagesLoader" << endl;
numImages = 0;
imPosition = -1;
ticksIgnored = true;
}
else {
numImages = imsLoader->numImages(imName);
imPosition = 0;
ticksIgnored = false;
}
} // end of ImagesPlayer()
void ImagesPlayer::updateTick()
/ We assume that this method is called every animPeriod ms /
{
if (!ticksIgnored) {
// update total animation time, modulo the animation sequence duration
struct timeval tv;
sceKernelLibcGettimeofday(&tv, NULL);
int animTime = tv.tv_sec*1000 + tv.tv_usec/1000;
animTotalTime = (animTotalTime + (animTime - animLastTime)) % seqDuration;
animLastTime = animTime;
// calculate current displayable image position
imPosition = (int) (animTotalTime / animPeriod); // in range 0 to num-1
if ((imPosition == numImages-1) && (!isRepeating)) { // at end of sequence
ticksIgnored = true; // stop at this image
if (watcher != NULL)
watcher->sequenceEnded(imName); // call callback
}
}
} // end of updateTick()
int ImagesPlayer::getCurrentImage()
{ if (numImages != 0)
return imPosition;
return 0;
} // end of getCurrentImage()
void ImagesPlayer::restartAt(int imPosn)
/ Start showing the images again, starting with image number
imPosn. This requires a resetting of the animation time as
well. /
{
if (numImages != 0) {
if ((imPosn < 0) || (imPosn > numImages-1)) {
cout << "Out of range restart, starting at 0";
imPosn = 0;
}
imPosition = imPosn;
// calculate a suitable animation time
animTotalTime = (long) imPosition * animPeriod;
ticksIgnored = false;
}
} // end of restartAt()
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Your code fragment does not include a call to updateTick(), so we have to assume that when you call ifDone(), updateTick() was never called. From that fragment more than that we cannot tell.
Either way you look it at it we only have your word for it, and it is not plausible on lack of evidence presented.
I suggest that you learn to use the debugger (actually I suggest you use a different tool with a working and usable debugger!).
Clifford
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So my word isn't good enough ;)
I missed out the Sprite class, updateTick is called from updateSprite
I was thinking the problem was with sub-classing an abstract class(ImagesPlayer) or modifying a class variable in the (defined)abstract method but thats not the case since I changed ImagesPlayer from abstract and it still didn't work.
@matic: I did that, thats how i know "sequenceEnded" is called
also isn't "this" a ptr to the instance of the class itself, so how could it be wrong.
class Sprite
{
// default step sizes (how far to move in each update)
static const int XSTEP = 0;
static const int YSTEP = 0;
// default dimensions when there is no image
static const int SIZE = 12;
bool isPlaying;
int pWidth, pHeight; // panel dimensions
// a sprite is updated and drawn only when it is active
// protected vars
protected:
int locx, locy; // location of sprite
int dx, dy; // amount to move for each update
ImagesPlayer player; // for playing a loop of images
ImagesManager *imsLoader;
bool isActive_;
public:
Sprite(int , int , int , int , ImagesManager* , string );
void setImage(string );
void playImage(int );
void loopImage(int );
void stopLooping();
// of the sprite's image
int getWidth(){ return imsLoader->getWidth(imageName); }
// of the sprite's image
int getHeight(){ return imsLoader->getHeight(imageName); }
// of the enclosing panel
int getPWidth(){ return pWidth; }
// of the enclosing panel
int getPHeight(){ return pHeight; }
bool isActive(){ return isActive_; }
void setActive(bool a){ isActive_ = a; }
void setPosition(int x, int y){ locx = x; locy = y; }
void translate(int xDist, int yDist){ locx += xDist; locy += yDist; }
int getXPosn(){ return locx; }
int getYPosn(){ return locy; }
void setStep(int dx, int dy){ this->dx = dx; this->dy = dy; }
int getXStep(){ return dx; }
int getYStep(){ return dy; }
Rectangle getMyRectangle()
{ Rectangle rect = new SDL_Rect;
rect->x = locx;
rect->y = locy;
rect->w = getWidth();
rect->h = getHeight();
return rect;
}
void updateSprite();
void drawSprite(Image );
}; // end of Sprite class
Sprite::Sprite(int x, int y, int w, int h, ImagesManager *imsLd, string name)
{
isActive_ = true;
locx = x; locy = y;
pWidth = w; pHeight = h;
dx = XSTEP; dy = YSTEP;
imsLoader = imsLd;
setImage(name); // the sprite's default image is 'name'
} // end of Sprite()
void Sprite::setImage(string name)
// assign the name image to the sprite
{
imageName = name;
/ image = imsLoader.getImage(imageName);
if (image == NULL) { // no image of that name was found
cout << "No sprite image for " << imageName << endl;
width = SIZE;
height = SIZE;
}
else {
width = image->w;
height = image->h;
}/
// no image loop playing
//player~ImagesPlayer();
isPlaying = false;
} // end of setImage()
void Sprite::playImage(int animPeriod)
/ Switch on loop playing. The total time for the loop is
seqDuration secs. The update interval (from the enclosing
panel) is animPeriod ms. /
{
if (imsLoader->numImages(imageName) > 1) {
player.modify(imageName, animPeriod, false, imsLoader);
isPlaying = true;
}
else
cout << imageName << " is not a sequence of images" << endl;
} // end of loopImage()
void Sprite::loopImage(int animPeriod)
/ Switch on loop playing. The total time for the loop is
seqDuration secs. The update interval (from the enclosing
panel) is animPeriod ms. /
{
if (imsLoader->numImages(imageName) > 1) {
player.modify(imageName, animPeriod, true, imsLoader);
isPlaying = true;
}
else
cout << imageName << " is not a sequence of images" << endl;
} // end of loopImage()
void Sprite::stopLooping()
{
if (isPlaying) {
player.stop();
isPlaying = false;
}
} // end of stopLooping()
void Sprite::updateSprite()
// move the sprite
{
if (isActive()) {
locx += dx;
locy += dy;
if (isPlaying)
player.updateTick(); // update the player
}
} // end of updateSprite()
void Sprite::drawSprite(Image s)
{
int index = 0;
if (isActive()) {
if (isPlaying)
index = player.getCurrentImage();
imsLoader->drawImage(s, imageName, locx, locy, index);
}
} // end of drawSprite()
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Well you told us how it works, but it doesn't work, so no. ;-)
Now you have added code to show where updateTick() is called, but not enough to demonstrate that it is actually called or how or when. We cannot see updateSprite() being called. My point is that we need to see where, when and how these functions are called, and specifically the sequence between updateTick() and ifEnded()
Matik is probably right, no one is going to take the trouble wade through all that code, especially since it is incomplete and highly likely that the problem exists elsewhere, or is contributed to by code not shown.
In essence unless you post sufficient code to allow us to compile, link and debug it, you are probably not going to get very far. Usually when you post that much code without being able to compile it, well meaning people will start picking on the flaws they can see rather than teh one you want addressing. With that much code there is bound to be something someone does not like but which is irellevant to the problem in hand.
I appreciate that you might not want to post all your code, a) because it is yours, and b) because there is probably a lot of it. However I suggest that had you done so, I would have placed a break point at done = true, and one on ifEnded(), and executed the code to see not only that both occur, but that the occur in the expected order. As I suggested however, the Dev-C++ debugger is worse than useless, so I'd be using a different tool with a better debugger.
Is this code multi-threaded? If it is and done is set in one thread and read in another, remember to declare done volatile.
Clifford
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
>maybe its not called on right objects. log the object (this) ptr also etc
matik was right, in one object sequenceEnded was called and then in another ifDone.
The problem is right here
explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
apparently push_back stores a copy of object and not the object itself, is that how it suppose to be or am i doing something wrong?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Okay, I just read up on std::list and that is how it suppose to be. I want to know if you could make your own allocator so that you will just store the object and not a copy.
If not... Anyway will the original Explosion object be destroyed when the function that calls explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader)); exits? or do I have to do it explicitly.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
> I want to know if you could make your own allocator so
> that you will just store the object and not a copy.
That is easily achieved by by creating a std::list of pointers to objects. You then simply place &object rather than object onto the list.
> Anyway will the original Explosion object be destroyed when the function
> that calls explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
> exits?
It will be destroyed as soon immediately after the push_back(); it is a temporary object, not a scoped variable. Place a break point in the destructor to see when precisely this happens. However if you are going to use a list of pointers, the object must not be destroyed at all, which means instantiating it with the 'new' operator. It would be destroyed using 'delete' when the list is 'unpacked' (usually best done in the destructor of the class that contains the list).
Clifford
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I am trying to modify the variable "done" from the class "Explosion" through the "sequenceEnded" function(see classes below)
The problem is after "sequenceEnded", "ifDone()" still returns false.
"sequenceEnded" is called from "updateTick()" in the "ImagesPlayer" class
"ifDone()" is called from the "drawSprite" method in the "Meteor" class from an iterator.
I'm now learning C++ so I might be making a silly mistake or maybe not i don't know.
include <string>
using namespace std;
class ImagesPlayerWatcher
{
public:
virtual void sequenceEnded(string imageName) =0;
};
class Explosion: public Sprite, public ImagesPlayerWatcher{
bool done;
public:
Explosion (int x, int y, int pw, int ph, ImagesManager* il):Sprite(x, y, pw, ph, il, "explosion"){
done = false;
Sprite::setStep(0, 0);
player.setWatcher(this);
Sprite::playImage(33);
}
bool ifDone(){return done;}
void sequenceEnded(string imageName){
done = true;
}
};
class Meteor: public Sprite{
list<Explosion> explosions;
list<Shot> shots;
Sprite ship;
public:
Meteor (int x, int y, int pw, int ph, ImagesManager il, string s, list<Shot> shots, Sprite ship):Sprite(x, y, pw, ph, il, s){
this->shots = shots;
this->ship = ship;
}
bool intersect(Rectangle rec){
bool result = false;
Rectangle bdrec = getMyRectangle();
if ((bdrec->x <= rec->x)&&(bdrec->x+bdrec->w >= rec->x)&&(rec->y <= bdrec->y+bdrec->h)&&(rec->y+rec->h > bdrec->y)){
explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
result = true;
}
else if ((rec->x <= bdrec->x)&&(rec->x+rec->w >= bdrec->x)&&(bdrec->y <= rec->y+rec->h)&&(bdrec->y+bdrec->h > rec->y)){
explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
result = true;
}
delete rec;
delete bdrec;
return result;
}
bool blownUp(){
list<Shot>::iterator it = shots->begin();
for (; it != shots->end(); it++){
if (intersect((it).getMyRectangle())){
shots->erase(it);
return true;
}
}
if (intersect(ship->getMyRectangle()))
return true;
return false;
}
void updateSprite(){
Sprite::updateSprite();
if ((locx < 0)||(blownUp())){
locx = 480;
}
list<Explosion>::iterator it = explosions.begin();
for (; it != explosions.end(); it++)
(*it).updateSprite();
}
void drawSprite(Image screen){
Sprite::drawSprite(screen);
list<Explosion>::iterator it = explosions.begin();
for (; it != explosions.end(); it++){
it->drawSprite(screen);
cout << explosions.size() <<"\n";
if (it->ifDone()){
cout << "to erase\n";
it = explosions.erase(it);
}
};
class ImagesPlayer
{
string imName;
bool isRepeating, ticksIgnored;
ImagesManager *imsLoader;
public:
ImagesPlayer(){};
ImagesPlayer(string s, int i, bool b, ImagesManager* im){
_ImagesPlayer(s, i, b, im);
}
/ updateTick() calls will no longer update the
total animation time or imPosition. /
void stop(){ ticksIgnored = true; };
// are we at the last image and not cycling through them?
bool atSequenceEnd(){ return ((imPosition == numImages-1) && (!isRepeating)); };
// start at previous image position
{
if (numImages != 0)
ticksIgnored = false;
};
}; // end of ImagesPlayer class
void ImagesPlayer::_ImagesPlayer(string nm, int ap, bool isr, ImagesManager il)
{cout << "ImagesPlayer\n";
imName = nm;
animPeriod = ap;
seqDuration = animPeriodil->numImages(imName);
isRepeating = isr;
imsLoader = il;
} // end of ImagesPlayer()
void ImagesPlayer::updateTick()
/ We assume that this method is called every animPeriod ms /
{
if (!ticksIgnored) {
// update total animation time, modulo the animation sequence duration
struct timeval tv;
sceKernelLibcGettimeofday(&tv, NULL);
int animTime = tv.tv_sec*1000 + tv.tv_usec/1000;
} // end of updateTick()
int ImagesPlayer::getCurrentImage()
{ if (numImages != 0)
return imPosition;
return 0;
} // end of getCurrentImage()
void ImagesPlayer::restartAt(int imPosn)
/ Start showing the images again, starting with image number
imPosn. This requires a resetting of the animation time as
well. /
{
if (numImages != 0) {
if ((imPosn < 0) || (imPosn > numImages-1)) {
cout << "Out of range restart, starting at 0";
imPosn = 0;
}
} // end of restartAt()
well, the program is long and i guess no-one will debug it for you. you have to start debugging self.
i suggest maybe add to "sequenceEnded" method something like:
cout << "sequenceEnded called!" << endl;
and see where it goes. maybe its not called on right objects. log the object (this) ptr also etc
Your code fragment does not include a call to updateTick(), so we have to assume that when you call ifDone(), updateTick() was never called. From that fragment more than that we cannot tell.
Either way you look it at it we only have your word for it, and it is not plausible on lack of evidence presented.
I suggest that you learn to use the debugger (actually I suggest you use a different tool with a working and usable debugger!).
Clifford
So my word isn't good enough ;)
I missed out the Sprite class, updateTick is called from updateSprite
I was thinking the problem was with sub-classing an abstract class(ImagesPlayer) or modifying a class variable in the (defined)abstract method but thats not the case since I changed ImagesPlayer from abstract and it still didn't work.
@matic: I did that, thats how i know "sequenceEnded" is called
also isn't "this" a ptr to the instance of the class itself, so how could it be wrong.
class Sprite
{
// default step sizes (how far to move in each update)
static const int XSTEP = 0;
static const int YSTEP = 0;
// default dimensions when there is no image
static const int SIZE = 12;
// image-related
string imageName;
Image image;
int width, height; // image dimensions
// a sprite is updated and drawn only when it is active
// protected vars
protected:
int locx, locy; // location of sprite
int dx, dy; // amount to move for each update
ImagesPlayer player; // for playing a loop of images
ImagesManager *imsLoader;
bool isActive_;
public:
Sprite(int , int , int , int , ImagesManager* , string );
}; // end of Sprite class
Sprite::Sprite(int x, int y, int w, int h, ImagesManager *imsLd, string name)
{
isActive_ = true;
locx = x; locy = y;
pWidth = w; pHeight = h;
dx = XSTEP; dy = YSTEP;
} // end of Sprite()
void Sprite::setImage(string name)
// assign the name image to the sprite
{
imageName = name;
/ image = imsLoader.getImage(imageName);
if (image == NULL) { // no image of that name was found
cout << "No sprite image for " << imageName << endl;
width = SIZE;
height = SIZE;
}
else {
width = image->w;
height = image->h;
}/
// no image loop playing
//player~ImagesPlayer();
isPlaying = false;
} // end of setImage()
void Sprite::playImage(int animPeriod)
/ Switch on loop playing. The total time for the loop is
seqDuration secs. The update interval (from the enclosing
panel) is animPeriod ms. /
{
if (imsLoader->numImages(imageName) > 1) {
player.modify(imageName, animPeriod, false, imsLoader);
isPlaying = true;
}
else
cout << imageName << " is not a sequence of images" << endl;
} // end of loopImage()
void Sprite::loopImage(int animPeriod)
/ Switch on loop playing. The total time for the loop is
seqDuration secs. The update interval (from the enclosing
panel) is animPeriod ms. /
{
if (imsLoader->numImages(imageName) > 1) {
player.modify(imageName, animPeriod, true, imsLoader);
isPlaying = true;
}
else
cout << imageName << " is not a sequence of images" << endl;
} // end of loopImage()
void Sprite::stopLooping()
{
if (isPlaying) {
player.stop();
isPlaying = false;
}
} // end of stopLooping()
void Sprite::updateSprite()
// move the sprite
{
if (isActive()) {
locx += dx;
locy += dy;
if (isPlaying)
player.updateTick(); // update the player
}
} // end of updateSprite()
void Sprite::drawSprite(Image s)
{
int index = 0;
if (isActive()) {
if (isPlaying)
index = player.getCurrentImage();
imsLoader->drawImage(s, imageName, locx, locy, index);
} // end of drawSprite()
> So my word isn't good enough ;)
Well you told us how it works, but it doesn't work, so no. ;-)
Now you have added code to show where updateTick() is called, but not enough to demonstrate that it is actually called or how or when. We cannot see updateSprite() being called. My point is that we need to see where, when and how these functions are called, and specifically the sequence between updateTick() and ifEnded()
Matik is probably right, no one is going to take the trouble wade through all that code, especially since it is incomplete and highly likely that the problem exists elsewhere, or is contributed to by code not shown.
In essence unless you post sufficient code to allow us to compile, link and debug it, you are probably not going to get very far. Usually when you post that much code without being able to compile it, well meaning people will start picking on the flaws they can see rather than teh one you want addressing. With that much code there is bound to be something someone does not like but which is irellevant to the problem in hand.
I appreciate that you might not want to post all your code, a) because it is yours, and b) because there is probably a lot of it. However I suggest that had you done so, I would have placed a break point at done = true, and one on ifEnded(), and executed the code to see not only that both occur, but that the occur in the expected order. As I suggested however, the Dev-C++ debugger is worse than useless, so I'd be using a different tool with a better debugger.
Is this code multi-threaded? If it is and done is set in one thread and read in another, remember to declare done volatile.
Clifford
>maybe its not called on right objects. log the object (this) ptr also etc
matik was right, in one object sequenceEnded was called and then in another ifDone.
The problem is right here
explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
apparently push_back stores a copy of object and not the object itself, is that how it suppose to be or am i doing something wrong?
Okay, I just read up on std::list and that is how it suppose to be. I want to know if you could make your own allocator so that you will just store the object and not a copy.
If not... Anyway will the original Explosion object be destroyed when the function that calls explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader)); exits? or do I have to do it explicitly.
> I want to know if you could make your own allocator so
> that you will just store the object and not a copy.
That is easily achieved by by creating a std::list of pointers to objects. You then simply place &object rather than object onto the list.
> Anyway will the original Explosion object be destroyed when the function
> that calls explosions.push_back(Explosion(rec->x, rec->y, 480, 272, imsLoader));
> exits?
It will be destroyed as soon immediately after the push_back(); it is a temporary object, not a scoped variable. Place a break point in the destructor to see when precisely this happens. However if you are going to use a list of pointers, the object must not be destroyed at all, which means instantiating it with the 'new' operator. It would be destroyed using 'delete' when the list is 'unpacked' (usually best done in the destructor of the class that contains the list).
Clifford
okay thanks