예시 - 스카이림 드웨머 유적 키트와 결과물

 

레벨 디자인을 할 때 배경 에셋을 마치 레고처럼
정해진 규격에따라 미리 만들어두고 조립하는 방식을 모듈식 레벨디자인이라고 합니다.

어느정도 규모있는 게임들은 모듈식 레벨디자인을 사용하고 있으며 다음과 같은 장점이 있습니다.

- 재사용 가능
- 쉽게 배치하고 수정할 수 있음
- 최적화에도 어느정도 도움됨

 

 

 

모듈식 레벨 디자인 기초

(기본적으로 정해야하는 것 + 지켜져야하는 사항들)

 

 

용어 정리

 

이게 모든 회사 (특히 우리나라)에서 공통적으로 사용하는 용어인지는 모르겠습니다.
베데스다 기준 + 지금 우리 팀에서 진행중인 프로젝트를 기준으로 작성됨
적당히 이해하기 좋게 의역함

 

  • 키트 : 모듈 모음집 (테마별로 구분)
  • 범위 : 모듈 하나가 차지할 수 있는 범위
  • 규격 : 문, 창문 등 모듈 사이의 통로에 사용되는 항목의 규격
  • 모듈 : 최종적으로 엔진에서 배치하는 것
  • 접착제 : 모듈을 그리드에 맞추지 않고 배치했을 때 틈을 메꾸는 파츠
  • 플러그/소켓 : 문, 창문등의 구멍과 틀
  • 부품 : 모듈을 구성하는 기본 요소 (바닥, 벽, 천장 등)

 

 

 

범위 정하기

 

플레이어를 기준으로 정육면체의 기본 범위(Foot Print)를 정해야 합니다.

 

 

 

범위에 벗어난 모듈을 만들지 않기

 

기본 범위는 플레이어가 돌아다닐 수 있는 공간의 크기를 의미하는 것이 아니라
모듈의 모델이 속한 공간을 의미합니다

정해전 범위 외부까지 모델링하면 모듈들끼리 겹쳐보이거나
z-fighting 등의 이슈가 발생할 수 있습니다.

 

 

 

범위 확장

 

만약 높은 통로처럼 기본 범위안에 들어갈 수 없는 모듈이라면
기본 범위의 배수만큼 늘려 활용할 수 있습니다. (256 → 512 → 768 …)

배수로 늘려야 규칙을 유지할 수 있음

 

 

 

피봇 정하기

 

 

보통 모듈의 하단 + 중앙에 피봇을 설정합니다.

당연하지만 한번 정한 피봇을 변경하면 기존에 배치해둔 레벨이 망가지니 주의해야 합니다.

 

 

 

규격 정하기

 

 

문이나 창문같은 모듈 사이의 통로에 사용되는 항목의 규격을 정해야 합니다.

 규격을 정해야하는 이유?

  • 다른 종류의 모듈끼리 잘 연결될 수 있음
  • 문을 통과하는 캐릭터를 설계할 때 (AI 혹은 애니메이션) 발생할 수 있는 문제를 줄일 수 있음

 

 

 

플러그와 소켓

 

 모듈 사이의 통로에 플러그를 추가해 자연스럽게 연결할 수 있습니다.

 

 

 

(응용편)

 

그리드에서 벗어나기

 

모든 것을 그리드에 맞춰 배치하면 레벨이 지루하게 느껴질 수 있습니다.

그리드에 벗어나게 모듈을 배치하고
모듈의 틈을 기둥 등 다른 모델(접착제)로 채우면 자연스러운 배치가 가능해집니다.

 

동굴 벽을 자연스럽게 배치한 모습 예시

 

일직선 복도를 비틀어서 자연스러운 통로로 배치한 예시

 

 

 

모듈 겹치기

 

 

구멍이 있는 모듈들을 같이 배치해 다양한 조합을 만들 수 있습니다.

 

 

 

모듈 만들기

모델링

 

 

위 이미지처럼 범위에 맞는 모델을 통째로 모델링해 사용할 수도 있지만

 

 

모듈 단위가 아니라 모듈의 부품을 다 분해해 제작하고
엔진에서 모듈 단위로 다시 조립하여(프리팹) 사용하면 제작 비용을 아낄 수 있습니다.

 

 

 

 

