1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Minecraft 1.13.2のmodでGUIを表示してみた

Posted at

前文

実際に解説に入る前に、なぜこの記事を書いたのかについて書いておく。

誰得?

既に1.12系以前のGUIに関する知識を有していて、1.13系に移行したい人向け。

なぜGUI?

実際に1.12.2から1.13.2に移行した際、GUI関連、特にregisterExtensionPoint<T>に関して適当な記事がまだなかったので。
NetworkHooks.openGuiを使え」という記事はたくさんあるのに。。。

解説

GUIファクトリの登録

前文で触れたNetworkHooks.openGuiでGUIを表示するためには、事前にModLoadingContext#registerExtensionPoint<T>を呼び出してGUIファクトリを登録しておく必要があります。

ExampleMod.java
public ExampleMod() {
    /* 省略 */
    IEventBus bus = FMLJavaModLoadingContext.get().getModEventBus();
    bus.addListener(this::doClientStuff);
    /* 省略 */
}

private void doClientStuff(final FMLClientSetupEvent event) {
    registerGuiFactory();
}

@OnlyIn(Dist.CLIENT)
private static void registerGuiFactory() {
    ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.GUIFACTORY, () -> (openContainer) -> {
        switch (openContainer.getId().toString()) {
            case "examplemod:foo":
                return new GuiContainerFoo(new ContainerFoo());
            case "examplemod:bar":
                return new GuiContainerBar(new ContainerBar());
            /* 必要に応じてGuiScreenの派生オブジェクトを返せるようにしておく */
        }
        return null;
    });
}

ModLoadingContext#registerExtensionPoint<T>ExtensionPoint<T>Supplier<T>を引数に取る。
第1引数に指定するExtensionPoint.GUIFACTORYの型はExtensionPoint<Function<FMLPlayMessages.OpenContainer, GuiScreen>>なので、第2引数にはSupplier<Function<FMLPlayMessages.OpenContainer, GuiScreen>>型のオブジェクトを渡す必要がある。
実際には、FMLPlayMessages.OpenContainerを受け取ってGuiScreenを継承したクラスのオブジェクトを返すFunctionを返すSupplierを渡す。ここで、FMLPlayMessages.OpenContainerのread系のメソッドで、GUI呼び出し側のPacketBufferに書き込んだデータを受け取ることができる。
GuiScreenを継承したクラスについては1.12系と変わらないので割愛する。

また、SSPではなくSMPを対象にしたMOD制作ではregisterExtensionPoint<T>の呼び出し箇所に注意が必要となる。
サーバ側でも呼ばれる箇所(例えばコンストラクタ等)でregisterExtensionPoint<T>を呼ぶと、「GuiScreenがないよ」とお叱りを受けてクラッシュするので、クライアント側でのみ呼び出されるFMLClientSetupEventを受け取るイベントハンドラ(前述の例ではdoClientStuffがこれに相当する)で呼び出すのがよい。

GUIの呼び出し

アイテムを右クリックした場合

NetworkHooks.openGuiを呼び出してGUIを表示する。
第2引数のインタラクションオブジェクト(後述する)で、GUIの表すコンテンツのコンテナオブジェクトを作成したり、どのGUIを表示するかを指定したりする。
第3引数のConsumer<PacketBuffer>内で、GUIファクトリとの間でデータの受け渡しを行う。
このPacketBufferのwrite系のメソッドで書き込んだデータは、GUIファクトリ側ではFMLPlayMessages.OpenContainerのread系のメソッドで読み取ることができる。

ItemFoo.java
public class ItemFoo extends Item {
    /* 省略 */
    public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand) {
        ItemStack foo = player.getHeldItem(hand);
        if (!world.isRemote && player instanceof EntityPlayerMP) {
            NetworkHooks.openGui((EntityPlayerMP)player, new InteractionObjectFoo(foo, hand), (buffer) -> {
                /* bufferを通じて、必要な情報をGUIファクトリに受け渡す */
            });
        }
        return ActionResult.newResult(EnumActionResult.SUCCESS, foo);
    }
}

ブロックを右クリックした場合

こちらも同様にNetworkHooks.openGuiを呼び出してGUIを表示する。
ただ、第2引数のインタラクションオブジェクトは個別に実装する必要はなく、TileEntityの派生クラスにIInteractionObjectインタフェースを実装することで対応する。

BlockFoo.java
public class BlockFoo extends Block {
    /* 省略 */
    @Override 
    public boolean onBlockActivated(IBlockState state, World world, BlockPos pos, EntityPlayer player, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
        if (world.isRemote) {
            return true;
        }
        TileEntity tile = world.getTileEntity(pos);
        if (tile instanceof TileEntityFoo && player instanceof EntityPlayerMP) {
            NetworkHooks.openGui((EntityPlayerMP)player, (TileEntityFoo)tile, (buffer) -> {
                /* bufferを通じて、必要な情報をGUIファクトリに受け渡す */
            });
        }
        return true;
    }
}

インタラクションオブジェクト

NetworkHooks.openGuiの第2引数に渡すIInteractionObjectインタフェースを実装するオブジェクトについて解説する。

InteractionObjectFoo.java
public class InteractionObjectFoo implements IInteractionObject {
    private final ItemStack foo;
    private final EnumHand hand;

    public InteractionObjectFoo(ItemStack foo, EnumHand hand) {
        this.foo = foo;
        this.hand = hand;
    }

    @Override
    public ITextComponent getCustomName() {
        return null;
    }

    @Override
    public ITextComponent getName() {
        return null;
    }

    @Override
    public boolean hasCustomName() {
        return false;
    }

    @Override
    public Container createContainer(InventoryPlayer arg0, EntityPlayer arg1) {
        return new ContainerFoo(this.foo, arg0, this.hand);
    }

    @Override
    public String getGuiID() {
        return "examplemod:foo";
    }
}

createContainerはクライアント・サーバの両側で用いられるContainerクラスを継承したオブジェクトを返す。
getGuiIDの返す文字列が、GUIファクトリ側でFMLPlayMessages.OpenContainer.getIdとして参照できる文字列となる。

コンテナオブジェクト

コンテナオブジェクトについては1.12系から特段変わった点は少ない。
変更点といえばaddSlotToContaineraddSlotに名称変更されたくらい。

ContainerFoo.java
public class ContainerFoo extends Container {
    public ContainerFoo(ItemStack foo, InventoryPlayer inventoryPlayer, EnumHand hand) {
        addSlot(new Slot(/* 省略 */));
    }
}

まとめ

既に1.14系のForgeも出ているので1.13系の記事は今更感がありますが、何かの役に立てればと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?