Up is Down, Left is Right – How to create a 2D Camera Effect in Monkey X

Creating a 2D camera in a video game is a little trickier than I thought. I started out doing it wrong (didn’t look up enough info), and wasted a lot of time trying to figure out what was going on. Hopefully, I can remove some of that trickiness for you and give you a nice simple example of using a 2D camera in a game.

I looked up stuff in the Monkey X forums and came across this article, but it wasn’t plain enough. So this is my attempt at creating the article I wish existed for Monkey X users and 2D following cameras.

This is all code taken from game #3 in my one game a month challenge. Feel free to use it as needed.

The Theory Behind the Code

I started out by watching the tutorial video for camera movement in the Creating a Simple Game Tutorial. The problem was that it just moves the camera down and is not connected to player movement at all.

I wanted to follow my player around in all 4 directions to increase the play area beyond the screen size. This is where my problems began.

Most of the drawing stuff like scaling and rotating rely on matrix math. Moving the “camera” is done by translating the drawing matrix. This is a fancy way of shifting it one way or another.

So What Does Translating Have to Do With the Camera?

The trick is, when we use a camera in 2D, we aren’t actually moving the camera to follow the player. Instead, we treat the drawing space like a little window into the game, and move the game world into the view.

Think of it like a tablet or smartphone, if you want to see whats to the left you swipe to the right. If you want to see whats down below, you swipe up. Its the same with the camera, if you want to follow your player to the right, you move the world and player left and into your view.

Its that simple. Reading this discussion is what made it click for me.

The Trick

You need to determine how far to move you camera. I ended up using the velocity that the player is moving. However, you have to subtract the velocity from the camera’s “position” to get it to behave right.

This is one of the things I messed up before I figured out that left is right and up is down.

Without further ado, here is a simplified version of…

The Code

So before we begin with the camera we need a few things.

1. A character to follow around
2. A way to move the character
3. Something that lets us know when the character is moving

A character to follow and way to move him

Lets create a simple player with a couple of “vectors” that represent his position and movement.


Class Vec2D
	' Vec2D class shamelessly borrowed from Jim's Small Time Outlaws
	' Youtube channel on creating basic games with Monkey X
	
	Field x:Float
	Field y:Float
	
	Method New(x:Float, y:Float)
		Set(x, y)
	End
	
	Method Set(x:Float, y:Float)
		Self.x = x
		Self.y = y
	End
	
	' My own personal touch to Jim's code
	' Calculates Euclidean Distance
	Method Distance(point:Vec2D)
		Local xdelta:Float = point.x - Self.x
		Local ydelta:Float = point.y - Self.y
		
		Return Sqrt(xdelta * xdelta + ydelta * ydelta)		
	End
	
End

Class Player

	Field position:Vec2D
	Field old_position:Vec2D
	Field velocity:Vec2D
	Field target:Vec2D
	Field distance:Float

	Field speed:Float
	
	Method New(name:String, x:Float, y:Float, speed:Float)
		Self.name = name
		Self.position = New Vec2D(x, y)
		Self.old_position = New Vec2D(x, y)
		Self.speed = speed
		
		Self.velocity = New Vec2D(0, 0)
		Self.distance = 0
	End
	
	Method Draw()
		SetColor(0, 255, 0)
		DrawRect(position.x, position.y, 10, 10)
		SetColor(255, 255, 255)
	End
	
	Method Update()
		' update position
		Self.old_position.Set(position.x, position.y)
		Self.position.Set(position.x + velocity.x, position.y + velocity.y)
		' update velocity
		If (target <> Null)
			If (position.Distance(target)) < distance
				distance = position.Distance(target)
			Else
				' we are not getting closer to the target anymore so stop moving
				velocity.Set(0, 0)
			End
		End
		' update size
		If exp >= 10
			size += 1
			exp -= 10
		End
		' update box
		Self.box.Set(position.x, position.y, 10, 10)
	End
	
	Method SetTarget(x:Float, y:Float)
		Self.target = New Vec2D(x - 5, y - 5)
		
		distance = position.Distance(target)
		
		Local deltax:Float = Abs(target.x - position.x)
		Local deltay:Float = Abs(target.y - position.y)
		Local sum_delta:Float = deltax + deltay
		
		If (target.x > position.x)
			velocity.x = speed * (deltax / sum_delta)
		Else If (target.x < position.x)
			velocity.x = -speed * (deltax / sum_delta)
		End
		
		If (target.y > position.y)
			velocity.y = speed * (deltay / sum_delta)
		Else If (target.y < position.y)
			velocity.y = -speed * (deltay / sum_delta)
		End
		
	End

