Frog on Lotus 개발 기록 (1)


이 게임을 만든 이유

부트캠프가 끝나고 수료자들에게 인턴십 지원 기회가 주어졌다.
하지만 비전공자에 부트캠프 경력밖에 없는 내가 이력서를 쓴다고 해봐야
초라하고 개성 없는 몇 줄밖에 쓰지 못할 것 같았다.
조금이라도, 의지라도 보여줄만한 무언가가 필요했다.

게임 개발자는 게임으로 말해야 된다는 지론이 있었기에,
작은 프로젝트를 하나 만들면 될 거라고 생각했다.
마지막 팀 프로젝트 기간 동안 게임 기획을 구상하고 애셋을 미리 찾아놨다.

기획은 최대한 작게 만들려고 했다.
게임 개발 입문자들이 가장 많이 하는 실수가
자신의 역량을 넘어서는 게임 기획을 생각해내고서
막상 구현에 들어가니 의지가 꺾이는 건데, (경험담이다)
그런 상황은 최대한 피하려고 했다.

그렇게 개구리가 시간 안에 날벌레들을 잡아먹는 스테이지 형식의 게임을 생각해냈고,
개발을 시작했다.



1일차


파리의 움직임 구현

가장 중요한게 파리의 움직임이었다.
실제 파리처럼 움직이면서도 무작위성을 더하고 싶었다.

모든 문제는 우선 최대한 단순화하여 풀어봐야 한다고 알고 있기에
일단 랜덤한 직선 움직임을 구현하고 있었는데,

마침 부트캠프 교육장으로 올라왔던 동기에게
랜덤한 움직임을 어떻게 구현하면 좋을지 물어봤더니
spline interpolation이라는 걸 써보라고 키워드를 알려줬다.

유튜브를 보고 구현을 완료했지만,
내가 원하던 자연스러운 움직임이 나오지 않았다.



2일차

그래서 다시 원점으로 돌아가 어떻게 하면 좋을지 생각해보았다.

alt text

우선 내가 무슨 움직임을 원하는지 태블릿에 한 번 그려봤다.
원과 그 사이를 잇는 곡선으로 단순화할 수 있을 것 같았다.

alt text

원 궤도를 구현하기 위해 칠판에 구상을 그려봤다.
처음엔 현실 세계의 중력을 똑같이 적용시키면 되지 않을까 했는데,
머릿속에서 상상해보니 가속도 때문에 전혀 자연스러울 것 같지 않았다.

그래서 유사 중력을 고안해냈다.
속도 벡터를 고정시키면 가속도 없이 자연스러워보일 거라는 생각이 들었다.
항상 중심점을 가리키는 벡터와 그 직교 벡터를 더하면 될 것 같았다.

결과물이 나쁘지 않아 파리의 움직임은 이정도로 마무리했다.

개구리 공격 구현

혀가 길어지다가 파리에 닿으면 다시 돌아오는 기능을 구현하고 싶었다.

위와 같은 방식들을 찾아보았지만, 이렇게까지 어렵게 구현해야 할지 의문이 들었고,
그렇게 어렵게 구현해봤자 내가 원하는 결과물이 안 나올 것 같다는 생각이 들었다.

You can make your texture repeatable in import settings then change the tiling in your material.

그러다가 이 레딧 댓글을 보게 됐다.
팀 프로젝트 기간 때 살짝 찾아봤던 9-slice가 떠올랐다.

alt text

aseprite로 혀를 그리고, 9-slice를 적용한 뒤, 크기를 늘려봤더니
정확히 내가 원하던 모습이 나왔다.

남은 건 마우스의 위치까지 혀가 늘어난 뒤
도착했다면 다시 줄어드는 기능.

void Update()
{
    // 혀 공격은 혀를 늘리는 동작과 줄이는 동작으로 나뉜다
    if (isAttacking)
    {
        // 점에 도착하지 못했다면 혀를 늘리고
        if (!arrivedPoint)
            StretchTongue();
        // 점에 도착했다면 혀를 줄인다
        else
            ShrinkTongue();

    }
}

나는 아직 이벤트를 잘 모르고, 시간도 없었기에
당장 알고 있는 Update문과 if문으로 해당 기능을 구현했다.

(위 코드는 간단한 구조를 보여주기 위해 세부 구현은 생략했다.
StretchTongue()과 ShrinkTongue()은 캡슐화한 건 맞음.)

mousePos = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());
tonguePos = tongue.position;
direction = mousePos - tonguePos;
direction.z = 0; // magnitude 왜곡 방지
targetLength = direction.magnitude;

그랬더니 마우스 클릭한 점보다 훨씬 더 많이 늘어나는 버그가 발생했다.
mousePos의 z좌표가 0이 아니라 -10인가 10으로 되어 있어서 발생한 문제였다.
받아온 Vector3의 z값을 0으로 초기화시켜주어 해결했다.

혀 늘어나는 기능까지 성공적으로 구현했다.



3일차


개구리 좌우 반전

alt text

클릭하면 개구리의 localScale을 좌우 반전 시켰더니
개구리의 자식 오브젝트인 혀가 클릭한 곳의 반대로 가버리는 현상이 발생했다.
localScale 대신 Sprite를 반전하는 것으로 해결했다.

그런데 바로 해결된 건 아니고,
좌나 우로 공격을 두 번 해야 방향이 바뀌는 버그가 있었다.

    private void FlipCharacter()
    {
        bool TrueIsLeft = (mousePos.x - tongue.position.x < 0) ? true : false;
        (생략)
    }

FlipCharacter()에는 mousePos가 계산에 사용되는데,

FlipCharacter();
mousePos = Camera.main.ScreenToWorldPoint(Mouse.current.position.ReadValue());

FlipCharacter()가 실행된 이후에 mousePos를 갱신해주니 발생한 문제였다.
그래서 한 박자씩 늦게 방향 전환이 되었던 것.

FlipCharacter()의 위치를 mousePos 초기화 구문 아래로 바꿔주니 해결됐다.

혀 끝에 닿으면 파리 죽임

    private void ShrinkTongue()
    {
        if (tongueSR.size.x <= 0)
        {
            tongueCollider.radius = 0f; // 콜라이더 크기 0으로 돌려놓기
        }
        tongueCollider.offset = new Vector2(tongueCollider.offset.x
        - tongueSpeed * targetLength * Time.deltaTime, 0);
    }

(코드는 대부분 생략했다)

StretchTongue()과 ShrinkTongue()에 혀 콜라이더의 위치를 갱신하는 구문을 넣었다.
공격 하지 않으면 콜라이더의 크기를 0으로 돌려놓았다.

alt text

이로써 혀와 함께 콜라이더의 위치까지 늘어나게 만들었다.
하지만 버그가 있었다. 혀의 콜라이더가 파리의 콜라이더와 충돌을 하지 않았다.

리지드바디 여기에 붙여보고 저기에 붙여보고,
트리거로 해보고, 대문자 소문자 확인해보고…
별 짓을 다해봐도 간단한 ‘충돌’이 안 되니까 답답했다.



다음 글: Frog on Lotus 개발 기록 (2)