疲れたらやすむ

Javaを学ぶ上でハマったところを書いていきます。iPhoneアプリ開発や日常ネタもあるかも。

【Xcode】Failed to create provisioning profileの解決方法

今回はXcodeのエラーの原因と解決方法についてです。

事象

実際のエラーの状態はこんな感じです。

f:id:chibiCat:20190616175302p:plain

Failed to create provisioning profile

No profiles for 'Bundle Identifier' were found

の2つのエラーが表示されます。

このエラーが出ていると、多分ですが実機テストが出来ません。
問答無用でBuild Failedとなってしまいます。
シミュレータではビルド可能です。

原因

私の場合は、問題なく動いていたプロジェクトを削除して、再度同じ名前のプロジェクトを作成した場合にエラーが出ました。

どうやらBundle Identifierが重複しているために怒られているそうです。
物理的にプロジェクトを削除しただけではダメらしい。

そもそもBundle Identifierとは、アプリを区別出来る一意の値です。
そのため、AppStoreで公開する時に被るのは当然、ローカルでも被ってしまうことは許されないのです。

この辺りの仕様が正直よくわかりません。
一意のIDが重複してはいけないのは理解出来るのですが、そもそもプロジェクトを削除しているので平気のはず!と思ってしまいます。

解決方法

簡単に解決するなら、別名のプロジェクトを作成するのが早そうです。

結局のところ調べてもわかりませんでした。
作成したプロジェクトを消すことは想定されていないのかもしれません。

一応Derived Dataのプロジェクト削除も行いましたが効果なし。
方法は、「Xcode」→「Preferences...」の「Locations」タブを開き、Derived Dataの項目のパス内にあるプロジェクトのデータを削除。

f:id:chibiCat:20190616234711p:plain

パスの右側の矢印マークをクリックするとFinderで開いてくれると書いてありましたが私は開きませんでした。
なので、Finder上でCommand+Shift+Gでパスを貼り付けエンターで開きました。

フォルダの中身は自分で作ったプロジェクト名から始まるフォルダと謎のフォルダが2つくらい。
自作プロジェクト名が頭に付いたフォルダだけ削除してXcodeを再起動させましたが変化なしでした。


にしても困る事象ですよね。
プロジェクト削除してるのに重複扱いとは。

何かわかり次第更新します。


【2019年6月22日追記】
後日、再度Xcodeを立ち上げてみると何故かエラーが消えていました。
何ごともなくビルドも通り実機テストも出来ました。

なんでしょう。
やったことは
・キーチェーンの整理(期限切れのものを削除)
・Derived Dataの削除
・実機のiPhone側のデベロッパAppの登録解除
ぐらいですかね。
意外とOSの再起動とかが必要だった可能性もありますが。

ちなみに、再現しようとしてプロジェクトを削除後に同名のプロジェクトを作成してもエラーは出ませんでした。
なぞい。

【Java】ジェネリクスについて

今回はジェネリクスに関する記事になります。

クラスやメソッド宣言に、<T>などと記述されていることがあります。
それがジェネリクスと呼ばれるものです。

ジェネリクスとは

Javaにおいて、ジェネリクスとは「型の安全性を高め、さらに汎用性を持たせる」ことが出来る仕組みです。
普段よく使用するものでは、ListやMapはジェネリクスを使用しています。

自作のジェネリクスクラスは以下の様に定義します。

GenericsSample.java

public class GenericsSample<T> {

	private T t;

	public GenericsSample(T t) {
		this.t = t;
	}

	public T getT() {
		return this.t;
	}
}

これがジェネリクスを使用したクラスで、総称型と呼ばれるクラスになります。
ジェネリクスはクラスやインターフェース、メソッドなどに記述出来ます。

クラス名の右側に<T>と記述されていますが、これを型引数と言います。
<>内の文字は何でもOKですが基本的には以下の通り。

変数名 指針
E コレクションに格納される要素
K, V キーと値
T 上記以外の一般的な要素
R 戻り値

このGenericsSampleを実際に使用する場合はこんな感じになります。

Main.java

public class Main {
	public static void main(String[] args) {

		GenericsSample<String> generics = new GenericsSample<String>("Hello");
		System.out.println(generics.getT());
	}
}

実行結果

Hello

使用には総称型の型引数<T>の部分をパラメータ化する必要があり、今回はString型でパラメータ化しています。
パラメータ化は、後に触れる境界を指定していない場合はどんな型でもOKです。
StringやInteger、独自クラスでもなんでも。
ListやMapなどでも型を指定すると思いますが、それと同じです。

