In dit artikel kijken we naar het bouwen van een katapult in Unreal Engine, als alternatief voor typische wapenmechanieken om een projectiel af te vuren. Een katapult is een 2-handig gereedschap: één hand houdt en richt, de andere hand grijpt en trekt het projectiel naar achteren om de uiteindelijke richting en lanceersnelheid te bepalen, en laat het los om het te lanceren.

Tech Stack
Voor deze verkenning gebruiken we de volgende tech stack:
Katapult-opbouw
We verdelen de katapult-implementatie in de volgende onderdelen:
- Slingshot mesh - een Y-vormig mesh dat gegrepen kan worden
- Projectile mesh - een bolvormig mesh dat gegrepen kan worden wanneer de katapult met de andere hand wordt vastgehouden
- Actor Blueprint - combineert de verschillende componenten en voegt logica toe, bijvoorbeeld om een projectiel te lanceren
- Projectile grab/release handling - de logica om het projectiel te verplaatsen wanneer het wordt gegrepen en het te lanceren wanneer het wordt losgelaten
- Sling Niagara system - toont het dynamische elastiek tussen het katapult-mesh en het projectiel voordat het projectiel wordt gelanceerd
- Aim Niagara system - toont een laserstraal in de richting waar het projectiel wordt afgevuurd om te helpen bij het richten
- Physics Constraint - simuleert het projectiel dat in het elastiek hangt om het wat dynamischer te maken tijdens het bewegen
Slingshot Mesh
Elk Y-vormig mesh kan worden gebruikt om de katapult weer te geven. Als er geen dergelijk mesh beschikbaar is, kunnen we het eenvoudig maken met Unreal Engines Modeling Mode door de volgende stappen te volgen:
- Maak een dunne cilinder voor het I-deel
- Maak een grotere cilinder en trek een kleinere cirkel uit het midden om een O-vorm te maken
- Plane-cut de O-vorm om een U-vorm te maken
- Plaats de U-vorm bovenop het I-deel en merge ze om de uiteindelijke Y-vorm te maken
- Verplaats de pivot naar center-min-Z zodat deze onderaan staat

In de Static Mesh Editor kunnen we enkele Sockets toevoegen om de positie en oriëntatie van belangrijke punten te markeren die we later in het Blueprint zullen gebruiken:
Grip- dit is waar de hand naar zal springen bij het grijpen van de katapult, zodat deze altijd in dezelfde positie/oriëntatie wordt vastgehoudenAim- dit is waar we het Aim Niagara System plaatsenProjectile- dit is waar we het Projectiel plaatsen wanneer het niet door de andere hand wordt vastgehoudenSlingLeft- dit is waar we het Sling Niagara System aan de linkerkant bevestigenSlingRight- dit is waar we het Sling Niagara System aan de rechterkant bevestigen

Het gebruik van sockets maakt het later gemakkelijker om verschillende katapult-meshes met hetzelfde Blueprint te gebruiken.
Projectile Mesh
Voor het Projectiel-mesh gebruiken we een eenvoudige bol met de pivot in het midden.

Actor Blueprint
We maken een nieuw Blueprint met GrippableStaticMeshActor van de VR Expansion Plugin als parent-klasse, die al de VRGrip Interface implementeert en een uitstekende basis biedt voor onze katapult:
- We configureren de actor-eigenschappen
- Gebruik het katapult-mesh als Static Mesh in de root-component
Slot Default Grip Typeis ingesteld opAttachment Gripzodat de katapult aan de hand wordt bevestigd terwijl deze wordt vastgehoudenGameplay Tagszijn ingesteld opDropType.OnPrimaryGripReleaseenGrabType.OnPrimaryGripom grijpen/loslaten met standaardbesturingen mogelijk te maken- Schakel
Simulate Physicsin zodat de katapult niet in de lucht zweeft wanneer deze niet wordt vastgehouden - Collision presets zijn ingesteld op
PhysicsActor

Blueprint met het katapult-mesh - We voegen een
Hand Socket BasecomponentPrimaryGriptoe voor de primaire grijppositie/oriëntatie en passen de handpose aan zodat deze de katapult goed omvat- Schakel
Flip For Off Handin zodat een linkshandige greep de gespiegelde pose gebruikt - Schakel
Use Custom Pose Deltasin zodat de geconfigureerde handpose daadwerkelijk wordt gebruikt

