cocos2d-x: sequential action running on multiple sprites - c++

i have many sprites and i want to move them in screen sequentially. for example 'A' moves to (x1,y1) place then 'B' goes to (x2,y2). i want to run these actions sequentially means first 'A' finishes it's job then 'B' starts.
i have a function that when i call it, returns a sprite and a place and i should send the sprite to that place. in the move function i have somthing like this:
void move(){
for(int i=0;i<10;i++){
pair<CCSprite,CCPoint> x=get();
CCFinitTimeAction* act=CCMoveTo::actionWithDuration(DIST/SPEED,x.second);
x.first->runAction(act)
}
}
now how can i make a delay until a motion finishes. i had put this line after runAction but it didn't work:
while(!act->isDone());

Assuming that get() can be called to fetch sprite 2 right when sprite 1 finishes, you could implement this using a callback that calls get() and starts the next sprite's movement. For this, you need to create a callback method. Assuming you are doing this in a scene, the following code should do it
void YourSceneClass::move() {
moveNext();
}
void YourSceneClass::moveNext() {
pair<CCSprite,CCPoint> x=get();
CCFinitTimeAction* move=CCMoveTo::actionWithDuration(DIST/SPEED,x.second);
CCCallFunc* startNext=CCCallFunc::create( this,
callfunc_selector(YourSceneClass::moveNext) );
CCSequence* act=CCSequence::create(move, startNext, NULL);
x.first->runAction(act);
}
With this code in your scene, you should be able to call move and have the first sprite start moving with the second one moving after that and so on. To make it stop, you have to add an appropriate condition in moveNext().

Related

GLUT animation with glutPostRedisplay

