この記事では、Minecraft でゴピチャンドの音を鳴らす為に ogg ファイルを読み込むよ。
Minecraft 1.7.10 の記事になります。

Minecraft の Modding の全体像については、こちらのページ をご覧ください。

Gopichand の音源(ogg ファイル)

Minecraft で鳴らす音源を準備する。
ソフトウェアで作ったり、楽器を演奏して録音したり。著者は、実際に自宅にあったゴピチャンドを弾いて録音したよ!

音源は、下記のようなフォーマットの ogg ファイルで用意する。

  • サンプルレート: 44,100 hz
  • ビットレート: 96 kbps VBR
  • ビット深度: 16 Bit
  • チャンネル数: 1(モノラル)

ビットレートは、音源毎に変えてもOK。
チャンネル数は、2(ステレオ)でも良いけど、再生時にモノラルに変換される。容量の無駄なのでモノラルの音源を用意した方が良い。

ogg ファイルを出力する環境がない場合は、一旦、wav ファイルに出力し、下記のような ogg ファイルの出力に対応してる波形編集ソフトに読み込み、ogg ファイルで保存する。

フリーウェアで有名なのが、Audacity
私は、Sound Forge Pro の Version 10 を使ってるよ!
DTM の環境がある人なら DAW から ogg ファイルの出力ができる可能性が高い。

私が用意した ogg ファイルは下記のとおり。

GopichandMod では、プレイヤーの状態によってゴピチャンドの奏法が変わるので、奏法毎の ogg ファイルを用意した。

gopichand_pick.ogg は、ゴピチャンドの弦を普通に指で弾いた音源。プレイヤーが立った状態でゴピチャンドを弾くと鳴る。
gopichand_up.ogg は、ゴピチャンドのネックに力を入れ、弦を緩めた状態から弾いた音源。プレイヤーがスニーク状態(Shift を押しっぱ)でゴピチャンドを弾くと鳴る。
gopichand_tremolo.ogg は、ゴピチャンドのネックに断続的に力を入れた状態で弦を弾いた音源。トレモロ奏法。プレイヤーがジャンプしているとゆうか足が地面についていない状態でゴピチャンドを弾くと鳴る。

音源用のパッケージ

Eclipse で ogg ファイルを登録するパッケージを下記のフォーマットで作成する。

assets.『任意のパッケージの名前』.sounds(.『任意のパッケージの名前』)

ogg ファイルをフォルダ分けしたい場合は、sounds 以降にパッケージを設定できる。
著者の場合は、楽器の音と効果音を分けたかったので、下記のようにした。

assets.gopichandmod.sounds.instruments

音源ファイルをプロジェクトに登録する

先ほど、作成したパッケージに音源を登録する。
“パッケージ・エクスプローラー”のパッケージ assets.gopichandmod.sounds.instruments に ogg ファイルをドラックアンドドロップする。

次にパッケージに ogg ファイルの読み込みの設定を記した json ファイルを作る。
手順は、lang ファイルを作った時と同じ。

“パッケージ・エクスプローラー”のパッケージ上で

右クリック → 新規 → ファイル

の順でクリックすると、“新規ファイル”のダイアログが表示されるので“ファイル名”は、

sounds.json

と入力する。

ファイルは、

assets.gopichandmod.sounds.json

に配置する。

sounds.json のフォーマットは下記のとおり。

{
	"(『任意のパッケージの名前』.)『拡張子を除いた ogg ファイル名 A』: {
		"category": "master",
		"sounds": [
			{
				"name": "(『任意のパッケージの名前』/)『拡張子を除いたoggファイル名 A』",
				"stream": false
			}
		]
	},
	"(『任意のパッケージの名前』.)『拡張子を除いた ogg ファイル名 B』: {
		"category": "master",
		"sounds": [
			{
				"name": "(『任意のパッケージの名前』/)『拡張子を除いたoggファイル名 B』",
				"stream": false }
		]
	},
	"(『任意のパッケージの名前』.)『拡張子を除いた ogg ファイル名 C』: {
		"category": "master",
		"sounds": [
			{
				"name": "(『任意のパッケージの名前』/)『拡張子を除いたoggファイル名 C』",
				"stream": false
			}
		]
	}
}

例として assets.testmod.sounds にある testsound.ogg を設定する場合は、