今回のGenericsSampleのソースでは、T型のフィールドtをprivateで宣言しています。
この時点ではTが何型であるのか決まっていません。
ただし実際にはパラメータ化(型の指定)をする必要があるため、実行後は型が決まります。


ジェネリクスをメソッドで使用する場合は以下の様に記述します。

public <T> void doSomething() {
	T t;
}

安全性と汎用性

ジェネリクスを使用することで、予想外の例外を未然に防げたりクラスやメソッドなどの汎用性を高めることが可能です。

まずは安全性について。

少し極端な例ですが、下記のコードがあったとします。

List list = new ArrayList<>();
list.add("Hello");
list.add(new Integer(1));

for (Object o : list) {
	String s = (String) o;
}

Listは総称型であるため、基本的に使用にはそのListは何の型を扱うのかをパラメータ化することが推奨されています。
普段であれば、Listを定義する際にそのListが何型なのかを<>の中に記述するはずです。
しかし記述しない場合でもコンパイルエラーとはならず実行出来てしまいます。

その結果、意図せず複数の型の値が格納されたListが完成してしまいました。

ではfor文で取り出してみましょう。
String型の値しか入っていないと思っていたListの中にInteger型の値の混ざっていました。
その結果、実行時にClassCastExceptionが発生。

つまり、パラメータ化しないと実行するまでバグであることがわからないのです。


そしてパラメータ化した場合。

List<String> list = new ArrayList<String>();
list.add("Hello");
list.add(new Integer(1));

Integer型の値をaddした時点でコンパイルエラーとなります。
バグを未然に防いでくれ、これが型の安全性を高めます。


そして汎用性とは、例えばListでは複数の型でパラメータ化することにより様々な型のListが定義出来ます。

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

ジェネリクスを使用しない場合、String型やInteger型用のListを定義する必要があり、非常に冗長ですよね。
あまり気にしないで普段使用していますが、意外と身近にジェネリクスの便利な部分が転がっています。

制約がある

ジェネリクスを使用したクラスにはいくつか制約があります。

予めお伝えしておくと、以下のコードは各箇所でコンパイルエラーとなります。

GenericsSample.java

public class GenericsSample<T> {

	static T t1; // 1.

	T t2 = new T(); // 2.

	T[] tArray = new T[1]; // 3.

	public void doSomething() {
		Class<T> cls = T.class; // 4

		T t3;
		boolean b = t3 instanceof T); // 5
	}
}

1.static修飾子の型変数は定義出来ない

2.型変数のインスタンス生成は出来ない

3.型変数が要素の配列は生成出来ない

4.型変数のclass参照は出来ない

5.instanceofによる型の判定は出来ない

型境界

ジェネリクスには型の境界を設けることが可能です。
型境界を指定すると、パラメータ化する型を限定することが出来ます。
たぶん初見でジェネリクスに苦手意識を持ってしまうのはこのあたりのせいだと思います。

この内容では継承が絡んできます。
まずは例として継承関係にあるクラスを作成します。

A.java

public class A {

}

B.java

public class B extends A {

}

C.java

public class C extends B {

}

これらのクラスの関係としては、Aが親でBが子、さらにBが親のCが子となります。

この継承関係で、型境界をBに指定してみます。
型境界は、<>内の型名の後ろにextendsを付けて境界にする型を記述します。

GenericsSample.java

public class GenericsSample<T extends B> {
	
}

このジェネリクスのクラスを実際に使用してみます。

Main.java

public class Main {
	public static void main(String[] args) {

		// コンパイルエラー
		GenericsSample<A> genericsA = new GenericsSample<A>();

		// OK
		GenericsSample<B> genericsB = new GenericsSample<B>();

		// OK
		GenericsSample<C> genericsC = new GenericsSample<C>();

		// コンパイルエラー
		GenericsSample<String> genericsString = new GenericsSample<String>();
	}
}

今回は境界にBを指定しているため、Bとして扱える型であれば許容されます。
つまり、境界に指定した型とis-a関係であれば良いということになります。

AはBを子に持つがBではないためコンパイルエラー。
BはBであるためOK。
Cは親にBを持ち、Bとして扱うことが可能であるためOK。
StringはBとは無関係のためコンパイルエラー。

ワイルドカードと上下限の境界

ジェネリクスをパラメータ化する際に、ワイルドカード(<?>)を使用することが出来ます。
ワイルドカードとは、何らかの型が指定されることを意味しており、つまりどんな型でも許容します。

