// Author: Daniele Giardini - http://www.demigiant.com // Created: 2014/09/03 19:36 // // License Copyright (c) Daniele Giardini. // This work is subject to the terms at http://dotween.demigiant.com/license.php using DG.Tweening.Core; using DG.Tweening.Core.Easing; using DG.Tweening.Core.Enums; using DG.Tweening.Plugins.Core; using DG.Tweening.Plugins.Core.PathCore; using DG.Tweening.Plugins.Options; using UnityEngine; #pragma warning disable 1591 namespace DG.Tweening.Plugins { /// /// Path plugin works exclusively with Transforms /// public class PathPlugin : ABSTweenPlugin { public const float MinLookAhead = 0.0001f; public override void Reset(TweenerCore t) { t.endValue.Destroy(); // Clear path t.startValue = t.endValue = t.changeValue = null; } public override void SetFrom(TweenerCore t, bool isRelative) {} public static ABSTweenPlugin Get() { return PluginsManager.GetCustomPlugin(); } public override Path ConvertToStartValue(TweenerCore t, Vector3 value) { // Simply sets the same path as start and endValue return t.endValue; } public override void SetRelativeEndValue(TweenerCore t) { if (t.endValue.isFinalized) return; Vector3 startP = t.getter(); int count = t.endValue.wps.Length; for (int i = 0; i < count; ++i) t.endValue.wps[i] += startP; } // Recreates waypoints with correct control points and eventual additional starting point // then sets the final path version public override void SetChangeValue(TweenerCore t) { Transform trans = (Transform)t.target; if (t.plugOptions.orientType == OrientType.ToPath && t.plugOptions.useLocalPosition) t.plugOptions.parent = trans.parent; if (t.endValue.isFinalized) { t.changeValue = t.endValue; return; } Vector3 currVal = t.getter(); Path path = t.endValue; int unmodifiedWpsLen = path.wps.Length; int additionalWps = 0; bool hasAdditionalStartingP = false, hasAdditionalEndingP = false; // Create final wps and add eventual starting/ending waypoints if (path.wps[0] != currVal) { hasAdditionalStartingP = true; additionalWps += 1; } if (t.plugOptions.isClosedPath && path.wps[unmodifiedWpsLen - 1] != currVal) { hasAdditionalEndingP = true; additionalWps += 1; } int wpsLen = unmodifiedWpsLen + additionalWps; Vector3[] wps = new Vector3[wpsLen]; int indMod = hasAdditionalStartingP ? 1 : 0; if (hasAdditionalStartingP) wps[0] = currVal; for (int i = 0; i < unmodifiedWpsLen; ++i) wps[i + indMod] = path.wps[i]; if (hasAdditionalEndingP) wps[wps.Length - 1] = wps[0]; path.wps = wps; // Finalize path path.FinalizePath(t.plugOptions.isClosedPath, t.plugOptions.lockPositionAxis, currVal); t.plugOptions.startupRot = trans.rotation; t.plugOptions.startupZRot = trans.eulerAngles.z; // Set changeValue as a reference to endValue t.changeValue = t.endValue; } public override float GetSpeedBasedDuration(PathOptions options, float unitsXSecond, Path changeValue) { return changeValue.length / unitsXSecond; } public override void EvaluateAndApply(PathOptions options, Tween t, bool isRelative, DOGetter getter, DOSetter setter, float elapsed, Path startValue, Path changeValue, float duration, bool usingInversePosition, UpdateNotice updateNotice) { float pathPerc = EaseManager.Evaluate(t.easeType, t.customEase, elapsed, duration, t.easeOvershootOrAmplitude, t.easePeriod); float constantPathPerc = changeValue.ConvertToConstantPathPerc(pathPerc); Vector3 newPos = changeValue.GetPoint(constantPathPerc); changeValue.targetPosition = newPos; // Used to draw editor gizmos setter(newPos); if (options.mode != PathMode.Ignore && options.orientType != OrientType.None) SetOrientation(options, t, changeValue, constantPathPerc, newPos, updateNotice); // Determine if current waypoint changed and eventually dispatch callback bool isForward = !usingInversePosition; if (t.isBackwards) isForward = !isForward; int newWaypointIndex = changeValue.GetWaypointIndexFromPerc(pathPerc, isForward); if (newWaypointIndex != t.miscInt) { t.miscInt = newWaypointIndex; if (t.onWaypointChange != null) Tween.OnTweenCallback(t.onWaypointChange, newWaypointIndex); } } // Public so it can be called by GotoWaypoint public void SetOrientation(PathOptions options, Tween t, Path path, float pathPerc, Vector3 tPos, UpdateNotice updateNotice) { Transform trans = (Transform)t.target; Quaternion newRot = Quaternion.identity; if (updateNotice == UpdateNotice.RewindStep) { // Reset orientation before continuing trans.rotation = options.startupRot; } switch (options.orientType) { case OrientType.LookAtPosition: path.lookAtPosition = options.lookAtPosition; // Used to draw editor gizmos newRot = Quaternion.LookRotation(options.lookAtPosition - trans.position, trans.up); break; case OrientType.LookAtTransform: if (options.lookAtTransform != null) { path.lookAtPosition = options.lookAtTransform.position; // Used to draw editor gizmos newRot = Quaternion.LookRotation(options.lookAtTransform.position - trans.position, trans.up); } break; case OrientType.ToPath: Vector3 lookAtP; if (path.type == PathType.Linear && options.lookAhead <= MinLookAhead) { // Calculate lookAhead so that it doesn't turn until it starts moving on next waypoint lookAtP = tPos + path.wps[path.linearWPIndex] - path.wps[path.linearWPIndex - 1]; } else { float lookAheadPerc = pathPerc + options.lookAhead; if (lookAheadPerc > 1) lookAheadPerc = (options.isClosedPath ? lookAheadPerc - 1 : path.type == PathType.Linear ? 1 : 1.00001f); lookAtP = path.GetPoint(lookAheadPerc); } if (path.type == PathType.Linear) { // Check if it's the last waypoint, and keep correct direction Vector3 lastWp = path.wps[path.wps.Length - 1]; if (lookAtP == lastWp) lookAtP = tPos == lastWp ? lastWp + (lastWp - path.wps[path.wps.Length - 2]) : lastWp; } Vector3 transUp = trans.up; // Apply basic modification for local position movement if (options.useLocalPosition && options.parent != null) lookAtP = options.parent.TransformPoint(lookAtP); // LookAt axis constraint if (options.lockRotationAxis != AxisConstraint.None) { if ((options.lockRotationAxis & AxisConstraint.X) == AxisConstraint.X) { Vector3 v0 = trans.InverseTransformPoint(lookAtP); v0.y = 0; lookAtP = trans.TransformPoint(v0); transUp = options.useLocalPosition && options.parent != null ? options.parent.up : Vector3.up; } if ((options.lockRotationAxis & AxisConstraint.Y) == AxisConstraint.Y) { Vector3 v0 = trans.InverseTransformPoint(lookAtP); if (v0.z < 0) v0.z = -v0.z; v0.x = 0; lookAtP = trans.TransformPoint(v0); } if ((options.lockRotationAxis & AxisConstraint.Z) == AxisConstraint.Z) { // Fix to allow racing loops to keep cars straight and not flip it if (options.useLocalPosition && options.parent != null) transUp = options.parent.TransformDirection(Vector3.up); else transUp = trans.TransformDirection(Vector3.up); transUp.z = options.startupZRot; } } if (options.mode == PathMode.Full3D) { // 3D path Vector3 diff = lookAtP - trans.position; if (diff == Vector3.zero) diff = trans.forward; newRot = Quaternion.LookRotation(diff, transUp); } else { // 2D path float rotY = 0; float rotZ = Utils.Angle2D(trans.position, lookAtP); if (rotZ < 0) rotZ = 360 + rotZ; if (options.mode == PathMode.Sidescroller2D) { // Manage Y and modified Z rotation rotY = lookAtP.x < trans.position.x ? 180 : 0; if (rotZ > 90 && rotZ < 270) rotZ = 180 - rotZ; } newRot = Quaternion.Euler(0, rotY, rotZ); } break; } if (options.hasCustomForwardDirection) newRot *= options.forward; trans.rotation = newRot; } } }