흑...ㅠ
StateMachine.... 아직 글로 풀어쓰는게 어렵다... 너무 어렵다...
그래서 며칠간 좀 고민만 하다 TIL 도 못썼는데 (다른 것도 있었지만)
아무튼 오늘은 대신 개인과제에 쓰인 걸 써봐야겠다.
CameraStateMachine
여기서 내가 구현하고자 한 것은 마우스 커서에 따라 3D 효과를 주는 카메라 연출과
버튼을 눌렀을 때 카메라가 돌아가 반대편 화면을 찍으며 3D 효과는 그대로 가져가는 것이다.
기본적으로 역 참조 구조인 것을 활용하였다.
public class TitleSceneCameraController : MonoBehaviour
public class CameraStateMachine : StateMachine
public class CameraStateBase : IState
TitleSceneCameraController 의 FixedUpdate에서 커서를 따라가는 카메라 연출을 담당하고있다.
public class TitleSceneCameraController : MonoBehaviour
{
public CameraStateMachine StateMachine { get; private set; }
public bool IsChanging { get; private set; } = false;
private void Awake()
{
StateMachine = new CameraStateMachine(this);
}
private void Start()
{
StateMachine.ChangeState(StateMachine.mainState);
}
private void FixedUpdate()
{
StateMachine.FixedUpdate();
}
}
TitleSceneCameraController 에서 담당한다고 말한 이유는
public class CameraStateBase : IState
{
protected CameraStateMachine _stateMachine;
protected Transform mTrans;
protected Quaternion mStart;
protected Vector2 mRot = Vector2.zero;
public CameraStateBase(CameraStateMachine stateMachine)
{
_stateMachine = stateMachine;
mTrans = stateMachine.Controller.transform;
mStart = Quaternion.identity;
}
public virtual void Enter() { }
public virtual void Exit() { }
public virtual void FixedUpdate()
{
if (_stateMachine.Controller.IsChanging)
return;
Vector3 pos = Input.mousePosition;
float halfWidth = Screen.width * 0.5f;
float halfHeight = Screen.height * 0.5f;
float x = Mathf.Clamp((pos.x - halfWidth) / halfWidth, -1f, 1f);
float y = Mathf.Clamp((pos.y - halfHeight) / halfHeight, -1f, 1f);
mRot = Vector2.Lerp(mRot, new Vector2(x, y), Time.deltaTime * 5f);
mTrans.localRotation = mStart * Quaternion.Euler(-mRot.y * _stateMachine.Range.y, mRot.x * _stateMachine.Range.x, 0f);
}
}
어느 상태이던 관계없이 FixedUpdate로 실행이 되도록 설계했기 때문이다.
위의 코드 중 Quaternion mStart가 카메라 연출의 중심이 되는 Rotation인데
public ModuleTiltState(CameraStateMachine stateMachine) : base(stateMachine)
{
}
public override void Enter()
{
base.Enter();
mStart = Quaternion.Euler(15, 180, 0);
_stateMachine.Controller.ChangeCameraLerp(mStart);
}
public MainTiltState(CameraStateMachine stateMachine) : base(stateMachine)
{
}
public override void Enter()
{
base.Enter();
mStart = Quaternion.Euler(0, 0, 0);
_stateMachine.Controller.ChangeCameraLerp(mStart);
}
각 상태에 진입하면 mStart를 변경시켜 해당 Rotation 을 중심으로 cursor Following을 연출했다.
State의 동작 순서는 Enter만 사용하여 진입할 때 현재 상태에서 중심이 되는 점을 설정 후,
그 점으로 부드럽게 회전하는 연출을 주었다.
이 때, FixedUpdate는 상태에 관계없기 때문에 계속해서 실행이 된다.
그렇기 때문에 중심점 변경 후 카메라 회전중엔 FixedUpdate가 실행이 되면 안 됐기 때문에 이를 조건을 걸 필요가 있었고
이를 코루틴으로 해결하였다.
하지만 여기서 문제가 하나 더 있는데, State와 StateMachine은 Monobehavior를 상속받은 구조가 아니기 때문에
코루틴을 사용하지 못한다. 하지만 State, StateMachine, Controller 는 서로를 역 참조 하고 있는 구조라 타고 타고
Monobehavior를 상속받은 Controller에서 해당 코루틴 함수를 호출하는 래핑 함수를 호출했다.
public void ChangeCameraLerp(Quaternion changeQuat)
{
StartCoroutine(CoChangeCameraRoutine(changeQuat));
}
private IEnumerator CoChangeCameraRoutine(Quaternion changeQuat)
{
IsChanging = true;
UI_BlockerPopup blocker = Managers.UI.ShowPopupUI<UI_BlockerPopup>();
float current = 0;
float percent = 0;
Quaternion startRot = transform.localRotation;
while (percent < 1)
{
current += Time.deltaTime;
percent = current / 1f;
transform.localRotation = Quaternion.Lerp(startRot, changeQuat, curve.Evaluate(percent));
yield return null;
}
IsChanging = false;
blocker.ClosePopup();
}
해결은 어찌어찌 하였지만 사실 중간에 있을 RotationState가 따로 있었다면 좀 더 깔끔한 처리가 가능하지 않았을까?
라는 의문이 든다. 이 부분은 내일 과제를 다듬으며 적용시도를 해보아야겠다.
'스파르타 내배캠' 카테고리의 다른 글
스파르타 내배캠 Unity 3기 43일차 (0) | 2024.02.27 |
---|---|
스파르타 내배캠 Unity 3기 42일차 (0) | 2024.02.23 |
스파르타 내배캠 Unity 3기 40일차 (0) | 2024.02.19 |
스파르타 내배캠 Unity 3기 39일차 (0) | 2024.02.19 |
스파르타 내배캠 Unity 3기 38일차 (1) | 2024.02.18 |