Hi Isaac, Thanks for the kind words. I'm replying publicly to your PM because I think the topic is of general interest. The FreeLook negative-radius hack from earlier versions is now outdated. We're working on a POV rig, but until it's ready there are a couple of ways to create POV cameras with Cinemachine. 1. Create a simple vcam, with Body set to HardLockToTarget on the character's head, and Aim set to POV, like this: 2. Or, if you want to use the standard FirstPersonCharacterController, you can do it like this: Drag the FPSController or the RigibBodyFPSController prefab into your scene. The prefab includes an embedded Camera, meant to replace the Main Camera. You need to keep your original Main Camera, and replace the embedded Camera with a Cinemachine Virtual Camera. To do this, you disable (not delete) the embedded Camera component, and add a Cinemachine Virtual Camera component in its place, like this: Make sure that your actual Main Camera has a CinemachineBrain on it (this will happen automatically if you've used the Cinemachine menu to create a Virtual Camera). You can add the Brain component onto the main camera if you don't have it there already. In the image above, I've untagged the embedded MainCamera. It would probably be a good idea to rename it as well. Two objects called MainCamera can be confusing. You now have a vcam that is controlled by the FirstPersonController. It will play nicely with the other vcams in your scene (e.g. blending, timeline, post-processing, etc).
Thanks, Gregory! This was an epic way to start the week! Thank you for taking the time to explain this. I know other users will be just as happy once they find this workaround. Can't wait to get started!!
I just found that this way wouldn’t work correctly for FPS Controller (camera will move left and right only).
@KosmoDED It works, you have probably not set it up correctly. Can you post images of your vcam inspector?
Camera moving only left and right. As i said, it happens only with FPS Controller ( Rigidbody FPS Controller working well).
Looks like you're using an old version of CM. Upgrade to the latest, and follow the instructions exactly - e.g. "Do Nothing" in Aim and Body, not "Hard Constraint".
Thank you for mentioning that I use previous version of CM. I updated it. Tried again. Unfortunately, result is the same: RigidBodyFPSController working well with cinemachine, timeline etc. FPSController working too, but camera move only horizontally. I installed latest beta of Unity and latest Standard Character Asset. I suspect that problem is in FPS controller script.
I nailed it! FirstPersonController: (you can make changes in original script but i created new one. Unfortunately i need to set all properties ( Walk speed, Run speed etc.) manually in this case. I made m_Camera public. In Start() function disabled one line. Aded Camera from FPS controller manualy. P.S. Now i can use FPS controller and its camera as cinemachine Virtual Camera camera 100 %.
Hi Gregoryl! Is this still the best way to configure a First Person Camera with Cinemachine? Or there were / there will be some advance on that subject on the latest releases? Thanks a lot!
@cfree The best way is still to take a working FPS camera controller and replace the Camera component with a "Do Nothing" vcam. That opens up the FPS controller to all the benefits of Cinemachine: Timeline, blending, Collider, Noise, etc.
I'm going through this .. and still don't see how can i use cinemachine in a 1st person game, if I make a vCM that follows the player with POV aim then follow is fine but looking around won't make the gun move with the camera. and if I make vCM that is only POV aim and i child the gun in the main camera then in case of cutscenes the gun will fly with the camera to.. basically anywhere... isn't there a normal solution?
Very excited to see it on official Cinemachine roadmap. Cinemachine is so awesome, i am looking forward to see its usage on first person scenarios. @Adam_Myhill @Gregoryl Please, any news on that? Is it really being worked? Thanks!
Thank you @Gregoryl for the guidance on how to configure cinemachine for FPS POV. This help tremendously!
Yes thank you @Gregoryl, I was struggling a bit to figure this out on my own! Any way to make this part of an official tutorial of upgrading the FirstPersonController to use Cinemachine?
I have a suggestion. It would be great if you could ignore or lock rotations in the AIM | POV settings. I just need a simple lock aim Y rotation to the follow target...but keep the vertical and X rotations the same as the POV settings. When I try to implement my own solution on top of cinemachine and choose "Do Nothing" for all options, the camera wont let me change its position or rotation.... it just locks in place. Tried it in Update, FixedUpdate and LateUpdate.
You can do that very simply with a custom CinemachineExtension that overrides the Y rotation at the end of the CM pipeline. Enable it when needed.
For those Interested...you can change the Raw orientation and position on the States in extension classes...this occurs after the vcam script does its calculations. Thanks @Gregoryl Code (CSharp): using UnityEngine; using Cinemachine; public class CinemachineFPOVExtension : CinemachineExtension { protected override void Awake() { base.Awake(); ConnectToVcam(true); } protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime) { if (vcam.Follow) { if (stage == CinemachineCore.Stage.Aim) { var followRot = vcam.Follow.rotation.eulerAngles; var camRot = state.RawOrientation.eulerAngles; state.RawOrientation = Quaternion.Euler(new Vector3(camRot.x, followRot.y, camRot.z)); } } } }
Hiya @KosmoDED I'm running into this same issue :/ I'm using the Rigid Body First Person Controller instead (seems you were using the other one?) and can't seem to get this to work. Any insights you could maybe give looking at it? I'd really appreciate it! -craigz Code (CSharp): using System; using UnityEngine; using UnityStandardAssets.CrossPlatformInput; namespace UnityStandardAssets.Characters.FirstPerson { [RequireComponent(typeof (Rigidbody))] [RequireComponent(typeof (CapsuleCollider))] public class RigidbodyFirstPersonController : MonoBehaviour { [Serializable] public class MovementSettings { public float ForwardSpeed = 8.0f; // Speed when walking forward public float BackwardSpeed = 4.0f; // Speed when walking backwards public float StrafeSpeed = 4.0f; // Speed when walking sideways public float RunMultiplier = 2.0f; // Speed when sprinting public KeyCode RunKey = KeyCode.LeftShift; public float JumpForce = 30f; public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f)); [HideInInspector] public float CurrentTargetSpeed = 8f; #if !MOBILE_INPUT private bool m_Running; #endif public void UpdateDesiredTargetSpeed(Vector2 input) { if (input == Vector2.zero) return; if (input.x > 0 || input.x < 0) { //strafe CurrentTargetSpeed = StrafeSpeed; } if (input.y < 0) { //backwards CurrentTargetSpeed = BackwardSpeed; } if (input.y > 0) { //forwards //handled last as if strafing and moving forward at the same time forwards speed should take precedence CurrentTargetSpeed = ForwardSpeed; } #if !MOBILE_INPUT if (Input.GetKey(RunKey)) { CurrentTargetSpeed *= RunMultiplier; m_Running = true; } else { m_Running = false; } #endif } #if !MOBILE_INPUT public bool Running { get { return m_Running; } } #endif } [Serializable] public class AdvancedSettings { public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this ) public float stickToGroundHelperDistance = 0.5f; // stops the character public float slowDownRate = 20f; // rate at which the controller comes to a stop when there is no input public bool airControl; // can the user control the direction that is being moved in the air [Tooltip("set it to 0.1 or more if you get stuck in wall")] public float shellOffset; //reduce the radius by that ratio to avoid getting stuck in wall (a value of 0.1f is nice) } public Camera cam; public MovementSettings movementSettings = new MovementSettings(); public MouseLook mouseLook = new MouseLook(); public AdvancedSettings advancedSettings = new AdvancedSettings(); private Rigidbody m_RigidBody; private CapsuleCollider m_Capsule; private float m_YRotation; private Vector3 m_GroundContactNormal; private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded; public Vector3 Velocity { get { return m_RigidBody.velocity; } } public bool Grounded { get { return m_IsGrounded; } } public bool Jumping { get { return m_Jumping; } } public bool Running { get { #if !MOBILE_INPUT return movementSettings.Running; #else return false; #endif } } private void Start() { m_RigidBody = GetComponent<Rigidbody>(); m_Capsule = GetComponent<CapsuleCollider>(); mouseLook.Init (transform, cam.transform); } private void Update() { RotateView(); if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump) { m_Jump = true; } } private void FixedUpdate() { GroundCheck(); Vector2 input = GetInput(); if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded)) { // always move along the camera forward as it is the direction that it being aimed at Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x; desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized; desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed; desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed; desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed; if (m_RigidBody.velocity.sqrMagnitude < (movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed)) { m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse); } } if (m_IsGrounded) { m_RigidBody.drag = 5f; if (m_Jump) { m_RigidBody.drag = 0f; m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z); m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse); m_Jumping = true; } if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f) { m_RigidBody.Sleep(); } } else { m_RigidBody.drag = 0f; if (m_PreviouslyGrounded && !m_Jumping) { StickToGroundHelper(); } } m_Jump = false; } private float SlopeMultiplier() { float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up); return movementSettings.SlopeCurveModifier.Evaluate(angle); } private void StickToGroundHelper() { RaycastHit hitInfo; if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo, ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.stickToGroundHelperDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore)) { if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f) { m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal); } } } private Vector2 GetInput() { Vector2 input = new Vector2 { x = CrossPlatformInputManager.GetAxis("Horizontal"), y = CrossPlatformInputManager.GetAxis("Vertical") }; movementSettings.UpdateDesiredTargetSpeed(input); return input; } private void RotateView() { //avoids the mouse looking if the game is effectively paused if (Mathf.Abs(Time.timeScale) < float.Epsilon) return; // get the rotation before it's changed float oldYRotation = transform.eulerAngles.y; mouseLook.LookRotation (transform, cam.transform); if (m_IsGrounded || advancedSettings.airControl) { // Rotate the rigidbody velocity to match the new direction that the character is looking Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up); m_RigidBody.velocity = velRotation*m_RigidBody.velocity; } } /// sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom private void GroundCheck() { m_PreviouslyGrounded = m_IsGrounded; RaycastHit hitInfo; if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo, ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore)) { m_IsGrounded = true; m_GroundContactNormal = hitInfo.normal; } else { m_IsGrounded = false; m_GroundContactNormal = Vector3.up; } if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping) { m_Jumping = false; } } } }
Cinemachine is working so well in so many cases It works great for POV and I was able to set it up in minutes. I used a follow instead of hard track with damping set to zero so that I could offset the z a touch to simulate how your head leans over the front of your chest a bit when you look down. CinemachinePipeline even auto handled the transition between my two cams, and I like the proxy architecture of having the cameras be virtual cams that control the main camera isnread of being attached to it themselves. This and the UI system are two of my favourite Unity extensions because they have so many options when you come up with a new problem.
How did you managed to prevent the camera from looking at the character's body and rotating weirdly? I started working with Cinemachine a couple of days ago and I'm kinda lost here.. This is my current Virtual Camera settings. If you could explain what I'm doing wrong here it would be really helpful. Thanks in advance!
@bmcapereira looks like you're using an outdated version of CM. We are now up to 2.6.0-preview.2. What version are you using?
I just let it look at the body. I turn the body to face the camera direction so if I look down I just see my feet running and I can see my hands jogging in front of me. I hide the head which is a separate mesh so that I don’t see it from my point of view. If you position the camera right it’s just like looking at your own body. I also have an option to hide the body completely which is probably the simplest option.
Thanks for the early reply. Ok, I've already upgraded to the lastest version. I want to limit how much the camera can rotate, so I can proper simulate the character's head. Should I use the Axis Value Range for that? Aren't those values relative to the mouse inicial position. I may be confused.