Tutorials
Tutorials

This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this tutorial in English

Level complete - proyecto de ejemplo

En este proyecto de ejemplo, que puedes abrir desde el editor o descargar desde GitHub, demostramos efectos para mostrar el conteo de puntuación que podría ocurrir cuando se completa un nivel. Una puntuación total se cuenta hacia arriba y aparecen tres estrellas cuando se alcanzan distintos niveles. El ejemplo también usa la funcionalidad de recarga para una iteración rápida al ajustar valores.

La escena se activa mediante un mensaje del juego. El mensaje contiene la puntuación total obtenida y en qué niveles deben aparecer las tres estrellas. Cuando esto ocurre, el texto de encabezado (“Level completed!”) aparece gradualmente mientras se reduce a su tamaño regular (100%). Esto se hace en on_message() más abajo.

Después de completar la animación del texto de encabezado, la puntuación total empieza a contar hacia arriba. Cada vez que esto ocurre, la puntuación actual se incrementa en un paso pequeño. Luego comprobamos si se ha cruzado uno de los niveles de estrella, en cuyo caso empieza la animación de una estrella (ver más abajo). Mientras no hayamos alcanzado la puntuación objetivo, la puntuación total se anima con un efecto de rebote. También crecerá hacia un máximo de escala cuanto más cerca esté de la puntuación total. De la misma manera, su color cambia gradualmente de blanco a verde. Esto se hace en inc_score().

Cada vez que aparece una estrella, se desvanece hacia dentro y se encoge hasta su tamaño regular. Esto se hace en animate_star().

Cuando la estrella ha terminado de animarse, las estrellas más pequeñas se generan en un círculo alrededor de la estrella grande. Esto se hace en spawn_small_stars().

Luego se animan para salir disparadas aleatoriamente desde la estrella. Se aleatorizan tanto en velocidad como en escala mientras se expanden hacia afuera. Luego se desvanecen y finalmente se eliminan. Esto se hace en animate_small_star() y delete_small_star().

Cuando la puntuación ha alcanzado la puntuación total, la marca de high-score aparece gradualmente y se encoge de vuelta a su lugar. Esto empieza al final de inc_score() y se realiza en animate_imprint().

La función setup() se asegura de que los nodos tengan los valores iniciales correctos. Al llamar a setup() desde on_reload(), nos aseguramos de que todo se configure correctamente cada vez que el script se recarga desde el Defold Editor.

-- file: level_complete.gui_script

-- qué tan rápido se incrementa la puntuación por segundo
local score_inc_speed = 51100
-- cuánto tiempo hay entre cada actualización de la puntuación
local dt = 0.03
-- escala de la puntuación al inicio del conteo
local score_start_scale = 0.7
-- escala de la puntuación cuando se ha alcanzado la puntuación objetivo
local score_end_scale = 1.0
-- cuánto "rebota" la puntuación en cada incremento
local score_bounce_factor = 1.1
-- cuántas estrellas pequeñas generar por cada estrella grande
local small_star_count = 16

local function setup(self)
    -- hace transparente el color del encabezado
    local c = gui.get_color(self.heading)
    c.w = 0
    gui.set_color(self.heading, c)
    -- hace transparente la sombra del encabezado
    c = gui.get_shadow(self.heading)
    c.w = 0
    gui.set_shadow(self.heading, c)
    -- define inicialmente el encabezado al doble de escala
    local s = 2
    gui.set_scale(self.heading, vmath.vector3(s, s, s))
    -- define la puntuación inicial (0)
    gui.set_text(self.score, "0")
    -- define el color de la puntuación como blanco opaco
    gui.set_color(self.score, vmath.vector4(1, 1, 1, 1))
    -- define la escala para que la puntuación pueda crecer mientras se cuenta
    gui.set_scale(self.score, vmath.vector4(score_start_scale, score_start_scale, 1, 0))

    -- hace transparentes todas las estrellas grandes
    for i=1,#self.stars do
        gui.set_color(self.stars[i], vmath.vector4(1, 1, 1, 0))
    end
    -- hace transparente la marca
    gui.set_color(self.imprint, vmath.vector4(1, 1, 1, 0))
    -- la puntuación que se muestra actualmente
    self.current_score = 0
    -- la puntuación objetivo durante el conteo
    self.target_score = 0
