프로젝트 개요Project Overview
2019년에 시작한 오픈월드 게임이다. 내가 스토어에 처음 올린 프로젝트이기도 하다. 기여도는 100%, 즉 혼자 만들었다.
This is an open-world driving game I started in 2019. It was also the first project I ever published to a store. I built 100% of it on my own.
| 항목 | 내용 |
|---|---|
| 시작 | 2019년 6월 |
| 엔진 | Unity 2021.3.45f2 |
| 기여도 | 100% (1인 개발) |
| 리소스 | 일부는 상업 이용 가능한 무료 에셋, 나머지는 직접 제작 (3D · 2D · 애니메이션) |
| 상태 | 출시 완료. 2022년을 기점으로 낮은 API 버전 문제로 스토어에서 내려감 |
| Item | Details |
|---|---|
| Started | June 2019 |
| Engine | Unity 2021.3.45f2 |
| Role | 100% (Solo development) |
| Assets | Mix of commercially free assets and custom-made work (3D, 2D, animations) |
| Status | Released. Delisted from the store around 2022 due to outdated target API level |
Unity 입문 직후에 오픈월드 멀티플레이라는 기획으로 시작했다. 지금 돌아보면 무모한 규모였지만, 그 과정에서 코드 작성 능력과 유니티 전반의 사용법을 익혔다.
Right after picking up Unity, I jumped straight into planning an open-world multiplayer game. Looking back, the scope was reckless for a beginner, but wrestling with it is how I learned to write code and use Unity end to end.
탈 것을 붙이는 방식Implementing Vehicles
오픈월드에 탈 것을 추가하기 위해 여러 방법을 시도했다. Unity가 표준으로 제공하는 차량부터 강좌에서 알려주는 방식까지 다양하게 적용해 봤다.
To put drivable vehicles into the world, I tried several approaches—from Unity's standard vehicle prefabs to various setups from online tutorials.
그 결과 각각의 스크립트를 확인해 보면서, 여러 차량에 공통으로 적용할 수 있고 기능을 쉽게 추가할 수 있는 형태로 합쳤다. 차량마다 따로 스크립트를 두지 않고 하나로 모은 것이 이후에 도움이 됐다.
After inspecting how each script worked, I merged them into a single unified vehicle script that could be shared across different cars and extended easily. Avoiding separate scripts per vehicle paid off later when adding new vehicle types.
- 트럭을 추가할 때 4륜 고정이 아니라 6륜으로 늘릴 수 있었다.
- FWD · RWD · AWD 구동 방식을 구분해서 쓸 수 있게 되어 주행감을 좀 더 현실적으로 만들 수 있었다.
- When adding trucks, I could expand the wheel setup to 6 wheels instead of being locked to 4.
- Supporting FWD, RWD, and AWD drivetrain modes made it possible to give each vehicle more believable handling.
트레일러 연결Trailer Hitching
유로트럭이라는 시뮬레이션 게임의 트럭·트레일러 시스템에 매력을 느껴서, 같은 것을 만들어 보고 싶었다. 여러 번 시행착오를 거친 끝에 트레일러를 연결했을 때 자연스럽게 따라 움직이도록 구현했다.
I liked the truck-and-trailer mechanics in Euro Truck Simulator and wanted to build something similar. After plenty of trial and error, I got the trailer hitch working so the trailer articulated naturally behind the cab.
NPC 레이싱 AINPC Racing AI
체크포인트 시스템을 기반으로 NPC 차량 알고리즘을 만들었다. 길을 이탈했을 때 3가지 패턴으로 반응하며, 여기에는 현실적인 진로 변경 로직이 포함된다.
I built the NPC vehicle controller around a checkpoint system. When an NPC car goes off course, it reacts using one of three recovery patterns, alongside basic lane-change and steering logic.
애니메이션과 상호작용Animations and Interactions
캐릭터 애니메이션은 처음에 구해와서 일부만 수정해 썼다. mixamo처럼 무료로 받을 수 있는 곳에서 애니메이션을 내려받고, 필요 없는 부분의 키프레임을 삭제한 뒤 위치를 조정하는 식이었다.
At first, I downloaded free character animations from sites like Mixamo and tweaked them—deleting unnecessary keyframes and adjusting bone positions to fit.
상호작용의 양을 늘리려다 보니 그것만으로는 부족해서, 이후에는 애니메이션을 직접 제작하고 수정했다. 덕분에 제작 시간을 줄이면서 원하는 애니메이션을 추가할 수 있게 됐다.
Once I started adding more world interactions, tweaked stock animations were no longer enough. I switched to authoring and editing animations myself, which cut down turnaround time whenever I needed a specific motion.
상호작용은 실내 가구, 차량 주유 같은 단순하면서도 게임 요소를 늘릴 수 있는 것들부터 추가해 나갔다.
For interactions, I started with simple things that immediately gave the player something to do, like interacting with indoor furniture and refueling vehicles.
로딩 화면Loading Screen
메인 메뉴에서 씬을 넘어갈 때 쓴 코드다. 당시 작성한 그대로 옮긴다.
Here is the scene transition coroutine I used in the main menu, copied verbatim from the original project.
IEnumerator LoadScene(string name)
{
lodingImg.SetActive(true);
yield return null;
AsyncOperation op = SceneManager.LoadSceneAsync(name);
op.allowSceneActivation = false;
float timer = 0.0f;
while (!op.isDone)
{
yield return null;
timer += Time.deltaTime;
if (op.progress < 0.9f)
{
progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, op.progress, timer);
if (progressBar.fillAmount >= op.progress)
{
timer = 0f;
}
}
else
{
progressBar.fillAmount = Mathf.Lerp(progressBar.fillAmount, 1f, timer);
if (progressBar.fillAmount == 1.0f)
{
op.allowSceneActivation = true;
yield break;
}
}
}
}
이 코드가 하는 일What This Code Does
allowSceneActivation 을 false 로 막아두면 씬 로딩이 끝나도 전환되지 않는다. 로딩바를 끝까지 채운 다음 직접 true 로 바꿔 전환시키기 위한 구조다.
Setting allowSceneActivation to false prevents Unity from switching scenes even after loading finishes. The idea is to wait until the UI progress bar fills all the way up, then flip it to true manually.
Unity의 AsyncOperation.progress 는 allowSceneActivation 이 false 일 때 0.9에서 멈춘다. 나머지 0.1은 씬 활성화 몫이라 활성화를 허용하기 전까지는 올라가지 않는다. 위 코드가 0.9f 를 기준으로 분기를 나눈 이유가 이것이다.
In Unity, AsyncOperation.progress stops at 0.9 while allowSceneActivation is false. The remaining 0.1 is reserved for the actual scene activation step. That is why the code branches at 0.9f.
지금 다시 보면 보이는 것Looking Back at This Code Now
당시에는 동작하니까 넘어갔지만, 지금 코드를 읽으면 걸리는 부분이 몇 군데 있다. 기록해 둔다.
At the time, it worked so I moved on. Reading it now, a few things stand out.
Mathf.Lerp의 세 번째 인자에 누적 시간을 넣었다.Lerp(a, b, t)의t는 0~1 비율인데timer는 초 단위로 계속 커진다. 목표에 닿으면timer를 0으로 되돌려 다시 시작하는 방식이라 결과적으로 굴러가긴 하지만, 프레임 레이트에 따라 채워지는 속도가 달라진다.Mathf.MoveTowards로 초당 증가량을 지정하는 편이 예측하기 쉽다.fillAmount == 1.0f로 부동소수를 정확 비교한다. 이 경우는Lerp의t가 1 이상이면 정확히1f를 반환하므로 통과하지만, 일반적으로는>= 1f - 0.001f처럼 여유를 두는 편이 안전하다.- 변수명
lodingImg는loading의 오타다. 동작에는 문제가 없지만 그대로 남아 있다.
- Passing accumulated time as the third argument to
Mathf.Lerp. InLerp(a, b, t),tis a 0–1 interpolation factor, whereastimerkeeps growing in seconds. Resettingtimerto 0 whenever the bar reaches the target makes it work by accident, but the fill speed depends on frame rate. UsingMathf.MoveTowardswith a fixed rate per second would be much more predictable. - Comparing floats with exact equality (
fillAmount == 1.0f). It passes here becauseLerpclamps and returns1foncet >= 1, but in general it is safer to check>= 1f - 0.001f. - The variable name
lodingImgis a typo forloading. It does not affect behavior, so it stayed there.
데이터 유실과 복구Data Loss and Recovery
2022년, 작업하던 컴퓨터가 고장 나면서 프로젝트 파일 대부분을 잃었다.
In 2022, my development PC died and took most of the project files with it.
그해에는 다른 활동과 맞물려 잠시 손을 놓았지만, 스토어 출시의 핵심인 key.store 파일만은 포기할 수 없었다. 이 파일이 없으면 같은 앱으로 업데이트를 올릴 수 없기 때문이다. 2023년부터 복구를 시도했고, 2025년에 복구에 성공했다.
I was busy with other things that year and had to set the project aside, but the one file I could not afford to lose was key.store. Without it, I could never push an update to the existing store listing. I started recovery attempts in 2023 and finally recovered the keystore in 2025.
현재Current Status

