Saltar al contenido principal

Módulo Scene

El módulo Scene proporciona un sistema completo de gestión de escenas para juegos y aplicaciones interactivas en JARU. Una escena agrupa sprites, gestiona una cámara con seguimiento suave y límites configurables, integra física con gravedad y resolución de colisiones, e incorpora un GridMap opcional para mapas de tiles.

La escena actúa como contenedor central que coordina la actualización de física, el movimiento de cámara y el culling de sprites fuera de pantalla, simplificando enormemente el desarrollo de juegos con scroll.

Uso

use Display
use Sprite
use Scene

Funciones del Módulo

new

La función Scene.new() crea una nueva escena vacía con valores por defecto.

var scene = Scene.new()

La escena se crea con:

  • Cámara en posición (0, 0)
  • Sin gravedad
  • Sin límites de cámara
  • Sin sprites ni GridMap asignados
  • Modo de cámara CLAMP en ambos ejes
  • 3 iteraciones del solver de física

Constantes

Modos de Cámara

ConstanteValorDescripción
Scene.CLAMP0La cámara se detiene en los límites
Scene.FREE1La cámara se mueve libremente sin restricciones
Scene.WRAP2La cámara envuelve (movimiento toroidal)

Propiedades de Instancia

gravity

Obtiene o establece la gravedad de la escena en píxeles/segundo².

// Obtener gravedad actual
var g = scene.gravity

// Establecer gravedad (simula caída)
scene.gravity = 500

// Sin gravedad (juego espacial)
scene.gravity = 0

iters

Obtiene o establece el número de iteraciones del solver de física. Más iteraciones producen colisiones más precisas pero consumen más CPU.

// Obtener iteraciones actuales
var i = scene.iters

// Establecer más iteraciones para mayor precisión
scene.iters = 5

// Valor mínimo: 1
scene.iters = 1

El valor por defecto es 3, que ofrece un buen equilibrio entre precisión y rendimiento.

Métodos de Instancia

Gestión de Sprites

addSprite

Añade un sprite a la escena. Los sprites añadidos participan en la física y se actualizan con update().

var player = Sprite.load("player.spr")
scene.addSprite(player)
ParámetroTipoDescripción
spriteSpriteSprite a añadir

Retorno

Devuelve true si se añadió correctamente, false en caso de error.

removeSprite

Elimina un sprite de la escena.

scene.removeSprite(enemy)
ParámetroTipoDescripción
spriteSpriteSprite a eliminar

Retorno

Devuelve true si se eliminó correctamente, false si no se encontró.

clearSprites

Elimina todos los sprites de la escena.

scene.clearSprites()

spriteCount

Obtiene el número de sprites en la escena.

var count = scene.spriteCount()
println("Sprites en escena: ", count)

Retorno

Devuelve un entero con la cantidad de sprites.


GridMap

setGridMap

Asigna un GridMap a la escena. Se utiliza para calcular los límites de cámara automáticos con setBoundsFromGrid().

var map = GridMap.new(20, 15)
scene.setGridMap(map)
ParámetroTipoDescripción
gridmapGridMap / nilGridMap a asignar, o nil para desasignar

getGridMap

Obtiene el GridMap asignado a la escena.

var map = scene.getGridMap()
if (map != nil) then
println("GridMap asignado")
end

Retorno

Devuelve el GridMap asignado, o nil si no hay ninguno.


Cámara - Posición Manual

setCamera

Establece la posición de la cámara directamente. Al llamar a este método, se desactiva el seguimiento automático (follow).

scene.setCamera(100, 50)
ParámetroTipoDescripción
xnumberPosición X de la cámara
ynumberPosición Y de la cámara

getCamera

Obtiene la posición actual de la cámara.

var cam = scene.getCamera()
println("Cámara en: ", cam[0], ", ", cam[1])

Retorno

Devuelve una lista [x, y] con la posición de la cámara.


