Moving and Animating in 2D with Quorum

This tutorial tells you how to move and animate items on a screen in 2 dimension

Moving and Animating Items

This tutorial will discuss how to make items move, rotate, and scale in your game as well as how to do animation. To assist with this tutorial, you can download this zip file which contains a project with an ImageSheet and all the images used in this tutorial.

Moving Items

  • include your use statement for the Drawable library
  • instantiate a Drawable (call it "spaceship" for this example)
  • load an image into the Drawable (use "assets/alien.png" for this example)
  • add the Drawable to the game

Your code should look like this:

use Libraries.Game.Game
//this line includes the Drawable library
class Main is Game
    // this line declares our drawable object
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        // this line loads the image file to our Drawable
        spaceship:Load("assets/alien.png")

        // this line adds the drawable to the game
        Add(spaceship)
    end

    action Update(number seconds)
    end
end

In order to move the object we need to set its new position on the game screen. Every Drawable has a set of properties attached to it. Two of those properties are its x and y position on the game screen. By default, Drawables are located at position (0, 0), which is the bottom left of the screen. Since we haven't previously set new coordinates, all the objects we've drawn so far have been positioned at the default (0,0) position. If we want to position an item in a different location, we can use the SetX and SetY actions that are part of the Drawable class. These actions both take a number parameter that represents the new x or y coordinate of the Drawable. The modifications to the code below set the x and y coordinates of our spaceship to a new location:

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        Add(spaceship)

        // this line changes the x position of our spaceship drawable to be at 100.
        spaceship:SetX(100)

        // this line changes the y-position of our spaceship drawable to be at 50.
        spaceship:SetY(50)
    end

    action Update(number seconds)
    end
end

The result of this code is shown in the screenshot below.

This is an image of the alien Position

The code above places the image to the position specified as it's initial position on the game screen.

To achieve smooth movement across a number of frames, we must move the image incrementally over a period of time. To do this, we can take advantage of our Update action, which you may remember is called by the engine on each frame while game's main loop is active.

If we want to insert movement code in the update action, navigate to the Update(number seconds) action. This is where we write our code to move the Drawable across the screen on each frame. To do this, we need two number parameters that represent the new x position and the new y position calculated during the game's main loop. We can calculate the new x and y positions by getting the current x and y positions plus some speed value multiplied by the seconds parameter. (Remember that the seconds parameter is the amount of time elapsed since the last time the Update action was called by the engine. The speed value represents how many pixels to move across the screen in a second. So, if the number chosen is small, the movement will be slow, maybe even too slow to see! Likewise, if the number chosen is too large, the movement will not appear smooth; instead, the Drawable will appear to jump across the screen. Play around with different speed values to see their effect.

An example of code that moves a Drawable across the screen is below.

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        Add(spaceship)
    end

    action Update(number seconds)
        // these two statements calculate the new x and y positions by getting the current x and y
        // positions of the Drawable and adding a "speed" factor multiplied by seconds
        number newX = spaceship:GetX() + 100 * seconds
        number newY = spaceship:GetY() + 50 * seconds

        // this statement sets the new x and y positions of the drawable to the values just
        // calculated.  Note we can use SetPosition to set both X and Y at the same time.
        spaceship:SetPosition(newX, newY)
    end
end

If we run our program now (F6), we will see our spaceship glide across the screen in the upwards diagonal direction until it eventually leaves the screen.

Rotating and Scaling Images

In addition to moving across the screen, Drawables can also be rotated, flipped, and scaled using the Quorum Games Library. This section will cover how to rotate, flip, and scale your Drawable during your game. Drawables can be rotated, flipped about an axis, or scaled during a game. Internally, Drawables store information relating to their orientation about a 2D axis, including the degree of rotation and whether the Drawable has been flipped across the x or y axis, and whether the Drawable has been scaled.

Rotation

Let's start with rotation. Drawables have actions that allow us to change their rotation in our game. The SetRotation action takes a number that represents the rotation angle in degrees. So, passing a value of 90 to this action will rotate our Drawable 90 degrees in the clockwise rotation. Negative values can be passed to this function as well, having the effect of changing the direction of rotation. For example, passing a value of -90 to the SetRotation action will rotate the Drawable 90 degrees in the counter-clockwise direction. An example use of the SetRotation action is below.

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        spaceship:SetPosition(100, 100)

        // this line causes the spaceship to be rotated 45 degrees in 
        // the counterclockwise direction
        spaceship:SetRotation(-45)

        Add(spaceship)
    end

    action Update(number seconds)
    end
end

These lines of code produce the following effect on our spaceship:

