강좌/Minecraft Mod 제작2015. 11. 4. 16:02

강의 환경 : 마인크래프트 1.7 이상

강의 목표 :

1. 모드를 언어로 현지화할 있다.

 

이 강좌에서는 모드를 영문, 한글과 같은 언어로 현지화하는 방법을 알아보도록 합시다.

 

이 강좌는 기본적으로 자신이 새로 만든 모드에 적용되며,

 

이미 있던 모드를 현지화하는 경우에도 적용할 수 있습니다.

 

1. Lang 파일 만들기

 

현지화를 위해서는 우선 lang 파일을 만드셔야 합니다.

 

새로 만드신 모드에 적용하는 경우에는 프로젝트 폴더에서src\main\resources\assets\(모드 아이디) 폴더로 이동하신 후,

 

여기에 lang 폴더를 만드시면 됩니다.

 

이미 있는 모드를 현지화하고 싶으신 경우에는resources\assets\(모드 아이디)\lang폴더로 이동하세요.

 

이제 lang 파일을 이용해 한국어와 국제 표준어인 영어로 각각 현지화를 해 보기로 합시다.

 

저 위치에 en_US.lang 파일과 ko_KR.lang 파일을 만들어 주세요.

(이미 있는 모드를 현지화할 경우에는 이미 있는 en_US.lang 파일을 복사하여 ko_KR.lang 파일을 만드시면 됩니다)

 

 

저렇게 말입니다.

 

영어부터 우선 작업해 봅시다. en_US.lang을 메모장으로 열어주세요.

(이미 있던 모드의 경우, en_US.lang 파일은 이미 작성이 되어 있을 테니 한국어 현지화 부분까지 넘어가세요)

 

블럭과 아이템을 현지화하려면, tile.tutorial.name, item.tutorialtem.name 같은 것 대신 현지화된 이름을 주어야 하겠죠?

 

그러니까 다음과 같이, 마치 대입하듯이 = 앞에 현지화 전 이름을, 뒤에 현지화한 후의 이름을 적으시면 됩니다.

 

 

여기서 비현지화 이름을 구하는 방법은 몇 가지가 있습니다.

1. 현지화 전에 eclipse에서 마인크래프트를 실행하신 후 블록/아이템에 대해 뜨는 이름을 찾습니다.

2. 그냥 블록은 tile.(블록 비현지화 이름).name, 아이템은 item.(아이템 비현지화 이름).name입니다. (…)

이때 비현지화 이름은setBlockName/setItemName 혹은 setUnlocalizedName으로 설정했던 이름입니다.

 

이제 ko_KR.lang파일도 작성해 주어야겠죠?

 

역시 = 앞에는 현지화 전 이름, = 뒤에는 한글로 현지화한 후의 이름을 써주세요.

(이미 있던 모드의 경우에는 영어로 현지화된 이름을 한글로 번역하시면 됩니다.

또 블록/아이템만 현지화된 건 아닐 테니. 알아서 잘 번역하세요;)

 

저의 경우 다음과 같이 작성했습니다.

 

 

이때, 제일 처음 줄은 빈칸으로 놓아야 제대로 해석이 될 겁니다.

 

마지막으로 저장할 때 주의점이 있습니다.

 

인코딩을 UTF-8로 변환해 주어야 한다는 것이죠. (한글에만 해당합니다)

 

어떻게 UTF-8로 저장하냐고요? 우선 다른 이름으로 저장으로 가주세요.

 

 

여기서 인코딩이 아마 ANSI일 텐데, UTF-8로 바꾸시면 됩니다.

 

한번만 이렇게 인코딩을 바꿔놓으면 계속 유지되기 때문에 다시 하실 필요는 없습니다.

 

그렇게 저장을 하면, 현지화가 끝났습니다! 한번 영어부터 확인해 볼까요?

 

 

 

제대로 되었군요!

 

이제 한국어로 확인해 봅시다.

 

 

