-
[UE5] 퍼즐 진행 상황 기록 기능언리얼엔진/노노그램 2023. 11. 8. 17:23
난이도가 높은 퍼즐은 푸는데 시간이 오래걸려서 일단 상황을 기록해두고 나중에 다시 이어서 하는 경우가 있다.
그래서 퍼즐을 푸는중에 나가면 퍼즐의 마지막 상황을 기록하게 했다.
그렇다면 퍼즐이 진행중이라는 것을 사용자에게 표현해줘야 하는데 이 부분은 모바일게임인 No2g의 방식을 따라했다.
No2g에서 진행중인 퍼즐을 보여주는 방법 No2g에서는 진행중인 퍼즐의 마지막 상황을 퍼즐의 썸네일에 보여준다.
그리고 완료한 퍼즐은 아래처럼 보여준다.
No2g에서 완료한 퍼즐을 보여주는 방법 완료한 퍼즐의 결과물을 보여주는 것은 성취감을 느끼게 하는데 필수적이라고 생각해서 이것도 따라해보기로 했다.
목표는 정했으니 구현 방식을 정해야한다.
퍼즐을 완료한 상태는 정해진 답이 있으니 결과물이 정해져있지만, 퍼즐이 진행중인 상태에는 상황에 따라서 달라지므로 동적으로 변경할 수 있는 방법을 고민해봐야한다.
일단 현재 퍼즐을 푸는 화면은 사용자 위젯의 그리드 패널을 사용하고있고, 각 칸에 이미지를 하나씩 띄우는 방법을 사용하고있다.
이 방식을 이용해서 퍼즐의 썸네일에 그리드 패널을 넣고 각 칸에 이미지를 넣으면 위와 같이 구현은 가능하다.
하지만 퍼즐의 썸네일은 게임 화면에 하나씩만 나오는 것이 아니고 여러개의 썸네일이 동시에 여러개 나타나야한다.
그리고 퍼즐의 썸네일은 타일 뷰에 들어있어서 최적화를 위해 썸네일 아이템이 화면에 뜰 때마다 사용자 위젯이 다시 설정된다.
사용자 위젯에서 리스트 위젯에 해당하는 목록 뷰(List View), 타일 뷰(Tile View) 등은 스크롤 박스처럼 여러개의 항목을 넣고 스크롤 하는 방식으로 보여준다.
그런데 항목의 개수가 많아지면 버벅이는 등의 문제가 생길 수 있기 때문에 리스트 위젯은 가상화를 사용한다.
리스트 위젯에는 위젯을 넣는 것이 아니라 위젯에 표시되어야 할 정보를 가지고있는 오브젝트를 넣고, 위젯은 클래스만 지정해둔다.
그러면 리스트 위젯은 지정된 위젯을 필요한 만큼만 생성하고, 스크롤 할 때 화면에서 사라진 위젯을 화면에 새로 등장한 위젯에 다시 지정해주고, 해당하는 항목 오브젝트의 정보를 통해 위젯을 새로 설정하는 방식을 사용한다.
따라서 퍼즐의 썸네일에 그리드 패넣을 넣어서 퍼즐의 정답에 맞게 각 칸에 이미지를 띄우는 방법을 사용하면, 스크롤 될 때마다 그리드 패널을 초기화 하고 이미지를 생성해서 색을 변경하고 그리드 패널에 넣는 작업을 계속 반복하게 된다.
이 과정을 한번 하는 것은 문제가 없지만 짧은 시간에 여러번 반복하게되면 프로세서에 부담이 되고 버벅이는 문제가 심해진다.
그러므로 다른 방식을 사용해야한다.
그래서 여러개의 이미지가 들어가는 그리드 패널 대신 단일 이미지를 띄우는 방법으로 구현이 가능할지 고민했다.
이전에 슬레이트 위젯을 만들었던 경험을 이용해서 슬레이트 위젯에서
FSlateDrawElement::MakeBox
함수를 적절히 사용하면 그리드 패널처럼 이미지의 원하는 영역에 원하는 색으로 사각형을 채울 수 있지 않을까 싶었다.하지만
MakeBox
함수에는 사각형이 채워질 영역을 지오메트리로 지정해줘야하는데 지오메트리를 마음대로 다루는 함수들이 대부분 Deprecated된 듯 해서 사용할 수가 없었다.위젯의 지오메트리는 잘 모르는 영역이라 다른 방법을 찾기로 했다.
그래서 퍼즐에 맞는 이미지를 직접 생성하기로 했다.
그래서 퍼즐의 완료한 이미지는 텍스쳐 에셋으로 게임에 넣어두고, 진행중인 이미지는 내부 저장소에 새로 생성해서 그 이미지를 불러오는 방법을 사용하기로 했다.
외부에 있는 이미지를 텍스쳐로 불러오는 것은 언리얼 엔진의
DownloadImage
를 사용하면 비동기 방식으로 이미지를Texture2DDynamic
으로 가져올 수 있다.그리고 이미지를 새로 생성하는 것은 OpenCV를 이용하기로 했다.
언리얼엔진에서도 플러그인으로 OpenCV를 사용할 수 있어서 적용은 금방 할 수 있었다.
에디터에서 OpenCV 플러그인을 활성화 하고,
Build.cs
파일의PublicDependencyModuleNames
에OpenCV
와OpenCVHelper
를 넣으면 된다.그리고 아래의 헤더파일들을 include하면 된다.
일단 검색하면서 찾은 것들을 다 넣어놨다.
#include "PreOpenCVHeaders.h" #include "OpenCVHelper.h" #include <ThirdParty/OpenCV/include/opencv2/imgproc.hpp> #include <ThirdParty/OpenCV/include/opencv2/highgui/highgui.hpp> #include <ThirdParty/OpenCV/include/opencv2/core.hpp> #include "opencv2/calib3d.hpp" #include "opencv2/imgproc.hpp" #include "PostOpenCVHeaders.h"
퍼즐의 각 칸을 픽셀 하나라고 생각하면 퍼즐의 상태를 OpenCV를 이용해서 이미지로 만드는 것은 어렵지 않다.
매트릭스를 만들어서 각 칸에 대응되는 픽셀의 값을 직접 지정해주고 저장하면 된다.
문제는 퍼즐의 상황을 저장하는 부분에서 생겼다.
진행 상황을 게임에서도 저장하고있어야한다.
퍼즐의 판은 2차원 배열로 관리되어있는데 이것을 SaveGame에 저장하기에는 데이터의 크기가 커질수도 있고, 번거로울수도 있다.
그래서 퍼즐의 상황을 문자열로 인코딩해서 저장했다가 불러올때는 디코딩해서 적용하기로 했다.
인코딩 방식은 Run-length를 사용했다.
Run-length 인코딩 방식은 문자열의 경우 반복되는 문자가 많을때 사용하기 적합하다.
예를들어 "AAABBBBCC"라는 문자열이 있다면 이걸 Run-length 방식으로 인코딩하면 "A3B4C2"로 된다.
연속된 A가 3개, B가 3개, C가 2개 존재한다는 뜻이다.
노노그램 특성상 이런 경우가 많기 때문에 이 방식이 적합하다고 생각했다.
그리고 Run-length 방식을 조금 바꿨다.
기존 방식대로는 칸이 어떤 상태인지(비어있는지, X로 막혀있는지, 채워져있는지 등)를 알파벳으로 표현하고 연속으로 몇칸 이어져있는지를 숫자로 표현해야한다.
여기에서는 그걸 반대로 적용했다.
칸의 상태를 숫자로, 연속으로 몇칸 있는지를 알파벳으로 표현했다.
연속된 칸이 길이는 10개를 넘을 수 있는데 이럴 경우 숫자를 표현하는 문자열이 두개가 된다.
그리고 노노그램 퍼즐의 가로 세로 칸의 개수를 최대 30개 정도로 제한할 예정이다.
알파벳의 개수는 26개로, 대문자와 소문자를 구분하면 52개이므로 30까지의 숫자를 표현하는데에는 충분하고, 문자 하나로 표현할 수도 있다.
A~Z는 0~25, a~z는 26~51으로 정했다.
그리고 숫자 0은 빈칸, 1은 채워진 칸, 2는 X로 막힌 칸으로 정했다.
줄바꿈은 | 으로 표현했다.
예시로 아래와 같은 퍼즐의 상황은
0C1C0C1C0C|0C1C0C1C0C|0C1C0C1C0C|0C1C0C1C0C|0B1I0B|1K|1K|1C0B1E0B1C|1E0C1E|0B1I0B
로 변환이 된다.
퍼즐이 10X10 크기로 100칸인데 인코딩된 문자열의 길이는 81으로 나름 괜찮은듯 하다.
이 문자열을 규칙에 따라 디코딩하면 퍼즐의 상황을 다시 불러올 수 있다.
그리고 이미지를 저장하는 객체에게 퍼즐의 상황을 전달할때에도 사용할 수 있다.
그렇게 해서 저장된 결과 이미지는 아래와 같다.
10X10 픽셀의 작은 이미지다.
파일의 크기는 108 바이트로 굉장히 작다.
게임에서 불러오면 이렇게 나타난다.
참고로 텍스쳐로 불러온 다음에는 텍스쳐의 필터 방식을 바꿔야한다.
크기가 매우 작은 이미지이므로 게임 화면에서는 확대되어서 표시되는데, 필터 방식을 바꾸지 않으면 아래처럼 된다.
Default Filter 오른쪽을 보면 블러처리된 것 처럼 이미지가 흐려진다.
필터 방식을 Nearest로 바꾸면 해결된다.
Nearest Filter - 2024.01.06 내용 추가 -
위에서는 로컬 파일의 이미지를 가져올때
Download Image
노드를 사용했다.원래
Download Image
노드는 웹에 있는 이미지를 다운받아서 Texture 2D Dynamic 타입으로 리턴해주는 노드다.그런데 URL 대신에 로컬 파일의 경로의 맨 앞에
file:///
이라는 scheme을 붙인 문자열을 넣으면 로컬 파일도 읽어서 Texture 2D Dynamic으로 리턴해준다.하지만 이 방식은 언리얼 엔진 5.2버전까지만 가능하고 언리얼 엔진 5.3부터는 불가능하게 되었다.
게다가 언리얼 엔진 5.2버전이더라도 안드로이드에서 테스트해보니 작동하지 않아서 에디터에서만 작동하는 기능인듯하다.
그래서 로컬의 이미지 파일을 텍스처로 사용하려면
Download Image
대신에Import File as Texture 2D
노드를 사용해야한다.Import File as Texture 2D 노드
퍼즐을 완료한 경우에는 미리 임포트했던 텍스쳐를 띄우는 방식을 사용했다.
해당 이미지도 비슷한 방식으로 만들었다.
아래 영상처럼 작동한다.
'언리얼엔진 > 노노그램' 카테고리의 다른 글
[UE5] 사용자 제작 퍼즐 - 퍼즐 검증, 노노그램 Solver (0) 2023.11.09 [UE5] 사용자 제작 퍼즐 - 서론 (0) 2023.11.08 [UE5] 노노그램 플레이 중 피드백에 관해서 - 5 (1) 2023.10.14 [UE5] 노노그램에 Undo, Redo 기능 추가 (0) 2023.10.11 [UE5] 노노그램 플레이 중 피드백에 관해서 - 4 (2) 2023.10.11