강의 환경 : forge-1.8.9-11.15.1.1855

1.8.9에서 작성된 강의이지만, NBT를 다루는 개념은 다른 버전과 동일합니다. 단, 텍스처(texture)와 관련된 부분은 완전히 다르니 적절히 코드를 수정하시길 바랍니다.

 

배경 지식

  1. 기본 아이템
  2. 아이템 이벤트 함수
  3. NBT
  4. 아이템 스택

강의목표

  1. NBTTagCompound를 아이템 스택(Item Stack)에 대해 사용하는 방법을 익힌다.

 

1. NBT(Named Binary Tag)

NBT는 마인크래프트에서 데이터를 저장하기 위한 파일에 쓰이는 자료 형식입니다. 다양한 이름표(tags)로 이루어진 트리(tree) 구조를 띄도록 설계 되었습니다. 각 이름표는 다양한 자료형의 데이터를 담을 수 있습니다. 가능한 자료형은 int, float과 같은 숫자부터 String, List, Compound(hash)까지 다양합니다. 트리 구조라는 것은 하나의 이름표가, 또 다른 이름표들을 계층적으로 담을 수 있다는 의미입니다.

여러 기능을 모드에 추가하다 보면 아이템에, 정확히는 아이템 스택에, 정보를 저장해야 할 일이 생깁니다. 모드를 처음 만들기 시작하는 사람들은 종종 아이템 클래스 내부에 멤버 변수를 만들어 문제를 해결하려고 합니다. 하지만 이런 접근은 완전히 잘못된 접근입니다. 왜냐하면 아이템 객체는 모든 아이템 스택이 공유하기 때문입니다. 따라서 데이터를 저장하기 위해서는 아이템 스택에 NBT의 형태로 정보를 저장해야 합니다.

그림 2 객체 표현도

아이템 스택(Item Stack)의 NBT는 항상 Compound의 자료형을 가집니다. Compound는 해쉬(Hash) 구조를 가지고 있어서 문자열(String) 키(Key)를 이용해서 다양한 NBT를 값(Value)으로 저장할 수 있습니다. 때문에 Compound가 가지는 모든 키(Key)는 유일 해야 합니다. 만약 같은 키를 가진 값이 입력되면 덮어쓰게 됩니다.

 

2. NBT를 사용하는 아이템 만들기

이번 강의에서는 특정 좌표를 지정해서 귀환할 수 있는 아이템을 만들어 보도록 하겠습니다. Shift + 우클릭을 하면 현재 좌표가 아이템의 NBT에 저장이 되고, 이후 우클릭을 하면 저장된 위치로 이동합니다. 본 예제에서는 NBT를 쓰고, 읽는 것과 더불어서 엔티티(Entity)를 조작하는 것 또한 부가적으로 다뤄집니다.

먼저 귀환석(ItemTelepoter)의 아이템 클래스를 작성합니다. 지난 강의에서 다뤘던 족집게(ItemTweezer)와 뼈대가 상당히 비슷하니 참고하시길 바랍니다.

ItemTeleporter.java

package oortcloud.basictutorial.item;

 

import net.minecraft.creativetab.CreativeTabs;

import net.minecraft.item.Item;

import net.minecraftforge.fml.common.registry.GameRegistry;

 

public class ItemTeleporter extends Item {

 

    public ItemTeleporter() {

     this.setMaxStackSize(1);

this.setCreativeTab(CreativeTabs.tabTools);

this.setUnlocalizedName("teleporter");

GameRegistry.registerItem(this,"teleporter");

    }    

}

이제 언어(Language) 파일과 텍스처(Texture) 및 모델(Model)을 추가해서 기본적인 틀을 갖추어 보도록 하겠습니다. 1.8.9 버전을 기준으로 하기 때문에, 이 부분에서 지난 강의들과 차이가 있습니다. 이곳을 확인하세요. 언어 파일은 동일합니다. ModItems 또한 적당히 작성해 주시면 됩니다.

그림 3 아이템 뼈대 적용

 

 

3. NBT를 사용하여 기능을 구현하기

이제 이벤트 함수에 NBT를 이용해서 아이템에 좌표를 저장하고, 저장된 좌표로 순간이동을 하는 코드를 작성해보도록 하겠습니다. 아이템을 손에 들고 우 클릭 했을 때 호출되는 이벤트 함수는 onItemRightClick ()입니다.

public ItemStack onItemRightClick(ItemStack itemStackIn, World worldIn, EntityPlayer playerIn)

ItemStack itemStack

사용된 아이템 스택(Item Stack)입니다.

World worldIn

아이템 스택(Item Stack)이 사용된 세계(World) 객체입니다.

EntityPlayer playerIn

아이템 스택(Item Stack)을 사용한 플레이어(Player)입니다.

반환

아이템 스택(Item Stack)이 사용되고 난 후의 아이템 스택(Item Stack)이 반환됩니다.

 

ItemTeleporter.java

    @Override

    public ItemStack onItemRightClick(ItemStack itemStackIn, World worldIn, EntityPlayer playerIn) {

 

        if (worldIn.isRemote)

            return itemStackIn;

 

        if (playerIn.isSneaking()) {

            // Shift + RC

        } else {

            // RC

        }

        

        return itemStackIn;

    }

 

가장 먼저 코드가 서버(Server)에서만 실행될 수 있도록 클라이언트(Client)에서 실행되면 바로 반환하게 했습니다. 아이템의 NBT는 서버에서 수정이 이뤄지면 자동으로 클라이언트로 전달되기 때문에, 서버에서만 수정하는 것이 바람직하기 때문입니다. 엔티티(Entity)의 정보를 수정하는 것도 마찬가지 입니다.

isSneaking()이라는 함수를 이용해서 Shift를 누른 상태인지 확인하였습니다. 이제 각 경우에 맞추어 기능을 구현하겠습니다.

 

Shift+우 클릭을 하는 경우 플레이어의 좌표를 Compound에 "xpos", "ypos", "zpos"라는 이름으로 저장하고, 그것을 다시 아이템 스택에 저장하는 코드입니다. BlockPos는 1.8에서 추가된 개념이며 1.7.10에서는 각 좌표를 직접 플레이어 객체에서 가져오면 됩니다.

ItemTeleporter.java

    // Shift + RC

    NBTTagCompound tag = new NBTTagCompound();

    BlockPos pos = playerIn.getPosition();

    tag.setInteger("xpos", pos.getX());

    tag.setInteger("ypos", pos.getY());

    tag.setInteger("zpos", pos.getZ());

    itemStackIn.setTagCompound(tag);

 

우 클릭만 하는 경우 NBT를 아이템 스택에서 가져오고, 좌표가 저장되어 있으면 해당 좌표로 플레이어를 이동시킵니다.

ItemTeleporter.java

    // RC

    NBTTagCompound tag = itemStackIn.getTagCompound();

    if (tag != null && tag.hasKey("xpos") && tag.hasKey("ypos") && tag.hasKey("zpos")) {

        playerIn.setPositionAndUpdate(tag.getInteger("xpos"), tag.getInteger("ypos"), tag.getInteger("zpos"));

    }

 

추가적으로 여러 개의 좌표를 저장하거나, 메시지나 시각 효과를 연출하는 것도 좋은 연습이 될 것입니다.

 

원하는 대로 기능 구현이 잘 되었습니다.

 

참고자료

http://www.minecraftforge.net/wiki/Creating_NBT_for_items

http://minecraft.gamepedia.com/NBT_format

+ Recent posts