Scrolling Camera System

We created a tilemap but what if our map's size is bigger than our window's size. We are controlling our player character in the game and this character has to go everywhere where it can. It can go everywhere already with the current state of the game, however, we aren't able to see that. That's why we have to implement a scrolling camera system on the game as a solution. This system will be focused on the player and follow it. But there is an illusion on it. Actually, the camera doesn't follow the player, when we moved the player, the tilemap will be updated on the screen according to the player's position. 

Let's visualize this mechanic below. The player is represented by red dot, the rectangle with a blue border represents a game display just the user can see and there is a map generated with brown and green rectangles. When the player was moving up, actually, the map moving down. As you see can:
Scrolling Camera System
If the player moves down, the map will be moved up in fact:
Scrolling Camera System
This situation is valid when the player moved through the right and left:
Scrolling Camera System
Everything make sense, I mean we have to calculate positions of every object in the game according to target which is also player.Let's start with typing Camera Class:
import pygame
from pygame.math import Vector2

class Camera:
    def __init__(self, target, width, height):
        self.width = width
        self.height = height
        self.camera = Vector2(target.pos.x, target.pos.y)
        self.target = target
        self.offset = Vector2(0,0)
We are going to use a vector for the camera, also we need to screen's width and height. We will use width and height values for centering the target and camera on the screen. A vector in math has a length and direction. We created a camera vector that will contain the position of our camera and assigned position of target object, initially. The target variable contains our player what we want to focus on with the camera. The offset vector will be used for setting the position of each object in the game.

We are adding a method called scroll for calculating offset value and the tilemap:
    def scroll(self):
        self.pointTarget = self.target.pos - self.camera
        self.camera += self.pointTarget
        self.offset = -self.camera + Vector2(self.width/2, self.height/2)
We calculated pointTarget vector that will be point to the target vector. Because when the player moved, the camera's position must be updated according to the target. So, we will calculate the distance between the camera and the target and this distance that is pointTarget will be added to camera for to the getting position of player. Finally, we calculate the offset value according to the camera vector. If you realize, Vector2(self.width/2, self.height/2) is added to the offset in addition, to do centering to the player on the screen. 

Lets use it in main.py:
import pygame
import random

from player import Player
from tilemap import *
from camera import Camera

...

player = Player(sprites_group, (0,0), (25,25), (0,0,255))

map_data = generate_map(1024, 1024)

camera = Camera(player, 640, 480)

e = Entity(sprites_group, (32,32), (32,32), (255,0,255))

def main():
    ...
    while running:
        ...

        camera.scroll()
        
        # draw
        screen.fill((255,255,255))
        draw_map(screen, map_data, camera)
        for sprite in sprites_group:
             screen.blit(sprite.image, (sprite.rect.topleft + camera.offset))
        
        ...
camera the object is created and passed player object. In the game loop, we are calculating offset with camera.scroll() and we will update positions of all sprites in the game according to camera vector with for loop and render them at the same time. But this scrolling hasn't affected the tilemap yet. As a solution, we have to change to draw_map function a little bit, and actually, we do the same thing that we do for the sprites. Every tile's position has to be updated with offset value as in the code below:
def draw_map(screen, map_data, camera = None):
    ...
                    
    if camera:
        for row in range(MAP_HEIGHT):
            for col in range(MAP_WIDTH):
                screen.blit(textures[map_data[row][col]],
                            (col*TILE_SIZE + camera.offset.x, row*TILE_SIZE + camera.offset.y))
    ... 
So we did it, we can around everywhere on the map.
Scrolling Camera System

Well, maybe I should fill the background with black color. 👀

You are able to reach full of source code on the Github repo.

No comments:

Post a Comment