GenericsSample.java

public class GenericsSample<T> {

}

Main.java

public class Main {
	public static void main(String[] args) {

		// 変数宣言でのワイルドカード(下限境界)
		GenericsSample<? super B> generics = new GenericsSample<>();
	}

	// メソッドの引数でのワイルドカード
	public void doSomething(GenericsSample<?> generics) {

	}
}

この様に、ジェネリクスクラスの変数宣言にワイルドカードを使用します。

<?>を使用した場合は型が決まっておらず、言わばObject型のような感覚です。
実際、<?>は<? extends Object>を省略したものとされています。

そして、型境界でも出てきたextendsと、もう1つsuperを使用し境界を指定出来ます。
superはextendsの逆という解釈でOKです。

先ほどの型境界で登場したA、B、Cそれぞれのクラスが存在した場合。

Main.java

public class Main {
	public static void main(String[] args) {

		// OK
		doSomething(new GenericsSample<A>());

		// OK
		doSomething(new GenericsSample<B>());

		// コンパイルエラー
		doSomething(new GenericsSample<C>());
	}

	static public void doSomething(GenericsSample<? super B> generics) {

	}
}

Bか、Bのスーパータイプを許容するのが<? super B>です。

ワイルドカードやsuperは、型引数には使用出来ないので混同しない様に注意が必要です。

Java Goldやら基本情報やらに手を出す(予定)

みなさん資格は取っていますでしょうか。
私はようやく興味が出始めてきました。

なぜ今まで資格に興味が無かったか。

資格取るのってお金も労力も必要じゃないですか。

嫌でした。本当に。
しかも今まで長時間勉強したこともないです。

で、そんな人がなぜ資格に興味を持ったかです。


ずばり転職制度のふたつの要素です。


業種にもよりますが、ITの分野で言えば今は転職にもってこいの時期なんじゃないでしょうか。
人手が足りている会社はあまり見かけないです。

「出来るエンジニアだけが足りていないんじゃないの?」

と思いますが、実は新米でも重宝されています。
とにかく少しでもプログラム触れる人だったり資料作れる人だったり。
時代はどんどんIT化が進んでいますもんね。

そんな中、私も今年始めに転職しました。
私自身、エンジニアとしてはまだ2年にも満たないほどのペーペーです。

その時に某転職サイトを利用したのですが、実はこの業界に入る時も使っていました。
それが、少しでも実務経験や資格を持っていると結構企業からスカウトが来るんです。

スカウトとは、「ぜひ面接に来てほしい」という企業側からの意思表示みたいなものです。
100%内定がもらえるわけではありませんが、スカウトを受けただけで書類選考免除だったり1次面接免除だったりと嬉しい特典があります。

転職サイトに資格として基本情報があれば、さらに多くの企業の目に留まるのではないでしょうか。
キャリアアップの幅が広がります!


そして制度というのは、会社の福利厚生にあたる部分です。
例えば資格を持っているだけで給料が上がったり、資格の受験費用を負担してくれたり。

以前の会社は給料は上がらなかったのですが、一応負担はしてくれました。
ただし合格した場合のみ料金負担。
落ちる前提で受ける人はいないとは思いますが、とは言え万が一落ちて自己負担は嫌ですよね。

ところが、今の会社は資格に応じて給料アップ、受験料負担(落ちても)、受験勉強用の教材の購入費用負担という神サービスです。
これはもう資格を取らない意味はないです。


そんな動機で良いんかいと自分で突っ込みたくなりますが、今のところとてもモチベーション上がっています。
年内Java Gold。
翌年に基本情報を考えています。

書籍、悩みますね。
約1年前にJava Silverを取得しましたが、その時はそこまでがっつり勉強しませんでした。
しかしGoldはそうもいかないと思います。
最低でも50時間は勉強時間として確保しないと。

うーん。
頑張ります。

【Java】【Minecraft】Mod作成基礎編

今回解説するのは以下の6点です。
・ブロックとアイテムの追加
・レシピの追加
・テクスチャの設定
・クリエイティブタブの追加
・日本語表記対応
・jarの生成

たくさんありますがMod作成としては初歩的な部分だと思いますので1つの記事にまとめます。
Minecraftのバージョンは1.7.10で進めていきます。
開発環境構築については以下の記事を参考にしてください。

【Java】【Minecraft】Modの開発環境構築

今の所は1.7.10以外のバージョンではModを作成を行う予定はありません。
ですが、1.12.2もModの数が豊富なので気持ちが揺らいでいる部分はあります。

