Class: RGame::Engine::Camera

Inherits:
Object
  • Object
show all
Defined in:
lib/rgame/engine/camera.rb

Overview

A 2D camera: a point in the world to look at, plus the world bounds it may not show past. Pure; the offset it produces is applied as a draw-time transform, never baked into a node.

camera = Camera.new(world_width: map.pixel_width, world_height: map.pixel_height)
camera.center_on(player.abs_x, player.abs_y)   # in update, every tick
camera.resolve(view_width, view_height)        # at draw, for one viewport
camera.x, camera.y                             # the offset to translate by

Why the viewport size is an argument and not state

A camera used to be built with its viewport size and clamp against it in center_on. That cannot survive split-screen: the same world is drawn through several viewports whose rects come from the layout and change when a player joins or the window resizes. Clamping has to happen against the rect actually being drawn into, and the difference is visible rather than theoretical — near a world edge, the same target sits at a different place on screen in a half-width viewport than in a full-width one.

So center_on records intent and resolve computes the offset. Nothing calls resolve by hand: the platform resolves each camera against the viewport it is about to draw, which is what keeps the two from drifting.

The camera belongs to a player, not to a scene

A scene may have any number of viewers, so it cannot own "the" camera. RGame::Engine::Player owns one; a scene sets its world bounds when it loads a map, and a CameraFollow component in the world points it.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(world_width: nil, world_height: nil) ⇒ Camera

world_width / world_height bound what the camera may show. Left nil the camera is unbounded and follows its target exactly — which is the right default: a game that has not declared its world yet gets a camera that visibly works, rather than one silently pinned to the origin.



41
42
43
44
45
46
47
48
# File 'lib/rgame/engine/camera.rb', line 41

def initialize(world_width: nil, world_height: nil)
  @world_width = world_width
  @world_height = world_height
  @target_x = 0.0
  @target_y = 0.0
  @x = 0.0
  @y = 0.0
end

Instance Attribute Details

#target_xObject (readonly)

Returns the value of attribute target_x.



34
35
36
# File 'lib/rgame/engine/camera.rb', line 34

def target_x
  @target_x
end

#target_yObject (readonly)

Returns the value of attribute target_y.



34
35
36
# File 'lib/rgame/engine/camera.rb', line 34

def target_y
  @target_y
end

#world_heightObject

Returns the value of attribute world_height.



35
36
37
# File 'lib/rgame/engine/camera.rb', line 35

def world_height
  @world_height
end

#world_widthObject

Returns the value of attribute world_width.



35
36
37
# File 'lib/rgame/engine/camera.rb', line 35

def world_width
  @world_width
end

#xObject (readonly)

Returns the value of attribute x.



34
35
36
# File 'lib/rgame/engine/camera.rb', line 34

def x
  @x
end

#yObject (readonly)

Returns the value of attribute y.



34
35
36
# File 'lib/rgame/engine/camera.rb', line 34

def y
  @y
end

Instance Method Details

#center_on(world_x, world_y) ⇒ Object

Look at this world point. Records the target; the offset is worked out by #resolve, which is the only place that knows how big the view is.



52
53
54
55
56
# File 'lib/rgame/engine/camera.rb', line 52

def center_on(world_x, world_y)
  @target_x = world_x
  @target_y = world_y
  self
end

#resolve(view_width, view_height) ⇒ Object

Work out the draw offset for a viewport of this size, clamped so the view never shows past the world's edges.



60
61
62
63
64
# File 'lib/rgame/engine/camera.rb', line 60

def resolve(view_width, view_height)
  @x = clamp(@target_x - (view_width / 2.0), @world_width, view_width)
  @y = clamp(@target_y - (view_height / 2.0), @world_height, view_height)
  self
end