PrimaryGrip component met handpose - Schakel
- We voegen een
ScenecomponentProjectileOrigintoe en bevestigen deze aan deProjectilesocket
Projectile origin component - We voegen een
GrippableStaticMeshComponentProjectiletoe en bevestigen deze aan deProjectileOrigin, en configureren deze correct:Slot Default Grip TypeenFree Default Grip Typezijn ingesteld opCustom Gripomdat we aangepast gedrag zullen hebben terwijl het projectiel wordt vastgehoudenGameplay Tagszijn ingesteld opDropTypeenGrabTypeom grijpen/loslaten standaard uit te schakelen (we schakelen dit alleen in wanneer de katapult zelf wordt vastgehouden)

Projectile component - We voegen een
Hand Socket BasecomponentProjectileGriptoe voor de projectielgrijppositie/oriëntatie en passen de handpose aan zodat deze het projectiel goed omvat- Schakel
Flip For Off Handin zodat een linkshandige greep de gespiegelde pose gebruikt - Stel
Mirror Axisin opYzodat de handpose correct is bij het gebruik van de linkerhand om het projectiel te grijpen - Schakel
Use Custom Pose Deltasin zodat de geconfigureerde handpose daadwerkelijk wordt gebruikt

ProjectileGrip component met handpose - Schakel
Opmerking: Normaal gesproken zouden we de PrimaryGrip component aan de Grip socket van het katapult-mesh bevestigen, maar deze component werkt niet goed met sockets. In plaats daarvan stellen we in het Begin Play event de locatie/oriëntatie in op die van de socket.
Projectile Grab/Release Handling
Momenteel kunnen we de katapult met beide handen grijpen, maar we kunnen nog niet met het projectiel interageren, omdat we de standaardinstellingen hebben ingesteld om alle grijp-/loslaatacties te negeren.
Daarom voegen we wat logica toe wanneer de katapult wordt gegrepen/losgelaten om het projectielgrijpen in/uit te schakelen door de corresponderende VR Grip Interface methoden op de actor te implementeren:
OnGrip- schakel het projectielgrijpen/loslaten in door de Gameplay TagsGrabType.OnPrimaryGrip/DropType.OnPrimaryGripReleasetoe te voegen aan de projectielcomponentOnGripRelease- schakel het projectielgrijpen/loslaten uit door de Gameplay TagsGrabType.OnPrimaryGrip/DropType.OnPrimaryGripReleasete verwijderen van de projectielcomponent

Nu moeten we afhandelen wanneer het projectiel zelf wordt gegrepen door de corresponderende events op de Projectile component te implementeren:
OnGripped- sla de grijp informatie op en begin met het bijwerken van de projectielcomponentlocatie/oriëntatie op basis van de handlocatie/oriëntatie in het actorTickeventOnDropped- lanceer een projectiel en breng de projectielcomponent terug naar de oorspronkelijke locatie/oriëntatieEvent Tick- als het projectiel wordt vastgehouden, werk de positie/oriëntatie bij naar die van de hand die het vasthoudt

Om een projectiel te lanceren wanneer de projectielcomponent wordt losgelaten, implementeren we onze LaunchProjectile methode:
- We zorgen ervoor dat de projectielcomponent zich in een geldig bereik bevindt (bijvoorbeeld minimaal 20 cm achter de oorspronkelijke positie), anders lanceren we geen projectiel, zodat het projectiel kan worden teruggeplaatst zonder te worden gelanceerd
- We bepalen de lanceerrichting door de
Look At Rotationte vinden van de projectielcomponent naar waar de oorspronkelijke positie zich momenteel bevindt - We spawnen een nieuw
BP_Projectileen stellen de oriëntatie in volgens de lanceerrichting - We spelen een 'whoosh' geluid af om een succesvolle lancering aan te geven

Tussentijds Resultaat 1
De onderstaande video laat zien dat we nu een functionele katapult hebben in onze Mixed Reality testmap.

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.
Natuurlijk kunnen we veel verbeteringen toevoegen, waarvan we er enkele in de volgende secties zullen behandelen.
Sling Niagara System
Momenteel lijkt het projectiel in de katapult te zweven, dus laten we een elastiek maken tussen de katapult en het projectiel.
Eerst maken we een materiaal voor het elastiek zoals getoond in de afbeelding hieronder. Omdat we Niagara beams zullen gebruiken voor het elastiek en het standaard beam-materiaal additief is en een gradient heeft (wat betekent dat het niet volledig ondoorzichtig is), maken we ons eigen beam-materiaal dat volledig ondoorzichtig kan zijn en zelfs een beetje fade langs de beamrand toevoegt om het idee van een niet-vlakke beam te geven.

