With that, we've got the base of our Movement done, but if we take a look at Genshin Impact, we quickly notice that this
isn't how Movement works there. Not only does the Player rotate
towards the Movement Direction, that Movement Direction is relative
to the Camera and not to the Input. If we press "W" to move forwards, we'll always
move forwards towards where we are looking at. That also means that if we
press "S", we'll move backwards, or to the opposite of where we are looking at. To be able to do the same, we
need to create a Camera System. Thankfully for us, we're going to be using
"Cinemachine", which easily allows us to add one. This seems to be what Genshin Impact uses as well. We've already downloaded it
when setting up the project, so we can now easily create our Camera
by right clicking in the Hierarchy Window and going to "Cinemachine > Virtual Camera". I'll name it "PlayerCamera". While I was able to somewhat replicate
Genshin Camera with a "FreeLook" camera, there was a missing option that we would
need to add ourselves and apparently "Virtual Cameras" are more performant as they
only use 1 rig while "FreeLook Cameras" use 3. To start off with our Camera, we need a
target for the "Follow" and "Look At" fields. "Follow" is the target that
our Camera will follow around. "Look At" is the target that
our Aim will be relative to. By "relative" here, I mean that the center
of our Aim will always be on the target. To add these, select our "Player"
Game Object and add a new child Game Object named "CameraLookPoint". I'll add an icon to this game object so
that we can easily see where it's placed. Then, I'll make its "Y" axis be at "1.4". When that's done, we'll set our
Camera targets to be this new object. So, back into our "PlayerCamera", drag this new
Game Object to the "Follow" and "Look At" fields. Note that Cinemachine should add a Cinemachine
Brain to your Main Camera automatically, but if it didn't, make sure you do it yourself. This simply makes it so Cinemachine
Cameras can control your Main Camera. Our Camera is now looking
towards our Camera Look Point, but it's still using the old Input System,
so our current mouse Input won't work. Thankfully, it's quite simple to
swap it with the new Input System, as all that we need to do is to add a new
component named "Cinemachine Input Provider". This component accepts an "XY" axis, which will represent the camera rotation
and a "Z" axis, which will represent the distance. Of course, to assign an Input
here we need to create it first. So, go ahead and open the Input Actions Window. In here, lets first create a new
Action for our mouse movement by pressing on the plus (+) icon
and naming it "Look". For our types, we'll have it
be a "Value" of "Vector2", as we want to constantly get our X and Y axis. In its binding, select "Mouse > Delta". Delta is apparently the change in pixels in Vector2 since the
last rendered frame mouse position. For our "Zoom", we'll add another action
named "Zoom" and set it to "Value" as well. We simply want a float here to know
whether we are scrolling up or down. The Value Type for float is called "Axis". Now, we'll use the value this input returns
to know how much we should scroll up or down but it currently comes with high values such
as "120" so we'll clamp it to a lower value. To do that, add a "Processor" of type "Clamp"
and set it to be from "-0.1" to "0.1". This Input also comes inverted, which
means scrolling up would return positive and scrolling down would return negative. We want the opposite of that, so add yet another
"Processor" but this time of type "Invert". When that's done, set its
Binding to be "Mouse > Scroll Y". Then, save the asset. Back in our "PlayerCamera", in the Input Provider
Component, select the corresponding Inputs, so "Look" for the "XY" axis and
then "Zoom" for the "Z" axis. We're now using the new
Input System for our Camera. We're only a few settings away of having
our Camera behaving like Genshin's camera. The first thing we'll be
doing is set our FOV to "60". The next thing is setting both
the camera "Body" and "Aim". The "Body" sets the algorithm that the
Camera uses to "Follow" its target. The "Aim" sets the algorithm that the
Camera uses to "Look At" its target. If we take a look at Genshin Impact camera, we
can see that we can rotate around the target. In the "FreeLook" Camera this is
known as an "orbital transposer". However, in the "Virtual"
Camera, the "orbital transposer" does not contain a vertical
rotation, but only horizontal. Thankfully, we can imitate
the "FreeLook" Camera setting using the "Framing Transposer"
Body together with the "POV" Aim. The "Framing Transposer" tries
to keep the camera centered to the Follow Object at the provided distance. The "POV" aim moves the camera according
to your input, which should translate to "where you are looking at",
relative to the "Look At" target. If we enter Play Mode, our Camera should have the same orbital
behaviour that Genshin Impact camera offers. However, there are a few problems: Besides the camera movement being too fast, the faster you move your mouse
around, the slower the movement gets. What we want here is for the camera sensitivity
to be as close to the mouse speed as possible. To fix that problem, open up the "Aim" area and swap both inputs from "Max
Speed" to "Input Value Gain". Make sure your Vertical Axis also
has the "Inverted" option enabled, as otherwise we'll be rotating towards
the opposite vertical direction. Next, we'll set the speed on the vertical axis to
be "0.1" and on the horizontal axis to be "0.16", as Genshin Camera has a
faster horizontal movement. If we now move our Camera around, it should be slower and also have a
closer 1 to 1 sensitivity with our mouse. If you wanted a fully 1 to 1 sensitivity,
then you would need to set the speed to "1". There still are some problems left to resolve. One example is our vertical axis, as we
aren't able to rotate 90 degrees up or down. That's solvable by updating the "Value
Range" field from the vertical axis. We'll set it to be from "-90" to "90". For our Horizontal Axis,
we'll go with "0" to "360". I'm not entirely sure if we actually need this one but I'll leave it as is to
make sure nothing breaks. I'll also enable "Wrap" to allow the camera to keep rotating once we get
to the "360" degree rotation, as otherwise it would stop there and
we would need to move the camera back. The next thing is that if you start
moving your camera around and stop it, you might notice something: The acceleration and deceleration
of the camera are instantaneous. In Genshin Impact however there is a
small acceleration and deceleration. We can easily set that up by updating
the Vertical and Horizontal Axis "Accel Time" and "Decel Time" fields. For the vertical axis we'll go with "0.8"
acceleration and "0.05" deceleration time. For the horizontal axis, we'll also go with
"0.8" acceleration but "0.25" deceleration time. Our camera should now start or stop a bit slower. I'm not really sure what the
"Recenter Target" does here but I have mine set to "Follow Target Forward"
and it works fine so I'll add it here as well. In this case, it seems to be the same as the
"Look" one because both have the same target, but I don't really know what this "Axis
angles are relative to Target" means. I'm assuming it has something to do with
the "Vertical" and "Horizontal Recentering" and that it recenters relative to that
target, but I'm not completely sure. If you do know, please leave a comment below explaining it so that we
know what it actually does. This "Horizontal Recentering" option is
what Free Look Cameras don't provide us with but we'll only need this quite a bit
later so leave it disabled for now. For our "Body", we'll be setting
both the "X Damping" to be "0.2" and the "Y Damping" to be around "0.4". Damping is how fast or slow the camera tries
to keep itself centered with the target, which in this case, should be the "Follow" target. While we won't be needing to set this here, I'll also go ahead and set
the "Camera Distance" to "6". We now have a Camera working quite nicely but
we still need 2 more things to take care off. The first one is that currently our Camera goes
through the ground instead of colliding with it. The second one is that we
can't yet zoom in or zoom out. Thankfully, both are quite easy to do. Lets start by exiting Play Mode. For the Camera Collisions, go to the
bottom of the Cinemachine Component and open up the "Add Extension" dropdown. There should be an option
named "CinemachineCollider". Feel free to press on it. This should add a new component to our Camera Object that allows us to set
Collisions with the Camera. What we want here is to set the "Collide
Against" layer to something to collide with. To do that, we'll create a layer for our
Environment so that the Camera collides with every existing Environment instead
of just the Ground. So, add in a new layer named "Environment". Then, go to all of our Environment
Game Objects besides the "Water" and set their layer as "Environment". When that's done, go back to the Camera and
now set the "Collide Against" to "Environment" and remove the "Default" layer from there. For the "Ignore Tag" we'll set it to "Player". Of course, we need to add this tag to
the player so select its Game Object and add the "Player" tag. Next, back into our Camera, we'll be changing
the "Strategy" of the Camera Collider. This is simply how the camera behaves
when colliding with something. In our case, we want the camera to "Pull
forward" when we collide with the Environment, which simply pulls the camera
to the front of the collider. I'll leave the rest with its default values
but feel free to change them if you want to. If we enter Play Mode, our Camera
Collisions should be working. That means that all that's
left is our Camera Zoom. Unfortunately, I don't think there's
a built-in functionality for this so we'll have to do it ourselves. Thankfully, with the Input Provider "Z"
axis and the Body "Camera Distance", this is quite simple to achieve. That's because the input provider "Z" axis gives
us the value of when we scroll our mouse wheel, while the "Camera Distance" is
how far we are from the target. So, go back to the "Scripts" folder
and create a new folder named "Camera". Inside, we'll create a new C# Script,
to which I'll name "CameraZoom". When you're done creating it, add it
as a component of our Player Camera. Then, open it up and remove the default methods. We'll start by creating 3 variables:
The default distance that the camera will start at and the minimum and maximum
distances that the Camera can go to. So, type in "[SerializeField]
private float defaultDistance;" and default it to "6f". Then, duplicate the variable line twice and swap "default" with "minimum"
and then "default" with "maximum". I'll set their default values
to "1f" and "6f" respectively. We'll also make it so we can update
these variables with a slider, so to do that simply add in after the
"SerializeField" attribute: "[Range(0f, 10f)]", meaning we can choose a value
from 0 to 10 through a slider. Next, we'll add two more variables:
A value to smooth our distance lerp and a value to multiply our "Z" axis value with. So, duplicate one of the variables above twice and
then change the first one to be named "smoothing" and the second one to be "zoomSensitivity". I'll default the "smoothing" to "4f"
and the "zoomSensitivity" to "1f". That's all the data we need to be able to set
in the inspector so now we need two more things: A reference to the Cinemachine Framing
Transposer, which represents our "Body" and a reference to the Cinemachine Input
Provider, which contains our "Z" axis value. So, create a new "private" variable
of type "CinemachineFramingTransposer" named "framingTransposer". Make sure you import the "Cinemachine" namespace. Then, create another "private" variable of type
"CinemachineInputProvider" named "inputProvider". To get their references, we'll
use the "GetComponent" method so type in "Awake" and inside type in "framingTransposer = GetComponent<CinemachineVirtualCamera>()", which gets our Virtual Camera. We'll now get our framing transposer by using the
".GetCinemachineComponent<>()" method instead. For the type, we'll of course pass
in "CinemachineFramingTransposer". We use the "GetCinemachineComponent" method here because the "Body" is part
of the Cinemachine Component. For our provider, type in "inputProvider =
GetComponent<CinemachineInputProvider>();". When that's done, call in the
MonoBehaviour "Update" method and call in a new method named "Zoom();". We can now start zooming our camera. The way we'll do that is by
retrieving the value of our scroll, which we can do from the input provider "Z" axis. To do that, type in "float zoomValue
= inputProvider.GetAxisValue();". Here, we need to pass in an axis index,
which is the index of "2" for the "Z" axis. Then, we multiply this value by our "zoomSensitivity". We now need to add this value
to our current distance target, which we currently have no variable of. So, above, create a new "private
float" named "currentTargetDistance". This will start with a default value of "0" but
we want it to start with the "defaultDistance" so in the "Awake" method type in
"currentTargetDistance = defaultDistance;". Then, back in our "Zoom" method, type in "currentTargetDistance =
currentTargetDistance + zoomValue;". We need to make sure we clamp this target
distance to not pass our maximumDistance, so add in "Mathf.Clamp()"
surrounding our assignment and then pass in "minimumDistance"
as the second parameter and "maximumDistance" as the third one. We can now lerp the current
distance towards the target distance so that we slowly get there
instead of it being an instant change. To do that, create a "float" named "currentDistance" and assign to it
"framingTransposer.m_CameraDistance;". Then, type in "if (currentDistance ==
currentTargetDistance)", we "return;". This makes it so that if we're already at
the target distance, we won't do anything. To lerp our value, type in
"float lerpedZoomValue = Mathf.Lerp(currentDistance, currentTargetDistance,
smoothing * Time.deltaTime);". We pass in "smoothing * Time.deltaTime"
here because we don't really want it to be an amount of seconds
for the whole lerp but just keep it as consistent as possible at every
change and get there whenever it gets there. Then, finish it up by setting the "framingTransposer.m_CameraDistance = lerpedZoomValue;". That's all we need to Zoom our camera,
so save it up and go back to Unity. If we now enter play mode, we
should be able to zoom our Camera.