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

 

배경 지식

  1. 기본 아이템
  2. 아이템 스택

 

강의목표

  1. 아이템의 메타데이터(metadata)를 이해한다.
  2. 아이템의 손상 값(Damage)을 이해한다.
  3. 손상 값(Damage)을 이용해서 하위 아이템을 만들 수 있다.

 

1. 아이템의 메타데이터

메타데이터(Metadata)는 데이터를 설명하기 위한 추가적인 데이터입니다. 예를 들면, 디지털 카메라에서는 사진을 찍을 때마다 촬영 시간, 노출, 플래시 사용 여부, 해상도, 사진 크기 등의 정보를 화상 데이터와 같이 저장하게 되어 있습니다. 이런 추가적인 데이터는 훗날 사진을 정렬하고 관리할 때 상당히 유용한 지표가 됩니다. 메타데이터는 일반적으로 이처럼 데이터의 검색과 관리를 편리하게 하기 위해서 부여됩니다.

마인크래프트에서 말하는 메타데이터는 약간 차이가 있습니다. 마인크래프트 세계에는 최소 수 백만 개의 블록이 존재하기 때문에 각 블록에 저장될 수 있는 데이터가 많지 않습니다. 일단 서로 다른 블록을 구분하도록 ID값이 저장되고, 거기에 추가적으로 4비트만큼의 저장 공간이 제공됩니다. 0~15까지 저장할 수 있는 이 공간을 메타데이터라고 부릅니다.

이러한 메타데이터는 단순한 데이터를 저장할 때 사용됩니다. 양털의 색, 화로의 방향, 도가니의 물과 같이 몇 개의 숫자만으로 충분히 표현 가능한 자료들을 저장합니다. 양털은 파괴되어 아이템이 된 후에도 그 색이 보존되기 때문에 아이템 스택에도 메타데이터를 저장하는 공간이 있다는 것을 유추할 수 있습니다. 이 값이 바로 아이템의 메타데이터입니다.

그림 1서로 다른 양털

 

 

2. 손상 값(Damage)

앞서 말한 메타데이터는 damage라는 변수에 저장됩니다. 이름 그대로 도구 아이템의 내구도를 나타낼 때 흔히 사용됩니다. 그렇기 때문에 내구도가 있는 도구는 메타데이터까지 따로 저장할 수는 없습니다. 한 변수에 두가지 정보를 저장하는 것은 굉장히 번거로운 일이니까요. 블록과 달리 아이템의 메타데이터는 -32768~32767의 훨씬 넓은 저장공간을 제공합니다.

이번 강의에서는 손상 값을 이용해서 하위 아이템을 만드는 법을 알아볼 것입니다. 이러한 방식이 가진 장점은 1) 32000까지 제한되어 있는 아이템 ID를 절약할 수 있고 2) 메타데이터를 이용하는 블록과 연계된 아이템을 만들 수 있으며, 3) 마지막으로 손상 값 자체를 응용해서 유연한 프로그래밍을 할 수 있습니다.

 

 

3. 하위 아이템을 사용하는 Item 클래스

지난 강의에서 추가한 기본 아이템과는 다르게 특수한 기능을 가지고 있기 때문에 새로 클래스를 작성해야 합니다. 객체지향의 원리에 따라 Item 클래스를 상속해야만 호환이 됩니다. 우선은 다음과 같이 클래스를 작성합니다. 패키지는 지난 강의와 같은 oortcloud.basictutorial.item으로 하겠습니다.

ItemMetadata.java

package oortcloud.basictutorial.item;

 

import net.minecraft.item.Item;

 

public class ItemMetadata extends Item {

 

}

여기서 하위 아이템을 가지는 아이템은 반드시 setHasSubtypes 메소드를 이용해서 해당 아이템이 하위 아이템을 가지는 것을 명시해야 합니다. 그렇지 않으면 서로 다른 손상 값이 모두 무시되어 인벤토리에서 그냥 겹쳐집니다. 이 사항은 하위 아이템을 가지는 모든 아이템 객체에 대해서 해당되는 사항이므로 생성자에 포함시키도록 하겠습니다.

ItemMetadata.java

public ItemMetadata() {

    setHasSubtypes(true);

}

그럼 이대로 적절한 이름(Unlocalized Name)을 부여해서 등록해보도록 하겠습니다.

