初心者研修編リンク
- 新規Modの作成からビルドまで
- 早速プログラムを書いてみる
- ItemとItemStackの違い ← here
- BlockとIBlockStateとTileEntityは一緒です(大嘘)
- Modを国際化する
- デバッグを実践する
- イベント購読
- 音とか粒子とか
- ワールドとディメンションは何が違うんだ
- 「メタデータ」ってかっこいいな
- Entityを少しだけ操る
- 強敵NBT
- メインページへ戻る
はじめに
前回の記事(早速プログラムを書いてみる)では、レシピの登録を題材にプログラミングを実践しました。
意外と簡単だったと思います。(難しかったらゆっくりやればいいだけですので)
今回はマイクラのModdingに欠かせないItem
とItemStack
の違いを理解するだけの記事です。
それじゃあつまらないだろうと思ったので、(不完全ではありますが)アイテムを登録するソースコードも一緒に付けました。ぜひ遊んでみてください。
(なんならレシピ登録のプログラミングより楽かもしれない)
目次
1. とりあえずアイテムを追加してみる
2. Item
とItemStack
の違い
3. イメージ図
4. Item
とItemStack
の違いを踏まえてコードを読む
5. 終わりに
1. とりあえずアイテムを追加してみる
最短!アイテムの追加コード!(非推奨)
実は機能や見た目、利便性等々を一切考えなければ、アイテムの追加は短い1行で終了します。
event.getRegistry().register(new Item().setRegistryName("my_item1"));
これをpublic void registerItems(RegistryEvent.Register<Item> event) {
の直後にコピペするだけでアイテムを追加できます。
が、正直、このコードはあまり勉強向きでない例1なのでわざわざやらなくて良いです。
わざわざやった例
まず、コードをコピペした時のスクショです。
40行目にコピペしました。(前回のレシピ登録コードを追加している場合は行数がずれます。)
/give @p my_first_mod:my_item1
でしか入手できない2ので非常に不便です。
└わざわざやった例終わり
アイテムを追加するコード
さて、もう多少は学びがいのある3アイテム追加練習コードを用意しました。
// アイテムの見本を生成
Item myItem1 = new Item();
// 必須
// 内部名の設定(半角小文字とアンダーバーのみ使用可能)
// 好きな名前を付けられる(他との被り不可)
myItem1.setRegistryName("my_item1");
// 必須
// 翻訳基本名の設定(半角小文字やアンダーバー、ドット辺りを使うことが多い)
// setRegistryNameと同じものを使うのが自然
myItem1.setTranslationKey(new ResourceLocation(Tags.MODID, "my_item1").toString());
// アイテムを入手できるクリエイティブタブの設定
// CreativeTabs.****でエラーにならないものなら何でも可
myItem1.setCreativeTab(CreativeTabs.FOOD);
// アイテム(の見本)の登録(生成しただけじゃゲーム内では使用できない!)
event.getRegistry().register(myItem1);
さてコードの中身を軽めに解説しておこう!
少しづつJavaに慣れて行くためにも、まずは比較的シンプルなコードを上から順にみていきましょう。
// アイテムの見本を生成
Item myItem1 = new Item();
まずはアイテムの見本を生成する行です。
new
って書いてある通り、新しいアイテムの見本が作られます。
わざわざ見本と強調している通り、ここで作られるものはイメージするゲーム中のアイテムとは異なります。
(実は後述するItemStackこそがゲーム内のアイテムに対応するという早すぎるネタバレ)
以降のコードでは生成した見本をmyItem1
という名前で一時的に呼ぶことにします。
じゃあ永久的な名前は?というとその次のコードです。
// 必須
// 内部名の設定(半角小文字とアンダーバーのみ使用可能)
// 好きな名前を付けられる(他との被り不可)
myItem1.setRegistryName("my_item1");
生成したいアイテム見本の内部名4を設定します。
"my_item1"
の部分を変更することで任意の名づけが可能ですが、ModIdと同様に半角アルファベットやアンダーバーしか使えない5ことに注意しましょう。
空白や大文字さえも非推奨です。
レシピの内部名と同様に、被らない名前6を付けましょう。
// 必須
// 翻訳基本名の設定(半角小文字とアンダーバー、ドット辺りを使うことが多い)
// setRegistryNameと同じものを使うのが自然
myItem1.setTranslationKey(new ResourceLocation(Tags.MODID, "my_item1").toString());
ここはまあ、一つ前と同じもの8を設定しておけば特に問題は無いです。わざわざ説明するほどの重要な話はありません。
もうちょっと真面目な説明
翻訳関連については次の記事でより詳細に解説する予定のため、ここではこのコードの説明や特徴の解説に留めます。
アイテム名の翻訳キーをitem.(ModId):(設定名).name
に設定するコードです。
(今までのコードをアレンジせずに来た場合はitem.my_first_mod:my_item1.name
が翻訳キーです。)
Tags.MODID
がModIdを表しています。
名前にModIdを組み合わせることで他Modと重複することを防いでいます。
setRegistryName
は自動でModIdが付くのになぜ同じそういう仕様にしなかったのか
└もうちょっと真面目な説明終わり
続いてが一番カスタムしがいのある(?)部分です。
// アイテムを入手できるクリエイティブタブの設定
// CreativeTabs.****でエラーにならないものなら何でも可
myItem1.setCreativeTab(CreativeTabs.FOOD);
コメントを読めば何をする部分かは割と分かります。
別に食料でも何でもないのに食料タブに設定してる
お好みで好きなクリエイティブタブを設定してください。
簡単な設定方法(復習)
前記事 の「2.成果品を別アイテムにする」でアイテムの選択肢表示を出す方法の説明を行いましたが、これも同様です。(全部一緒)
FOOD
の部分を消し、myItem1.setCreativeTab(CreativeTabs.!!ココ!!);
にキャレットがある状態(クリックした状態)でCtrl+スペースを押して予測表示を出しましょう。
└簡単な設定方法(復習)終わり
ちなみに、クリエイティブタブで入手できないようにしたい(例:コマンドブロック)場合は、この行を丸ごと削除すればOKです。
裏を返せば、このプログラムコードでちゃんと設定しないとクリエイティブタブに現れないということです。
食料タブに設定したからといって、食べ物になるわけではありません。
食料アイテムの作成は初級編で解説予定です。
最後にできあがったアイテム見本を登録するコードです。
// アイテム(の見本)の登録(生成しただけじゃゲーム内では使用できない!)
event.getRegistry().register(myItem1);
いわゆる「武器や防具は持っているだけじゃ意味が無いぞ、ちゃんと装備しよう!」ってのと同じです。
ちゃんと登録しておかないと使えません。
これ以上説明することは無い
└さてコードの中身を軽めに解説しておこう!終わり
このアイテム登録コードをregisterItems
のメソッドの中にコピペしてください。
public void registerItems(RegistryEvent.Register<Item> event) {
の次の行ですね。
画像のようにメソッドが折り畳まれている場合はちゃんと展開してからにしましょう9。(行番号の右の>
をクリック)
(前回のレシピ登録コードを追加している場合は開始行番号がずれます。)
コピペがミスっていなければそのまま2.Run Client
できるはずです。
バグった見た目と名前のアイテムが指定したクリエタブに追加されていますね。
本当に何の機能も見た目も持たない"スカ"なアイテムが追加されました。
手にもって右クリックしても何も起きません。
あっけなく終わったように感じますが、これでアイテム追加コードの基礎は完了です!
「見た目と名前バグってるんだけど終わり?」という声が聞こえてきそうですが、実はアイテムの見た目と名前はJavaのコードではなくリソースパック10の領分のため、解説はリソパ編に回します。
(食料アイテムの追加方法を含め、アイテムの追加方法特化の解説は初級編で行う予定です。)
無機能アイテムの追加は非常に簡単ですが、Javaのコードとリソースパックの両方を組み合わせる必要があります
ワールドに入る際になんか警告が出た時
これは「前回プレイ時に登録されていたアイテムが今回は登録されていないため、既に存在していたワールド内の該当のアイテムのスロットは全て空になります11」という警告です。
Modを抜いたり入れ替えたりして遊んだ場合にはよく見かけるメッセージですね。
アイテムの内部名を変更した際にはその度に出るので慣れます。
逆に何も変更していないはずなのにこれが出たらヤバイ(大抵は入るワールド間違えたパターンだけど)
└ワールドに入る際になんか警告が出た時終わり
2. ItemとItemStackの違い
さて突然ですがクイズです。以下の二つのアイテムの違いは何でしょう。
ひっかけ問題ではありません。スロット位置が違う!12
.
.
.
どう見ても耐久値が違いますね。
では第2問、先ほどの二つのアイテムの共通点は何でしょう。同じ画面内!
.
.
.
.
.
.
これは正答が無数にあると思いますが、一番は 「どちらも同じ鉄の剣」 ということです。
両方とも両方とも鉄の剣という名前で、両方とも武器で、両方とも攻撃力が6で、両方とも焼くと鉄ナゲット(鉄塊)ができて…
両方とも同じ性質・属性のアイテムです。
今回は2個ですが、いくつ鉄の剣があっても全て同じ性質・属性を持ちます。
対して、耐久値はモノによって違います。
どんなエンチャントが付いているかもモノによって違います。
金床で付けた名前もモノによって違うでしょう13。
つまり、二つは同じ鉄の剣でも、常に共通する要素とモノによって異なりうる要素の2つの要素が混ざっているということです。
この共通要素がItem
、ひな形、テンプレート、設計図、見本であり14、
個別要素がItemStack
、個体、インスタンス15です。
当然ながら個別要素ItemStack
は共通要素Item
を土台にしています。
(例えばアイテム名は個別に設定できる個別要素であるが、特段変更しない限りは共通要素として決まっている名前がデフォルトとして使われるようなイメージ)
つまり、二つの鉄の剣は同じItem
だが、異なるItemStack
ということです。
鉄インゴットに耐久値やエンチャントのような概念はありませんが、「個数」16の概念はあります。
そしてそれはモノによって違う=個別要素(ItemStack
)ということです。
ここで少しItem
とItemStack
という言葉の使い方を修正しておきます。(ふーん。程度でOK)
-
Item
はそのアイテムの共通要素(共通設定)を全てまとめたモノ(クラス17) -
ItemStack
はItem
をベースに追加で個別要素を持っているモノ(クラス)
3. イメージ図
人間言葉で説明されるより図見たほうがずっと理解しやすい!
ということでItem
とItemStack
の違いを図にしてみました。はいここテスト出るぞーちゃんと板書しとけ~
Item
は共有しており、ItemStack
は個別に存在しています。
例えば左上の鉄の剣①の耐久値(ItemStack
の要素)が1減ったとしても、下の鉄の剣②には一切影響がありませんが、
鉄の剣の元の攻撃力(Item
の要素)自体を変えてしまうと、ゲーム内全ての鉄の剣に影響が出ます。
つまり、
-
Item
は基本設定を持つ。 -
ItemStack
はItem
を参照しつつ、個別状態を持つ18。
特に、Item
はゲームを通してずっと同じ設定値だけを持ち、ItemStack
はプレイ中に変化しうる状態を保持するイメージです。
(例えばデフォルトのアイテム名がプレイ中に突然変化することは不自然、逆にどんなに使用しても個数が3個のままだったらそれも不自然)
4. Item
とItemStack
の違いを踏まえてコードを読む
Item
本記事最初19のコードを再掲します。
// アイテムの見本を生成
Item myItem1 = new Item();
// 必須
// 内部名の設定(半角小文字とアンダーバーのみ使用可能)
// 好きな名前を付けられる(他との被り不可)
myItem1.setRegistryName("my_item1");
// 必須
// 翻訳基本名の設定(半角小文字やアンダーバー、ドット辺りを使うことが多い)
// setRegistryNameと同じものを使うのが自然
myItem1.setTranslationKey(new ResourceLocation(Tags.MODID, "my_item1").toString());
// アイテムを入手できるクリエイティブタブの設定
// CreativeTabs.****でエラーにならないものなら何でも可
myItem1.setCreativeTab(CreativeTabs.FOOD);
// アイテム(の見本)の登録(生成しただけじゃゲーム内では使用できない!)
event.getRegistry().register(myItem1);
アイテムを登録するコードですが、Item
を扱っています。
すなわち(ゲームを通して変わらない)基本設定値のみを設定しています。
- 「内部名」は固定値でないと正しく動作しない
- 「翻訳基本名」は翻訳版内部名なので「内部名」と同じと思って良い
- 「クリエイティブタブ」はプレイ中に変化することは相当考え難い
どれもItem
として扱うべき基本設定であることが分かります。
イメージ図を踏まえると、「最大スタック数や最大耐久値はどうやって指定するの?」と疑問に思うでしょうが、単純に設定する方法を紹介していないだけです。
それらを踏まえたアイテム登録の基本解説は初級編で行います。
ItemStack
ItemStack
は前記事でレシピを登録する際に登場しました。
{
//--------------------不定形レシピの追加--------------------
// クラフト材料の設定(アイテムの指定は順不同)
NonNullList<Ingredient> ingredients = NonNullList.create();
ingredients.add(Ingredient.fromItem(Items.APPLE)); // リンゴ
ingredients.add(Ingredient.fromItem(Items.GOLD_INGOT)); // 金インゴット
// クラフト成果品の設定
ItemStack output = new ItemStack(Items.GOLDEN_APPLE, 2); // 金リンゴ×2
// レシピの作成
ShapelessRecipes recipe = new ShapelessRecipes("", output, ingredients); // groupと成果品と材料からレシピ作成
// レシピの登録
recipe.setRegistryName(new ResourceLocation(Tags.MODID, "apple_plus_gold_ingot")); // 登録時の内部レシピ名指定
event.getRegistry().register(recipe); // 登録
}
これのクラフト成果品を作る部分です。
// クラフト成果品の設定
ItemStack output = new ItemStack(Items.GOLDEN_APPLE, 2); // 金リンゴ×2
Items.GOLDEN_APPLE
は金リンゴのItem
です。
そしてコンマの後ろの2
は個数です。
つまり、Item
に個数という状態を組み合わせてItemStack
を生成(new
)しています。
ということは、ここで鉄の剣をItem
として指定し、ダメージ値という状態を与えることによって
消耗した鉄の剣をクラフトするレシピも作れそうですね。
消耗した鉄の剣レシピ
耐久値20が200削れた鉄の剣をクラフトするレシピです。
{
//--------------------不定形レシピの追加--------------------
// クラフト材料の設定(アイテムの指定は順不同)
NonNullList<Ingredient> ingredients = NonNullList.create();
ingredients.add(Ingredient.fromItem(Items.STICK)); // 棒
ingredients.add(Ingredient.fromItem(Items.IRON_NUGGET)); // 鉄塊
ingredients.add(Ingredient.fromItem(Items.IRON_NUGGET)); // 鉄塊
// クラフト成果品の設定
ItemStack output = new ItemStack(Items.IRON_SWORD, 1, 200); // 鉄の剣×1 (耐久値が200削れている)
// レシピの作成
ShapelessRecipes recipe = new ShapelessRecipes("", output, ingredients); // groupと成果品と材料からレシピ作成
// レシピの登録
recipe.setRegistryName(new ResourceLocation(Tags.MODID, "almost_broken_iron_sword")); // 登録時の内部レシピ名指定
event.getRegistry().register(recipe); // 登録
}
さらにエンチャントまで付けちゃう
エンチャントについては研修でやるほどの面白い内容が無いので初級編で扱います。
また、NBTの説明は研修編の終わりの方で解説予定です。
{
//--------------------不定形レシピの追加--------------------
// クラフト材料の設定(アイテムの指定は順不同)
NonNullList<Ingredient> ingredients = NonNullList.create();
ingredients.add(Ingredient.fromItem(Items.STICK)); // 棒
ingredients.add(Ingredient.fromItem(Items.IRON_NUGGET)); // 鉄塊
ingredients.add(Ingredient.fromItem(Items.IRON_NUGGET)); // 鉄塊
// クラフト成果品の設定
ItemStack output = new ItemStack(Items.IRON_SWORD, 1, 200); // 鉄の剣×1 (耐久値が200削れている)
// エンチャントの設定
output.addEnchantment(Enchantments.UNBREAKING, 2); // 耐久力(Lv2)
output.addEnchantment(Enchantments.VANISHING_CURSE, 1); // 消滅の呪い
// レシピの作成
ShapelessRecipes recipe = new ShapelessRecipes("", output, ingredients); // groupと成果品と材料からレシピ作成
// レシピの登録
recipe.setRegistryName(new ResourceLocation(Tags.MODID, "almost_broken_enchanted_iron_sword")); // 登録時の内部レシピ名指定
event.getRegistry().register(recipe); // 登録
}
└さらにエンチャントまで付けちゃう終わり
└消耗した鉄の剣レシピ終わり
5. 終わりに
とりあえず、Item
は「アイテムの基本設定(基本情報)」、ItemStack
は「Item
+状態」と思っておけば良いですね。
Modding初心者23は大抵Item
とItemStack
を混同しているため、区別できるようになればそれだけで初心者脱却への一歩です。
初心者研修編 - BlockとIBlockStateとTileEntityは一緒です(大嘘)に続く
-
setTranslationKey
を呼んでない、なんならそもそもItemを継承せずにそのままインスタンス化するような状況がほぼ無い
工業Mod依存者とか素材アイテム大好きマンなら別だが↩ -
JEI入ってるじゃんという屁理屈は不要 ↩
-
いきなり別ファイルに継承クラスを書き始められると学ぶ側は不安になってしまうので、ここではごくシンプルなものを使う ↩
-
他のアイテムと区別するために使用する名前、識別名
この名前を基準にアイテムを認識しているため、名前が変わると別アイテム扱い
昔は数値ID(のみ)で認識していたので、それよりは進歩した ↩ -
厳密にはModId同様、使えなくはないがバグの元 ↩
-
他Modやバニラとは被っても良い
内部名は(ModId):(設定した名前)
7となるため、別Modの場合は別名扱い
なお、ブロックとアイテムで名前が被ってもよい(別管理のため) ↩ -
こういった
(ModId):
の部分を名前空間と呼ぶ ↩ -
ModIdが付いてるじゃんという屁理屈は受け付けておりません ↩
-
あえて一度ミスってみるのも手
やらかした時にどうなるかを予め知っておけばいざという時に焦りづらい
マイクラ秋のエラー祭り
↩ -
意外に思えるが、Modはリソースパックを内蔵している
そのため、リソースパックの知識が必要(「リソースパックを知っていれば簡単」とも言い換えられる) ↩ -
一度空になってしまった場合、Modを入れ直してワールドに入っても復活しない
ただし、文字列で保存されている場合はModを入れ直せば大抵元通りになる
クエストModとかは割とそう ↩ -
「スロット位置が違う」は「ItemStackのインスタンスが違う」という意味でクイズの適切な正解ではあるが… ↩
-
全てに同じ名前を付ける
狂じ…狂ったひ…変じ…ちょっと変わった人は別 ↩ -
Item
は「アイテムの種類」でItemStack
は「実際のアイテム」と呼ぶこともできそうだが、「アイテムの種類」だと「ツールor食べ物or素材」という上位の種類ばかりイメージしてしまうのが難点か
「ひな形」という言い回しがそれより優れているかどうかはまた悩ましい問題である↩ -
Item
も(プログラミングの文脈では)インスタンスだという指摘は話を面倒にするのでナシで
あくまでゲームMinecraftとしての考え方 ↩ -
ItemStack
のStackは英語で「積み重なった(ひとまとまりの)もの」という意味
カタカナ読みは「スタック」 ↩ -
「クラス」はプログラミング用語
おおざっぱには「モノ」だが、「インスタンス(個体・個別具体物)」に対して「それらの元となる設計図・ひな形・概念」というニュアンスの言葉
教義的にはオブジェクトの属性と操作を定義したもの ↩ -
マイクラ全体で見れば、「ゲーム内要素であること」という基本から
Block
やItem
という個別に分かれるという階層構造で考える方法もあるかもしれない ↩ -
最初じゃなくて2番目だという屁理屈は不要 ↩
-
ItemStack
の第3引数はメタデータ21を設定するところである
耐久値は最大値-ダメージ値(メタデータ)
で計算されるため、耐久値が250-200=50残った剣がクラフトされる ↩ -
メタデータ(マイクラ内部仕様用語)とは、アイテム22の状態を数値で表現したもの
当然状態なのでItem
ではなくItemStack
が保持する
羊毛や木材の種類もこの値に依存する ↩ -
アイテムだけじゃなくてブロックにもあるが、その話はまた今度 ↩
-
諸説あるが、勘でModdingしてきたせいで↩Item
とItemStack
の違いが分からないとんでもない人間もいるかもしれないしいないかもしれない