end

function init(self)
    -- recupera nodos para acceder a ellos con más facilidad
    self.heading = gui.get_node("heading")
    self.stars = {gui.get_node("star_left"), gui.get_node("star_mid"), gui.get_node("star_right")}
    self.score = gui.get_node("score")
    self.imprint = gui.get_node("imprint")
    -- color inicial de la puntuación
    self.score_start_color = vmath.vector4(1, 1, 1, 1)
    -- guarda el color de la puntuación y anima hacia él durante el conteo más adelante
    self.score_end_color = gui.get_color(self.score)
    setup(self)
end

-- elimina una estrella pequeña, llamada cuando la estrella ha terminado de animarse
local function delete_small_star(self, small_star)
    gui.delete_node(small_star)
end

-- anima una estrella pequeña según la posición inicial y el ángulo dados
local function animate_small_star(self, pos, angle)
    -- dirección de desplazamiento de la estrella pequeña
    local dir = vmath.vector3(math.cos(angle), math.sin(angle), 0, 0)
    -- crea una estrella pequeña
    local small_star = gui.new_box_node(pos + dir * 20, vmath.vector3(64, 64, 0))
    -- define su textura
    gui.set_texture(small_star, "small_star")
    -- define su color como blanco completo
    gui.set_color(small_star, vmath.vector4(1, 1, 1, 1))
    -- define una escala inicial baja
    local start_s = 0.3
    gui.set_scale(small_star, vmath.vector3(start_s, start_s, 1))
    -- variación en la escala de cada estrella pequeña
    local end_s_var = 1
    -- escala final real de esta estrella
    local end_s = 0.5 + math.random() * end_s_var
    gui.animate(small_star, gui.PROP_SCALE, vmath.vector4(end_s, end_s, 1, 0), gui.EASING_NONE, 0.5)
    -- variación en la distancia recorrida (básicamente la velocidad de la estrella)
    local dist_var = 300
    -- distancia real que recorrerá la estrella
    local dist = 400 + math.random() * dist_var
    gui.animate(small_star, gui.PROP_POSITION, pos + dir * dist, gui.EASING_NONE, 0.5)
    gui.animate(small_star, gui.PROP_COLOR, vmath.vector4(1, 1, 1, 0), gui.EASING_OUT, 0.3, 0.2, delete_small_star)
end

-- genera una cantidad de estrellas pequeñas
local function spawn_small_stars(self, star)
    -- posición de la estrella grande alrededor de la cual aparecerá la estrella pequeña
    local p = gui.get_position(star)
    for i = 1,small_star_count do
        -- calcula el ángulo de la estrella pequeña particular
        local angle = 2 * math.pi * i/small_star_count
        -- además de la posición
        local pos = vmath.vector3(p.x, p.y, 0)
        -- genera y anima la estrella pequeña
        animate_small_star(self, pos, angle)
    end
end

-- inicia la animación de una estrella grande apareciendo gradualmente
local function animate_star(self, star)
    -- duración de aparición gradual
    local fade_in = 0.2
    -- la hace transparente
    gui.set_color(star, vmath.vector4(1, 1, 1, 0))
    -- aparece gradualmente
    gui.animate(star, gui.PROP_COLOR, vmath.vector4(1, 1, 1, 1), gui.EASING_IN, fade_in)
    -- escala inicial
    local scale = 5
    gui.set_scale(star, vmath.vector3(scale, scale, 1))
    -- se encoge de vuelta a su lugar
    gui.animate(star, gui.PROP_SCALE, vmath.vector4(1, 1, 1, 0), gui.EASING_IN, fade_in, 0, spawn_small_stars)
end