그럼 이렇게 되었다는 것을 확인하실 수 있습니다!

 

 

 

이제 드디어 예쁜 현지화된 블럭, 아이템 이름을 보실 수 있는 겁니다!

(있는 모드를 현지화하는 경우 여기부터 읽지 않으셔도 됩니다.)

 

 

2. 커스텀 문자열 현지화하기

(선행강좌: 아직 작성되지 않음(…))

 

실제로 현지화가 필요한 것은 블럭, 아이템 이름 뿐만이 아니죠. 알림 메시지 등도 현지화가 필요합니다.

 

여기서는 이러한 커스텀 문자열을 현지화하는 방법을 알아보도록 하겠습니다.

 

튜토리얼 블럭을 우클릭했을 때, 현지화된 문자열이 뜨도록 하는 예제를 통해 알아보도록 합시다.

 

우클릭했을 때는 onBlockActivated 메서드가 호출되니까, 이 메서드를 상속받고 다음과 같이 작성해주면 됩니다.

 

(잘린 줄은 다음과 같은 내용입니다:

player.addChatMessage(new ChatComponentText(StatCollector.translateToLocal("command.activate")));

)

 

여기서, 1개의 조건문과 3개의 메서드를 호출했음을 알 수 있습니다. 각각에 대해 설명하자면,

 

0. world.isRemote: 클라이언트 월드에 대해 true, 서버 월드에 대해 false 값을 갖고 있습니다.

onBlockActivated는 CLIENT side, SERVER side 양쪽 모두에서 호출되기 때문에,

메시지를 1번만 보내기 위해 체크를 하였습니다.

1. player.addChatMessage(IChatComponent comp) : 플레이어에게 알림 메시지를 보냅니다.

2. new ChatComponentText(String text) : String 텍스트로 구성된 채팅 메시지 컴포넌트를 만듭니다.

3. StatCollector.translateToLocal(String unloctext) : 해당 문자열을 현지화시킵니다!

 

그러니까, 간단히 StatCollector.translateToLocal을 통해 현지화를 해주면 되는 겁니다.

 

물론 lang 파일에 command.activate를 현지화하는 줄을 넣어야겠죠?

 

 

 

이런 식으로 작성한 다음 저장해 주시면..!

 

영어로 설정한 경우,

 

 

저렇게 뜨고요.

 

한국어로 설정하면

 

 

이렇게 뜨게 됩니다!

 

여기까지 간단하게 커스텀 문자열을 현지화하는 방법을 알아보았습니다.

 

정리하자면,

 

StatCollector.translateToLocal(String unloctext)

 

를 사용하면 된다는 겁니다. 물론 unloctext는 command.activate나 abs.tutorial.call 같은 형식으로 해놓고,

 

lang 파일에서 command.activate=(현지화된 문자열) 으로 하면 되는 겁니다.

 

그런데 만약 %s, %d 같은 것으로 문자열이나 수를 입력하고 싶다면 어떻게 해야 할까요?

 

이때는 대신 StatCollector.translateToLocalFormatted(String unloctext, Object... obj)

 

를 사용하시면 됩니다! 여기서, unloctext는 command.activate 같은 형식으로 하시고,

 

lang 파일에서 현지화된 문자열을 넣는 위치에 적당히 %s, %d를 배치해 주시면 되는 거죠.

 

예를 들자면 lang 파일에 abs.tutorial.call=Calling %s...

 

이런 식으로 해 놓고 translateToLocalFormatted의 2번째 인자에 String을 넣어도 되는 것이죠.

 

 

여기까지 lang 파일을 이용한 모드 현지화 강좌였습니다!

Posted by Abastro
강좌/Minecraft Mod 제작2015. 11. 4. 08:03

강의 환경 : forge-1.7.10-10.13.0.1160-1.7.10

선행 과정 : [1.7.10] 모드기반제작

강의 목표 :

