- Home /
MainCamera, LookAt script and activation tracks in timeline
I have a problem with a script and timeline.
I have several hundred objects in my scene used to label items. When the camera moves through the scene the objects point towards it for visibility, using the following:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class alwaysFaceCamera : MonoBehaviour
{
private Transform camera;
void Start()
{
camera = Camera.main.transform;
}
void Update()
{
transform.LookAt(camera);
}
}
Which seems very straightforward. However, now I would like to make the sequence more useful (meaning able to see more parts of the scene) by using several cameras in sequence.
To do this I have simply set up a list of cameras as animation tracks in Unity timeline. Each are deactivated in the inspector until activated by the timeline, each has the tag MainCamera (because they are deactivated until the activation track activates them).
The sequence of cameras works fine and is very flexible, but the problem is the LookAt script no longer works. It finds the first camera and then stops. I think this may be because the main camera is being set in void Start(); and is not being updated. I'm now not clear about how a script such as the above should interact with the timeline (TBH I thought activating and deactivating the cameras in the timeline would simply work because there would only be one active main camera in the scene at a time).
Any help appreciated, I should probably mention 97% of my time is spent doing art and about 3% coding. Thanks.
Answer by andrew-lukasik · May 24, 2021 at 09:47 PM
Solution B, Scripts:
LookAtCamera.cs
using System.Collections.Generic;
using UnityEngine;
[ExecuteAlways]
public class LookAtCamera : MonoBehaviour
{
public static List<LookAtCamera> instances = new List<LookAtCamera>();
void OnEnable () => instances.Add( this );
void OnDisable () => instances.Remove( this );
}
LookAtCameraSystem.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
[ExecuteAlways]
public class LookAtCameraSystem : MonoBehaviour
{
[SerializeField] Method _method = Method.LookAt;
static LookAtCameraSystem _instance = null;
void Awake ()
{
if( _instance==null )
_instance = this;
else
Debug.LogError($"There is more that one instance of {nameof(LookAtCameraSystem)}, this will result in reduced performance and undefined behaviour.",gameObject);
}
void OnEnable () => RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;
void OnDisable () => RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
void OnBeginCameraRendering ( ScriptableRenderContext context , Camera camera )
{
if( _method==Method.LookAt )
ForeachLookAt( LookAtCamera.instances , camera );
else if( _method==Method.Align )
ForeachAlign( LookAtCamera.instances , camera );
}
void ForeachLookAt ( IList<LookAtCamera> list , Camera camera )
{
for( int i=0 ; i<list.Count ; i++ )
list[i].transform.LookAt( worldPosition:camera.transform.position , worldUp:camera.transform.up );
}
void ForeachAlign ( IList<LookAtCamera> list , Camera camera )
{
var rotation = Quaternion.LookRotation( forward:-camera.transform.forward , upwards:-camera.transform.up );
for( int i=0 ; i<list.Count ; i++ )
list[i].transform.rotation = rotation;
}
public enum Method { LookAt = 0 , Align = 1 }
}
LookAtCamera
is intended to work as kind of a tag whereLookAtCameraSystem
is a system that processes all of the tag instances and should be considered asingleton
, so create a single instance of this component in your scene and no more.Note: Remove
[ExecuteAlways]
attribute or disable localLookAtCameraSystem
instance if you don't want it to execute in scene view while working.
Solution A, Shaders:
Replacing this script with a "mesh look at camera" shader will resolve this issue auto-magically.
https://i.imgur.com/LpECGOq.jpg
Note:
This shader works fine while object rotation is zero-ed and scale is uniform. Removing this requirement is technically possible but introduces silly and totally unnecessary calculations (in shader graph case). So, just make sure rotation is ( 0 , 0 , 0 )
, scale is (1,1,1)
(or (1.5,1.5,1.5)
) and carry on.
@andrew-lukasik Many thanks for this! Must admit I would not have even thought about a shader as a possible solution, so this opens up a lot of new possibilities to explore too. Setting rotations to zero should not be an issue.
Just one thing... Don't suppose you have a higher res version of the screengrab? The web page isn't allowing me to enlarge it much, it's a bit hard to read! :)
(@moderators, a feature similar to the way of displaying code but for graphs might be worth thinking about for the future? Cheers).
(Actually please ignore last request - it upscales nicely in Photoshop!)
Thanks for adding the link. Unfortunately this isn't working for me. Your shader graph was very clear and I believe I've replicated it without errors :) however the object I've applied it to as a test is not reacting. Clearing out my assumptions first: I've created this as an HDRP lit shader graph, second I've applied it to an object that's a child of an FBX import, third I'm assu$$anonymous$$g this will only work at runtime.
I think I understand most of the script and I would have thought it should work in my context, so I can only imagine it's something missing / incorrectly applied in my specific project. If you happened to notice something without spending too much time it would be really appreciated.
First, thanks again for the help - really appreciated. In fact you did mention the scale issue, but I'd dived straight into the graph and missed it. Creating a correctly sized object in a DCC app won't be a problem.
But TextMesh... Drat. I was going to point out the TextMesh element is only parented to the mesh with the shader and wondered if this would make a difference - I'll run a quick test to see but I can imagine why that would be an issue. I'll have to keep looking I suspect.
I updated my answer to give you a script-based solution too. It will work everywhere as it's recalculated for every camera on your scene (editor view too).
Sir, you are a legend! That works perfectly. The shader was impressive in itself, I see why the TextMesh element wouldn't work but even so that's a great graph to have and experiment with in future. The scripts though solve all issues exactly. Thank you very much indeed!
As a bonus I expanded LookAtCameraSystem
a tiny bit and added an option to switch between 2 different rotation methods. This new one might be a better suited for your case as it makes objects look more orderly.
Thank you! Just seen this and I'll try it in the scene shortly. In one particular use case (looking down on the scene from a helicopter perspective) the first version already produces better results than my original LookAt script in that the labels all keep the same horizontal alignment, which makes them more legible. Double bonus!