1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自分の絵をマイクラの花火で打ち上げてみよう!

1
Posted at

概要

  • この記事を拝見し、マイクラでも実装できるのではないかと思いました。
  • GUIの実装が鬼門すぎたのでとりあえず内部的に処理することにします。

とりあえず絵を描く

処理の負荷を考えるとできるだけ小さいのが望ましいです。
Star.png
こちらの画像をassets/examplemod/textures/fireworksのディレクトリ構造を作って格納しましょう。

コード

Entityクラスを作る

FireworkRocketEntityクラスを継承します。

MyArtFireworkEntity.java
package com.example.examplemod.EditHere;

import com.example.examplemod.ExampleMod;
import com.mojang.math.Vector3f;
import net.minecraft.client.Minecraft;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.projectile.FireworkRocketEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.network.NetworkHooks;
import org.jetbrains.annotations.NotNull;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;

public class MyArtFireworkEntity extends FireworkRocketEntity {
  // コンストラクタ1: 登録用
  public MyArtFireworkEntity(EntityType<? extends FireworkRocketEntity> entityType, Level level)
  {
      super(entityType,level);
  }
  // コンストラクタ2: 発射用
  public MyArtFireworkEntity(Level level, double x, double y, double z, ItemStack itemStack)
  {
      super(level, x, y, z, itemStack);
  }
  // コンストラクタ3: プレイヤーが撃つ用
  public MyArtFireworkEntity(Level level, ItemStack itemStack, LivingEntity shooter)
  {
      super(level, itemStack, shooter);
  }

  @Override
  public @NotNull EntityType<?> getType() {
      return ExampleMod.MY_ART_FIREWORK;
  }

  @Override
  public void handleEntityEvent(byte pId) {
      if(this.level.isClientSide)
      {
          System.out.println("DEBUG: イベント受信 ID="+pId);
      }
      // ID 17が花火の爆発イベント
      if(pId==17 && this.level.isClientSide)
      {
          System.out.println("DEBUG: 爆発イベント(17)を検知!自作パーティクルを開始します。");
          // バニラの爆発エフェクトは呼ばずに、
          // 自作のパーティクル生成処理を呼ぶ
          spawnArtParticle();
          return;
      }
      super.handleEntityEvent(pId);
  }
  @OnlyIn(Dist.CLIENT)
  private void spawnArtParticle(){
      try{
          ResourceLocation textureLocation=new ResourceLocation(ExampleMod.MODID, "textures/fireworks/star_firework.png");
          if(!Minecraft.getInstance().getResourceManager().hasResource(textureLocation))
          {
              System.out.println("DEBUG: エラー!画像ファイルが見つかりません: "+textureLocation);
              return;
          }
          Resource resource=Minecraft.getInstance().getResourceManager().getResource(textureLocation);
          InputStream stream=resource.getInputStream();
          BufferedImage image= ImageIO.read(stream);
          System.out.println("画像読み込み完了!");

          int width=image.getWidth();
          int height=image.getHeight();
          double scale=0.2D;

          double startX=this.getX();
          double startY=this.getY();
          double startZ=this.getZ();

          double yawRadians=Math.toRadians(-this.getYRot());
          double cos=Math.cos(yawRadians);
          double sin=Math.sin(yawRadians);

          for(int x=0; x<width; x++){
              for(int y=0; y<height; y++){
                  int color=image.getRGB(x,y);
                  int alpha=(color>>24)&0xff;
                  if(alpha==0){
                      continue;
                  }
                  float r=((color>>16)&0xff)/255.0F;
                  float g=((color>>8)&0xff)/255.0F;
                  float b=(color&0xff)/255.0F;

                  double offsetX=(x-width/2.0F)*scale;
                  double offsetY=(height-y-height/2.0F)*scale;

                  double rotatedX=offsetX*cos;
                  double rotatedZ=offsetX*sin;

                  this.level.addParticle(
                          new DustParticleOptions(new Vector3f(r,g,b),1.0F),
                          startX+rotatedX,
                          startY+offsetY,
                          startZ+rotatedZ,
                          0,0,0
                  );
              }
          }
          stream.close();
      } catch (IOException e) {
          System.out.println("DEBUG: 画像読み込み中に例外が発生しました");
          throw new RuntimeException(e);
      }
  }

