Devlog: Building a Slingshot for Unreal Engine

Devlog: Building a Slingshot for Unreal Engine

Thijs ReusThijs Reus
·December 12, 2025·27 min read

In this post, we look at building a slingshot in Unreal Engine, as an alternative to typical gun mechanics to shoot a projectile. A slingshot is a 2-handed tool, one hand to hold and aim it, and the other hand to grab and pull back the projectile to determine the final aim and launch speed, and releasing the projectile to launch it.

Slingshot in Mixed Reality
Slingshot in Mixed Reality

Tech Stack

For this exploration, we're using the following tech stack:

Slingshot breakdown

We break down the slingshot implementation in the following parts:

  • Slingshot mesh - a Y-shaped mesh that can be grabbed
  • Projectile mesh - a sphere mesh that can be grabbed when the Slingshot is being held by the other hand
  • Actor Blueprint - combines the various components and adds logic e.g. to launch a projectile
  • Projectile grab/release handling - the logic to move the projectile when grabbed, and launch a projectile when released
  • Sling Niagara system - displays the dynamic sling between the Slingshot mesh and the projectile before the projectile is launched
  • Aim Niagara system - displays a laser beam in the direction where the projectile will be launched to help aiming
  • Physics Constraint - simulates the projectile hanging in the sling to give it a bit more dynamic while moving around

Slingshot Mesh

Any Y-shaped mesh can be used to represent the slingshot. If no such mesh is available, we can easily create it with Unreal Engines Modeling Mode using the following steps:

  • Create a thin cylinder for the I-part
  • Create a larger cylinder and subtract a smaller circle from its center to create an O-shape
  • Plane-cut the O-shape to create a U-shape
  • Place the U-shape on top of the I-shape, and merge them to create the final Y-shape
  • Move the pivot to center-min-Z so it's at the bottom
Y-shaped mesh for the slingshot
Y-shaped mesh for the slingshot

In the Static Mesh Editor, we can add some Sockets to mark the position and orientation of significant points we'll use later in the Blueprint:

  • Grip - this is where the hand will snap to when grabbing the slingshot, so that it is always held in the same position/orientation
  • Aim - this is where we'll place the Aim Niagara system
  • Projectile - this is where we'll place the Projectile when it is not held by the other hand
  • SlingLeft - this is where we'll attach the Sling Niagara system on the left side
  • SlingRight - this is where we'll attach the Sling Niagara system on the right side
Sockets for the slingshot
Sockets for the slingshot

Using sockets makes it easier to later use different slingshot meshes with the same Blueprint.

Projectile Mesh

For the Projectile mesh we use a simple Sphere with the pivot in the center.

Sphere for the Projectile
Sphere for the Projectile

Actor Blueprint

We create a new Blueprint with GrippableStaticMeshActor from the VR Expansion Plugin as the parent-class, which already implements the VRGrip Interface and provides an excellent basis for our slingshot:

  • We configure the actor properties
    • Use the slingshot mesh as Static Mesh in the root component
    • Slot Default Grip Type is set to Attachment Grip so that the slingshot is attached to the hand while being gripped
    • Gameplay Tags are set to DropType.OnPrimaryGripRelease and GrabType.OnPrimaryGrip to allow grip/drop with default controls
    • Enable Simulate Physics so the slingshot does not float in the air while not being held
    • Collision presets are set to PhysicsActor
    Blueprint using the slingshot mesh
    Blueprint using the slingshot mesh
  • We add a Hand Socket Base component PrimaryGrip for the primary grip location/orientation, and adjust the hand-pose so that it properly wraps the slingshot
    • Enable Flip For Off Hand so that a left hand grap uses the mirrored pose
    • Enable Use Custom Pose Deltas so that the hand pose we've configured is actually used
    PrimaryGrip component with hand pose
    PrimaryGrip component with hand pose
  • We add a Scene component ProjectileOrigin and attach it to the Projectile socket
    Projectile origin component
    Projectile origin component
  • We add a GrippableStaticMeshComponent Projectile and attach it to the ProjectileOrigin, and configure it properly:
    • Slot Default Grip Type and Free Default Grip Type are set to Custom Grip as we will have customized behavior while the projectile is held
    • Gameplay Tags are set to DropType and GrabType so disable grab/drop by default (we will only enable it when the Slingshot itself is held)
    Projectile component
    Projectile component
  • We add a Hand Socket Base component ProjectileGrip for the projectile grip location/orientation, and adjust the hand-pose so that it properly wraps the projectile
    • Enable Flip For Off Hand so that a left hand grap uses the mirrored pose
    • Set Mirror Axis to Y so the hand pose is correct when using left hand to grab the projectile
    • Enable Use Custom Pose Deltas so that the hand pose we've configured is actually used
    ProjectileGrip component with hand pose
    ProjectileGrip component with hand pose

