Scrolling credits

Started by wizard_wizzle (aka ZeldaHistorian), September 05, 2015, 01:10:34 AM

Previous topic - Next topic
I'm trying to get a scrolling credits screen figured out, and I've hit a roadblock. How I'd like to do it is have a table with all of the credit text, then loop through each piece of text and display it on screen as it's own line, which would start at the bottom of the screen and slowly move up. My thought was to create a text surface for each line and apply a path movement north, then detect when it hits the top of the screen and destroy that surface. My problem is that I can't get the loop to create the surfaces and the separate loop to draw each surface working correctly. Help please?

Pseudo-code: Where should I put the code that turns text into text surfaces (in on_started right now)

local credits = { "Thank you for playing!" , "" , "CREDITS", "" }

function map:on_started(destination)
  map:get_game():set_hud_enabled(false)
  map:get_hero():set_position(-100, -100)
  map:get_hero():freeze()

  for i, line in ipairs(credits) do
    sol.timer.start(self, 4000, function()
      line_text = line
      line = sol.text_surface.create()
      line:set_text(line_text)
      local m = sol.movement.create("path")
      m:set_xy(camera_height, camera_width/2)
      m:set_path({2,2})
      m:set_loop(true)
      m:set_speed(32)
      m:start(line)
  end
end

function map:on_draw(dst_surface)
  for i, line in ipairs(credits) do
      local text_x, text_y = line:get_xy()
      if text_x < 100 then line:fade_out() end
      line:draw(dst_surface, text_x, text_y)
  end
end

September 05, 2015, 02:56:26 AM #1 Last Edit: September 05, 2015, 02:59:30 AM by Diarandor
Hi! There are lots of ways to do this. I would do the following:

I would put the code of the credits in the script of a map like you have done.

In that script, I would define a function called "function map:start_line(text_line)". This function would create a text surface with the given text (text_line), set the initial coordinates x,y of the text surface, create/start the movement, and define an event movement:on_position_changed() so that when the text surface has reached the end we can use the fade_out() function or something like that. (You can also put this code inside the map:on_started() function and not define the function, as you have done, but it's not important.)

In the event "function map:on_started(destination)", I would use a timer which calls the function map:start_line(text_line) on each iteration to create a new line. (You can increase a variable n -> n+1 at each iteration and set text_line = credits[n], or something like that.) You need to set a suitable amount of time for the timer, so that the lines are created with the desired separation, and obviuosly destroy the timer when you reach the end of your "credits" list.

With small changes of your code you can do it. (I think that you forgot to set as local the variable line_text.)

"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Getting closer! I appreciate and agree with your comments on structure. I'm having issues with the movement and the drawing itself though - nothing is actually showing on screen. I know that the text_line creation loop works because of a print statement I put in. Any thoughts on fine tuning?


local map = ...
local i = 0
local j = 0
local line = {}
local camera_x, camera_y, camera_width, camera_height = map:get_camera_position()
local surface = sol.surface.create(camera_width, camera_height)
local m = sol.movement.create("target")

local credits = { "Thank you for playing!" , "" , "CREDITS", "" ,
  "Solarus Engine: created by Christopho" , "" ,
  "Maps:", "ZeldaHistorian", "Renkineko (Lost Woods)"
}

function map:start_line(text_line)
  line[i] = sol.text_surface.create()
  line[i]:set_text(text_line)
print(line[i]:get_text())
  m:set_xy(camera_height, camera_width/2)
  m:set_target(0, camera_width/2) -- Move from the bottom to the top of the screen
  m:set_speed(16)

  function m:on_position_changed()
    local text_x, text_y = self:get_xy()
    local text_width, text_height = line[i]:get_size()
    line[i]:draw_region(0, 0, text_width, text_height, surface, text_x, text_y)
    if text_x < 50 then
      line[i]:fade_out() -- Fade out line and destroy movement when at top of screen
      sol.timer.start(self, 1000, function() self:stop() end)
    end
  end

  m:start(line[i])
end

function map:on_started(destination)
  map:get_game():set_hud_enabled(false)
  map:get_hero():set_position(-100, -100)
  map:get_hero():freeze()
  sol.timer.start(self, 4000, function()
    i = i + 1
    self:start_line(credits[i])
    return true
  end)
end

function map:on_draw(dst_surface)
  surface:draw(dst_surface)
end

I think you forgot to draw the lines (stored in your line list) in the map:on_draw event. That should solve the main problem.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I was attempting to write each line to "surface" with line:draw_region(0, 0, text_width, text_height, surface, text_x, text_y), and then draw just the surface. I thought this was similar to how HUD items were drawn, but I probably didn't implement it correctly.

I share my script if you like. I think it's pretty simple to customize.

I actually just finished my own - thanks for the share though! I went a different route and implemented it as a menu that fades blocks of text in and out instead of scrolling. It ended up looking great!