Vervolgens maken we een Niagara System met 2 emitters, één voor elke kant van het elastiek.


De emitters zijn ingesteld op Local Space zodat het elastiek met de katapult meebeweegt, anders kunnen de elastieken achterblijven bij het bewegen van de katapult. Als bonus hoeven we het start- en eindpunt niet elke frame bij te werken, omdat ze constant blijven ten opzichte van de katapult.
We definiëren 5 parameters om het elastiek vanuit het blueprint te besturen:
SlingStart- het begin van de eerste helft van het elastiek, relatief ten opzichte van de Niagara System componentSlingMiddle- het einde van beide helften van het elastiek, relatief ten opzichte van de Niagara System componentSlingEnd- het begin van de tweede helft van het elastiek, relatief ten opzichte van de Niagara System componentSlingColor- de kleur van de elastiekenSlingWidth- de breedte van de elastieken
Opmerking: de Beam End locatie is relatief ten opzichte van Beam Start, dus in de emitter moeten we de Beam Start waarde aftrekken van de SlingMiddle waarde om een correcte positie voor Beam End te garanderen.
Nu het Niagara System klaar is, voegen we het toe aan het katapult blueprint, bevestigen het aan de SlingLeft socket en geven enkele betekenisvolle standaardwaarden voor de parameters:
SlingStartblijft op(0, 0, 0)wat overeenkomt met de locatie van deSlingLeftsocketSlingMiddleis ingesteld op(9, 0, -1)wat overeenkomt met het midden van het projectielSlingEndis ingesteld op(18, 0, 0)wat overeenkomt met de locatie van deSlingRightsocket- We zouden wat logica kunnen toevoegen aan
Begin Playom daadwerkelijk de juiste waarde van de socket te nemen en in te stellen, we laten dit als oefening voor de lezer
- We zouden wat logica kunnen toevoegen aan
SlingWidthis ingesteld op1.0wat dik genoeg is voor het elastiek

Nu moeten we ervoor zorgen dat het midden van het elastiek altijd overeenkomt met de locatie van het projectiel, dus voegen we een nieuwe functie UpdateSlingPosition toe aan het blueprint en roepen deze aan op 2 locaties in de EventGraph:
- Nadat het projectiel is teruggebracht naar de oorspronkelijke locatie na het loslaten
- Nadat het projectiel is verplaatst naar de locatie van de grijpende controller wanneer het wordt vastgehouden

Bij het bijwerken van het midden van het elastiek moeten we ervoor zorgen dat de positie die we instellen relatief is ten opzichte van de Niagara System component, want dat is wat ons Niagara System verwacht.

Nu hebben we een functionerend elastiek dat met het projectiel meebeweegt, wat de katapult al aanzienlijk verbetert. In toekomstige iteraties kunnen we het nog beter maken, bijvoorbeeld door:
- De kleur en breedte van het elastiek aan te passen wanneer het langer wordt (door het projectiel te grijpen en naar achteren te bewegen) om toenemende spanning op het elastiek aan te geven
- Een 'bucket'-mesh toe te voegen dat het projectiel vasthoudt en het elastiek eraan te bevestigen (in plaats van het elastiek te verbinden met het midden van het projectiel)
Aim Niagara System
Om de gebruiker te helpen bij het richten van de katapult, voegen we nog een Niagara System toe voor een laser die in de richting wijst waar het projectiel wordt afgevuurd. Het geeft ook aan wanneer het projectiel wordt gelanceerd bij het loslaten, omdat we de laser alleen inschakelen wanneer het projectiel meer dan 15 cm van de oorspronkelijke oorsprong verwijderd is.
Eerst maken we een materiaal voor het richten zoals getoond in de afbeelding hieronder, zodat we wat meer transparantie hebben naar de randen van de beam. In plaats van Additive gebruiken we een Translucent materiaal, zodat het ook zichtbaar is in gebieden waar passthrough is ingeschakeld.

Vervolgens maken we een Niagara System met 1 emitter.