ModItems.java

public final class ModItems {

    

    public static Item itemNumber;

    

    public static final void init() {

        itemNumber = new ItemMetadata().setUnlocalizedName("itemNumber").setCreativeTab(CreativeTabs.tabMisc);

        GameRegistry.registerItem(itemNumber, "itemNumber");

    }

    

}

/give 명령어를 이용해서 서로 다른 손상 값을 가지는 아이템을 받아보면 서로 겹쳐지지 않는 것을 확인할 수 있습니다. 반대로 조약돌은 아무리 손상 값을 달리해도 그냥 한 종류의 아이템으로 취급됩니다.

그림 2하위 아이템

여기서 우리는 총 세가지 문제를 확인할 수 있습니다. 1) 이름이 올바로 표시되지 않는다. 2) 텍스처가 올바로 표시되지 않는다. 3) 크리에이티브 탭에 한 종류(손상 값이 0)만 나타난다. 하나씩 해결해보도록 합시다.

 

1) 손상 값에 따른 이름(Unlocalized Name) 지정

아바스토로님의 강의를 참고하면 언어 설정에 맞게 올바른 이름이 표시되게 할 수 있습니다. 하지만 이 방법만으로는 setUnlocalizedName을 통해 부여한 이름이 단 하나의 번역에 대응되기 때문에 손상 값에 따라 다른 이름을 부여할 수는 없습니다. 따라서 손상 값에 따라 현지화 되지 않은 이름(Unlocalized Name)이 바뀌도록 해야합니다. lang파일이 다음처럼 서로 다른 손상 값에 대해 대응될 수 있도록 말이죠.

en_US.lang

item.itemNumber_0.name=Number Zero

item.itemNumber_1.name=Number One

item.itemNumber_2.name=Number Two

item.itemNumber_3.name=Number Three 

 

우리가 작성한 ItemMetadata 클래스 안에서 getUnlocalizedName(ItemStack itemStack)이라는 메소드를 오버라이드하면 아이템 스택의 손상 값이나 NBT 값에 따라서 서로 다른 이름을 대응시킬 수 있습니다. 우리는 손상 값에 따라 이름을 달리 지정할 것이므로 간단히 본래의 이름 뒤에 _(손상값)을 붙이도록 합시다.

ItemMetadata.java

@Override

public String getUnlocalizedName(ItemStack itemStack) {

    return getUnlocalizedName()+"_"+itemStack.getItemDamage();

}

getUnlocalizedName()은 아이템 스택의 상태(손상 값, NBT)에 영향 받지 않는 setUnlocalizedName()으로 등록한 이름 그 자체를 반환하고, itemStack.getItemDamage() itemStack 의 손상 값을 반환합니다.

 

다시 실행해보면 이름 문제는 해결된 것을 확인할 수 있습니다.

 

2) 손상 값에 따른 텍스처 지정

텍스처를 지정하는 과정은 2단계로 이루어집니다. 먼저 다수(혹은 하나)의 텍스처를 등록을 하고, 아이템 스택의 정보를 받아 상황에 맞는 텍스처를 반환하는 형식입니다. 우리는 사용할 텍스처를 모두 등록해서 배열에 저장한 후 주어진 아이템 스택의 손상 값에 맞는 텍스처를 반환하도록 하겠습니다.

텍스처 등록은 해당 아이템의 클래스 내에서 registerIcons(IIconRegister iconRegister) 메소드를 오버라이드하여 제어할 수 있습니다. 앞서 말했듯이 이렇게 등록한 텍스처(IIcon) 객체를 저장해 두어야하기 때문에 해당하는 배열 또한 클래스의 멤버 변수로서 생성해두도록 하겠습니다. 본 강의의 경우에는 ItemMetadata 클래스 내부에 다음의 코드를 추가로 작성하게 됩니다.

ItemMetadata.java

public IIcon[] icons = new IIcon[4];

 

( … )

 

@Override

public void registerIcons(IIconRegister iconRegister) {

for (int i = 0; i < icons.length; i++) {

this.icons[i] = iconRegister.registerIcon("basictutorial:itemNumber_" + i);

}

}

iconRegister.registerIcon 에 전달되는 인자는 setTectureName 메소드와 형식이 동일하여 (Mod ID):(파일 이름)의 형태로 작성해주시면 됩니다.

