Minecraft screenshot with moon setting in y=-59
개발
기타 개발
Arctic-Midi2McFunction 개발 후기
2023-03-08, 18:34 | HoonKun
Arctic-Midi2McFunction 개발 후기

Midi 파일을 마인크래프트에서 노트블럭으로 재생할 수 있도록 .mcfunction 으로 변환하는 툴 작성 후기.

마인크래프트를 야생을 돌리다가 맵의 특정 위치에 노트블럭 몇 개를 두고 그걸로 음악을 틀 수 있었으면 좋겠다는 생각이 들었다.
대충 커맨드 블럭을 써서 실시간으로 노트블럭을 바꿔치기 해주고(note 라는 값이 있으므로) 레드스톤 블럭을 가지고 활성화해주면 되지 않을까? 해서.

그래서 Midi 파일을 읽어 마인크래프트에서 플레이할 수 있도록 .mcfunction으로 변환하는 프로그램을 작성하되, 최대한 마인크래프트에서는 할 게 없었으면 해서 '하나의 .mcfunction 을 커맨드블럭에 연결하는 것 만으로 모든게 실행될 것'이라는 목표를 가지고 개발을 시작했다.

Midi 파일 읽기

javax 패키지에 Midi 관련 구현이 있는 것 같았으나, 가난한 개발자인 고훈 군은 openjdk 를 쓰고있었기 때문에 사용하지 못했다.
그래서 JFugue 라는 라이브러리를 사용했으며, Midi를 다루는 것이 처음이라 조금 헤매긴 했으나 특정 조건을 만족하도록은 구현할 수 있었다.

특히 BeatTime 과 관련하여 해멨는데, 미디에서 두 음으로 이루어진 화음은 'n초에 1번 음으로 x초만큼 플레이하고, n초 시점으로 타임슬립하여 다시 3번 음으로 y초만큼 플레이 해'라는 방식으로 기록된다는 것을 알았다.
신기한 방식이 아닐 수 없었다...

두 가지 길

커맨드 함수 구현 방식은 대충 두 가지 방식이 떠올랐다:

  • 매 틱마다 모든 노트의 시간값과 현재 시간을 비교하여 맞는게 있으면 재생
  • 모든 노트를 엔티티로 만들고, 엔티티 선택자로 맞는 시간을 가진 엔티티를 직접 선택해서 그것만 재생

두 가지는 각각 다음 특징을 가졌다:

  • 가장 확실하지만 매 틱마다 실행하는 커맨드가 늘어남
  • 노트 수가 많으면 엔티티도 많아지며, 변수값을 자유롭게 사용하기 어렵기 때문에 추가 커맨드가 필요하지만 틱당 커맨드는 위의 방안에 비해 크게 줄어듬

다시말해 약 2000개의 엔티티를 만들고 100개 이하의 커맨드를 수행하느냐, 매 틱당 6000개가 넘는 커맨드를 실행하냐의 차이였다.

선택은 전자였다. 엔티티는 너무나도 쉽게 제거될 수 있으며 커맨드보다는 엔티티가 월드에 있어 훨씬 무거운 존재라고 판단했다.
그리고 커맨드는 필요하지 않을 때는 실행하지 않으면 그만이지만 엔티티는 월드에 항상 상주하게 되기 때문에.

초기 구현

시간 관리는 역시 스코어보드를 통해 수행했다.
다만 하나의 함수 파일에서 모든것을 수행해야했기 때문에 execute if 를 통해 조건 확인이 필요했다.

노트의 플레이에 있어서는, 모든 노트에 대해 다음 세 커맨드가 있었다:

  • 노트블럭 배치
  • 활성화를 위한 레레기 블럭 배치
  • 배치한 레레기 블럭 회수

그리고 마지막으로 시간 값에 1을 추가하고 마지막 노트의 시간과 일치하면 0으로 돌리는 커맨드를 수행했다.

1차 최적화

근데 이렇게 하고 나니 커맨드가 조금 많이 많았다(?).
노트가 3000개면 최소 9000개 이상의 커맨드가 매 틱마다 실행되어야했다.

그래서 우선 노트 플레이 과정의 첫 번째 항목을 조금 줄였다.
노트블럭의 배치는 해당 위치에 이미 같은 음을 내는 노트블럭이 있었을 경우 필요하지 않으므로, 해당 위치에 현재 배치된 노트블럭을 저장하여 일치할 경우 커맨드 실행을 스킵했다.
즉, execute if 등의 분기적 처리가 아니라 아예 커맨드 수행 자채를 스킵한다.

위의 최적화로 약 1/5 정도의 커맨드가 감소했다. 물론 이 수치는 곡의 특성에 따라 변할 수 있는 부분이긴 하다.

2차 최적화

또, 노트 플레이 과정의 마지막 항목도 최적화했다.
레레기 블럭을 배치하는건 어떤 노트냐에 따라 위치가 다르지만, 배치한 레레기 블럭을 치우는건 매 틱마다 항상 같은 동작으로 수행할 수 있었다.

따라서 모든 노트 플레이 과정에서 마지막 과정을 삭제하고, 커맨드 함수가 끝나기 전 한 번만 레레기 블럭을 치우는 코드를 삽입했다.

위의 최적화로 약 1/3 정도의 커맨드가 감소했다.

그래서 잘 됨?

솔직히 너무나도 잘되어서 조금 놀랐다. 커맨드 함수 라인 수가 5000개인 것과 12000개인 것 두 개를 확인해보았는데, 공허 세계에서 문제없이 플레이됨을 확인했다.

도대체 초당 20번 12000개의 커맨드를 실행하는데 그게 문제없이 잘된다니 마인크래프트는 무슨 겜인거지 (...)

물론 공허세계이기 때문에 정말 최상의 환경에서 테스트한 것이 되겠지만, 그래도 이정도면 꽤 만족스러운 결과물이었다.
빠르게 개발한 초간단한 프로젝트긴 하지만, 그래도 심심풀이로 잠깐 하는것도 꽤 의미가 있었던 것 같다.

저장소는 여기이고, 아마 이 글을 적는 시점에는 변속이 없는 미디 파일만 제대로 동작할거지만 추후에 이것도 수정해봐야겠다.

키위새의 아무말 저장소
  • 개발
  • 마인크래프트
  • 생명과학II
  • 아무말
Blog Logo