This is an image of the spaceship rotate 45 degrees
//these lines tell Quorum what libraries we will be using
use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        spaceship:SetPosition(100, 100)

        // this line causes the spaceship to be rotated 45 degrees in 
        // the counterclockwise direction
        spaceship:Rotation(-45)

        Add(spaceship)
    end

    action Update(number seconds)
    end
end

This code produces the same effect on our drawable as above.

So, "What's the difference between SetRotation and Rotate?" you may ask. SetRotation sets the Drawable to rotate to the degree specified by the number parameter. If our Drawable is already rotated 30 degrees when we make a call to SetRotation with 60 degrees, our Drawable will rotate 30 degrees and end up at degree 60. But if our Drawable is already rotated to 30 degrees and we call Rotate with 60 degrees, our Drawable will rotate 60 degrees from that point and end up at degree 90. A demonstration of the differences in these actions is below.

//these lines tell Quorum what libraries we will be using
use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        spaceship:SetPosition(100, 100)

        // this line sets the initial rotation of our spaceship to be at 30 degrees
        spaceship:SetRotation(30)

        // this line tells our spaceship to rotate to degree 60
        spaceship:SetRotation(60)

        Add(spaceship)
    end

    action Update(number seconds)
    end
end

The code above produces the following result:

This is an image of the spaceship SetRotate example
//these lines tell Quorum what libraries we will be using
use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        spaceship:SetPosition(100, 100)

        // this line sets the initial rotation of our spaceship to be at 30 degrees
        spaceship:SetRotation(30)

        // this line tells our spaceship to rotate to degree 60 clockwise
        spaceship:Rotation(60)

        Add(spaceship)
    end

    action Update(number seconds)
    end
end

The code above produces the following result:

This is an image of the spaceship Rotate example

Rotation can also be performed in the Update loop of our game to have the Drawable rotate throughout the game. Simply place the call to rotate the Drawable in the Update(number seconds) action instead of the CreateGame action. An example of this is shown below.

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        spaceship:SetPosition(100,100)
        Add(spaceship)
    end

    action Update(number seconds)
        // this line causes our spaceship to rotate 1 degree every frame
        spaceship:Rotate(1)
    end
end

The code above produces the effect of our spaceship rotating clockwise while the game window is open.

Before we can learn how to move an item around the screen, we must first create a Drawable object. Refer to the Drawing tutorial for a refresher on how Drawables and drawing to the screen works. Remember to perform the following steps:

Another option we have to rotate our Drawable is to use the Rotate action. Like the SetRotate action, Rotate takes a number parameter that represents a number in degrees that we want to rotate our Drawable. An example use of this action is below.

Flipping Images

In addition to rotation, Drawables in Quorum can be flipped horizontally and/or vertically. A horizontal flip is a flip about the x-axis. To flip a Drawable horizontally, we call the FlipX action on the Drawable. A vertical flip is a flip about the y-axis. To flip a Drawable vertically, we call the FlipY action on the Drawable. Example uses of these two actions are shown below. Triangle shapes are used instead of our spaceship image because, in order to demonstrate the results of flipping vertically or horizontally, we needed an image that was not symmetrical.

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable
use Libraries.Game.Graphics.Color

class Main is Game
    action Main
        StartGame()
    end

    action CreateGame
        // create a Color object to change color of our triangles
        Color color

        // create our original triangle and set its position
        Drawable original
        original:LoadFilledTriangle(0, 0, 100, 0, 80, 50, color:Red())
        original:SetPosition(150, 200)
        Add(original)

        // create a triangle we will flip horizontally with the same vertices
        // as the original triangle and set its position
        Drawable flippedX
        flippedX:LoadFilledTriangle(0, 0, 100, 0, 80, 50, color:Green())
        flippedX:SetPosition(350, 200)
        flippedX:FlipX()
        Add(flippedX)

        // create our triangle we will flip vertically with the same vertices
        // as the original triangle and set its position
        Drawable flippedY
        flippedY:LoadFilledTriangle(0, 0, 100, 0, 80, 50, color:Blue())
        flippedY:SetPosition(550, 200)
        flippedY:FlipY()
        Add(flippedY)
    end

    action Update(number seconds)
    end
end

Notice how the vertices of all of our triangles are the same. They differ only in color and position before we flip them. Running the above code produces three triangles on our screen. The red triangle represents the original triangle. The green triangle represents the triangle we flipped horizontally, and the blue triangle represents the triangle we flipped vertically.

This is an image of the 3 triangles example

Scaling

In some applications, you may want to make your image appear larger or smaller on the screen. In Quorum (and many other applications), this is known as scaling. To scale a Drawable in Quorum, we can call the Scale action. This action takes a number parameter that represents the factor the image will be scaled by. For example, a number of 2 will increase the image's size by 2, doubling it. A number of 0.5 will decrease the image's size by a factor of 0.5, halving it. An example program that makes use of scaling is below.

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable

