배경

회사를 그만두게 되었다. 이런 때야말로 혼자 개발하기 딱 좋은 때라는 생각이 들었다. 마침 이직용 포트폴리오에 회사 코드를 첨부하기도 애매했었고, 1인 개발은 더욱 필연 같았다.

회사에서 새로 개발할 게임에 대해 이야기를 할 때, 늘 나왔던 말은 "빠르게 작업해서 빠르게 출시하자"였다. 그런데 늘 그렇게 되지 않았고, 루즈해지는 기간도 꽤 길었었다.

그래서 1인 개발이지만 이번에야말로 진짜로 빠르게 작업해서 빠르게 출시해보자! 라는 마음가짐으로 기획을 시작했었다. 내가 처음 계획한 기간은 기획, R&D, 개발, 디자인 합해서 40일이었다.

계획 세우기

회사에서 일감을 Notion이나 Trello로 나누는 것이 편했었는데, 노션을 무료로 사용할 때에 제한들이 좀 있기도 하고 어차피 혼자 사용할 거라서 Obsidian을 사용했다.

이때쯤엔 이것까지 하면 되겠다 싶을 정도로 계획을 잡았다. 그리고 필요하다고 생각되는 일들을 정리해서 시각화했다.

개발 들어가기 전

체계적으로 개발을 하고 싶었고 처음 3일 정도는 기획을 하면서 일종의 R&D를 병행했다. 이번 프로젝트에서 가장 욕심냈던 부분은 "객체지향적인 프로그래밍"이었다. 클래스 구조를 잘 짜보고 싶었고 그러려면 시작이 가장 중요할 것이라고 생각했다. 그래서 애초에 개발 전에 디자인 패턴이나 SOLID 원칙 등 가장 기본적인 부분부터 다시 복습도 하고 몰랐던 부분도 공부했다.

개발 시작


회사 일이 아니고, 혼자서만 일을 하려다 보니 게을러지는 것이 가장 큰 걸림돌일 것이라 생각했다. 그런데 일을 하나하나 해치워가면서 하니까 너무 재미있었고 그래서 더 열심히 했다. 위에 보면 포도알이 참 예쁘게 찍혔다.

게임 안의 코드들은 내가 회사에서 배운 것, 사용하던 것들을 거의 그대로 사용했다. 여긴 포트폴리오가 아니니까 모두 다 세세히 적기보다는 재밌었던 작업, 힘들었던 작업 몇 가지 적어보겠다.


재미있었던 것

1.어드레서블

회사에서도 어드레서블을 사용하기는 했었다. CTO님이 세팅을 하시고, 나는 빌드할 때 조심조심 업데이트만 하는 정도였다. 잘못 건드리면 큰일 난다! 느낌이 강해서 "제가 해봐도 될까요?"라고 섣불리 말씀을 못 드렸었다.

프로젝트의 에셋 다운로드를 local이 아닌 remote로 설정해서 인터넷에서 다운받도록 하는 기능이 있는데, 이 기능을 사용하면 스토어에 등록되는 앱의 용량도 줄일 수 있고, 에셋을 게임 내에서 추가로 받는다던가 스토어에서의 패치 없이 CDN 스토리지를 업데이트하는 것만으로도 게임의 에셋 일부를 업데이트할 수도 있다.

그리고 저번 프로젝트에서 이 기능을 쓰시려다 잘 안 돼서 결국 local로 돌리셨다는 사실을 나는 알고 있었다. 너무 remote가 쓰고 싶었고 열심히 조사했다.

CDN 서비스는 아마존이 아닌 Cloudflare에서 R2라는 이름으로 무료로 제공하고 있었다. 돈 아꼈다는 생각에 너무 신났었는데, 이걸 사용하려면 내 도메인이 필요했었다. 내 세상이 무너졌다.

그런데 카페24에서 도메인을 550원에 파는 이벤트를 하고 있었다. 비록 1년이라는 기간 제한이 있었지만 그때 돼서 연장하면 될 일이었다. 몹시 신났었다.

DNS, 네임서버 등 뭔가 낯선 단어들이 많았지만 하나하나 세팅하면서 개발을 같이 했고, 결국 성공했다!

2.인터페이스

별건 아니지만 인터페이스를 진짜 찐하게 써봤다. 회사 일할 때는 만들어져 있는 부모 클래스나 인터페이스를 분석해서 내가 쓰는 정도였는데, 시작부터 내가 하려니까 더 재미있었다.