  @Override
  public @NotNull Packet<?> getAddEntityPacket() {
      return NetworkHooks.getEntitySpawningPacket(this);
  }
}
  • colorについての補足
    getRGBによって返ってくる値は32ビットの2進数の形で、透明度、赤、緑、青の順にそれぞれ8ビット(1バイト)ずつ数値が割り振られています。
    つまり16進数で表せば0xAA|RR|GG|BBといった形になっています。(それぞれの文字が4ビット分の情報を持つとしています。)
    ここから、論理シフト演算と論理積を用いてそれぞれの個別の情報を取り出していきます。
    例えば赤の場合、右シフト演算で16桁ずらすことで0x00|00|AA|RRという形(0が1つで4ビット分としています)になり、最後に下位8ビットが全て1である16進数0xffと論理積を取ることで、RRだけが取り出されることになります。

Itemクラスを作る

MyArtFireworkItem.java
package com.example.examplemod.EditHere;

import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.*;
import net.minecraft.world.level.Level;

public class MyArtFireworkItem extends Item {
  public MyArtFireworkItem()
  {
      super(new Properties().tab(CreativeModeTab.TAB_MISC));
  }

  @Override
  public InteractionResultHolder<ItemStack> use(Level pLevel, Player pPlayer, InteractionHand pUsedHand) {
      if(!pLevel.isClientSide)
      {
          MyArtFireworkEntity firework=new MyArtFireworkEntity(pLevel, pPlayer.getX(), pPlayer.getEyeY()-0.1F, pPlayer.getZ(), pPlayer.getItemInHand(pUsedHand));
          firework.setOwner(pPlayer);
          firework.setDeltaMovement(0.0F, 0.5F, 0.0F);
          firework.setYRot(pPlayer.getYRot());
          pLevel.addFreshEntity(firework);
      }
      if(!pPlayer.getAbilities().instabuild)
      {
          pPlayer.getItemInHand(pUsedHand).shrink(1);
      }
      pPlayer.awardStat(Stats.ITEM_USED.get(this));
      return InteractionResultHolder.sidedSuccess(pPlayer.getItemInHand(pUsedHand), pLevel.isClientSide());
  }
}

ExampleModで登録

アイテムとエンティティのインスタンス

ExampleMod.java
public static final Item MY_ART_FIREWORK_ITEM=new MyArtFireworkItem().setRegistryName(MODID, "my_art_firework_item");
public static final EntityType<MyArtFireworkEntity> MY_ART_FIREWORK=EntityType.Builder
            .<MyArtFireworkEntity>of(MyArtFireworkEntity::new, MobCategory.MISC)
            .sized(0.25F, 0.25F)
            .setTrackingRange(64)
            .updateInterval(1)
            .setShouldReceiveVelocityUpdates(true)
            .build("my_art_firework");

エンティティの登録

ExampleMod.java
@SubscribeEvent
public static void onEntitiesRegistry(final RegistryEvent.Register<EntityType<?>> event) {
    event.getRegistry().register(MY_ART_FIREWORK.setRegistryName(MODID,"my_art_firework"));
}

アイテムの登録

ExampleMod.java
private static final Item[] registerItems = {
                MY_ART_FIREWORK_ITEM
        };

レンダラーの登録(既存のものを使う)

ExampleMod.java
private void doClientStuff(final FMLClientSetupEvent event) {
    EntityRenderers.register(MY_ART_FIREWORK, FireworkEntityRenderer::new);
}

Dispenserの登録

これをやっておかないとディスペンサーに入れた時に打ち上がりません。

ExampleMod.java
private void doClientStuff(final FMLClientSetupEvent event) {
    DispenserBlock.registerBehavior(MY_ART_FIREWORK_ITEM, new DefaultDispenseItemBehavior(){
            @Override
            protected ItemStack execute(BlockSource pSource, ItemStack pStack) {
                Direction direction=pSource.getBlockState().getValue(DispenserBlock.FACING);

                double x=pSource.x()+(double) direction.getStepX()*0.7D;
                double y= pSource.y()+(double) direction.getStepY()*0.7D;
                double z=pSource.z()+(double) direction.getStepZ()*0.7D;

                MyArtFireworkEntity entity=new MyArtFireworkEntity(pSource.getLevel(), x, y, z, pStack);
                entity.shoot(direction.getStepX(), direction.getStepY(), direction.getStepZ(), 0.5F, 1.0F);
                pSource.getLevel().addFreshEntity(entity);
                pStack.shrink(1);
                return pStack;
            }
        });
}

動画

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?