출시 자체는 완료했지만 2022년을 기점으로 낮은 API 버전 문제로 스토어에서 내려갔다. 구글 플레이는 일정 시점마다 앱이 대상으로 하는 API 레벨의 하한을 올리는데, 그 기준을 넘기지 못한 앱은 신규 노출에서 제외되고 업데이트도 거부된다.
Although the game was published, it was taken down from the store around 2022 due to a low target API level. Google Play periodically raises the minimum required target API level, and apps that fall behind are hidden from new users and blocked from updates.
복구한 key.store 와 과거 기획 자료를 바탕으로 재개발에 착수했다. 재개발은 Unity로 진행하고 있다.
With the recovered key.store and my old design notes in hand, I have started rebuilding the game in Unity.
정리Wrap-Up
성과라고 적어둘 만한 것은 두 가지다. 첫 출시작으로 1000회 이상 다운로드를 기록한 것, 그리고 당시 실력으로 오픈월드를 만들겠다는 집념 하나로 끝까지 짜냈다는 것.
Two outcomes are worth noting: recording over 1,000 downloads on my very first store release, and stubbornly pushing an open-world project across the finish line with the skills I had at the time.
그래픽 리소스를 직접 만들면서 겪은 어려움은 나중에 팀으로 작업할 때 그래픽 작업자의 상황을 이해하고 조율하는 데 도움이 됐다. 오픈월드라는 큰 기획을 구현부터 유실, 복구까지 겪으면서 프로젝트에 필요한 기초 설계와 개발 전 과정을 몸으로 익혔다고 생각한다.
Struggling through custom 3D and 2D asset creation on my own later helped me understand and coordinate with artists when working in teams. Going through the full cycle—building a large-scale project, losing the data, and recovering it—taught me the fundamentals of project structure the hard way.
복잡한 물리와 NPC 알고리즘을 독학으로 구현해 낸 것도 이 프로젝트에서 얻은 것이다. 아직 끝나지 않았다.
Figuring out vehicle physics and NPC driving algorithms from scratch was another takeaway. And the project is not over yet.