1. 기본적인 블록을 제작할 있다.

 

이 강좌에서는 기본적인 블록을 제작하는 방법을 알아보도록 하겠습니다.

 

이를 통해 블록 타입에 해당하는 객체를 만들고, 마인크래프트에 등록해주는 작업을 하게 될 것입니다.

 

 

그럼, 우선 블록의 성질을 결정하게 될 클래스를 만들어 봅시다.

 

보통 블록, 아이템 각각에 대해 block, item과 같은 패키지를 만들어 관리하는데,

 

저도 이 관례를 따라 abr.tutorial.block에 블록 클래스들을 두기로 하였습니다.

 

굳이 이를 따르실 필요는 없습니다.

 

이제 Block을 상속한 블록 클래스를 만들면 됩니다. 저는 BlockTutorial라는 클래스를 만들었습니다.

 

그럼 이렇게 컴파일 오류가 발생합니다. 자동으로 해결해 보면,

 

 

여기서 Material이 생성자의 인자로 들어가야 한다는 사실을 알 수 있습니다.

 

이렇게 블록에는 항상 Material을 지정해 주어야 하는데,

 

여기서 Material은 블록의 재질을 나타내며, 공기/액체/고체 여부와 블록을 밟았을 때의 소리 등을 결정합니다.

 

보통 블록은 고정된 재질을 가지므로 다음과 같이 super에 원하는 재질을 넣어주시면 됩니다.

 

저는 적당히 돌(Material.rock) 로 하기로 결정했습니다.

 

다른 재질을 원하신다면, Material의 전역변수 중에서 찾으시면 됩니다.

 

 

이제 생성자에서 블록의 몇 가지 성질을 지정할 수 있습니다.

 

기본적으로 Block#setBlockName(String name)으로 블록의 이름을 설정할 수 있고,

 

Block#setCreativeTab(CreativeTabs tab)으로 크리에이티브 탭을 설정할 수 있습니다.

 

여기서 CreativeTabs의 인자로 넣을 탭 객체는 보통 CreativeTabs의 전역변수 중에서 찾아볼 수 있습니다.

 

저는 이름은 tutorial로, 크리에이티브 탭은 기타(Miscellaneous) 탭에 해당하는 CreativeTabs.tabMisc를 쓰기로 결정했습니다.

 

 

이제 텍스쳐를 할당해 보도록 합시다.

 

여기서는 간단히 하나의 텍스처를 모든 면에 대해 적용하는 방법만 알아보고,

 

각 면에 따라 다른 텍스처를 이용하는 방법은 추후에 강좌를 올리도록 하겠습니다.

 

우이를 위해서, Block#setBlockTextureName을 이용해 텍스처 이름을 설정해 주되,

 

반드시 모드ID (MODID) 뒤에 :를 붙인 후 텍스처 이름을 써 주셔야 합니다.

 

예를 들어 텍스처 이름이 tutorial(.png)이면, 다음과 같이 하시면 됩니다.

 

 

이제 텍스처를 위치시켜 봅시다.

 

(프로젝트 위치)/src/main/resources/assets/(MODID)/texture/blocks 위치에 지정한 이름의 png 텍스처 파일을 위치시키면 됩니다.

 

저의 경우 다음과 같습니다.

 

 

여기까지 하면 기본적인 블록의 형태는 완성이 된 것입니다.

 

이제 추가적인 성질들을 설정해 봅시다.

 

블록에는 다음과 같은 성질들이 있습니다.

 

인자

역할

설정 방법

경도

(Hardness)

float

블록을 캘 때 걸리는 시간을 결정합니다.

Block#setHardness

나무: 2, 흑요석: 20

위키 링크(영어)

폭발 저항 강도

(Resistance)

float

블록의 폭발 저항 강도를 결정합니다.

Block#setResistance

나무: 15, 흑요석: 6,000

위키 링크(영어)

스텝 소리

(StepSound)

SoundType

