Moving On and Off

Download the code for Interactive Animation

We've created our first behavior that turns cycling on and off. Now let's create another switch behavior that moves the sprite when you click it.

You'll need a contemporary browser with javascript turned on to be able to see this page

MovingOnOff Constructor

Let's look at the behavior's constructor code and the two lines that turn it into a child class of the parent class, SwitcherBehavior.

var MovingOnOff = function(movingOnOffArgs) {
  "use strict";		
  SwitcherBehavior.call(this, movingOnOffArgs);
  if (!movingOnOffArgs) {
	movingOnOffArgs = {};
  }
  this.canvasW = movingOnOffArgs.canvasW || 1200; 
  this.canvasH = movingOnOffArgs.canvasH || 800; 
  this.lastTime = 0.0;
  this.name = "movingOnOff";
  
  return this;	
};
MovingOnOff.prototype = Object.create(SwitcherBehavior.prototype);
MovingOnOff.prototype.constructor = MovingOnOff;

Like CyclingOnOff, MovingOnOff inherits properties and methods from SwitcherBehavior. It first calls its parent's constructor which in turn instantiates Switcher and Clickable and passes them callback functions.

Velocity

So far, MovingOnOff is very similar to CyclingOnOff. The real difference occurs in the execute method.

To get our sprite moving, we add two new properties on the spriteProps object literal in the setup script (see js/project/interactive-animation/moving-on-off-setup.js). Those to properties are: vx and vy (velocity X and velocity Y). Velocity has two components, speed and direction. Our vx and vy together make up the sprite's velocity.

Here's an example that shows how we'll use velocity to move our sprite. If the sprite's x and y positions are 100, 100 and we add a vx of 10 to the sprite's x position and a vy of 20 to sprite's y position, we move the sprite to a new position of 110, 120. If we draw a line between the old and new position, we have the sprite's velocity—both its speed and its direction.

sprite velocity, speed and direction

Execute Method

Let's look at the execute method for our MovingOnOff behavior.

MovingOnOff.prototype.execute = function(sprite, context, time) {
  "use strict";	
  //call clickable so it can update its mouseIsOver property with the sprite's current mouseIsOver property
  this.clickable.execute(sprite, context, time);
  
  if (this.lastTime !== 0.0) {	//skip the first time until you have an accurate time count on this.lastTime
  	var timeDelta = time - this.lastTime;
	if (this.switchOn) {	
	  sprite.x += timeDelta/1000 * sprite.vx;
	  sprite.y += timeDelta/1000 * sprite.vy;
	}
  }	
   
  this.lastTime = time;		
};

Like before, we first call Clickable's execute method and pass it the sprite, context, and time arguments. Clickable uses sprite to determine if the mouse is over the sprite.

The first time we call execute, our lastTime property is set to zero. We have a conditional statement that checks that lastTime property and if it's equal to zero, we skip over the main logic of the method and just set lastTime equal to time.