참고자료


레벨디자인 북 (겁나 유용한 무료 레벨디자인 가이드 북)
https://book.leveldesignbook.com/process/blockout/metrics/modular

스카이림의 모듈러 레벨디자인 (2013 GDC 강연)
http://blog.joelburgess.com/2013/04/skyrims-modular-level-design-gdc-2013.html

폴아웃4의 모듈러 레벨디자인 (2016 GDC 강연) (스카이림 강연의 확장팩 같은 느낌)
(이 강연에서 많이 참고했습니다)

 

https://www.slideshare.net/JoelBurgess/gdc-2016-modular-level-design-of-fallout-4

 

 

블로그 이미지

stuban

ㅇ.ㅇ

,

https://youtu.be/SuR9ZSycV5E?si=NxrEhPDd490Mx1KQ

 

 

지금 스팀에서 플레이할 수 있습니다.


 

Devilish League on Steam

Swing, hit, and bounce! Blast enemies with your heavy bat in this hilarious cartoon roguelike!

store.steampowered.com

 

데빌리쉬 리그는 배트 스윙 액션이 가미된 카툰 로그라이크 게임입니다.
모든 것을 날려버리는 시원한 전투, 수십 가지의 독특한 아이템들로 중독성있는 모험을 즐겨보세요!

 

개발 기간 : 2023년 3월 ~ 2024년 1월
인원 : 17명 (기획 3, 플밍 2, 아트 11, QA 1)
개발환경 : 유니티, 젠킨스, 파이썬
레포지토리 : https://github.com/Cuty444/QT_Proto (졸작들 중 유일하게 전체 코드 공개 ㅋㅋ)


 

프로그래머가 2명이 있었지만 다른 프로그래머는 조기 취업으로 인해 작업량에 한계가 있었으므로
2학기부터는 사실상 혼자 작업하는 형태였습니다...

저는 프로그래머로서 적과 전투아이템, 스탯과 버프각종 툴 개발빌드 과정 관리 등을 담당하였습니다.

 


학생 프로젝트지만 최대한 프로답게 (프로세스 & 퀄리티 & 재미) 만들고 싶었습니다.

그래서 최대한 유지보수 하기 좋은 형태로 코드를 설계하고
엔진에 익숙하지 않은 작업자도 쉽게 작업할 수 있는 다양한 툴을 개발하고
개발한 것들에 대한 문서화를 지속적으로 진행하고
청강대 졸작 최초(아마도?)자동 빌드 시스템을 구축하였습니다.

 

노션으로 정리한 이 프로젝트에 대한 자세한 포트폴리오는 여기를 참고하세여

'내가 만든거' 카테고리의 다른 글

길따라 로봇 road robot 제작기(BIC 전시기)  (1) 2019.09.25
등산 시뮬레이터  (0) 2018.06.23
맵 에디터 !!!!  (0) 2018.05.11
유니티 스와이프  (0) 2018.02.20
유니티 사운드 매니저  (2) 2017.08.22
블로그 이미지

stuban

ㅇ.ㅇ

,

 

스프라이트 애니메이션을 개발하며

유니티 애니메이션 클립 미리보기같은 느낌으로
에디터에서 애니메이션을 미리보는 기능을 만들었다

 

개발하다보니 문제가 생겼는데

에디터의 OnInspectorGUI나 OnPreviewGUI 같은 함수는
"마우스가 인스펙터 위로 지나갈 때"만 다시 그려진다는 것

https://docs.unity3d.com/ScriptReference/Editor.RequiresConstantRepaint.html

요 함수를 오버라이드해 주기적으로 Repaint를 요청할 수 있다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;
using UnityEditor;
 
[CustomEditor(typeof(AnimationData), true)]
public class AnimationDataEditor : Editor
{
    private double lastTime;
 
 
    public override bool RequiresConstantRepaint() => true;
 
    public override void OnInspectorGUI()
    {
        // EditorApplication.timeSinceStartup 을 사용해 에디터에서 시간 측정을 할 수 있다
        var currentTime = (float) (EditorApplication.timeSinceStartup - lastTime); 
 
        ...
    }
}
cs

 

블로그 이미지

stuban

ㅇ.ㅇ

,

 

