Skip to content

Commit

Permalink
Refactored the follow object feature to let the user choose if they w…
Browse files Browse the repository at this point in the history
…ant to follow the object position only or if they also want the camera to follow its rotation.
  • Loading branch information
originalnicodr committed Mar 8, 2024
1 parent 4faf58c commit 96efbc3
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 160 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ The original Unity Explorer had a Freecam feature, but even if it was useful at
- Blocked rotation from going further when looking directly up or directly down.

### Follow Object
You can click on the "Follow object" button on the panel and select the object you want to follow on screen or click on the "Follow object" button in the inspector screen for more granularity. Should be useful for creating motion blur, or to use alongside the [camera paths](#camera-paths).
You can click on the "Follow object" button on the panel and select the object you want the camera to follow or click on the "Follow object" button in the inspector screen for more granularity. This can be used as it is, but it's even more useful when playing with [camera paths](#camera-paths), as you can create a path for the camera to walk relative to the object

By default the camera only follows the object's position, but you can also make it follow its rotation as if the camera was physically bound to the object by checking the "Follow Object Rotation" toggle. Should be useful for mimicking a car camera, a character POV, or creating motion blur.

### Game input block for Unity's legacy system
Added game input block for Unity's legacy system. You can now block (or unblock) the game's input when using the freecam, as long as the game is using the Unity Legacy Input system. If the game uses a custom solution or the latest Unity system then this won't work. Implementing this for Unity's new system is in the backlog, so if you find a game using it (should say "Initialized new InputSystem support." on the logs) then please let me know so I can implement it using that game!
Expand Down Expand Up @@ -322,6 +324,10 @@ Building individual configurations from your IDE is fine, though note that the i
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], the ScriptEvaluator from RUE's REPL console was used as the base for UnityExplorer's C# console.
* [Geoffrey Horsington](https://github.com/ghorsington) for [mcs-unity](https://github.com/sinai-dev/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.

# Crediting

Even tho it's not necessary, if you happen to use this mod in the marketing campaign of a game and find it useful please consider including it in the credits!

# Support

If you have found this project fun or useful in any capacity please consider [supporting me on Ko-fi](https://ko-fi.com/originalnicodr). And please also consider donating to the people in the acknowledgments above! As well as the mod loader maintainers.
Expand Down
13 changes: 4 additions & 9 deletions src/Cinematic/Cells/CamPathNodeCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public virtual GameObject CreateContent(GameObject parent)
ButtonRef moveToCameraButton = UIFactory.CreateButton(UIRoot, "Copy Camera pos and rot", "Copy Camera pos and rot");
UIFactory.SetLayoutElement(moveToCameraButton.GameObject, minWidth: 130, minHeight: 25, flexibleWidth: 9999);
moveToCameraButton.OnClick += () => {
point.position = GetCamPathsPanel().followObject != null ? FreeCamPanel.ourCamera.transform.localPosition : FreeCamPanel.ourCamera.transform.position;
point.rotation = GetCamPathsPanel().followObject != null ? FreeCamPanel.ourCamera.transform.localRotation : FreeCamPanel.ourCamera.transform.rotation;
point.rotation = FreeCamPanel.GetCameraRotation();
point.position = FreeCamPanel.GetCameraPosition();
GetCamPathsPanel().controlPoints[index] = point;

GetCamPathsPanel().MaybeRedrawPath();
Expand All @@ -50,13 +50,8 @@ public virtual GameObject CreateContent(GameObject parent)
ButtonRef moveToPointButton = UIFactory.CreateButton(UIRoot, "Move Cam to Node", "Move Cam to Node");
UIFactory.SetLayoutElement(moveToPointButton.GameObject, minWidth: 100, minHeight: 25, flexibleWidth: 9999);
moveToPointButton.OnClick += () => {
if (GetCamPathsPanel().followObject != null){
FreeCamPanel.ourCamera.transform.localPosition = point.position;
FreeCamPanel.ourCamera.transform.localRotation = point.rotation;
} else {
FreeCamPanel.ourCamera.transform.position = point.position;
FreeCamPanel.ourCamera.transform.rotation = point.rotation;
}
FreeCamPanel.SetCameraRotation(point.rotation);
FreeCamPanel.SetCameraPosition(point.position);
FreeCamPanel.ourCamera.fieldOfView = point.fov;
};

Expand Down
131 changes: 109 additions & 22 deletions src/UI/Panels/CamPaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public CamPaths(UIBase owner) : base(owner)
{
controlPoints = new List<CatmullRom.CatmullRomPoint>();
followObject = null;
pathVisualizer = new GameObject("PathVisualizer");
pathVisualizer = null;
time = 10;

// Timer setup
Expand Down Expand Up @@ -69,6 +69,8 @@ public CamPaths(UIBase owner) : base(owner)
public int ItemCount => controlPoints.Count;
private static bool DoneScrollPoolInit;

CamPointsUpdater camPointsUpdater;

public override void SetActive(bool active)
{
base.SetActive(active);
Expand Down Expand Up @@ -248,10 +250,9 @@ void TensionCatmullRom_OnEndEdit(string input)

private void ToggleVisualizePath(bool enable){
// Had to include this check because the pathVisualizer was null for some reason
if (pathVisualizer == null) pathVisualizer = new GameObject("PathVisualizer");
if (enable){
if (controlPoints.Count > 2){
if (followObject != null) pathVisualizer.transform.SetParent(followObject.transform, true);
CreateAndMovePathVisualizer();

UpdateCatmullRomMoverData();
List<CatmullRom.CatmullRomPoint> lookaheadPoints = GetCameraPathsManager().GetLookaheadPoints();
Expand All @@ -265,9 +266,16 @@ private void ToggleVisualizePath(bool enable){
}

Vector3 arrowPos = lookaheadPoints[i].position;
if (followObject != null) arrowPos = followObject.transform.TransformPoint(arrowPos);
if (followObject != null){
if (!FreeCamPanel.followRotationToggle.isOn){
arrowPos = Quaternion.Inverse(FreeCamPanel.followObject.transform.rotation) * arrowPos;
}
// TODO: Avoid using TransformPoint
arrowPos = followObject.transform.TransformPoint(arrowPos);
}
Quaternion arrowRot = lookaheadPoints[i].rotation;
if (followObject != null) arrowRot = followObject.transform.rotation * arrowRot;
if (followObject != null && FreeCamPanel.followRotationToggle.isOn) arrowRot = followObject.transform.rotation * arrowRot;

// We could expose the color of the arrow to a setting
GameObject arrow = ArrowGenerator.CreateArrow(arrowPos, arrowRot, Color.green);
arrow.transform.SetParent(pathVisualizer.transform, true);
Expand All @@ -278,7 +286,8 @@ private void ToggleVisualizePath(bool enable){
else {
if (pathVisualizer) {
UnityEngine.Object.Destroy(pathVisualizer);
pathVisualizer = new GameObject("PathVisualizer");
pathVisualizer = null;
CreateAndMovePathVisualizer();
}
}
}
Expand Down Expand Up @@ -353,11 +362,10 @@ void Stop_OnClick(){
void AddNode_OnClick(){
EventSystemHelper.SetSelectedGameObject(null);

Camera freeCam = FreeCamPanel.ourCamera;
CatmullRom.CatmullRomPoint point = new CatmullRom.CatmullRomPoint(
followObject != null ? freeCam.transform.localPosition : freeCam.transform.position,
followObject != null ? freeCam.transform.localRotation : freeCam.transform.rotation,
freeCam.fieldOfView
FreeCamPanel.GetCameraPosition(),
FreeCamPanel.GetCameraRotation(),
FreeCamPanel.ourCamera.fieldOfView
);

controlPoints.Add(point);
Expand Down Expand Up @@ -387,53 +395,132 @@ private CatmullRom.CatmullRomMover GetCameraPathsManager(){

public void UpdatedFollowObject(GameObject obj){
// Had to include this check because the pathVisualizer was null for some reason
if (pathVisualizer == null) pathVisualizer = new GameObject("PathVisualizer");
if (followObject != null){
TranslatePointsToGlobal();
pathVisualizer.transform.SetParent(null, true);
// destroy the obhject from the parent
GameObject.Destroy(camPointsUpdater);
camPointsUpdater = null;
}
followObject = obj;
if (obj != null){
TranslatePointsToLocal();
pathVisualizer.transform.SetParent(obj.transform, true);
// create the component on the object itself
camPointsUpdater = RuntimeHelper.AddComponent<CamPointsUpdater>(obj, typeof(CamPointsUpdater));
//camPointsUpdater = obj.AddComponent<CamPointsUpdater>();

camPointsUpdater.SetFollowObject(obj);
}
//nodesScrollPool.Refresh(true, false);
}

void TranslatePointsToGlobal() {
public void CreateAndMovePathVisualizer(){
if (pathVisualizer == null)
pathVisualizer = new GameObject("PathVisualizer");

if (followObject != null){
pathVisualizer.transform.position = followObject.transform.position;
if (FreeCamPanel.followRotationToggle.isOn){
Vector3 offset = pathVisualizer.transform.position - followObject.transform.position;
pathVisualizer.transform.position = pathVisualizer.transform.position - offset + followObject.transform.rotation * offset;
pathVisualizer.transform.rotation = followObject.transform.rotation;
}
}
}

public void TranslatePointsPositionToGlobal(){
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
Vector3 newPos = point.position + followObject.transform.position;

CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, point.rotation, point.fov);
newControlPoints.Add(newPoint);
}

controlPoints = newControlPoints;
}

// Also changes position based on rotation
public void TranslatePointsRotationToGlobal(){
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
Vector3 newPos = followObject.transform.TransformPoint(point.position);
Quaternion newRot = followObject.transform.rotation * point.rotation;
Vector3 newPos = followObject.transform.rotation * point.position;

CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, newRot, point.fov);
newControlPoints.Add(newPoint);
}

controlPoints = newControlPoints;
}

public void TranslatePointsPositionToLocal(){
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
Vector3 newPos = point.position - followObject.transform.position;
CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, point.rotation, point.fov);

newControlPoints.Add(newPoint);
}

controlPoints = newControlPoints;
}

void TranslatePointsToLocal() {
// Also changes position based on rotation
public void TranslatePointsRotationToLocal(){
List<CatmullRom.CatmullRomPoint> newControlPoints = new List<CatmullRom.CatmullRomPoint>();
foreach(CatmullRom.CatmullRomPoint point in controlPoints){
Vector3 newPos = followObject.transform.InverseTransformPoint(point.position);
Quaternion newRot = Quaternion.Inverse(followObject.transform.rotation) * point.rotation;
CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, newRot, point.fov);
Vector3 newPos = Quaternion.Inverse(followObject.transform.rotation) * point.position;

CatmullRom.CatmullRomPoint newPoint = new CatmullRom.CatmullRomPoint(newPos, newRot, point.fov);
newControlPoints.Add(newPoint);
}

controlPoints = newControlPoints;
}

void UpdateCatmullRomMoverData(){
void UpdateCatmullRomMoverData() {
GetCameraPathsManager().setClosedLoop(closedLoop);
GetCameraPathsManager().setLocalPoints(followObject != null);
GetCameraPathsManager().setSplinePoints(controlPoints.ToArray());
GetCameraPathsManager().setTime(time);
GetCameraPathsManager().setCatmullRomVariables(alphaCatmullRomValue, tensionCatmullRomValue);

GetCameraPathsManager().CalculateLookahead();
}
}

public class CamPointsUpdater : MonoBehaviour
{
GameObject followObject;

#if CPP
static CamPointsUpdater()
{
ClassInjector.RegisterTypeInIl2Cpp<CamPointsUpdater>();
}

public CamPointsUpdater(IntPtr ptr) : base(ptr) { }
#endif

internal void SetFollowObject(GameObject obj){
followObject = obj;
}

internal void Update()
{
if (followObject == null)
return;

//CamPathsPanel.MaybeRedrawPath();

CamPaths CamPathsPanel = UIManager.GetPanel<CamPaths>(UIManager.Panels.CamPaths);

if (CamPathsPanel.pathVisualizer){
CamPathsPanel.pathVisualizer.transform.position = followObject.transform.position;
if (FreeCamPanel.followRotationToggle.isOn){
Vector3 offset = CamPathsPanel.pathVisualizer.transform.position - FreeCamPanel.followObject.transform.position;
CamPathsPanel.pathVisualizer.transform.position = CamPathsPanel.pathVisualizer.transform.position - offset + FreeCamPanel.followObject.transform.rotation * offset;
CamPathsPanel.pathVisualizer.transform.rotation = followObject.transform.rotation;
}
}
}
}
}
Loading

0 comments on commit 96efbc3

Please sign in to comment.