블록 위를 걸어갈 때 나는 소리를 설정합니다.

Block#setStepSound

대부분 Block 클래스에 전역 변수로 설정

돌: #soundTypeStone

나무: #soundTypeWood

캐는 도구 설정

(HarvestLevel)

String toolclass, int level

블록을 캘 때 필요한 도구와 그 레벨을 결정합니다.

Block#setHarvestLevel

Toolclass: 곡괭이 "pickaxe", 삽 "shovel", 도끼 "axe";

level: Wood: 0, Stone: 1

Iron: 2, Diamond: 3

 

저는 적당한 강도에 돌 소리가 나며 돌 곡괭이 이상으로 캘 수 있도록 다음과 같이 설정하였습니다.

 

 

마지막으로 이렇게 구성한 블록을 등록해 줄 차례입니다.

 

preInit에서 블록의 인스턴스를 다음과 같이 만들어 등록해 주시면 됩니다. (저번에도 설명했지만 반드시 preInit이어야 합니다!)

 

GameRegistry#registerBlock(Block block, String id)를 이용해 간편히 등록할 수 있습니다.

 

저의 경우 다음과 같이 되었지요.

 

 

* Block#getUnlocalizedName()을 이용하는 실수는 하지 마시길 바랍니다.

저는 실수로 넣었지만, tile.tutorial같은 이상한 ID로 등록하는 바람에 버그가 발생하는 것을 원치 않으신다면

그냥 직접 타이핑하시는 것을 추천드립니다.)

 

이때, 반드시 클래스가 아닌 객체 형태로 등록해야 합니다.

 

블록 타입을 나타내는 것은 블록 클래스가 아니라 객체이기 때문입니다.

 

또한 이 객체는 나중에 이용하실 일이 많기 때문에, 미리 저장해 두시는 것을 추천합니다.

 

 

이제 우리의 새로운 블록이 추가되었는지 확인해 봅시다.

 

 

저렇게 잘 추가되었음을 볼 수 있습니다.

 

(이름이 tile.tutorial.name인 것은 아직 현지화 단계를 거치지 않았기 때문입니다.

현지화 강좌는 이곳에서 확인해 보실 수 있습니다!)

 

여기까지 기본적인 블록을 제작해 보았습니다.

 

다음 강좌에서는 메타데이터에 대해 알아보고 이를 다루는 법에 대해 알아보도록 하겠습니다!

Posted by Abastro
강좌/Minecraft Mod 제작2015. 10. 28. 20:42

 

강의 환경 : forge-1.7.10-10.13.0.1160-1.7.10

강의 목표 :

  1. 마인크래프트가 실행될 발생하는 FML 기본 이벤트들을 이해할 있다.

 

이번 강좌에서는 FML 기본 이벤트들에 대해 알아보겠습니다.

 

저번 '모드 기반 제작' 강좌에서,

  • FMLPreInitializationEvent
  • FMLInitializationEvent
  • FMLPostInitializationEvent

를 배웠었죠?

 

사실 이들 말고도 이런 종류의 이벤트들이 더 있습니다.

 

이들은 모두 같은 방식으로 이용할 수 있습니다:

 

  1. 모드 기반 클래스에 @EventHandler가 붙은 메소드를 통해 이벤트를 받습니다.
  2. 이벤트 인자를 통해 받을 이벤트의 종류를 결정할 수 있습니다.
  3. 이벤트 인자 내부 필드/메소드들을 통해 이벤트와 관련된 여러 작업을 할 수 있습니다.

 

우선 이미 보았던 FML 기본 이벤트들을 더 자세히 살펴봅시다.

 

1.시작시 발동되는FML 기본 이벤트들

 

이벤트

시기

주요 용도

클라이언트

서버

FMLPreInitializationEvent

모드 로딩 시 1순위

* 모드 초기화 작업

*아이템/블록/엔티티 등록

FMLInitializationEvent

