Pause/Resume Action/Animation on Sprite in Cocos2d - c++

Using Cocos2d-x and C++, I'm trying to play and pause an animation for a sprite.
I'm using version 3.15.1 of Cocos2dx.
I have a class called PlayerSprite which is derrived from the cocos2d::Sprite class. Inside PlayerSprite initialization, I've setup my animation with the following code:
SpriteBatchNode* playerSpriteBatch = SpriteBatchNode::create("player.png");
SpriteFrameCache* spriteFrameCache = SpriteFrameCache::getInstance();
spriteFrameCache->addSpriteFramesWithFile("player.plist");
Vector<SpriteFrame*> animFrames(2);
char str[18] = { 0 };
for (int i = 1; i < 3; i++) {
sprintf(str, "player_idle_%d.png", i);
SpriteFrame* frame = spriteFrameCache->getSpriteFrameByName(str);
animFrames.pushBack(frame);
}
Animation* idleAnim = Animation::createWithSpriteFrames(animFrames, 0.8f);
self->idleAction = self->runAction(RepeatForever::create(Animate::create(idleAnim)));
self->idleAction->setTag(0);
When I run the code, it works fine and the animation loops correctly.
In my void update() method, I am trying to pause/play the action/animation based of weather the player is moving or idle.
I do this with the following code:
const bool isIdleActionRunning = this->getNumberOfRunningActionsByTag(0) > 0 ? true : false;
const bool isMoving = !vel.isZero();
if (!isMoving && !isIdleActionRunning) {
// Player is idle AND animation is not running
// Run animation
this->runAction(idleAction);
} else if (isMoving && isIdleActionRunning) {
// Player is moving but animation is running
// Pause animation
this->stopActionByTag(0);
}
When I run this code now, my character falls, and as soon as he hits the gound, I get an error at this->runAction(idleAction); saying:
Access violation reading location 0xDDDDDDE5
I believe this is caused due to this->stopActionByTag(0) deleting the action pointer. I've tried to clone the action to avoid this but have had no success.

I know this is a bit late and you might already have solved this but here goes...
Your problem is that you cannot use one instance of Action (idleAction) multiple times. So, once you have run it and removed it, it is released and cannot be used. So, you have 2 options now,
Either create a new idleAction Action every time before running the action.
Or, have an idleAction retained and don't run it ever. Instead, create a clone of this idleAction and run a new clone each time. i.e.
idleAction->retain();
const bool isIdleActionRunning = this->getNumberOfRunningActionsByTag(0) > 0 ? true : false;
const bool isMoving = !vel.isZero();
if (!isMoving && !isIdleActionRunning) {
// Player is idle AND animation is not running
// Run animation
Action idleActionClone = idleAction->clone();
this->runAction(idleActionClone);
} else if (isMoving && isIdleActionRunning) {
// Player is moving but animation is running
// Pause animation
this->stopActionByTag(0);
}

Solution: call retain() to keep your action.
It's a matter of memory management of cocos2d-x.
In create() function of your RepeatForever class (derived from Ref), the reference count is set to 1 and there is a line of code ret->autorelease() before returning the object, which means this object will be released automatically at the end of this frame.
You called runAction() function the same frame you created it, the action is retained by ActionManager, it's reference count set to 2, and then 1 at the end of the frame (autorelease).
After your stopping it, it's released by ActionManager, reference count set to 0 and it's deleted. After this you use your action, it will be an access violation method.
*Edit: don't forget to release the action manually when PlayerSprite is deleted, or it's a leak.

When you stop action it's being recycled from memory. In order to play action once more, you have to recreate it. So you may just make a creator function, which returns your animation. The downside is you're recreating animation each time and it'll also play from the beginning (technically you can rewind it).
But I've developed a simpler technique to pause/resume animations:
Let's say you have an action:
action = MoveBy::create(5.0f, Vec2(50, 100));
Now, you can embed this action into Speed action like this:
action = Speed::create(MoveBy::create(5.0f, Vec2(50, 100)), 1.0f);
1.0f - is speed, so it's normal action rate. Now to pause just call:
action->setSpeed(0.0f);
and to resume:
action->setSpeed(1.0f);
you can also use different speed if you need it for some reason or another ;)