De emitter is ingesteld op Local Space zodat het richten met de katapult meebeweegt, anders kan het richten achterblijven bij het bewegen van de katapult. Hier nemen we aan dat het begin van de aim beam altijd op de locatie van de Niagara System component is.
We definiëren 3 parameters om het richten vanuit het Blueprint te besturen:
AimEnd- het einde van de aim beam, relatief ten opzichte van de Niagara System componentAimColor- de kleur van de aim beamAimWidth- de breedte van de aim beam
Nu het Niagara System klaar is, voegen we het toe aan het katapult blueprint, bevestigen het aan de Aim socket en geven enkele betekenisvolle standaardwaarden voor de parameters:
AimEndis ingesteld op(100, 0, 0)zodat we het kunnen zien in de blueprint viewport en kunnen verifiëren dat het op de juiste locatie staat en de juiste oriëntatie heeftAimWidthis ingesteld op1.0wat dik genoeg is voor het richten

Nu moeten we ervoor zorgen dat:
- de aim beam alleen zichtbaar is wanneer het projectiel minimaal 15 cm van de oorspronkelijke oorsprong verwijderd is (waar het loslaten een projectiel zou lanceren), en
- de richting van de aim beam overeenkomt met de richting waarin het projectiel nu zou worden gelanceerd, en
- de aim beam stopt bij het eerste zichtbare object/oppervlak dat het raakt (het zou niet door vaste objecten/oppervlakken moeten gaan)
Hiervoor voegen we een nieuwe functie UpdateAimPosition toe aan het Blueprint en roepen deze aan op 3 locaties in de EventGraph:
BeginPlayevent - dit zorgt ervoor dat de aim beam verborgen is aan het begin van het level, ervan uitgaande dat het projectiel op zijn oorsprong is- Nadat het projectiel is teruggebracht naar de oorspronkelijke locatie na het loslaten
- Nadat het projectiel is verplaatst naar de locatie van de grijpende controller wanneer het wordt vastgehouden

De implementatie doet in wezen de volgende stappen:
- Toon of verberg het richten op basis van de huidige afstand tussen het projectiel en de oorspronkelijke oorsprong
- Stel de richting van de aim beam in op de richting van het projectiel als het nu zou worden gelanceerd
- Voer een visibility LineTrace uit vanaf de aim start locatie in de aim richting en pas de aim beam lengte dienovereenkomstig aan (zodat het niet door vaste objecten/oppervlakken gaat)


Nu is het richten veel gemakkelijker, zelfs wanneer de aim start positie iets onder de werkelijke projectiellanceerlocatie ligt. Dit is geen groot probleem, gezien de relatief hoge lanceersnelheid en het feit dat het projectiel momenteel wordt beïnvloed door zwaartekracht, terwijl het richten een rechte lijn is.
Tussentijds Resultaat 2
De onderstaande video laat zien dat we nu een katapult hebben in onze Mixed Reality testmap met een functionerend elastiek en richten, die de gebruiker ook aangeeft wanneer een projectiel zou worden gelanceerd bij het loslaten van het projectiel, of wanneer het gewoon terug zou keren naar de oorspronkelijke oorsprong.

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.
Natuurlijk kunnen we nog steeds enkele verbeteringen toevoegen, met name een manier om de projectiellocatie minder rigide te maken wanneer deze niet wordt vastgehouden, zodat het eruitziet alsof het projectiel in het elastiek hangt.
Physics Constraint
Momenteel hangt het projectiel zeer rigide in de katapult, wat niet erg mooi is. Om het projectiel een beetje te laten bewegen op basis van de katapultbeweging, kunnen we een PhysicsConstraint toevoegen aan de katapult.
We hebben besloten om een onzichtbare proxy te simuleren voor het projectiel (PhysicsProjectile) en de zichtbare projectiellocatie bij te werken naar de proxy-locatie wanneer deze niet wordt vastgehouden. Op die manier hoeven we de PhysicsConstraint niet te breken/terug te zetten wanneer de gebruiker het projectiel grijpt of loslaat.
Voor deze setup hebben we 3 extra componenten nodig:
- Een
PhysicsOrigincomponent die het vaste punt van de constraint is, die geen fysica simuleert en niet wordt beïnvloed door zwaartekracht - Een
PhysicsProjectilecomponent die de onzichtbare proxy voor het projectiel is, die fysica simuleert en wordt beïnvloed door zwaartekracht - Een
PhysicsConstraintcomponent die beide componenten verbindt en het PhysicsProjectile op de juiste plaats houdt, zelfs als we de katapult verplaatsen of roteren
De PhysicsOrigin heeft de volgende eigenschappen:
- Deze is iets boven de
ProjectileOrigingeplaatst, in dit geval 1 cm Collisionis ingesteld opNo CollisionHidden in Gameis ingeschakeld, omdat we dit alleen gebruiken als referentiepunt voor de PhysicsConstraint

