提供: Minecraft Modding Wiki
目次
パーティクルの追加
溶岩の炎のエフェクトやポータルのエフェクトのような効果を追加するためのチュートリアルです。
Minecraft本体に含まれるエフェクトを発生させる方法と、独自のエフェクトを発生させる方法を解説します。
独自のエフェクトの例としては、Forestry for Minecraftの養蜂で蜂の形のパーティクル等があります。
本チュートリアルはForge 9.10.0.840で確認しました。
煙突ブロックを追加し、煙突ブロックから煙のパーティクルを発生させる例を以下に示します。
AddParticle.java
package addparticle; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.creativetab.CreativeTabs; import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod.EventHandler; import cpw.mods.fml.common.SidedProxy; import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.common.registry.LanguageRegistry; @Mod(modid = "AddParticle", name = "AddParticle") public class AddParticle { @SidedProxy(clientSide = "addparticle.ClientProxy", serverSide = "addparticle.CommonProxy") public static CommonProxy proxy; // 煙突ブロックのブロックID。良い子のみんなは設定ファイルで設定できるようにしましょう。 public static int chimneyBlockID = 4000; // 煙突ブロック。今回追加するパーティクルの発生源として用意したブロック。脇役なのでパーティクル発生以外の機能はありません。 public static Block chimney = new BlockChimney(chimneyBlockID, Material.rock).setUnlocalizedName("addparticle:chimney").func_111022_d("addparticle:chimney").setCreativeTab(CreativeTabs.tabDecorations); @EventHandler public void preInit(FMLPreInitializationEvent event) { GameRegistry.registerBlock(chimney, "addparticle:chimney"); LanguageRegistry.instance().addStringLocalization("addparticle:chimney", "ja_JP", "煙突"); } @EventHandler public void preInit(FMLInitializationEvent event) { proxy.init(); } }
CommonProxy.java
package addparticle; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.world.World; import cpw.mods.fml.common.network.IGuiHandler; public class CommonProxy implements IGuiHandler { @Override public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { return null; } @Override public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) { return null; } public void init() { } }
ClientProxy.java
package addparticle; import net.minecraftforge.common.MinecraftForge; public class ClientProxy extends CommonProxy { @Override public void init() { // イベントをフックしてパーティクル用の画像を登録します MinecraftForge.EVENT_BUS.register(new Particles()); } }
Particles.java
package addparticle; import net.minecraft.client.renderer.texture.IconRegister; import net.minecraft.util.Icon; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.event.ForgeSubscribe; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; /** * パーティクル用の画像登録クラス */ public class Particles { private static Particles instance; // 画像パス。eclipseの場合はforge/mcp/src/minecraft/assets/addparticle/textures/items/smokecircle.pngに展開される // 実環境では%ziproot%/assets/addparticle/textures/items/smokecircle.png String[] iconNames = {"addparticle:smokecircle"}; Icon icons[]; public static Particles getInstance() { if (instance == null) { instance = new Particles(); } return instance; } /** * パーティクル用の画像をまとめて登録(今回はひとつしかないけど) * ブロックやアイテムと異なりEntityFXはregisterIconメソッドがないのでTextureStitchEvent.Preイベントをフックして登録します */ @ForgeSubscribe @SideOnly(Side.CLIENT) public void handleTextureRemap(TextureStitchEvent.Pre event) { if (event.map.textureType == 1) { this.getInstance().registerIcons(event.map); } } @SideOnly(Side.CLIENT) public void registerIcons(IconRegister par1IconRegister) { icons = new Icon[iconNames.length]; for(int i = 0; i < icons.length; ++i) { icons[i] = par1IconRegister.registerIcon(iconNames[i]); } } @SideOnly(Side.CLIENT) public Icon getIcon(String iconName) { for(int i = 0; i < iconNames.length; ++i) { if(iconName.equalsIgnoreCase(iconNames[i])) { return icons[i]; } } return null; } }
BlockChimney.java
package addparticle; import java.util.Random; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.world.World; import cpw.mods.fml.client.FMLClientHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; public class BlockChimney extends Block { public BlockChimney(int par1, Material par2Material) { super(par1, par2Material); } @SideOnly(Side.CLIENT) @Override public void randomDisplayTick(World par1World, int par2, int par3, int par4, Random par5Random) { if(par5Random.nextInt(10) == 0) { // Minecraft本体に含まれるパーティクルを発生させる場合 // 第一引数の文字列はパーティクルの種類です。どのような種類があるかはRenderGlobalクラスのdoSpawnParticleを参照してください。 // par1World.spawnParticle("portal", d0, d1, d2, d3, d4, d5); // 本MODで追加するパーティクルを発生させる場合 // パーティクル発生地点。ブロック上面中心から半径0.8の円周上のランダムな場所 double r = 0.8D + par5Random.nextDouble(); double t = par5Random.nextDouble() * 2 * Math.PI; double d0 = par2 + 0.5D + r * Math.sin(t); double d1 = par3 + 1.0D + par5Random.nextDouble(); double d2 = par4 + 0.5D + r * Math.cos(t); // パーティクルの移動速度。+0.03Dで上昇する double d3 = Math.sin(t) / 64.0D; double d4 = 0.03D; double d5 = Math.cos(t) / 64.0D; EntitySmokeCircleFX entityFX = new EntitySmokeCircleFX(par1World, d0, d1, d2, d3, d4, d5); // パーティクルの画像を設定。画像は各自で用意してください entityFX.func_110125_a(Particles.getInstance().getIcon("addparticle:smokecircle")); FMLClientHandler.instance().getClient().effectRenderer.addEffect(entityFX); } } }
EntitySmokeCircleFX.java
package addparticle; import net.minecraft.client.particle.EntityFX; import net.minecraft.world.World; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) public class EntitySmokeCircleFX extends EntityFX { // 発生地点のY座標。消滅条件に利用する double orginalPosY; public EntitySmokeCircleFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12) { super(par1World, par2, par4, par6); this.orginalPosY = par4; this.motionX = par8; this.motionY = par10; this.motionZ = par12; } /* * tickごとの更新。スーパークラスであるEntityFXでは放射状に拡散してparticleMaxAge経過で消滅しますが、 * ここでは発生地点から1m上昇したら消滅するように変更しています。 * 作ろうとしているパーティクルに適した動きを実装してください。 * 例えばポータルエフェクトのパーティクルはポータルブロックに吸い込まれるような動きを実装しています。 */ @Override public void onUpdate() { this.prevPosX = this.posX; this.prevPosY = this.posY; this.prevPosZ = this.posZ; // 時間経過とともに大きくなる this.particleScale = this.particleScale + 0.1F; // 発生地点から1m以上上昇したら消滅する if (this.posY > orginalPosY + 1.0D && this.particleAge++ >= this.particleMaxAge) { this.setDead(); } this.moveEntity(this.motionX, this.motionY, this.motionZ); } @Override public int getFXLayer() { // たぶん数値が大きいほど手前に描画される? // 1or2でないと例外が発生するのでとりあえず2に設定 return 2; } }
補足
注意すべき点としては、EntityFXは他のEntityと異なりクライアントサイドのみに存在する点が挙げられる。(描画のみのため)
そのため、他のEntityと異なりサーバーサイドには存在せず、EntityRegistryに登録する必要もない。