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

 

배경 지식

  1. 기본 모드 파일
  2. 기본 아이템
  3. 서버와 클라이언트

 

강의목표

  1. 사건 기반 프로그래밍(Event-driven Programming, EDP)의 원리를 이해한다.
  2. 아이템 클래스의 이벤트 함수 각각을 이해한다.
  3. 원하는 기능을 구현하는 아이템을 만들 수 있다.

 

1. 사건 기반 프로그래밍(Event-driven Programming, EDP)

이 단락은 아이템의 동작에 대한 보충 설명이므로 굳이 읽지 않으셔도 됩니다.

사건 기반 프로그래밍은 사용자의 행동(마우스, 키보드 입력)이나 다른 프로그램이 보낸 메시지와 같은 '사건(Event)'에 따라 프로그램의 동작을 결정하는 프로그래밍 패러다임(Paradigm)입니다. 이러한 EDP(Event-driven Programming)는 주로 GUI나 사용자의 입력에 반응하는 것이 주 기능인 프로그램들에 사용됩니다. 주요 반복문(Main Loop)이 이벤트를 인식하고 관련된 함수를 호출하는 방식으로 구현됩니다.

마인크래프트의 아이템도 사건 기반 프로그래밍에 기반하여 동작합니다. 사용자가 아이템으로 무엇을 하냐에 따라서 아이템의 기능을 구현하는 것입니다. 예를 들어 아이템을 버리고, 세계(World)와 상호작용할 때마다 특수한 함수가 불려집니다. 개발자는 Item 클래스 내부의 이벤트 함수를 오버라이드하여 이러한 기능들을 조작할 수 있습니다.

 

 

2. 예시를 통한 개념 학습 – 포춘 쿠키(Fortune Cookie)

이번 단락에서는 아이템을 사용할 때 오늘의 운세를 출력하는 간단한 아이템을 만들어 보겠습니다. 포춘 쿠키라는 이 아이템은 우 클릭으로 사용하면 하나가 소모되고 채팅 창에 임의의 운세를 출력합니다. 크리에이티브 모드인 경우에는 소모되지 않도록 하겠습니다. 본 예시를 통해서 이벤트 함수를 어떻게 조작하는지 유심히 살펴보시고 지금까지 다뤄왔던 내용들을 복습하는 시간을 가지시길 바랍니다. 또한 실제 구현에서 자주 사용되는 함수들을 따로 강조해 드리겠습니다

1) 아이템 틀 만들기

지난 강의에서처럼 기능을 구현해야하기 때문에 Item을 상속하는 클래스를 만듭니다. 그리고는 이 클래스에는 추가적인 코드없이 기본 과정을 따라 등록해보도록 합시다. 텍스처는 이것을 이용하겠습니다.

ItemFortuneCookie.java

public class ItemFortuneCookie extends Item {

 

}

 

ModItems.java

public final class ModItems {

    

    public static Item itemFortuneCookie;

    

    public static final void init() {

        itemFortuneCookie = new ItemFortuneCookie().setUnlocalizedName("fortuneCookie").setCreativeTab(CreativeTabs.tabMisc).setTextureName("basictutorial:fortuneCookie");

        GameRegistry.registerItem(itemFortuneCookie, "fortuneCookie");

    }

    

}

 

en_US.lang

item.fortuneCookie.name=Fortune Cookie

 

그림 1 등록을 마친 아이템

 

2) 이벤트 함수 오버라이드

이제 우 클릭을 할 때 호출되는 함수를 오버라이드하여 기능을 구현하도록 하겠습니다. 이벤트 함수는 일반적으로 'on'으로 시작하고 주석 처리가 잘 되어 있어서 어떤 기능인지 확인하기는 용이합니다. onItemRightClick이라는 메소드(Method)는 아이템을 들고 우 클릭을 하는 모든 경우에서 호출됩니다. 플레이어의 손에 들린 아이템 스택, 플레이어가 속한 세계(World), 그리고 플레이어의 객체를 인자로 넘겨 받습니다. 그리고 다시 해당 아이템 스택을 반환합니다. 이 메소드는 클라이언트와 서버 모두에서 실행됩니다.

ItemFortuneCookie.java

@Override

public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player) {

 

    return itemStack;

 

}

 

먼저 플레이어에게 채팅 메시지를 출력하는 메소드를 살펴보겠습니다. 채팅 메시지는 player.addChatComponentMessage(IChatComponent msg)를 호출하여 출력합니다. IChatComponent 인터페이스를 반드시 구현할 필요는 없으며 단순한 문자 출력이 목적이라면 new ChatComponentText(String text)를 이용해서 간단히 객체를 생성할 수 있습니다. 그러면 간단한 메시지를 출력해보도록 하겠습니다

ItemFortuneCookie.java

@Override