Note: Normally we would attach the PrimaryGrip component to the Grip socket of the slingshot mesh, however this component does not work well with sockets. So instead, in the Begin Play event, we set its location/orientation to that of the socket.

Projectile Grab/Release Handling

Currently we can grab the slingshot with either hand, but we cannot yet interact with the projectile, as we've set the defaults to ignore any grab/release actions.

Thus, we add some logic when the slingshot is grabbed/released to enable/disable the projectile grab, by implementing the corresponding VR Grip Interface methods on the actor:

  • OnGrip - enable the projectile grab/release by adding the Gameplay Tags GrabType.OnPrimaryGrip / DropType.OnPrimaryGripRelease to the projectile component
  • OnGripRelease - disable the projectile grab/release by removing the Gameplay Tags GrabType.OnPrimaryGrip / DropType.OnPrimaryGripRelease from the projectile component
OnGrip / OnGripRelease logic
OnGrip / OnGripRelease logic

Now we need to handle when the projectile itself is grabbed by implementing the corresponding events on the Projectile component:

  • OnGripped - store the grip information, and start updating the projectile component location/orientation based on the hand location/orientation in the actor Tick event
  • OnDropped - launch a projectile and return the projectile component back to its original location/orientation
  • Event Tick - if the projectile is being held, update its position/orientation to that of the hand that holds it
Projectile OnGripped / OnDropped / Tick logic
Projectile OnGripped / OnDropped / Tick logic

In order to launch a projectile when the projectile component is dropped, we implement our LaunchProjectile method:

  • We ensure that the projectile component is in a valid range (e.g. at least 20 cm behind its original position), otherwise we don't launch a projectile, so that the projectile can be put back without being launched
  • We determine the direction of the launch by finding the Look At Rotation from the projectile component to where its original position currently is
  • We spawn a new BP_Projectile and set its orientation according to the launch direction
  • We play a 'whoosh' sound to indicate a successful launch
Launch projectile logic
Launch projectile logic

Intermediate Result 1

The video below shows we now have a functional slingshot in our Mixed Reality test map.

Video Preview

Clicking play will embed the YouTube player and may set third-party cookies and collect data from YouTube.

Of course, we can add a lot of improvements, some of which we'll cover in the following sections.

Sling Niagara System

Currently the projectile seems to be floating in the slingshot, so let's make a sling between the slingshot and the projectile.

First we create a material for the sling as shown in the image below. Since we'll be using Niagara beams for the sling, and the default beam material is additive and has a gradient (meaning it is not fully opaque), we create our own beam material that can be fully opaque, and even adds a little bit of fade along the beam-edge to give the idea of a non-flat beam.

Sling material for the Niagara beams
Sling material for the Niagara beams

Then we create a Niagara System with 2 emitters, one for each side of the sling.

Sling Niagara System with an emitters for each sling half
Sling Niagara System with an emitters for each sling half
Second sling half emitter
Second sling half emitter

The emitters are set to Local Space so that the sling moves with the slingshot, otherwise the slings may lag behind when moving the slingshot around. As a bonus, we don't need to update the start and end point every frame, as they remain constant relative to the slingshot.

We define 5 parameters to control the sling from the blueprint:

  • SlingStart - the start of the first half of the sling, relative to the Niagara system component
  • SlingMiddle - the end of both halves of the sling, relative to the Niagara system component
  • SlingEnd - the start of the second half of the sling, relative to the Niagaray system component
  • SlingColor - the color of the slings
  • SlingWidth - the width of the slings

Note: the Beam End location is relative to the Beam Start, so in the emitter we have to subtract the Beam Start value from the SlingMiddle value to ensure a correct position for the Beam End.

Now that the Niagara System is ready, we add it to the slingshot blueprint, attach it to the SlingLeft socket, and provide some meaningful default values for the parameters:

  • SlingStart remains at (0, 0, 0) which corresponds to the location of the SlingLeft socket
  • SlingMiddle is set to (9, 0, -1) which corresponds to the middle of the projectile
  • SlingEnd is set to (18, 0, 0) which corresponds to the location of the SlingRight socket
    • We could add some logic to Begin Play to actually take the proper value from the socket and set it, we leave this as an exercise to the reader
  • SlingWidth is set to 1.0 which is thick enough for the sling
