You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
radium/radium/engine.coffee

150 lines
4.0 KiB
CoffeeScript

window.pass = undefined # This will give us Python-like noop calls
class Engine
constructor: (@resource_manager) ->
@canvases = {}
@fps = 45
@last_frameskip_collection = Math.floor(Date.now())
@frameskip = 0
@current_frameskip = 0
@current_frame = 0
@scenes = {}
@objects = {}
@sounds = {}
@sprites = {}
@tilesets = {}
@named_timers = {}
@unnamed_timers = []
# The following is to make the engine object available in library methods
# FUTURE: Iterate over easing methods and create an engine-bound version
# in the local engine object, overriding the prototype methods?
@ease.engine = this
@draw.engine = this
@resource_manager.engine = this
addCanvas: (canvas, label = "") =>
@canvases[label] = util.unpackElement(canvas)
createSurface: (label) =>
@canvases[label] = document.createElement("canvas")
getSurface: (label) =>
if typeof label == "string"
return @canvases[label]?.getContext("2d")
else if label.tagName == "CANVAS"
return label.getContext("2d")
else
return label
updateCanvasSize: (canvas, w, h) =>
canvas.width = w
canvas.height = h
canvas.style.width = "#{w}px"
canvas.style.height = "#{h}px"
start: () =>
@loop()
loop: () =>
@iteration()
iteration: () =>
# Calculation of next frame and frameskip collection check
frame_interval = (1000 / @fps)
current_frame = Date.now()
next_frame = current_frame + frame_interval
@current_frame += 1
if Math.floor(current_frame) > @last_frameskip_collection
@frameskip = @current_frameskip
@current_frameskip = 0
@last_frameskip_collection = Math.floor(current_frame)
# Actual iteration code...
# Then process registered timers.
@updateTimers()
# Now we run the scene-specific code.
scene.iteration() for name, scene of @scenes when scene.active
# Frameskip check and triggering next iteration
if Date.now() < next_frame
setTimeout(@iteration, (next_frame - Date.now()))
else
# Frameskip!
overtime = Date.now() - next_frame
skipped_frames = Math.floor(overtime / frame_interval)
@current_frameskip += skipped_frames
@current_frame += skipped_frames
@skipTimers(skipped_frames)
belated_timeout = overtime % frame_interval
setTimeout(@iteration, belated_timeout)
updateTimers: =>
for timer in @unnamed_timers.concat (val for key, val of @named_timers)
timer.step()
# Clean up finished timers
@unnamed_timers = @unnamed_timers.filter (obj) -> not obj.finished
for timer_name, timer of @named_timers
if timer.finished
delete @named_timers[timer_name]
skipTimers: (frames) =>
for timer in @unnamed_timers.concat (val for key, val of @named_timers)
timer.skip(frames)
setInitialScene: (scene) =>
@initial_scene = scene
setPreloadScene: (scene) =>
@preload_scene = scene
switchPreloadScene: =>
@preload_scene.addTargetSurface(@canvases[""])
switchInitialScene: =>
@preload_scene?.removeTargetSurface(@canvases[""])
@initial_scene.addTargetSurface(@canvases[""])
createScene: (name) =>
scene = new Scene(this, name)
@initial_scene ?= scene
@scenes[name] = scene
createObject: (name) =>
@objects[name] = new Object(this, name)
createSound: (name, sound) =>
@sounds[name] = new Sound(this, name, @resource_manager.getSound(sound))
createSprite: (name, image) =>
@sprites[name] = new Sprite(this, name, @resource_manager.getImage(image))
createSprites: (sprites) =>
for name, image of sprites
@createSprite(name, image)
createTileset: (name, image, tile_width, tile_height) =>
@tilesets[name] = new Tileset(this, name, @resource_manager.getImage(image), tile_width, tile_height)
getScene: (name) =>
if typeof name == "string" then @scenes[name] else name
getObject: (name) =>
if typeof name == "string" then @objects[name] else name
getSound: (name) =>
if typeof name == "string" then @sounds[name] else name
getSprite: (name) =>
if typeof name == "string" then @sprites[name] else name
getTileset: (name) =>
if typeof name == "string" then @tilesets[name] else name