1
0
mirror of https://github.com/Cardidi/dotween-upm-fork.git synced 2025-12-20 01:06:02 +08:00

249 lines
12 KiB
C#

// 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
{
/// <summary>
/// Path plugin works exclusively with Transforms
/// </summary>
public class PathPlugin : ABSTweenPlugin<Vector3, Path, PathOptions>
{
public const float MinLookAhead = 0.0001f;
public override void Reset(TweenerCore<Vector3, Path, PathOptions> t)
{
t.endValue.Destroy(); // Clear path
t.startValue = t.endValue = t.changeValue = null;
}
public override void SetFrom(TweenerCore<Vector3, Path, PathOptions> t, bool isRelative) {}
public static ABSTweenPlugin<Vector3, Path, PathOptions> Get()
{
return PluginsManager.GetCustomPlugin<PathPlugin, Vector3, Path, PathOptions>();
}
public override Path ConvertToStartValue(TweenerCore<Vector3, Path, PathOptions> t, Vector3 value)
{
// Simply sets the same path as start and endValue
return t.endValue;
}
public override void SetRelativeEndValue(TweenerCore<Vector3, Path, PathOptions> 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<Vector3, Path, PathOptions> t)
{
Transform trans = ((Component)t.target).transform;
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 (!Utils.Vector3AreApproximatelyEqual(path.wps[0], currVal)) {
hasAdditionalStartingP = true;
additionalWps += 1;
}
if (t.plugOptions.isClosedPath) {
Vector3 endWp = path.wps[unmodifiedWpsLen - 1];
if (path.type == PathType.CubicBezier) {
if (unmodifiedWpsLen < 3) {
Debug.LogError(
"CubicBezier paths must contain waypoints in multiple of 3 excluding the starting point added automatically by DOTween" +
" (1: waypoint, 2: IN control point, 3: OUT control point — the minimum amount of waypoints for a single curve is 3)"
);
} else endWp = path.wps[unmodifiedWpsLen - 3];
}
if (endWp != 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.addedExtraStartWp = hasAdditionalStartingP;
path.addedExtraEndWp = hasAdditionalEndingP;
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<Vector3> getter, DOSetter<Vector3> setter, float elapsed, Path startValue, Path changeValue, float duration, bool usingInversePosition, UpdateNotice updateNotice)
{
if (t.loopType == LoopType.Incremental && !options.isClosedPath) {
int increment = (t.isComplete ? t.completedLoops - 1 : t.completedLoops);
if (increment > 0) changeValue = changeValue.CloneIncremental(increment);
}
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) {
int prevWPIndex = t.miscInt;
t.miscInt = newWaypointIndex;
if (t.onWaypointChange != null) {
// If more than one waypoint changed, dispatch multiple callbacks
// bool isBackwards = newWaypointIndex < prevWPIndex;
bool isBackwards = t.isBackwards;
if (t.loopType == LoopType.Yoyo) {
isBackwards = !t.isBackwards && t.loops > 1 && t.completedLoops % 2 != 0
|| t.isBackwards && t.loops > 1 && t.completedLoops % 2 == 0;
}
if (isBackwards) {
// for (int i = prevWPIndex - 1; i > newWaypointIndex - 1; --i) Tween.OnTweenCallback(t.onWaypointChange, i);
for (int i = prevWPIndex - 1; i > newWaypointIndex - 1; --i) Tween.OnTweenCallback(t.onWaypointChange, i);
} else {
// for (int i = prevWPIndex + 1; i < newWaypointIndex + 1; ++i) Tween.OnTweenCallback(t.onWaypointChange, i);
for (int i = prevWPIndex + 1; i < newWaypointIndex; ++i) Tween.OnTweenCallback(t.onWaypointChange, i);
}
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 = ((Component)t.target).transform;
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;
DOTweenExternalCommand.Dispatch_SetOrientationOnPath(options, t, newRot, trans);
//#if RIGIDBODY
// if (options.isRigidbody) ((Rigidbody)t.target).rotation = newRot;
// else trans.rotation = newRot;
//#else
// trans.rotation = newRot;
//#endif
}
}
}