Slingshot blueprint with sling Niagara system
Slingshot blueprint with sling Niagara system

Now we need to ensure that the middle of the sling always corresponds with the location of the projectile, so we add a new function UpdateSlingPosition to the blueprint, and call it in 2 locations in the EventGraph:

  • After the projectile has been moved back to its initial location after being dropped
  • After the projectile has been moved to the location of the gripping controller when being held
Triggering the UpdateSlingPosition function in the event graph
Triggering the UpdateSlingPosition function in the event graph

When updating the middle of the sling we must ensure the position we set is relative to the Niagara System component, as this is what our Niagara System expects.

UpdateSlingPosition function implementation
UpdateSlingPosition function implementation

Now we have a functioning sling that moves with the projectile, which already improves the slingshot a lot. In future iterations we could make it even better, for example by:

  • Adjusting the color and width of the sling when it gets longer (by grabbing the projectile and moving it backward) to indicate increasing tension on the sling
  • Adding a 'bucket'-mesh that holds the projectile, and attach the sling to it (instead of connecting the sling to the center of the projectile)

Aim Niagara System

To help the user aim the slingshot, we add another Niagara system for a laser that points in the direction where the projectile will be fired at. It also indicates when the projectile will be launched when dropped, as we only enable the laser when the projectile is more than 15cm from its original origin.

First we create a material for the aim as shown in the image below, so that we have a bit more transparency towards the edges of the beam. Instead of Additive we use a Translucent material, so that it is also visible in areas where passthrough is enabled.

Aim material for the Niagara beams
Aim material for the Niagara beams

Then we create a Niagara System with 1 emitter.

Aim Niagara System with a single emitter for the aim beam
Aim Niagara System with a single emitter for the aim beam

The emitter is set to Local Space so that the aim moves with the slingshot, otherwise the aim may lag behind when moving the slingshot around. Here we assume that the start of aim beam is always at the location of the Niagara system component.

We define 3 parameters to control the aim from the Blueprint:

  • AimEnd - the end of the aim beam, relative to the Niagaray system component
  • AimColor - the color of the aim beam
  • AimWidth - the width of the aim beam

Now that the Niagara System is ready, we add it to the slingshot blueprint, attach it to the Aim socket, and provide some meaningful default values for the parameters:

  • AimEnd is set to (100, 0, 0) so we can see it in the blueprint viewport and verify it's in the correct location and has the correct orientation
  • AimWidth is set to 1.0 which is thick enough for the aim
Slingshot blueprint with Aim Niagara system
Slingshot blueprint with Aim Niagara system