{
	"testsound": {
		"category": "master",
		"sounds": [
			{
				"name": "testsound",
				"stream": false
			}
		]
	}
}

のようになる。

assets.testmod.sounds.instruments にある testsound.ogg を設定する場合は、

{
	"instruments.testsound": {
		"category": "master",
		"sounds": [
			{
				"name": "instruments/testsound",
				"stream": false
			}
		]
	}
}

のようになる。

上段は、. 区切りで下段は / 区切りになっているので注意。

著者の場合は下記のようになった。

{
	"instruments.gopichand_pick": {
		"category": "master",
		"sounds": [
			{
				"name": "instruments/gopichand_pick,
				"stream": false
			}
		]
	},
	"instruments.gopichand_up": {
		"category": "master",
		"sounds": [
			{
				"name": "instruments/gopichand_up",
				"stream": false
			}
		]
	},
	"instruments.gopichand_tremolo": {
		"category": "master",
		"sounds": [
			{
				"name": "instruments/gopichand_tremolo",
				"stream": false
			}
		]
	}
}

これで Minecraft に ogg ファイルが読み込まれる。

プロジェクトの構成は、下記のようになった。

src/main/java
	com.gopichandmod
		GopichandMod.java
		Recipes.java
	com.watermelonheadman.gopichandmod.items
		GopichandItems.java
		ItemGopichand.java
		ItemGopichandBody.java
		ItemGopichandHead.java
		ItemIronString.java
src/main/resources
	assets.gopichandmod
		sounds.json
	assets.gopichandmod.lang
		en_US.lang
		ja_JP.lang
	assets.testmod.sounds.instruments
		gopichand_pick.ogg
		gopichand_up.ogg
		gopichand_tremolo.ogg
	assets.gopichandmod.textures.items
		gopichand.png
		gopichandbody.png
		gopichandhead.png
		ironstring.png

楽器の音のクラス

音を管理するパッケージを作る。

com.gopichandmod.audio

先ほど作ったパッケージの中に楽器の音のクラスを作る。

package com.watermelonheadman.gopichandmod.audio;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.audio.PositionedSound;
import net.minecraft.util.ResourceLocation;

@SideOnly( Side.CLIENT )
public class InstrumentSound extends PositionedSound
{
	public int entityid;

	public InstrumentSound(
			ResourceLocation resourcelocation,
			int id,
			float x,
			float y,
			float z,
			float vol,
			boolean loop )
	{
		super( resourcelocation );

		this.entityid = id;
		this.volume = vol;
		//protected float field_147663_c = 1.0F;
		this.xPosF = x;
		this.yPosF = y;
		this.zPosF = z;
		this.repeat = loop;
	}
}

このクラスは、場所の情報を持っているクラス PositionedSound を継承する。

メンバー変数の entityid は、音を鳴らしたプレイヤーの ID を記録する。
ゴピチャンドはモノフォニーな楽器なので音が既に鳴っている状態で鳴らした場合、前に鳴らしてた音は止める必要がある。

コンストラクタの引数はそれぞれ下記のとおり。

ResourceLocation resourcelocation → 鳴らす音
int id → 音を鳴らすプレイヤーの ID
float x → 音を鳴らす x 座標
float y → 音を鳴らす y 座標
float z → 音を鳴らす z 座標
float vol → 鳴らす音の音量
boolean loop → 鳴らす音を繰り返すか

楽器の音を管理するクラス

楽器の音を管理するクラスを作る。
このクラスでは、先ほど作ったクラス InstrumentSound を作ったり、プレイヤーがどの音を鳴らしたかの情報を管理する。

package com.gopichandmod.audio;

import java.util.HashMap;
import java.util.Map;

