유니티 - Cinemachine을 이용한 Camera Shake
기존 카메라 쉐이크는 메인 카메라에서 tranform을 움직여서 쉐이크를 하는 방식이였다.
이번엔 시네머신을 통해서 카메라에 쉐이크를 해보면서 더욱 간단하게 만들 수 있었다.
- 메인 카메라 쉐이크 -
public class ShakeCamera : MonoBehaviour
{
private bool _isShaking = false; // 쉐이킹 상태를 추적
private float _shakeDuration; // 쉐이킹 지속 시간
private float _shakeMagnitude; // 쉐이킹 강도
private float _elapsedTime; // 경과 시간
private Vector3 _originalPosition; // 원래 위치
private float _strength;
private bool _isSkillShaking = false;
private void Update()
{
ShakeFunc(_strength);
}
public void Shake(float duration, float magnitude, float strength)
{
if (_isShaking) // 현재 쉐이킹 중이 아니면 시작
return;
_isShaking = true;
_shakeDuration = duration;
_shakeMagnitude = magnitude;
_elapsedTime = 0.0f; // 경과 시간 초기화
_originalPosition = transform.localPosition; // 원래 위치 저장
_strength = strength;
}
//코루틴으로 대체 가능
public void ShakeFunc(float strength)
{
if (_isShaking)
{
if (_elapsedTime < _shakeDuration)
{
// X와 Y축으로 무작위 흔들림 생성
float x = Random.Range(-strength, strength) * _shakeMagnitude;
float y = Random.Range(-strength, strength) * _shakeMagnitude;
// 카메라의 위치를 업데이트
transform.localPosition = _originalPosition + new Vector3(x, y, 0);
// 경과 시간 갱신
_elapsedTime += Time.unscaledDeltaTime;
if(!BattleManager.Instance.IsShake)
return;
}
else
{
// 흔들림 종료 후 원래 위치로 복원
transform.localPosition = _originalPosition;
_isShaking = false; // 쉐이킹 종료
}
}
}
}
- 시네 머신 카메라 쉐이크 -
활성화 될 카메라에 NoiseSettings 을 만들어준다.
*NoiseSetting ScriptableObject에서 Noise될 Position과 Rotation을 셋팅해줘야한다.
public class CameraShake : MonoBehaviour
{
private CinemachineBasicMultiChannelPerlin noise;
private void Awake()
{
noise = GetComponent<CinemachineBasicMultiChannelPerlin>();
}
public void Shake(float intensity, float duration)
{
//중복 방지
if (noise.AmplitudeGain != 0)
return;
//StartCoroutine(ShakeRoutine(intensity, duration));
ShakeRoutineUniTask(intensity, duration);
}
/*private IEnumerator ShakeRoutine(float intensity, float duration)
{
noise.AmplitudeGain = intensity;
yield return new WaitForSeconds(duration);
noise.AmplitudeGain = 0;
}*/
private async void ShakeRoutineUniTask(float intensity, float duration)
{
noise.AmplitudeGain = intensity;
await UniTask.Delay(TimeSpan.FromSeconds(duration));
noise.AmplitudeGain = 0;
}
}
이렇게 훨씬 코드가 간단해졌다.
이제 호출을 통해서 Shake를 해주면 된다.
private void CameraShake(float intensity, float duration)
{
//현재 활성화된 VirtualCamera 가져오기 (ICinemachineCamera 타입)
ICinemachineCamera activeCam = _mainCamera.ActiveVirtualCamera;
//VirtualCameraGameObject를 통해 GameObject 접근
CinemachineCamera camObj = (activeCam as CinemachineVirtualCameraBase)?.GetComponent<CinemachineCamera>();
CameraShake cameraShake = camObj?.GetComponent<CameraShake>();
cameraShake?.Shake(intensity, duration); // 원하는 세기로 흔들기
}
-비교-
Main Camera - transform.position
장점:
- 구현이 단순하고 직관적 (그냥 transform.position += Random.insideUnitCircle)
- Cinemachine 안 써도 됨 (순수 카메라)
단점:
- Cinemachine을 쓴다면 충돌 가능성 높음
→ Cinemachine은 매 프레임마다 카메라 위치를 덮어쓰기 때문에,
transform.position을 건드려도 무효가 되거나 튕김 현상 발생 - 타 시스템(Zoom, Follow 등)이랑 충돌할 수 있음
- 흔들림이 자연스럽지 않을 수 있음 (Perlin Noise나 감쇠 같은 처리 직접 구현해야 함)
Cinemachine Noise (CinemachineBasicMultiChannelPerlin)
장점:
- Cinemachine의 모든 기능과 충돌 없이 연동 가능
- 진짜 "카메라 흔들림" 느낌이 남
→ Perlin Noise 기반이라 랜덤하지만 부드럽고 감쇠 효과도 좋음 - Virtual Camera마다 따로 설정 가능 (연출 다르게 가능)
단점:
- 셋업이 처음엔 약간 귀찮음 (Virtual Camera + Noise 셋업)
- 코드 접근 시 캐스팅 필요 (앞에서 했던 VirtualCameraGameObject 방식 등)
즉 시네머신을 사용하는 프로젝트에서는 Noise를 사용하고 사용하지 않는 프로젝트에서는 원래대로 transform을 건드리면 될거같다.