Jump to content

Recommended Posts

Hi guys,

 

Several months ago I was working on a reaction tester where I used GSAP timeline to aid with animating balls around

a screen.  I was happy with my experience using GSAP, but left an issue on the table that I have revisited in the past week

but have had problems diagnosing exactly where the problem lays.... In my codepen, I have a function called movekill()

that is called when the animation completes by hitting a border.

// movekill() is initiated by an onComplete callback method, a property of the GreenSock Animation Platform.
// We use GSAP timelinelite's .to method to 'tween' the x,y and rotation properties and upon animation completion
// this function is called to resupply the animation aka. 'tween' with new x,y, and rotation properties.
function moveKill(tween, obj, border) {
  
   if ( tween.target[0]._gsTransform.x == undefined || tween.target[0]._gsTransform.y == undefined ) {
      alert ("undefined _gsTransform.x or _gsTransform.y")
   } 
   // Get tween.target (shape) current position
   obj.posX = Math.floor(tween.target[0]._gsTransform.x);
   obj.posY = Math.floor(tween.target[0]._gsTransform.y);  
  
   console.log("id " + obj.id + " pX " + obj.posX + " pY " + obj.posY + " " + ((obj.stageRight === true) ? "R" : "L") + " " + ((obj.stageTop === true) ? "T" : "B") + " CurX " + Math.floor(obj.posX) + " CurY " + Math.floor(obj.posY) + " wall " + border );
   var i = obj.index, wall="";
  
   // Check if our shape hit a corner
   if (( obj.posX === 0 && obj.posY === 0 ) ||
       ( obj.posX === 0 && obj.posY === obj.borderY ) ||
       ( obj.posX === obj.borderX && obj.posY === 0 ) ||
       ( obj.posX === obj.borderX && obj.posY === obj.borderY ))
   {
      // debugger;
   }
  
   if ( obj.posX <= 0){
      obj.toX = obj.borderX;
      obj.stageRight = true;
      wall="L";
   } else if ( obj.posX >= obj.borderX){ 
      obj.toX = 0;
      obj.stageRight = false;
       wall="R";     
   }
   if ( obj.posY <= 0 ) {
      obj.toY = obj.borderY;
      obj.stageTop = false;
      wall="T";         
   } else if ( obj.posY >= obj.borderY ){
      obj.toY = 0;
      obj.stageTop = true;
      wall="B";       
   }
   if ( wall === "" ) {
          console.log ("houston we have a problem");
   }
  
   t2[i].kill(null, obj.id);
   // $("h1").text("x: " + obj.posX + " y: " + obj.posY);
   moveTarget(obj, myConst.SET_SPEED_FALSE);
}

Using chrome developer tools I placed a js breakpoint at line 269, to track an intermittent glitch where sometimes a ball hits a

corner and then 1 or 2 things happens: the ball appears in 2 places at once on the screen, or the ball freezes in the corner.

 

I have stepped through my javascript code from the breakpoint until the re-entry of a new timeline and the glitch appears

to be occuring within the GSAP timeline method

 

I am not sure where to go from here.  I have attached a chrome developer tool profile that shows function execution times.  I don't see anything obvious.  The code appears to be running correctly 99% of the time, but that 1% is the part I need help with and would appreciate whatever guidance you can offer.

 

Sorry I trimmed the javascript length in the codepen as best as I could.  The moveTarget() and moveKill() functions are where I use the GSAP timeline.to method and moveKill() is where I kill the tween and resupply moveTarget's obj with a new X and Y position for the ball that has hit the border.

post-45523-0-05694100-1478541844_thumb.png

See the Pen BQyzmQ?editors=1010 by KerryRuddock (@KerryRuddock) on CodePen

Share this post


Link to post
Share on other sites

Sorry, but I tried looking at your code and I'm really at a loss for even knowing where to begin.

I'm also not quite sure how to use the demo in order to see the glitch. I hit the "rapid fire" button and watched a bunch of balls bounce around until I saw game over.

 

Unfortunately we just don't have the time to sift through 400+ lines of code to try to debug stuff like this.

From your description I'm leaning towards there being something that needs fixing in your app logic and not necessarily a GSAP bug.

 

If you can provide a very simple reduced test that replicates the conditions where things don't work OR can more clearly point out something you need help with regarding the GSAP API we will do our best to help.

  • Like 1

Share this post


Link to post
Share on other sites