import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.ISound;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
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 InstrumentManager
{
	private static final String header = "instruments.";

	public static final int GOPICHAND_PICK = 0;
	public static final int GOPICHAND_UP = 1;
	public static final int GOPICHAND_TREMOLO = 2;

	public static final int MAX = 256;

	private static String[] name;

	private static Map< Integer, ISound > playermap;

	public static void registry() {
		InstrumentManager.name = new String[ InstrumentManager.MAX ];

		InstrumentManager.name[ 0 ] = "gopichand_pick";
		InstrumentManager.name[ 1 ] = "gopichand_up";
		InstrumentManager.name[ 2 ] = "gopichand_tremolo";

		playermap = new HashMap< Integer, ISound >( 256 );

		return;
	}

	public static String getName( int n )
	{
		return InstrumentManager.header + InstrumentManager.name[ n ];
	}

	@SideOnly( Side.CLIENT )
	public static void play(
		int soundid,
		int entityid,
		float x,
		float y,
		float z,
		float vol,
		boolean loop )
	{
		Minecraft mc = FMLClientHandler.instance().getClient();

		ISound isound = playermap.get( entityid );

		if ( isound != null ) {
			if ( mc.getSoundHandler().isSoundPlaying( isound ) ) {
				mc.getSoundHandler().stopSound( isound );
			}
		}

		InstrumentSound sound = new InstrumentSound(
			new ResourceLocation (
				"gopichandmod",
				InstrumentManager.getName( soundid ) ),
			entityid,
			x,
			y,
			z,
			vol,
			loop );
		mc.getSoundHandler().playSound( sound );

		playermap.put( entityid, sound );
	}
}

とりあえず鳴らしてみる

ゴピチャンドのクラスに音を鳴らす処理を追加する。
この処理はシングルプレイでのみ有効です。
このままでは、他のプレイヤーが鳴らした音は聞こえない。鳴らした本人のみに音が聞こえる。
マルチプレイ対応版は、次回の記事で書きます。

package com.gopichandmod.items;

import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import cpw.mods.fml.common.registry.GameRegistry;
import com.watermelonheadman.gopichandmod.audio.InstrumentManager;

public class ItemGopichand extends Item
{
	public ItemGopichand()
	{
		String name = "gopichand";

		this.setCreativeTab( CreativeTabs.tabMisc );
		this.setUnlocalizedName( name );
		this.setTextureName( "gopichandmod:gopichand" );
		maxStackSize = 1;

		GameRegistry.registerItem( this, name );

		return;
	}

	@Override
	public ItemStack onItemRightClick(
		ItemStack item,
		World world,
		EntityPlayer player )
	{
		player.setItemInUse( item, this.getMaxItemUseDuration( item ) );

		if ( !world.isRemote ) {
			if ( player.onGround == false ) {
				InstrumentManager.play(
						InstrumentManager.GOPICHAND_TREMOLO,
						player.getEntityId(),
						(float)player.posX,
						(float)player.posY,
						(float)player.posZ,
						1.0f,
						false );
			} else if ( player.isSneaking() ) {
				InstrumentManager.play(
						InstrumentManager.GOPICHAND_UP,
						player.getEntityId(),
						(float)player.posX,
						(float)player.posY,
						(float)player.posZ,
						1.0f,
						false );
			} else {
				InstrumentManager.play(
						InstrumentManager.GOPICHAND_PICK,
						player.getEntityId(),
						(float)player.posX,
						(float)player.posY,
						(float)player.posZ,
						1.0f,
						false );
			}
		}

		return item;
	}

	public int getMaxItemUseDuration( ItemStack par1ItemStack )
	{
		return 72000;
	}

	public EnumAction getItemUseAction( ItemStack par1ItemStack )
	{
		return EnumAction.block;
	}
}

プロジェクトの構成は、下記のようになった。

java
	com.watermelonheadman.gopichandmod
		GopichandMod.java
		Recipes.java
	com.watermelonheadman.gopichandmod.audio
		InstrumentManager.java
		InstrumentSound.java
	com.watermelonheadman.gopichandmod.items
		GopichandItems.java
		ItemGopichand.java
		ItemGopichandBody.java
		ItemGopichandHead.java
		ItemIronString.java
resource
	assets.gopichandmod
		sounds.json
	assets.gopichandmod.lang
		en_US.lang
		ja_JP.lang
	assets.testmod.sounds.instruments
		gopichand_pick.ogg
		gopichand_up.ogg
		gopichand_tremolo.ogg
	assets.gopichandmod.textures.items
		gopichand.png
		gopichandbody.png
		gopichandhead.png
		ironstring.png

テストする

ゴピチャンドを手に持って、右クリックする。
ジャンプやスニークした状態で音が変わるか確認する。

音がならない場合は、Eclipse のコンソールに何かしら出力されていると思うので確認する。

ここまででとりあえず音が鳴るようになりましたが、今の状態ではマルチでは対応していません。(自分にしか音が聞こえない)

次の記事でマルチに対応させます。