모드 로딩 시2순위

*PreInit 다음에 수행되어야 하는 초기화 작업

*조합법 등의 작업 등록

*모드간의 호환성 작업

(초기 수정)

FMLPostInitializationEvent

모드 로딩 시 3순위

*모드간의 호환성 작업

(후기 수정)

 

이 중FMLPreInitializationEvent이벤트만이 (유용한) 메소드들을 지니고 있습니다.

 

이벤트 / 메소드 이름

형태

용도

FML-

Pre-

Initialization-

Event

getSourceFile

File …()

소스 파일의 경로를 가져옵니다.

getModMetadata

ModMetadata …()

모드 ID, 이름 등의 정보를 담고 있는 객체를 가져옵니다.

getModConfigurationDirectory 

File …()

모드 컨피그들이 들어있는 경로를 가져옵니다. ((마크 위치)\config)

getSuggestedConfigurationFile 

File …()

해당 모드에 대해 추천되는 컨피그 파일 경로를 제공합니다.

getAsmData

ASMDataTable …()

해당 모드에 대한 ASM 데이터를 가져옵니다.

getVersionProperties 

Properties …()

해당 모드의 버전 속성을 가져옵니다.

getModLog 

Logger …()

해당 모드를 위한 Logger를 가져옵니다.

(디버깅 목적으로 유용합니다)

 

특히 getModLog, getModContigurationDirectory/getSuggestedConfigurationFile은 각각 디버깅, 컨피그 부분에서

 

유용하게 쓸 메소드들이니 꼭 기억해 두세요!

 

 

2.상태에 관련된FML 기본 이벤트들

 

이제 상태에 관련된 FML 기본 이벤트들을 알아봅시다.

 

이들은 주로 모드를 불러오거나, 서버가 시작되거나 끝날 때 발동됩니다.

 

이들 대부분은 상당히 중요하며 요긴하게 쓰이는 이벤트들이므로 꼭 기억해 두세요.

 

(FMLInitializationEvent 등지도 상태 관련 이벤트이지만, 편의상 따로 두었습니다)

 

이벤트

시기

주요 용도

클라이언트

서버

FMLConstructionEvent

모드 로딩 전

(모드 기반 클래스가 처음 로딩되었을 때)

모드 기반 클래스 로딩

FMLLoadCompleteEvent

모드 로딩 완료 후

모든 로딩이 완료되었을 때 작업을 수행합니다.

(PostInit에 등록을 하는 모드들을 호환시키기 위해 필요합니다..)

FMLServerAboutToStartEvent

월드 시작시

1순위

서버 세팅 로딩 후

(PostInit후)

서버 세팅 로딩

FMLServerStartingEvent

월드 시작시

2순위

서버 월드 로딩 후

월드 관련 작업

서버 커맨드 등록

FMLServerStartedEvent

월드 시작시

3순위

서버가

시작된 직후

서버 최종 세팅

FMLServerStoppingEvent

월드

종료시 1순위

서버 종료시 1순위

데이터 저장, 백업 등

FMLServerStoppedEvent

월드 종료시 2순위

서버 종료시 2순위

데이터 저장, 백업 등

 

(볼드체로 쓴 이벤트들이 중요합니다!)

 

여기서 유용한 메소드는 단 3개입니다.

 

FMLServerAboutToStartEvent or FMLServerStartingEvent #getServer(): 서버를 관리하는 객체인 MinecraftServer의 인스턴스를 줍니다.

 

FMLServerStartingEvent #registerServerCommand(ICommand command): 서버 커맨드를 등록합니다. (커맨드를 등록하는 유일한 방법!)

 

 

3.기타FML 기본 이벤트들

 

기타로 다음과 같은 이벤트들이 추가로 있습니다.

 

이벤트

시기

주요 용도

클라이언트

서버

FMLFingerprintViolationEvent 

모드 파일의 무단 변경이 감지되었을 때

보안용