class Main is Game
    Drawable spaceship

    action Main
        StartGame()
    end

    action CreateGame
        spaceship:Load("assets/alien.png")
        spaceship:SetPosition(100, 100)

        // this line sets the scale of the drawable to be 0.25. The drawable will
        // now be 1/4 of its normal size
        spaceship:Scale(0.25)

        Add(spaceship)
    end  

    action Update(number seconds)
    end
end

The image below shows the result of the above code.

This is an image of the spaceship scale small

Animation (Quorum Studio only)

In this part of the tutorial, we will be using the ImageSheet already included in the project. If you have your own images you want to include, see the previous tutorial on Drawing for a refresher on how to create ImageSheets.

This is an image of the assets animation
Texture Atlas

Animations in 2D games works in much the same way as Flip Book Animation. To achieve animation in Quorum, we simply change the image shown by our Drawable after a specified amount of time. It is helpful when doing animations to use a (called ImageSheets in Quorum). The ImageSheet ensures that we do not waste time loading images over and over again. Each image is loaded once and able to be used again and again.

Under the line that begins with class Main is Game we need to add a statement that instantiates an ImageSheet object. For this tutorial, we have called this object "sheet". We also need declare our Drawable here which we will call "girl" in this case.

In order to perform our animation, we need a couple of variable to track some information:


  • fps: an integer variable to keep track of how many times to change frames each second
  • elapsedTime: a number variable to keep track of how long it has been since the last frame change
  • frame: an integer variable to keep track of which frame we are on in the animation

In this example, we are going to chage frames 4 times per second and start at frame 1. The initial elapsed time will be set to 0.

Before we can begin writing our code to change the frames, we need to set up our ImageSheet and Drawable. In the CreateGame action, we need to load our ImageSheet, set an initial image for our drawable, and add it to our game.

In order to have our drawable animate, we need to repeatedly change which image the drawable is displaying each time the elapsedTime exceeds the interval determined by the frame rate. We can do this by taking advantage of our game's main loop and write code in the Update(number seconds) action to load different images to our drawable each time the interval has elapsed. Recall from the Getting Started tutorial, the seconds parameter of the Update action is the number of seconds that elapsed between game frames. We will use this parameter and our elapsedTime variable to decide when to change frames. If the elapsedTime variable is greater than or equal to 1/fps, we know to update the frame displayed by our Drawable. If we update our frame, we need to reset the elapsedTime timer so that we can begin counting again for the next frame change. On each frame, first we add seconds to our counter elapsedTime so that it is updated with the cumulative seconds since the last update, then we test to see if it's exceeded our interval.

Additionally, we need to keep track of what frame we are showing. If we change frames, we need to increment our frame counter. Since we only have three frames, if our frame counter is greater than three, we should reset it to 1. Because our images in our ImageSheet are named in the following format: "Girl frame #"we can use our frame counter with string concatenation to access the correct drawable frame.

The code that accomplishes this animation is shown below.

use Libraries.Game.Game
use Libraries.Game.Graphics.Drawable
use Libraries.Game.Graphics.ImageSheet

class Main is Game
    ImageSheet sheet
    Drawable girl

    //this line declares a number variable representing the frame rate for the animation
    number fps = 4
    //this line creates a number variable to track the time since the last frame change
    number elapsedTime = 0
    //this line creates an integer variable to track which frame we are displaying
    integer frame = 1

    action Main
        StartGame()
    end

    action CreateGame
        //these line load the ImageSheet and initial frame into a drawable and add it to the game
        sheet:Load("resources/girl.atlas")
        girl:Load(sheet:GetDrawable("Girl frame 1"))
        Add(girl)
    end  

    action Update(number seconds)
        //this line adds the seconds since the last frame to our timer variables
        elapsedTime = elapsedTime + seconds
        //this line checks if the elapsed time is greater than our interval for changing frames
        if elapsedTime >= 1/fps
            //if yes, increment the frame
            frame = frame + 1

            //if frame is greater than 3, reset it to one
            if frame > 3
                frame = 1
            end

            //now that you know the next frame, set the Drawable to the next image by 
            //loading the image from the ImageSheet
            girl:Load(sheet:GetDrawable("Girl frame " + frame))

            //once we have updated our drawable, we need to reset the timer.  Instead of
            //reseting to 0, we subtract the length of time between frames to prevent any 
            //extra time from building up over time, slowing our animation
            elapsedTime = elapsedTime - 1/fps
        end
    end
end

Every time you want to have animations in a game, you will have an animation loop similar to this one.

End of Lesson

You have reached the end of the lesson for 2D Graphics. To view more tutorials, press the button below or you can go back to the previous lesson pages.