1.Basic Gripping - Tutorial
This tutorial will go over the bare minimum to get going with a flexible grip setup with the plugin. It will NOT go as in depth as the full implementation in the example template where gameplay tags and special properties are taken into account.
You can reference the template for a complex full implementation. Keeping in mind that some things are purely for template reasons, like the gameplay tag input re-mapping, something you normally would not need in a game.
The gripping system is intended to be as open and flexible as possible, to follow this central design principal, I try NOT to automate things when possible as every fully automatic step is another step where the end user loses control in the logic chain. This means that the setup to grip is generally a series of utility nodes in a logic chain that the end user defines, giving them full control over when/why/how to grip objects, but also making the setup more complicated than “grip this”.
Due to the sparse nature of the grip logic pre-implementation, you are free to control grips in your character, player controller, the gripped object itself, or wherever you choose and can pull a GripMotionController reference in. This guide however will go over running grips from the Character itself.
The basic concept of the gripping system is to call a “GripObject” function from the motion controller attempting a grip, when done with the grip you can then call a “DropObject” function to release the held object.
There are different variations of the grip / drop nodes, but we will just be covering the primary two types, (Grip/Drop)Object and (Grip/Drop)ObjectByInterface.
GripObject allows you to define core grip settings on ANY primitive object in engine and then grip it. GripObjectByInterface uses the built in VRGripInterface to take advanced settings and features and automatically grip the object based on those settings.
The plugin comes with a large set of “Grippable” objects (Grippable mesh / skeletal mesh / shapes / components), with the grip interface and a bunch of bug fixes and utilities already implemented in them. The state of the VRGripSettings struct on those grippables defines how GripByInterface behaves. You can manually add the VRGripInterface to any object as well as long as you implement all of its functions.
Generally for most workflows you will use GripObjectByInterface 99% of the time, GripObject is saved for non interface objects or when you want to do something special like ignore an objects grip settings.
You can run both (like the template does) by pre-checking if the target object has the VRGripInterface or not and then filtering the node selection based on that.
For the purposes of this tutorial you should create any objects that you want to grip as a GrippableStaticMeshActor or a GrippableSkeletalMeshActor.
GripMotionControllerComponents have the concept of a “Custom Pivot”, which is a settable scene component that the controller uses as the base of its gripping / location tracking for grips instead of itself. This is generally very useful for things like offsetting the grip position into the palm or being able to shift things around slightly at will.
Its less useful with OpenXR as the controllers are already palm centered but it is fine to use regardless and generally recommended.
Setting a custom pivot for a grip controller is as easy as calling “SetCustomPivotComponent” on the controller as some point during initialization.
All internal grip controller code checks GetPivotTransform when performing operations and offsets grip input transforms to account for custom pivots already. Specifically SecondaryAttachments track to the secondary attachments PivotTransform if it is a grip controller.
1.2.Finding an object
The very basic form of checking for an object to grip is below, you can substitute tracing for overlapping or any other method that you choose (the template both traces AND overlaps to cover downsides with each method).
You can copy and paste the nodes directly into the editor with this link:
1.3.Trace For a Grip
To try and grip the traced object lets fill out and create a function for it.
Make a new function called “AttemptToGripFromTrace” and fill out the node structure like below. A screenshot of a basic grip of a component is below, the full node layout for choosing between the actor and the component and then gripping one is linked afterwards.
This function will automatically grip any interfaced object with its default settings. The world offset input is the world location relative to the motion controller that you want the gripped object to sit at. For instance, passing in the full transform of the object as was done above will lock the object in at that distance and rotation from the motion controller no matter how it moves.
If instead you passed in an already relative transform of 0,0,0 (Identity) then the object would snap to the motion controllers base point. This is abused to allow for socket snapping, offsetting, and full control over the required location relative to the gripping controller.
1.4.Adding In Slot Grips
Slotting is a term used through-out the plugin to define what is commonly more known as “SnapGrips”, a grip that “snaps” to location and doesn’t retain its original offset to the hand.
You can control these any way that you want, really all slotting involves is passing in a different transform to the grip request. However there is a default method of implementing one using mesh sockets with pre-defined prefix’s on their name.
For instance look at the mesh shown above, it has two mesh sockets added to it, VRGripP and VRGripS, the number after is just the identifier, you can have as many of them as you want. Unless changed these are the two default prefix’s for our utility gripping nodes. We will focus on VRGripP, the default prefix for a “Primary” grip (main hand). To utilize these sockets on the shown mesh we need to change around the gripping setup in our function a bit. Also to make it more clean with this method it has also been switched to “bIsAlreadyRelative” when gripping, generally you want to pass in relative values anyway.
This setup is now first checking for if there is a “GripSlotInRange” of the motion controller (range is setup in the VRGripInterface properties on the object). Then if there was one, it uses that location as the grip point instead of the objects world location, this will “snap” the object to the hand at that relative location (the sockets location on the object). If there was no slot in range then it instead grips the object in its world position (made relative as well). Setting bIsSlotGrip should be done, however it has no internal plugin use and exists for the end user to more easily differentiate grips.
The snap transform is attained by getting the objects relative transform to the sockets world transform. You “could” inverse the socket transform in component space for essentially the same thing, except then you also have to handle de-scaling it. I’ll note that if your motion controller isn’t scaled 1:1 with the world that this setup would need to change a little to accommodate the scale, I keep it simple because usually people are not scaling their characters.
If the default setup isn’t to your liking you can use your own transform getting method to pass in to the grip function yourself, or you can override the ClosestGripSlotInRange function on the object itself and pass out whatever you want. You will likely want a scale of 1 to be always passed out though so that the relative transform setup above works correctly. The default implementation does this already. If you pass out another scale it will inversely effect the scale of the end grip.
For example, overriding the function like above and cutting out the Parent implementation. You could pass out scene component locations on the object instead to act like snap points. Or pass out true for HadSlotInRange always and a set transform, or check if the calling controller is left or right handed and adjust sockets or logic accordingly. Doing an override like this in a base class for your grippables would let ALL of them behave differently for you.
In the end, how you handle passing in transforms and how you setup socketing is entirely up to you. You can modify or change any step in this chain as you wish. This is the reason that I made the gripping be a set of utility nodes and don’t specifically handle the how/why/when logic for you.
1.5.Dropping a Grip
Dropping a grip is simple, depending on if you set up your game to allow for more than one grip at a time per hand (the plugin supports 128 concurrent grips per hand) then you will need more or less advanced logic.
I will be covering the most simplistic case of just dropping all held objects from a hand.
This is the most simplistic case of dropping all held grips on a loop. The DropObject nodes can drop based off of the Object OR the GripID, it is preferrable to drop off of the GripID when possible as it is safer when doing things in multiplayer.
The OptionalVelocity pins are there for adjusting throw velocities or when passing client side values up to the server on release time, ignore them for now.
1.5.1.Drop And Socket A Grip
Dropping and socketing a grip is a newer setup for the plugin, before it was added it was expected that the end user implement their own attachment and “socketing” post drop so that they had full control over it.
In the end this was flawed, due to some serious attachment replication issues in engine and a concurrency issue with the physics thread I ended up creating a dedicated DropAndSocket node to help people wade through it.
This node will automatically drop a grip, attach it to the given parent at the given relative transform, and if they were both in a simulation state, will also delay a tick for the physic thread to catch up and then reset the relative position to solve some concurrency issues.
Its fairly self explanatory how it works, there is also the RequestsSocketing VRGripInterface function that can be overridden to allow anything to query the object and ask if it wants socketing prior to dropping it in the case where dropping is controlled outside of the object but you want the object to decide if socketing should happen on drop.
*Please note that as of 4.22 there is still a major engine issue with replicating attachment welding, in that the engine just straight up doesn’t do it. I have a bug report in with Epic concerning this oversight.*
*As of 4.24 I have fixed this in the plugins grippable base actor classes, however I cannot fix it for base engine classes*
1.6.Multi Gripping & Secondary Attachments
In the VRGripInterfaceSettings of grippable base classes there is a boolean exposed to bAllowMultipleGrips. This value is specifically checked against when going to grip an object with any of the GripObject motioncontroller functions.
If you enable this then the functions will allow you to grip an interfaced object ANY NUMBER of times with no technical limit from multiple controllers (end user must filter authority here).
From a single controller a total of 64 locally authoritative and 64 server authoritative grips are possible at once, but an object can be gripped by any number of total controllers at once.
This setting is useful for fully physical interactions like the physics props in the example map. Or things like collaborative movement of objects or using objects as even throwers or visual hand placements.
Secondary attachments on the other hand are the generalized (what people initially think of when thinking of two handed grips) method of handling two handed interactions. Secondary Attachments are grip modifiers, they are placed onto an already active grip to designate a target scene component as something the grip should be tracking and modifying itself by.
The default setting is to simply rotate the held object to match the difference in movement for the secondary attachment (front hand of a gun for example). However alternate secondary grip modes also exist that allow for scaling the held object or simply throwing events for when one is added.
Since a secondary attachment is a modifier to a grip, generally tracking changes in its relative position to the held object. Its “grip transform” is calculated differently than an actual grip. You have to provide the relative transform of the secondary attachment to the held object.
You can also mix in the standard GetClosestSocketInRange interface function checking for secondary sockets instead.
For secondary attachments generally you want to get the motion controllers PivotTransform in case you ever SetCustomPivot, as the logic for secondary attachments checks the pivot transform if the secondary attachment is a grip controller.
1.7.Offsetting by controller profile
If you are not using the bOffsetByControllerProfile setting in your GripMotionController and are using controller profiles. Then you will need to manually implement the offset yourself by injecting it when the grip transform is calculated.
Up until 4.26 this was part of the template by default, however due to it being confusing for people I removed it in 4.26 and have recorded the method here for future reference. You only need to use this on Socket Grips as they have a hard coded transform compared to free grips which use the current relative position.
Screenshot of how the injection is performed:
The VRGripInterface comes with a lot of options on the back end, however considering that this is a “basic” gripping tutorial I will go over the public facing settings that are on each “Grippable” object type.
You can find the grip settings in the properties window of a grippable actor / component.
The descriptions of the basic settings are below
|Grip Logic Scripts||Grip Logic Scripts are modular grip logic scripts that allow you to just add additional logic on to a grippable object without custom scripting. This is an array of them that you want applied to your object.|
|Deny Gripping||If true then this object cannot be gripped, can be set with SetDenyGripping in blueprints/code.|
|Allow Multiple Grips||If true, the GripObject functions will not deny gripping on an object that is already held.|
|On teleport behavior||The behavior of the object when the controlling motion controller has PostTeleportGrips called on it.|
|Simulate On Drop||Whether the object should automatically enter simulation mode on being dropped (if false the end user has to simulate it if they want).|
|Slot Default Grip Type||The default grip type to use for Slotted (socket) grips. Details about the different grip types are in the Grip Types section.|
|Free Default Grip Type||The default grip type to use for Free (non socketed) grips. Details about the different grip types are in the Grip Types section.|
|Secondary Grip Type||The secondary grip type to use for the object. Details about the secondary grip types are in the secondary grip types section.|
|Movement Replication Type||The type of replication that this object wants to use, this is covered in more detail in the Replication Settings section.|
|Late Update Setting||This controls how the object wants to be late updated. Epics rendering thread is a full frame behind the game thread, so by the time it is presented to be rendered, the players hands can be greatly offset from where they were on the game thread.
To help this visual discontinuity they added a LateUpdate pathway to the engine that moves the render bodies to match the difference between the current hand location and the location it was at in the game thread.
|Constraint Stiffness||If this is a physics based grip, this is the stiffness (rigidity) of the physics constraint that moves the held object.|
|Constraint Damping||If this is a physics based grip, this is the damping (smoothing) of the physics constraint that moves the held object. It prevents rubber banding from trying too hold position too rigidly.|
|Constraint Break Distance||If this is a grip type that can be blocked by geometry (sweep or physics based) then this is how far away from the object (in the objects relative space) that the hand has to be before the grip is automatically dropped. If the value is 0.0 then the grip is never dropped.|
|Primary Slot Range||If using the default socketing logic, this is the distance that slots are considered “hittable” within. IE: If the hand attempting to grip the object is within this range of the socket then it will use that socket for the grip.|
|Secondary Slot Range||The same as the primary slot range, except for the secondary slots.|
|Advanced Grip Settings||Advanced settings for physics / secondary grips and things not needed as often. Advanced physics options are fairly important though as Force based constraints give more realistic behavior for things like melee weapons.|
There are many different Grip Types that are built in to the plugin, here is an overview of them.
|InteractiveCollisionWithPhysics||Uses physics constraints to move the gripped object. It is fully interactible with the environment.|
|InteractiveCollisionWithSweep||Uses the standard engine movement sweep to move the gripped object. It is not physics based, but will still stop on walls and obstacles.|
|InteractiveHybridCollisionWithPhysics||Uses physics constraints like its base version. However when not actually colliding with something the constraint strength is multiplied to make it more rigidly follow the hand.|
|InteractiveHybridCollisionWithSweep||Swaps between physics and position setting based on if it is colliding or not. This lets it follow the hand perfectly 1:1 and snap into place when not colliding, but still have interactability with the environment.|
|SweepWithPhysics||Only sweeps the grip, does not interact or stop on geometry but does throw OnHit events.|
|PhysicsOnly||Does not sweep or trigger OnHit events, just follows the hand.|
|ManipulationGrip||Is a physics based grip without any wrist rotation influence, only follows the position. Useful for things like physics based levers or drawers or interactible items.|
|ManipulationGripWithWristTwist||Same as above except includes the wrist roll|
|AttachmentGrip||Uses the engines native attachment to attach the object to the hand (like most VR gripping is done in engine).|
|CustomGrip||Throws the events for the grip and provides a TickGrip event in blueprints and c++ that an be used to “tick” movement when held. This is the mode that the interactibles use (lever, dial, slider, ect). It is for custom “hand scripted” movements and logic.|
|EventsOnly||This grip type does nothing except throw the OnGrip/OnGripRelease events.|
2.2.Secondary Grip Types
Secondary Grips are “Grip Modifiers” that are added to normal grips, they alter the grip behavior to add Rotation/Scaling based on the position of the Secondary scene component.
Used mostly for things like guns and two handed interactions.
|SG_None||Flags the object as not allowing secondary attachments|
|SG_Free||Allow secondary attachments anywhere|
|SG_SlotOnly||Allow secondary attachments only at Secondary Slot positions.|
|SG_Free_Retain||Same as SG_Free except when the attachment is released, the object will retain the positional offset.|
|SG_FreeWithScaling_Retain||Same as the normal Retain mode, except the object also scales with the hand distance.|
|SG_Custom||Does nothing except throw the OnSecondaryGrip and OnSecondaryGripReleased events. Lets the end user handle logic based on those.|
|SG_ScalingOnly||Does not track the attachment with rotation, only scales the object.|
2.3.Movement Replication Types
Movement replication types change how the plugin handles moving and authority with the grip. Depending on the setting the plugin will expect different user behavior to a point.
|KeepOriginalMovement||Will check the bReplicateMovement setting of the object and retains it as grip movement replication. Rarely (if ever) used.|
|ForceServerSideMovement||Server Authoritative: All grip and drop functions must be called server side.
Forces the objects movement logic to only be called on the servers side,
*Note that the engine currently does not have a native movement replication that is smooth when not using physics, so smooth motion would have to be handled by the object itself*
|ForceClientSideMovement||Server Authoritative: All grip and drop functions must be called server side.
Each client moves the object separately and do not rely on the servers movement replication (this is the default grip type).
|ClientSide_Authoritive||Client Authoritative: All grip and drop functions must be called on the gripping clients end.
Client auth grips are handled locally, they tell the server what they gripped and the server is expected to obey. The massive upside to this grip mode is that there is no intial grip / drop delay while waiting for the server to intialize the grip.
|ClientSide_Authoritive_NoRep||Client Authoritative: All grip and drop functions must be called on the gripping clients end.
This grip isn’t even sent to the serve to let it know what is happening. It is most useful if you want to have default non replicating objects on the body and manually grip / drop it on all clients separately, avoiding all replication requirements. Can be used for “proxy” stand in objects or menus or client side only objects.
The VRGrip Interface provides settings, functions, and events on an object that implements it. These allow you to easily adjust an objects functionality. For example, you can alter an object from being a realistic, functional gun to a magical orb that hovers when dropped with just a few settings.
The plugin comes with prefab components and actors already implemented with the interface and ready to drop in and use (Grippable Actor, Static Mesh Actor, Skeletal Mesh Actor, Sphere Component, ect ect).
However you can manually implement the interface in any object, generally you will need to implement most of the interface functions though so its best to use the prefabs when possible.
Below are the events built into the VRGrip Interface. Some are called automatically (On Grip), while others require user input to setup (like Secondary Grips and On Input) to avoid dictating workflow.
|On Grip||Event triggers on the interfaced object when gripped. (On all connections)|
|On Grip Release||Event triggers on the interfaced object when grip is released. (On all connections)|
|On Secondary Grip||Event triggered on the interfaced object when “Secondary” gripped. (On all connections)
The VRGrip Interface does not force the second grip of an object to be considered “Secondary”. If you want to use any “Secondary” events/functions, you must call Add Secondary Attachment Point on the gripping Grip Motion Controller Component when desired.
Example: See template Vive_PawnCharacter function GripOrDropObject and GunBase.
|On Secondary Grip Release||Event triggered on the interfaced object when a “Secondary” grip is released. (On all connections)|
|On Child Grip||Event triggered on the interfaced object when child components are gripped. (On all connections)
“On Child” means if the actor itself implements the VRGrip Interface, then every Component of that actor will call this event when gripped.
|On Child Grip Release||Event triggered on the interfaced object when child components are released. (On all connections)|
|On Used||Manually call to use an object.
See example template’s GunBase “EventOnUsed” (called from Vive_PawnCharacter InputActionUseHeldObjectLeft or InputActionUseHeldObjectRight > CheckUseHeldItem > OnUsed) for an example of this.
|On End Used||Manually call to stop using an object.|
|On Secondary Used||Manually call to use an object gripped with a Secondary grip.|
|On End Secondary Used||Manually call to stop using an object gripped with a Secondary grip.|
|On Input||Manually call to send an input action to the object.|
|Set Held||Fires true when interfaced object is gripped. Fires false when released.|
|Tick Grip||Event triggered each tick on the interfaced object when gripped, can be used for custom movement or grip-based logic.
Note: “Always Send Tick Grip” on the gripping motion controller must be set to true for this event to fire.
3.2.VR Grip Interface Functions
Below are the various functions available to objects that utilize the VRGrip interface. These are generally used to get information about the current settings on an interfaced object (like is the object allowing multiple grips).
Advanced Grip Settings
Get the advanced grip settings for the target (contains everything located underneath Advanced Grip Settings in the VRGrip Interface settings)
Allows Multiple Grips
Returns if an object allows multiple grips at one time
Closest Grip Slot in Range
Called to get the closest grip socket in range. Requires you to provide proper inputs.
Get Grip Scripts
Returns and array of all Grip Logic Scripts used by the interfaced object.
Get Grip Stiffness and Damping
Gets interfaced objects current Grip Stiffness and Grip Damping values to use if using a physics constraint.
Get Primary Grip Type
Which Default Grip Type is being used (input IsSlot lets you ask for either Slot Default Grip Type or Free Default Grip Type).
See 2.1 Grip Types for more info.
Secondary Grip Type
Same as above, but for the Secondary Grip.
Grip Break Distance
What distance to break a grip at (only relevant with physics enabled grips).
Grip Late Update Setting
Define the late update setting
See 2. Grip Settings – Late Update setting for more info.
Grip Movement Replication Type
Define which movement replication setting to use
Returns if the object is held and if so, which controllers are holding it.
Is Denying Grips
Returns the objects current state of Deny Gripping.
Setup as deny instead of allow so that default allows for gripping.
Returns if the object requests to be socketed to something.
To set this up, override the function Requests Socketing inside of the interfaced object. See GunBase in the template for an example.
Simulate On Drop
Should this object simulate physics on drop
How and interfaced object behaves when teleporting. See 2. Grip Settings – On Teleport Behavior.