지금까지는 객체끼리 충돌 처리를 할 때 CompareTag를 사용하고 각각 다른 getComponent를 쓰는 정도였다. 보통 다들 그렇게 하는 것 같다. 그런데 그러면 태그마다 다른 케이스로 코드를 작성해야 하니까 OnTrigger 함수 안의 코드가 굉장히 길어지고 지저분했었다.

그래서 이번에는 충돌하는 오브젝트들에게 IInteractable이라는 인터페이스를 붙여주었다. 그러면 플레이어의 OnTrigger 함수는

	//예시 코드
    private void OnTriggerEnter2D(Collider2D collision)
    {
        IInteractable interactableObject = collision.GetComponent<IInteractable>();
        if (interactableObject != null)
        {
            interactableObject.EnterSingleInteraction();
        }
    }

이게 전부다. 우물 안 개구리로서 작성한 코드라서 이게 더 좋은 코드가 맞는지 확신은 없지만 어마어마한 깔끔함에 이미 자지진 상태였다.

3.아트 리소스 제작

그림은 생각보다 빠르게 나왔었다. 프로젝트 시작한 지 7일 차에 캐릭터, 배경들의 이미지는 나왔었다. 그런데 애니메이션이 문제였다.


내가 원했던 애니메이션 스타일은 쫙쫙 늘어나고 띠용띠용거리는 귀여운 애니메이션이었다. 최근에 오구와 비밀의 숲을 인상 깊게 보기도 했고, 원래 그런 걸 좋아한다. 이건 한 장 한 장 이미지를 그려서 빠르게 교체하는 고전적인 방식으로 만드는 거라고 알고 있다. (혹시 그게 아니라면 제발 알려주세요)

그래서 한 번 직접 그려보려던 중 감이 안 와서 일단 납작해졌다가 늘어나는 정도만 Idle 애니메이션 용으로 해봤는데


즉시 포기했다. 이건 내가 접근할 만한 영역은 아니라고 판단했고 애니메이터 포폴 만드는 거 아니잖냐고 스스로를 달래면서 Spine을 사용하는 것으로 선회했다. 뼈대 하나하나 만지면서 애니메이션을 만드는 건 나름 재미있었다.

힘들었던 것

1. 물리메소드

오브젝트가 지정된 위치에서 스폰이 돼야 하는데, 로컬 기준 (0,0,0)에서 스폰된 채 뭔가 버벅거리는 일이 계속 생겼었다. 내가 개발할 때 가장 힘든 부분은 “간헐적으로 나타나는” 버그인 것 같다. 이 버그도 10번에 3번 정도만 발생해서 도대체 뭐가 원인인지 알 수가 없었다. 이것 때문에 이틀 정도를 통으로 사용한 것 같다.

유니티 Discussion에 직접 질문했고 찐하게 해결했다.
https://velog.io/@jajup/Transform%EA%B3%BC-Rigidbody%EC%97%90-%EB%8C%80%ED%95%B4

2. 귀찮은 일

1인 개발이라는 건 귀찮은 일까지 모두 혼자서 해야 된다는 걸 알고는 있었지만 뒤늦게 체감했다. 이번에 깨달은 건데 회사에서 내가 계속 해오던 일은 "틀"을 만들어주는 것뿐이고 기획자분들이 항상 그 틀 안에 내용물을 채워줬었는데 이게 보통 힘든 일이 아니다.

퇴사 전: 대화 기능 만들어주기
1인 개발: 대화 기능 만들고, 대사 채우고, 연출 위치,타이밍 잡기

퇴사 전: 데이터를 가진 캐릭터를 만들어주기
1인 개발: 데이터를 가진 캐릭터를 만들고 수많은 캐릭터에 데이터 채워주고 밸런스 신경 쓰기

등등… 저 데이터 테이블 채우는 부분이 너무 끔찍해서 거기서 가장 하기 싫었다. 동료의 소중함을 한 번 더 깨달았다. 차라리 개발을 시켜달라.


개발 끝

40일이라는 기간을 잡았었고, 39일 차에 모든 게 끝났다.
라고 생각했었으나, 생각보다 구글의 검토는 까다로웠다.