Cámara - Seguimiento

follow

Configura la cámara para seguir automáticamente a un sprite. Opcionalmente se puede especificar un factor de suavizado.

// Seguimiento instantáneo
scene.follow(player)

// Seguimiento suave (0.1 = muy suave, 1.0 = instantáneo)
scene.follow(player, 0.1)
ParámetroTipoDescripción
spriteSpriteSprite objetivo a seguir
smoothFactornumber(Opcional) Factor de suavizado entre 0.0 y 1.0. Por defecto: 1.0
Suavizado de Cámara

Un smoothFactor bajo (como 0.05 o 0.1) produce un efecto de cámara que se desplaza suavemente hacia el objetivo, muy utilizado en plataformas y juegos de aventuras. Un valor de 1.0 centra la cámara instantáneamente.

unfollow

Detiene el seguimiento automático de la cámara. La cámara se queda en su posición actual.

scene.unfollow()

Cámara - Límites

setBounds

Establece los límites manuales de la cámara. La cámara no podrá salir de esta región (dependiendo del modo de cámara).

scene.setBounds(0, 0, 640, 480)
ParámetroTipoDescripción
minXnumberLímite izquierdo
minYnumberLímite superior
maxXnumberLímite derecho
maxYnumberLímite inferior

setBoundsFromGrid

Calcula y establece automáticamente los límites de la cámara a partir del GridMap asignado a la escena. Los límites se ajustan para que la cámara no muestre áreas fuera del mapa.

scene.setGridMap(map)
scene.setBoundsFromGrid()

Si los límites calculados resultan menores que cero (mapa más pequeño que la pantalla), la cámara se centra sobre el mapa automáticamente.

Configuración Automática de Modo

Este método también configura automáticamente el modo de cámara por eje: si el GridMap tiene wrap activado en un eje, el modo de cámara para ese eje se establece como FREE (la cámara se mueve sin restricciones y el GridMap gestiona el tileado toroidal).

Requiere GridMap asignado

Si no hay ningún GridMap asignado a la escena, setBoundsFromGrid() lanzará un error. Llama siempre a setGridMap() antes.

clearBounds

Elimina los límites de la cámara, permitiendo movimiento libre.

scene.clearBounds()

cameraMode

Obtiene o establece el modo de comportamiento de la cámara para los ejes X e Y de forma independiente.

Getter — devuelve una lista [modeX, modeY]:

var mode = scene.cameraMode()
println("Modo X: ", mode[0], " Modo Y: ", mode[1])

Setter — recibe una lista [modeX, modeY]:

// Ambos ejes con clamp
scene.cameraMode([Scene.CLAMP, Scene.CLAMP])

// Wrap horizontal, clamp vertical
scene.cameraMode([Scene.WRAP, Scene.CLAMP])
ModoConstanteComportamiento
0Scene.CLAMPLa cámara se detiene al llegar a los límites
1Scene.FREELa cámara se mueve libremente sin restricciones
2Scene.WRAPLa cámara envuelve de un extremo al otro (toroidal)
nota

Los modos solo tienen efecto cuando hay límites activos (setBounds() o setBoundsFromGrid()). En modo FREE los límites se ignoran aunque estén definidos.


Actualización

update

Actualiza toda la escena: ejecuta el paso de física para todos los sprites, actualiza la posición de la cámara (seguimiento y límites) y recalcula el área de culling.

scene.update()

El orden interno de operaciones en cada llamada es:

  1. Guarda las posiciones previas de todos los sprites (para sweep de colisiones)
  2. Ejecuta PhysicsStep con la lista de sprites, gravedad e iteraciones del solver
  3. Actualiza la posición de la cámara (follow + smoothing)
  4. Aplica los límites de cámara según el modo configurado
  5. Recalcula la región de culling visible
Importante

Llama a update() una vez por frame, antes de dibujar. Este método se encarga de toda la simulación de física, incluyendo la gravedad, velocidades, aceleraciones, colisiones y resolución de penetraciones.