-- inicia la animación de la marca apareciendo gradualmente
local function animate_imprint(self)
    -- espera un poco antes de que aparezca la marca
    local delay = 0.8
    -- duración de aparición gradual
    local fade_in = 0.2
    -- escala inicial
    local scale = 4
    gui.set_scale(self.imprint, vmath.vector4(scale, scale, 1, 0))
    -- se encoge de vuelta a su lugar
    gui.animate(self.imprint, gui.PROP_SCALE, vmath.vector4(1, 1, 1, 0), gui.EASING_IN, fade_in, delay)
    -- también aparece gradualmente
    gui.animate(self.imprint, gui.PROP_COLOR, vmath.vector4(1, 1, 1, 1), gui.EASING_IN, fade_in, delay)
end

-- incrementa la puntuación un paso hacia el objetivo
local function inc_score(self, node)
    -- cuánto se incrementa la puntuación en este paso
    local score_inc = score_inc_speed * dt
    -- nueva puntuación después del incremento
    local new_score = self.current_score + score_inc
    for i = 1,#self.stars do
        -- empieza a animar una estrella grande si cruzamos el nivel de puntuación para que aparezca
        if self.current_score < self.star_levels[i] and new_score >= self.star_levels[i] then
            animate_star(self, self.stars[i])
        end
    end
    -- actualiza la puntuación, pero la limita al objetivo
    self.current_score = math.min(new_score, self.target_score)
    -- actualiza la puntuación en pantalla
    gui.set_text(self.score, tostring(self.current_score))
    -- si todavía no terminamos, sigue animando e incrementando
    if self.current_score < self.target_score then
        -- qué tan cerca estamos del objetivo
        local f = self.current_score / self.target_score
        -- mezcla el color para obtener un desvanecimiento lento
        local c = vmath.lerp(f, self.score_start_color, self.score_end_color)
        gui.animate(self.score, gui.PROP_COLOR, c, gui.EASING_NONE, dt, 0, inc_score)
        -- nueva escala para este paso
        local s = vmath.lerp(f, score_start_scale, score_end_scale)
        -- aumenta la escala por el factor de rebote
        local sp = s * score_bounce_factor
        -- anima desde la escala rebotada de vuelta a la escala apropiada
        gui.set_scale(self.score, vmath.vector4(sp, sp, 1, 0))
        gui.animate(self.score, gui.PROP_SCALE, vmath.vector4(s, s, 1, 0), gui.EASING_NONE, dt)
    else
        -- terminamos, aparece gradualmente la marca
        -- NOTE! en un caso real, esto debería comprobarse contra el high score almacenado real
        animate_imprint(self)
    end
end

function on_message(self, message_id, message, sender)
    -- alguien nos dice que debemos mostrar la escena de nivel completado
    if message_id == hash("level_completed") then
        -- recupera la puntuación obtenida y los niveles de puntuación en los que se deben mostrar las estrellas
        self.target_score = message.score
        self.star_levels = message.star_levels
        -- aparece gradualmente el encabezado ("level completed")
        local c = gui.get_color(self.heading)
        c.w = 1
        gui.animate(self.heading, gui.PROP_COLOR, c, gui.EASING_IN, dt, 0.0, inc_score)
        c = gui.get_shadow(self.heading)
        c.w = 1
        gui.animate(self.heading, gui.PROP_SHADOW, c, gui.EASING_IN, dt, 0.0)
        -- se encoge a su lugar
        gui.animate(self.heading, gui.PROP_SCALE, vmath.vector4(1, 1, 1, 0), gui.EASING_IN, 0.2, 0.0)
    end
end

-- esta función se llama cuando el script se recarga
-- al configurar la escena y simular nivel completado, obtenemos un flujo de trabajo realmente rápido para ajustar valores
function on_reload(self)
    -- asegúrate de que se tengan en cuenta los cambios de configuración
    setup(self)
    -- simula que el nivel se ha completado
    msg.post("#gui", "level_completed", {score = 102000, star_levels = {40000, 70000, 100000}})
end