이제 getIconFromDamage(int damage) 메소드를 오버라이드해서 아이템의 손상 값에 따라 서로 다른 텍스처(IIcon)를 전달할 수 있습니다. /give 명령어를 사용하면 우리가 고려하지 않은 손상 값을 가진 아이템 스택도 생성될 수 있으므로, 사전에 꼭 손상 값을 확인하도록 합시다. 이 또한 당연히 ItemMedata 클래스 내부에서 작성됩니다.

ItemMetadata.java

@Override

public IIcon getIconFromDamage(int damage) {

    if (damage > icons.length) {

        damage = 0;

    }

    return icons[damage];

}

 

텍스처 등록 및 지정과 관련된 모든 과정을 구현했습니다. 이제 텍스처 파일이 있어야 하는데, 4개의 파일을 제공해 드리겠습니다. 각 파일의 경로는 이전 강의와 당연히 동일하고, 파일 이름은 텍스처를 등록하는 과정에서 지정한 것과 동일합니다.

그림 3텍스처 등록

 

3) 크리에이티브탭에 등록

이 과정은 구현에 필수는 아니지만 사용자의 편의를 위해 구현 해두면 좋습니다. 마인크래프트에 등록된 아이템은 기본적으로 손상 값이 0인 아이템 하나만 크리에이티브 탭에 표시됩니다. 크리에이티브 탭에 표시될 아이템 스택들을 제어하려면 getSubItems(Item item, CreativeTabs tab, List list) 구현해야 합니다.

ItemMetadata.java

@Override

public void getSubItems(Item item, CreativeTabs tab, List list) {

    for (int i = 0; i < icons.length; i ++) {

list.add(new ItemStack(item, 1, i));

}

}

Item은 현재 등록하려는 item의 객체, tab은 현재 추가하려는 크리에이티브 탭의 종류, list는 지금까지 추가된 아이템들을 모두 보관하고 있는 리스트입니다. add명령어를 이용해서 우리가 원하는 아이템 스택을 등록해주면 됩니다.

그림 4크리에이티브 탭

 

최종적으로 작성된 코드를 보여드리겠습니다.

ItemMetadata.java

package oortcloud.basictutorial.item;

 

import java.util.List;

 

import net.minecraft.client.renderer.texture.IIconRegister;

import net.minecraft.creativetab.CreativeTabs;

import net.minecraft.item.Item;

import net.minecraft.item.ItemStack;

import net.minecraft.util.IIcon;

 

public class ItemMetadata extends Item {

    

    public IIcon[] icons = new IIcon[4];

    

    public ItemMetadata() {

        setHasSubtypes(true);

    }

    

    @Override

    public void getSubItems(Item item, CreativeTabs tab, List list) {

        for (int i = 0; i < icons.length; i ++) {

     list.add(new ItemStack(item, 1, i));

     }

    }

    

    @Override

    public void registerIcons(IIconRegister iconRegister) {

     for (int i = 0; i < icons.length; i++) {

     this.icons[i] = iconRegister.registerIcon("basictutorial:itemNumber_" + i);

     }

    }

    

    @Override

    public IIcon getIconFromDamage(int damage) {

        if (damage > icons.length) {

            damage = 0;

        }

        return icons[damage];

    }

    

    @Override

    public String getUnlocalizedName(ItemStack itemStack) {

        return getUnlocalizedName()+"_"+itemStack.getItemDamage();

    }

      

    

}

 

ModItems.java

package oortcloud.basictutorial.item;

 

import cpw.mods.fml.common.registry.GameRegistry;

import net.minecraft.creativetab.CreativeTabs;

import net.minecraft.item.Item;

 

public final class ModItems {

    

    public static Item itemNumber;

    

    public static final void init() {

        itemNumber = new ItemMetadata().setUnlocalizedName("itemNumber").setCreativeTab(CreativeTabs.tabMisc);

        GameRegistry.registerItem(itemNumber, "itemNumber");

    }

    

}

 

 

 

참고자료

http://bedrockminer.jimdo.com/modding-tutorials/basic-modding-1-7/metadata-blocks-and-items/

https://ko.wikipedia.org/wiki/%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0

http://minecraft-ko.gamepedia.com/%EB%8D%B0%EC%9D%B4%ED%84%B0_%EA%B0%92

+ Recent posts