Hello :)
I'm starting with Solarus engine and I was wondering : is it possible to create complex movements including changement of directions and pause?
Something like :
movement.add(straight_movement(east))
movement.add(wait(5))
movement.add(facing(east))
movement.add(jumping(right))
For the moment, I don't understand how to include a pause in a movement, and the only way I find to change a direction after a movement is to create a lot of movement:on_finished() which is pretty redundant.
Sorry if the solution already exist, I didn't manage to find it in the documentation :S.
For example, I want that a character named Sheik does this pattern at the map's loading :
- few steps upward
- face left
- wait few time
- few steps downward
Here is the quicker way to code that I found :
function sheik_intro_movement()
local step = 0
local movement = sol.movement.create("path")
movement:set_speed(64)
movement:start(sheik)
function movement:on_finished()
if step == 0 then
movement:set_path{2,2,2,2,2,2}
movement:start(sheik)
step = step + 1
elseif step == 1 then
sheik:get_sprite():set_direction(2)
sol.timer.start(200, function()
step = step + 1
movement:on_finished()
end)
elseif step == 2 then
movement = sol.movement.create("path")
movement:set_speed(64)
movement:set_path{6,6,6,6,6,6}
movement:start(sheik)
step = step + 1
end
end
end
Please note that in step 2 I'm realocating the movement variable movement = sol.movement.create("path")
because if I just do movement:set_path{2,2,2,2,2,2}
, the character is teleported at the destination for some reason.
Watch the videos in my YouTube chanel to see what is possible to do with normal enemies. You have their code in the repository of Children of Solarus, in github and gitlab. We recently moved to gitlab (because Microsoft bought github), so the github repo will not be updated. Use the scripts to study and make tests (some are quite complex).
PS: if you like my videos, click on "thumbs up". :)
I am temporarily on pause mode for now, due to too much work in my real life. When I come back I will keep adding more cool enemies.
If you are doing basic stuff, better not to read my code and try it yourself with timers and movements. Always start with short code and basic stuff.
stdgregwar made an experimental script to make this kind of custscenes more easily: https://gitlab.com/solarus-games/zelda-mercuris-chest/blob/dev/data/scripts/maps/cutscene.lua
You can chain movements but also dialogs, pauses, etc.
Wow thank you for the extra quick answers ;D !
Christopho, that seems to perfectly fit, thank you ! Diarandor I'll watch your videos on the train :).
Quote from: Christopho on June 24, 2018, 08:10:02 PM
stdgregwar made an experimental script to make this kind of custscenes more easily: https://gitlab.com/solarus-games/zelda-mercuris-chest/blob/dev/data/scripts/maps/cutscene.lua
You can chain movements but also dialogs, pauses, etc.
This is a really cool script! it seems like it might make cutscenes easier to debug. Does anyone know what actions this can be used to preform? It gives the example of .wait(length of pause), and .dialog(dialog)
Would any method work if you use the right syntax? There's also an example of .hero_start_treasure(), would something like .map_create_enemy(enemy info) work? Or .game_set_ability("swim", 1)?
Quote from: Max on June 25, 2018, 06:59:12 PM
Does anyone know what actions this can be used to preform? It gives the example of .wait(length of pause), and .dialog(dialog)
Looks like the actions are defined between lines 128 & 259.
You could add your own functions to that section if you need to do something else not included.
That's weird, I try to use the script but
cutscene.builder(game, map, hero)
cutscene.wait(500)
cutscene.exec(function() print("test") end)
cutscene.start()
Edit: It works now, should have read the tutorial above.
Here is an example of how the custscene builder should be used (example from Zelda OLB SE):
cutscene.builder(game, map, hero)
.dialog("out.e4.pit_hello")
.exec(function()
hero:freeze()
end)
.movement({
type = "straight",
entity = pit,
properties = {
angle = 0,
speed = 64,
max_distance = 176,
ignore_obstacles = true,
},
})
.exec(function()
pit:remove()
hero:unfreeze()
end)
.start()
Quote from: Christopho on July 08, 2018, 12:58:50 PM
Here is an example of how the custscene builder should be used (example from Zelda OLB SE):
cutscene.builder(game, map, hero)
.dialog("out.e4.pit_hello")
.exec(function()
hero:freeze()
end)
.movement({
type = "straight",
entity = pit,
properties = {
angle = 0,
speed = 64,
max_distance = 176,
ignore_obstacles = true,
},
})
.exec(function()
pit:remove()
hero:unfreeze()
end)
.start()
Thanks ! It works now
More complex example from stdgregwar:
this is the final custcene of Zelda XD2 rewritten with his script.
-- Final cinematic, but flat
function map:start_cinematic_redux()
cutscene.builder(game,map,hero)
.wait(500)
.dialog('final.zelda_4',player_name)
.set_direction(hero,1)
.dontWaitFor.hero_animation('walking')
.movement{ --movement 1
type='target',
entity=hero,
movement_properties={
target = {104,192},
speed = 50,
smooth = true,
ignore_obstacles = true,
}
}
.set_direction(hero,0)
.dontWaitFor.hero_animation('stopped')
.wait(500)
.dialog('final.zelda_5',player_name)
.wait(500)
.dialog('final.mr_grump_3')
.wait(500)
.dialog('final.zelda_6',player_name)
.set_direction(hero,2)
.dontWaitFor.hero_animation('walking')
.movement{--movement 2
type = 'target',
entity=hero,
movement_properties={
target = {-32,192},
speed = 80,
smooth = true,
ignore_obstacles = true
}
}
.wait(500)
.set_direction(hero,0)
.dontWaitFor.hero_animation('carrying_walking')
.dontWaitFor.sprite_animation(cocktails:get_sprite(),'walking')
.movement(--movement 3
{
type='target',
entity=hero,
movement_properties={
target = {104,192},
speed = 80,
smooth = true,
ignore_obstacles = true
}
},
function(mov)
function mov:on_position_changed()
if cocktails then
local hero_x,hero_y = hero:get_position()
cocktails:set_position(hero_x,hero_y-16)
end
end
end)
.dontWaitFor.hero_animation('carrying_stopped')
.dontWaitFor.sprite_animation(cocktails:get_sprite(),'on_ground')
.wait(1500)
.exec(
function()
cocktails:remove()
end)
.dontWaitFor.hero_animation('stopped')
.dialog('final.zelda_7',player_name)
.wait(500)
.dontWaitFor.hero_animation('dying')
.wait(2000)
.dontWaitFor.hero_animation('walking')
.set_direction(hero,2)
.movement{--movement 4
type='target',
entity=hero,
movement_properties = {
target = {-32,192},
speed = 80,
smooth = true,
ignore_obstacles = true
}
}
.wait(1000)
.dialog('final.zelda_8')
.wait(500)
.and_then(
function(cont)
fade_sprite = sol.sprite.create('entities/heart_fade')
local camera_x, camera_y = map:get_camera():get_position()
local zelda_x, zelda_y = grump_and_zelda:get_position()
fade_x = zelda_x - camera_x
fade_y = zelda_y - camera_y - 16
fade_sprite:set_animation('close',cont)
end)
.exec(
function()
-- Fill screen with black.
local quest_w, quest_h = sol.video.get_quest_size()
black_surface = sol.surface.create(quest_w, quest_h)
black_surface:fill_color({0, 0, 0})
end)
.wait(1000)
.exec(
function()
local menu_font, menu_font_size = language_manager:get_menu_font()
end_text = sol.text_surface.create{
horizontal_alignment = "center",
vertical_alignment = "middle",
color = {255, 255, 255},
font = menu_font,
font_size = menu_font_size * 2,
text_key = "final.end_text",
}
end)
.wait(2000)
.exec(
function()
-- Launch Ending credits.
local dialog_box = game:get_dialog_box()
dialog_box:set_position("bottom")
end)
.dialog('final.credits')
.exec(
function()
local statistics = statistics_manager:new(game)
game:set_suspended(true)
sol.menu.start(game, statistics)
function statistics:on_finished()
-- Reset game.
sol.main.reset()
end
end)
.start()
end