0

I am rendering a sprite to a canvas element, and I am trying to avoid sub-pixel positioning in order to avoid blurry images (based on information here: http://seb.ly/2011/02/html5-canvas-sprite-optimisation/).

My problem happens when I try to reassign the x position like such:

this.pos.x = ~~ this.pos.x;

this keeps my sprite from ever moving on the screen, and logging its position to the consol shows I am stuck at my starting position.

Now, if I do not perform the bitwise operator on my position, and simply log the output:

console.log(~~ this.pos.x);

I can see the bitwise operator performing as expecting, showing:

100
101
102
...

Note: My animation is framerate independent, and thus I am multiplying it by a delta and getting floats like:

100.64
101.36
102.04
102.68
103.32000000000001
104.00000000000001
104.68000000000002
...

I update the position here, and only here:

this.pos.x += BE.delta.getSeconds() * this.accelleration;

and thus

this.pos.x += ~~ (BE.delta.getSeconds() * this.accelleration);

is not working

grep
  • 3,986
  • 7
  • 45
  • 67
  • Are you using the current value of `this.pos.x` in your calculations? If so, you might be preventing your movement calculation to proceed by constantly rounding the position back to its original value. Consider keeping separate vars for the true position (as coming from your calculations) and the rounded position that you use for positioning images. – lanzz May 29 '12 at 13:16
  • 1
    I think you missed something out of your question. You said that `console.log(~~ this.pos.x);` shows you `100, 101, 102` as expected, but you didn't show how/where you're updating `this.pos.x` in order for that to happen. The only thing updating `this.pos.x` that you've shown is the thing you said doesn't work. Can you show us the other update? – T.J. Crowder May 29 '12 at 13:17
  • Keep in mind - http://stackoverflow.com/questions/2983206/bitwise-and-in-javascript-with-a-64-bit-integer – henryabra May 29 '12 at 13:20

2 Answers2

2

The bitwise NOT operator (~) just truncates the fractional portion off the number (e.g., ~~100.1 is 100; ~~-100.1 is -100). So naturally if you're adding values of less than 1 to this.pos.x, if you do this.pos.x = ~~this.pos.x, you're never going to see it increase, because you're constantly wiping out those fractional values.

You haven't provided a lot of context, but it may be that you want to allow this.pos.x to remain a floating point number, but use a floored version of it for rendering, e.g.:

var renderX = ~~this.pos.x;
// Use `renderX` rather than `this.pos.x` to render to the canvas;
// `renderX` will remain 100 while `this.pos.x` will gradually climb
// from 100 to 100.1, 100.5, 100.999999, etc. Once `this.pos.x`
// reaches 101 (or 101.1, etc.), `renderX` will become 101.

Separately, it may be more appropriate to round rather than flooring. If this.pox.x is 100.999999999, surely it makes more sense to render it at 101 than 100?

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 2
    It depends if the number is positive or negative, it `floors` positive numbers but `ceils` negative. `Math.ceil( -5.7 ) === ~~-5.7 true Math.floor( 5.7 ) === ~~5.7 true` – Esailija May 29 '12 at 13:21
  • 1
    @Esailija or in other words, it _truncates_. – Alnitak May 29 '12 at 13:27
  • This is what I had to do. Keep a separate 'renderable' value. Thanks! – grep May 29 '12 at 14:12
2

When you animate the images you should keep the position in a variable separate from the image position. That way you can keep the floating point value for calculations and make the image position an integer value.

If you chop off the fraction part of the coordinates, and then use that as input for the next calculation, the values will just jump back and forth:

100 + 0.64 = 100.64
~~100.64 = 100
100 + 0.64 = 100.64
~~100.64 = 100
...

Also, use the round method to convert the floating point value to the closest integer value:

x += BE.delta.getSeconds() * this.accelleration;
this.pos.x = Math.round(x);
Guffa
  • 687,336
  • 108
  • 737
  • 1,005