DEV Community

Cover image for Adding Rotation and Scaling🕹️🌟 to my Game Engine - Part 4
Abhishek
Abhishek

Posted on • Edited on

Adding Rotation and Scaling🕹️🌟 to my Game Engine - Part 4

Hello, developers! 👋💖

I'm back with some news! I added rotation and image scaling to our game objects. There are many approaches to doing this and this is what I have done.

In my previous post I tried making a flappy bird game in my engine Helicity.ai and it was clear that we needed Rotation and Scaling for a normal and usable Experience.

I find this sequence of events particularly interesting because *it is like taking a pencil and moving it to where we want to draw something , rotating the page and then drawing and then rotating back the page and bringing the pencil back to where it was. *

Here is the Github

Image description

Changes to how a Game Object is Rendered

Take a peek at the GameObject class. We're calling the 'render' method every frame after clearing the canvas.

// GameObject.js
render() {
  if (this.image.loaded) {
    Renderer.drawImage(
      this.image,
      this.x,
      this.y,
      this.width * this.xscale,
      this.height * this.yscale,
      this.angle,
      this.alpha
    );
  } else {
//No image was provided. Just drawing a black block.
    Renderer.drawBlock(
      "black",
      this.x,
      this.y,
      this.width * this.xscale,
      this.height * this.yscale,
      this.angle,
      this.alpha
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

The GameObject now scales according to 'xscale' and 'yscale' properties and rotates according to the 'angle' property. You have full control over these values 🎮.

But what happens in both those functions? Let's see

Now, let's journey to our Renderer class:

// Renderer.js
drawImage(image, x, y, width, height, angle, alpha) {
  this.context.save();
  this.context.translate(x, y);
  this.context.rotate(-angle);
  this.context.globalAlpha = alpha;
  this.context.drawImage(image, 0, 0, width, height);
  this.context.restore();
}

drawBlock(color, x, y, width, height, angle, alpha) {
  this.context.save();
  this.context.translate(x, y);
  this.context.rotate(-angle);
  this.context.globalAlpha = alpha;
  this.context.fillStyle = color;
  this.context.fillRect(0, 0, width, height);
  this.context.restore();
}
Enter fullscreen mode Exit fullscreen mode

Explanation of the Sequence of Events

Notice something new? The 'drawImage' and 'drawBlock' methods now include 'angle' and 'alpha' parameters.

Let's break down what's happening in the Renderer's drawImage and drawBlock methods. They both follow a similar sequence of operations.

1.** Saving the Canvas State** (context.save()): Before any transformations, we save the current state of the canvas. The save() method pushes the current state onto a stack. This allows us to apply transformations and then revert back to the original state after we've completed our drawing.

  1. Translating the Origin (context.translate(x, y)): The translate(x, y) function shifts the (0,0) origin of our canvas to the provided (x,y) point. This is particularly useful because rotation in the canvas is always performed around the current origin. So by translating the origin to the point around which we want to rotate (in this case, the center of our image or block), we prepare the canvas for rotation.

  2. Rotating the Canvas (context.rotate(-angle)): Now that the origin is at the center of our image or block, we can rotate our canvas. The rotate(angle) function rotates the canvas around the current origin by the specified angle (measured in radians). We use -angle because the canvas's positive rotation is clockwise, while typically in 2D games, we treat positive rotation as counterclockwise.

  3. Setting Transparency (context.globalAlpha = alpha): The globalAlpha property is used to apply transparency to our image or block. It can take a value between 0.0 (fully transparent) and 1.0 (fully opaque).

  4. Drawing the Image or Block: With our transformations set, we can now draw the image or block. For images, we use the drawImage(image, 0, 0, width, height) method, which draws the specified image onto the canvas. For blocks, we use the fillRect(0, 0, width, height) method after setting the fill color with fillStyle = color.

  5. Restoring the Canvas State (context.restore()): After we're done with drawing, we restore the canvas back to its original state by using the restore() method. This pops the most recently saved state from the stack and applies it. All of the transformations we applied (translation, rotation, and globalAlpha change) are undone, so they won't affect any further drawing on the canvas.

This sequence of operations lets us draw images and blocks onto the canvas with control over their position, size, rotation, and transparency, giving a lot of flexibility in how objects can be displayed on the canvas.

Conclusions

Because we did this, we can now make the bird in our game rotate to make it look like it's falling.
You can try out this variant of the game.

//In the update method of the bird
 //change angle of bird to make it look like it is falling. -90 is straight down
    //dont make it indefinitely dependent on velocityY, or else it will keep rotating
    this.angle = Math.min(this.velocityY * 0.05, 90);

Enter fullscreen mode Exit fullscreen mode

However, there are some new challenges.

Physics

This cute function is not good enough to handle rotations lol.

 //Adjusted for scaling but NOT rotation
  static checkCollision(object1, object2) {
    return (
      object1.x < object2.x + object2.width * object2.xscale &&
      object1.x + object1.width * object1.xscale > object2.x &&
      object1.y < object2.y + object2.height * object2.yscale &&
      object1.y + object1.height * object1.yscale > object2.y
    );
  }
Enter fullscreen mode Exit fullscreen mode

Sprite Origins

So as of now a Game Object's origin, its x or y is the top left corner of the image. This is fine for now but it means that all rotations will happen about that point, making it look like the bird is moving in a bit wierd way... but its a bird so it looks normal lol.

If you like this and are interested in the development, do consider joining the discord server!
The Helicity.ai Team 🚀

Top comments (0)