Now we need to ensure that:

  1. the aim beam is only visible when the projectile is at least 15cm from its original origin (where dropping it would launch a projectile), and
  2. the direction of the aim beam matches the direction that the projectile would launch to right now, and
  3. the aim beam stops at the first visible object/surface it hits (it shouldn't passthrough any solid objects/surfaces)

For this, we add a new function UpdateAimPosition to the Blueprint, and call it in 3 locations in the EventGraph:

  • BeginPlay event - this ensures the aim beam is hidden at the start of the level, assuming the projectile is at its origin
  • After the projectile has been moved back to its initial location after being dropped
  • After the projectile has been moved to the location of the gripping controller when being held
Triggering the UpdateAimPosition function in the event graph
Triggering the UpdateAimPosition function in the event graph

The implementation basically does the following steps:

  • Show or hide the aim based on the current distance between the projectile and its original origin
  • Set the direction of the aim beam to the direction of the projectile if it would be launched right now
  • Do a visibility LineTrace from the aim start location in the aim direction, and adjust the aim beam length accordingly (so it doesn't go through solid objects/surfaces)
UpdateSlingPosition function implementation (1st part)
UpdateSlingPosition function implementation (1st part)
UpdateSlingPosition function implementation (2nd part)
UpdateSlingPosition function implementation (2nd part)

Now aiming is a lot easier, even when the aim start position is slightly below the actual projectile launch location. This is not a big issue, given the relatively high launch velocity, and the fact that currently the projectile is affected by gravity, while the aim is a straight line.

Intermediate Result 2

The video below shows we now have a slingshot in our Mixed Reality test map with a functioning sling and aim, which also indicates to the user when a projectile would be launched when dropping the projectile, or when it would simply return to its original origin.

Video Preview

Clicking play will embed the YouTube player and may set third-party cookies and collect data from YouTube.

Of course, we can still add some improvements, specifically a way to make the projectile location less rigid when it's not being held, so that it looks like the projectile is hanging in the sling.

Physics Constraint

Currently the projectile is hanging very rigidly in the slingshot, which is not very nice. To make the projectile move around a little based on the slingshot movement, we can add a PhysicsConstraint to the slingshot. We've decided to simulate an invisible proxy for the projectile (PhysicsProjectile), and update the visible projectile location to the proxy location whenever it's not being held. That way we don't need to break / reset the PhysicsConstraint whenever the user grabs or releases the projectile.

For this setup, we need 3 additional components:

  • A PhysicsOrigin component which is the fixed point of the constraint, which does not simulate physics and is not affected by gravity
  • A PhysicsProjectile component which is the invisible proxy for the projectile, which simulates physics and is affected by gravity
  • A PhysicsConstraint component that links both components and keeps the PhysicsProjectile in the correct place, even if we move or rotate the slingshot

The PhysicsOrigin has the following properties:

  • It is placed slightly above the ProjectileOrigin, in this case 1 cm
  • Collision is set to No Collision
  • Hidden in Game is enabled, as we only use this as a reference point for the PhysicsConstraint
PhysicsOrigin component in the slingshot blueprint
PhysicsOrigin component in the slingshot blueprint

The PhysicsProjectile has the following properties:

  • Simulate Physics is enabled
  • Collision is set to Physics Only, and all collision responses are set to Ignore
  • Hidden in Game is enabled, as we only use this for the simulation while the projectile is not being held
PhysicsProjectile component in the slingshot blueprint
PhysicsProjectile component in the slingshot blueprint

The PhysicsConstraint has the following properties:

  • It is placed at the Projectile socket of the slingshot
  • The constraint Component Names are set to PhysicsOrigin and PhysicsProjectile (this has to match the component names exactly)
  • Disable Collision is enabled, as we only let the PhysicsProjectile move very limited amount (2cm) within the slingshot
  • Parent Dominates is enabled, as we only want the move the PhysicsProjectile, and consider the PhysicsOrigin a fixed reference point within the slingshot
  • We set X / Y / Z Motion to Limited and set the Limit to 2 cm
  • We set the Angular Limits to Locked as we're not interested in rotations for now
  • We set the Linear Motor Position Target to 0, 0, 0 with 25 strength
  • We set the Linear Motor Velocity Target to 0, 0, 0 with 1 strength
PhysicsConstraint component (1st part) in the slingshot blueprint
PhysicsConstraint component (1st part) in the slingshot blueprint
PhysicsConstraint component (2nd part) in the slingshot blueprint
PhysicsConstraint component (2nd part) in the slingshot blueprint

In order to make this work properly when moving the slingshot, we need to wake the PhysicsProjectile and adjust the Reference Frame every tick. We do this by adding a new function UpdatePhysicsConstraint, and calling this at the start of the Event Tick in the EventGraph.

Event graph to update the physics constraint every tick
Event graph to update the physics constraint every tick
UpdatePhysicsConstraint implementation
UpdatePhysicsConstraint implementation

The result can be seen in the video below, which looks a lot nicer with the hanging projectile now.

Video Preview

Clicking play will embed the YouTube player and may set third-party cookies and collect data from YouTube.

Further improvements

The slingshot we've built so far already works pretty well, but of course there are further improvements that can be done:

  • Enable the tick-processing only when the slingshot is being held
  • Use a GrippableSphereComponent for the projectile grip (instead of a GrippableStaticMeshComponent), so we can make it a little bigger than the projectile itself to make the projectile grip easier for the user (currently the user may accidentally grip the slingshot itself when the hand is too much too the left or right from the projectile)
  • Separate the projectile from the slingshot (so that the user must pickup a projectile from somewhere else, and can then use it with the slingshot)
  • Limit the range in which a projectile will be launched, currently any direction is allowed, even when it makes no sense (e.g. pull the projectile straight down through the slingshot)
  • Adjust the launched projectile speed based on how far the projectile was pulled back from the slingshot

Conclusion

A slingshot is a nice variation of the traditional gun mechanics and can provide additional immersion by providing a fun way to aim and launch projectiles in a VR or MR environment.

We at Immerstory are excited to support our customers with their immersive experiences, and look forward to sharing more exploration progress soon.

What immersive experience would you like to see? Let us know!

Additional Resources

Documentation for the MetaXR plugin, specifically the Mixed Reality Utility Kit which was used for this demo, can be found here

Sound effects in the demo app have been thankfully sourced from Sonniss GameAudioGDC

More from the blog