2D로 로프 플랫픔 조정 및 테스트 진행
2D로 원형 톱 장애물 오브젝트 구현
2D 로프 플랫폼 테스트 씬 구현 및 테스트 진행
플레이어 담당자의 씬 화면에서 로프 시스템 분석 및 버그 픽스
오브젝트를 3D로 전환하여 맵 제작 요청하여 2D 물리 + 3D 맵 제작
총알 테스트 진행 및 물리 관련 기능 수정
맵 담당자로서 느끼는 것은, 코드적으로 뭔가를 많이 짜는 느낌은 아니라 무언가 물리 엔진을 건드는 작업이 많아 매우 어려웠다. 까딱 설정을 잘못하면 부자연스러운 움직임이 되고, 정말 세밀하게 조정이 많이 필요한 부분이라는 걸 알게 되었다.
로프와 연결된 플랫폼을 만드는 방식은 어제 구현했지만 이와 같은 문제점이 존재했다.
이와 같이 플랫품이 연달아 연결되어 있을 경우 아래로 너무 출렁거리고, 안정화되는 데까지 시간이 너무 걸린다는 점. 또 팀장님의 요청으로 좀 더 출렁거리는 움직임을 주기 위해서 이와 같은 방법으로 물리를 조정했다.
로프는 무게는 2로 두고 대신 중력을 낮췄다. 또한 끈이 돌아가면 부자연스럽게 보이는 문제 때문에, Z축에 대해 rotation을 제한했다.
플랫폼의 경우 처음에는 무게를 많이 두어 안정감을 잡으려고 했지만, 이게 도리어 출렁거림의 원인이 된다는 것을 알게되었다. 대신 로프보다는 중력을 많이 주어 무게는 1로 설정했다.
또한 여기서 주목해야 할 건 Collision Detection 부분인데, 플랫폼끼리 너무 빠르게 충돌할 경우 통과하는 문제가 발생하여 연속 충돌성 검사를 위해 Continuous로 설정했다.
이후 팀장님의 요청으로 3D 오브젝트로 2D 맵을 만드는, 이른바 2.5D를 노리는 방식을 요청했다.
아무래도 3D 오브젝트 같은 것이 쉐이더 등을 다루는 데에도 편하고 빛 등의 효과를 줄 수 있다는 기대 때문에, 이와 같이 3D 오브젝트를 이용한 2D 맵 구성을 시작했다.
해당 방식부터 찾아보기 시작했다. 방법은 다음과 같이 이루어진다.
Projection은 Orthographic으로, Background Type은 Solid Color로 하면 마치 2D 씬과 같은 환경이 만들어진다.
오브젝트는 3D로 생성한다. 다만 이와 같이 처음 생성하면 Box Collider와 같이 3D의 기본 옵션이 들어갈 것이다. 이런 설정은 2D에 맞춰 변환한다.
예시로 위의 박스를 2D 게임의 오브젝트로 사용한다고 치자.
이와 같이 Box Collider 를 지우고 대신 Box Collider 2D와 Rigidbody2D를 넣자.
그러면 이와 같이 3D 오브젝트이면서도 2D 오브젝트와 충돌할 수 있는 오브젝트가 만들어진다.
이 부분이 오늘 특히 어려웠던 부분이었다.
맵을 구성하기 위해서 오브젝트를 다양하게 배치하긴 했지만, 제일 문제가 되는 것은 '물체를 너무 많이 쌓은 탓에 무너진다'는 문제가 발생했다.
물체를 무너지지 않도록 하기 위해 균형을 잡는 등의 시도를 해 보았지만, 단순히 균형을 잡는 문제로는 해결되지 않아 보였다.
중력이나 무게를 건드린다고 될 문제도 아니었고, 특히 중력의 경우 0으로 처리해버리면 그냥 무중력처럼 움직여버리니 방법에 대한 고민이 필요했다.
그렇게 다양한 방법을 시도해 보았는데, 결론적으로 알아낸 건 한 가지 방법만으로는 원하는 의도대로 구현할 수 없다느 것이다. 따라서, 총 두 가지 방법을 사용해서 예시로 '총에 피격되었을 때 무너지는 오브젝트'를 구현했다.
Rigidbody2D에 Kinematic을 적용하면 물리 관련 작용을 무시하게 된다. 이와 같은 방식으로 초기에 실행하게 되면, 플랫폼은 무너질 일 없이 똑바로 서게 될 것이다.
다만 이와 같이만 처리하게 될 경우, 총알이 플랫폼을 움직이게 하는 작용은 불가능하다. 따라서, 총알에 해당 플랫폼이 피격되었을 때 Body Type를 바꾸는 방식이 필요하다.
간단하게 모든 Box에 한해서 이와 같이 스크립트를 붙일 수 있다.
이렇게 하면 총알에 피격되는 플랫폼은 물리 작용이 가능하게 된다. 하지만 여기서 또 문제가 발생하니, '총알이 피격된 플랫폼'만 물리 작용이 가능하다는 것이다.
그러면 이와 같은 상황이 벌어진다.
플랫품이 기둥 형태로 되어 있는데, 아래쪽 블럭을 쳐서 빼냈는데, 위쪽 블럭은 물리 작용을 받지 않아 공중에 머문 상태로 그냥 가만히 있게 된다.
아래쪽 블럭이 무너졌을 때 위쪽도 같이 무너저야 하는데, 이는 의도와 다른 방법이다.
이 문제를 해결하기 위해서 두 번째 방법이 필요하다.
방식은 다음과 같다.
모든 플랫폼 프리팹에 다음과 같이 FixedJoint를 설정하고, 각각 모두 자신보다 아래에 있는 Rigidbody2D를 연결시킨다.
또한 여기서 Break Force를 금방이라도 부서질 수 있는 낮은 수치로 둔다. 테스트를 해 봤을 때 3 정도가 적당해 보여서 우선은 이렇게 설정했다.
using Unity.VisualScripting;
using UnityEngine;
public class BoxController : MonoBehaviour
{
FixedJoint2D fixedJoint;
private Rigidbody2D rigid;
private void Awake()
{
rigid = GetComponent<Rigidbody2D>();
fixedJoint = GetComponent<FixedJoint2D>();
}
private void Update()
{
if (fixedJoint.IsDestroyed() ||
(fixedJoint != null && fixedJoint.connectedBody != null && fixedJoint.connectedBody.bodyType == RigidbodyType2D.Dynamic))
{
rigid.bodyType = RigidbodyType2D.Dynamic;
rigid.mass = 0.1f;
rigid.gravityScale = 0.3f;
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Bullet"))
{
rigid.bodyType = RigidbodyType2D.Dynamic;
rigid.mass = 0.1f;
rigid.gravityScale = 0.3f;
}
}
}
다음으로 FixedJoint를 조건으로 'FixedJoint가 파괴되었을 경우' 혹은 'FixedJoint에 연결된 이전 오브젝트의 body type이 Dynamic로 변경되었을 경우'를 조건으로 body type를 Dynamic로 변경한다.
여기서 이와 같이 두 조건을 건 이유는 다음과 같다.
이와 같이 조건을 만들고 테스트를 진행한 결과 위의 GIF와 같은 결과가 나왔다.
이 부분도 많이 고민했던 부분이다.
예를 들어 아래와 같은 상황을 생각해보자.
이렇게 플랫폼 중에는 Joint 하나만으로 연결되기에는 어려운 상황이 있다. 처음에는 달리 방법이 생각나지 않아 그냥 한쪽 다리만 Joint를 연결하고 쐈는데, 문제가 발생했다.
예를 들어 왼쪽 Joint만 연결해놓고 오른쪽 기둥을 부수면, 저 오브젝트는 아직 kinematic이라서 오른쪽 기둥이 부서지고 나서도 무너지지 않는 현상이 발견된 것이다.
그래서, Joint를 두 개를 만들고, 어느 한쪽이라도 부서지면 바로 Kinematic이 해제되는 방식에 대한 고민이 필요했다.
이 부분은 스크립트를 추가로 하나 더 만들어서 진행했다.
using Unity.VisualScripting;
using UnityEngine;
public class MultiJointBoxController : MonoBehaviour
{
FixedJoint2D[] fixedJoint;
private Rigidbody2D rigid;
private void Awake()
{
rigid = GetComponent<Rigidbody2D>();
fixedJoint = GetComponents<FixedJoint2D>();
}
private void Update()
{
if (FixedJointDisabled())
{
rigid.bodyType = RigidbodyType2D.Dynamic;
rigid.mass = 0.1f;
rigid.gravityScale = 0.3f;
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Bullet"))
{
rigid.bodyType = RigidbodyType2D.Dynamic;
rigid.mass = 0.1f;
rigid.gravityScale = 0.3f;
}
}
private bool FixedJointDisabled()
{
for (int i = 0; i < fixedJoint.Length; i++)
{
if (fixedJoint != null && fixedJoint[i].IsDestroyed() || (fixedJoint != null && fixedJoint[i].connectedBody != null && fixedJoint[i].connectedBody.bodyType == RigidbodyType2D.Dynamic))
{
return true;
}
}
return false;
}
}
이와 같이 두어 어느 조인트가 부서지더라도 바로 물리 작용이 가능한 상태로 전환되는 것을 확인했다.
팀장님은 요청으로 맵을 최소 3개까지는 만들자고 했다. 우선 하나까지는 만들었다 쳐도 두 개를 더 만들어야 한다.
관절 부분을 여러모로 손을 대서 좀 더 자연스럽게 표현해보았지만, 아직까지도 뻑뻑한 느낌이 없잖아 있다.
이 부분을 어떻게 할지 고민해보자.
로프 시스템을 3D 오브젝트로 다시 구현해서 맵을 만들어보자.
또한 테스트씬을 만든 다음 어떻게 맵을 디자인할 지에 대한 고민도 필요하고, 어떤 식의 물리 작용을 일으키는 지도 확인해봐야 할 듯 하다.
이 게임은 네트워크 게임이라는 것을 기억해 두자.
다만 실제로 동기화 테스트를 위해 패럴링크로 맵 테스트를 진행하자, 각자 플레이어의 시야에 잡힌 맵이 다르게 나오는 것을 확인할 수 있었다.
플레이어 각자의 화면에서 연산이 이루어지다 보니 서로 보고 있는 맵이 달라지는 문제가 발생했다.
이 부분을 어떻게 해결할지에 대한 고민이 필요해 보인다.
현재 생각해볼 수 있는 방법으론, 총알 담당자가 총알에 PhotonView를 넣지 않아서, 이걸 적용하는 방식으로 할지 고민중이다.