holopy3/Assets/Plugins/RootMotion/FinalIK/_DEMOS/FBBIK/Scripts/TouchWalls.cs
2020-12-10 15:25:12 +01:00

187 lines
7 KiB
C#

using UnityEngine;
using System.Collections;
using RootMotion.FinalIK;
namespace RootMotion.Demos {
/// <summary>
/// Demo script for using the Interaction System for touching scene geometry.
/// </summary>
public class TouchWalls : MonoBehaviour {
/// <summary>
/// Touching functionality for an effector
/// </summary>
[System.Serializable]
public class EffectorLink {
public bool enabled = true; // Can this effector start touching?
public FullBodyBipedEffector effectorType; // The type of the effector
public InteractionObject interactionObject; // The touch interaction object
public Transform spherecastFrom; // The bone to start the sperecast from
public float spherecastRadius = 0.1f; // The radius of sperecasting
public float minDistance = 0.3f; // The minimum spherecast radius to touch
public LayerMask touchLayers; // The layers to touch
public float lerpSpeed = 10f; // The speed of lerping the interaction object
public float minSwitchTime = 0.2f; // The minimum time to switch between touching
public float releaseDistance= 0.4f; // The distance from the original touch point where the effector will lose contact
public bool sliding; // Can the effector slide smoothly over the surfaces?
private Vector3 raycastDirectionLocal;
private float raycastDistance;
private bool inTouch;
private RaycastHit hit = new RaycastHit();
private Vector3 targetPosition;
private Quaternion targetRotation;
private bool initiated;
private float nextSwitchTime;
private float speedF;
// Initiate this instance
public void Initiate(InteractionSystem interactionSystem) {
// Find the direction to the interaction objet relative to the sperecastFrom Transform
raycastDirectionLocal = spherecastFrom.InverseTransformDirection(interactionObject.transform.position - spherecastFrom.position);
// Find the max distance the sperecast
raycastDistance = Vector3.Distance(spherecastFrom.position, interactionObject.transform.position);
// Add to the interaction system delegates
interactionSystem.OnInteractionStart += OnInteractionStart;
interactionSystem.OnInteractionResume += OnInteractionResume;
interactionSystem.OnInteractionStop += OnInteractionStop;
hit.normal = Vector3.forward;
targetPosition = interactionObject.transform.position;
targetRotation = interactionObject.transform.rotation;
initiated = true;
}
// Spherecasting to find the walls
private bool FindWalls(Vector3 direction) {
if (!enabled) return false;
bool found = Physics.SphereCast(spherecastFrom.position, spherecastRadius, direction, out hit, raycastDistance, touchLayers);
if (hit.distance < minDistance) found = false;
return found;
}
// Update this instance
public void Update(InteractionSystem interactionSystem) {
if (!initiated) return;
// The default position
Vector3 direction = spherecastFrom.TransformDirection(raycastDirectionLocal);
hit.point = spherecastFrom.position + direction;
// Spherecasting
bool wallFound = FindWalls(direction);
// Starting and ending the interactions
if (!inTouch) {
if (wallFound && Time.time > nextSwitchTime) {
interactionObject.transform.parent = null;
interactionSystem.StartInteraction(effectorType, interactionObject, true);
nextSwitchTime = Time.time + minSwitchTime / interactionSystem.speed;
targetPosition = hit.point;
targetRotation = Quaternion.LookRotation(-hit.normal);
interactionObject.transform.position = targetPosition;
interactionObject.transform.rotation = targetRotation;
}
} else {
if (!wallFound) {
// Resume if no wall found
StopTouch(interactionSystem);
} else {
if (!interactionSystem.IsPaused(effectorType) || sliding) {
targetPosition = hit.point;
targetRotation = Quaternion.LookRotation(-hit.normal);
}
}
if (Vector3.Distance(interactionObject.transform.position, hit.point) > releaseDistance) {
if (wallFound) {
targetPosition = hit.point;
targetRotation = Quaternion.LookRotation(-hit.normal);
} else {
StopTouch(interactionSystem);
}
}
}
float speedFTarget = !inTouch || (interactionSystem.IsPaused(effectorType) && interactionObject.transform.position == targetPosition)? 0f: 1f;
speedF = Mathf.Lerp(speedF, speedFTarget, Time.deltaTime * 3f * interactionSystem.speed);
// Interpolating the interaction object
float s = Time.deltaTime * lerpSpeed * speedF * interactionSystem.speed;
interactionObject.transform.position = Vector3.Lerp(interactionObject.transform.position, targetPosition, s);
interactionObject.transform.rotation = Quaternion.Slerp(interactionObject.transform.rotation, targetRotation, s);
}
// Stop touching the walls
private void StopTouch(InteractionSystem interactionSystem) {
interactionObject.transform.parent = interactionSystem.transform;
nextSwitchTime = Time.time + minSwitchTime / interactionSystem.speed;
if (interactionSystem.IsPaused(effectorType)) interactionSystem.ResumeInteraction(effectorType);
else {
speedF = 0f;
targetPosition = hit.point;
targetRotation = Quaternion.LookRotation(-hit.normal);
}
}
// Called by the interaction system
private void OnInteractionStart(FullBodyBipedEffector effectorType, InteractionObject interactionObject) {
if (effectorType != this.effectorType || interactionObject != this.interactionObject) return;
inTouch = true;
}
// Called by the interaction system
private void OnInteractionResume(FullBodyBipedEffector effectorType, InteractionObject interactionObject) {
if (effectorType != this.effectorType || interactionObject != this.interactionObject) return;
inTouch = false;
}
// Called by the interaction system
private void OnInteractionStop(FullBodyBipedEffector effectorType, InteractionObject interactionObject) {
if (effectorType != this.effectorType || interactionObject != this.interactionObject) return;
inTouch = false;
}
// Cleaning up the delegates
public void Destroy(InteractionSystem interactionSystem) {
if (!initiated) return;
interactionSystem.OnInteractionStart -= OnInteractionStart;
interactionSystem.OnInteractionResume -= OnInteractionResume;
interactionSystem.OnInteractionStop -= OnInteractionStop;
}
}
public InteractionSystem interactionSystem; // Reference to the interaciton sytem of the character
public EffectorLink[] effectorLinks; // The wall touching effectors
// Initiate the effector links
void Start() {
foreach (EffectorLink e in effectorLinks) e.Initiate(interactionSystem);
}
// Update touching
void FixedUpdate() {
for (int i = 0; i < effectorLinks.Length; i++) effectorLinks[i].Update(interactionSystem);
}
// Clean up the delegates
void OnDestroy() {
if (interactionSystem != null) {
for (int i = 0; i < effectorLinks.Length; i++) effectorLinks[i].Destroy(interactionSystem);
}
}
}
}