Grippaple Motion Controller
05/25/18 Gripping an actor now auto sets the actors owner as the grip controllers owner. This is to allow for some bug fixes and utility. You can reset owner to null on release if you want.
09/13/16 Added two handed grip functions and capability
Because of having to alter a class declared INSIDE of the main controllercomponent class as well as access the rendering thread I found it better to redefine the entire motion controller instead of inherit from the original and override functions, it is a stand alone class and you will NOT be able to cast to "UMotionControllerComponent" from it. I apologize for this as it also means it will have to be manually updated with changes to the UMotionControllerComponent class in engine but having it as a child was really messy and I had to re-write sections of almost all of the functions anyway.
This component uses direct movement of components to handle gripping instead of attachment, this is mostly for plans with multiple hand grips but also to get some features working.
How do I use it?
Replace your current MotionControllerComponents with GripMotionControllerComponents
When you wish to pick up an object call the components "GripObject" function, it will return "true/false" if the grip was successful. (See below for breakdowns of functions)
When you wish to drop an object call the "DropObject" function, it will return true/false if the drop was successful, false means it could not find that object as gripped. (See below for breakdowns of functions)
All actors to be gripped currently MUST have a UPrimitiveComponent derived component as the ROOT component (otherwise it will return false as it doesn't know what to grip or simulate). The root component also is REQUIRED to be movable. If gripping a Child component instead, it must also be attached to an actor set to movable.
When teleporting a character you have to call "PostTeleportMoveGrippedActors" on each GripMotionControllerComponent. This moves all gripped actors to the new location WITHOUT allowing for collision with obstacles in the way and without updating the physics velocity. If you do not do this then the gripped actors will follow WITH collision and may get stuck on obstacles on the way.
For the VRCharacter this is not required as it is done for you.
GripControllerComponent Bindable Delegates
OnGrippedObject - Called when an object has been gripped - utility delegate Called on all connections
OnDroppedObject - Called when an object has been dropped - utility delegate Called on all connections
OnControllerProfileTransformChanged - Called when the current controller profile transform has changed, primarily used to adjust procedural meshes to match the new transform.
OnGripOutOfRange - Called when a grip goes out of its interactible range. If this is not bound then the grip logic will auto drop the object, if this IS bound, then the delegate gets called instead. Called on all connections
bool bUseWithoutTracking - If set gripped actors will still follow the controller even if there is no tracking for the hand it is assigned to. This is mainly used for testing grip functionality without a headset on or VR equipment set up at the time. A first or third person pawn can use it with this set and it parented to the camera and still test gripping and dropping.
AdditionalLateUpdateComponents - An array of components that you want to force to follow late updates for this motion controller. Can be added / removed to/from in blueprint. Useful for things like arms with IK that track the hands but aren't attached to them. If using for arms each arm would need to be a separate mesh as otherwise the two late updates would conflict.
bool bOffsetByHMD - Offsets the controller back by the HMDs relative location, this is used with the SimpleCharacter as the HMD is set to x0,y0 in that character and the controllers relative location would otherwise be incorrect. Now exposed to blueprint In case someone needs it for custom logic or a custom character.
Grip Collision Styles
InteractiveCollisionWithPhysics - Collides with the enviroment and will offset from the hand and not pass through objects, uses physics only.
InteractiveCollisionWithSweep - Collides with the enviroment and will offset from the hand and not pass through objects, does not use physics for collision, will still interact with simulating objects.
InteractiveHybridCollisionWithPhysics (WIP) - Same as InteractiveCollisionWithPhysics, except when not in collision the physics constraint stiffness is multiplied by 10 to better match the hand. When in collision it is softened again to the passed in settings so that it won't jitter or be too springy on colliding surfaces.
InteractiveHybridCollisionWithSweep (incomplete) - Locks to hand during normal use, switches to physics based when it makes contact with a blocking actor.
SweepWithPhysics - Uses non colliding sweeps to move the gripped actor, will pass through the environment but will still trigger OnHit events and interact with simulating objects.
PhysicsOnly - Uses non colliding move commands to position the gripped actor, will pass through the environment but will still interact with simulating objects. Will NOT trigger OnHit events (essentially the same as AttachTo).
AttachmentGrip * - Attachs the object to the controller, doesn't set the location on tick unless it doesn't match the transform it is supposed to be at (secondary grips, ect).
ManipulationGrip - Uses a point of contact rotationally loose constraint to grip the object, this will provide good use cases for doors/drawers/knobs/levers/dials ect that use physics to function.
ManipulationGripWithWristTwist - Uses a point of contact rotationally loose constraint to grip the object, this will provide good use cases for doors/drawers/knobs/levers/dials ect that use physics to function. This one also has Wrist twisting motion enabled.
Grip Replication Styles
enum class EGripMovementReplicationSettings : uint8
Server must call grip for these
KeepOriginalMovement - Keeps whatever replication setting the object already had (Not local compatible)
ForceServerSideMovement - Forces server sided movement, objects likely need their own movement replication setup as the engine default is not lerped and is pretty terrible.
ForceClientSideMovement - Turns off movement replication while gripped, each client grips and moves the object to follow the controller. Server has authority on grip and replicates the settings and grip state.
Each client must call grip for this
Client Authority Client has total authority over this grip, they call Grip/Drop functions and the calls are automatically replicated to the server.
Client Authority_Not_Replicated - Pure local/client side gripping, can be used for objects that other players don't need to see or objects that other players will never directly interact with. IE: Guns that no other player can pick up can use this mode. This way the client will see no delay between attempting to pick up the object and picking it up. You would replicate the grip command to the server, and have it multicast back to all non owning clients to locally grip it as well. For this grip, any grip replication that you want must be manually handled by you.
If only picking up one actor at a time per hand, store them in a BP variable, then when going to pick up a new actor you can cancel if it is already held by that hand or make the other hand drop the actor if it is holding it (calling DropObject on it just to be safe is valid, you do not need to check in blueprint necessarily, but for this case I would have bSimulate turned off).
Use a sphere trace to check for an object from the controller to pickup, with the right radius you can match the diameter of the controller / hand and it has less problems than overlaps for quick movements. Use Persistent drawing on the trace until it looks right and matches where you want the grip location to be. Have it fallback to an overlap on fail to get subcases like when the controller is inside of the object.
Either remove collision from subcomponents to the controller or set "IgnoreMoveActor" on the pawn for a short period when dropping/throwing an item that may collide with the hand on the way out, otherwise the item may be thrown off course. Generally this applies mostly if the momentum is inward towards the hand itself and not outward away from it.
Only pass in mobile actors to the grip function. Setting mobility on an object during runtime is considered unsafe so the controller no longer does it.
Pre-filter in BP the actor types that are allowed to be picked up, the function does not care what you pass to it and will even attempt to pick up terrain actors.
Set some sort of breaking distance where if a gripped actor is too far away that it will get dropped. This can be managed with interaction settings.
Blueprint Function Explanations
ObjectToGrip - The object that you want picked up
WorldOffset - The transform that you want the picked up object to stick to, this is in worldspace and automatically creates the relative offset from the gripping component.
WorldOffsetIsRelative - If true then the worldoffset will be treated as relative already and will not be converted from world space. Can be used for snapping functionality by passing in a Sockets RelativeTransform (actor or component space) or to null out offsets at all by passing in an empty transform. (Transforms with positions of worldspace 0,0,0 would not be 0,0,0 relative to an object without this set to true).
OptionalSnapToSocketName - An optional input that if set to a valid socket name will override the WorldOffset and instead snap the object to the controller using the relative offset of the named socket. Socket must be on the root component if picking up an actor for this to work, it does not currently iterate child components to look for the socket.
GripCollisionType - Type of collision to use for the gripped actor
GripStiffness - Stiffness of the physx link when holding simulating actors
GripDamping - Damping on the movement of held simulating actors
LateUpdateSetting - Different available late update settings for the gripped object.
Returns true/false whether the object was gripped or not.
The default use of this would be to pass in the GetComponent/ActorTransform to the WorldOffset. The function would then get the relative position of the object from the controller and lock the object to it until dropped.
If you wanted snap functionality you would:
Create a mesh socket for the root component of the actor or the component to grip
Name the socket in the grip function
If the socket is not on the root component of an actor when gripping an actor then pass its component or actor space transform in as WorldOffset and set WorldOffsetIsRelative to true.
You can of course mix the two versions by using snap if the grab is within a distance of a snap point.
ObjectToDrop - The Object that you are attempting to drop, if it is not currently held it will just return false
bSimulate - Whether to set the gripped Object to simulate after dropping, if false the object will be frozen in place instead.
OptionalLinearVelocity - An optional node that if filled with a linear velocity will apply it to the dropped actor/component on the server side after it has been dropped. This is so that client side latency doesn't get in the way of throwing.
OptionalAngularVelocity - An optional node that if filled with a linear velocity will apply it to the dropped actor/component on the server side after it has been dropped. This is so that client side latency doesn't get in the way of throwing.
Returns true/false whether the object was found and dropped.
Useful for some interactions like doors, levers, gun bolts, ect. Due to having client side physics simulation gripping a component only will still turn on/off replicating movement for the entire parent actor, in multiplayer situations this may not be ideal and in that case you may want to compose intractable's out of multiple actors instead.
Returns a list of all currently gripped actors (useful for checking against other players or passing to a trace as ignore actors). You can also directly reference the GrippedActors array in Blueprint, this holds all of the grip information instead of just the held actors.
Moves a single actor gripped by this controller component to the new location without changing velocity or allowing interaction with the enviroment. Can be used for "snap back to position" or as an alternative to dropping if stuck on geometry.
Note In the VRCharacterActor this is called automatically when using the TeleportTo function node.
Moves all actors gripped by this controller component to the new location without changing velocity or allowing interaction with the enviroment.
Converts the given WorldSpace transform into being relative to the controller. Can be used in network environments for the remote client to convert a position into relative and send to the server so that it doesn't matter if the servers controller location is slightly out of date when gripping. Would require the grip function to specify the transform as already Relative though.
Converts the given WorldSpace transform into being relative to a gripped actor. Can be used in network environments for the remote client to convert a position into relative and send to the server so that it doesn't matter if the servers controller location is slightly out of date when gripping. This function is intended to be used with the secondary grip by default.
Get Physics Velocities
When passed in an ActorGripInformation struct it will output the Linear and Angular physics velocities of the gripped actor/component. This can be used client side before sending a drop statement to the server and passed to the server. When dropping the grip the server can pass it into the drop function and get dropping behavior closer to what the sending client expected. Otherwise the replication delay / additional inaccuracy may get in the way of the drop being thrown as expected on the client side.
bool GetPhysicsVelocities(const FBPActorGripInformation &Grip, FVector &AngularVelocity, FVector &LinearVelocity)
Returns true/false if it worked and the Angular / Linear velocity of the grip.
Get Is Held
Returns if the given object is held by this controller
Get Is Secondary Attachment
Returns if the given component is currently being used as a secondary attachment for a grip structure
Used to create a secondary grip on a held actor.
GrippedActorToAddAttachment - Actor that is currently held by this controller that you want to add a secondary attachment to.
SecondaryPointComponent - Component to have as the secondary grip point
Original Transform - Transform to use, either pass in a sockets relative transform on the base actor or the secondary point components relative transform to the base actor. Can use ConvertToGripRelativeTransform() node to do this for you.
bTurnOffLateUpdates - Whether to stop late updates when dual gripping, defaults to true, if using something like a gun this will prevent aim jitter.
LerpToTime - Time in seconds to lerp the gripped actor over to the new position
SecondarySmoothingScaler - Scaler 0.1 - 1.0 of how much influence the secondary attachment has from one frame to another (0.1 would add almost a 1 frame delay to movements).
Removes a secondary grip component from a grip.
GrippedActorToRemoveAttachment - Actor that is currently held by this controller that you want to remove the secondary attachment from.
LerpToTime - Time in seconds to lerp the two handed position back to the single handed position.
Returns a grip structure from the specified motion controller if it finds one with the given actor.
Returns a grip structure from the specified motion controller if it finds one with the given component.
(FORCES REPLICATION) Set the late update settings for a grip struct from the specified motion controller.
(FORCES REPLICATION) Set the relative transform for a grip struct from the specified motion controller.
(CLIENT SIDE) Set the AdditionTransform for a grip struct from the specified motion controller.
Networked / Multiplayer Setup
You shouldn't have to do anything different except to call the grip / drop functions on the server and make sure to handle what happens if two players try to grip the same actor.