(*Fingerprint(지문)를 지원하는 모드에 대해서만 발동됩니다)

FMLModDisabledEvent 

모드를 비활성화했을 때

(*현재 이 버전에선 작동하지 않습니다)

모드 비활성화 지원

FMLMissingMappingsEvent

월드에 저장된String ID에 해당하는 블록/아이템이 없을 때

1. 업데이트 시에 아이템/블록 ID를 바꾼 경우

2. ID가 바뀌었을 때 경고를 하고 싶은 경우

FMLModIdMappingEvent

월드에 저장된 int ID가 다른 int ID로 해석될 때

주로 1.6->1.7 업데이트 시 ID 시스템의 변화로 인한 문제를 감지하기 위함

IMCEvent 

FMLPreInit…과 FMLInit… 사이

모드간 통신 (즉 호환성을 위함)

 

1. FMLFingerPrintViolationEvent

모드 파일의 무단 변경이 감지되었을 때 발동되는 이 이벤트는 4개의 public 멤버변수를 지닙니다.

- Boolean isDirectory: 변경된 파일이 Directory인지 확인

- Set<String> fingerprints: 받아들인 파일 지문(Fingerprint)의 목록

- File source: 문제가 발생한 소스

- String expectedFingerprint: 예상한 파일 지문

이 이벤트를 이용하기 위해서는,

저번 강좌에 사용했던 @Mod 어노테이션의 certificateFingerprint필드에

예상되는 지문이 반드시 지정되어 있어야 합니다.

(꼭 필요하면 사용하시되 과신을 하지 마세요)

 

2. FMLModDisabledEvent

모드가 불활성화될 때 발동되(어야 하)는 이벤트입니다. ModList Gui의 Disable 버튼과 관련이 있는듯 하지만,

현재 버전에서는 아직 이용할 수 없습니다.

 

3. FMLMissingMappingEvent

월드에 저장된 ID에 해당하는 블록/아이템이 없을 때 발동되는 이벤트입니다. (이때 ID는 String입니다)

List<MissingMapping> get()을 통해 자신의 모드에 대한 ID들을,

List<MissingMapping> getAll()을 통해 자신의 모드에 대한 들을 가져올 수 있습니다.

 

이 각각의 MissingMapping들은 String 형태의 ID와 int 형태의 ID를 지니고 있는데,

이들을 확인한 후 ignore/ warn/ fail을 통해 무시/경고/실패를 알리거나,

Remap을 통해 알고 있는 블록/아이템들을 사용하도록 지정할 수 있습니다.

 

4. FMLModIdMappingEvent

1.6에서 1.7로 업데이트되면서 ID 시스템이 바뀌게 되었는데, 이로 인해 발생할 수 있는 오류를 감지하기 위한 이벤트입니다.

(1.6까지 항상 아이템/블록의 식별자는 정수였으나, 1.7부터 String으로 바뀌었습니다.

이로 인해 아이템/블록 저장/로딩 방식도 약간 바뀌면서 문제가 발생합니다)

 

이 이벤트에는 다음과 같은 멤버변수 하나가 있습니다.

ImmutableList<ModRemapping> remappedIds;

ModRemapping은 월드에 저장된 int id와, 바꿀 int id를 함께 지니고 있으며

remapTarget으로부터 블록인지 아이템인지를 알 수 있고,

tag로부터 블록 혹은 아이템의 이름을 알 수 있습니다.

이 이벤트를 통해서는 감지만 가능하며 어떤 것도 수정할 수 없습니다.

 

 

5. IMCEvent

마지막의 IMCEvent는 Forge가 모드간 통신을 가능하게 해준 Inter Mod Commons기능의 일부입니다.

이 부분은 언젠가 기회가 되면 따로 강좌하기로 하겠습니다.

 

 

이상으로 FML 기본 이벤트들에 대한 강좌를 마치도록 하겠습니다.

Posted by Abastro