Runner Power Up Magnet

what is the best way to implement this ?

getChildByName("coin")->runAction(Follow::create(hero));

or

getChildByName("coin")->runAction(MoveTo::create(0.1,Point(hero->getPositionX(), hero->getPositionY())));

both this methods seems buggy so far… sometimes the coin never approaches near hero
is there any better way ?

Hi, I don’t know how implement magnet for coins…
But, I can comment on the latter one.

The MoveTo is not buggy but should not work as expected in this case because your position where the coin has to reach would be changing even after doing:

So, if you’re putting this above code in update function then don’t do so. Also, if your player position is fixed after doing this above code then AFAIK it should work…

Well, now I guess Follow is the correct approach although I’ve not used it before :smiley:

Actually the Follow method is not working like its supposed to…
well i am still working on it though… maybe ill look up to it tomo…
is there any other possible methods i can check upon ??

I don’t know… but definitely setPosition for coins inside update is better option than moveBy or MoveTo where you’re directly setting the target position to be player

Here is reason and how to implement:

  1. Since MoveBy/MoveTo are actions. Suppose, if initial position of coins it at 0,0, and you have use MoveTo to 5,5 intially and now before completing its action you’re again changing it to suppose 6,2.
    So, you won’t have effect like it is following the player. Instead you’ll feel that it is going to the point where you first set to the MoveTo (5,5) and then the second position where you set to MoveTo (6,2) and so on…

But using setPosition directly will have ill effect in the sense that it will directly set its position to the position your player is, which won’t give you effect that coin is moving towards player… But you still can use this in the following way:

In update method,
a) Find the distance between current position of the coin and player. Then set the position of the coin to current position + (distance*dt)/10 for both x and y axis accordingly. 10 is just a random value but you may give accordingly… It is to ensure that it looks smooth and not jerky, i.e not shifting positions by long distances which will then appear as not following the player.
Also, multiplying with dt just ensure that it happens with every frame. You may drop it out but it always a good idea to use dt. Note

Also, if this doesnot give you good effect, it may happen that this is very slow…
So, interestingly you may also use MoveTo with distance*dt/10 and set the time duration of this interval fast enough but try to tweak distance to as low as possible if your duration of this actions is higher inorder to give smooth effect.

Cocos2d-x must have something nicer… Never knew Follow , so I never used it… Still I’ll explore it :stuck_out_tongue:

If you ask me,…If I don’t get any other way… I’ll use MoveTo implementation of what I told you in the implementation part.

One more thing, I forgot to mention is that.
If you’re using MoveTo/MoveBy as what I told in the implementation of previous post then an important thing that you need to take care is that…
If you are sure that your action will be finished before next update function call then its fine… But since Update function is called very very often, AFAIK, as less as 0.02( or 0.2 sec I forgot sorry) seconds then it won’t be possible to give a duration inside the action which would finish before next update call. So, better is to also Stop this particular action before doing this runAction.

So, for the implementation do something as:

PSEUDO:

update(dt)
{
Vector distanceX= currentPlayerPosX-currentCoinPosX
Vector distanceY= currentPlayerPosY-currentCoinPosY
action1= MoveBy(0.05,Vector(distanceX/10,distanceY/10))

this->coinSprite->stopAction(action1); // Don’t use stopAllAction if you don’t want other actions on the coin to get stop.
this->coinSprite->runAction(action1);
}

Note: AFAIK you cannot directly put action1 inside stopAction() because it won’t recognize it. So you’ve to give some Name or something explicitly to action1 and then use that inside stopAction() to stop any action with this name on this sprite, before starting new action…

I am quite sure this would work… But I hope for better method :smile:
If you get to know then please tell us also

thanks a lot for the valuable feedback… ill be working on this tomoz and possibly get back here after i get a solution.

Hey I don’ know why you requoted that code in your last post.
I mean, as I already mentionen, the target position inside MoveTo shouldnot be directly the player’s position otherwise it won’t give you effect like that. Infact you must divide that by some factor and not just this but also you’ve to call this inside update function and each time before running this action you need to remove this actions on the coin…
This is the funda :smiley:

Hi… just for fun, I tried implementing magnetic sprites following some player/ or other body.

I did exactly with what I described earlier… I implemented it using all the 3 things setPosition, MoveBy and MoveTo.
It was just about experimenting the values…

If you’ve not implemented yet, then I can help you further guide. Otherwise, it is exactly what I told you earlier

Only major decision to be taken is whether to keep coins physics bodies or normal sprites only
Difference is physics bodies allows exact shape/body of the coin to touch the player. But the normal sprites allows only boundingBox to be checked. So, if coins are circular then also you’ve to check bounding box which is not a good thing.

But, if we’re using physics bodies then also we’ve problem because then extra things will be on the scene and extra CPU will be used.

Thanks

Hello,
Firstly Thanks a lot for the valuable suggestions.
i have been working on this yesterday and here is a part of the code…
still a lot of reworking to be done…

