#Make the camera subscribe to the event
1 messages · Page 1 of 1 (latest)
Dal can you help lay out some pseudo for this?
@winter berry What is the cam subbing to, if the camera should be the one dictating whether it's 2d or 3d movement code to be ran?
Depends on the context but your original concern was to remove the necessity of having to reference of the camera with the movement code. My assumption is that movement code would be some controller and camera would subscribe functionalities to events - move left, right, up, down etc. Camera would handle how it behaves and the movement code would handle when it should be occurring - the normal pattern.
sorry just saw this
Yeah maybe I'm just a little confused.
I'm using the OnMove() method from Unity's Input System via "Send Messages".
public void OnMove(InputValue movementValue)
{
Vector2 _movementVector = movementValue.Get<Vector2>();
_movementVector.Normalize();
_movementX = _movementVector.x;
_movementY = _movementVector.y;
Vector3 _camForward = _cameraMainTransform.forward;
Vector3 _camRight = _cameraMainTransform.right;
_camForward.y = 0f;
_camRight.y = 0f;
_camForward.Normalize();
_camRight.Normalize();
//if (3dCamEnabled)
// 3D Movement
//_movement = _camRight * _movementX + _camForward * _movementY;
// 2D Movement - The 'Z' axis is oriented as left and right based on camera orientation.
_movement = new Vector3(0, 0, -_movementX);
}
So I have trigger zones that switch from a 2d cam to 3d cam, but I'm not quite getting how i can switch the movement code as well.
Is this movement relative to the player or the camera?
When it's the "2d" cam enabled, then player should only be allowed to move left/right.
Not sure I understand that question completely.
I'm just trying to understand an event way of doing:
if (3dCam) my3dmovement line of code.
else my 2dmovement line of code
How are you changing between 3d and 2d? If you're disabling/enabling something, you could subscribe behaviors and unsubscribe behaviors when enabling/disabling. Camera's would need to know the controller but the controller (event manager) would not need to know the cameras.
Yes, disabling/enabling CM cameras.
One 3d CM cam, and one 2d CM cam.
I don't know why, but I still can't wrap my head around events. High level, I get it, but I struggle to implement. 😦
I'm not making the connection on how i would be changing the _movement code in that method with the toggling of the cameras via events.
Only a condtional statement with direct references make sense :/ * to me
OnEnable()
Subscribe my movement function to the controller
OnDisable()
Unsubscribe my movement function to the controller```
Give me a sec. Seeing if I can get some mediocre example up and running.
Hopefully it's not soo hard lol 😭
@winter berry I may hop off here soon, but feel free to DM or @ me in UUD.
I had to do something. I'll message you later.
Sounds good. No problem, thanks.
public class PlayerController : MonoBehaviour
{
public event Action<Vector3> OnMove;
private Vector3 inputDirection;
private void Update()
{
inputDirection.x = Input.GetAxis("Horizontal");
inputDirection.y = Input.GetAxis("Vertical");
OnMove(inputDirection.normalized);
}
}``````cs
public class CamController : MonoBehaviour
{
[SerializeField] PlayerController pc;
[SerializeField] Rigidbody rb;
[SerializeField] float speed = 10f;
private void OnEnable()
{
pc.OnMove += Move;
}
private void OnDisable()
{
pc.OnMove -= Move;
}
private void Move(Vector3 movementDirection)
{
var velocity = rb.velocity;
velocity = transform.forward * movementDirection.y + transform.right * movementDirection.x;
velocity.y = rb.velocity.y;
rb.velocity = velocity * speed;
}
}``````cs
public class CameraFocus : MonoBehaviour
{
[SerializeField] GameObject first, second;
private void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent(out PlayerController _))
{
first.SetActive(true);
second.SetActive(false);
}
}
private void OnTriggerExit(Collider other)
{
if (other.TryGetComponent(out PlayerController _))
{
first.SetActive(false);
second.SetActive(true);
}
}
}```I don't know what you're specifically wanting but this is an example of swapping between an orthographic camera and perspective camera. My input is generic and relative to the direction of the virtual camera but you could make it relative to whatever you'd want.
In your case, the new Input System would handle input instead of polling - my example is with a event Action. You would define some means to control the object - likely bound to your camera or set to be active relative to your camera. Not wanting to simply flatten or configure the camera at runtime, I just opted to use two cameras - only one active at any one time.
Thanks @winter berry im going to review a bit later today when I get a break in work. Then I’ll respond.
More appropriate names would have been:
InputManager - in charge of acquiring input
PlayerController - in charge of managing player (on the virtual camera)
The last script was simply a means of allowing me to transition between the two very different cams. With one regular camera type and many virtual camera's, you'd not need to disable/manage the regular camera - I used two regular cams with the two virtual cam.
Ok so couple things that I am a bit lost on:
So PlayerController:
The in the inspector, I have two Cinemachine cams - One for 2d and one for 3d. By turning one on and the other off, CM automatically switches between them smoothly, no problem.
I'm using the CM Trigger Action script (which is basically just the OnTriggerEnter):
So you can see, I can drag in an Event - right now it's just increasing distance for the 2d cam, but I would make this "Change to 3d camera", that's easy.
But I don't see how when I say "Change to 3d cam ALSO switch the line of movement code from 2d to 3d"
I just want "You walked into a trigger that switches to the 3d camera, now the line of code in OnMove() has set the 3d movement code to run: _movement = _camRight * _movementX + _camForward * _movementY;
If you step into a trigger that switches to 2d camera, then enable 2d movement code in OnMove():
_movement = new Vector3(0, 0, -_movementX);
OnMove() is using Send Messages, via the Player Input component.
I'm assuming you're referring to two virtual cams.
Yes, well one is a FreeLook other is Virtual.
So just toggling one on/off will smoothly change no problem.
It's really just only the movement code, that I am trying to link up.
So enable 3d cam, 3d code runs, disable 3d cam, 2d code runs.
Yeah, I'm looking up the doc for TriggerAction https://docs.unity3d.com/Packages/com.unity.cinemachine@2.2/api/Cinemachine.CinemachineTriggerAction.html
Seems the component doesn't have any fields for modifying the referenced action. If you're unable to change the function for the trigger action, perhaps it's possible to work around this by having two components - one disabled.
Usually these Unity components that allow referencing of methods from the inspector would allow us to change their callback referenced function in code as well.
Example: Standard Unity Button allows the referencing of script functions from the inspector and from code.
Well you can call any function from it? It's just a Unity Event.
Am I misunderstanding?
That's just a function/script I made to increase distance.
I'm referring to replacing the referenced function through code.
I'm guessing that's what you're wanting? To be able to change out the referred movement function?
Exactly. Roll into Trigger Zone, it enables 3d camera (i got that portion down), but I need to switch to 3d movement code.
Either your could attach a intermediate function that manages an event/Action itself or have use multiple of these components.
I think it may be worthwhile to implement the first.
I'm specifically doing this to force myself to understand how to set up Actions, so I'm not exactly sure how to do it at the moment.
Basically a simple script with some UnityAction (possibly a collection of such) to handle the managing of the callback function so that this trigger action component does not need further interaction (changing). You'd then just manipulate your script component etc.
I wish I could say some of that made sense to me.
public void OnMove(InputValue movementValue)
{
Vector2 _movementVector = movementValue.Get<Vector2>();
_movementVector.Normalize();
_movementX = _movementVector.x;
_movementY = _movementVector.y;
Vector3 _camForward = _cameraMainTransform.forward;
Vector3 _camRight = _cameraMainTransform.right;
_camForward.y = 0f;
_camRight.y = 0f;
_camForward.Normalize();
_camRight.Normalize();
// 3D Movement
_movement = _camRight * _movementX + _camForward * _movementY;
// 2D Movement - The 'Z' axis is oriented as left and right based on camera orientation.
//_movement = new Vector3(0, 0, -_movementX);
}
I'll try to build a test sample more similar to yours.
Ok even pseudo is fine, but yes the parts are:
- OnMove() is using Unity Input System, via Send Messages.
a. Within it, I need to switch between 3d movement and 2d movement based on which camera is active. - I have a Freelook camera and a Virtual Camera. When turning on or off one of them, CM will seamlessly switch between.
a. I already this working with the Trigger zones.
The easy solution might be to simply have to TriggerActions for the two different behaviors (32/2d)
Sure, I'm fine with that, but how does that also change the movement code?
Else you would have to either be able to swap out the TriggerAction's referenced method during runtime or have the TriggerAction reference a single method and have that method invoke subscribed actions (it would be managing an event/action of it's own).
Yeah I think two TriggerActions sounds the best...
But let me know if you understand how i would get the 2d/3d movement switched, im still not grasping that.
public class PlayerController : MonoBehaviour
{
public UnityEvent<Vector3> OnMove;
public GameObject activeCam;
private Vector3 inputDirection;
private void Update()
{
inputDirection.x = Input.GetAxis("Horizontal");
inputDirection.y = Input.GetAxis("Vertical");
OnMove.Invoke(inputDirection.normalized);
}
}``````cs
public class MoveManager : MonoBehaviour
{
[SerializeField] Rigidbody rb;
[SerializeField] float speed = 10f;
public UnityEvent<Vector3> Move;
public void MoveXAxis(Vector3 input)
{
var velocity = rb.velocity;
velocity.x = input.x * speed;
rb.velocity = velocity;
}
public void MoveForward(Vector3 input)
{
var velocity = input.y * speed * rb.transform.forward;
velocity.y = rb.velocity.y;
rb.velocity = velocity;
rb.rotation *= Quaternion.Euler(0f, input.x, 0f);
}
public void MoveLocal(Vector3 input)
{
Vector3 velocity = input.x * speed * rb.transform.right + input.y * speed * rb.transform.forward;
velocity.y = rb.velocity.y;
rb.velocity = velocity;
}
public void MoveGlobal(Vector3 input)
{
var velocity = Vector3.right * input.x * speed + Vector3.forward * input.y * speed;
velocity.y = rb.velocity.y;
rb.velocity = velocity;
}
private void OnEnable()
{
var pc = rb.GetComponent<PlayerController>();
pc.OnMove = Move;
if(pc.activeCam != null)
pc.activeCam.SetActive(false);
pc.activeCam = gameObject;
}
}``````cs
public class TriggerActivate : MonoBehaviour
{
[SerializeField] GameObject target;
public void ActivateCam() => target.SetActive(true);
}```
You could insert a conditional branch (if-statement, switch, etc) there or reference a function and invoke it there.
The alternative I thought you wanted was to have completely different behaviors that might call for multiple move functions being subscribed/unsubscribed.
An if-statement would make subscribing OnMove obsolete and tightly couple the logic there.. ```cs
public void OnMove(InputValue movementValue)
{
Vector2 _movementVector = movementValue.Get<Vector2>();
_movementVector.Normalize();
_movementX = _movementVector.x;
_movementY = _movementVector.y;
Vector3 _camForward = _cameraMainTransform.forward;
Vector3 _camRight = _cameraMainTransform.right;
_camForward.y = 0f;
_camRight.y = 0f;
_camForward.Normalize();
_camRight.Normalize();
if(...)
// 3D Movement
_movement = _camRight * _movementX + _camForward * _movementY;
else
// 2D Movement - The 'Z' axis is oriented as left and right based on camera orientation.
_movement = new Vector3(0, 0, -_movementX);
}
Invoking a method could decouple some logic there and would look like...cs
public void OnMove(InputValue movementValue)
{
...
_movement = GetMovement.Invoke();//Event, action ect
}```Else use multiple OnMove for vastly different systems and OnMove would be subscribed Both of the above methods don't really make use of OnMove as an event but perhaps you aren't needing events 🤷♂️
It may not look like it but entering the sphere colliders completely changes the movement system in my illustration. The top right sphere only had movement availability in the x-axis so I had to drag the object to reactivate the other systems.
What I did in my MoveManager was reference a movement type. This component was added to each virtual camera and upon activation, subscribed it's movement system to the PlayerController's event pc.OnMove = Move.
@winter berry Yes, I do not want to couple with an IF statement. That's my whole point of this exercise 😛
I'm not sure I entirely understand all the code. Think I'm getting a bit confused due to OnMove() being the Unity Input System function that is using Send Messages. I don't entirely even understand how that works. For example, i don't have any logic in Update, besides limiting my speed.
Maybe I just want:
public void OnMove(InputValue movementValue)
{
...
_movement = GetMovement.Invoke();//Event, action ect
}
Yeah, I was merely too lazy to setup the new Input System and simply polled my input. The Input System should allow you to change what happens on some sort of action performed subscriber.
Random example from the net:
Yours would probably assign your movement action to the listener.
Then when the listener detects an action performed, it'd just execute the subscribed function (3d free move, 2d horizontal etc)