• 10
    いいね
  • 0
    コメント

この記事は、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の波が来てほしいです。