initial upload

This commit is contained in:
tom.hempel
2025-09-21 22:42:26 +02:00
commit d03bcd4ba5
6231 changed files with 351582 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 1a221864c2d4141cfab6c79846565174
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 15dd3a3d1606b41ab8169e92e914a946
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: b85ace1101bfa4110b70af1c694c1745
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 0c1f1d575fa3e405d8214cda9f3e60ae
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: eb8a7e9cae153408d8242a655f13e2ac
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 56ab62ebe17334ae19eab6a1dd946afc
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6b33fdae1f2dc4c7c875a10e438ea419
timeCreated: 1461307985
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 133e8d48ba7914813954c2a5b5d4e8bd
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d67a9d6cb5f28114d8a006db1dd1860c
timeCreated: 1456919434
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: f971715c34ef7496f930e939d65b2634
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 8777d34ec9e694f2c98e65d98922b023
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 45f325b7eef7b4c3dbb04f3c83398709
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 8064853db60c34f9d93fc3372647e7e9
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: cbedbd696f39a46c9bd4bd5d94c55c4a
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: c8c08a518832d484b8d8de7804c18a97
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 583d03f8c5b5e4b30b67347ab343a511
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 57465d524287546bba2116eb2e39ad01
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 6aa7e7dc669224b66abda13816fd66e8
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: d0377e8a32b944328bf2c301c8265a73
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 92d2f08133f9e43e3baf1be5c19efa98
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: c2e2ef913e2ad4a7ba3fdbcee7344b0b
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 0ef9773715d234a1c826469b246b2209
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 888b8015bdd29f540b676314d9b6576e
timeCreated: 1524812697
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: 87d20d0d91268468bb51673c2d887ee2
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,6 @@
fileFormatVersion: 2
guid: d48f3468e9a994c9ea6cee5166cf194d
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 9e79e75b388784e188c2a3b50d014ce1
folderAsset: yes
timeCreated: 1434626795
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,128 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Warping an effector from animation space to world space.
/// The weight curve of the warp is used to add the offset from "Warp From" to "Warp To" to the effector.
/// "Warp From" should be a Transform parented to the root of the character, hence in animation space (virtual position where a soccer player's foot hits the ball in the animation).
/// "Warp To" should be a Transform in the world space (the actual ball).
/// </summary>
public class AnimationWarping : OffsetModifier {
/// <summary>
/// Definition of a warp from 'warpFrom' to 'warpTo' by normalized time of the animation
/// </summary>
[System.Serializable]
public struct Warp {
[Tooltip("Layer of the 'Animation State' in the Animator.")]
public int animationLayer;
[Tooltip("Name of the state in the Animator to warp.")]
public string animationState;
[Tooltip("Warping weight by normalized time of the animation state.")]
public AnimationCurve weightCurve;
[Tooltip("Animated point to warp from. This should be in character space so keep this Transform parented to the root of the character.")]
public Transform warpFrom;
[Tooltip("World space point to warp to.")]
public Transform warpTo;
[Tooltip("Which FBBIK effector to use?")]
public FullBodyBipedEffector effector;
}
/// <summary>
/// Using effector.positionOffset or effector.position with effector.positionWeight?
/// </summary>
[System.Serializable]
public enum EffectorMode {
PositionOffset,
Position,
}
[Tooltip("Reference to the Animator component to use")]
public Animator animator;
[Tooltip("Using effector.positionOffset or effector.position with effector.positionWeight? " +
"The former will enable you to use effector.position for other things, the latter will weigh in the effectors, hence using Reach and Pull in the process.")]
public EffectorMode effectorMode;
[Space(10)]
[Tooltip("The array of warps, can have multiple simultaneous warps.")]
public Warp[] warps;
private EffectorMode lastMode;
protected override void Start() {
base.Start();
lastMode = effectorMode;
}
/// <summary>
/// Gets the current warping weight of the warp at the specified index.
/// </summary>
public float GetWarpWeight(int warpIndex) {
if (warpIndex < 0) {
Debug.LogError("Warp index out of range.");
return 0f;
}
if (warpIndex >= warps.Length) {
Debug.LogError("Warp index out of range.");
return 0f;
}
if (animator == null) {
Debug.LogError("Animator unassigned in AnimationWarping");
return 0f;
}
// Get the animator state info
AnimatorStateInfo info = animator.GetCurrentAnimatorStateInfo(warps[warpIndex].animationLayer);
// If not currently playing the animation state of the warp, return
if (!info.IsName(warps[warpIndex].animationState)) return 0f;
// Evaluate the weight of the warp by the current normalized time of the state
return warps[warpIndex].weightCurve.Evaluate(info.normalizedTime - (int)info.normalizedTime);
}
// Called each time before FBBIK solves
protected override void OnModifyOffset() {
// Go through all the warps...
for (int i = 0; i < warps.Length; i++) {
float warpWeight = GetWarpWeight(i);
// Get the offset form warpFrom to warpTo
Vector3 offset = warps[i].warpTo.position - warps[i].warpFrom.position;
// Add that offset to the effector (using positionOffset additively, because it will be reset to Vector3.zero by FBBIK after each update)
switch(effectorMode) {
case EffectorMode.PositionOffset:
ik.solver.GetEffector(warps[i].effector).positionOffset += offset * warpWeight * weight;
break;
case EffectorMode.Position:
ik.solver.GetEffector(warps[i].effector).position = ik.solver.GetEffector(warps[i].effector).bone.position + offset;
ik.solver.GetEffector(warps[i].effector).positionWeight = weight * warpWeight;
break;
}
}
// Switching modes safely, weighing out effector positionWeights
if (lastMode == EffectorMode.Position && effectorMode == EffectorMode.PositionOffset) {
foreach (Warp warp in warps) {
ik.solver.GetEffector(warp.effector).positionWeight = 0f;
}
}
lastMode = effectorMode;
}
// Set effector positionWeights to 0 if in "Position" effector mode
void OnDisable() {
if (effectorMode != EffectorMode.Position) return;
foreach (Warp warp in warps) {
ik.solver.GetEffector(warp.effector).positionWeight = 0f;
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 07341efad24fc425b88a57536e3a568c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Basic Mecanim Animator controller for 3rd person view.
/// </summary>
public class AnimatorController3rdPerson : MonoBehaviour {
public float rotateSpeed = 7f; // Speed of rotating the character
public float blendSpeed = 10f; // Animation blending speed
public float maxAngle = 90f; // Max angular offset from camera direction
public float moveSpeed = 1.5f; // The speed of moving the character with no root motion
public float rootMotionWeight; // Crossfading between procedural movement and root motion
protected Animator animator; // The Animator
protected Vector3 moveBlend, moveInput, velocity;
protected virtual void Start() {
animator = GetComponent<Animator>();
}
// Moving the character
void OnAnimatorMove() {
velocity = Vector3.Lerp (velocity, transform.rotation * Vector3.ClampMagnitude(moveInput, 1f) * moveSpeed, Time.deltaTime * blendSpeed);
// Crossfading between procedural movement and root motion.
transform.position += Vector3.Lerp(velocity * Time.deltaTime, animator.deltaPosition, rootMotionWeight);
}
// Move the character
public virtual void Move(Vector3 moveInput, bool isMoving, Vector3 faceDirection, Vector3 aimTarget) {
// Store variables that we need in other methods
this.moveInput = moveInput;
// Get the facing direction relative to the character rotation
Vector3 faceDirectionLocal = transform.InverseTransformDirection(faceDirection);
// Get the angle between the facing direction and character forward
float angle = Mathf.Atan2(faceDirectionLocal.x, faceDirectionLocal.z) * Mathf.Rad2Deg;
// Find the rotation
float rotation = angle * Time.deltaTime * rotateSpeed;
// Clamp the rotation to maxAngle
if (angle > maxAngle) rotation = Mathf.Clamp(rotation, angle - maxAngle, rotation);
if (angle < -maxAngle) rotation = Mathf.Clamp(rotation, rotation, angle + maxAngle);
// Rotate the character
transform.Rotate(Vector3.up, rotation);
// Locomotion animation blending
moveBlend = Vector3.Lerp(moveBlend, moveInput, Time.deltaTime * blendSpeed);
// Set Animator parameters
animator.SetFloat("X", moveBlend.x);
animator.SetFloat("Z", moveBlend.z);
animator.SetBool("IsMoving", isMoving);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 898bc8d3a77f74e0b8cd466413a4b20d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,122 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
// Extends the default Animator controller for 3rd person view to add IK
public class AnimatorController3rdPersonIK: AnimatorController3rdPerson {
[Range(0f, 1f)] public float headLookWeight = 1f;
public Vector3 gunHoldOffset;
public Vector3 leftHandOffset;
public Recoil recoil;
// The IK components
private AimIK aim;
private FullBodyBipedIK ik;
private Vector3 headLookAxis;
private Vector3 leftHandPosRelToRightHand;
private Quaternion leftHandRotRelToRightHand;
private Vector3 aimTarget;
private Quaternion rightHandRotation;
protected override void Start() {
base.Start();
// Find the IK components
aim = GetComponent<AimIK>();
ik = GetComponent<FullBodyBipedIK>();
ik.solver.OnPreRead += OnPreRead;
// Disable the IK components to manage their updating
aim.enabled = false;
ik.enabled = false;
// Presuming head is rotated towards character forward at Start
headLookAxis = ik.references.head.InverseTransformVector(ik.references.root.forward);
// Enable the upper-body aiming pose
animator.SetLayerWeight(1, 1f);
}
public override void Move(Vector3 moveInput, bool isMoving, Vector3 faceDirection, Vector3 aimTarget) {
base.Move(moveInput, isMoving, faceDirection, aimTarget);
// Snatch the aim target from the Move call, it will be used by AimIK (Move is called by CharacterController3rdPerson that controls the actual motion of the character)
this.aimTarget = aimTarget;
// IK procedures, make sure this updates AFTER the camera is moved/rotated
// Sample something from the current pose of the character
Read();
// AimIK pass
AimIK();
// FBBIK pass - put the left hand back to where it was relative to the right hand before AimIK solved
FBBIK();
// AimIK pass
AimIK();
// Rotate the head to look at the aim target
HeadLookAt(aimTarget);
}
private void Read() {
// Remember the position and rotation of the left hand relative to the right hand
leftHandPosRelToRightHand = ik.references.rightHand.InverseTransformPoint(ik.references.leftHand.position);
leftHandRotRelToRightHand = Quaternion.Inverse(ik.references.rightHand.rotation) * ik.references.leftHand.rotation;
}
private void AimIK() {
// Set AimIK target position and update
aim.solver.IKPosition = aimTarget;
aim.solver.Update(); // Update AimIK
}
// Positioning the left hand on the gun after aiming has finished
private void FBBIK() {
// Store the current rotation of the right hand
rightHandRotation = ik.references.rightHand.rotation;
// Offsetting hands, you might need that to support multiple weapons with the same aiming pose
Vector3 rightHandOffset = ik.references.rightHand.rotation * gunHoldOffset;
ik.solver.rightHandEffector.positionOffset += rightHandOffset;
if (recoil != null) recoil.SetHandRotations(rightHandRotation * leftHandRotRelToRightHand, rightHandRotation);
// Update FBBIK
ik.solver.Update();
// Rotating the hand bones after IK has finished
if (recoil != null) {
ik.references.rightHand.rotation = recoil.rotationOffset * rightHandRotation;
ik.references.leftHand.rotation = recoil.rotationOffset * rightHandRotation * leftHandRotRelToRightHand;
} else {
ik.references.rightHand.rotation = rightHandRotation;
ik.references.leftHand.rotation = rightHandRotation * leftHandRotRelToRightHand;
}
}
// Final calculations before FBBIK solves. Recoil has already solved by, so we can use its calculated offsets.
// Here we set the left hand position relative to the position and rotation of the right hand.
private void OnPreRead() {
Quaternion r = recoil != null? recoil.rotationOffset * rightHandRotation: rightHandRotation;
Vector3 leftHandTarget = ik.references.rightHand.position + ik.solver.rightHandEffector.positionOffset + r * leftHandPosRelToRightHand;
ik.solver.leftHandEffector.positionOffset += leftHandTarget - ik.references.leftHand.position - ik.solver.leftHandEffector.positionOffset + r * leftHandOffset;
}
// Rotating the head to look at the target
private void HeadLookAt(Vector3 lookAtTarget) {
Quaternion headRotationTarget = Quaternion.FromToRotation(ik.references.head.rotation * headLookAxis, lookAtTarget - ik.references.head.position);
ik.references.head.rotation = Quaternion.Lerp(Quaternion.identity, headRotationTarget, headLookWeight) * ik.references.head.rotation;
}
// Cleaning up the delegates
void OnDestroy() {
if (ik != null) ik.solver.OnPreRead -= OnPreRead;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 7cacab300efda4d8c8c72660683917b8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Contols animation for a third person person controller.
/// </summary>
public class CharacterAnimationThirdPersonIK: CharacterAnimationThirdPerson {
private FullBodyBipedIK ik;
protected override void Start() {
base.Start();
ik = GetComponent<FullBodyBipedIK>();
}
protected override void LateUpdate() {
base.LateUpdate();
// Rotate the upper body a little bit to world up vector if the character is rotated (for wall-running)
if (Vector3.Angle(transform.up, Vector3.up) <= 0.01f) return;
Quaternion r = Quaternion.FromToRotation(transform.up, Vector3.up);
RotateEffector(ik.solver.bodyEffector, r, 0.1f);
RotateEffector(ik.solver.leftShoulderEffector, r, 0.2f);
RotateEffector(ik.solver.rightShoulderEffector, r, 0.2f);
RotateEffector(ik.solver.leftHandEffector, r, 0.1f);
RotateEffector(ik.solver.rightHandEffector, r, 0.1f);
}
// Rotate an effector from the root of the character
private void RotateEffector(IKEffector effector, Quaternion rotation, float mlp) {
Vector3 d1 = effector.bone.position - transform.position;
Vector3 d2 = rotation * d1;
Vector3 offset = d2 - d1;
effector.positionOffset += offset * mlp;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: da28f9c80db074b10842df00a4b50b12
timeCreated: 1435755417
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using UnityEngine;
using System.Collections;
namespace RootMotion.Demos {
/// <summary>
/// Basic Mecanim character controller for 3rd person view.
/// </summary>
public class CharacterController3rdPerson: MonoBehaviour {
public CameraController cam; // The camera
private AnimatorController3rdPerson animatorController; // The Animator controller
void Start() {
animatorController = GetComponent<AnimatorController3rdPerson>();
cam.enabled = false;
}
void LateUpdate() {
// Update the camera first so we always have its final translation in the frame
cam.UpdateInput();
cam.UpdateTransform();
// Read the input
Vector3 input = inputVector;
// Should the character be moving?
// inputVectorRaw is required here for not starting a transition to idle on that one frame where inputVector is Vector3.zero when reversing directions.
bool isMoving = inputVector != Vector3.zero || inputVectorRaw != Vector3.zero;
// Character look at vector.
Vector3 lookDirection = cam.transform.forward;
// Aiming target
Vector3 aimTarget = cam.transform.position + (lookDirection * 10f);
// Move the character.
animatorController.Move(input, isMoving, lookDirection, aimTarget);
}
// Convert the input axis to a vector
private static Vector3 inputVector {
get {
return new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
}
}
// Convert the raw input axis to a vector
private static Vector3 inputVectorRaw {
get {
return new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical"));
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 2e2dfae99419b433289fb54f4c763435
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Demo for offsetting Effectors.
/// </summary>
public class EffectorOffset : OffsetModifier {
// If 1, The hand effectors will maintain their position relative to their parent triangle's rotation {root node, left shoulder, right shoulder}
[Range(0f, 1f)]
public float handsMaintainRelativePositionWeight;
// The offset vectors for each effector
public Vector3 bodyOffset, leftShoulderOffset, rightShoulderOffset, leftThighOffset, rightThighOffset, leftHandOffset, rightHandOffset, leftFootOffset, rightFootOffset;
protected override void OnModifyOffset() {
// How much will the hand effectors maintain their position relative to their parent triangle's rotation {root node, left shoulder, right shoulder} ?
ik.solver.leftHandEffector.maintainRelativePositionWeight = handsMaintainRelativePositionWeight;
ik.solver.rightHandEffector.maintainRelativePositionWeight = handsMaintainRelativePositionWeight;
// Apply position offsets relative to this GameObject's rotation.
ik.solver.bodyEffector.positionOffset += transform.rotation * bodyOffset * weight;
ik.solver.leftShoulderEffector.positionOffset += transform.rotation * leftShoulderOffset * weight;
ik.solver.rightShoulderEffector.positionOffset += transform.rotation * rightShoulderOffset * weight;
ik.solver.leftThighEffector.positionOffset += transform.rotation * leftThighOffset * weight;
ik.solver.rightThighEffector.positionOffset += transform.rotation * rightThighOffset * weight;
ik.solver.leftHandEffector.positionOffset += transform.rotation * leftHandOffset * weight;
ik.solver.rightHandEffector.positionOffset += transform.rotation * rightHandOffset * weight;
ik.solver.leftFootEffector.positionOffset += transform.rotation * leftFootOffset * weight;
ik.solver.rightFootEffector.positionOffset += transform.rotation * rightFootOffset * weight;
// NB! effector position offsets are reset to Vector3.zero after FBBIK update is complete.
// This enables to have more than one script modifying the position offset of effectors.
// Therefore instead of writing effector.positionOffset = value, write effector.positionOffset += value instead.
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c96ec8ee850b44337a19453c26b170ed
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,76 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Demo of exploding a viking using FBBIK
/// </summary>
public class ExplosionDemo : MonoBehaviour {
public SimpleLocomotion character; // Reference to the SimpleLocomotion component
public float forceMlp = 1f; // Explosion force
public float upForce = 1f; // Explosion up forve
public float weightFalloffSpeed = 1f; // The speed of explosion falloff
public AnimationCurve weightFalloff; // Explosion weight falloff
public AnimationCurve explosionForceByDistance; // The force of the explosion relative to character distance to the bomb
public AnimationCurve scale; // Scaling the bomb GameObject with the explosion
private float weight = 0f;
private Vector3 defaultScale = Vector3.one;
private Rigidbody r;
private FullBodyBipedIK ik;
void Start() {
// Storing the default scale of the bomb
defaultScale = transform.localScale;
r = character.GetComponent<Rigidbody>();
ik = character.GetComponent<FullBodyBipedIK>();
}
// Update is called once per frame
void Update () {
weight = Mathf.Clamp(weight - Time.deltaTime * weightFalloffSpeed, 0f, 1f);
// Exploding the bomb
if (Input.GetKeyDown(KeyCode.E) && character.isGrounded) {
// Set FBBIK weight to 1
ik.solver.IKPositionWeight = 1f;
// Set limb effector positions to where they are at the momemt
ik.solver.leftHandEffector.position = ik.solver.leftHandEffector.bone.position;
ik.solver.rightHandEffector.position = ik.solver.rightHandEffector.bone.position;
ik.solver.leftFootEffector.position = ik.solver.leftFootEffector.bone.position;
ik.solver.rightFootEffector.position = ik.solver.rightFootEffector.bone.position;
weight = 1f;
// Add explosion force to the character rigidbody
Vector3 direction = r.position - transform.position;
direction.y = 0f;
float explosionForce = explosionForceByDistance.Evaluate(direction.magnitude);
r.velocity = (direction.normalized + (Vector3.up * upForce)) * explosionForce * forceMlp;
}
if (weight < 0.5f && character.isGrounded) {
weight = Mathf.Clamp(weight - Time.deltaTime * 3f, 0f, 1f);
}
// Set effector weights
SetEffectorWeights(weightFalloff.Evaluate(weight));
// Set bomb scale
transform.localScale = scale.Evaluate(weight) * defaultScale;
}
// Set FBBIK limb end-effector weights to value
private void SetEffectorWeights(float w) {
ik.solver.leftHandEffector.positionWeight = w;
ik.solver.rightHandEffector.positionWeight = w;
ik.solver.leftFootEffector.positionWeight = w;
ik.solver.rightFootEffector.positionWeight = w;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 5ee6c787adaa94deaa3c39e20323f559
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,60 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Manages FBBIK settings that are not visible in the FBBIK custom inspector.
/// </summary>
public class FBBIKSettings : MonoBehaviour {
/// <summary>
/// Settings for a limb
/// </summary>
[System.Serializable]
public class Limb {
public FBIKChain.Smoothing reachSmoothing; // Smoothing of the Reach effect (since 0.2)
public float maintainRelativePositionWeight; // Weight of maintaining the limb's position relative to the body part that it is attached to (since 0.2, used to be IKEffector.Mode.MaintainRelativePosition)
public float mappingWeight = 1f;
// Apply the settings
public void Apply(FullBodyBipedChain chain, IKSolverFullBodyBiped solver) {
solver.GetChain(chain).reachSmoothing = reachSmoothing;
solver.GetEndEffector(chain).maintainRelativePositionWeight = maintainRelativePositionWeight;
solver.GetLimbMapping(chain).weight = mappingWeight;
}
}
public FullBodyBipedIK ik; // Reference to the FBBIK component
public bool disableAfterStart; // If true, will not update after Start
public Limb leftArm, rightArm, leftLeg, rightLeg; // The Limbs
public float rootPin = 0f; // Weight of pinning the root node to its animated position
public bool bodyEffectChildNodes = true; // If true, the body effector will also drag the thigh effectors
// Apply all the settings to the FBBIK solver
public void UpdateSettings() {
if (ik == null) return;
leftArm.Apply(FullBodyBipedChain.LeftArm, ik.solver);
rightArm.Apply(FullBodyBipedChain.RightArm, ik.solver);
leftLeg.Apply(FullBodyBipedChain.LeftLeg, ik.solver);
rightLeg.Apply(FullBodyBipedChain.RightLeg, ik.solver);
ik.solver.chain[0].pin = rootPin;
ik.solver.bodyEffector.effectChildNodes = bodyEffectChildNodes;
}
void Start() {
Debug.Log("FBBIKSettings is deprecated, you can now edit all the settings from the custom inspector of the FullBodyBipedIK component.");
UpdateSettings();
if (disableAfterStart) this.enabled = false;
}
void Update() {
UpdateSettings();
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ed9ee7f6cba9940a183a1b13f38f3c67
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,30 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Bend goal for FullBodyBipedIK.
/// </summary>
public class FBIKBendGoal: MonoBehaviour {
public FullBodyBipedIK ik; // Refernce to the FBBIK component
public FullBodyBipedChain chain; // Which limb is this bend goal for?
public float weight; // Bend goal weight
void Start() {
Debug.Log("FBIKBendGoal is deprecated, you can now a bend goal from the custom inspector of the FullBodyBipedIK component.");
}
void Update() {
if (ik == null) return;
ik.solver.GetBendConstraint(chain).bendGoal = transform;
ik.solver.GetBendConstraint(chain).weight = weight;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 1a95ef1aaef9e41ef94e5cf5cb1a2491
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Demo script for boxing with the combination of FullBodyBipedIK and Aim IK.
/// </summary>
public class FBIKBoxing : MonoBehaviour {
[Tooltip("The target we want to hit")]
public Transform target;
[Tooltip("The pin Transform is used to reference the exact hit point in the animation (used by AimIK to aim the upper body to follow the target)." +
"In Legacy and Generic modes you can just create and position a reference point in your animating software and include it in the FBX. " +
"Then in Unity if you added a GameObject with the exact same name under the character's root, it would be animated to the required position." +
"In Humanoid mode however, Mecanim loses track of any Transform that does not belong to the avatar, so in this case the pin point has to be manually set inside the Unity Editor.")]
public Transform pin;
[Tooltip("The Full Body Biped IK component")]
public FullBodyBipedIK ik;
[Tooltip("The Aim IK component. Aim IK is ust used for following the target slightly with the body.")]
public AimIK aim;
[Tooltip("The master weight")]
public float weight;
[Tooltip("The effector type of the punching hand")]
public FullBodyBipedEffector effector;
[Tooltip("Weight of aiming the body to follow the target")]
public AnimationCurve aimWeight;
private Animator animator;
void Start() {
animator = GetComponent<Animator>();
}
void LateUpdate() {
// Getting the weight of pinning the fist to the target
float hitWeight = animator.GetFloat("HitWeight");
// Pinning the first with FBIK
ik.solver.GetEffector(effector).position = target.position;
ik.solver.GetEffector(effector).positionWeight = hitWeight * weight;
// Aiming the body with AimIK to follow the target
if (aim != null) {
// Make the aim transform always look at the pin. This will normalize the default aim diretion to the animated pose.
aim.solver.transform.LookAt(pin.position);
// Set aim target
aim.solver.IKPosition = target.position;
// Setting aim weight
aim.solver.IKPositionWeight = aimWeight.Evaluate(hitWeight) * weight;
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d2b4d13a962bb4d0e9a622c7f12937d7
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,70 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Maintains FBBIK hands on a 2-handed prop, regardless of position offset of the hand effectors
/// </summary>
public class FBIKHandsOnProp: MonoBehaviour {
public FullBodyBipedIK ik; // Reference to the FBBIK component
public bool leftHanded;
void Awake() {
// Add to OnPreUpdate delegate to get a call before the solver starts updating
ik.solver.OnPreRead += OnPreRead;
}
private void OnPreRead() {
if (leftHanded) HandsOnProp(ik.solver.leftHandEffector, ik.solver.rightHandEffector);
else HandsOnProp(ik.solver.rightHandEffector, ik.solver.leftHandEffector);
}
private void HandsOnProp(IKEffector mainHand, IKEffector otherHand) {
// Get the animated direction from the main hand to the other hand
Vector3 toOtherHand = otherHand.bone.position - mainHand.bone.position;
// Get the hand direction relative to the main hand's rotation
Vector3 otherHandRelativeDirection = Quaternion.Inverse(mainHand.bone.rotation) * toOtherHand;
// Get the center point of two hands
Vector3 handsCenter = mainHand.bone.position + (toOtherHand * 0.5f);
// Get the other hand's rotation relative to the main hand's rotation
Quaternion otherHandRelativeRotation = Quaternion.Inverse(mainHand.bone.rotation) * otherHand.bone.rotation;
// Get the direction from the main hand to the other hand that icludes effector position offsets
Vector3 toOtherHandWithOffset = (otherHand.bone.position + otherHand.positionOffset) - (mainHand.bone.position + mainHand.positionOffset);
// Get the center point of two hands that includes effector position offsets
Vector3 handsCenterWithOffset = (mainHand.bone.position + mainHand.positionOffset) + (toOtherHand * 0.5f);
// Main hand position
mainHand.position = (mainHand.bone.position + mainHand.positionOffset) + (handsCenterWithOffset - handsCenter);
mainHand.positionWeight = 1f;
// Main hand rotation
Quaternion rotationOffset = Quaternion.FromToRotation(toOtherHand, toOtherHandWithOffset);
mainHand.bone.rotation = rotationOffset * mainHand.bone.rotation;
// Other hand position
otherHand.position = mainHand.position + mainHand.bone.rotation * otherHandRelativeDirection;
otherHand.positionWeight = 1f;
// Other hand rotation
otherHand.bone.rotation = mainHand.bone.rotation * otherHandRelativeRotation;
ik.solver.leftArmMapping.maintainRotationWeight = 1f;
ik.solver.rightArmMapping.maintainRotationWeight = 1f;
}
// Clean up delegates
void OnDestroy() {
if (ik != null) {
ik.solver.OnPreRead -= OnPreRead;
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: ec5a53b0c81f7774aabb2eec99071ea3
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,172 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Basic full body FPS IK controller.
///
/// If aimWeight is weighed in, the character will simply use AimIK to aim his gun towards the camera forward direction.
/// If sightWeight is weighed in, the character will also use FBBIK to pose the gun to a predefined position relative to the camera so it stays fixed in view.
/// That position was simply defined by making a copy of the gun (gunTarget), parenting it to the camera and positioning it so that the camera would look down its sights.
/// </summary>
public class FPSAiming : MonoBehaviour {
[Range(0f, 1f)] public float aimWeight = 1f; // The weight of aiming the gun towards camera forward
[Range(0f, 1f)] public float sightWeight = 1f; // the weight of aiming down the sight (multiplied by aimWeight)
[Range(0f, 180f)] public float maxAngle = 80f; // The maximum angular offset of the aiming direction from the character forward. Character will be rotated to comply.
public Vector3 aimOffset; // Can be used to adjust the aiming angle
public bool animatePhysics; // Is Animate Physiscs turned on for the character?
public Transform gun; // The gun that the character is holding
public Transform gunTarget; // The copy of the gun that has been parented to the camera
public FullBodyBipedIK ik; // Reference to the FBBIK component
public AimIK gunAim; // Reference to the AimIK component
public AimIK headAim; // AimIK solver used for look-at when aimWeight < 1
public CameraControllerFPS cam; // Reference to the FPS camera
public Recoil recoil; // The recoil component (optional)
[Range(0f, 1f)] public float cameraRecoilWeight = 0.5f; // How much of the recoil motion is added to the camera?
private Vector3 gunTargetDefaultLocalPosition;
private Vector3 gunTargetDefaultLocalRotation;
private Vector3 camDefaultLocalPosition;
private Vector3 camRelativeToGunTarget;
private bool updateFrame;
void Start() {
// Remember some default local positions
gunTargetDefaultLocalPosition = gunTarget.localPosition;
gunTargetDefaultLocalRotation = gunTarget.localEulerAngles;
camDefaultLocalPosition = cam.transform.localPosition;
// Disable the camera and IK components so we can handle their execution order
cam.enabled = false;
gunAim.enabled = false;
if (headAim != null) headAim.enabled = false;
ik.enabled = false;
if (recoil != null && ik.solver.iterations == 0) Debug.LogWarning("FPSAiming with Recoil needs FBBIK solver iteration count to be at least 1 to maintain accuracy.");
}
void FixedUpdate() {
// Making sure this works with Animate Physics
updateFrame = true;
}
void LateUpdate() {
// Making sure this works with Animate Physics
if (!animatePhysics) updateFrame = true;
if (!updateFrame) return;
updateFrame = false;
// Put the camera back to its default local position relative to the head
cam.transform.localPosition = camDefaultLocalPosition;
// Remember the camera's position relative to the gun target
camRelativeToGunTarget = gunTarget.InverseTransformPoint(cam.transform.position);
// Update the camera
cam.LateUpdate();
// Rotating the root of the character if it is past maxAngle from the camera forward
RotateCharacter();
Aiming();
LookDownTheSight();
}
private void Aiming()
{
if (aimWeight <= 0f) return;
// Remember the rotation of the camera because we need to reset it later so the IK would not interfere with the rotating of the camera
Quaternion camRotation = cam.transform.rotation;
if (headAim != null)
{
// Aim head towards camera forward
headAim.solver.IKPosition = cam.transform.position + cam.transform.forward * 10f;
headAim.solver.IKPositionWeight = 1f - aimWeight;
headAim.solver.Update();
}
// Aim the gun towards camera forward
gunAim.solver.IKPosition = cam.transform.position + cam.transform.forward * 10f + cam.transform.rotation * aimOffset;
gunAim.solver.IKPositionWeight = aimWeight;
gunAim.solver.Update();
cam.transform.rotation = camRotation;
}
private void LookDownTheSight() {
float sW = aimWeight * sightWeight;
//if (sW <= 0f && recoil == null) return;
// Interpolate the gunTarget from the current animated position of the gun to the position fixed to the camera
gunTarget.position = Vector3.Lerp(gun.position, gunTarget.parent.TransformPoint(gunTargetDefaultLocalPosition), sW);
gunTarget.rotation = Quaternion.Lerp(gun.rotation, gunTarget.parent.rotation * Quaternion.Euler(gunTargetDefaultLocalRotation), sW);
// Get the current positions of the hands relative to the gun
Vector3 leftHandRelativePosition = gun.InverseTransformPoint(ik.solver.leftHandEffector.bone.position);
Vector3 rightHandRelativePosition = gun.InverseTransformPoint(ik.solver.rightHandEffector.bone.position);
// Get the current rotations of the hands relative to the gun
Quaternion leftHandRelativeRotation = Quaternion.Inverse(gun.rotation) * ik.solver.leftHandEffector.bone.rotation;
Quaternion rightHandRelativeRotation = Quaternion.Inverse(gun.rotation) * ik.solver.rightHandEffector.bone.rotation;
//float handWeight = aimWeight > 0 && sightWeight > 0? aimWeight * sightWeight: 0f;
float handWeight = 1f;//aimWeight * sightWeight;
ik.solver.leftHandEffector.positionOffset += (gunTarget.TransformPoint(leftHandRelativePosition) - (ik.solver.leftHandEffector.bone.position + ik.solver.leftHandEffector.positionOffset)) * handWeight;
ik.solver.rightHandEffector.positionOffset += (gunTarget.TransformPoint(rightHandRelativePosition) - (ik.solver.rightHandEffector.bone.position + ik.solver.rightHandEffector.positionOffset)) * handWeight;
// Make sure the head does not rotate
ik.solver.headMapping.maintainRotationWeight = 1f;
if (recoil != null) recoil.SetHandRotations(gunTarget.rotation * leftHandRelativeRotation, gunTarget.rotation * rightHandRelativeRotation);
// Update FBBIK
ik.solver.Update();
// Rotate the hand bones relative to the gun target the same way they are rotated relative to the gun
if (recoil != null) {
ik.references.leftHand.rotation = recoil.rotationOffset * (gunTarget.rotation * leftHandRelativeRotation);
ik.references.rightHand.rotation = recoil.rotationOffset * (gunTarget.rotation * rightHandRelativeRotation);
} else {
ik.references.leftHand.rotation = gunTarget.rotation * leftHandRelativeRotation;
ik.references.rightHand.rotation = gunTarget.rotation * rightHandRelativeRotation;
}
// Position the camera to where it was before FBBIK relative to the gun
cam.transform.position = Vector3.Lerp(cam.transform.position, Vector3.Lerp(gunTarget.TransformPoint(camRelativeToGunTarget), gun.transform.TransformPoint(camRelativeToGunTarget), cameraRecoilWeight), sW);
}
// Rotating the root of the character if it is past maxAngle from the camera forward
private void RotateCharacter() {
if (maxAngle >= 180f) return;
// If no angular difference is allowed, just rotate the character to the flattened camera forward
if (maxAngle <= 0f) {
transform.rotation = Quaternion.LookRotation(new Vector3(cam.transform.forward.x, 0f, cam.transform.forward.z));
return;
}
// Get camera forward in the character's rotation space
Vector3 camRelative = transform.InverseTransformDirection(cam.transform.forward);
// Get the angle of the camera forward relative to the character forward
float angle = Mathf.Atan2(camRelative.x, camRelative.z) * Mathf.Rad2Deg;
// Making sure the angle does not exceed maxangle
if (Mathf.Abs(angle) > Mathf.Abs(maxAngle)) {
float a = angle - maxAngle;
if (angle < 0f) a = angle + maxAngle;
transform.rotation = Quaternion.AngleAxis(a, transform.up) * transform.rotation;
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 343587580f6674d5e9346929324ec997
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEngine;
using System.Collections;
namespace RootMotion.Demos {
/// <summary>
/// Demo character controller for the Full Body FPS scene.
/// </summary>
public class FPSCharacter: MonoBehaviour {
[Range(0f, 1f)] public float walkSpeed = 0.5f;
private float sVel;
private Animator animator;
private FPSAiming FPSAiming;
void Start() {
animator = GetComponent<Animator>();
FPSAiming = GetComponent<FPSAiming>();
}
void Update() {
// Aiming down the sight of the gun when RMB is down
FPSAiming.sightWeight = Mathf.SmoothDamp(FPSAiming.sightWeight, (Input.GetMouseButton(1)? 1f: 0f), ref sVel, 0.1f);
// Set to full values to optimize IK
if (FPSAiming.sightWeight < 0.001f) FPSAiming.sightWeight = 0f;
if (FPSAiming.sightWeight > 0.999f) FPSAiming.sightWeight = 1f;
animator.SetFloat("Speed", walkSpeed);
}
void OnGUI() {
GUI.Label(new Rect(Screen.width - 210, 10, 200, 25), "Hold RMB to aim down the sight");
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c2f6cdcc8d33545af81dd246de448e9d
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Triggering Hit Reactions on mouse button.
/// </summary>
public class HitReactionTrigger: MonoBehaviour {
public HitReaction hitReaction;
public float hitForce = 1f;
private string colliderName;
void Update() {
// On left mouse button...
if (Input.GetMouseButtonDown(0)) {
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// Raycast to find a ragdoll collider
RaycastHit hit = new RaycastHit();
if (Physics.Raycast(ray, out hit, 100f)) {
// Use the HitReaction
hitReaction.Hit(hit.collider, ray.direction * hitForce, hit.point);
// Just for GUI
colliderName = hit.collider.name;
}
}
}
void OnGUI() {
GUILayout.Label("LMB to shoot the Dummy, RMB to rotate the camera.");
if (colliderName != string.Empty) GUILayout.Label("Last Bone Hit: " + colliderName);
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 36c4cd7e8f3ef4beca08a45c21632be2
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Holding hands rig.
/// </summary>
public class HoldingHands : MonoBehaviour {
public FullBodyBipedIK rightHandChar, leftHandChar; // The characters
public Transform rightHandTarget, leftHandTarget; // IK targets for the hands
public float crossFade; // Which character is dominating?
public float speed = 10f; // Speed of smoothly lerping the hands target
private Quaternion rightHandRotation, leftHandRotation;
void Start() {
// Find the rotations of the hands target (this gameobject) in the rotation spaces of the hand bones
rightHandRotation = Quaternion.Inverse(rightHandChar.solver.rightHandEffector.bone.rotation) * transform.rotation;
leftHandRotation = Quaternion.Inverse(leftHandChar.solver.leftHandEffector.bone.rotation) * transform.rotation;
}
void LateUpdate () {
// Positioning the hands target
Vector3 targetPosition = Vector3.Lerp(rightHandChar.solver.rightHandEffector.bone.position, leftHandChar.solver.leftHandEffector.bone.position, crossFade);
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * speed);
// Rotating the hands target
transform.rotation = Quaternion.Slerp(rightHandChar.solver.rightHandEffector.bone.rotation * rightHandRotation, leftHandChar.solver.leftHandEffector.bone.rotation * leftHandRotation, crossFade);
// Set effector positions and rotations
rightHandChar.solver.rightHandEffector.position = rightHandTarget.position;
rightHandChar.solver.rightHandEffector.rotation = rightHandTarget.rotation;
leftHandChar.solver.leftHandEffector.position = leftHandTarget.position;
leftHandChar.solver.leftHandEffector.rotation = leftHandTarget.rotation;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 19f36a5a29ba54779bbe13c64fd30c69
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Demonstrating character-character FBBIK interaction.
/// </summary>
public class InteractionC2CDemo : MonoBehaviour {
// GUI for testing
void OnGUI() {
if (GUILayout.Button("Shake Hands")) {
character1.StartInteraction(FullBodyBipedEffector.RightHand, handShake, true);
character2.StartInteraction(FullBodyBipedEffector.RightHand, handShake, true);
}
}
public InteractionSystem character1, character2; // The InteractionSystems of the characters
public InteractionObject handShake; // The HandShake InteractionObject
void LateUpdate() {
// Positioning the handshake to the middle of the hands
Vector3 handsCenter = Vector3.Lerp(character1.ik.solver.rightHandEffector.bone.position, character2.ik.solver.rightHandEffector.bone.position, 0.5f);
handShake.transform.position = handsCenter;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c7e8eb3221b4c45078730c0cad5bde83
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,73 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Simple demo controller for the InteractionSystem.
/// </summary>
public class InteractionDemo : MonoBehaviour {
public InteractionSystem interactionSystem; // Reference to the InteractionSystem of the character
public bool interrupt; // Can we interrupt an interaction of an effector?
// The interaction objects
public InteractionObject ball, benchMain, benchHands, button, cigarette, door;
private bool isSitting;
// GUI for calling the interactions
void OnGUI() {
interrupt = GUILayout.Toggle(interrupt, "Interrupt");
// While seated
if (isSitting) {
if (!interactionSystem.inInteraction && GUILayout.Button("Stand Up")) {
interactionSystem.ResumeAll();
isSitting = false;
}
return;
}
// While standing
if (GUILayout.Button("Pick Up Ball")) {
interactionSystem.StartInteraction(FullBodyBipedEffector.RightHand, ball, interrupt);
}
if (GUILayout.Button("Button Left Hand")) {
interactionSystem.StartInteraction(FullBodyBipedEffector.LeftHand, button, interrupt);
}
if (GUILayout.Button("Button Right Hand")) {
interactionSystem.StartInteraction(FullBodyBipedEffector.RightHand, button, interrupt);
}
if (GUILayout.Button("Put Out Cigarette")) {
interactionSystem.StartInteraction(FullBodyBipedEffector.RightFoot, cigarette, interrupt);
}
if (GUILayout.Button("Open Door")) {
interactionSystem.StartInteraction(FullBodyBipedEffector.LeftHand, door, interrupt);
}
// This is a multiple-effector interaction
if (!interactionSystem.inInteraction && GUILayout.Button("Sit Down")) {
interactionSystem.StartInteraction(FullBodyBipedEffector.Body, benchMain, interrupt);
interactionSystem.StartInteraction(FullBodyBipedEffector.LeftThigh, benchMain, interrupt);
interactionSystem.StartInteraction(FullBodyBipedEffector.RightThigh, benchMain, interrupt);
interactionSystem.StartInteraction(FullBodyBipedEffector.LeftFoot, benchMain, interrupt);
interactionSystem.StartInteraction(FullBodyBipedEffector.LeftHand, benchHands, interrupt);
interactionSystem.StartInteraction(FullBodyBipedEffector.RightHand, benchHands, interrupt);
isSitting = true;
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a6c1fe7250c0a4474b17dce2b640d1da
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Simple GUI for quickly testing out interactions.
/// </summary>
public class InteractionSystemTestGUI : MonoBehaviour {
[Tooltip("The object to interact to")]
public InteractionObject interactionObject;
[Tooltip("The effectors to interact with")]
public FullBodyBipedEffector[] effectors;
private InteractionSystem interactionSystem;
void Awake() {
interactionSystem = GetComponent<InteractionSystem>();
}
void OnGUI() {
if (interactionSystem == null) return;
if (GUILayout.Button("Start Interaction With " + interactionObject.name)) {
if (effectors.Length == 0) Debug.Log("Please select the effectors to interact with.");
foreach (FullBodyBipedEffector e in effectors) {
interactionSystem.StartInteraction(e, interactionObject, true);
}
}
if (effectors.Length == 0) return;
if (interactionSystem.IsPaused(effectors[0])) {
if (GUILayout.Button("Resume Interaction With " + interactionObject.name)) {
interactionSystem.ResumeAll();
}
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 8cc0304f5a703416ebff81ad105ddf18
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,114 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// FBBIK example rig for a kissing emote.
/// As the IK targets of one FBBIK partner depend on the solved pose of another FBBIK partner and vice versa, the rig will have to be iterated a couple of times to make it work.
/// </summary>
public class KissingRig : MonoBehaviour {
/// <summary>
/// A partner in the emote
/// </summary>
[System.Serializable]
public class Partner {
public FullBodyBipedIK ik; // Reference to the FBBIK component
public Transform mouth; // The mouth bone, should be attached to the head
public Transform mouthTarget; // The target that we want to set the mouth bone to
public Transform touchTargetLeftHand, touchTargetRightHand; // Touch targets for the hands
public float bodyWeightHorizontal = 0.4f; // The body effector horizontal weight
public float bodyWeightVertical = 1f; // The body effector vertical weight
public float neckRotationWeight = 0.3f; // The neck rotation weight
public float headTiltAngle = 10f; // Tilting the head
public Vector3 headTiltAxis; // Head tilt axis
private Quaternion neckRotation;
public void Initiate() {
// Disable the FBBIK component to manage its updating
ik.enabled = false;
}
public void Update(float weight) {
// Set IK position and rotation weights
ik.solver.leftShoulderEffector.positionWeight = weight;
ik.solver.rightShoulderEffector.positionWeight = weight;
ik.solver.leftHandEffector.positionWeight = weight;
ik.solver.rightHandEffector.positionWeight = weight;
ik.solver.leftHandEffector.rotationWeight = weight;
ik.solver.rightHandEffector.rotationWeight = weight;
ik.solver.bodyEffector.positionWeight = weight;
// Inverse transform the shoulder and body effectors to set them relative to the mouth bone's position in the animation
InverseTransformEffector(FullBodyBipedEffector.LeftShoulder, mouth, mouthTarget.position, weight);
InverseTransformEffector(FullBodyBipedEffector.RightShoulder, mouth, mouthTarget.position, weight);
InverseTransformEffector(FullBodyBipedEffector.Body, mouth, mouthTarget.position, weight);
// Positioning the body effector horizontally
ik.solver.bodyEffector.position = Vector3.Lerp(new Vector3(ik.solver.bodyEffector.position.x, ik.solver.bodyEffector.bone.position.y, ik.solver.bodyEffector.position.z), ik.solver.bodyEffector.position, bodyWeightVertical * weight);
// Positioning the body effector vertically
ik.solver.bodyEffector.position = Vector3.Lerp(new Vector3(ik.solver.bodyEffector.bone.position.x, ik.solver.bodyEffector.position.y, ik.solver.bodyEffector.bone.position.z), ik.solver.bodyEffector.position, bodyWeightHorizontal * weight);
// Set hand effector positions to touch targets
ik.solver.leftHandEffector.position = touchTargetLeftHand.position;
ik.solver.rightHandEffector.position = touchTargetRightHand.position;
ik.solver.leftHandEffector.rotation = touchTargetLeftHand.rotation;
ik.solver.rightHandEffector.rotation = touchTargetRightHand.rotation;
// Store the neck rotation so we could slerp back to it after updating FBBIK
neckRotation = neck.rotation;
// Update the FBBIK solver
ik.solver.Update();
// Revert the neck back to its animated rotation
neck.rotation = Quaternion.Slerp(neck.rotation, neckRotation, neckRotationWeight * weight);
// Head tilting
ik.references.head.localRotation = Quaternion.AngleAxis(headTiltAngle * weight, headTiltAxis) * ik.references.head.localRotation;
}
// Get the neck bone
private Transform neck {
get {
return ik.solver.spineMapping.spineBones[ik.solver.spineMapping.spineBones.Length - 1];
}
}
// Placing an effector so that an arbitrary Transform (target) ends up at targetPosition
private void InverseTransformEffector(FullBodyBipedEffector effector, Transform target, Vector3 targetPosition, float weight) {
// Direction from the target to the effector
Vector3 toEffector = ik.solver.GetEffector(effector).bone.position - target.position;
// Positioning the effector
ik.solver.GetEffector(effector).position = Vector3.Lerp(ik.solver.GetEffector(effector).bone.position, targetPosition + toEffector, weight);
}
}
public Partner partner1, partner2; // The partners
[Range(0f, 1f)] public float weight; // The master weight
[Range(1, 4)] public int iterations = 3; // The number of iterating this rig.
// As the IK targets of one FBBIK partner depend on the solved pose of another FBBIK partner and vice versa,
// the rig will have to be iterated a couple of times to make it work.
void Start() {
// Initiating the partners
partner1.Initiate();
partner2.Initiate();
}
void LateUpdate() {
// Iterate the rig
for (int i = 0; i < iterations; i++) {
partner1.Update(weight);
partner2.Update(weight);
}
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 124812fe005074e3090d2734c12bd7e9
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,130 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Absorbing motion on FBBIK effectors on impact. Attach this to the GameObject that receives OnCollisionEnter calls.
/// </summary>
public class MotionAbsorb : OffsetModifier {
[System.Serializable]
public enum Mode {
Position,
PositionOffset
}
// Manages motion absorbing for an effector
[System.Serializable]
public class Absorber {
[Tooltip("The type of effector (hand, foot, shoulder...) - this is just an enum")]
public FullBodyBipedEffector effector;
[Tooltip("How much should motion be absorbed on this effector")]
public float weight = 1f;
private Vector3 position;
private Quaternion rotation = Quaternion.identity;
private IKEffector e;
// Set effector position and rotation to match its bone
public void SetToBone(IKSolverFullBodyBiped solver, Mode mode) {
e = solver.GetEffector(effector);
// Using world space position and rotation here for the sake of simplicity of the demo
// Ideally we should use position and rotation relative to character's root, so we could move around while doing this.
switch(mode) {
case Mode.Position:
e.position = e.bone.position;
e.rotation = e.bone.rotation;
return;
case Mode.PositionOffset:
position = e.bone.position;
rotation = e.bone.rotation;
return;
}
}
// Set effector position and rotation weight to match the value, multiply with the weight of this Absorber
public void UpdateEffectorWeights(float w) {
e.positionWeight = w * weight;
e.rotationWeight = w * weight;
}
// Set effector positionOffset to match the position, multiply with the weight of this Absorber
public void SetPosition(float w) {
e.positionOffset += (position - e.bone.position) * w * weight;
}
// Set effector bone rotation to match the rotation, multiply with the weight of this Absorber
public void SetRotation(float w) {
e.bone.rotation = Quaternion.Slerp(e.bone.rotation, rotation, w * weight);
}
}
[Tooltip("Use either effector position, position weight, rotation, rotationWeight or positionOffset and rotating the bone directly.")]
public Mode mode;
[Tooltip("Array containing the absorbers")]
public Absorber[] absorbers;
[Tooltip("Weight falloff curve (how fast will the effect reduce after impact)")]
public AnimationCurve falloff;
[Tooltip("How fast will the impact fade away. (if 1, effect lasts for 1 second)")]
public float falloffSpeed = 1f;
private float timer; // Used for fading out the effect of the impact
private float w;
private Mode initialMode;
protected override void Start() {
base.Start();
ik.solver.OnPostUpdate += AfterIK;
initialMode = mode;
}
void OnCollisionEnter(Collision c) {
// Don't register another contact until the effect of the last one has faded
if (timer > 0f) return;
// Reset timer
timer = 1f;
// Set effector position and rotation to match its bone
for (int i = 0; i < absorbers.Length; i++) absorbers[i].SetToBone(ik.solver, mode);
}
// Called by IKSolverFullBody before updating
protected override void OnModifyOffset() {
if (timer <= 0f) return;
mode = initialMode;
// Fading out the effect
timer -= Time.deltaTime * falloffSpeed;
// Evaluate the absorb weight
w = falloff.Evaluate(timer);
// Set the weights of the effectors
if (mode == Mode.Position) {
for (int i = 0; i < absorbers.Length; i++) absorbers[i].UpdateEffectorWeights(w * weight);
} else {
for (int i = 0; i < absorbers.Length; i++) absorbers[i].SetPosition(w * weight);
}
}
void AfterIK() {
if (timer <= 0f) return;
if (mode == Mode.Position) return;
for (int i = 0; i < absorbers.Length; i++) absorbers[i].SetRotation(w * weight);
}
protected override void OnDestroy() {
base.OnDestroy();
if (ik != null) ik.solver.OnPostUpdate -= AfterIK;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 85872a1e4a1574355b197e878d6c3d43
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,45 @@
using UnityEngine;
using System.Collections;
namespace RootMotion.Demos {
/// <summary>
/// Motion Absorb demo character controller.
/// </summary>
public class MotionAbsorbCharacter : MonoBehaviour {
public Animator animator;
public MotionAbsorb motionAbsorb;
public Transform cube; // The cube we are hitting
public float cubeRandomPosition = 0.1f; // Randomizing cube position after each hit
public AnimationCurve motionAbsorbWeight;
private Vector3 cubeDefaultPosition;
private AnimatorStateInfo info;
private Rigidbody cubeRigidbody;
void Start() {
// Storing the default position of the cube
cubeDefaultPosition = cube.position;
cubeRigidbody = cube.GetComponent<Rigidbody>();
}
void Update () {
// Set motion absorb weight
//motionAbsorb.weight = animator.GetFloat("MotionAbsorbWeight"); // NB! Using Mecanim curves is PRO only
// Using an animation curve so it works with Unity Free as well
info = animator.GetCurrentAnimatorStateInfo(0);
motionAbsorb.weight = motionAbsorbWeight.Evaluate(info.normalizedTime - (int)info.normalizedTime);
}
// Mecanim event
void SwingStart() {
// Reset the cube
cubeRigidbody.MovePosition(cubeDefaultPosition + UnityEngine.Random.insideUnitSphere * cubeRandomPosition);
cubeRigidbody.MoveRotation(Quaternion.identity);
cubeRigidbody.velocity = Vector3.zero;
cubeRigidbody.angularVelocity = Vector3.zero;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 278ffbe117e4c4edba3d761b55ba7bbb
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Custom positionOffset effector for FBBIK, could be used for example to make a spine or pelvis effector.
/// </summary>
public class OffsetEffector : OffsetModifier {
[System.Serializable]
public class EffectorLink {
public FullBodyBipedEffector effectorType;
public float weightMultiplier = 1f;
[HideInInspector] public Vector3 localPosition;
}
[Tooltip("Optional. Assign the bone Transform that is closest to this OffsetEffector to be able to call OffsetEffector.Anchor() in LateUpdate to match its position and rotation to animation.")]
public Transform anchor;
public EffectorLink[] effectorLinks;
private Vector3 posRelToAnchor;
private Quaternion rotRelToAnchor = Quaternion.identity;
protected override void Start() {
base.Start();
if (anchor != null)
{
posRelToAnchor = anchor.InverseTransformPoint(transform.position);
rotRelToAnchor = Quaternion.Inverse(anchor.rotation) * transform.rotation;
}
// Store the default positions of the effectors relative to this GameObject's position
foreach (EffectorLink e in effectorLinks) {
var bone = ik.solver.GetEffector(e.effectorType).bone;
e.localPosition = transform.InverseTransformPoint(bone.position);
if (e.effectorType == FullBodyBipedEffector.Body) ik.solver.bodyEffector.effectChildNodes = false;
}
}
protected override void OnModifyOffset() {
// Update the effectors
foreach (EffectorLink e in effectorLinks) {
// Using effector positionOffset
Vector3 positionTarget = transform.TransformPoint(e.localPosition);
ik.solver.GetEffector(e.effectorType).positionOffset += (positionTarget - (ik.solver.GetEffector(e.effectorType).bone.position + ik.solver.GetEffector(e.effectorType).positionOffset)) * weight * e.weightMultiplier;
}
}
public void Anchor()
{
if (anchor == null) return;
transform.position = anchor.TransformPoint(posRelToAnchor);
transform.rotation = anchor.rotation * rotRelToAnchor;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 863f1c6a1b50e40cfa321c4b7bd7b004
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,108 @@
using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Making a character hold on to a target and swing about it while maintaining his animation.
/// </summary>
public class PendulumExample : MonoBehaviour {
[Tooltip("The master weight of this script.")]
[Range(0f, 1f)] public float weight = 1f;
[Tooltip("Multiplier for the distance of the root to the target.")]
public float hangingDistanceMlp = 1.3f;
[Tooltip("Where does the root of the character land when weight is blended out?")]
[HideInInspector] public Vector3 rootTargetPosition;
[Tooltip("How is the root of the character rotated when weight is blended out?")]
[HideInInspector] public Quaternion rootTargetRotation;
public Transform target;
public Transform leftHandTarget;
public Transform rightHandTarget;
public Transform leftFootTarget;
public Transform rightFootTarget;
public Transform pelvisTarget;
public Transform bodyTarget;
public Transform headTarget;
public Vector3 pelvisDownAxis = Vector3.right;
private FullBodyBipedIK ik;
private Quaternion rootRelativeToPelvis;
private Vector3 pelvisToRoot;
private float lastWeight;
void Start() {
ik = GetComponent<FullBodyBipedIK>();
// Connect the left hand to the target
Quaternion targetRotation = target.rotation;
target.rotation = leftHandTarget.rotation;
FixedJoint j = target.gameObject.AddComponent<FixedJoint>();
j.connectedBody = leftHandTarget.GetComponent<Rigidbody>();
target.GetComponent<Rigidbody>().MoveRotation(targetRotation);
//target.rotation = targetRotation;
// Remember the rotation of the root relative to the pelvis
rootRelativeToPelvis = Quaternion.Inverse(pelvisTarget.rotation) * transform.rotation;
// Remember the position of the root relative to the pelvis
pelvisToRoot = Quaternion.Inverse(ik.references.pelvis.rotation) * (transform.position - ik.references.pelvis.position);
rootTargetPosition = transform.position;
rootTargetRotation = transform.rotation;
lastWeight = weight;
}
void LateUpdate() {
// Set effector weights
if (weight > 0f) {
ik.solver.leftHandEffector.positionWeight = weight;
ik.solver.leftHandEffector.rotationWeight = weight;
} else {
rootTargetPosition = transform.position;
rootTargetRotation = transform.rotation;
if (lastWeight > 0f) {
ik.solver.leftHandEffector.positionWeight = 0f;
ik.solver.leftHandEffector.rotationWeight = 0f;
}
}
lastWeight = weight;
if (weight <= 0f) return;
// Position the character relative to the ragdoll pelvis
transform.position = Vector3.Lerp(rootTargetPosition, pelvisTarget.position + pelvisTarget.rotation * pelvisToRoot * hangingDistanceMlp, weight);
// Rotate the character to the ragdoll pelvis
transform.rotation = Quaternion.Lerp(rootTargetRotation, pelvisTarget.rotation * rootRelativeToPelvis, weight);
// Set ik effector positions
ik.solver.leftHandEffector.position = leftHandTarget.position;
ik.solver.leftHandEffector.rotation = leftHandTarget.rotation;
// Get the normal hanging direction
Vector3 dir = ik.references.pelvis.rotation * pelvisDownAxis;
// Rotating the limbs
// Get the rotation from normal hangind direction to the right arm ragdoll direction
Quaternion rightArmRot = Quaternion.FromToRotation(dir, rightHandTarget.position - headTarget.position);
// Rotate the right arm by that offset
ik.references.rightUpperArm.rotation = Quaternion.Lerp(Quaternion.identity, rightArmRot, weight) * ik.references.rightUpperArm.rotation;
Quaternion leftLegRot = Quaternion.FromToRotation(dir, leftFootTarget.position - bodyTarget.position);
ik.references.leftThigh.rotation = Quaternion.Lerp(Quaternion.identity, leftLegRot, weight) * ik.references.leftThigh.rotation;
Quaternion rightLegRot = Quaternion.FromToRotation(dir, rightFootTarget.position - bodyTarget.position);
ik.references.rightThigh.rotation = Quaternion.Lerp(Quaternion.identity, rightLegRot, weight) * ik.references.rightThigh.rotation;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: a4e2a7601da0546edbfe147eb9f95826
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,159 @@
using UnityEngine;
using System.Collections;
using RootMotion;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Picking up an arbitrary object with both hands.
/// </summary>
public abstract class PickUp2Handed : MonoBehaviour {
// GUI for testing
public int GUIspace;
void OnGUI() {
GUILayout.BeginHorizontal();
GUILayout.Space(GUIspace);
if (!holding) {
if (GUILayout.Button("Pick Up " + obj.name)) {
interactionSystem.StartInteraction(FullBodyBipedEffector.LeftHand, obj, false);
interactionSystem.StartInteraction(FullBodyBipedEffector.RightHand, obj, false);
}
} else {
GUILayout.BeginVertical();
if (holdingRight)
{
if (GUILayout.Button("Release Right"))
{
interactionSystem.ResumeInteraction(FullBodyBipedEffector.RightHand);
}
}
if (holdingLeft)
{
if (GUILayout.Button("Release Left"))
{
interactionSystem.ResumeInteraction(FullBodyBipedEffector.LeftHand);
}
}
if (GUILayout.Button("Drop " + obj.name)) {
interactionSystem.ResumeAll();
}
GUILayout.EndVertical();
}
GUILayout.EndHorizontal();
}
protected abstract void RotatePivot();
public InteractionSystem interactionSystem; // The InteractionSystem of the character
public InteractionObject obj; // The object to pick up
public Transform pivot; // The pivot point of the hand targets
public Transform holdPoint; // The point where the object will lerp to when picked up
public float pickUpTime = 0.3f; // Maximum lerp speed of the object. Decrease this value to give the object more weight
private float holdWeight, holdWeightVel;
private Vector3 pickUpPosition;
private Quaternion pickUpRotation;
void Start() {
// Listen to interaction events
interactionSystem.OnInteractionStart += OnStart;
interactionSystem.OnInteractionPause += OnPause;
interactionSystem.OnInteractionResume += OnDrop;
}
// Called by the InteractionSystem when an interaction is paused (on trigger)
private void OnPause(FullBodyBipedEffector effectorType, InteractionObject interactionObject) {
if (effectorType != FullBodyBipedEffector.LeftHand) return;
if (interactionObject != obj) return;
// Make the object inherit the character's movement
obj.transform.parent = interactionSystem.transform;
// Make the object kinematic
var r = obj.GetComponent<Rigidbody>();
if (r != null) r.isKinematic = true;
// Set object pick up position and rotation to current
pickUpPosition = obj.transform.position;
pickUpRotation = obj.transform.rotation;
holdWeight = 0f;
holdWeightVel = 0f;
}
// Called by the InteractionSystem when an interaction starts
private void OnStart(FullBodyBipedEffector effectorType, InteractionObject interactionObject) {
if (effectorType != FullBodyBipedEffector.LeftHand) return;
if (interactionObject != obj) return;
// Rotate the hold point so it matches the current rotation of the object
holdPoint.rotation = obj.transform.rotation;
// Rotate the pivot of the hand targets
RotatePivot();
}
// Called by the InteractionSystem when an interaction is resumed from being paused
private void OnDrop(FullBodyBipedEffector effectorType, InteractionObject interactionObject) {
if (holding) return;
if (interactionObject != obj) return;
// Make the object independent of the character
obj.transform.parent = null;
// Turn on physics for the object
if (obj.GetComponent<Rigidbody>() != null) obj.GetComponent<Rigidbody>().isKinematic = false;
}
void LateUpdate() {
if (holding) {
// Smoothing in the hold weight
holdWeight = Mathf.SmoothDamp(holdWeight, 1f, ref holdWeightVel, pickUpTime);
// Interpolation
obj.transform.position = Vector3.Lerp(pickUpPosition, holdPoint.position, holdWeight);
obj.transform.rotation = Quaternion.Lerp(pickUpRotation, holdPoint.rotation, holdWeight);
}
}
// Are we currently holding the object?
private bool holding {
get {
return holdingLeft || holdingRight;
}
}
// Are we currently holding the object with left hand?
private bool holdingLeft
{
get
{
return interactionSystem.IsPaused(FullBodyBipedEffector.LeftHand) && interactionSystem.GetInteractionObject(FullBodyBipedEffector.LeftHand) == obj;
}
}
// Are we currently holding the object with right hand?
private bool holdingRight
{
get
{
return interactionSystem.IsPaused(FullBodyBipedEffector.RightHand) && interactionSystem.GetInteractionObject(FullBodyBipedEffector.RightHand) == obj;
}
}
// Clean up delegates
void OnDestroy() {
if (interactionSystem == null) return;
interactionSystem.OnInteractionStart -= OnStart;
interactionSystem.OnInteractionPause -= OnPause;
interactionSystem.OnInteractionResume -= OnDrop;
}
}
}

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 15d5147941ecd4e3d9c2d98b880da82b
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using UnityEngine;
using System.Collections;
using RootMotion;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Picking up a box shaped object with both hands.
/// </summary>
public class PickUpBox : PickUp2Handed {
// Rotate the pivot of the hand targets by 90 degrees so we could grab the object from any direction
protected override void RotatePivot() {
// Get the flat direction towards the character
Vector3 characterDirection = (pivot.position - interactionSystem.transform.position).normalized;
characterDirection.y = 0f;
// Convert the direction to local space of the object
Vector3 characterDirectionLocal = obj.transform.InverseTransformDirection(characterDirection);
// QuaTools.GetAxis returns a 90 degree ortographic axis for any direction
Vector3 axis = QuaTools.GetAxis(characterDirectionLocal);
Vector3 upAxis = QuaTools.GetAxis(obj.transform.InverseTransformDirection(interactionSystem.transform.up));
// Rotate towards axis and upAxis
pivot.localRotation = Quaternion.LookRotation(axis, upAxis);
// Match rotation of pivot to character rotation (added in v2.2)
Quaternion q = RootMotion.QuaTools.FromToRotation(pivot.rotation, interactionSystem.transform.rotation);
holdPoint.rotation = q * holdPoint.rotation;
}
}
}

Some files were not shown because too many files have changed in this diff Show More