유니티 패키지 매니저를 통해 Newtonsoft Json을 설치할 수 있지만
패키지 목록에서는 찾을 수 없고 
무조건 Add package by name 을 통해 추가해야 한다

(프로젝트 세팅의 enable pre-release packages 옵션과 관련 없음)

com.unity.nuget.newtonsoft-json

 

 

 

유니티 내장 json 툴을 사용하는 것을 권장하고
굳이 newtonsoft json쓸거면 패키지를 통해 설치하라고 한다

근데 왜 패키지 매니저 목록에 안나오게 하고
프로젝트 버전 바꿀때 마다 추가했던 패키지가 사라지는데???

https://docs.unity3d.com/Packages/com.unity.nuget.newtonsoft-json@3.2/manual/index.html

블로그 이미지

stuban

ㅇ.ㅇ

,

[프로그래밍/유니티] - 유니티 좌표에 따른 정렬 기준 바꾸기

기존에는 Edit->Project Setting->Graphics->Transparency Sort ModeCoustom Axis로 설정해
좌표에 따른 소팅 오더를 쉽게 세팅할 수 있었다

 

그런데 최근 URP로 전환하며 기존에 CoustomAxis를 설정하는 필드가 사라져버렸다

정확히는 숨어버린 것인데

프로젝트 경로\ProjectSettings\GraphicsSetting.asset

위 경로의 파일을 메모장으로 열고

TransparencySortModeTransparencySortAxis를 직접 바꿔주면 된다.

 

CustomAxis로 사용하기 위해서는

m_TransparencySortMode를 3으로 설정하고
TransparencySortAxis를 원하는 값으로 바꾼 뒤
프로젝트를 재시작하면 적용된다.

 

+ URP 2D에서는 Renderer 2D 설정에서 TransparencySortMode를 설정할 수 있다.
GraphicsSetting에 작성한 TransparencySortModeRenderer2D 설정으로 덮어 씌워진다

블로그 이미지

stuban

ㅇ.ㅇ

,

 

맥과 윈도우를 번갈아서 사용할 때
윈도우에서 작성한 파일을 맥에서 수정하는 경우 한 글자만 변경하더라도 파일의 모든 부분이 변경되었다고 표시되었다.

OS마다 줄 끝을 처리하는 방식이 달라서 생기는 문제였는데.

git config core.autocrlf

위 설정을 통해 깃이 줄 끝을 자동으로 처리하도록 설정할 수 있다.

 

맥/리눅스의 경우 autocrlf를  input 구성으로 설정하면 문제가 해결된다.

$ git config --global core.autocrlf input
# Configure Git to ensure line endings in files you checkout are correct for macOS

이러면 깃이 알아서 줄 끝을 맞춰준다

 

아니면 윈도우에서 autocrlf를 true로 설정하면 된다.

$ git config --global core.autocrlf true
# Configure Git to ensure line endings in files you checkout are correct for Windows.
# For compatibility, line endings are converted to Unix style when you commit files.

autocrlf를 true로 설정하면 애초에 줄 끝을 유닉스 스타일로 맞춰줘
맥이나 리눅스에서도 호환성 문제 없이 작업할 수 있다.

 

https://docs.github.com/en/get-started/getting-started-with-git/configuring-git-to-handle-line-endings

 

Configuring Git to handle line endings - GitHub Docs

About line endings Every time you press return on your keyboard you insert an invisible character called a line ending. Different operating systems handle line endings differently. When you're collaborating on projects with Git and GitHub, Git might produc

docs.github.com

 

 

'프로그래밍' 카테고리의 다른 글

모든 질문에 답을 해주는 사이트  (0) 2017.10.16
블로그 이미지

stuban

ㅇ.ㅇ

,

 

https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@16.0/manual/features/rendering-debugger.html

 

Rendering Debugger | Universal RP | 16.0.0

Rendering Debugger The Rendering Debugger window lets you visualize various lighting, rendering, and Material properties. The visualizations help you identify rendering issues and optimize Scenes and rendering configurations. This section contains the foll

docs.unity3d.com

 

urp를 사용할 때

컨트롤 + 백스페이스 혹은
게임패드의 양쪽 스틱을 동시에 누르면

렌더링 디버그 창을 띄운다

 