完成イメージ

新規ブロックの追加。

f:id:chibiCat:20190510172034p:plain

レシピの追加。
既存ブロックのレシピも追加可能です。
例えば土からダイヤモンドをクラフトするレシピも追加できます。

f:id:chibiCat:20190510172031p:plain

そしてクリエイティブタブの追加と和名表示。

f:id:chibiCat:20190510172028p:plain

ソース

パッケージ構成は以下のようになっています。

f:id:chibiCat:20190510175733p:plain

ここまで分ける必要があるか怪しいですが。
後々ブロックやアイテムの数が増えることを想定すると妥当かなと思っています。

以降は順番にソースの紹介です。

Mainのクラスです。
このクラスがModの核心になります。
クラス宣言の@ModのアノテーションによりModであることを宣言します。必須です。

そしてpreInitとinitの2つのメソッドがあります。
どちらも@EventHandlerのアノテーションを記述しています。これも必須。
文字通りではありますが、初期化処理です。
Modはまず、FMLPreInitializationEventを受け処理を行います。
次にFMLInitializationEventを受け処理を行います。
ブロックの追加やレシピの追加の順序は正しく行う必要があり、不正な順序である場合正しく動作しません。

SampleMod.java

package samplemod.main;

import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import net.minecraft.creativetab.CreativeTabs;
import samplemod.block.BlockManager;
import samplemod.creativetab.SampleModTab;
import samplemod.item.ItemManager;
import samplemod.recipe.RecipeManager;

@Mod(modid = SampleMod.MODID, name = SampleMod.MODNAME, version = SampleMod.VERSION)
public class SampleMod {

	public static final String MODID = "sampleMod";
	public static final String MODNAME = "SampleMod";
	public static final String VERSION = "[1.7.10]1.0.0";

	//クリエイティブタブ追加
	public static final CreativeTabs SAMPLEMOD_TAB = new SampleModTab();


	// 一番最初に実行される処理
	@EventHandler
	public void preInit(FMLPreInitializationEvent event) {
		// ブロックの登録
		BlockManager.registerBlock();
		// アイテムの登録
		ItemManager.registerItem();
	}

	// preInitの次に実行される処理
	@EventHandler
	public void init(FMLInitializationEvent event) {
		// レシピの登録
		RecipeManager.registerRecipe();
	}
}

ブロックの管理クラス。

BlockManager.java

package samplemod.block;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.block.Block;
import samplemod.block.blocks.SampleBlock;
import samplemod.main.SampleMod;

public class BlockManager {

	public static Block sampleBlock;

	public static void registerBlock() {
		// SampleBlockを生成し登録する
		sampleBlock = new SampleBlock()
				.setBlockName("sampleBlock")
				.setCreativeTab(SampleMod.SAMPLEMOD_TAB)
				.setBlockTextureName("samplemod:sampleBlock");
		GameRegistry.registerBlock(sampleBlock, "sampleBlock");
	}
}

追加するブロッククラス

SampleBlock.java

package samplemod.block.blocks;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import samplemod.main.SampleMod;

public class SampleBlock extends Block {

	public SampleBlock() {
		super(Material.rock);
		this.setHardness(1.5F);
		this.setResistance(10.0F);
		this.setStepSound(soundTypePiston);
	}
}

アイテムの管理クラス

ItemManager.java

package samplemod.item;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.item.Item;
import samplemod.item.items.SampleItem;
import samplemod.main.SampleMod;

public class ItemManager {

	public static Item sampleItem;

	public static void registerItem() {
		// SampleItemを生成し登録する
		sampleItem = new SampleItem()
				.setUnlocalizedName("sampleItem")
				.setCreativeTab(SampleMod.SAMPLEMOD_TAB)
				.setTextureName("samplemod:sampleItem");
		GameRegistry.registerItem(sampleItem, "sampleItem");
	}
}

追加するアイテムクラス

SampleItem.java

package samplemod.item.items;

import net.minecraft.item.Item;
import samplemod.main.SampleMod;

public class SampleItem extends Item {

	public SampleItem() {
		super();
	}
}

レシピの管理クラス

RecipeManager.java

package samplemod.recipe;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import samplemod.block.BlockManager;
import samplemod.item.ItemManager;

public class RecipeManager {

	public static final BlockManager BLOCK = new BlockManager();
	public static final ItemManager ITEM = new ItemManager();

