The 2D camera system allows you to create scrolling worlds, follow players, zoom in and out, and rotate the view. Talon provides full access to Raylib’s Camera2D system.
Understanding Camera2D
A 2D camera transforms the coordinate system, allowing you to:
- Follow a moving player
- Create parallax scrolling effects
- Zoom in and out of the game world
- Rotate the entire view
Creating a Camera
Create a Camera2D with four parameters:
import "raylib" for Camera2D, Vector2
var camera = Camera2D.new(
Vector2.new(screenWidth / 2.0, screenHeight / 2.0), // offset
Vector2.new(player.x, player.y), // target
0.0, // rotation
1.0 // zoom
)
Camera Properties
- offset: The camera’s position on the screen (usually screen center)
- target: The point in the world the camera is looking at
- rotation: Camera rotation in degrees
- zoom: Camera zoom level (1.0 = normal, 2.0 = 2x zoom)
Set the offset to the screen center to make the camera target appear in the middle of the screen.
Using the Camera
Wrap your drawing code between beginMode2D() and endMode2D():
Raylib.beginDrawing()
Raylib.clearBackground(Color.RayWhite)
// Start camera mode
Raylib.beginMode2D(camera)
// Draw world elements (these are transformed by camera)
Raylib.drawRectangleRec(player, Color.Red)
for (building in buildings) {
Raylib.drawRectangleRec(building, Color.Gray)
}
// End camera mode
Raylib.endMode2D()
// Draw UI elements (not affected by camera)
Raylib.drawText("Score: %(score)", 10, 10, 20, Color.Black)
Raylib.endDrawing()
Draw UI elements outside of beginMode2D() / endMode2D() so they stay fixed on screen.
Following the Player
Update the camera target to follow the player:
while (!Raylib.windowShouldClose()) {
// Handle player movement
if (Raylib.isKeyDown(KeyCode.KEY_RIGHT)) {
player.x = player.x + 2
} else if (Raylib.isKeyDown(KeyCode.KEY_LEFT)) {
player.x = player.x - 2
}
// Make camera follow player
camera.target = Vector2.new(player.x + 20, player.y + 20)
// Draw
Raylib.beginDrawing()
Raylib.clearBackground(Color.RayWhite)
Raylib.beginMode2D(camera)
Raylib.drawRectangleRec(player, Color.Red)
Raylib.endMode2D()
Raylib.endDrawing()
}
Camera Zoom
Implement smooth zooming with the mouse wheel:
import "math" for Math
// Get mouse wheel movement
var wheelMove = Raylib.getMouseWheelMove()
// Apply exponential zoom scaling for smooth feel
camera.zoom = Math.exp(Math.log(camera.zoom) + (wheelMove * 0.1))
// Clamp zoom to reasonable limits
if (camera.zoom > 3.0) {
camera.zoom = 3.0
} else if (camera.zoom < 0.1) {
camera.zoom = 0.1
}
Use exponential scaling (Math.exp and Math.log) for smooth, natural-feeling zoom.
Camera Rotation
Rotate the camera view:
if (Raylib.isKeyDown(KeyCode.KEY_A)) {
camera.rotation = camera.rotation - 1
} else if (Raylib.isKeyDown(KeyCode.KEY_S)) {
camera.rotation = camera.rotation + 1
}
// Clamp rotation to prevent disorientation
if (camera.rotation > 40) {
camera.rotation = 40
} else if (camera.rotation < -40) {
camera.rotation = -40
}
Complete Camera Example
Here’s the camera example from the Talon repository:
Initialize camera and world
import "raylib" for Color, Raylib, Rectangle, Vector2, Camera2D, KeyCode
import "math" for Math
var MAX_BUILDINGS = 100
var screenWidth = 800
var screenHeight = 450
Raylib.initWindow(screenWidth, screenHeight, "2D Camera Example")
var player = Rectangle.new(400, 280, 40, 40)
var buildings = []
var buildColors = []
// Generate random buildings
var spacing = 0
var i = 0
while (i < MAX_BUILDINGS) {
var height = Raylib.getRandomValue(100, 800)
var width = Raylib.getRandomValue(50, 200)
buildings.add(Rectangle.new(
-6000 + spacing,
screenHeight - 130.0 - height,
width,
height
))
spacing = spacing + width
buildColors.add(Color.new(
Raylib.getRandomValue(200, 240),
Raylib.getRandomValue(200, 240),
Raylib.getRandomValue(200, 250),
255
))
i = i + 1
}
Create camera
var camera = Camera2D.new(
Vector2.new(screenWidth / 2.0, screenHeight / 2.0),
Vector2.new(player.x + 20.0, player.y + 20.0),
0.0,
1.0
)
Raylib.setTargetFPS(60)
Update camera in game loop
while (!Raylib.windowShouldClose()) {
// Player movement
if (Raylib.isKeyDown(KeyCode.KEY_RIGHT)) {
player.x = player.x + 2
} else if (Raylib.isKeyDown(KeyCode.KEY_LEFT)) {
player.x = player.x - 2
}
// Camera follows player
camera.target = Vector2.new(player.x + 20, player.y + 20)
// Camera rotation
if (Raylib.isKeyDown(KeyCode.KEY_A)) {
camera.rotation = camera.rotation - 1
} else if (Raylib.isKeyDown(KeyCode.KEY_S)) {
camera.rotation = camera.rotation + 1
}
// Clamp rotation
if (camera.rotation > 40) {
camera.rotation = 40
} else if (camera.rotation < -40) {
camera.rotation = -40
}
// Zoom with mouse wheel
camera.zoom = Math.exp(Math.log(camera.zoom) + (Raylib.getMouseWheelMove() * 0.1))
if (camera.zoom > 3.0) {
camera.zoom = 3.0
} else if (camera.zoom < 0.1) {
camera.zoom = 0.1
}
// Reset camera
if (Raylib.isKeyDown(KeyCode.KEY_R)) {
camera.zoom = 1.0
camera.rotation = 0.0
}
Draw world with camera
Raylib.beginDrawing()
Raylib.clearBackground(Color.RayWhite)
Raylib.beginMode2D(camera)
// Draw ground
Raylib.drawRectangle(-6000, 320, 13000, 8000, Color.DarkGray)
// Draw buildings
var i = 0
while (i < MAX_BUILDINGS) {
Raylib.drawRectangleRec(buildings[i], buildColors[i])
i = i + 1
}
// Draw player
Raylib.drawRectangleRec(player, Color.Red)
// Draw camera target lines
Raylib.drawLine(
camera.target.x, -screenHeight * 10,
camera.target.x, screenHeight * 10,
Color.Green
)
Raylib.drawLine(
-screenWidth * 10, camera.target.y,
screenWidth * 10, camera.target.y,
Color.Green
)
Raylib.endMode2D()
// Draw UI (not affected by camera)
Raylib.drawText("SCREEN AREA", 640, 10, 20, Color.Red)
Raylib.drawText("Free 2D camera controls:", 20, 20, 10, Color.Black)
Raylib.drawText("- Right/Left to move", 40, 40, 10, Color.DarkGray)
Raylib.drawText("- Mouse Wheel to Zoom", 40, 60, 10, Color.DarkGray)
Raylib.drawText("- A/S to Rotate", 40, 80, 10, Color.DarkGray)
Raylib.drawText("- R to reset", 40, 100, 10, Color.DarkGray)
Raylib.endDrawing()
}
Raylib.closeWindow()
Camera in Breakout
The Breakout example uses camera for screen shake effects:
class Game {
construct new() {
_camera = Camera2D.new(
Vector2.new(0.0, 0.0),
Vector2.new(0.0, 0.0),
0.0,
1.0
)
_camera_shake_ttl = 0.0
}
update() {
// Trigger shake on collision
if (collision) {
_camera_shake_ttl = 10.0
}
}
draw() {
// Decrease shake over time
_camera_shake_ttl = Math.max(0.0, _camera_shake_ttl - 0.8)
// Note: The camera can be modified for screen shake
// _camera.offset.y = Math.sin(_camera_shake_ttl * 2.0) * 2.0 * _camera_shake_ttl / 10.0
_camera.beginMode2D()
// Draw game elements
_camera.endMode2D()
}
}
Camera methods can be called directly on the camera instance: camera.beginMode2D() instead of Raylib.beginMode2D(camera).
Advanced Camera Techniques
Smooth Camera Following
Create a smooth camera that lerps to the target:
// Lerp function
var lerp = Fn.new { |a, b, t|
a + (b - a) * t
}
// Smooth camera update
var targetX = player.x + 20
var targetY = player.y + 20
camera.target.x = lerp.call(camera.target.x, targetX, 0.1)
camera.target.y = lerp.call(camera.target.y, targetY, 0.1)
Camera Boundaries
Prevent the camera from showing areas outside the game world:
var worldWidth = 5000
var worldHeight = 2000
// Update camera target
camera.target = Vector2.new(player.x, player.y)
// Clamp camera to world bounds
if (camera.target.x < screenWidth / 2) {
camera.target.x = screenWidth / 2
}
if (camera.target.x > worldWidth - screenWidth / 2) {
camera.target.x = worldWidth - screenWidth / 2
}
Next Steps