메타데이터가 올바르지 않다, 광고가 올바르지 않은 연령대를 타겟팅하지 않는다 등등 메일도 꽤 주고받았다. 진상이었나 싶기도 했지만 재촉도 좀 했다. ㅠㅠ 그렇게 계획한 날짜에서 11일이 지난 후. 글 작성 날짜 기준 어제 프로덕션으로 게임이 잘 올라갔다.

 

너무 빠른 개발, 기간에 집중한 탓인지 만들어놓고 보니

게임의 스케일이나 기술이 "자 포트폴리오입니다!"라고 하기에는 너무 소박한 것 같다.

 

이제 친구한테 맥북 빌려서 애플에도 한 번 앱을 올려볼 계획이다. 회사에서 애플 빌드를 하시는 분이 계셔서 내가 만져볼 기회가 없었다. 애플도 검토 단계에서 리젝을 참 잘 준다고 들었던 것 같은데 미리 긴장해야겠다.

 

어차피 이거 조금 고쳐라 저거 조금 고쳐라 정도이지 않을까. 결과나 메일 기다리는 시간이 더 길게 뻔하다. 미뤄뒀던 여러 가지 공부도 병행해야겠다.

 


작성일: 2024년 10월 13일

'아카이브 > 개발,공부' 카테고리의 다른 글

벡터의 내적과 외적 -내적  (0) 2026.05.13
Transform과 Rigidbody에 대해  (0) 2026.05.12
군것질 후기  (0) 2026.05.12
유니티,깃 협업 환경 세팅 (팀원 공유용)  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12

작업 중 물체(GameObject)의 위치가 제대로 초기화되지않는 현상이 발견돼서, 소규모로 테스트를 만들어서 실행해보았다.

public class TransformTest : MonoBehaviour
{
    public Rigidbody2D _rb2D;
    public bool isRgMove;

    private void OnEnable()
    {
        SetPosition(Vector2.down);
    }

    private void OnDisable()
    {
        SetPosition(Vector2.up * 5);
    }

    protected void SetPosition(Vector2 newPosition)
    {
        if (isRgMove)
        {
            //_rb2D.position = newPosition;
            _rb2D.MovePosition(newPosition)
        }
        else
        {
            transform.position = newPosition;
        }
    }
}

물체를 enable/Disable 시에 isRgMove가 뭐던간에 게임오브젝트의 위치가 바뀌는것이 내가 생각한 정상적인 결과였다.

isRgMove가 true인 경우에는 생각한 대로 작동했지만 리지드바디를 사용한 이동은 약간의 지연이 있었다. 지연이 항상 있는것도 아니고, 10번에 3번정도 지연이 있었다. 그래도 그 작은 지연때문에 큰 버그가 생기는 중이었다.


RigidBody를 가진 물체의 이동

 transform.position = newPosition;

이 코드는 물체의 Position 을 직접 바꿔주는 코드다.
"이동시킨다" 보다는 순간이동에 가까운 코드다. 물리엔진을 우회하는 행위다.

순간이동 시에 게임오브젝트의 "위치"만 순간이동하기 때문에 Rigidbody나 Collider같은 물리적인 요소들은 같이 이동되지않고,뒤늦게 따라오게 되면서 리지드바디의 위치를 다시 계산해야한다.
(위의 컴포넌트가 없는 물체는 상관없다.)

_rb2D.position = newPosition

이 코드도 Transform이 아닌 리지드바디를조작하기는 하지만, 결국 포지션을 덮어씌움으로 물리엔진을 우회하고있다. transform.position보다는 빠르지만 안정적인 코드는 아니라고 알고있다.

그래서 RigidBody를 가진 물체를 이동시킬때에는 위의 두 코드보다,
아래의 코드가 빠르고 물리적으로 안정적이다.


Rigidbody.MovePosition

 _rb2D.MovePosition(newPosition)

이 친구는 Position을 직접 바꿔주는 코드처럼 물체를 순간이동 시키는 것이 아니라,
지정된 위치로 충돌,물리적 상호작용등을 고려해서 "이동"을 하는것이다.

굉장히 빠르고 안정적이라고 알고있다. 하지만 이것도 지연은 똑같이 있었다..


해결

(위의 테스트 말고) 실제 게임 내에서 문제가 됐던부분은
[물체 스폰 -> 순간이동] 의 흐름이 원활하지 않았던 것이다.

물리를 가진 물체를 순간이동 시키면 물리를 우회하고 안정적이지 않은 코드라고 앞서 말했었다.
그런데 게임오브젝트를 껐을때에는 물리도 같이 꺼진다.

