Hello, Im trying to implement camera that has following features:
- WSAD to move around (this works fairly well)
- Rotate horizontally with right mouse (this works fairly well)
- Rotate vertically with right mouse (this is janky, kinda works but not well)
- Zoom in with mouse scroll (kinda works but it’s janky with combination with rotation)
I believe many games have this style of camera. Any ideas how can i improve this code?
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using CodeMonkey.Utils;
using Unity.Cinemachine;
public class CameraTarget : MonoBehaviour {
public enum Axis {
XZ,
XY,
}
[SerializeField] protected Axis axis = Axis.XZ;
[SerializeField] protected float moveSpeed = 50f;
[SerializeField] protected CinemachineCamera virtualCam;
[SerializeField] protected CinemachineFollow cinemachineFollow;
public float rotateSpeed = 180f;
public float verticalLimit = 80f; // Limit the up/down rotation to avoid flipping
protected float rotationY = 0f;
protected float rotationX = 0f; // Track the current vertical rotation
protected Vector3 followOffset;
protected void Start() {
followOffset = cinemachineFollow.FollowOffset;
}
protected void Update() {
HandleWSADMove();
HandleCameraZoom();
HandleCameraRotate();
}
protected void HandleWSADMove() {
float moveX = 0f;
float moveY = 0f;
if (Input.GetKey(KeyCode.W)) {
moveY = +1f;
}
if (Input.GetKey(KeyCode.S)) {
moveY = -1f;
}
if (Input.GetKey(KeyCode.A)) {
moveX = -1f;
}
if (Input.GetKey(KeyCode.D)) {
moveX = +1f;
}
Vector3 moveDir;
switch (axis) {
default:
case Axis.XZ:
moveDir = new Vector3(moveX, 0, moveY).normalized;
break;
case Axis.XY:
moveDir = new Vector3(moveX, moveY).normalized;
break;
}
if (moveX != 0 || moveY != 0) {
// Not idle
}
if (axis == Axis.XZ) {
moveDir = UtilsClass.ApplyRotationToVectorXZ(moveDir, 30f);
}
transform.position += ((transform.forward * moveY) + (transform.right * moveX)) * (moveSpeed * Time.deltaTime);
}
protected void HandleCameraRotate() {
if (!Input.GetMouseButton(1)) {
return;
}
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
// Rotate horizontally
transform.Rotate(0, mouseX * rotateSpeed * Time.deltaTime, 0);
// Adjust vertical rotation
float verticalOffsetChange = -mouseY * rotateSpeed * Time.deltaTime * 2f; // Smooth control
float newY = Mathf.Clamp(cinemachineFollow.FollowOffset.y + verticalOffsetChange, 10f, 80f); // Prevent going below ground
// Adjust Z dynamically to maintain distance while looking up/down
float newZ = followOffset.z - (verticalOffsetChange * 0.5f); // Adjust Z without smoothing
// Apply changes
cinemachineFollow.FollowOffset = new Vector3(followOffset.x, newY, newZ);
}
protected void HandleCameraZoom() {
if (Input.mouseScrollDelta.y == 0) {
return;
}
float zoomSpeed = 5f;
float minZoom = 10f;
float maxZoom = 100f;
// Calculate zoom direction based on current offset
Vector3 zoomDirection = followOffset.normalized;
// Apply zoom movement in the correct direction
followOffset += zoomDirection * Input.mouseScrollDelta.y * zoomSpeed;
// Clamp the zoom range to prevent extreme values
float distance = followOffset.magnitude;
distance = Mathf.Clamp(distance, minZoom, maxZoom);
// Apply the clamped distance while maintaining direction
followOffset = zoomDirection * distance;
// Update the Cinemachine FollowOffset
cinemachineFollow.FollowOffset = followOffset;
}
}