	public static void registerRecipe() {
		// SampleBlockのレシピ追加
		GameRegistry.addRecipe(new ItemStack(BLOCK.sampleBlock, 1),
				" A ",
				"ABA",
				" A ",
				'A', ITEM.sampleItem,
				'B', new ItemStack(Items.dye, 1, 1));

		// SampleItemのレシピ追加
		GameRegistry.addRecipe(new ItemStack(ITEM.sampleItem, 2),
				"A",
				"B",
				'A', new ItemStack(Items.dye, 1, 12),
				'B', Blocks.iron_block);
	}
}

追加するクリエイティブタブクラス

SampleModTab.java

package samplemod.creativetab;

import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import samplemod.block.BlockManager;

public class SampleModTab extends CreativeTabs {

	public SampleModTab() {
		super("sampleMod");
	}

	@Override
	public ItemStack getIconItemStack() {
		return new ItemStack(BlockManager.sampleBlock);
	}

	@Override
	public Item getTabIconItem() {
		return null;
	}
}

以上がソースです。
あとは日本語表記に対応させるためのlangファイルが必要です。

ja_JP.lang

// ブロック和名
tile.sampleBlock.name=サンプルブロック

// アイテム和名
item.sampleItem.name=サンプルアイテム

// クリエイティブタブ和名
itemGroup.sampleMod=サンプルモッド

テクスチャ用の画像は6x16pxや32x32px等のpngが必要になります。
もしテクスチャを指定しなかった場合は黒と紫(ピンク)のテクスチャになります。
詳しくは後述します。

ブロックの追加

ブロックの追加はBlockManagerクラスで管理しています。
registerBlockメソッドで追加したいブロックを生成しGameRegistryクラスで登録します。
このメソッドをSampleModクラスのpreInitメソッドで呼び出します。

public static void registerBlock() {
	// SampleBlockを生成し登録する
	sampleBlock = new SampleBlock()
			.setBlockName("sampleBlock")
			.setCreativeTab(SampleMod.SAMPLEMOD_TAB)
			.setBlockTextureName("samplemod:sampleBlock");
	GameRegistry.registerBlock(sampleBlock, "sampleBlock");
}

setBlockName
Stringの引数を渡しブロックの名前を決めます。
sampleBlockを渡した場合、ブロック名は「tile.sampleBlock.name」になります。

setCreativeTab
CreativeTabsを引数に渡し登録先のクリエイティブタブを指定します。
SampleMod.SAMPLEMOD_TABを指定し今回追加するMod用のクリエイティブタブに登録します。
既存のクリエイティブタブに登録する場合はCreativeTabsクラスのメンバ変数を参照してください。
例えばCreativeTabs.tabBlockを指定することで建築ブロックタブに追加できます。

setBlockTextureName
Stringの引数を渡しブロックのテクスチャ名をドメイン名:テクスチャ画像名で指定します。
ドメイン名を指定しなかった場合は自動的にドメイン名が「minecraft」になります。
基本的にはドメイン名はModの名前を半角小文字で指定すれば良いと思います。
今回はModの名前がSampleModなのでドメイン名はsampleMod、テクスチャ画像名をsampleModにしています。


そして生成しているSampleBlockクラスはBlockクラスを継承しています。
コンストラクタを呼んでnewします。

public SampleBlock() {
	super(Material.rock);
}

super
親クラスであるBlockのコンストラクタです。
Materialを引数に渡しブロックの素材を指定します。
何のことかいまいちわかりづらいですが、簡単に言えばブロックの硬さや回収に必要な道具等が決められます。
例えばMaterial.woodを指定すると素手でブロックを破壊し回収できます。
今回指定しているMaterial.rockではピッケルを使わないと回収できません。
ですが、setHardnessメソッドで硬さを指定できますし、setHarvestLevelメソッドで回収に必要な道具も指定できます。
そのためMaterialはそこまで重要ではない気がしています。

アイテムの追加

基本はブロックの追加と同様です。
アイテムの追加はItemManagerクラスで管理しています。
registerBlockメソッドで追加したいブロックを生成しGameRegistryクラスで登録します。
呼び出し元はSampleModクラスのpreInitメソッドです。

public static void registerItem() {
	// SampleItemを生成し登録する
	sampleItem = new SampleItem()
			.setUnlocalizedName("sampleItem")
			.setCreativeTab(SampleMod.SAMPLEMOD_TAB)
			.setTextureName("samplemod:sampleItem");
	GameRegistry.registerItem(sampleItem, "sampleItem");
}

