この記事は、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 :)
一番最後ですが
>>> 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するような箇所といえば、「プレイヤーが持っているアイテムが○○のとき」のような分岐をしたいときです。
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をまとめて書けば多少はマシになりますが、とはいえ横に長くなりすぎて辛いです。
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で書くとこうなります。
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です)
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
といった適当な名前のファイルを作成します。
ここに、以下の関数を書きます。
fun EntityLiving.addTasks(vararg taskPairs: Pair<Int, EntityAIBase>) {
taskPairs.forEach { tasks.addTask(it.first, it.second) }
}
すると、呼び出し元は以下のように書くことができます。
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の波が来てほしいです。