I didn't have time to sift through all the code either, but it certainly smells like a logic issue. I noticed that you've got two tweens set up for each object, one for x and rotation and then the other for y and rotation. So you've got redundant rotation. You've also got both onCompletes pointed at the same function. That function seems to be handling stuff for both x and y. I could be wrong (because I really didn't have much time to dig into everything), but it sure looks like you've got logic issues in there; one call to moveTarget() results in 2 onComplete calls that each call moveTarget() again, thus you'd get exponentially more and more calls happening and potential conflicts. 

 

Does that help at all? Like Carl said, an even more reduced test case would certainly help identify things quicker. 

Share this post


Link to post
Share on other sites

Thanks for trying Carl, I will keep plugging away on this problem.  

 

I might have a question for you later. but for now I increased the number of balls so it

is easier to reproduce the problem under the Rapid Fire button.

 

Intermittent bugs are a pain to debug, you did right by choosing the rapid fire and there were 8 balls that bounced around.

The problem doesn't always reveal itself. 

 

In the moveKill() function is where I use .kill method and I tried switching it from this:

t2[i].kill(null, obj.id);

to this... didn't help, but worth a try

t2[i].kill({x:true, y:true, rotation:true}, obj.id);

I changed the tests I was using to detect if a ball is at either right border and/or bottom border as I thought

I may be rounding it off 1 pixel away from the right and/or bottom border.  I could see this having an impact

on not being able to detect a border correctly on occasion if the program was unable to resupply new

x,y destinatiion to moveTarget.().  The bottom/right border tests now work when the ball is 1 pixel away.

 

Unfortunately, this change didn't resolve the intermittent issue.


   // Get tween.target (shape) current position
   obj.posX = Math.floor(tween.target[0]._gsTransform.x);
   obj.posY = Math.floor(tween.target[0]._gsTransform.y);
   if (( obj.posX + 1 ) === obj.borderX ) {
      obj.posX = obj.borderX;
   }
   if (( obj.posY + 1 ) === obj.borderY ) {
      obj.posY = obj.borderY;
   }

Share this post


Link to post
Share on other sites

I didn't have time to sift through all the code either, but it certainly smells like a logic issue. I noticed that you've got two tweens set up for each object, one for x and rotation and then the other for y and rotation. So you've got redundant rotation. You've also got both onCompletes pointed at the same function. That function seems to be handling stuff for both x and y. I could be wrong (because I really didn't have much time to dig into everything), but it sure looks like you've got logic issues in there; one call to moveTarget() results in 2 onComplete calls that each call moveTarget() again, thus you'd get exponentially more and more calls happening and potential conflicts. 

 

Does that help at all? Like Carl said, an even more reduced test case would certainly help identify things quicker. 

 

Thanks Jack, you gave me something to think about.

I guess this is where I need your expertise, yep I have 2 tweens running to handle X and Y independently.

When the ball hits either the left or right border, I want OnComplete for X to call moveKill(), and its within this

function where I .kill both the X and Y tweens so that the onComplete does not fire off on the Y.

 

Does this make sense, or am I handling this incorrectly?

Share this post


Link to post
Share on other sites

I was showing you how to do this in your original thread, but you wanted to close the thread off instead.
 
Looking at your code again, I'm seeing the same problems I originally saw...
 
You should use mousedown instead of click/dblclick event listeners for detecting a hit. Think about a gun. It fires a bullet when you pull the trigger, not when you release it. It'd be pretty hard to hit a moving target if it fired when you released the trigger because there is a time difference between the two actions. 
 
And your click/dblclick event listeners are also where your logic errors originate from. You're passing in dirty objects to your moveTarget function, resulting in incorrect stageTop/Left values. This wouldn't be so much of an issue if you were properly tracking your velocities. Your animations are linear, so all you have to do is negate a velocity on a bounce.

// Bounce on x-axis
target.velocityX = -target.velocityX;

 
This would also eliminate the need for your moveKill function. Your animations are linear, so you could precompute every animation ahead of time if you wanted to. Look at how you're already calculating the bounce animation in your moveTarget function.

 

You can figure out the direction the ball is going to move by looking at the sign of the velocities. If the x velocity is negative, it's going to move left. If the y velocity is negative, it's going to move up. 

 

And you can figure which side the ball is going to bounce off of by looking at the duration for each animation. It's going to bounce of the one with the shortest duration. If they're equal, it's going to bounce off of both axes, i.e. the corner.

 
But there's an easier way to do bouncing now. I didn't show you this example because the ModifersPlugin was still in development at the time. Check out how you can do multiple bounce animations a single tween...

See the Pen yJgaYY?editors=0010 by osublake (@osublake) on CodePen


 
.

  • Like 4

Share this post


Link to post
Share on other sites
On 11/8/2016 at 10:52 PM, OSUblake said:


To be honest, when I read your reply 15 months ago about dirty objects I decided to hang up fixing this bug until I improved my javascript skills.

 

I am still not entirely sure what a dirty object is even after googling it.  But I want to share what I did to fix my ball freezing on borders/corners problem:

 

1) Re-factor the code placing all of it in an IIFE, in essence to make sure I wasn't stepping into the global environment.

2) Changed the way the click handler killed the animated target.  Old method I used this:

 


 t2[i].kill(null, target.id);

 

after reading more on the .kill(), I decided to go with this:


t2[i].kill(); 


3) Change the Shape constructor to ensure the ball was never randomly generated a new ball on a border. (rare, but it could happen.)

 

The above 3 changes seemed to help, but did not eliminate the problem entirely until I changed my moveTarget() function. Added a condition to

only use .set under new animation conditions, essentially if ( isSpeedSet === True ) also qualifies a new animation :

 


     if ( isSpeedSet === true ) {
       t2[i].set(animTarget, {x:target.posX, y:target.posY });
     }


 
So there you have it.  My new version on codepen can be found 

See the Pen xYywYQ by KerryRuddock (@KerryRuddock) on CodePen

.   I can't say I have come across other GSAP animations on codepen that repeatedly uses .kill() and .to() method in the same manner as I do.  One final note: I increased the # of balls to 400, 500, and 1000, and no ball freezing occurred.

 

  • Like 1

Share this post


Link to post
Share on other sites

Looks good!

 

The term "dirty" has a lot of meanings in programming, but I was using it to describe objects that have changes made to them that you didn't account for. You're reusing objects, but you weren't correctly resetting all the previous changes made to those objects, so I said they were dirty. In certain cases you were using values carried over from a previous run, which was causing problems.

 

It's been awhile since I looked at your code, and I don't know if you've fixed the problem, but I think the problem was happening somewhere in the if statement starting on line 185 of your v.005 demo. In the else part, some reused objects weren't being correctly reset. It didn't happen every time, just under a certain condition, which I really can't remember.

 

// If I remember correctly, there was a logic problem somewhere in here
if ( setSpeed === true ) {
  ...
} else {
  ...
}

 

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.