We skip the velocity logic because we need an accurate timeDelta—the difference between time and lastTime. (The time argument, passed to every behavior's execute method, stores the total number of milliseconds that have passed since we loaded our project in the browser.)

The first time we call execute, time's value can be quite variable. If we attached this MoveOnOff behavior to a sprite at the beginning of our project, time's value— depending on the speed of our machine and internet connection—could range from 16 milliseconds to over a 100 milliseconds. If we dynamically attached the behavior to our sprite in the middle of a project, time could have a much larger number.

To avoid this variability, the first time we call execute, we set lastTime equal to time and wait for a second execute call. And execute is called many times every second, so we won't have to wait too long.

The second time we call execute, lastTime is equal to time's previous value, and time has marched forward 16 milliseconds or more. When we calculate timeDelta (timethis.lastTime), we know exactly how many milliseconds have passed between the first and second call to execute. That's the number we need—the number of milliseconds since the previous call—to be able to accurately calculate a velocity in pixels per second. This is the code that does that calculation:

sprite.x += timeDelta/1000 * sprite.vx;
sprite.y += timeDelta/1000 * sprite.vy;

A Fraction of a Second

When we define our sprite's vx and vy in our setup script, we define those velocity values in pixels per second. If we set vx to 100, we move the sprite a hundred pixels along the x axis in one second. Since we're updating the sprite's position with each execute call—and execute is called many times a second—we want just a fraction of that 100 pixels per second to be added to sprite's x position.

Notice we divide timeDelta by 1000. Why do we do that?

Here's the reason: timeDelta holds the number of milliseconds that have passed since execute was last called; there are a thousand milliseconds in one second; when we divide our timeDelta by a 1000, we get the time that's passed as a percentage of a second. We need that percentage so we can move just a fraction of the velocity's pixel-per-second amount. We don't want to move the entire amount because that's the number of pixels we move in a whole second.

Here's a concrete example: if the time between execute calls is 20 milliseconds, we divide 20 by 1000 and get 0.02 or two percent. We use that two percent to calculate the velocity we'll add to our sprite's x and y position. If vx is set to 100 pixels per second, we multiply 100 by 0.02 and get 2 pixels. So we move our sprite two pixels to the right.

Switch On and Off

if (this.switchOn) {	
	  sprite.x += timeDelta/1000 * sprite.vx;
	  sprite.y += timeDelta/1000 * sprite.vy;
}

Notice that we're checking our switchOn property before we add any velocity to our sprite's position. We only move when our switchOn is true and that switch is turned on and off by clicking on the sprite. (Recall that switchOn is set by Switcher's flipIt method which in turn is called by Clickable when the mouse clicks on the sprite.)

Let's look briefly at the set-up for this switch behavior. Open js/project/interactive-animation/moving-on-off-setup.js. Here's the code for the setup script:

var MovingOnOffSetup = function (setupArgs) {
  "use strict";
  if (!setupArgs) {
	setupArgs = {};
  }
  var myMain = setupArgs.myMain;
 
  this.canvasDimensions = myMain.getCanvasDimensions();
  
  this.blueRect = {
	spriteProps:{name:'rect pink', x: 600, y: 400, w: 1200, h: 800, fillColor:'#0e5190', visible:true}, 
	painters:[new RectPainter()], 
	behaviors:[]
  };
  this.myBackground = new ShapeLineGraphic(this.blueRect);
  	

  this.catSheet = [
	{ x:0, y:0, w:400, h:220 },
	{ x:400, y:0, w:400, h:220 },
	{ x:800, y:0, w:400, h:220 },
	{ x:1200, y:0, w:400, h:220 },
	{ x:1600, y:0, w:400, h:220 },
	
	{ x:0, y:220, w:400, h:220 },
	{ x:400, y:220, w:400, h:220 },
	{ x:800, y:220, w:400, h:220 },
	{ x:1200, y:220, w:400, h:220 },
	{ x:1600, y:220, w:400, h:220 },
	
	{ x:0, y:440, w:400, h:220 },
	{ x:400, y:440, w:400, h:220 },
	{ x:800, y:440, w:400, h:220 },
	{ x:1200, y:440, w:400, h:220 },
	{ x:1600, y:440, w:400, h:220 },
  ];
	
  		  
  this.catAnim = {
	spriteProps:{name:'catAnim', x:100, y:320, w:400, h:220, vx: 400, vy: 0, graphics:['img/cat-sprite-sheet.png'], sheets:[this.catSheet], visible:true}, 
	painters:[new BitmapPainter()], 
	behaviors:[new MovingOnOff({canvasW:this.canvasDimensions.w, canvasH:this.canvasDimensions.h}), new CyclingOnOff({cycleTime: 60})]
  };
  
  this.myCatAnim = new BitmapGraphic(this.catAnim);

  this.sprites = [this.myBackground, this.myCatAnim];

  return this;		
};


MovingOnOffSetup.prototype.getSprites = function() {
  "use strict";
	return this.sprites;	
};

This looks similar to the cycling setup script.

Focus on the spriteProps of catAnim. We've added two new properties, vx and vy. We've set vx to 400 pixels per second, so the cat moves 400 pixels to the right each second. We've set vy is 0—we won't move in the y direction at all. You can set vx and vy to negative or positive numbers. Play with them to see how they affect the sprite's movement.

Finally, look at the behavior array. We've added two behaviors to the sprite: MovingOnOff and CyclingOnOff. With those behaviors in place, when you click on the sprite, it both moves and cycles. Click again and the movement and cycling stop.

Test out the MovingOnOff behavior. When you click on the sprite, it moves. Click it a second time and it stops moving.

A Puzzle

Here's a programming puzzle. Click on the sprite and let it move for a while. You'll soon see it move off screen. How could you update the MovingOnOff behavior so the sprite continually moves across the screen? As soon as it exits the screen, it re-appears on the opposite side and starts to traverse the screen again.

Here are a couple hints: First, you'll want to frequently check the sprite's position. Which MovingOnOff method is called several times a second? That method is probably a good place to add the logic that checks the sprite's position.

Next, notice that we're passing an object literal as an argument to MovingOnOff. That object literal contains two name-value pairs. The names are: canvasW and canvasH. The values are: this.canvasDimensions.w and this.canvasDimensions.w. Scroll to the top of our setup script. You'll see the following two lines of code:

this.main = setupArgs.myMain;
this.canvasDimensions = this.main.getCanvasDimensions();

The first line stores a reference to the Main object, a reference that's passed to MovingOnOffSetup from the start script (see js/projects/interactive-animation/moving-on-off-start.js). The next line runs a method on the Main object. That method returns the canvas' size and we pass those dimensions to MovingOnOff. Would knowing the canvas size help you solve the puzzle?

See if you can get the sprite to wrap around after its exits the screen.