setUnlocalizedName
Stringの引数を渡しアイテムの名前を決めます。
sampleItemを渡した場合、アイテム名は「item.sampleBlock.name」になります。

setCreativeTab
ブロックと同様、CreativeTabsを引数に渡し登録先のクリエイティブタブを指定します。

setBlockTextureName
ブロックと同様、Stringの引数を渡しブロックのテクスチャを「ドメイン名:テクスチャ画像名」で指定します。

生成しているSampleItemはItemクラスを継承したクラスです。
同じくnewしてコンストラクタを呼びます。

public SampleItem() {
	super();
}

アイテムの場合はMaterialを指定する必要はありません。

レシピの追加

レシピの追加はRecipeManagerクラスで管理しています。
メンバ変数に追加するブロックやアイテムのクラスを定義しておきます。

public static final BlockManager BLOCK = new BlockManager();
public static final ItemManager ITEM = new ItemManager();

そしてregisterRecipeメソッドでレシピを追加し、GameRegistryクラスのaddRecipeメソッドで登録します。
SampleModクラスのinitメソッドで呼び出します。
Initで呼び出す理由はブロックやアイテムの追加済みの状態で処理するためです。

public static void registerRecipe() {
	// SampleBlockのレシピ追加
	GameRegistry.addRecipe(new ItemStack(BLOCK.sampleBlock, 1),
			" A ",
			"ABA",
			" A ",
			'A', ITEM.sampleItem,
			'B', new ItemStack(Items.dye, 1, 1));

	// SampleItemのレシピ追加
	GameRegistry.addRecipe(new ItemStack(ITEM.sampleItem, 2),
			"A",
			"B",
			'A', new ItemStack(Items.dye, 1, 12),
			'B', Blocks.iron_block);
}

addRecipe
第1引数にItemStack、第2引数にObject型の可変長を指定します。
具体的には、第1引数であるItemStackとは作成するブロックやアイテムです。
そして第2引数以降には以下の順番で指定します。
1.String型のクラフトの配置の仕方を表すもの
2.Char型のクラフトの配置を表す記号
3.Block型やItem型、ItemStack型の記号に紐付かせるブロックやアイテム

例えば上記のソースのSampleBlockの例の場合。
まず第1引数にはSampleBlockをItemStackでnewしています。
SampleBlockを1つ作るレシピだと言うことを示しています。

続いてStirng型で第2引数に" A "、第3引数に"ABA"、第4引数に" A "を指定しています。
クラフトをする場合、作業台を使って3x3のマス目にブロックやアイテムを入れると思います。
まさにそれです。
任意の文字や空白を使用しどのマスにどんなブロックやアイテムを配置するかを決めます。
ここでは同じ文字は同じブロックやアイテムとして扱われます。

そして第5引数にChar型の'A'、第6引数にItem型の今回追加するアイテムであるSampleItemを指定しています。
これはセットとして考え、「Aが表すアイテムはSampleItem」であることを定義しています。
同様に'B'が赤色の染料であることも定義します。
染料や羊毛の場合、引数によって色を判別します。

new ItemStack(Items.dye, 1, 1)

の3つ目の1が赤色を表す引数です。
SampleItemのレシピの

new ItemStack(Items.dye, 1, 12)

は水色の染料です。

そしてString型で表す配置の仕方ですが、3x3マスすべてを表す必要はありません。
SampleItemの例の様に指定した場合、場所を問わず縦に'A'と'B'を並べれば作成可能となります。

注意としては、ぱっと見Char型で指定する'A'や'B'をString型で"A"や"B"と指定しないことです。
コンパイルエラーは出ませんがClassCastExceptionが発生します。
必ずChar型で指定しましょう。

テクスチャの設定

追加したブロックやアイテムに対しテクスチャを設定します。
テクスチャを指定しない場合では透明ではなく、黒と紫(ピンク)のチェック柄のようなテクスチャになります。
面倒ではないのでテクスチャは設定することをおすすめします。

まずはブロックやアイテム時に設定したテクスチャ名を確認します。
例えばSampleBlockのテクスチャ名はsamplemod:sampleBlockです。
この場合ドメイン名はsamplemodです。
テクスチャ画像を配置する場所は「src/main/resources/assets/ドメイン名/textures」内に作成する「blocks」または「items」フォルダ内です。
そしてテクスチャ画像名はsampleBlockです。
sampleBlock.pngが紐付く画像になります。
つまり、sampleBlockのテクスチャの場合は「src/main/resources/assets/samplemod/textures/blocks」に「sampleMod.png」を入れることで指定できます。