그래서 스폰되기 전에 게임오브젝트가 꺼진 상태에서 transform.position을 사용해서 위치를 강제로 옮긴 후 게임오브젝트를 켰다.
[순간이동 -> 물체스폰] 의 순서로 바뀐것이다. 이것은 물리요소가 뒤따라오지도, 다시 계산할 필요도 없다고 한다.

_rb2D.MovePosition 을 사용해서 해결한 것은 아니지만 좋은 함수인것은 분명하다.


https://discussions.unity.com/t/there-is-a-delay-when-changing-rigidbody2d-position/1516558/2

한밤중에 궁금해서 이곳저곳 찾아보던 중 UnityDiscussion 커뮤니티에 가입해서 직접 질문해봤는데,
유니티 2D물리 담당 개발자분까지 나서서 답변을 성심성의껏 해주셨다.

해결방법은 너무 간단했지만, 과정중에 2D물리 이동에 대해서는 정말 찐하게 찾아보고 공부해본것같다.

감사합니다 Kurt-Dekker , MelvMay 선생님들...

Kurt-Dekker
https://discussions.unity.com/u/kurt-dekker/summary

MelvMay
https://discussions.unity.com/u/melvmay/summary

 

 


작성일: 2024년 9월 9일

'아카이브 > 개발,공부' 카테고리의 다른 글

벡터의 내적과 외적 -내적  (0) 2026.05.13
1인개발 후기  (0) 2026.05.12
군것질 후기  (0) 2026.05.12
유니티,깃 협업 환경 세팅 (팀원 공유용)  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12

컴퓨터에 연결해서 쓸 유선 헤드폰을 샀다.

스피커랑 헤드폰이랑 왔다갔다하면서 쓰고싶었고, 헤드폰 선은 계속 꽂아둔 채로 출력기기를 쉽게 전환 하고싶었다. 따로 제어판을 들어가지않고 클릭이나 키입력 한번으로 말이다..

ms스토어에 있는 프로그램인데, 이거면 생각한대로 단축키를 딸깍 누르는 것만으로 출력 기기를 바꿀 수 있었다.

그런데 디스코드에서는 선을 직접 꽂고 빼고 하는게 아닌 이상 출력기기가 바뀌지 않았다.



추측하는 이유는 이렇다.
디스코드 오디오 설정에는 Default라는 항목이 있다. 이렇게 설정해두면 윈도우에서 설정된 기본 통신장치에서 통화소리가 나게 된다.


윈도우 소리 제어판에서 기본 장치  기본 통신장치 라는 두 단어를 볼 수 있었다.
위에 언급한 Audio Device Switcher는 기본장치를 바꿔주는 친구였다. 검색했을때 찾아본 프로그램들도 그랬다.
기본 통신장치는 그대로여서 노래는 스피커에서 나오고 디스코드 통화음성은 헤드폰으로 들리는 상황이라고 판단했다.


이런걸 설정하는 간단한 프로그램은 만들 수 있을것같았다.
유니티가 아니라 컴퓨터 시스템에 접근하는건 처음이어서 기대도 됐다.

작업했던 과정을 하나하나 캡쳐해두질 않아서,, 내 생각의 흐름이라도 잊어먹기 전에 적어서 남겨둘려고한다. 만드는 과정이 되게 재미있기도 했다.

처음 생각한 기능

위에 맨처음에 적었던 말 중에 "선은 계속 꽂아둔 채로" 가 있는데, 선을 아예 뽑고 꽂을때에는 기본통신장치까지 같이 잘 전환이 됐다.


이 [사용안함]을 누르게되면 선을 뽑은것과 같은 취급이 되는것 같다. 디스코드와 노래가 출력되는 기기가 잘 바뀌었다.
그래서 선이 꽂혀있기는 하지만 프로그램을 실행하면 헤드폰을 [사용안함] 으로 설정하도록 만들 수 있을것같았다.