on the Init
    this->schedule(schedule_selector(GameWorld::MagnetCheck),  1 ); // Check for coins every Secs

void GameWorld::MagnetCheck(float dt)
{
    if(hero->isMagnet){
        magnet(this);
    }
}


void GameScene::magnet(Node *pNode)
{
        for(const auto &child : pNode->getChildren())
        {
                if(child == pNode->getChildByName("coin")){
                child->getPhysicsBody()->setEnable(false);    // Remove the Physics Body So it wont Collide through other objects in the map
                auto  move = MoveTo::create(0.2,Point(hero->getPositionX(), hero->getPositionY())); 
                auto callback = CallFunc::create( [this]() {
                    MagnetCallback(this);
                });
                auto sequence = Sequence::create(move, callback, NULL);
                child->runAction(sequence);
            }
        }
}
void GameScene::MagnetCallback(Node *pNode)
{
    pNode->removeChildByName("coin");
    coins++;
}

Hey, one thing I forgot to mention is that Follow action is not what you were expecting… It was just a camera kind of thing… I implemented Follow but it worked only when I was attaching action with Layer, so it followed the player wherever it goes…which means that it was keeping the player at the center of the screen and then moving the whole layer instead of player :smile:
But while implementing this follow action I got a certain doubt, which I’ve asked in another post.

… but anyways that is another issue. I was just conveying for the information sake…

Ok now looking at your code…

Here are my comments on your code points: wise:
I don’t know you exact implementation of other part. So, I am assuming few things and replying on this basis

  1. What is “this” in your

It is layer… Hope you know!!
So it seems you’re using it as layer only to access children in it with this:

So, its redundancy… “this” will always be known in the current layer so no need until you’re reusing this function in some other layers, in which case your passing “this” as argument is fine. And the way you check children by name “coin” is also fine… Also, hope you’ve set the sprite with the name “coin” because it won’t take name as the variable name but the name which you explicitly set.

OK… it seems you were aware of these things… so I guess no change… I commented so, in case you missed out something

  1. I don’t know whether we can give same names to same sprites… I still need to check this…
    So, if we can given same name “coin” to suppose 5(>=1) sprites then your code up till here is obviously correct
  1. Now, let see what you actually asked :stuck_out_tongue:

It is not correct !!! as I told you earlier also.
With the portion of your code:

Suppose, there is coin at the topside of my game screen and player is at the platform on the bottom side of my screen.
Assume position of my player is not changing. Now, my player becomes magnetic. Then your function is probably correct.
But for the case when the player position is changing then your way won’t give you a smooth effect. It will probably give you jerky effect. And also you’re using callback once the move action is completed. How will this every callback.
In my understanding, since you’re scheduling

then every 1 second if the action move is not completed then it will remove the actions on the coin because the function stack will be removed from the memory. So, placing callback inside sequence is not good idea for your case.
So, better to put if() where you must check that if the coin’s position is at the player’s position, then you may remove that Child with name ‘Coin’

Also, by your function:

It says to remove which sprite with name “coin”.
It seems that we cannot give same Name and Tag to more than 1 sprite because otherwise we do have function as
removeChildByName() but no function like removeChildrenByName()

So, why can’t you simply pass the sprite coin inside this function instead of passing the “this”. then you can simply remove it like:

pNode->removeFromParentAndCleanup(true)

  1. BORED!! Actually your code needs to be changed a lot that is why I am writing so much…

  2. Since update function is called after every 0.02 seconds so it is better to check whether coin has touched the player there only, otherwise if your player is moving then it is not a good idea to check it after 1 or 2 seconds gap.

  3. Also, now finally commenting on your moveTo.
    As I already told you that if player is moving then it is better to give
    (hero->getPositionX())/10 and similarly for Y. Change this 10 to 8 or 5 according to your needs.
    So, this will add in smoothness of your coin’s movement while it is following player.
    Now, change the duration to 0.5 or 0.8 seconds because this should be more than duration we scheduled this function.
    Note: it should be more otherwise it will give you jerky motions.
    Change your scheduled function interval from 2 to 0.2 seconds. Again, it is lesser than MoveTo duration.

Below, I am saying with my experience, rest I cannot guarantee. But, it does give me results assuming below…

Note: the uncompleted action will automatically be removed because it is created under local scope and that scope is removed and created again every 0.2 seconds. So, in such case we don’t have to stop prev Actions on child because they’re automatically removed.

Try it otherwise, I can help you with the code. But I would suggest you to do and learn.
If in case for code, I would need a little information like whether you’re having more than 1 magnetic coins, player is moving or not, etc.

All the best. :smile:

1 Like

hmm i know my code looks bad, i haven’t worked on this part much… but it does the trick
few things to mention… my game does fake scrolling so the player position is almost the same and it isn’t much jerky…
and regarding the coins… i have a separate class which spawns and cleans up the coins…
so far, for my case, i am having no issues with the type of the magnet stuff i wanted and works perfect…

haha great :smiley: