Cutscenes
In deze tutorial ga ik de basis van een cutscene uitleggen. Het is moeilijk om te vertellen hoe je een cutscene moet maken, omdat elke cutscene anders is. Echter, ik denk dat je het kunt leren als ik uitleg hoe je een simpele cutscene kunt coden. Daarom heb ik zelf een kleine, simpele cutscene gemaakt, en ik zal uitleggen hoe. Onthoudt dat je bijna elke opcode die je erbij zet moet testen. Zo kan je bijvoorbeeld met waits ertussen alles veel mooier maken, moet je testen voor de camera, etcetera.
Example cutscene
Een cutscene is eigenlijk gewoon een hoop opcodes die draaien zonder input van de player. Wanneer de player niets akn doen, en gewoon moet kijken, is er een cutscene. Onthoudt dit in deze tutorial. We zullen starten met de volledige code van de cutscene die ik gescript heb:
//---------------------
//Start Cutscene
//---------------------
:CUTSCENE
03BC: $begin = create_sphere_at 2489.0088 -1658.4554 13.3519 radius 1.0
repeat
wait 0
until 00FE: actor $PLAYER_ACTOR sphere 0 in_sphere 2489.0088 -1658.4554 13.3519 radius 1.0 1.0 1.0
Player.CanMove($PLAYER_CHAR, false)
//---------------------
//Load Models
//---------------------
model.Load(#army)
model.Load(#patriot)
model.load(#m4)
023C: load_special_actor 'SWEET' as 1 // models 290-299
023C: load_special_actor 'KENDL' as 2 // models 290-299
023C: load_special_actor 'CESAR' as 3 // models 290-299
038B: load_requested_models
:load_check
wait 0
if and
model.available(#army)
model.available(#patriot)
model.available(#m4)
023D: special_actor 1 loaded
023D: special_actor 2 loaded
023D: special_actor 3 loaded
else_jump @load_check
//---------------------
//Spawn everything in fade
//---------------------
fade 0 500
repeat
wait 0
until 816B: not fading
02A3: enable_widescreen 1
03BD: destroy_sphere $begin
Car.Create($car, #patriot, 2503.0217, -1672.2841, 13.3594)
Car.Angle($car) = 82.3844
0129: 1@ = create_actor_pedtype 23 model #army in_car $car driverseat
01C8: 2@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 0
01C8: 3@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 1
01C8: 4@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 2
01B2: give_actor 1@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 2@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 3@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 4@ weapon 31 ammo 60 // Load the weapon model before using this
009A: 5@ = create_actor_pedtype 4 model #SPECIAL01 at 2479.1279 -1680.2635 13.338
Actor.Angle(5@) = 359.6866
009A: 6@ = create_actor_pedtype 4 model #SPECIAL02 at 2477.7996 -1678.5049 13.3386
Actor.Angle(6@) = 266.6259
009A: 7@ = create_actor_pedtype 4 model #SPECIAL03 at 2479.3577 -1676.2166 13.337
Actor.Angle(7@) = 176.3852
00A1: put_actor $PLAYER_ACTOR at 2480.9185 -1678.2045 13.3404
Actor.Angle($PLAYER_ACTOR) = 98.7008
04ED: load_animation "GANGS"
repeat
wait 0
until 04EE: animation "GANGS" loaded
0605: actor 5@ perform_animation_sequence "PRTIAL_GNGTLKA" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 6@ perform_animation_sequence "PRTIAL_GNGTLKB" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 7@ perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor $PLAYER_ACTOR perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
Camera.SetPosition(2472.7151, -1681.5944, 16.9041, 0.0, 0.0, 0.0)
Camera.PointAt(2494.2827, -1669.9541, 13.3359, 2)
wait 2000
00A7: car $car drive_to 2487.1799 -1670.8324 13.3359
fade 1 500
repeat
wait 0
until 816B: not fading
//---------------------
//Behaviour after fade
//---------------------
repeat
wait 10
until 01AE: car $car sphere 0 near_point 2487.1799 -1670.8324 radius 3.0 3.0 stopped
0160: set_camera_point_at 2494.2827 -1669.9541 9.3359 mode 2
05CD: AS_actor 1@ exit_car $car
05CD: AS_actor 2@ exit_car $car
05CD: AS_actor 3@ exit_car $car
05CD: AS_actor 4@ exit_car $car
0635: AS_actor 1@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 2@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 3@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 4@ aim_at_actor $PLAYER_ACTOR -1 ms
074D: AS_actor $PLAYER_ACTOR turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 5@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 6@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 7@ turns_to_and_look_at_actor 1@ timelimit -1
wait 2500
05C4: AS_actor $PLAYER_ACTOR hands_up -1 ms
05C4: AS_actor 5@ hands_up -1 ms
05C4: AS_actor 6@ hands_up -1 ms
05C4: AS_actor 7@ hands_up -1 ms
wait 5000
//---------------------
//Cleanup
//---------------------
fade 0 500
repeat
wait 0
until 816B: not fading
00A6: destroy_car $car
009B: destroy_actor 1@
009B: destroy_actor 2@
009B: destroy_actor 3@
009B: destroy_actor 4@
009B: destroy_actor 5@
009B: destroy_actor 6@
009B: destroy_actor 7@
0687: clear_actor $PLAYER_ACTOR task
Player.CanMove($PLAYER_CHAR, true)
0373: set_camera_directly_behind_player
02EB: restore_camera_with_jumpcut
02A3: enable_widescreen 0
wait 2000
fade 1 500
repeat
wait 0
until 816B: not fading
Verhaal en begin
Het verhaal is: er is een Patriot, met 4 army actors erin. Er zijn ook 4 special actors: Sweet, Kendl, Cesar en CJ. Deze actors praten met elkaar (met een animatie). De Patriot moet naar de 4 special actors rijden, en de army mannen moeten uit de auto gaan. Dan moeten de 4 special actors naar de army mannen draaien. Deze army mannen hebben een M4 en richten op de groep van 4 special actors. Tot slot geven de 4 special actors zich over (handen omhoog).
We zullen een sphere maken, waar de player in moetgaan staan voor de cutscene begint. Het zal raar zijn wanneer de player CJ kan bewegen tijdens de cutscene, daarom maken we het zo dat hij niet kan bewegen. Daarna zullen we alles laden wat in de cutscene gebruikt wordt. Ik denk niet dat ik dat nog eens uit moet leggen.
DEFINE OBJECTS 1
DEFINE OBJECT SANNY BUILDER 3.03
DEFINE MISSIONS 0
DEFINE EXTERNAL_SCRIPTS 0 // Use -1 in order not to compile AAA script
DEFINE UNKNOWN_EMPTY_SEGMENT 0
DEFINE UNKNOWN_THREADS_MEMORY 0
{$VERSION 3.1.0027}
//-------------MAIN---------------
thread 'MAIN'
01F0: set_max_wanted_level_to 6
042C: set_total_missions_to 1
030D: set_max_progress 1
set_wb_check_to 0
00C0: set_current_time_hours_to 10 minutes_to 0
04E4: unknown_refresh_game_renderer_at 2488.5601 -1666.549 13.3438
Camera.SetAtPos(2488.5601, -1666.549, 13.3438)
$PLAYER_CHAR = Player.Create(#NULL, 2488.5601, -1666.549, 13.3438)
$PLAYER_ACTOR = Actor.EmulateFromPlayer($PLAYER_CHAR)
set_weather 6
wait 0
0180: set_on_mission_flag_to $4 // Note: your missions have to use the variable defined here
Player.SetClothes($PLAYER_CHAR, "PLAYER_FACE", "HEAD", Head)
Player.SetClothes($PLAYER_CHAR, "JEANSDENIM", "JEANS", Legs)
Player.SetClothes($PLAYER_CHAR, "SNEAKERBINCBLK", "SNEAKER", Shoes)
Player.SetClothes($PLAYER_CHAR, "VEST", "VEST", Torso)
Player.Build($PLAYER_CHAR)
Player.CanMove($PLAYER_CHAR) = True
032B: $5 = create_weapon_pickup #AK47 group 15 ammo 15000 at 117.215 -102.7513 2.95162
fade 1 0
select_interior 0
0629: change_integer_stat 181 to 4
016C: restart_if_wasted_at 237.5228 2499.1719 2.2405 angle 90.0 town_number 0
016D: restart_if_busted_at 237.5228 2499.1719 2.2405 angle 90.0 town_number 0
create_thread @CUTSCENE
set_weather 6
015A: restore_camera
:MAIN_LOOP
wait 0 ms
00BF: $TIME_HOURS = current_time_hours, $TIME_MINS = current_time_minutes
jump @MAIN_LOOP
end_thread
//---------------------
//Start Cutscene
//---------------------
:CUTSCENE
03BC: $begin = create_sphere_at 2489.0088 -1658.4554 13.3519 radius 1.0
repeat
wait 0
until 00FE: actor $PLAYER_ACTOR sphere 0 in_sphere 2489.0088 -1658.4554 13.3519 radius 1.0 1.0 1.0
Player.CanMove($PLAYER_CHAR, false)
//---------------------
//Load Models
//---------------------
model.Load(#army)
model.Load(#patriot)
model.load(#m4)
023C: load_special_actor 'SWEET' as 1 // models 290-299
023C: load_special_actor 'KENDL' as 2 // models 290-299
023C: load_special_actor 'CESAR' as 3 // models 290-299
038B: load_requested_models
:load_check
wait 0
if and
model.available(#army)
model.available(#patriot)
model.available(#m4)
023D: special_actor 1 loaded
023D: special_actor 2 loaded
023D: special_actor 3 loaded
else_jump @load_check
//---------------------
//Spawn everything in fade
//---------------------
fade 0 500
repeat
wait 0
until 816B: not fading
02A3: enable_widescreen 1
03BD: destroy_sphere $begin
Car.Create($car, #patriot, 2503.0217, -1672.2841, 13.3594)
Car.Angle($car) = 82.3844
0129: 1@ = create_actor_pedtype 23 model #army in_car $car driverseat
01C8: 2@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 0
01C8: 3@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 1
01C8: 4@ = create_actor_pedtype 23 model #army in_car $car passenger_seat 2
01B2: give_actor 1@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 2@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 3@ weapon 31 ammo 60 // Load the weapon model before using this
01B2: give_actor 4@ weapon 31 ammo 60 // Load the weapon model before using this
009A: 5@ = create_actor_pedtype 4 model #SPECIAL01 at 2479.1279 -1680.2635 13.338
Actor.Angle(5@) = 359.6866
009A: 6@ = create_actor_pedtype 4 model #SPECIAL02 at 2477.7996 -1678.5049 13.3386
Actor.Angle(6@) = 266.6259
009A: 7@ = create_actor_pedtype 4 model #SPECIAL03 at 2479.3577 -1676.2166 13.337
Actor.Angle(7@) = 176.3852
00A1: put_actor $PLAYER_ACTOR at 2480.9185 -1678.2045 13.3404
Actor.Angle($PLAYER_ACTOR) = 98.7008
04ED: load_animation "GANGS"
repeat
wait 0
until 04EE: animation "GANGS" loaded
0605: actor 5@ perform_animation_sequence "PRTIAL_GNGTLKA" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 6@ perform_animation_sequence "PRTIAL_GNGTLKB" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor 7@ perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
0605: actor $PLAYER_ACTOR perform_animation_sequence "PRTIAL_GNGTLKC" IFP_file "GANGS" 4.0 loop 1 0 0 0 time -1
Camera.SetPosition(2472.7151, -1681.5944, 16.9041, 0.0, 0.0, 0.0)
Camera.PointAt(2494.2827, -1669.9541, 13.3359, 2)
wait 2000
00A7: car $car drive_to 2487.1799 -1670.8324 13.3359
fade 1 500
repeat
wait 0
until 816B: not fading
//---------------------
//Behaviour after fade
//---------------------
repeat
wait 10
until 01AE: car $car sphere 0 near_point 2487.1799 -1670.8324 radius 3.0 3.0 stopped
0160: set_camera_point_at 2494.2827 -1669.9541 9.3359 mode 2
05CD: AS_actor 1@ exit_car $car
05CD: AS_actor 2@ exit_car $car
05CD: AS_actor 3@ exit_car $car
05CD: AS_actor 4@ exit_car $car
0635: AS_actor 1@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 2@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 3@ aim_at_actor $PLAYER_ACTOR -1 ms
0635: AS_actor 4@ aim_at_actor $PLAYER_ACTOR -1 ms
074D: AS_actor $PLAYER_ACTOR turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 5@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 6@ turns_to_and_look_at_actor 1@ timelimit -1
074D: AS_actor 7@ turns_to_and_look_at_actor 1@ timelimit -1
wait 2500
05C4: AS_actor $PLAYER_ACTOR hands_up -1 ms
05C4: AS_actor 5@ hands_up -1 ms
05C4: AS_actor 6@ hands_up -1 ms
05C4: AS_actor 7@ hands_up -1 ms
wait 5000
//---------------------
//Cleanup
//---------------------
fade 0 500
repeat
wait 0
until 816B: not fading
00A6: destroy_car $car
009B: destroy_actor 1@
009B: destroy_actor 2@
009B: destroy_actor 3@
009B: destroy_actor 4@
009B: destroy_actor 5@
009B: destroy_actor 6@
009B: destroy_actor 7@
0687: clear_actor $PLAYER_ACTOR task
Player.CanMove($PLAYER_CHAR, true)
0373: set_camera_directly_behind_player
02EB: restore_camera_with_jumpcut
02A3: enable_widescreen 0
wait 2000
fade 1 500
repeat
wait 0
until 816B: not fading
:LOOP_CUTSCENE
wait 100 ms
jump @LOOP_CUTSCENE
Andere opcodes
Er zijn veel meer dingen die je kunt regelen in een cutscene. Je kunt ook gewoon settings veranderen, zoals het weer:
01B6: set_weather 1
Of gewoon het Wanted Level (al raad ik dit niet aan omdat je cutscene dan erg onvoorspelbaar wordt, je weet niet of er politie in de buurt is):
010D: set_player $PLAYER_CHAR wanted_level_to 0
010E: set_player $PLAYER_CHAR minimum_wanted_level_to 2
01F0: set_max_wanted_level_to 6
Iets anders dat je kunt gebruiken in een cutscene, is de "skip cutscene" opcode. Wanneer je een missie maakt, en de player moet het nog een keer doen, dan wil die waarschijnlijk niet de cutscenes opnieuw doen. Er is een oplossing daarvoor. Een voorbeeld van de missie "General Dilemma" (gescript door PatrickW en mij):
0707: start_scene_skip_to @GENDIL_3502
:GENDIL_3502
0701: end_scene_skip
if
08D0: cutscene_skipped
jf @GENDIL_3523
fade 0 100
:GENDIL_3523
Dus, wanneer de player wil dat de cutscene niet opnieuw komt, gaat hij naar GENDIL_3502. Het zal waarschijnlijk zo zijn dat de "normale" code (dus de code mét de cutscene) er ook komt, daardoor kan je een check maken of de cutscene geskipped is. In dit geval, is er nog fade nodig. Als de cutscene niet geskipped was, dan had hij waarschijnlijk al fade gehad voordat hij bij dit aan komt.
Je kunt ook objecten, andere animaties, pickups, teksten, etcetera gebruiken. Zo lang je maar in gedachte houdt dat je alles creeerd en verwijderd in de fades, kan je alles doen! Kijk vooral veel naar de AS opcodes, die erg handig zijn bij het maken van cutscenes. Bijvoorbeeld:
05CD: AS_actor -1 exit_car 22@
Deze opcodes zijn allemaal niet-bestuurbare opcodes, dus erg handig voor in cutscenes!
Ik hoop dat je wat meer geleerd hebt over cutscenes. Wanneer je vragen of opmerkingen hebt over deze tutorial, of je hebt een request voor een andere tutorial, dan kan je altijd terecht in het reactietopic.