1차 시기

  1. 비주얼스튜디오 c# 콘솔앱 프로그램으로 프로젝트를 만들었다. 이유는 평소에 콘솔앱으로 실용적인것을 만들어보고싶기도 했고, 사실 익숙한게 비주얼스튜디오 c#뿐이라서 선택했다..ㅎ
  2. 연결된 기기에 접근하려면 nuGet이라는 패키지가 필요했고, 설치 후 이것저것 구경했다.
  3. 받은 패키지의 메소드로 연결된 기기들의 목록을 쿼리로 받을 수 있었고, 기기마다 ID, Name, State 등의 여러 프로퍼티가 있었다.
  4. 그 중 State는 사용안함,사용중,연결끊김 등의 상태들이 저장돼있었다.
  5. 하지만 어떤 수를 써도 그 State를 코드로 직접 바꿔줄 수는 없었다.. 애초에 Get밖에 안되는 친구였고 set할수있는 함수도 없어보였다.

2차 시기

  1. [사용안함] 처리하는게 아니라, 상황마다 적절한 장치를 기본장치와 기본통신장치로 동시에 설정해주면 어떨까 싶었다. 스피커를사용중이면 헤드폰을 설정하고,헤드폰을사용중이면 스피커를 설정하도록.
  2. 설정하려고 쿼리문을 많이 보고쓰게 됐다. 덕분에 WMI 테스터라는것도 알게됐다.
  3. 결국 기본장치설정도 비주얼스튜디오에서 직접 바꿔줄 수 없었다. 내 능력 부족일수도 있지만.. 내가 찾아보기에는 방법이 없어보였다.
  4. 그러다 PowerShell을 사용하면 설정해줄 수 있다는걸 알게됐다!!

3차 시기

  1. PowerShell을 관리자권한으로 실행 후, Get-AudioDevice -List 를 하면 현재 연결된 오디오 장치의 리스트를 얻어올 수 있었다.
    여기서 저 Default와 DefaultCommunication이 기본장치와 기본통신장치를 뜻한다는걸 눈치로 알 수 있었다.
  2. 다음은 디바이스를 Name으로 구분해서 상황에따라 다른 기기를 기본장치와 기본통신장치로 설정하도록 해주었다.
$defaultDevice = Get-AudioDevice -List | where Default -eq $true

if ($defaultDevice.Name -eq "스피커(4- High Definition Audio Device)") {
    Get-AudioDevice -List | where Type -like "Playback" | where name -like "헤드폰(4- High Definition Audio Device)" | Set-AudioDevice -Verbose
} elseif ($defaultDevice.Name -eq "헤드폰(4- High Definition Audio Device)") {
    Get-AudioDevice -List | where Type -like "Playback" | where name -like "스피커(4- High Definition Audio Device)" | Set-AudioDevice -Verbose
} else {
    Write-Host "기본 장치를 확인할 수 없습니다."
}
  1. 너무 잘 작동했다. 일단 여기서 댄스 한사바리 제꼈다..
  2. 메모장에 .cmd파일을 작성했다. 파워쉘을 실행하고 위의 코드를 실행시키도록 만들었다. 순조로웠고, 실행파일을 누르자 컴퓨터가 나한테 뱉은 답은 아래와 같았다.
  3. 湲곕낯 ?μ튂瑜??뺤씤?????놁뒿?덈떎.
    계속하려면 아무 키나 누르십시오 . . .
  1. 잠시 좌절했지만 내가 코드에 적었던 한글이 문제가 됐을거라 생각했다. 장치의 Name으로 식별했었기 때문에 헤드폰(4- High Definition Audio Device) 같은 한글이름이 들어가서 그랬던건데, 장치의 index로(숫자로) 식별하도록 더 간단하게 바꾸었고, 오류시 뱉을 텍스트도 영어로 바꾸었다.
  1. 실행파일이 완성됐고 잘 작동하는것을 확인했다.

물론 디스코드까지 쉽게 전환되는 프로그램이 구글에 이미 있는데 내가 못찾은것 일수도 있다. 한 10분 찾아보면 있을거같기도 하다..

게임프로그래밍이 아니라 윈도우에 접근하는게 너무 어색했고 용어나 툴부터 여러가지 어려움이 있었다.
그래도 매일먹던밥 먹다가 군것질 한번 하니까 오히려 내가 하는 일에 더 흥미가 붙은 기분이다.

진짜 재미있었다.

 
 

 


작성일: 2024년 8월 20일

'아카이브 > 개발,공부' 카테고리의 다른 글

1인개발 후기  (0) 2026.05.12
Transform과 Rigidbody에 대해  (0) 2026.05.12
유니티,깃 협업 환경 세팅 (팀원 공유용)  (0) 2026.05.12
객체지향 프로그래밍  (0) 2026.05.12
Generic  (0) 2026.05.12

+ Recent posts