End

This is a slightly complicated player class that moves the player toward some target position that the user touched on the screen. You can read more about the Vec2D Distance method and the math behind the player SetTarget method in my previous article.

Something that lets us know our character is moving correctly

This particular game is about eating things (think Pacman crossed with snake and my own particular twist). So we are going to put some little dots on the screen for our player to eat. I called them plants (the code that lets him eat them is removed from this example, you can find the full game code on github).


Class PlantLife
	Field position:Vec2D
	
	Method New(x:Float, y:Float)
		Self.position = New Vec2D(x, y)
	End
	
	Method Draw()
		SetColor(0, 100, 255)
		DrawRect(position.x, position.y, 5, 5)
		SetColor(255, 255, 255)
	End
	
End

We will be drawing a few of these to the screen as points of reference to see how we are moving.

The Camera

Surprisingly, the Camera class is really simple.


Class Camera
	' Camera class alos shamelessly borrowed from Jim's Small Time Outlaws
	' Youtube channel on creating basic games with Monkey X 
	' Great stuff you should seriously check it out
	Field original_pos:Vec2D
	Field position:Vec2D
	
	Method New(x:Float=0, y:Float=0)
		Self.position = New Vec2D(x, y)
		Self.original_pos = New Vec2D(x, y)
	End
	
	Method Reset()
		Self.position.Set(original_pos.x, original_pos.y)
	End
	
	' My own take on the update method though
	' This is what we use to follow the player around
	Method Update(velocity:Vec2D)
		Self.position.x -= velocity.x
		Self.position.y -= velocity.y
	End
End

Now Putting It All Together

The actual game app is where it all comes together.


Import classfile

Class AppName Extends App
	
	Field player:Player
	Field cam:Camera
	
	Field plants:List
	Field max_plants:Int
	
	Field map_width:Float
	Field map_height:Float
	
	Method OnCreate()
		SetUpdateRate(60)
		
		map_width = 1000
		map_height = 1000
		
		max_plants = 100
		plants = New List()
		player = New Player("Me", 320, 240, 4.0)
		cam = New Camera( )
		GeneratePlants()
		' Set the random seed for this instance of the game
		Seed = Millisecs()
	End
	
	Method OnUpdate()
		‘ Get our target position to move the player
		If TouchDown(0)
			player.SetTarget(TouchX(0) - cam.position.x, TouchY(0) - cam.position.y)
		End
		‘ Update the camera before we update the player
		‘ Because we don’t want to move the camera before the player starts moving
		cam.Update(player.velocity)
		player.Update()
		‘ Refill our eaten plants
		GeneratePlants()
	End
	
	Method OnRender()
		Cls(255, 255, 255)
		‘ Push the matrix so we can draw a HUD later
		PushMatrix()
		‘ Move our world to the camera position
		Translate(cam.position.x, cam.position.y)
		‘ Draw in our player and our plant life
		player.Draw()
		For Local plant:PlantLife = Eachin plants
			plant.Draw()
		End
		‘ Pop the matrix back out
		PopMatrix()
	End
	
	Method GeneratePlants()
		‘ draw some plants on the screen so we can see when we move
		Local plant_count:Int = plants.Count()
		If plant_count < max_plants
			For Local i:Int = plant_count Until max_plants
				Local xpos:Float = Rnd(25.0, map_width - 20)
				Local ypos:Float = Rnd(25.0, map_height - 20)
				
				plants.AddLast(New PlantLife(xpos, ypos))
			End
		End
	End
End

Function Main()
	New AppName()
End

And that should give you a simple skeleton game with a player that moves around to wherever you touch on the screen and is always in the middle of the camera.

Bonus post coming Wednesday this week on Game Design podcasts.



I Want to Be a Better Developer