public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player) {

 

    player.addChatComponentMessage(new ChatComponentText("Example Message"));

 

    return itemStack;

}

그림 2 2번 출력되는 메시지

아이템을 한 번만 사용해도 메시지가 2번 출력됩니다. 앞서 말했듯이 addChatComponentMessage 가 클라이언트와 서버 모두에서 실행되기 때문에 일어나는 문제입니다. 클라이언트에서 addChatComponentMessage 가 호출되면 바로 채팅창에 출력되고, 서버에서 호출되면 클라이언트로 해당 메시지를 전송합니다. 이 기능은 플레이어에게 메시지를 출력하는 것이 목적이므로 클라이언트에서만 메시지를 출력하도록 바꾸어 보도록 하겠습니다.

World 클래스에 존재하는 isRemote라는 멤버 변수는 클라이언트 세계면 true, 서버 세계면 false의 값을 가집니다. 따라서 다음과 같이 코드를 작성하면 클라이언트에서만 코드가 실행되며 문제가 해결됩니다.

ItemFortuneCookie.java

@Override

public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player) {

 

    if (world.isRemote) {

        player.addChatComponentMessage(new ChatComponentText("Example Message"));

    }

 

    return itemStack;

}

 

본래의 목적은 임의의 메시지를 출력하는 것이므로 코드를 조금 추가 하겠습니다. 내용은 아이작의 구속에 나오는 운세들입니다. EntityLivingBase 클래스에는 Random 객체를 반환하는 getRNG()라는 메소드가 있습니다. 이 객체를 이용해서 임의의 값을 획득하는게 가능합니다. EntityPlayer 클래스가 EntityLivingBase의 하위 클래스이기 때문에 해당 메소드를 호출할 수 있습니다.

ItemFortuneCookie.java

public static String[] fortune = {"BRING HIM THE PHOTO", "YOU WILL DIE ALONE", "YOU ARE THROWING YOUR LIFE AWAY", "GO OUTSIDE!", "ASK AGAIN LATER", "THINK FOR YOURSELF", "QUESTION AUTHORITY", "YOU ARE WORSHIPING A SUN GOD", "DON'T LEAVE THE HOUSE TODAY", "GIVE UP!", "MARRY AND REPRODUCE", "STAY ASLEEP", "WAKE UP", "WE WILL ALL DIE ONE DAY", "LOOK TO LA LUNA", "STEVEN LIVES ", "MEET STRANGERS WITHOUT PREJUDICE", "A HANGED MAN WILL BRING YOU NO LUCK TODAY", "WHAT DO YOU WANT TO DO TODAY?", "YOU ARE DARK INSIDE", "HAVE YOU SEEN THE EXIT?", "GET A BABY PET IT WILL CHEER YOU UP", "YOUR PRINCESS IS IN ANOTHER CASTLE", "YOU ARE PLAYING IT WRONG GIVE ME THE CONTROLLER", "TRUST GOOD PEOPLE", "LIVE TO DIE", "WHEN LIFE GIVES YOU LEMONS REROLL!"};

 

@Override

public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player) {

 

    if (world.isRemote) {

        player.addChatComponentMessage(new ChatComponentText(fortune[player.getRNG().nextInt(fortune.length)]));

    }

 

    return itemStack;

}

 

마지막으로 아이템의 개수를 하나 감소시키는 과정을 추가하도록 하겠습니다. 이때 일반적으로 크리에이티브 모드에서는 아이템이 소모되지 않도록 합니다. 아이템 스택의 stacksize 변수가 개수를 나타냅니다. player.capabilities.isCreativeMode는 플레이어가 크리에이티브 모드이면 true, 아니면 false의 값을 가집니다. 아이템 스택의 개수가 0이 되면 onItemRightClick을 호출한 코드가 알아서 인벤토리에서 해당 아이템을 삭제합니다. 지금 추가된 구문은 아이템을 소모시킬 때 널리 사용됩니다.

ItemFortuneCookie.java

@Override

public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player) {

 

    if (!player.capabilities.isCreativeMode) {

        --itemStack.stackSize;

    }

 

    if (world.isRemote) {

        player.addChatComponentMessage(new ChatComponentText(fortune[player.getRNG().nextInt(fortune.length)]));

    }

 

    return itemStack;

}

그림 3 완성본

이렇게 해서 포춘 쿠키가 완성 되었습니다. Item 클래스 내의 이벤트 함수들에 대한 구체적인 정보도 따로 정리하여 올리겠습니다.

 

 

참고자료

https://en.wikipedia.org/wiki/Event-driven_programming

http://bindingofisaac.wikia.com/wiki/Fortune_Telling_Machine

http://bedrockminer.jimdo.com/modding-tutorials/advanced-modding/modding-tips-and-tricks/#chatSP

https://en.wikipedia.org/wiki/Fortune_cookie

+ Recent posts