Using the Splashkit Camera

Written by Andrew Cain and others on Aug 16 2018

This article provides information on how to use the camera in SplashKit to create games that exist within a world that is larger than the size viewable on the window. In these contexts you can work with game coordinates and move the camera around to determine what the user can see.

Coordinates and the Camera

The camera provides an easy way to move around a game world. It is set to 0,0 by default, however the offset can be changed using the different camera procedures in order to explore a larger game world. When the camera position changes it moves the window to another part of the game world, as you can see in the image below.

Illustration of the camera and game coordinates

When drawing use ‘game coordinates’ for each of your entities, which describe where an entity is in relation to other entities in the game. Some of these may be on the screen or off the screen, but we can let SplashKit take care of that. In the above image the dancing frog is at 250,300 in the game world.

When you draw to the screen SplashKit adjusts the screen position based on the current camera offset. So the dancing frog in the above image is at 200,200 on the screen as the camera is at 50,100. The camera defines the coordinate of the top left of the window.

With the camera you can create the effect of moving past stationary background objects by moving the player and the camera at the same time. As you move the camera, stationary objects drawn to the screen will appear in different locations on the Window.

Where is the mouse?

Once you use the camera, then you need to map any interactions with the screen from screen to world coordinates. For example, when you read mouse input the values are in 'screen coordinates’. In this system the coordinates always have 0,0 as the top left-hand corner, regardless of the camera position.

You can translate between these coordinate systems using the To Screen and To World functions.

  • To Screen converts values from world coordinates to screen coordinates
  • To World converts a screen coordinate to a game world coordinate.
  • To World X convert a screen x value to its location in the game world
  • To World Y convert a screen y value to its location in the game world
  • To Screen X convert a game world x value to its location in the screen
  • To Screen Y convert a game world y value to its location in the screen

Drawing directly to the screen

Some game elements will need to remain in the same place, regardless of the camera position. To achieve this you need to use the Option To Screen. Drawing options allow you to customise each of the drawing instructions. Each drawing operation include a variation that accepts a Drawing Option value.

Example Code

The following program code demonstrates the use of some of these camera operations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include "splashkit.h"

#define SCREEN_BORDER 100

void update_camera_position(double player_x, double player_y)
{
    // Test edge of screen boundaries to adjust the camera
    double left_edge = camera_x() + SCREEN_BORDER;
    double right_edge = left_edge + screen_width() - 2 * SCREEN_BORDER;
    double top_edge = camera_y() + SCREEN_BORDER;
    double bottom_edge = top_edge + screen_height() - 2 * SCREEN_BORDER;

    // Test if the player is outside the area and move the camera
    // the player will appear to stay still and everything else
    // will appear to move :)

    // Test top/bottom of screen
    if (player_y < top_edge)
    {
        move_camera_by(0, player_y - top_edge);
    }
    else if (player_y > bottom_edge)
    {
        move_camera_by(0, player_y - bottom_edge);
    }

    // Test left/right of screen
    if (player_x < left_edge)
    {
        move_camera_by(player_x - left_edge, 0);
    }
    else if (player_x > right_edge)
    {
        move_camera_by(player_x - right_edge, 0);
    }
}

/**
 * Entry point.
 * 
 * Manages the initialisation of data, the event loop, and quitting.
 */
int main()
{
    open_window("Camera Test", 800, 800);

    double player_x = 400, player_y = 400;

    while ( not quit_requested() )
    {
        // Handle input to adjust player movement
        process_events();
        
        if (key_down(LEFT_KEY)) player_x -= 3;
        if (key_down(RIGHT_KEY)) player_x += 3;
        if (key_down(DOWN_KEY)) player_y += 3;
        if (key_down(UP_KEY)) player_y -= 3;

        update_camera_position(player_x, player_y);

        // Redraw everything
        clear_screen(COLOR_BLACK);

        // Draw to the screen
        draw_text("HUD - top left", COLOR_WHITE, 0, 0, option_to_screen());

        // as well as the player who can move
        fill_circle(COLOR_YELLOW, player_x, player_y, 20);

        // including something stationary - it doesn't move
        fill_rectangle(COLOR_WHITE, 400, 200, 10, 10);

        refresh_screen(60);
    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using System;
using SplashKitSDK;

public class Program
{
    public const int SCREEN_BORDER = 100;

    private static void UpdateCameraPosition(double playerX, double playerY)
    {
        // Test edge of screen boundaries to adjust the camera
        double leftEdge = Camera.X + SCREEN_BORDER;
        double rightEdge = leftEdge + SplashKit.ScreenWidth() - 2 * SCREEN_BORDER;
        double topEdge = Camera.Y + SCREEN_BORDER;
        double bottomEdge = topEdge + SplashKit.ScreenHeight() - 2 * SCREEN_BORDER;

        // Test if the player is outside the area and move the camera
        // the player will appear to stay still and everything else
        // will appear to move :)

        // Test top/bottom of screen
        if (playerY < topEdge)
        {
            SplashKit.MoveCameraBy(0, playerY - topEdge);
        }
        else if (playerY > bottomEdge)
        {
            SplashKit.MoveCameraBy(0, playerY - bottomEdge);
        }

        // Test left/right of screen
        if (playerX < leftEdge)
        {
            SplashKit.MoveCameraBy(playerX - leftEdge, 0);
        }
        else if (playerX > rightEdge)
        {
            SplashKit.MoveCameraBy(playerX - rightEdge, 0);
        }
    }

    public static void Main()
    {
        new Window("Camera Test", 800, 800);

        double playerX = 400, playerY = 400;

        while ( ! SplashKit.QuitRequested() )
        {
            // Handle input to adjust player movement
            SplashKit.ProcessEvents();
            
            if (SplashKit.KeyDown(KeyCode.LeftKey))   playerX -= 3;
            if (SplashKit.KeyDown(KeyCode.RightKey))  playerX += 3;
            if (SplashKit.KeyDown(KeyCode.DownKey))   playerY += 3;
            if (SplashKit.KeyDown(KeyCode.UpKey))     playerY -= 3;

            UpdateCameraPosition(playerX, playerY);

            // Redraw everything
            SplashKit.ClearScreen(Color.Black);

            // Draw to the screen
            SplashKit.DrawText(SplashKit.PointToString(Camera.Position), Color.White, 0, 0, SplashKit.OptionToScreen());

            // as well as the player who can move
            SplashKit.FillCircle(Color.Yellow, playerX, playerY, 20);

            // including something stationary - it doesn't move
            SplashKit.FillRectangle(Color.White, 400, 200, 10, 10);
            SplashKit.DrawText("400,400", Color.White, 400, 400);

            SplashKit.RefreshScreen(60);
        }
    }
}