Is there any difference between calling glutPostRedisplay() at the end of my display function and using an idle function callback that does nothing but call my display function? I have seen both ways used in examples and cannot tell the difference by observation.
A main loop generally looks like this:
Process and handle events
calling stuff like glutKeyboardFunc/glutMouseFunc.
Advance/update 3D state (physics/animation etc)
typically in glutIdleFunc
Re-draw the scene if needed
use glutDisplayFunc
glutPostRedisplay simply sets a flag, that tells glut to call the display callback on the next loop iteration. It doesn't actually call display [1] [2].
If you have a game, which always updates every frame this might not be that useful. Maybe if you're alt-tabbed or dragging the window you don't need to be calling display. Or you might be frame limiting by dropping frames (although I'd suggest this).
void idle()
{
...
animatedThing.value += deltaTime
glutPostRedisplay(); //scene is always changing. always call display
}
Having a "dirty" flag becomes more useful when you don't need to re-render continuously. Maybe in something like a 3D modelling package where there isn't any animation and you only move the camera occasionally. Or a GUI where you only need to update when you hover and click on stuff.
void mousedown(int button, int state, int x, int y)
{
if (clickedGUI(x, y))
glutPostRedisplay();
}
void idle()
{
...
if (myKeys[MOVE_VIEW_FORWARD])
{
view.z -= deltaTime;
glutPostRedisplay();
}
}
Anyway, to answer your question, no, there's probably not much difference. However...
I'd put the glutPostRedisplay in idle as above. Calling from within display works but gives up some control you might want later. It's essentially this:
bool shouldDraw = true;
while (1)
{
// check events, input etc
// idle/update state
if (shouldDraw)
{
shouldDraw = false;
// draw
shouldDraw = true;
}
}
I also wouldn't call display from idle from a design perspective as it removes some control from glut. For example if there's a case where glut needs to override the post-redisplay (not that I know of one) it won't be able to.

update sprite position with schedule function

In my game I have an animated sprite. I added box2d to my game to add gravity. now I want to update my sprite's position using the schedule function. I got it working with objective-C, but I want it to be a multiplatform game so now I'm trying it in c++.
In my init I have the following line:
this->schedule(cocos2d::SEL_SCHEDULE(View::tick(1.0f)));
and the method tick:
void View::tick(float dt) {
world->Step(dt, 10, 10);
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if(b->GetUserData() != NULL) {
}
}
}
I get the following error on the line in my init: "cannot cast from type ' void' to member pointer type 'coco2d::sel_schedule' (aka void coco2d::CCobject::*)(float)
I have no idea what to do now. I tried to google, but everyone either uses objective - c or schedule_selector (which apparently doesn't work anymore)
Thanks in advance
You should probably not be using the schedule function to update a single sprite.
In your scene class, override the virtual method for update(...):
virtual void update(float dt)
{
CCLOG("This was called.");
// Do updates for everything in the scene you need to call on a tick.
}
Also, override onEnter() and onExitTransitionDidStart(). In the first, call scheduleUpdate to set up your scene for automatically being called on a tick. In the second, call unscheduledUpdate (as the scene is exiting) to stop the updates.
void IntroScene::onEnter()
{
CCScene::onEnter();
scheduleUpdate();
}
void IntroScene::onExitTransitionDidStart()
{
CCScene::onExitTransitionDidStart();
unscheduleUpdate();
}
Doing it this way gives you explicit control over what gets updated when...you only have a single function called during an update cycle.
HOWEVER, if you really want to do it:
Declare your custom updater like this:
void IntroScene::myUpdate(float dt)
{
CCLOG("Also called");
}
Schedule it like this:
schedule(schedule_selector(IntroScene::myUpdate), 1.0f);
Unschedule it like this:
unschedule(schedule_selector(IntroScene::myUpdate));
** NOTE ** As far as I know, you can only "schedule" objects derived from CCNode.
Was this helpful?

Access violation reading location 0x00000004

I have a Class Player like this:
class Player
{
public:
Player();
~Player(void);
Sprite *sprite;
Sprite *rocket;
void draw(int x, int y, SpaceInvaders *system);
}
and in Player.cpp
void Player::draw(int x, int y, SpaceInvaders *system) {
sprite = system->createSprite("data/player.bmp");
sprite->draw(x, y);
}
Player::~Player(void)
{
sprite->destroy();
rocket->destroy();
}
This draw method is called in a while loop in main:
player.draw(int(xPos), 480-32, system);
The game runs fine until I X the window. That's when I get "Access violation reading location 0x00000004" on the first line in the Player::draw method.
I've read that it might be due to passing a null pointer or null reference but I don't know how to fix this.
Would appreciate any help, thanks!
It's most probably because when closing the window, something gets destroyed while draw is called - most probably the system pointer.
In your case, draw should never be called when the user wants to close its window (unless the x calls another function to start a closing process of some sort). The best would be to first validate that system is not NULL or even better, use a shared pointer to ensure it is still valid when being used. Afterwwards, you shoiuld ensure that draw is not called when the window is closing - that should be done when calling the draw function (or above depending on how you've designed your application.
On a side note, unless you have a caching mechanism (and even that is not the best way to do it), you're recreating your sprite everytime it's being drawn. I suggest you keep a member variable and initialize the sprite in the construtor.

cocos2d onEnter, onExit

eg)
1. When X sprite is touched, pointer of X is stored in class Y
2. when X sprite is removedFromParent, X sprite will notify Y that it's going away
How do I achieve the second step?
Specifically, onExit(of class X) is a good place to do this kind of stuff?
When does onEnter/onExit get called? I don't find the reference helpful on this.
(void) onExit callback that is called every time the CCNode leaves the 'stage'. If the CCNode leaves the 'stage' with a
transition, this callback is called when the transition finishes.
During onExit you can't access a sibling node.
X::onExit()
{
getY()->notify_X_Dies(this);
}
yeah, you are right. onExit is called when you remove node from it's parent, or when it's parent is removed from the stage/other parent.

Only the last object of a vector is drawn when they should all be drawn

I have an Application object that has a vector of GameChar objects as one of its member variables. This GameChar objects contain a GLbatch that is initialized in the constructor and drawn when you call the draw method of the GameChar object. When the program starts I create an application object and three GameChar objects, and I push the GameChar's into the Application object's vector with the push_back() method. Then, inside a game loop, I iterate through the vector and call the draw method of all I'ts objects. The problem is that only the object that was last put in the vector gets drawn. I have assured that the vector is iterated correctly, and that the draw method is called on all the objects, but no matter in what order I call this draw methods (I have tried to iterate the vector from end to start to assure this), only the object that was last inserted to the vector gets drawn.
Here is the code that creates the application and GameChar objects (the constructor gets the arguments in this order: width,height,x,y):
Application app;
GameChar character(0.5f,0.5f,0.0f,0.0f);
GameChar character2(0.5f,0.5f,0.5f,0.5f);
GameChar character3(0.5f,0.5f,-1.0f,-1.0f);
app.characters.push_back(character);
app.characters.push_back(character2);
app.characters.push_back(character3);
And this is the loop:
while (1)
{
vector<GameChar>::size_type sz=characters.size();
unsigned int i;
for (i=0;i<sz;i++)
{
characters[i].update();
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
for (i=0;i<sz;i++)
{
characters[i].draw();
}
glfwSwapBuffers();
}
UPDATE:
Here is the constructor of GameChar:
GameChar::GameChar(GLfloat lWidth,GLfloat lHeight,GLfloat lX,GLfloat lY)
{
xPos=lX;
yPos=lY;
height=lHeight;
width=lWidth;
if (xPos<-1)
{
xPos=-1;
}
if (xPos+width>1)
xPos=1-width;
if (yPos<-1)
yPos=-1;
if (yPos+height>1)
yPos=1-height;
verts[0]=xPos;
verts[1]=yPos;
verts[2]=0.0f;
verts[3]=xPos+width;
verts[4]=yPos;
verts[5]=0.0f;
verts[6]=xPos;
verts[7]=yPos+height;
verts[8]=0.0f;
verts[9]=xPos+width;
verts[10]=yPos+height;
verts[11]=0.0f;
batch.Begin(GL_TRIANGLE_STRIP, 4);
batch.CopyVertexData3f(verts);
batch.End();
}
The draw method:
void GameChar::draw()
{
batch.Draw();
}
And I've updated the game loop with the complete code.
Also, If i try to change the code:
for (i=0;i<sz;i++)
{
characters[i].draw();
}
with this code:
for (i=0;i<sz;i++)
{
GLBatch batch;
batch=characters[i].batch;
batch.Draw();
}
No square gets drawn. I really find all this a nonsense
ANOTHER UPDATE:
If I pop_back the third object after pushing it in, like this:
Application app;
GameChar character(0.5f,0.5f,0.0f,0.0f);
GameChar character2(0.5f,0.5f,0.5f,0.5f);
GameChar character3(0.5f,0.5f,-1.0f,-1.0f);
app.characters.push_back(character);
app.characters.push_back(character2);
app.characters.push_back(character3);
app.characters.pop_back();
nothing is drawn, but if don't push it in, like this:
Application app;
GameChar character(0.5f,0.5f,0.0f,0.0f);
GameChar character2(0.5f,0.5f,0.5f,0.5f);
GameChar character3(0.5f,0.5f,-1.0f,-1.0f);
app.characters.push_back(character);
app.characters.push_back(character2);
The second character is drawn. So I think the problem is that the characters that are already in the vector are invalidated when a new one is pushed, but I don't know why this happens, or how to fix it
I finally managed to fix it! I still don't know what's the cause of the problem, but I've changed the vector of GameChar to a vector of pointers to GameChar objects, and now it works!