Ejemplo Completo: Plataformas con Scroll

use Display, GridMap, Sprite, Scene, Input

const ACT_LEFT = 0
const ACT_RIGHT = 1
const ACT_JUMP = 2

var draw = Display.draw

const ANCHO = 320
const ALTO = 240
const VIEW_W = 280
const VIEW_H = 240

// ------------------------------------------------
// Pantalla
// ------------------------------------------------
Display.viewWidth = VIEW_W
Display.viewHeight = VIEW_H
Display.open(ANCHO, ALTO)
Display.orientation(0)
Display.mode(2)
draw.colorBG = 0x4040C0
Display.autoClear = true
Display.showBG = false

// ------------------------------------------------
// Controles
// ------------------------------------------------
Input.map(ACT_LEFT, "LEFT")
Input.map(ACT_RIGHT, "RIGHT")
Input.map(ACT_JUMP, "SPACE")

// ------------------------------------------------
// Tilemap
// ------------------------------------------------
var tileset = Bitmap.load("Images/TileSet02.bmp")

var grid = GridMap.load("Maps/prumap_tiles.gmap")
grid.tileset = tileset
grid.wrapX = true
grid.bouncy = true
grid.elasticity = 0.9
grid.solids = [15, 18, 23, 0]
grid.platforms = [13, 3]

// ------------------------------------------------
// Player
// ------------------------------------------------
var player = Sprite.load("Sprites/player.spr")
player.bodyType = Sprite.DYNAMIC
player.mass = 5
player.elasticity = 0.7
player.pivot = [8, 8]
player.x = 200
player.y = 20
player.friction = 0.1
player.velocity = [0, 0]
player.collider = [player.width div 2, player.height div 2, 6]

// ------------------------------------------------
// Escena
// ------------------------------------------------
var scene = Scene.new()
scene.setGridMap(grid)
scene.addSprite(player)
scene.gravity = 200
scene.follow(player, 0.1)

// ------------------------------------------------
// Bucle principal
// ------------------------------------------------
while (true)

var vel = player.velocity

if (Input.pressed(ACT_LEFT)) then
vel[0] = -120
elsif (Input.pressed(ACT_RIGHT)) then
vel[0] = 120
else
vel[0] = 0
end

if (Input.justPressed(ACT_JUMP) and player.onGround()) then
vel[1] = -250
end

player.velocity = vel

// Física de la escena
scene.update()

// Colisión contra el mapa
grid.hits(player)

// Render completo: mapa + sprites + cámara
draw.scene(scene)

// Debug
var cam = scene.getCamera()
draw.text(5, 5, "CAM: " + int(cam[0]).toString() + "," + int(cam[1]).toString(), 0xFFFFFF)

if (player.onGround()) then
draw.text(5, 15, "GROUND", 0x00FF00)
else
draw.text(5, 15, "AIR", 0xFF0000)
end

Display.update()
pause(16)
end



Consideraciones de Rendimiento

Optimización
  • Usa spriteCount() para monitorear la cantidad de sprites activos en la escena.
  • setBoundsFromGrid() solo necesita llamarse una vez al cargar el nivel, no en cada frame.
  • El número de iteraciones del solver (iters) afecta directamente al rendimiento: usa el mínimo necesario para tu juego (el valor por defecto de 3 es adecuado para la mayoría de casos).
  • Para optimizar el dibujado, comprueba la posición del sprite frente a la cámara antes de llamar a draw(). La región de culling se actualiza automáticamente en cada update() y está disponible internamente; puedes consultar la cámara con getCamera() y las dimensiones de pantalla para hacer tus propias comprobaciones.

Plataformas Soportadas

PlataformaSoporteNotas
WindowsSoporte completo
ESP32Soporte completo con LittleFS/SD
Emscripten (Web)Soporte completo
SiFive🚧En desarrollo