Related

Updating image UI with unity

I was trying to make a program that updated the amount of hearts the player has every turn (full, half and empty hearts). When I was doing this I instantiated a gameobject of that prefab as a variable and then assigned it to my UI panel in unity. However (I'm not sure but I think that) the variables used in the update just before are still being referenced after being destroyed in the next loop giving me the error:
MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
Here is the update loop:
void Update()
{
if (HP <= 0)
{
anim.SetBool("Death", true);
Destroy(GameObject.Find("Hearts"));
Destroy(GameObject.Find("Inventory"));
text.alignment = TextAnchor.LowerCenter;
text.text = "\n YOU DIED";
text.fontSize = 150;
text.color = new Color(255, 0, 0);
} else {
foreach (Transform child in GameObject.Find("Hearts").transform)
{
Destroy(child.gameObject);
}
for (var i = 0; i<(int)HP; i++)
{
GameObject Heart = Instantiate(heart, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
Heart.transform.SetParent(GameObject.Find("Hearts").transform);
}
if (HP - (float)((int)HP) == 0.5F) {
GameObject HalfHeart = Instantiate(halfheart, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
HalfHeart.transform.SetParent(GameObject.Find("Hearts").transform);
}
for (var i =0; i<Mathf.Floor(MaxHP-HP); i++)
{
GameObject EmptyHeart = Instantiate(emptyheart, new Vector3(0, 0, 0), Quaternion.identity) as GameObject;
EmptyHeart.transform.SetParent(GameObject.Find("Hearts").transform);
}
}
Is there a way to instantiate a prefab without making a variable?, or a way to make the variable reference temporary so it only lasts one update?
Thank you for your help in advance!
The problem is that once HP goes below zero, every subsequent update will enter the first if-statement and try to delete the "Hearts" and "Inventory" objects over and over. You can solve this by adding a bool called isDead and change the statement to if (HP <= 0 && !isDead) and then set isDead = true inside the block. This will prevent it from entering it twice.
Frankly though, your way of solving things is entirely backwards. As others have pointed out, deleting and instantiating objects every frame is very inefficient, and Transform.Find is also slow. You don't really need to destroy anything at all - you can rather just have a list of hearts and enable/disable an appropriate amount whenever the HP changes. You can have a single half-heart at the end of the list and enable/disable it when appropriate - if you are using a HorizontalLayoutGroup, it will still align correctly. You might want to make it so that you can only change the HP using a property or function (something like ModifyHealth(float amount)), and put the logic for updating the hearts display in there.

How do I make a custom event with VTK?

I'm making a thread software with VTK, where I need to change the model itself in real time, while I need to change his method of rendering. Everything is working fine, but, the problem start with the interactor->start(); , the model data gets updated just fine, but it's only showed on screen when I move The camera. Also I have selected some methods for generating a 3D data from a imagedata file, for that I need to close the vtk window (interactor window) and then the code will reopen it and send the new data generated to it...
I would need something like these:
int force close_window = false; int refresh_interactor = false;
I managed to make the Window close, but only with vtkcommand::Keypressed command, but idk how do I do with a new command :S, I tried the vtkcommand::UserEvent but I didn't found a good information about how to deal with that data (like some way to call it)
the way I'm dealing with VTK is with two threads, the first one, is just about the vtk iren loop, and the second one would manage the models and check if iren requires to be updated.
In my dream code it should be something like this:
=======================================================
bool VTKWindow()
{
...
vtkSmartPointer ator = vtkSmartPointer::New();
iren = vtkSmartPointer::New();
RenWindow = vtkSmartPointer::New();
render->SetBackground(.1, .2, .3);
RenWindow->AddRenderer(renderer);
iren->SetRenderWindow(RenWindow);
if(data_type == voxel_type)
{
Render->AddViewProp(VoxelData);
}
else
{
actor->SetMapper(PolyData);
Render->AddActor(Actor);
}
RenWindow->Render();
iren->Start();
}
void ManageVTK()
{
while true loop...
if(force close_window == true)
do some command to exit the iren loop
if(refresh_interactor == true)
do some command to refresh iren
}
Sorry for the english, it's not my native language, and also sorry about the question format, it's the first time I'm using stackoverflow
It may sounds stupid, but, I found a kind of solution for the problem.
I saw on related links this guy vtkRenderWindowInteractor event loop and threading and, it's almost the same problem...
class VTKNewEvent : public vtkCommand{
public:
vtkTypeMacro(VTKNewEvent , vtkCommand);
static VTKNewEvent * New(){
return new VTKNewEvent ;
}
void Execute(vtkObject * caller, unsigned long vtkNotUsed(eventId), void * vtkNotUsed(callData)){
vtkRenderWindowInteractor *iren = static_cast<vtkRenderWindowInteractor*>(caller);
if (iren_close == true){
iren->GetRenderWindow()->Finalize // Stop the interactor
iren->TerminateApp();
iren_close = false;
}
if (iren_update== true){
renderJanela->Render();
iren_update= false;
}
}
};
bool VTKWindow(){
vtkSmartPointer<VTKNewEvent > IrenRefresh= vtkSmartPointer<VTKNewEvent>::New();
...
iren->CreateRepeatingTimer(1);//this makes that IrenRefresh will be called at every 1ms
iren->AddObserver(vtkCommand::TimerEvent, IrenRefresh);
iren->Start();
...
}
it's simple, but, maybe not the best, but it did Th job, I hope this link will help people that are starting into the VTK world, since threads + rendering loop wasn't a simple job to understand what was going on

How to detect touch except the falling bodies from top in cocos2d-x ios game using c++

In my game there are certain zombies coming from top of the screen.I have stored all zombies sprites in an CCArray.Then using foreach loop I am making them falling down.
I just want to perform combo.It means that whenever I kill a zombie on tap, the combo_counter increases.
On killing two consecutive zombies the combo_counter goes to 2 but if I tap at any other location on the screen the combo_counter should go to 0.
So my problem is how to detect whether I have not tapped a zombie and tapped anyother place on the screen.I am attaching my code also of cctouchbegan method
zombies is a CCArray where all zombie sprites are stored
void Level1::ccTouchesBegan(cocos2d::CCSet *pTouch, cocos2d::CCEvent *pEvent)
{
CCTouch* touch = (CCTouch*)(pTouch->anyObject());
CCPoint location = touch->getLocationInView();
location = CCDirector::sharedDirector()->convertToGL(location);
CCObject* touchedzombie;
CCARRAY_FOREACH(zombies, touchedzombie)
{
if(!((CCSprite*) touchedzombie)->isVisible())
continue;
//
if(((CCSprite*)touchedzombie)==zombies->objectAtIndex(0))
{
// if((CCSprite*(touchedzombie)==zombies-))
if(touchedzombie!=NULL&&((CCSprite*)touchedzombie)->boundingBox().containsPoint(location))
{
this->setScoreonGame();
combo_counter++;
CCString *comboString=CCString::createWithFormat("comboX %d",combo_counter);
zombies_left--;
CCLOG("left = %d",zombies_left);
CCSize tt=((CCSprite*)touchedzombie)->getContentSize();
CCPoint pos_of_sprite=((CCSprite*)touchedzombie)->getPosition();
int rand_die1=Level1::random1();
CCString *str = CCString::createWithFormat("z2%d.png", rand_die1);
changedSprite = CCSprite::create(str->getCString());
CCLOG("Inside index 0");
((CCSprite*)touchedzombie)->setVisible(false);
changedSprite->setPositionX(pos_of_sprite.x);
changedSprite->setPositionY(pos_of_sprite.y);
changedSprite->setScaleX(Utils::getScaleX());
changedSprite->setScaleY(Utils::getScaleY());
this->addChild(changedSprite);
combo=CCLabelTTF::create(comboString->getCString(), "HoboStd", 50);
combo->setColor(ccRED);
combo->setPosition((ccp(changedSprite->getContentSize().width*0.50,changedSprite->getContentSize().height*1.05)));
changedSprite->addChild(combo,40);
this->runAction(CCSequence::create(delayAction,
callSelectorAction,
NULL));
this->removeChild( ((CCSprite*)touchedzombie),true);
this->Level1::reloadZombies();
// touchedzombie=NULL;
}
}
if(((CCSprite*)touchedzombie)==zombies->objectAtIndex(3))
{
// if((CCSprite*(touchedzombie)==zombies-))
if(touchedzombie!=NULL&&((CCSprite*)touchedzombie)->boundingBox().containsPoint(location))
{
// iftouched++;
this->setScoreonGame();
combo_counter++;
CCString *comboString=CCString::createWithFormat("comboX %d",combo_counter);
zombies_left--;
CCLOG("left = %d",zombies_left);
CCSize tt=((CCSprite*)touchedzombie)->getContentSize();
CCPoint pos_of_sprite=((CCSprite*)touchedzombie)->getPosition();
int rand_die1=Level1::random1();
CCString *str = CCString::createWithFormat("z2%d.png", rand_die1);
changedSprite3 = CCSprite::create(str->getCString());
// CCLOG("%s",str->getCString());
// CCLOG("Sprite Toucheddd");
CCLOG("Inside index 4");
// CCLog("width= %f height =%f",tt.width,tt.height);
// CCLog("x location =%f y location =%f",location.x,location.y);
// CCLog("Positon of Sprite X=%f Y=%f",pos_of_sprite.x,pos_of_sprite.y);
((CCSprite*)touchedzombie)->setVisible(false);
changedSprite3->setPositionX(pos_of_sprite.x);
changedSprite3->setPositionY(pos_of_sprite.y);
changedSprite3->setScaleX(Utils::getScaleX());
changedSprite3->setScaleY(Utils::getScaleY());
this->addChild(changedSprite3);
combo=CCLabelTTF::create(comboString->getCString(), "HoboStd", 50);
combo->setColor(ccRED);
combo->setPosition((ccp(changedSprite3->getContentSize().width*0.50,changedSprite3->getContentSize().height*1.05)));
changedSprite3->addChild(combo,40);
this->runAction(CCSequence::create(delayAction,
callSelectorAction3,
NULL));
this->removeChild( ((CCSprite*)touchedzombie),true);
this->Level1::reloadZombies();
touchedzombie=NULL;
}
//..upto 9 indexes...
}
}
First of all, it is not neccesary to do this checks : if(((CCSprite*)touchedzombie)==zombies->objectAtIndex(0))
How CCARRAY_FOREACH works, is it takes each object from the provided CCArray and assigns it to your touchedZombie variable - this means that if there are 10 elements in the array, this code will be run 10 times (exactly!). This means that with the first run, you will fall into the first if check (the one with objectAtIndex(0), with the second it will fall into the if check with objectAtIndex(1). Removing this if's not will not only speed up your function, but also tidy it up.
This would save you a lot of space, plus if you wanted to change something you would only have to do it in one place, which is safer.
Ok, to the problem at hand :
I see two solutions to this :
Leaving your code : you should move the combo code from the if blocks, and replace it with a flag. This flag should be set to false at the beginning of ccToucheBegan, and if you you detect a touch on a zombie, set it to true. Then after the CCARRAY_FOREACH block, this flag will tell you if there was a tap on a zombie or not. Change your combo accordingly.
Changing your code : You could also make the zombies CCMenuItemImages - this way they would have a different callback function than the rest of the screen. So whenever the ccTouchBegan method would be fired, you will know that it wasn't a zombie that was touched.
I hope everything is clear, if not - let me know.

how to force GLUT to redraw the scene before the function returns?

I have an OpenGL project which loads an object file, after the object is loaded i want to be able to move it with keyboard smoothly, so i wrote this block of code to do the job:
while (remains) {
if (x_remains) {
refPosition[0] += speed.x;
if (refPosition[0] > nextPos[0]) {
x_remains = false;
}
}
if (y_remains) {
refPosition[1] += speed.y;
if (refPosition[1] > nextPos[1]) {
y_remains = false;
}
}
if (z_remains) {
refPosition[2] += speed.z;
if (refPosition[2] >= nextPos[2]) {
z_remains = false;
}
}
remains = x_remains || y_remains || z_remains;
glutPostRedisplay();
}
as you see I want to redisplay the scene in the while but when this is executed glutpostredisplay() just sets a flag and the scene is redrawn in the next iteration of the mainloop. my question is how can I redraw the scene before next loop of the while and before the function returns
You won't lose performance or see jerky rendering by going through glutPostRedisplay instead of drawing directly. (Or, if every microsecond really is that precious, why the heck are you using GLUT to begin with?)
On some systems you CAN'T draw outside the display function anyway, because the GL context won't be valid.
This is not unique to GLUT either. All the modern graphics/GUI toolkits I'm familiar with also require you to have a designated display function that gets invoked by an event handler, whether it's WM_PAINT messages, NSView drawRect:, or whatever.
If your program is not updating smoothly, it will be for some other reason.

Invoke a method only once from a class

I have a shape class in which there is a method (hitTest(int,int)) that continuously checks if the mouse is inside its bounds or not. In another method, I keep on checking if the mouse has stayed there for more than 1 sec.
If it has, trigger a function (by notification/event) that runs animation
It it has not, then don't trigger the animation
If it has already triggered the animation and the animation is running but the mouse leaves the area during this, trigger an interrupt function (by notification/event)
//OnLoad _initHover = false;
void update() //called continously in the application per frame
{
if(hitTest(getMouseX(), getMouseY())){
if(!_initHover){
_initHover = true;
_hoverStartTime = getCurrentTime(); //start hover time
cout<<"Start hist test\n";
}
//If it has hovered over the video for 1.0 sec
if((ofGetElapsedTimef() - _hoverStartTime) > 1.0){
cout<<"Hitting continously for 1 sec\n";
notificationCenter->postNotification(new AnimationStartNotification);
}
}
else{
_initHover = false;
notificationCenter->postNotification(new AnimationInterruptNotification);
}
}
The above code runs fine but there's a logical issue I am facing while trying to use. There are multiple instances of the above Shape class and each class consequently has their update() method as well. The mouse cursor has which has animationStarthandler and animationStophandlers is a single class in the whole application.
Issue 1: So, even when one of the shape just notifies the animationStarthandler to fire, the other shape classes on which hit test is false set the animation to interrupt and the animation does not run.
Issue 2: When the hit test succeeds and the cursor has been in the area for more than 1 sec, the hit test will keep on sending the notification to start the animation (anim's duration 1.5 sec approx.) How do I restrict the hit test to fire the animation only once and keep on firing the same animation again and again?
If in the main method of my application, I directly try to fire the animation by calling the method playAnimation in the pointer class, I get the required result. But I want to give this hover timing and animation functionality to the ShapeClass itself. Any suggestions?
I think that you should consider adding a new boolean, which holds the information of the triggering of the animation (called in the code sample _animationTriggered). This prevents shapes that have not triggered the animation to stop it and the animation that triggered it to make it several times.
if(hitTest(getMouseX(), getMouseY()))
{
if(!_initHover)
{
_initHover = true;
_hoverStartTime = getCurrentTime();
cout<<"Start hist test\n";
}
if((ofGetElapsedTimef() - _hoverStartTime) > 1.0)
{
if (!_animationTriggered)
{
cout<<"Hitting continously for 1 sec\n";
notificationCenter->postNotification(new AnimationStartNotification);
_animationTriggered = true;
}
}
}
else
{
if ( _animationTriggered )
{
_initHover = false;
notificationCenter->postNotification(new AnimationInterruptNotification);
_animationTriggered = false;
}
}
Don't forget to initialie this new boolean in the same place as _initHover