Inhoud
- Wat vooraf ging
- Inleiding
- Deur openen en sluiten met een Timeline
- LERP (lineaire interpolatie)
- Praktische voorbeelden
- Project Level Creation – Level Blueprint
- Cursisten in actie
Wat vooraf ging
- U hebt basiskennis van het programmeren van Blueprint Visual Scripting.
- U bent vertrouwd met de werkomgeving van Unreal Engine.
- U weet hoe u een Blueprint klasse aanmaakt.
- U kunt reeds interactie met de omgeving programmeren.
Inleiding
In een vorige handleiding hebben we reeds interactie met de omgeving geprogrammeerd. Heel vaak zal deze interactie verlopen over een zeker tijd. Een deur gaat niet meteen van toe naar open maar zal dit doen over een tijdspanne van bv. 2 seconden. Hiertoe hebben we een Timeline nodig.
Deze handleiding bouwt verder op de handleiding Interactie met de omgeving waarin het praktische voorbeeld van het openen van een deur werd uitgewerkt zonder gebruik te maken van een Timeline.
Situering van deze handleiding binnen Unreal Engine
Deur openen en sluiten met een Timeline
Zoals gezegd wil ik dit voorbeeld uitwerken zowel vanuit de Level Blueprint als vanuit een Blueprint Class zodat u beide werkwijzen eens naast mekaar ziet.
Het voordeel van een Blueprint Class is dat u deze Blueprint Class, deze deur, gemakkelijk kunt hergebruiken op verschillende plaatsen in het level, in verschillende levels en zelfs in verschillende projecten, daar waar een Level Blueprint gebonden is aan die specifieke deur in dat specifiek level.
Voor het programmeren van een deur is een Blueprint Class dan ook aangewezen!
Level Blueprint
We hebben een deel van de Level Blueprint reeds uitgewerkt in de handleiding Interactie met de omgeving. Ik herneem dit even.
- Sleep de Static Meshes SM_Door en SM_DoorFrame in het level.
- Gebruik de gekende technieken om deze actors de plaatsen en eventueel te roteren en te schalen.
- Omdat de deur moet kunnen roteren moet de Mobility van SM_Door op Movable staan.
Om een object, een deur, interactief te maken moet u een Trigger toevoegen.
- Voeg een BoxTrigger (die ik de naam DeurTriggerBox heb gegeven) toe zodat deze de deur langs beide zijden van de deur overlapt.
Met alle actors op zijn plaats en correct ingesteld kunnen we aan de programmeren slaan.
Wel, de deur moet open gaan, of meer specifiek, de deur moet 90° roteren. De deur (SM_Door) is het object waarmee iets moet gebeuren.
- Selecteer SM_Door.
Het programmeren in dit voorbeeld gebeurt in Level Blueprint.
- Met SM_Door nog steeds geselecteerd, klik in de knoppenbalk van de Viewport op Blueprints – Open Level Blueprint.
- Klik met de rechtermuisknop ingeduwd in Level Blueprint. Omdat we de actor SM_Door geselecteerd hebben kunnen we gemakkelijk een referentie naar de actor aanmaken door te kiezen voor Create a Reference to SM_Door.
Een referentie naar de actor SM-Door uit het persistent level is toegevoegd.
Nu we een referentie naar ons object MS_Door toegevoegd hebben aan Level Blueprint kunnen we dit gebruiken.
Blueprint Visual Scripting
Wat moet er gebeuren met deze deur? Wel, de deur moet roteren rond zijn eigen as. Dit kan via de functionaliteit SetActorRelativeRotation (omdat de rotatie relatief is ten opzichte van de actor zal deze ook blijven werken moesten we de actor verplaatsen). Moest u zich afvragen of ik met deze kennis (dat de functionaliteit SetActorRelativeRotation moet gebruikt worden) geboren ben, wel, nee. Dit is een kwestie van opzoeken en een beetje gezond verstand gebruiken.
- Sleep dus een node vanuit onze referentie naar MS_Door en zoek naar SetActorRelativeRotation.
- Klik deze aan als u ze gevonden hebt. Merk op dat SM_Door als Target wordt gekoppeld.
- Er moet 90° geroteerd worden rond de Z-as. U kunt nu gewoon die 90 intypen bij Z.
Wanneer moet de deur open gaan?
Wel, wanneer de “speler” de ruimte bepaald door de DeurTriggerBox binnen treedt. Of nog meer concreet bij de Event OnActorBeginOverlap van de actor DeurTriggerBox.
- Selecteer de DeurTriggerBox in uw level.
- Keer terug naar Level Blueprint en klik met de rechtermuisknop ingedrukt in Level Bleuprint.
- Omdat onze DeurTriggerBox geselecteerd is, kunt u gemakkelijk events voor deze DeurTriggerBox toevoegen. De event die we zoeken vindt u onder Collision – Add On Actor Begin Overlap.
- Klik deze aan.
De Event OnActorBeginOverlap van de actor DeurTriggerBox is toegevoegd.
- Verbindt de node van de Event OnActorBeginOverlap met de node SetActorRelativeRotation.
Oké, laten we dit testen.
- Compileer het Level Blueprint door te klikken op de knop Compile. Vergeet dit niet!
- Build en start het level.
Het werkt!
Alle, toch bijna, de deur vliegt open en eens open gaat ze niet meer dicht.
Ah, geen probleem, we voegen een Delay toe zoals bij de lamp. Oké, maar nee, een Delay zal er enkel voor zorgen dat het iets langer duurt voor de deur met dezelfde snelheid open vliegt. Het is het openen van de deur zelf die moet vertraagd worden.
Een Timeline toevoegen
Een Timeline (tijdlijn) laat toe om een waarde over een zekere tijdspanne te wijzigen.
De rotatie van de deur moet niet van 0 naar 90 gaan in 0.0000 seconden maar pakweg in 2 seconden (dit is misschien wat te lang, praktischer zou bv. 1.2 seconden kunnen zijn, maar ik wil zeker zijn dat we het gezien hebben).
Hoe doen we dit?
- Eerst gaan we de Timeline-functionaliteit toevoegen. Deze zal moeten starten bij OnActorBeginOverlap.
- Binnen deze tijdslijn bepalen we dat de waarde van 0 naar 90 moet wijzigen in een tijdsspanne van 2 seconden.
- De tijdslijn wordt vervolgens gebruikt om de Z-waarde van SetActorRelativeRotation te “sturen”.
Laten we dit uitvoeren.
- sleep een node uit de Event OnActorBeginOverlap en zoek naar Add Timeline.
Deze Timeline plaatst zich mooi tussen de huidige structuur eb is verbonden met OnActorBeginOverlap via Start en verbindt op zijn beurt SetActorRelativeRotation via Update. Na wat schuifwerk ziet u dit beter.
- Dubbelklik nu de Timeline.
U komt terecht in de Editor voor de Timeline (meer specifiek Timeline_0 omdat we het nagelaten hebben een specifieke naam te geven). Hier moet u eerst bepalen welk soort van tijdslijn u wilt toevoegen, wat u wilt wijzigen over tijd.
- F – Float track, animeert een Float waarde.
- V – Vector track, animeert vectoren voor rotatie, locatie of schaal.
- ! – Event track, dit levert een Event-pin die zal getriggerd worden op een bepaald moment in de tijdlijn.
- C – Kleur track, animeert een kleur.
- ~ – Voegt een bestaande curve in vanuit de Content Browser
In ons voorbeeld moet de waarde voor de rotatie gaan van 0 naar 90 over 2 seconden. We willen dus een waarde, meer specifiek een float wijzigen.
- Klik dus op de f-knop (Add Float Track).
U krijgt onderstaande schermindeling.
- Track Name – De naam van de tijdlijn.
- External Curve group – Voeg een externe curve toe vanuit de Content Browser.
- Track timeline – Hier voegt u de Keyframes toe.
- Vul een gewenste naam in (Bv. DeurRotatie).
We moeten nu twee punten toevoegen aan de tijdslijn om van 0 naar 90 te gaan in 2 seconden.
Een beginpunt op tijd 0 en een waarde van 0.
Een eindpunt op tijd 2 (seconden) met een waarde van 90.
- Klik nu op de tijdlijn terwijl u de Shift-toets ingedrukt houdt. Er verschijnt een punt met als waarde voor de tijd de plek waarop u geklikt hebt.
- Wijzig nu de Time van dit punt naar 0 (laat de waarde voor Value staan) en druk op Enter. Het punt schuift op helemaal naar links van de tijdlijn.
Doe nu hetzelfde om het tweede punt toe te voegen.
- Klik met de Shift-toets ingedrukt ergens op de tijdslijn (of klik met de rechtermuisknop ingedrukt op de tijdslijn en kies Add key to …). Wijzig de waarde van Time naar 2 en van Value naar 90 (en druk op Enter).
Wellicht verwacht u een lijn te zien maar ziet u deze niet meteen.
- Klik dan op de knopjes Zoom To Fit Horizontal en het knopje ernaast Zoom TO Fit Vertical.
Nu ziet u wellicht wel de verwachte lijn.
Eigenlijk zouden we hier kunnen stoppen, de tijdslijn is gemaakt, maar ik ga nog twee kleine aanpassingen doen.
Ten eerste verloopt de overgang nu lineair, op zich niets mis mee maar he zou mooier ogen als de deur in het begin, en op het einde, iets langzamer beweegt.
- Selecteer beide punten (door ze allebei aan te klikken met de Ctrl-toets ingedrukt).
- Klik met de rechtermuisknop op één van beide punten en vink Auto aan.
- Zet de totale lengte van de tijdslijn eveneens op 2 in het vakje Length en klik op de knoppen Compile en Save.
- Sluit nu het tabblad van de Timeline en keer terug naar de Event Graph
- Merk op dat er een “optie” Deur rotatie is bijgekomen.
De waarde van Deur rotatie moet nu gekoppeld worden met de Z-waarde van SetActorRelativeRotation.
- Sleep de Deur rotatie naar New Relative Rotation en… merk op dat er een fout wordt getoond.
Deze fout zou u niet mogen verbazen. De Float-waarde van Deur rotatie is immers maar één waarde die we proberen te koppelen aan 3 rotatiewaarden (X, Y en Z) en dit kan dus niet (bovendien kwamen de kleurtjes groen en paars niet overeen).
De oplossing bestaat er uit de drie waarden van de rotatie op de splitsen in drie afzonderlijke waarden voor X, Y en Z. Dit doe je als volgt:
- Klik met de rechtermuisknop ingedrukt op New Relative Rotation en kies voor Split Struct Pin.
U ziet nu drie verschillende New Relative Rotation voor X, Y en Z (elk met een groen kleurtje, dus van het type Float).
- Sleep nu de Deur rotatie naar New Relative Rotation Z(Yaw) en… merk op dat het nu lukt!
Let op, naargelang de oriëntatie van de deur kan het zijn dat u moet kiezen voor een andere as. Dus gaat de deur niet in de gewenste richting open, probeer het dan eens via een andere as.
Laten we dit testen.
- Compileer de Level Blueprint door te klikken op de knop Compile.
- Build en start het level.
Het werkt!
De deur sluiten
Rest ons nu nog de deur te sluiten.
Wanneer moet de deur sluiten?
Wel, wanneer de “speler” de ruimte, bepaald door de DeurTriggerBox, verlaat of meer concreet bij de Event OnActorEndOverlap van de actor DeurTriggerBox.
- Selecteer de DeurTriggerBox in uw level.
- Keer terug naar Level Blueprint en klik met de rechtermuisknop ingedrukt in Level Bleuprint.
- Omdat onze DeurTriggerBox geselecteerd is, kunt u gemakkelijk events voor deze DeurTriggerBox toevoegen. De event die we zoeken vindt u onder Collision – Add On Actor End Overlap.
- Klik deze aan.
De Event OnActorEndOverlap van de actor DeurTriggerBox is toegevoegd.
De deur sluiten is eigenlijk het omgekeerde als de deur openen. Je zou kunnen zeggen, het is de tijd van de tijdslijn die de deur opent terugdraaien. Wel, dit gaan we dan ook doen.
- Verbind de Event OnActorEndOverlap met de Reverse-actie van de Timeline.
Laten we dit testen.
- Compileer het Level Blueprint door te klikken op de knop Compile.
- Build en start het level.
Het werkt, onze deur gaat open en toe!
Een Blueprint Class
Een Blueprint Class, aan te raden is indien u meerdere deuren in uw level kunt hebben, gaat u als volgt te werk:
- In de Content Browser, klik op Add New – Blueprint Class (of klik met de rechtermuisknop in de net aangemaakte folder en klik op Blueprint Class).
- Kies de Parent Class Actor.
- Geef het een gepaste naam (bv. MijnDeur).
- Dubbelklik om de Blueprint te openen.
Merk de DefaultSceneRoot op die de positie in het level zal bepalen, deze is reeds Movable.
Componenten toevoegen
Er moeten 2 componenten worden toegevoegd:
- Een Static Mesh dat de deur bevat (met de naam Deur).
- Een Box Collision (te vergelijken met een TriggerBox) voor de interactie (met de naam DeurTriggerBox).
- U ziet de hieronder de toegevoegde componenten.
- Voeg de Static Mesh SM_Door toe aan de eigenschap Static Mesh van Deur.
- Eens toegevoegd, dubbelklik de deur (in de Eigenschap Static Mesh) en voeg Collision toe.
- Plaats de DeurTriggerBox rond de deur.
Events
De deur moet opengaan als de DeurTriggerBox overlapt wordt en nadien weer gesloten bij het beëindigen van de overlapping.
- Selecteer de DeurTriggerBox en vindt onderaan, bij Events, de event On Component Begin Overlap en On component End Overlap.
- Klik op de +-knoppen van On Component Begin Overlap en On Component End Overlap.
- Ga naar Event Graph.
Het programmeren verloopt gelijkaardig als het programmeren binnen de Level Blueprint. Het enige verschil is dat de Target van de functionaliteit SetActorRelativeRotation nu verwijst naar het Static Mesh component toegevoegd aan de Blueprint Class.
Merk de extra casting op die er voor zorgt dat de deur enkel maar open gaat als een PlayerCharacter (of een FirstPersonCharacter) de triggerbox overlapt. Deze extra controle mag ook toegevoegd worden aan de code in de Level Blueprint (ze is dus niet eigen aan een Blueprint Class).
Timeline
Deze Timeline is identiek aan de Timeline uit de Level Blueprint.
LERP (lineaire interpolatie)
Als aanvulling wil ik even stilstaan bij een veelgebruikte techniek LERP (lineaire interpolatie).
Bij een LERP wordt een waarde tussen A en B gekozen op basis van Alpha. Heel vaak wordt deze Alpha aangestuurd door een Timeline waardoor van de waarde van A naar de waarde van B wordt overgegaan gedurende een periode (tijd) bepaald door de Timeline.
Een Lerp-node heeft volgende Inputs:
A – Startwaarde
B – Eindwaar
C – Alpha, de waarde die de positie bepaalt tussen A en B
De Output (Return Value) is een waarde die ligt tussen A en B op basis van de waarde van Alpha. Is Alpha = 0 dan is de Output gelijk aan de waarde van A, Is Alpha = 1 dan is de Output gelijk aan de waarde van B. Is Alpha een waarde tussen 0 en 1 dan wordt, evenredig, een gelijkwaardige waarde tussen A en B als Output gegeven.
Praktische voorbeelden
Schuifdeur
Een schuifdeur roteert niet maar verplaatst zich over één van de assen (dit kan zowel de X-as als de Y-as zijn naargelang de oriëntatie van de deur maar ook eventueel de Z-as indien u wenst dat de deur naar boven schuift).
Onderstaande code werkt dit uit.
- Bij BeginPlay bepaal de locatie van de deur in de wereld via de functionaliteit GetWorldLocation en bewaar deze locatie in een variabele BeginLocatie.
- Bij het BeginOverlap, en ook bij EndOverlap van een triggerbox, kijk eerst of de overlappende actor de speler is door een Cast uit te voeren, let op eventueel moet u casten naar ThirdPersonCharacter of het type van spelerkarakter dat u gebruikt.
- Bij BeginOverlap van een triggerbox Play de Timeline, bij EndOvelap Reverse de Timeline.
- De Timeline wordt hier gestuurd door een vector, zie hieronder, deze vector levert een gewijzigde locatie op voor één, of meer, van de assen (naargelang de verschuiving). Deze gewijzigde locatie, afgeleverd door de Timeline wordt vervolgens opgeteld bij de vastgestelde Beginlocatie en dit wordt de nieuwe locatie van de schuifdeur die ingesteld wordt met de functie SetWorldLocation.
- De Timeline is op basis van een vector die de waarde van de X-as wijzigt over een tijd van bv. 2 seconden. In dit geval verschuift de X-as maar het kan ook een andere as (of assen) zijn.
Flikkerend licht
Onderstaande code toont hoe u een licht kunt laten flikkeren door een constante wijziging van zijn intensiteit (Set Intensity).
De Timeline neemt een grillige willekeurige vorm aan om het flikkeren weer te geven. Merk op dat Loop (herhalende lus) aangevinkt is zodat het flikkeren blijft duren.
Pickup
Onderstaand voorbeeld laat een actor roteren en de lichtintensiteit toenemen en afnemen. Hiertoe gebruikt u 2 tijdlijnen binnen dezelfde Timeline, één voor de lichtintensiteit (type Float) en één voor de rotatie (type Vector).
Merk ook op dat binnen de Timeline AutoPlay aangevinkt is zodat deze Timeline automatisch wordt afgespeeld en niet hoeft gekoppeld te worden aan een event (bv. Event BeginPlay).
Magnetische pickups
Onderstaande code demonstreert hoe u een actor (Pickup) naar u toe kunt laten bewegen (magnetisch), eens ze u bereiken (overlappen) worden ze vernietigd. De Outrange is een Sphere Collision die zich rondom de actor bevindt en bepaalt wanneer het “overvliegen” moet getriggerd worden.
- In een vorig voorbeeld heb ik getest of de overlappende actor ons PlayerCharacter is via een Casting, hier gebruik ik een alternatieve techniek door te vergelijken.
- U hebt zowel de locatie van zowel uw PlayerCharacter als de Actor nodig, ik gebruik hier 2 verschillende technieken (via GetActorTransform gevolgd door een Break Transform en GetActorLocation) die beiden de locatie opleveren.
- Gebruik de functie VInterp To om de overgang tussen de twee locaties vlot te laten verlopen, bepaald door de variabele Snelheid (ik gebruik hier voor snelheid een waarde 15).
- Dit levert een nieuwe locatie op die opgeteld wordt bij de waarde van de Timeline die lineair loopt van 0 naar 2 en bepaalt hoelang het “overvliegen” duurt.
- Dit levert de nieuwe locatie op van de actor die wordt ingesteld via SetActorLocation.
- Bij overlapping met uw Player Character moet de Actor vernietigd worden via de functionaliteit DestroyActor.
De Timeline loopt lineair van 0 naar 2 en bepaalt de duur van het “overvliegen”.
Movement Platforms
Onderstaande video werkt bewegende platformen uit.
Project Level Creation – Level Blueprint
Ons doorlopend project zit nu ook in de programmeerfase. Er wordt ook een deur geopend (en gesloten) maar deze keer een schuifdeur.
Ons doorlopend project is bijna ten einde. Nog één filmpje maar dit is voor de volgende handleiding.
Cursisten in actie
In onderstaande video opent cursist Thomas Van Sande een deur op basis van een Timeline.
Behandelde Basiscompetenties uit de module ICT Programmeren – Specifieke ontwikkelomgeving: eenvoudige functionaliteiten
- IC BC234 – kan de basisprincipes van programmeren in een specifieke ontwikkelomgeving toepassen
- IC BC236 – kan eenvoudige wijzigingen aan een programma aanbrengen
- IC BC241 – kan een programma in een specifieke ontwikkelomgeving maken
- IC BC247 – kan de bouwstenen van een specifieke ontwikkelomgeving gebruiken
- IC BC249 – kan de instellingen van een specifieke ontwikkelomgeving wijzigen
- IC BC250 – kan bij het programmeren in functie van een specifieke ontwikkelomgeving, een juiste logica volgen