12
7

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.

KotlinAdvent Calendar 2016

Day 16

KotlinでMinecraft Modding

Last updated at Posted at 2016-12-15

この記事は、Kotlin Advent Calendar 2016 16日目の記事です。


今年の2月、Kotlinのバージョン1.0が公開され、公式のBlogに投稿されました。

Kotlin 1.0 Released: Pragmatic Language for JVM and Android
https://blog.jetbrains.com/kotlin/2016/02/kotlin-1-0-released-pragmatic-language-for-jvm-and-android/

JVMへの完全互換とJavaへの高い親和性、IntelliJベースのAndroidStudioによる協力なサポートで、Androidでの勢力を伸ばしているKotlinですが、このページには他にも「こんなのでKotlinが使えるよ!」というのが書かれています。

  • IntelliJ IDEA, Android Studio and Eclipse
  • Maven, Gradle and Ant
  • Spring Boot (Kotlin support released today!)
  • GitHub, Slack and even Minecraft :)

https://blog.jetbrains.com/kotlin/2016/02/kotlin-1-0-released-pragmatic-language-for-jvm-and-android/

一番最後ですが

>>> Minecraft <<<

ここで言うMinecraftとは、Minecraft Modのことです。

「Modification」や「Modding file」の略で、主にパソコンゲーム用の改造データのこと。
ユーザーが拡張ファイルを作り、素の状態のファイル(バニラ)に追加・上書きを行うことでゲームの機能を拡張する。
https://www26.atwiki.jp/minecraft/pages/940.html

MinecraftがもともとJava製なため、Modは基本Javaで書かれてきました。
KotlinはJVM互換なので、もちろんMinecraft Modでも使うことができます。
MinecraftでModを作る事実上のスタンダードとなっているMinecraft ForgeにKotlinを導入する過程については、以下で詳しくまとまっているのでそちらをご覧ください。

KotlinでMinecraftのModdingをするための環境構築

Kotlinによって便利に

Minecraft内部のソースコードは、最近はNullableアノテーションとかがしっかり付くようになってきており、Kotlin側から見た時に型のNullable/NonNullがしっかりしている箇所が多くなってきました。

Minecraft Modにおいて、nullでreturnするような箇所といえば、「プレイヤーが持っているアイテムが○○のとき」のような分岐をしたいときです。

BlockBreakEventHandler.java
public class BlockBreakEventHandler {

    @SubscribeEvent
    public void onBlockBreak(BlockEvent.BreakEvent event) {
        EntityPlayer player = event.getPlayer();
        if (player == null) {
            return;
        }
        if (player.getHeldItemMainhand() == null) {
            return;
        }
        Item item = player.getHeldItemMainhand().getItem();
        if (item != null) {
            return;
        }
        // itemがnon-nullのときだけここに来る
    }
}

nullになる箇所は3箇所

  • event.getPlayer()
  • player.getHeldItemMainhand()
  • player.getHeldItemMainhand().getItem()

(ちなみに、この内@Nullableアノテーションがしっかりと付いているのは真ん中の物のみです。)
ifをまとめて書けば多少はマシになりますが、とはいえ横に長くなりすぎて辛いです。

BlockBreakEventHandler.java
public class BlockBreakEventHandler {
    @SubscribeEvent
    public void onBlockBreak(BlockEvent.BreakEvent event) {
        EntityPlayer player = event.getPlayer();
        if (event.getPlayer() == null || event.getPlayer().getHeldItemMainhand() == null || event.getPlayer().getHeldItemMainhand().getItem() != null) {
            return;
        }
        Item item = event.getPlayer().getHeldItemMainhand().getItem();
        // itemがnon-nullのときだけここに来る
    }
}

ここをKotlinで書くとこうなります。

BlockBreakEventHandler.kt
class BlockBreakEventHandler {
    @SubscribeEvent
    fun onBlockBreak(event: BlockEvent.BreakEvent) {
        val item = event?.player?.heldItemMainhand?.item ?: return
        // itemがnon-nullのときだけここに来る
    }
}

nullでの早期returnが1行で済みました。

他の部分も見てみましょう

例えば、追加した動物のAIを書き換えるとします。
普通であれば以下のようにEntityのコンストラクタで指定します。
(第1引数が優先度、第2引数が追加するAIです)

EntityNewAnimal.java
public EntityNewAnimal(World worldIn) {
    super(worldIn);
    setSize(0.9f, 0.9f);
    tasks.addTask(1, new EntityAISwimming(this));
    tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4f));
    tasks.addTask(6, new EntityAIMate(this, 1.0d));
    tasks.addTask(7, new EntityAIWander(this, 1.0d));
    tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0f));
    tasks.addTask(9, new EntityAILookIdle(this));
}

ここをKotlinでスッキリさせてみます。

まず、新しくKotlinExtension.ktといった適当な名前のファイルを作成します。
ここに、以下の関数を書きます。

KotlinExtension.kt
fun EntityLiving.addTasks(vararg taskPairs: Pair<Int, EntityAIBase>) {
    taskPairs.forEach { tasks.addTask(it.first, it.second) }
}

すると、呼び出し元は以下のように書くことができます。

EntityNewAnimal.kt
init {
    setSize(0.9f, 0.9f)
    addTasks(1 to EntityAISwimming(this),
             3 to EntityAILeapAtTarget(this, 0.4f),
             6 to EntityAIMate(this, 1.0),
             7 to EntityAIWander(this, 1.0),
             9 to EntityAIWatchClosest(this, EntityPlayer::class.java, 8.0f),
             9 to EntityAILookIdle(this))
}

メソッド呼び出しは減り、見た目もスッキリしました。

はじめにKotlinExtension.ktに書いたものが、Kotlinの拡張関数です。

今回は、EntityLivingクラスにaddTasksという名前で、IntとEntityAIBaseのPairの可変長引数を取る関数を追加しているように見せています。
(EntityNewAnimalはEntityLivingのサブクラスです)


といった感じで、Kotlinを使ってMinecraft Moddingする際の便利なTipsを2点ほど載せてみました。

Minecraftの世界にもKotlinの波が来てほしいです。

12
7
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
12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?