Het PhysicsProjectile heeft de volgende eigenschappen:
Simulate Physicsis ingeschakeldCollisionis ingesteld opPhysics Only, en alle collision responses zijn ingesteld opIgnoreHidden in Gameis ingeschakeld, omdat we dit alleen gebruiken voor de simulatie terwijl het projectiel niet wordt vastgehouden

De PhysicsConstraint heeft de volgende eigenschappen:
- Deze is geplaatst op de
Projectilesocket van de katapult - De constraint
Component Nameszijn ingesteld opPhysicsOriginenPhysicsProjectile(dit moet exact overeenkomen met de componentnamen) Disable Collisionis ingeschakeld, omdat we het PhysicsProjectile slechts zeer beperkt (2 cm) binnen de katapult laten bewegenParent Dominatesis ingeschakeld, omdat we alleen het PhysicsProjectile willen verplaatsen en de PhysicsOrigin beschouwen als een vast referentiepunt binnen de katapult- We stellen
X / Y / Z Motionin opLimiteden stellen deLimitin op 2 cm - We stellen de
Angular Limitsin opLockedomdat we ons momenteel niet interesseren voor rotaties - We stellen het Linear Motor
Position Targetin op0, 0, 0met 25 sterkte - We stellen het Linear Motor
Velocity Targetin op0, 0, 0met 1 sterkte


Om dit goed te laten werken bij het verplaatsen van de katapult, moeten we het PhysicsProjectile wekken en het Reference Frame elke tick aanpassen. We doen dit door een nieuwe functie UpdatePhysicsConstraint toe te voegen en deze aan het begin van de Event Tick in de EventGraph aan te roepen.


Het resultaat is te zien in de onderstaande video, die er nu veel beter uitziet met het hangende projectiel.

Als je op de afspeelknop klikt, wordt de YouTube-speler ingesloten en kunnen cookies van derden worden ingesteld en gegevens van YouTube worden verzameld.
Verdere verbeteringen
De katapult die we tot nu toe hebben gebouwd werkt al vrij goed, maar er zijn natuurlijk verdere verbeteringen mogelijk:
- Schakel de tick-verwerking alleen in wanneer de katapult wordt vastgehouden
- Gebruik een
GrippableSphereComponentvoor de projectielgreep (in plaats van eenGrippableStaticMeshComponent), zodat we deze iets groter kunnen maken dan het projectiel zelf om de projectielgreep gemakkelijker te maken voor de gebruiker (momenteel kan de gebruiker per ongeluk de katapult zelf grijpen wanneer de hand te veel naar links of rechts van het projectiel is) - Scheid het projectiel van de katapult (zodat de gebruiker een projectiel ergens anders moet oppakken en het dan met de katapult kan gebruiken)
- Beperk het bereik waarin een projectiel wordt gelanceerd, momenteel is elke richting toegestaan, zelfs wanneer het geen zin heeft (bijvoorbeeld het projectiel recht naar beneden door de katapult trekken)
- Pas de gelanceerde projectielsnelheid aan op basis van hoe ver het projectiel van de katapult is teruggetrokken
Conclusie
Een katapult is een leuke variatie op de traditionele wapenmechanieken en kan extra immersie bieden door een leuke manier te bieden om projectielen te richten en te lanceren in een VR- of MR-omgeving.
Wij bij Immerstory zijn enthousiast om onze klanten te ondersteunen bij hun immersieve ervaringen en kijken ernaar uit om binnenkort meer verkenningstoezicht te delen.
Welke immersieve ervaring zou je graag willen zien? Laat het ons weten!
Aanvullende bronnen
Documentatie voor de MetaXR plugin, specifiek het Mixed Reality Utility Kit dat voor deze demo werd gebruikt, is hier te vinden
Geluidseffecten in de demo-app zijn dankbaar verkregen van Sonniss GameAudioGDC

