
위와 같은 피스톤을 제작해 보아서, 이에 대한 내용을 정리해 볼 것이다.
protected virtual void FixedUpdate()
{
// 타겟에 도착하면 다음 타겟으로 변경
if (IsArrived())
{
ChangeTargetPoint();
}
movingPad.position += speed * Time.fixedDeltaTime * moveDir;
// 플레이어가 있을 때, 플레이어 움직임 처리
if(detector.IsIn)
player.MovePosition(player.position + speed * Time.fixedDeltaTime * moveDir);
}
// 타겟 포인트에 도착했는지 확인
protected bool IsArrived()
{
float distance = Vector3.Distance(points[index].position, movingPad.position);
if (distance > REACH_THRESHOLD)
{
return false;
}
movingPad.position = points[index].position;
return true;
}
// 다음 타겟 포인트로 변경
private void ChangeTargetPoint()
{
index = (index + 1) < points.Length ? index + 1 : 1;
moveDir = (points[index].position - movingPad.position).normalized;
}
위는 움직이는 플랫폼에 대한 코드이다.
왜 갑자기 움직이는 플랫폼에 대한 코드를 곁들이냐면, 피스톤이 이를 상속받았기 때문이다.
피스톤 또한 위와 아래로 계속 움직이는 플랫폼이기 때문에 이를 사용해 보았다.
위 코드의 각 메서드에 대한 자세한 설명은 아래와 같다.
IsArrived():
타겟 포인트에 도착하면 true, 그렇지 않으면 false를 반환한다.
현재 위치와 타겟의 위치 사이의 거리를distance에 저장하고, 그 값이 기준 값인REACH_THRESHOLD보다 커야지 도달했다고 간주한다.
ChangeTargetPoint():
다음 타겟 포인트로index값을 변경하고 방향 벡터를 산출한다.
index값은 처음이 1로 설정되어 있다. 이는points를 가져올 때,GetComponentsInChildren<>을 사용하였기 때문에 0 값이 부모 오브젝트의Transform이 들어가서 필요 없기 때문이다.
FixedUpdate()에서 플레이어의 움직임을 처리해 준 이유는 보통 움직이는 플랫폼 위에 있는 플레이어는 플레이어 혼자 멈춰있고 플랫폼만 움직이다. 따라서, 이러한 처리를 통해 자연스럽게 플레이어가 플랫폼과 동화되어 움직일 수 있다.
protected override void Start()
{
base.Start();
detector.Init(this);
}
protected override void FixedUpdate()
{
if (detector.IsIn)
{
// 플레이어가 있을 때, 타겟 포인트에 도착
if (IsArrived())
{
// 만약 피스톤이 실행 중이라면
if (excuted)
{
excuted = false;
// 플레이어에게 힘을 가함
player.AddForce(movingPad.up * power, ForceMode.Impulse);
}
// 피스톤이 실행 중이 아니라면, 밀어야 하는 상태로 변경
ChangeTargetPoint(PistonState.Push);
excuted = true;
}
}
else
{
excuted = false;
}
// 타겟 포인트에 도착하지 않았다면, 계속 움직임
if(!IsArrived())
MoveHead(detector.IsIn);
}
// 헤드 오브젝트를 움직이는 메서드
private void MoveHead(bool hasPlayer)
{
float speed = excuted ? EXCUTE_SPEED : RECOVER_SPEED;
movingPad.position += speed * Time.fixedDeltaTime * moveDir;
// 플레이어 있을 때는 플레이어도 같이 움직임
if(hasPlayer) player.MovePosition(player.position + speed * Time.fixedDeltaTime * moveDir);
}
// 밀어야 하는 상태인지, 당겨야 하는 상태인지 변환
public void ChangeTargetPoint(PistonState state)
{
index = (int)state;
moveDir = (points[index].position - movingPad.position).normalized;
}
피스톤의 움직임은 이번 과제에서는 반복적인 움직임이 아닌, 플레이어가 위에 있을 때만 당겨지다가, 다 당겨지면 발사하는 로직으로 만들어 보았다.
코드가 좀 복잡해 보이는데, FixedUpdate()만 잘 이해하면 된다.
주석이 달려 있기는 하나, 훗날 까먹을 나를 위해 차근차근히 설명해 볼 것이다.
움직임 파트
if(!IsArrived())
MoveHead(detector.IsIn);
피스톤의 움직임은 간단하게 타겟 포인트에 도착했는지를 검사해서, 도착하지 않으면 계속 움직이게 하였다.
타겟 변경(피스톤을 당기는지 미는지 변경)
detector.Init(this);
... another script : detector ...
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.layer == GameManager.PLAYER_LAYER)
{
isIn = true;
piston?.ChangeTargetPoint(PistonState.Pull);
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.layer == GameManager.PLAYER_LAYER)
{
isIn = false;
piston?.ChangeTargetPoint(PistonState.Push);
}
}
위처럼 피스톤을 당기는지 미는지에 대한 변경은 플레이어의 감지 여부에 따라 변경하는 것을 알 수가 있다. 또한, 아래와 같이 플레이어가 피스톤에 감지되어 있는 상태에서 끝까지 당겨졌다면 다시 미는 상태로 변경되게 하였다.
if (detector.IsIn)
{
// 플레이어가 있을 때, 타겟 포인트에 도착
if (IsArrived())
{
...
// 피스톤이 실행 중이 아니라면, 밀어야 하는 상태로 변경
ChangeTargetPoint(PistonState.Push);
excuted = true;
}
}
플레이어에게 힘 부여
if (detector.IsIn)
{
// 플레이어가 있을 때, 타겟 포인트에 도착
if (IsArrived())
{
// 만약 피스톤이 실행 중이라면
if (excuted)
{
excuted = false;
// 플레이어에게 힘을 가함
player.AddForce(movingPad.up * power, ForceMode.Impulse);
}
// 피스톤이 실행 중이 아니라면, 밀어야 하는 상태로 변경
ChangeTargetPoint(PistonState.Push);
excuted = true;
}
}
위와 같이, 당겨야 하는 상태에서 밀어야 하는 상태로 변경되면서 excuted 변수가 true가 되면, 이후에 타겟 포인트에 도착했을 때, 플레이어에게 힘을 가하게 된다.
이때, 단순히 y축 방향이 아닌 오브젝트가 돌아간 방향에 맞게 힘을 가하도록 구현하였다.
오늘부로 OnlyUp과 같은 과제를 만드는 프로젝트가 끝나게 되었는데, 뭔가 아쉽다.
확실히 2D랑은 다르게 축 하나가 추가된 점이 너무나도 성가신 점이다.
그리고, 내가 2D 게임을 사랑하다 보니 이전 프로젝트보다 머리가 덜 돌아가는 느낌?을 받았다.
3D랑 친해지려면 아직 멀고 먼 것 같다...