最新版 |
編集中の文章 |
1行目: |
1行目: |
− | {{前提MOD|reqmod="Minecraft Forge Universal 14.23.1.2555~"}}
| |
− | {{チュートリアル難易度|difficulty=0|clear=none}}
| |
− | {{チュートリアルカテゴリー|difficulty=0|type=Entity}}
| |
| | | |
− | ==エンティティの追加== | + | == '''1.12.2でのエンティティの追加''' == |
− | 1.12.2ではエンティティの追加には、 | + | エンティティの追加にはまず、 |
− | * '''Model ~''' クラス
| + | * Model~ クラス |
− | * '''Render ~''' クラス | + | * Render~ クラス |
− | * '''Entity ~''' クラス | + | * Entity~ クラス |
− | の3つのクラスが必要になります。<br>
| + | の3つのクラスから作られます。 |
− | 今回は、まず動かないエンティティを追加し、
| |
− | そこに機能を追加していきたいと思います。
| |
− | | |
− | == ソースコード ==
| |
− | 今回は、3つのクラスを作り<br>
| |
− | それをEntityInitというクラスを使って登録していきたいと思います。
| |
− | === EntityMod.java ===
| |
− | MODの起動に必須のメインクラスです。<br>
| |
− | ここで登録用の関数を呼びます。<br>
| |
− | | |
− | ModのIDはここではわかりやすく''entitymod''としていますがどんなIDでも大丈夫です。<br>
| |
− | (ただしIDは小文字にしてください)
| |
− | | |
− | <source lang = java>
| |
− | // ここのパッケージは、自分の開発環境に合わせて変えてください。
| |
− | package defeatedcrow.tutorial;
| |
− | | |
− | import net.minecraft.item.Item;
| |
− | import net.minecraft.util.ResourceLocation;
| |
− | import net.minecraftforge.fml.common.Mod;
| |
− | import net.minecraftforge.fml.common.Mod.EventHandler;
| |
− | import net.minecraftforge.fml.common.Mod.Instance;
| |
− | import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
| |
− | import net.minecraftforge.fml.common.registry.ForgeRegistries;
| |
− | import net.minecraftforge.fml.relauncher.Side;
| |
− | import net.minecraftforge.fml.relauncher.SideOnly;
| |
− | | |
− | @Mod(modid = "entitymod", //MODID登録
| |
− | name = "EntityMod", //MODの表示される名前を登録
| |
− | version = "1.0.0", //MODのバージョンを登録
| |
− | acceptedMinecraftVersions=1.12.2, //MODのMinecraftの対応バージョンを登録
| |
− | useMetadata = true)
| |
− | public class EntityMod
| |
− | {
| |
− | @Instance("karasuman_item")
| |
− | public static CrowItem instance;
| |
− | | |
− | /* 追加アイテムのインスタンス */
| |
− | public static Item crowFace;
| |
− | | |
− | @EventHandler
| |
− | public void preInit(FMLPreInitializationEvent event) {
| |
− | // インスタンスの生成
| |
− | // setRegistryNameが必須になったのが過去版との変更点
| |
− | crowFace = new ItemCrowFace().setUnlocalizedName("crow_face").setRegistryName(
| |
− | new ResourceLocation(MOD_ID, "crow_face"));
| |
− | | |
− | // Itemの登録。
| |
− | // RegistryEventを利用しなくても過去バージョンのように登録用メソッドは呼び出せる
| |
− | ForgeRegistries.ITEMS.register(crowFace);
| |
− | | |
− | // モデル登録もpreInitで呼ぶ。initでは遅い
| |
− | // モデルなどクライアント限定の要素は、サーバー側では読み込むだけでクラッシュしてしまう
| |
− | // なので、イベントのSideがどちらかを判別して、条件分けをする。
| |
− | if (event.getSide().isClient()) {
| |
− | // クライアントサイドの時だけ、モデル登録を呼ぶ。
| |
− | registerModels();
| |
− | }
| |
− | }
| |
− | | |
− | @SideOnly(Side.CLIENT)
| |
− | public void registerModels() {
| |
− | // モデル登録メソッド
| |
− | // フルパスで入れているのは、以下のクラスがClientOnlyのため、Serverサイドでのimport事故を防ぐ目的
| |
− | net.minecraftforge.client.model.ModelLoader.setCustomModelResourceLocation(crowFace, 0,
| |
− | new net.minecraft.client.renderer.block.model.ModelResourceLocation(MOD_ID + ":crow_face", "inventory"));
| |
− | /*
| |
− | * いくつかの注意事項がある
| |
− | * 1: モデルはメタデータごとに設定しなければならない
| |
− | * なので、0~15までメタ値があるアイテムなら16回繰り返すことになる
| |
− | * .
| |
− | * 2: アイテムのモデルの場合はインベントリ内での表示である"inventory"だけでよい
| |
− | * .
| |
− | * 3: ModelResourceLocationに渡すStringはそのままファイルパスとファイル名になる
| |
− | * 上記の場合は、assets/modid名/models/item/crow_face.jsonとなる
| |
− | * .
| |
− | * 追加アイテムが多い場合など、jsonファイルをフォルダ分けしたい場合はここで設定できる
| |
− | * 例えばMOD_ID + ":domein/crow_face"とすれば、
| |
− | * modelsフォルダ下の「domein」フォルダの中にあるcrow_face.jsonを読み取るようになる
| |
− | */
| |
− | }
| |
− | }
| |
− | </source>
| |
− | | |
− | === CrowItem.java ===
| |
− | Item継承のクラスは作らなくても一応Item作成はできるが、凝ったことがしたい場合はこのようにクラスファイルを作成した方が作りやすい。
| |
− | <source lang = java>
| |
− | package defeatedcrow.tutorial;
| |
− | | |
− | import java.util.List;
| |
− | | |
− | import com.google.common.collect.HashMultimap;
| |
− | import com.google.common.collect.Multimap;
| |
− | | |
− | import net.minecraft.block.material.Material;
| |
− | import net.minecraft.block.state.IBlockState;
| |
− | import net.minecraft.client.util.ITooltipFlag;
| |
− | import net.minecraft.creativetab.CreativeTabs;
| |
− | import net.minecraft.entity.SharedMonsterAttributes;
| |
− | import net.minecraft.entity.ai.attributes.AttributeModifier;
| |
− | import net.minecraft.entity.player.EntityPlayer;
| |
− | import net.minecraft.init.Blocks;
| |
− | import net.minecraft.init.SoundEvents;
| |
− | import net.minecraft.inventory.EntityEquipmentSlot;
| |
− | import net.minecraft.item.Item;
| |
− | import net.minecraft.item.ItemStack;
| |
− | import net.minecraft.util.ActionResult;
| |
− | import net.minecraft.util.EnumActionResult;
| |
− | import net.minecraft.util.EnumFacing;
| |
− | import net.minecraft.util.EnumHand;
| |
− | import net.minecraft.util.NonNullList;
| |
− | import net.minecraft.util.SoundCategory;
| |
− | import net.minecraft.util.math.BlockPos;
| |
− | import net.minecraft.world.World;
| |
− | import net.minecraftforge.fml.relauncher.Side;
| |
− | import net.minecraftforge.fml.relauncher.SideOnly;
| |
− | | |
− | /*
| |
− | * オリジナルのItemのクラスを作る
| |
− | * クラスを作らなくてもItem追加はできるが、凝った機能をつけたい場合は必要
| |
− | * 今回は、使いたい人が多そうな機能に絞って、Overrideすることで機能を追加できるメソッドの解説を添えた。
| |
− | */
| |
− | | |
− | /*
| |
− | * Itemを継承(extends)させなければItemとして登録できない<br>
| |
− | * Itemを継承した別のアイテムクラスでもOK
| |
− | * */
| |
− | public class ItemCrowFace extends Item {
| |
− | | |
− | public ItemCrowFace() {
| |
− | super();
| |
− | // この部分でもItemの設定を一部追加できる
| |
− | | |
− | this.setCreativeTab(CreativeTabs.MISC);// 追加するクリエイティブタブ
| |
− | this.setMaxStackSize(16); // 最大のスタック数
| |
− | }
| |
− | | |
− | /* === 機能の例 === */
| |
− | | |
− | /**
| |
− | * このアイテムを持って、空中を右クリックしたときの動作
| |
− | *
| |
− | * @return 処理が成功したかどうかと、結果のItemStackを返す
| |
− | * @param world 現在地のワールド(ディメンジョン)
| |
− | * @param player プレイヤー
| |
− | * @param hand 持ち手 メインハンドかオフハンドか
| |
− | */
| |
− | @Override
| |
− | public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand) {
| |
− | // プレイヤーが持っているItemStackは、プレイヤーから取得
| |
− | ItemStack stack = player.getHeldItem(hand);
| |
− | | |
− | // 今回は使用すると自分を回復するアイテムにしてみる
| |
− | player.heal(20.0F); // ハート20個
| |
− | | |
− | // 使用後にアイテムを減らす処理
| |
− | // このメソッドでは、数を減らした後に0個の場合EMPTYに差し替える処理などはなくてOK
| |
− | stack.shrink(1);
| |
− | | |
− | // 結果を返す
| |
− | return new ActionResult(EnumActionResult.SUCCESS, stack);
| |
− | }
| |
− | | |
− | /**
| |
− | * ブロックをターゲットしながら右クリックした場合はこっち
| |
− | *
| |
− | * @return 処理が成功したかどうかを返す
| |
− | * @param world 現在地のワールド(ディメンジョン)
| |
− | * @param player プレイヤー
| |
− | * @param pos 右クリック対象のBlockPos
| |
− | * @param facing 右クリックしたブロックの面
| |
− | * @param hitX 右クリックした場所がブロックのどのあたりか(中心なら0.5F)
| |
− | * @param hand 持ち手 メインハンドかオフハンドか
| |
− | */
| |
− | @Override
| |
− | public EnumActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing facing,
| |
− | float hitX, float hitY, float hitZ) {
| |
− | ItemStack stack = player.getHeldItem(hand);
| |
− | | |
− | // こちらは右クリックしたブロックが草ブロックだった場合に、草の小道ブロックに置き換えてみる
| |
− | | |
− | //
| |
− | if (!player.canPlayerEdit(pos.offset(facing), facing, stack)) {
| |
− | return EnumActionResult.FAIL;
| |
− | } else {
| |
− | IBlockState state = world.getBlockState(pos);
| |
− | | |
− | // IBlockStateは、その座標のBlockやメタデータ、Stateなどいろいろ確認できる
| |
− | // 今回は草ブロックかどうかの判定に使う
| |
− | if (state.getBlock() == Blocks.GRASS) {
| |
− | | |
− | // 小道ブロック(GRASS_PATH)を設置
| |
− | // ブロックを置くときもIBlockStateが必要なので、ここではgetDefaultState()で初期設定を使用
| |
− | world.setBlockState(pos, Blocks.GRASS_PATH.getDefaultState());
| |
− | | |
− | // シャベルで地面をならしたときの効果音を呼んでいる
| |
− | world.playSound(player, pos, SoundEvents.ITEM_SHOVEL_FLATTEN, SoundCategory.BLOCKS, 1.0F, 1.0F);
| |
− | }
| |
− | | |
− | }
| |
− | return EnumActionResult.SUCCESS;
| |
− | }
| |
− | | |
− | /**
| |
− | * ブロックの破壊速度を変えることができる。
| |
− | * <br>
| |
− | * ツールの破壊可能対象とは違うので、破壊できないブロックでも早く壊してしまう点に注意。
| |
− | *
| |
− | * @return 速度をfloatで返す。1.0Fで素手と同じ、大きくすると早くなる。
| |
− | * @param stack 持っているアイテム
| |
− | * @param state 対象のブロック
| |
− | */
| |
− | @Override
| |
− | public float getDestroySpeed(ItemStack stack, IBlockState state) {
| |
− | // マテリアルが砂だと早く掘れるようにしてみる
| |
− | Material material = state.getMaterial();
| |
− | if (material == Material.SAND) {
| |
− | return 15.0F;
| |
− | } else {
| |
− | return 1.0F;
| |
− | }
| |
− | }
| |
− | | |
− | /**
| |
− | * Entityを殴ったときの攻撃力と攻撃速度を変更する。<br>
| |
− | * Entityのステータス変更はAttributeModifierというものを使用する。<br>
| |
− | * ちなみに、AttributeModifierを自作してオリジナルのステータス変化を作ることもできる。
| |
− | *
| |
− | * @return AttributeModifiersの設定を入れたMultiMap
| |
− | * @param slot アイテムが装備されているスロット
| |
− | * @param stack 対象のアイテム
| |
− | */
| |
− | @Override
| |
− | public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack) {
| |
− | Multimap<String, AttributeModifier> multimap = HashMultimap.<String, AttributeModifier>create();
| |
− | | |
− | // メインハンドの時のみ
| |
− | if (slot == EntityEquipmentSlot.MAINHAND) {
| |
− | // 攻撃力
| |
− | // 数値(10.0F)以外は固定なのでそのままコピペで使用すると楽
| |
− | multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(),
| |
− | new AttributeModifier(ATTACK_DAMAGE_MODIFIER, "Weapon modifier", 10.0F, 0));
| |
− | // 攻撃速度
| |
− | // 数値(-2.5DF)以外は固定
| |
− | multimap.put(SharedMonsterAttributes.ATTACK_SPEED.getName(),
| |
− | new AttributeModifier(ATTACK_SPEED_MODIFIER, "Weapon modifier", -2.5D, 0));
| |
− | }
| |
− | | |
− | return multimap;
| |
− | }
| |
− | | |
− | /**
| |
− | * アイテムのツールチップの内容を増やす。<br>
| |
− | * 頭に@SideOnly(Side.CLIENT)のついているものは、サーバーサイドでは読み込めないので要注意!<br>
| |
− | * これを付け忘れると、マルチプレイのときだけクラッシュしてしまう、よくあるバグModを生み出す
| |
− | *
| |
− | * @param stack 対象のアイテム
| |
− | * @param world 現在のワールド
| |
− | * @param tooltipの文字列が入ったリスト
| |
− | * @param flag f3+Hで拡張ツールチップに切り替えているかどうか
| |
− | */
| |
− | @Override
| |
− | @SideOnly(Side.CLIENT)
| |
− | public void addInformation(ItemStack stack, World world, List<String> tooltip, ITooltipFlag flag) {
| |
− | tooltip.add("呼ばれて飛び出てカラスマン!!!");
| |
− | }
| |
− | | |
− | /**
| |
− | * どのクリエイティブタブにこのアイテムを表示するか。<br>
| |
− | * 1.12では、ここで不必要なTabに出現しないように場合分けしないと、全Tabに出てくるようになってしまった(!!!)<br>
| |
− | * バグっぽい挙動なのだがよくわからない<br>
| |
− | * 複数のタブに出現させたり、特定のメタデータだけを表示させる、NBTデータを持たせて表示させるなどの細かい制御もここでできる。
| |
− | *
| |
− | * @param tab クリエイティブタブの種類
| |
− | * @param subItems アイテムのリスト
| |
− | */
| |
− | @Override
| |
− | @SideOnly(Side.CLIENT)
| |
− | public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> subItems) {
| |
− | // 表示したいタブと一致しているかの判定メソッド
| |
− | if (this.isInCreativeTab(tab)) {
| |
− | // このアイテムのスタックを作ってリストに追加する。
| |
− | // 今回はメタ0、スタック数は1個とした
| |
− | // このスタックにはNBTを持たせられるので、エンチャントされた状態でタブに登録したりもできる
| |
− | subItems.add(new ItemStack(this, 1, 0));
| |
− | }
| |
− | }
| |
− | | |
− | }
| |
− | </source>
| |
− | | |
− | === crow_face.json ===
| |
− | このアイテムのjsonモデル。<br>
| |
− | jsonモデルは、parent(親モデル)を指定することができる。親モデルの一部を変えたい場合は、変えたい部分のみを記述すればそこだけ置き換わる。<br>
| |
− | | |
− | 下記の例は、"item/generated"(ふつうの平面アイコン)を指定した上で"texture"だけ指定しているので、テクスチャだけ差し替えて、そのほかは親モデルと同じ平面アイコンを使うモデルになる。<br>
| |
− | テクスチャの種類(ここでは"layer0")は、親と同じものを指定することで、指定した種類を差し替えることができる。<br>
| |
− | | |
− | テクスチャはファイルが入っているパスを指定する。<br>
| |
− | この場合、karasuman_item/textures/items/crow_face.pngになる。
| |
− | <source lang = java>
| |
− | {
| |
− | "parent":"item/generated",
| |
− | "textures":{"layer0":"karasuman_item:items/crow_face"}
| |
− | }
| |
− | </source>
| |