テクスチャ画像のサイズは16x16pxや32x32pxなどにします。
試していませんが64x64px以上もOKのはず。

クリエイティブタブの追加

クリエイティブタブの追加はCreativeTabsクラスを継承したSampleModTabクラスで行います。

public SampleModTab() {
	super("sampleMod");
}

@Override
public ItemStack getIconItemStack() {
	return new ItemStack(BlockManager.sampleBlock);
}

@Override
public Item getTabIconItem() {
	return null;
}

SampleModTab
コンストラクタ。
Stringを引数に親クラスのCreativeTabsのコンストラクタを呼び出しクリエイティブタブの名前を決めます。
sampleModを渡した場合、クリエイティブタブ名は「itemGroup.sampleMod」になります。

getIconItemStack
CreativeTabsクラスを継承した際に実装する必要があるメソッド。
クリエイティブタブのアイコンを設定できます。
戻り値にアイコンにしたいブロックやアイテムをItemStack型で指定します。

getTabIconItem
CreativeTabsクラスを継承した際に実装する必要があるメソッド。
恐らくクリエイティブタブのアイコンを設定できます。
getIconItemStackメソッドで設定済みなのでnullを返しておきます。

そしてSampleModクラスのメンバ変数にSampleModTabクラスをnewします。

//クリエイティブタブ追加
public static final CreativeTabs SAMPLEMOD_TAB = new SampleModTab();

ブロックやアイテム追加時にsetCreativeTabメソッドの引数にSampleMod.SAMPLEMOD_TABを指定することで追加したクリエイティブタブに登録されます。

日本語表記対応

テクスチャの設定と似ており、和名を定義したファイルを追加することで日本語表記に対応します。
「ja_JP.lang」ファイルを「src/main/resources/assets/samplemod/lang」に配置します。
内容はソースに記載した通りです。
ブロック名と和名を紐付かせます。
SampleBlockの場合は

// ブロック和名
tile.sampleBlock.name=サンプルブロック

の様に記述します。
コメントはjavaの書き方をしなくても問題ありません。
個人的にそうしたかっただけです。

メモ帳などで作成し、ファイル形式をlangに変更しましょう。

jarの生成

通常Modはjar形式になっており、modsフォルダに入れることでModとして動作します。

まずはコマンドラインでワークスペースの1つ上のフォルダまで行きます。
名前を変更していなければ「forge-1.7.10〜src」のフォルダです。binフォルダやbuildフォルダがある所ですね。
Windowsの場合はエクスプローラーで該当フォルダを開き、ウインドウのアドレスバーに「cmd」を入力しENTER。
Macの場合はターミナルを開き「cd 」と入力後、該当フォルダをターミナルにドラッグ&ドロップでENTER。

次にコマンドラインで以下を入力しENTER。
Windowsの場合は「gradlew build」。
Macの場合は「./gradlew build」です。
ビルドに成功した場合は「BUILD SUCCESSFUL」と表示されます。

ビルド後、binフォルダ内にあるlibsフォルダにjarが生成されています。
生成時の名前は「modid-1.0.jar」となっているので手動で変更します。
生成したjarを使って実際にプレイしてみましょう。

今回は以上になります。

2年半ぶりに家族に会って感じたこと

先日、ゴールデンウィークということで家族が東京に来ており久々に会いました。
私が実家を出て早6年。
最初は少なくとも1年ペースで実家に帰って顔を合わせていたのですが、ここ最近はめっきり帰っていませんでした。
連絡も基本的に取りませんし、本当に久しぶりでした。

結果から言うと会ってよかったです。
その感情に至るまでの経緯を順を追って書いていきます。

そもそも私はこんな人生を歩んできました

私は地方で生まれ、小学校から高校、そして社会人デビューも地元で果たしました。
家族は両親と妹と私の4人家族です。
実家にいる時の私は、それはもう反抗期真っ盛りでろくに親とも口を利かず、親からしてみれば可愛げのない息子だったと思います。
内心は照れくささや恥ずかしさがあって話をしたくないような感情。

父とはとくに不仲で、最後に笑いあったのはいつなのかすら覚えていません。
私が不仲と思っていただけで父がどう思っていたのかはわかりませんが。
少なくとも自慢の息子だとは思っていなかったでしょう。
なんせ私は話もせず意見も言わず、言いつけも守りませんでしたからね。

