Report a Bug

SIGN IN!

So You Wanna Scale Individual Entities?

How to Scale an Individual Entity in Impact

So you're a big shot, and you wanna scale each Entity by its own factor?

Here's how:

  1. draw: function(){
  2. var ctx = ig.system.context;
  3. ctx.save();
  4. ctx.translate( ig.system.getDrawPos( this.pos.x - this.offset.x - ig.game.screen.x ),
  5. ig.system.getDrawPos( this.pos.y - this.offset.y - ig.game.screen.y ) );
  6. ctx.scale( this.scale, this.scale );
  7. this.currentAnim.draw( 0, 0 );
  8. ctx.restore();
  9. }

Not too bad, huh? Let's break it down.

Impact's HTML5 canvas is broken into two variables:

  • ig.system.canvas - The HTML5 canvas element created on index.html
  • ig.system.context - The corresponding 2d context for ig.system.canvas

In order to bypass Impact's draw() functionality, we need to reference the context directly to draw to the canvas; thus, we cache ig.system.context locally to save us having to type out "ig.system.context" every time and to optimize our code to prevent the Javascript engine having to walk the ig.system namespace every time a reference is encountered.

Next, we .save() the current canvas state (I'll explain why in a bit).

After this we set the canvas's origin = the Entity's position. This part's a little tricky. When we scale the canvas (see the next step), we're scaling the entire canvas. So if we don't scale the canvas directly to the Entity's position, we'd have to offset the Entity's position by the amount we're scaling. This requires some math - not too hard - but it's simpler to .translate() the canvas. Incidentally, compare the parameters passed to .translate() vs. those passed by ig.Entity.draw().

Now we get to the good part - we .scale() our Entity! Notice I'm referencing some .scale property here. ig.Entity does not have this property, so you'll have to add it yourself. If you're reading this tutorial, I'm trusting you already know how to do that. Also note that you can scale individually by the x & y components. Nifty!

Following this we draw our image by calling ig.Animation.draw(). Why not draw directly to the context? Because then we'd have to figure out which frame we're on, grab the proper image and rotate it! Or, we can, you know, use Impact.

Finally, we clean everything up by calling .restore(). Why? Remember the .save() we did earlier? If we don't restore the previous canvas state, all images drawn hereafter will be translated and scaled. This is a good thing - right? I mean, maybe we'll make the most avant-garde indie game ever! One in which the game isn't even displayed right on the screen!

Whew! That wasn't so hard. Now you're feeling cocky and wanna show off your new skillz! Let's throw this into a plugin to show to the community:

  1. ig.module(
  2. 'plugins.scale'
  3. )
  4. .requires(
  5. 'impact.entity'
  6. )
  7. .defines(function(){
  8.  
  9. ig.Entity.inject({
  10.  
  11. scale: { x: 1, y: 1 },
  12. _offset: { x: 0, y: 0 },
  13. _scale: { x: 1, y: 1 },
  14. _size: { x: 1, y: 1 },
  15.  
  16. init: function( x, y, settings ){
  17. this.parent( x, y, settings );
  18. this._offset.x = this.offset.x;
  19. this._offset.y = this.offset.y;
  20. this._size.x = this.size.x;
  21. this._size.y = this.size.y;
  22. this.setScale( this.scale.x, this.scale.y );
  23. },
  24.  
  25. draw: function(){
  26. var ctx = ig.system.context;
  27. ctx.save();
  28. ctx.translate( ig.system.getDrawPos( this.pos.x.round() - this.offset.x - ig.game.screen.x ),
  29. ig.system.getDrawPos( this.pos.y.round() - this.offset.y - ig.game.screen.y ) );
  30. ctx.scale( this._scale.x, this._scale.y );
  31. this.currentAnim.draw( 0, 0 );
  32. ctx.restore();
  33. },
  34.  
  35. setScale: function( x, y ){
  36. var old = {
  37. x: this.size.x, y: this.size.y
  38. };
  39. this.scale.x = x || this.scale.x;
  40. this.scale.y = y || this.scale.y;
  41. this._scale.x = this.scale.x / ig.system.scale;
  42. this._scale.y = this.scale.y / ig.system.scale;
  43. this.offset.x = this._offset.x * this._scale.x;
  44. this.offset.y = this._offset.y * this._scale.y;
  45. this.size.x = this._size.x * this._scale.x;
  46. this.size.y = this._size.y * this._scale.y;
  47. this.pos.x += old.x - this.size.x;
  48. this.pos.y += old.y - this.size.y;
  49. }
  50.  
  51. });
  52.  
  53. });

Now everybody can simply require 'plugins.scale' in their main.js. w00t!

I made several changes when porting this over to a plugin, because we have account for the game's scale. For example, let's say your Entity is set to scale = { x: 1, y: 1 } and ig.system.scale = 2 (Game Scale), in order to scale the Entity properly, we have to scale it relative to the game's scale. Thus, an Entity's scale = Entity Specified Scale / Game Scale. Going back to our example, to scale the Entity properly, we'd take 1 (Entity Scale) / 2 (Game Scale) to calculate the relative scale.

To ensure relative scaling, I provided a .setScale() method that will do all the dirty work for you. Thus, if you want to double the size of your Entity, you'd call myEntity.setScale( 2, 2 ). This will set the .scale = { x: 2, y: 2 }, and set ._scale to the relative scale. Or you can simply change this.scale.x = 2 & this.scale.y = 2 and call myEntity.setScale() sans parameters.

That's it! Feel free to leave me comments, and I'll do my best to answer them. Best!


quidmonkey
@supergoat

The latest plugin can be found on my github: https://github.com/quidmonkey/Scale-Individual-Entities-Plugin

Thanks!
superprat
FIX:
Added a line to make sure that currentAnim is not null for an Entity

if(this.currentAnim != null)
this.currentAnim.draw( 0, 0 );
superprat
I'm getting an error on line 31

Uncaught TypeError: Cannot call method 'draw' of null
Contributed by:
quidmonkey
View Profile
Category:Animation
Updated:June 25, 2012
Rating:
Your Rating (0)
Average Rating (5)
Ready to get to the point?

Your Email will remain private and is only used for good. We promise!


Please use only letters, numbers or underscores.

SIGN UP!