11using System.Collections;
12using System.Collections.Generic;
36 [AddComponentMenu(
"")]
57 private bool _wasKinematicBeforeActivation =
false;
92 refreshRequiredComponents();
96 initWorkstationPoseFunctions();
100 refreshRequiredComponents();
103 Debug.LogWarning(
"WorkstationBehaviour expects its AnchorableBehaviour's tryAnchorNearestOnGraspEnd property to be enabled.", this.gameObject);
115 _wasKinematicBeforeActivation = _intObj.
rigidbody.isKinematic;
123 _intObj.
rigidbody.isKinematic = _wasKinematicBeforeActivation;
128 private void refreshRequiredComponents() {
129 _intObj = GetComponent<InteractionBehaviour>();
130 _anchObj = GetComponent<AnchorableBehaviour>();
137 private void onGraspedMovement(Vector3 preSolvePos, Quaternion preSolveRot,
138 Vector3 curPos, Quaternion curRot,
139 List<InteractionController> controllers) {
153 _intObj.
rigidbody.rotation = (Quaternion.FromToRotation(_intObj.
rigidbody.rotation * Vector3.up, Vector3.up)) * _intObj.
rigidbody.rotation;
158 private void onPostObjectGraspEnd() {
165 targetPosition = _intObj.
rigidbody.position;
168 targetPosition = determineWorkstationPosition();
171 Quaternion targetRotation = determineWorkstationRotation(targetPosition);
175 targetPosition, targetRotation);
186 private const float MAX_TRAVEL_SPEED = 4.00F;
188 private Tween _travelTween;
190 private float _initTravelTime = 0F;
197 private Vector3 _travelTargetPosition;
200 private Vector2 _minMaxWorkstationTravelTime =
new Vector2(0.05F, 1.00F);
201 private Vector2 _minMaxTravelTimeFromThrowSpeed =
new Vector2(0.30F, 8.00F);
203 private void beginTraveling(Vector3 initPosition, Vector3 initVelocity,
204 Quaternion initRotation, Vector3 initAngVelocity,
205 Vector3 targetPosition, Quaternion targetRotation) {
206 _initTravelTime = Time.time;
207 _initTravelPosition = initPosition;
208 _initTravelVelocity = initVelocity;
209 _initTravelRotation = initRotation;
210 _initTravelAngVelocity = initAngVelocity;
212 float velMagnitude = _initTravelVelocity.magnitude;
213 if (velMagnitude > MAX_TRAVEL_SPEED) {
214 float capSpeedMultiplier = MAX_TRAVEL_SPEED / velMagnitude;
215 _initTravelVelocity *= capSpeedMultiplier;
218 _effGravity =
Vector3.Lerp(
Vector3.zero,
Physics.gravity, initVelocity.magnitude.Map(0.80F, 3F, 0F, 0.70F));
222 _travelTargetPosition = targetPosition;
223 _travelTargetRotation = targetRotation;
229 .
OverTime(_initTravelVelocity.magnitude.Map(_minMaxTravelTimeFromThrowSpeed.x, _minMaxTravelTimeFromThrowSpeed.y,
230 _minMaxWorkstationTravelTime.x, _minMaxWorkstationTravelTime.y))
236 private void onTravelTweenProgress(
float progress) {
237 float curTime = Time.time;
238 Vector3 extrapolatedPosition = evaluatePosition(_initTravelPosition, _initTravelVelocity, _effGravity, _initTravelTime, curTime);
239 Quaternion extrapolatedRotation = evaluateRotation(_initTravelRotation, _initTravelAngVelocity, _initTravelTime, curTime);
244 _intObj.
rigidbody.position =
Vector3.Lerp(extrapolatedPosition, _travelTargetPosition, progress);
245 _intObj.
rigidbody.rotation =
Quaternion.Slerp(extrapolatedRotation, _travelTargetRotation, progress);
248 private void cancelTraveling() {
256 private Vector3 evaluatePosition(Vector3 initialPosition, Vector3 initialVelocity, Vector3 gravity,
float initialTime,
float timeToEvaluate) {
257 float t = timeToEvaluate - initialTime;
258 return initialPosition + (initialVelocity * t) + (0.5f * gravity * t * t);
264 private Quaternion evaluateRotation(Quaternion initialRotation, Vector3 angularVelocity,
float initialTime,
float timeToEvaluate) {
265 float t = timeToEvaluate - initialTime;
266 return Quaternion.Euler(angularVelocity * t) * initialRotation;
271 #region Workstation Pose
274 Vector3 workstationObjInitPosition, Vector3 workstationObjInitVelocity,
float workstationObjRadius,
275 List<Vector3> otherWorkstationPositions, List<float> otherWorkstationRadii);
293 private List<Vector3> _otherStationObjPositions =
new List<Vector3>();
294 private List<float> _otherStationObjRadii =
new List<float>();
297 private void initWorkstationPoseFunctions() {
302 private Vector3 determineWorkstationPosition() {
305 _otherStationObjPositions, _otherStationObjRadii);
308 private Quaternion determineWorkstationRotation(Vector3 workstationPosition) {
313 Vector3 workstationObjInitPosition, Vector3 workstationObjInitVelocity,
float workstationObjRadius,
314 List<Vector3> otherWorkstationPositions, List<float> otherWorkstationRadii) {
316 Vector3 towardsCamera = (userEyePosition - workstationObjInitPosition).normalized;
317 float towardsCameraness = Mathf.Clamp01(Vector3.Dot(towardsCamera, workstationObjInitVelocity.normalized));
318 workstationObjInitVelocity = workstationObjInitVelocity + Vector3.Lerp(Vector3.zero, -towardsCamera * 2.00F, towardsCameraness);
321 Vector3 groundPlaneVelocity = Vector3.ProjectOnPlane(workstationObjInitVelocity, Vector3.up);
322 float groundPlaneDirectedness = groundPlaneVelocity.magnitude.Map(0.003F, 0.40F, 0F, 1F);
323 Vector3 groundPlaneDirection = groundPlaneVelocity.normalized;
326 Vector3 cameraGroundPlaneForward = Vector3.ProjectOnPlane(userEyeRotation * Vector3.forward, Vector3.up);
327 float cameraGroundPlaneDirectedness = cameraGroundPlaneForward.magnitude.Map(0.001F, 0.01F, 0F, 1F);
328 Vector3 alternateCameraDirection = (userEyeRotation * Vector3.forward).y > 0F ? userEyeRotation * Vector3.down : userEyeRotation * Vector3.up;
329 cameraGroundPlaneForward = Vector3.Slerp(alternateCameraDirection, cameraGroundPlaneForward, cameraGroundPlaneDirectedness);
330 cameraGroundPlaneForward = cameraGroundPlaneForward.normalized;
333 Vector3 placementDirection = Vector3.Slerp(cameraGroundPlaneForward, groundPlaneDirection, groundPlaneDirectedness);
336 float minPlacementDistance = 0.25F;
337 float maxPlacementDistance = 0.51F;
338 Vector3 placementPosition = userEyePosition + placementDirection * Mathf.Lerp(minPlacementDistance, maxPlacementDistance,
339 (groundPlaneDirectedness * workstationObjInitVelocity.magnitude)
340 .Map(0F, 1.50F, 0F, 1F));
343 float overallDirectedness = workstationObjInitVelocity.magnitude.Map(0.00F, 3.00F, 0F, 1F);
344 placementPosition = Vector3.Lerp(workstationObjInitPosition, placementPosition, overallDirectedness * overallDirectedness);
347 float placementHeightFromCamera = -0.30F;
348 placementPosition.y = userEyePosition.y + placementHeightFromCamera;
351 Vector2 cameraXZ =
new Vector2(userEyePosition.x, userEyePosition.z);
352 Vector2 stationXZ =
new Vector2(placementPosition.x, placementPosition.z);
353 float placementDist = Vector2.Distance(cameraXZ, stationXZ);
354 if (placementDist < minPlacementDistance) {
355 float distanceLeft = (minPlacementDistance - placementDist) + workstationObjRadius;
356 Vector2 xzDisplacement = (stationXZ - cameraXZ).normalized * distanceLeft;
357 placementPosition +=
new Vector3(xzDisplacement[0], 0F, xzDisplacement[1]);
360 return placementPosition;
364 Vector3 toCamera = userEyePos - workstationPosition;
366 Quaternion placementRotation = Quaternion.LookRotation(toCamera.normalized, Vector3.up);
368 return placementRotation;
This example script constructs behavior for a very specific kind of UI object that can animate out a ...
void ActivateWorkstation()
static Vector3 DefaultDetermineWorkstationPosition(Vector3 userEyePosition, Quaternion userEyeRotation, Vector3 workstationObjInitPosition, Vector3 workstationObjInitVelocity, float workstationObjRadius, List< Vector3 > otherWorkstationPositions, List< float > otherWorkstationRadii)
const float MIN_SPEED_TO_ACTIVATE_TRAVELING
If the rigidbody of this object moves slower than this speed and the object wants to enter workstatio...
static Quaternion DefaultDetermineWorkstationRotation(Vector3 userEyePos, Vector3 workstationPosition)
WorkstationState workstationState
delegate Quaternion WorkstationRotationFunc(Vector3 userEyePosition, Vector3 targetWorkstationPosition)
delegate Vector3 WorkstationPositionFunc(Vector3 userEyePosition, Quaternion userEyeRotation, Vector3 workstationObjInitPosition, Vector3 workstationObjInitVelocity, float workstationObjRadius, List< Vector3 > otherWorkstationPositions, List< float > otherWorkstationRadii)
WorkstationRotationFunc workstationRotationFunc
The function used to calculate this workstation's target rotation. By default, will make the workstat...
WorkstationPositionFunc workstationPositionFunc
The function used to calculate this workstation's target position. By default, will attempt to choose...
void DeactivateWorkstation()
TransformTweenBehaviour workstationModeTween
const float MAX_SPEED_AS_WORKSTATION
If the rigidbody of this object moves faster than this speed and the object is in workstation mode,...
bool isTraveling
Gets whether the workstation is currently traveling to a target position to open in workstation mode....
AnchorableBehaviours mix well with InteractionBehaviours you'd like to be able to pick up and place i...
Action OnPostTryAnchorOnGraspEnd
Called just after this anchorable behaviour's InteractionBehaviour OnObjectGraspEnd for this anchor....
bool tryAnchorNearestOnGraspEnd
Anchor preferredAnchor
Gets the anchor this AnchorableBehaviour would most prefer to attach to. This value is refreshed ever...
InteractionBehaviours are components that enable GameObjects to interact with interaction controllers...
Rigidbody rigidbody
The Rigidbody associated with this interaction object.
GraspedMovementEvent OnGraspedMovement
Called directly after this grasped object's Rigidbody has had its position and rotation set by its cu...
Tween Play()
Starts playing this Tween. It will continue from the same position it left off on,...
Direction direction
Gets or sets whether or not this Tween is moving forwards or backwards.
bool isRunning
Returns whether or not the Tween is currently running.
Tween OverTime(float seconds)
Specifies that this Tween should travel from begining to end over a certain number of seconds.
Tween OnProgress(Action< float > action)
Specifies an action to be called every step of the Tween. This callback happens after:
Tween OnReachEnd(Action action)
Specifies an action to be called whenever this Tween reaches the end.
static Tween Single()
Create a single-use Tween that will auto-release itself as soon as it is finished playing.
bool isValid
Returns whether or not this Tween is considered valid. A Tween can become invalid under the following...
void Stop()
Stops this Tween. If it is not a persistant Tween, it will be recycled right away.
float progress
Gets or sets how far along completion this Tween is. This value is a percent that ranges from 0 to 1.