そんな父ですが、病気を患っていました。
反抗期だった私は病名も気にせず、聞こうともしませんでした。
後から知ることになるのですが、慢性血栓閉塞性肺高血圧症という難病です。
辛そうにしている時もありましたが家族に弱音を吐いたりはしませんでした。
もしかしたら私の耳に入らなかっただけなのかもしれませんけど。
定期的に通院はしているようでしたが、入院はしておらずふつうに仕事もしていました。
寝るときは生命維持装置っていうんですかね。呼吸の補助をするような装置を使っていました。


そして社会人2年目、私は上京を決意します。
お世辞にも居心地が良いとは言えない実家から外に出たいという気持ちが強かったからです。
両親にそれを伝えても反対されませんでした。
ただ、父にこんなことを言われました。
「俺はもういつ亡くなってもおかしくない状態だから、もしそうなった時は母さんと妹が残される。」
それに対し私は
「亡くならないでしょ。」
と言いました。
それを聞いて父は少し笑っていたと思います。

上京前日、家族全員で居酒屋に行ってお別れ会をしました。
家族が集まって外食をすること自体が珍しかったですし、その時初めて父とお酒を飲みました。


東京に引っ越して半年ほどでしょうか。
出勤前に母親から着信。
電話自体が珍しい上にこの時間だったので嫌な予感。
「お父さんが亡くなった。」と涙声で母が言いました。
何となく察してはいましたが。

急いで地元に帰ると、既に親戚の方々が集まっていました。
しばらく実家を離れていて父と会っていなかったこともあり、亡くなった事に正直あまり実感が湧きません。
でも火葬の直前。
最後のお別れで顔のそばに花を添える際に、父の冷めきった額を触って涙が出ました。
もう父は生きていないんだという事実。
どちらかと言えば嫌いだった人でも、いざ亡くなると悲しいんだなあと。
もはや嫌いという感情が嘘のように思います。

母も父とはよく言い合いの喧嘩をしており、子ども目線でも仲の悪い夫婦でした。
それでも母も悲しかったでしょう。
そして何より悲しかったのは妹だと思います。
妹は父と仲が良く、よく遊んだり話したりしていました。

私はその一件を境に、母や妹と話をするようになりました。
家族の大切さをようやく知ったんだと思います。
でも代償は大きい。自分自身が情けないです。


それからというもの、人生はどんどん前に進むのですがうまくはいきません。
離婚をしたり、会社を辞めたり。
母からお金を借りることもありました。

今まではそういった話は人から聞いたりテレビで見たりする側でしたが、気づけば自分の身に起きていました。
実際にそんな状況になってみると、結構つらいです。
今までこうやって生きてこれたのも周りに支えてくれる人がいてくれたおかげです。

久しぶりに会ってみて

母と妹とはタイトルにもある通り2年半ぶりに会いました。
母はシワが増えたような気がしましたし、妹は背が伸びていました。

2人は東京観光がしたいということで今回東京へ来ており、私は行きたい場所へ案内する役。
天気にも恵まれ、良い観光が出来たと思います。
母も妹もおしゃべりな方ではないので会話はあまり弾みませんでしたが思い出に残る1日でした。

一緒に暮らしていて毎日顔を合わせていると窮屈に感じてしまうかもしれませんが、たまにしか会えないとなると感じ方は違います。
親と会ったり出かけたりするのはダサいとか昔は思っていました。
でも今は、残された人生の中であと何回会えるのか、どうやって親孝行しようかとか考えています。
血のつながっている家族なので長生きして幸せでいてほしいと思います。

これからのこと

先ほども書きましたが、離婚や退職や家族からの借金。
父の話を真面目に聞かずに実家を出て行っておきながら、残された母と妹に合わせる顔がありません。

思い返してみれば、父が亡くなったことは私にも責任があります。
慢性血栓閉塞性肺高血圧症は肺と心臓の病気で、少量の運動でも息苦しくなるそうです。
そうなることを知っていながら、私は話を無視したり空返事をしたりで父の怒りを蓄積させていました。
時に怒鳴られることもあり、怒りによる興奮で体調が悪化していた部分もあると思います。
今後悔しても遅いんですが悔やむことは多々あります。

それでも信じてくれる、支えてくれる家族がいました。
「頑張れ。幸せになれ。」と言ってくれる母と妹。
本当につらくて苦労しているのは2人の方のはずなのに。


今回家族に会って励ましの言葉をもらって、とても良い刺激を受けました。
人一倍稼いで、家族に良い思いをしてもらいたいです。
日々頑張ろうと思いました。