Files
pgs/Assets/ProCamera2D/Code/Extensions/ProCamera2DRails.cs

276 lines
10 KiB
C#
Raw Normal View History

2026-02-21 16:58:22 -08:00
using UnityEngine;
using System.Collections.Generic;
namespace Com.LuisPedroFonseca.ProCamera2D
{
public enum FollowMode
{
BothAxis,
HorizontalAxis,
VerticalAxis
}
#if UNITY_5_3_OR_NEWER
[HelpURLAttribute("http://www.procamera2d.com/user-guide/extension-rails/")]
#endif
public class ProCamera2DRails : BasePC2D, IPreMover
{
public static string ExtensionName = "Rails";
[HideInInspector]
public List<Vector3> RailNodes = new List<Vector3>();
public FollowMode FollowMode;
public List<CameraTarget> CameraTargets = new List<CameraTarget>();
Dictionary<CameraTarget, Transform> _cameraTargetsOnRails = new Dictionary<CameraTarget, Transform>();
List<CameraTarget> _tempCameraTargets = new List<CameraTarget>();
KDTree _kdTree;
override protected void Awake()
{
base.Awake();
_kdTree = KDTree.MakeFromPoints(RailNodes.ToArray());
for (int i = 0; i < CameraTargets.Count; i++)
{
var railTransform = new GameObject(CameraTargets[i].TargetTransform.name + "_OnRails").transform;
_cameraTargetsOnRails.Add(
CameraTargets[i],
railTransform
);
var cameraTarget = ProCamera2D.AddCameraTarget(railTransform);
cameraTarget.TargetOffset = CameraTargets[i].TargetOffset;
}
if (CameraTargets.Count == 0)
enabled = false;
ProCamera2D.AddPreMover(this);
Step();
}
protected override void OnDestroy()
{
base.OnDestroy();
if(ProCamera2D)
ProCamera2D.RemovePreMover(this);
}
#region IPreMover implementation
public void PreMove(float deltaTime)
{
if (enabled)
Step();
}
public int PrMOrder { get { return _prmOrder; } set { _prmOrder = value; } }
int _prmOrder = 1000;
#endregion
void Step()
{
var pos = Vector3.zero;
for (int i = 0; i < CameraTargets.Count; i++)
{
switch (FollowMode)
{
case FollowMode.HorizontalAxis:
pos = VectorHVD(
Vector3H(CameraTargets[i].TargetPosition) * CameraTargets[i].TargetInfluenceH,
Vector3V(ProCamera2D.LocalPosition),
0);
break;
case FollowMode.VerticalAxis:
pos = VectorHVD(
Vector3H(ProCamera2D.LocalPosition),
Vector3V(CameraTargets[i].TargetPosition) * CameraTargets[i].TargetInfluenceV,
0);
break;
case FollowMode.BothAxis:
pos = VectorHVD(
Vector3H(CameraTargets[i].TargetPosition) * CameraTargets[i].TargetInfluenceH,
Vector3V(CameraTargets[i].TargetPosition) * CameraTargets[i].TargetInfluenceV,
0);
break;
}
_cameraTargetsOnRails[CameraTargets[i]].position = GetPositionOnRail(pos);
}
}
/// <summary>Add a target for the camera to follow on rails.</summary>
/// <param name="targetTransform">The Transform of the target</param>
/// <param name="targetInfluenceH">The influence this target horizontal position should have when calculating the average position of all the targets</param>
/// <param name="targetInfluenceV">The influence this target vertical position should have when calculating the average position of all the targets</param>
/// <param name="targetOffset">A vector that offsets the target position that the camera will follow</param>
/// <param name="duration">The time it takes for this target to reach it's influence. Use for a more progressive transition.</param>
public void AddRailsTarget(Transform targetTransform, float targetInfluenceH = 1f, float targetInfluenceV = 1f, Vector2 targetOffset = default(Vector2), float duration = 0f)
{
if (GetRailsTarget(targetTransform) != null)
return;
var newCameraTarget = new CameraTarget()
{
TargetTransform = targetTransform,
TargetInfluenceH = targetInfluenceH,
TargetInfluenceV = targetInfluenceV,
TargetOffset = targetOffset
};
CameraTargets.Add(newCameraTarget);
var railTransform = new GameObject(targetTransform.name + "_OnRails").transform;
_cameraTargetsOnRails.Add(
newCameraTarget,
railTransform
);
ProCamera2D.AddCameraTarget(railTransform, targetInfluenceH, targetInfluenceV, duration, targetOffset);
enabled = true;
}
/// <summary>Removes a target from being followed on the rails</summary>
/// <param name="targetTransform">The Transform of the target</param>
public void RemoveRailsTarget(Transform targetTransform)
{
var cameraTarget = GetRailsTarget(targetTransform);
if (cameraTarget != null)
{
CameraTargets.Remove(cameraTarget);
ProCamera2D.RemoveCameraTarget(_cameraTargetsOnRails[cameraTarget]);
Destroy(_cameraTargetsOnRails[cameraTarget].gameObject);
}
}
/// <summary>Gets the corresponding CameraTarget from an object's transform.</summary>
/// <param name="targetTransform">The Transform of the target</param>
public CameraTarget GetRailsTarget(Transform targetTransform)
{
for (int i = 0; i < CameraTargets.Count; i++)
{
if (CameraTargets[i].TargetTransform.GetInstanceID() == targetTransform.GetInstanceID())
{
return CameraTargets[i];
}
}
return null;
}
/// <summary>Disables the rails targets. This method is used by the Rails trigger</summary>
/// <param name="transitionDuration">The time it takes to disable the targets</param>
public void DisableTargets(float transitionDuration = 0f)
{
if(_tempCameraTargets.Count != 0)
return;
for (int i = 0; i < _cameraTargetsOnRails.Count; i++)
{
ProCamera2D.RemoveCameraTarget(_cameraTargetsOnRails[CameraTargets[i]], transitionDuration);
_tempCameraTargets.Add(ProCamera2D.AddCameraTarget(CameraTargets[i].TargetTransform, CameraTargets[i].TargetInfluenceH, CameraTargets[i].TargetInfluenceV, transitionDuration, CameraTargets[i].TargetOffset));
}
}
/// <summary>Enables the rails targets. This method is used by the Rails trigger</summary>
/// <param name="transitionDuration">The time it takes to enable the targets</param>
public void EnableTargets(float transitionDuration = 0f)
{
for (int i = 0; i < _tempCameraTargets.Count; i++)
{
ProCamera2D.RemoveCameraTarget(_tempCameraTargets[i].TargetTransform, transitionDuration);
ProCamera2D.AddCameraTarget(_cameraTargetsOnRails[CameraTargets[i]], 1, 1, transitionDuration);
}
_tempCameraTargets.Clear();
}
Vector3 GetPositionOnRail(Vector3 pos)
{
var closestNode = _kdTree.FindNearest(pos);
if (closestNode == 0)
{
return GetPositionOnRailSegment(RailNodes[0], RailNodes[1], pos);
}
else if (closestNode == RailNodes.Count - 1)
{
return GetPositionOnRailSegment(RailNodes[RailNodes.Count - 1], RailNodes[RailNodes.Count - 2], pos);
}
else
{
var leftSeg = GetPositionOnRailSegment(RailNodes[closestNode - 1], RailNodes[closestNode], pos);
var rightSeg = GetPositionOnRailSegment(RailNodes[closestNode + 1], RailNodes[closestNode], pos);
if ((pos - leftSeg).sqrMagnitude <= (pos - rightSeg).sqrMagnitude)
return leftSeg;
else
return rightSeg;
}
}
Vector3 GetPositionOnRailSegment(Vector3 node1, Vector3 node2, Vector3 pos)
{
var node1ToPos = pos - node1;
var segmentDirection = (node2 - node1).normalized;
float node1Dot = Vector3.Dot(segmentDirection, node1ToPos);
if (node1Dot < 0f)
{
return node1;
}
else if (node1Dot * node1Dot > (node2 - node1).sqrMagnitude)
{
return node2;
}
else
{
var fromNode1 = segmentDirection * node1Dot;
return node1 + fromNode1;
}
}
#if UNITY_EDITOR
override protected void DrawGizmos()
{
base.DrawGizmos();
var cameraDimensions = Utils.GetScreenSizeInWorldCoords(ProCamera2D.GameCamera, Mathf.Abs(Vector3D(transform.localPosition)));
Gizmos.color = EditorPrefsX.GetColor(PrefsData.RailsColorKey, PrefsData.RailsColorValue);
if(_tempCameraTargets.Count > 0)
Gizmos.color = Gizmos.color * .7f;
// Rails nodes
for (int i = 0; i < RailNodes.Count; i++)
{
Gizmos.DrawWireCube(RailNodes[i], cameraDimensions * .01f);
}
// Rails path
if (RailNodes.Count >= 2)
{
for (int i = 0; i < RailNodes.Count - 1; i++)
{
Gizmos.DrawLine(RailNodes[i], RailNodes[i + 1]);
}
}
}
#endif
}
}