나 같은 경우는 게임패드의 양쪽 스틱을 동시에 눌러 발동하는 스킬을 개발하고 있었는데
디버그 창을 띄우는 단축키와 동일해 문제를 겪고 있었다
(심지어 공식 문서에서도 끄는 법이 안나와 있음)

렌더링 디버그 창을 띄우는 단축키를 변경하고 싶다면

DebugManager.Actions.csRegisterInputs() 함수 내용을 수정해주면 된다.
(12.1.9 버전 기준 295번줄)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void RegisterInputs()
    {
#if UNITY_EDITOR && !USE_INPUT_SYSTEM
    var inputEntries = new List<InputManagerEntry>
    {
        new InputManagerEntry { name = kEnableDebugBtn1,  kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left ctrl",   altBtnPositive = "joystick button 8" },
        new InputManagerEntry { name = kEnableDebugBtn2,  kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "backspace",   altBtnPositive = "joystick button 9" },
        new InputManagerEntry { name = kResetBtn,         kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left alt",    altBtnPositive = "joystick button 1" },
        new InputManagerEntry { name = kDebugNextBtn,     kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "page down",   altBtnPositive = "joystick button 5" },
        new InputManagerEntry { name = kDebugPreviousBtn, kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "page up",     altBtnPositive = "joystick button 4" },
        new InputManagerEntry { name = kValidateBtn,      kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "return",      altBtnPositive = "joystick button 0" },
        new InputManagerEntry { name = kPersistentBtn,    kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "right shift", altBtnPositive = "joystick button 2" },
        new InputManagerEntry { name = kMultiplierBtn,    kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "left shift",  altBtnPositive = "joystick button 3" },
        new InputManagerEntry { name = kDPadHorizontal,   kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "right",       btnNegative = "left", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
        new InputManagerEntry { name = kDPadVertical,     kind = InputManagerEntry.Kind.KeyOrButton, btnPositive = "up",          btnNegative = "down", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
        new InputManagerEntry { name = kDPadVertical,     kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Seventh, btnPositive = "up",    btnNegative = "down", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
        new InputManagerEntry { name = kDPadHorizontal,   kind = InputManagerEntry.Kind.Axis, axis = InputManagerEntry.Axis.Sixth,   btnPositive = "right", btnNegative = "left", gravity = 1000f, deadZone = 0.001f, sensitivity = 1000f },
    };
 
    InputRegistering.RegisterInputs(inputEntries);
#endif
 
#if USE_INPUT_SYSTEM
    // Register input system actions
    var enableAction = debugActionMap.AddAction(kEnableDebug, type: InputActionType.Button);
    enableAction.AddCompositeBinding("ButtonWithOneModifier")
        .With("Modifier""<Gamepad>/rightStickPress")
        .With("Button""<Gamepad>/leftStickPress")
        .With("Modifier""<Keyboard>/leftCtrl")
        .With("Button""<Keyboard>/backspace");
 
    ...    
 
#endif
    }
}
cs

 

난 인풋 시스템을 사용하고 있고
디버그 창을 띄우는 키를 게임패드의 스타트와 메뉴 버튼으로 바꿨다

1
2
3
4
5
6
var enableAction = debugActionMap.AddAction(kEnableDebug, type: InputActionType.Button);
enableAction.AddCompositeBinding("ButtonWithOneModifier")
    .With("Modifier""<Gamepad>/start")
    .With("Button""<Gamepad>/menu")
    .With("Modifier""<Keyboard>/leftCtrl")
    .With("Button""<Keyboard>/backspace");
cs

 

블로그 이미지

stuban

ㅇ.ㅇ

,

최근 알고리즘 공부가 필요할 것 같아서 고민하다

프로그래머스 라는 코딩 테스트 사이트에 도전하기로 마음먹게 되었다

프로그래머스에는 코딩테스트 고득점 Kit라는 섹션이 있는데

여기 있는 Kit의 유형들을 하나씩 공부할 예정

(참고로 유형에 있는 문제 해답은 공유 안할거임)

 

그래서 오늘 주제는 '해싱'이다

 

 


해싱은 키 값을 가지고 자료를 저장하는 방식을 말한다

 

 

보통 자료를 저장하는 곳은 배열로 되어있고 해시 테이블이라고 불리고

키 값을 이용해서 배열의 주소를 구하는 방식으로 되어있다

이때 키 값을 주소로 바꾸기 위해 해시 함수를 사용한다

 

해시 함수를 사용하면 자료가 있는 곳까지 바로 안내하기 때문에

이상적으로는 시간 복잡도가  O(1) 이 된다.

 

 

대강 그림을 그리자면 다음과 같다

 

 

> 해시 함수

 

해시 함수는 키 값을 해시 테이블의 주소로 변환하는 역할을 한다

 

해시 함수를 만들때 가장 중요하게 봐야할 것중 하나는 충돌 이다

 

해시 테이블의 크기는 한정되어있기 때문에 한 주소에 여러개의 키가 들어갈 수 있는데

이것을 충돌이라고 한다

 

충돌 예시

 

충돌이 많이 일어나면 자료를 다른 곳에 저장해야하고

그 다른 곳을 찾는 연산을 추가로 해야하기 때문에 탐색 속도가 느려지게 된다

 

따라서 좋은 해시 함수는 

1. 충돌이 적어야하며

2. 해시 함수 값이 해시 테이블의 주소 영역 내에 골구로 분포되어야하고

3. 계산이 빠를 수록 좋다

 

 

 

>해시 테이블

 

지금까지 나온 해시 함수중 충돌이 없는 함수가 없다고 한다

따라서 테이블도 충돌에 대비한 설계가 필요한데

 

2가지 정도의 해결책이 있겠다

 

1. 조사 (probing)

 

조사법은 특정 버킷(테이블의 구성요소를 버킷이라고 부른다)에서 충돌이 일어나면

해시 테이블에서 비어있는 다른 버켓을 찾는 방법이다

 

>선형 조사법 (linear probing)

 

만약 해시 테이블의 [n]번 버킷에서 충돌이 일어나면 [n+1]번 버킷이 비어있는지 확인하고

만약 거기도 차있으면 [n+2]를 살펴보는 식으로 차례차례 빈 공간을 찾는 것을 선형 조사법이라고 한다

 

 

>이차(제곱) 조사법 (quadratic probing)

 

선형조사법이 차례대로 조사하는 것이라면 이번에는 제곱수만큼 움직이는 것이다

만약 [n]번 버킷에서 충돌이 일어나면 [n+1²] 번으로 그 다음은 [n+2²], [n+3²], [n+4²] ...

이런식으로 찾아가는 것이다

 

>이중 해싱 (재해싱)  (double hashing)

 

이중 해싱은 해시 함수를 2개 만들어두고

만약 충돌이 일어나면 2번째 해시함수로 다시 해싱을 하는 방식이다

 

 

 

2. 체이닝 (chaining)

 

위와 같은 경우에는 버킷 하나당 고정된 양의 자료만 저장할 수 있어서

버킷이 꽉 차면 모든 테이블을 돌아다니며 자리를 찾아야 했고

해시 주소가 다른 탐색키와도 비교를 해야하기 때문에 시간이 많이 걸릴 수 있었다

 

만약 버킷에 슬롯을 만들어서 한번에 같은 해시 주소를 가진 여러개의 자료를 저장할 수 있게 한다면 어떨까?

 

체이닝은 연결리스트를 사용해 슬롯을 만든다

연결리스트를 사용하기 때문에

충돌이 일어나도 오버플로(버킷이 꽉 차는 경우)가 발생할일이 없고

삽입과 삭제가 수월해진다

 

 

 

 


 

 

하지만 실제로 프로젝트를 진행하면서 (심지어 프로그래머스 코딩테스트에서도)

별 특이한 경우가 아니고서야 실제로 사전 구조를 처음부터 만드는 일은 없을 것이다

그래서

 

C++에서는

<map>의 map

을 사용하고

 

C#에서는
System.Collections.Generic 의 Dictionary

를 사용할 수 있겠다

 

근데 프로그래머스 고득점 Kit 해시 문제 중에 절반은 해시 안 쓰는 게 더 간단하던데... 뭐지?

'프로그래밍 > 알고리즘,자료구조' 카테고리의 다른 글

퀵 정렬 (Quick sort)  (0) 2018.06.13
블로그 이미지

stuban

ㅇ.ㅇ

,