jarファイルにするにあたってソースを改変する部分

エクリプスで実行している場合とjarファイルにして実行する場合ではいくらかソースを変更する必要があった。ここではそれをまとめます。ほとんど(すべて)リソースの読み込みの部分。

変更にあたっての注意点は以下の通り。

  • リソースを読み込むのにgetClassLoader()やgetResourceAsStream()を使うこと。
  • jarファイルの中ではファイルセパレータは全部「/」を使うこと。
  • エクリプスとjarファイル、双方の実行環境でも動くように例外処理をしておくこと。

今回のプロジェクトではリソースは

  • 3Dモデルオブジェクト(テキストファイル)
  • サウンド(.wavファイル)
  • 3Dモデルに使うテクスチャやタイトル画面に使う画像(.pngファイル)

以上3種類。

桃缶たべたいさんのブログから3Dモデルオブジェクトを読み込んだりディスプレイに描画したりするクラスをいただいてきた。サウンドを鳴らすクラスはlwjglのwikiのチュートリアルにあったスペースインベーダーから頂いた。

まずは単純な画像ファイルの読み込み。ももかんさんのプロジェクトの「TextureLoader」クラスを使って読み込んでいる。クラスローダーを使うメソッドを追加させていただいた。以下改変させていただいた「TextureLoader」クラスのソースの一部。

package shooting.TextureLoader;
/**
*  指定されたパスの画像ファイルをテクスチャーに変換して返す
*/
public Texture loadTexture(String imagePath) throws IOException {
return loadTexture(ImageIO.read(new FileInputStream(imagePath)));
}
/**
*  指定されたパスの画像ファイルを、指定された ClassLoader で探して、テクスチャーに変換して返す
*/
public Texture loadTexture(String imagePath, ClassLoader classLoader) throws IOException {
return loadTexture(ImageIO.read(classLoader.getResourceAsStream(imagePath)));
}

上半分はもともとあったメソッド。下半分が追加したメソッド。

「FileInputStream(imagePath)」を「classLoader.getResourceAsStream(imagePath)」に変更してある。これらのメソッドはBufferedImageを読み込む本体のメソッドを呼び出している。本体は変更していない。

そしてメインクラスでタイトル画像「SpaceVacterian.png」とゲームオーバー画像「gameover.png」をこのクラスを使って読み込んでいる。以下メインクラスのソースより。

package shooting.Main;
//エクリプスで起動するときはgetClassLoaderは使えない。jarファイルの時はgetClassLoaderを使う。
//なのでnullだった時にgetClassLoaderで再度読み込むようにした。
try{
titleTexture = textureLoader.loadTexture("images/" + "SpaceVacterian.png");
gameOver = textureLoader.loadTexture("images/" + "gameover.png");
}
catch(FileNotFoundException e){
System.out.println("loading by getClassLoader....");
titleTexture = textureLoader.loadTexture("images/" + "SpaceVacterian.png", Main.class.getClassLoader());
gameOver = textureLoader.loadTexture("images/" + "gameover.png", Main.class.getClassLoader());
}

try文の中ではエクリプスで読み込む場合を想定している。jarファイルで実行するとこのままでは「ファイルがみつからねえ云々」というエラーがでるのでそれをcatch文の中で受け止めている。getClassLoader()を追加している。逆にエクリプスではgetClassLoader()だとエラーを吐き出す。なので例外処理で双方の環境に対応できるようにした。

3Dモデルオブジェクトを作るModelクラスでも同じような改変をしている。以下、モデルのテクスチャーを読み込む部分の改変後。

package shooting.Main;
String  texturePath;
//WindowsでもLinuxでもjarファイルの中でのディレクトリのセパレータは「/」でよい。
texturePath = imagesDir + "/" + material.textureName;
//エクリプスで起動するときはgetClassLoaderは使えない。jarファイルの時はgetClassLoaderを使う。
//なのでnullだった時にgetClassLoaderで再度読み込むようにした。
try{
material.texture = textureLoader.loadTexture(texturePath);
}
catch(FileNotFoundException e){
System.out.println("loading by getClassLoader....");
material.texture = textureLoader.loadTexture(texturePath, Model.class.getClassLoader());
}

Windowsではファイルセパレータは”\(¥)”だが、jarファイルの中では”/”で良いことに注意。jarファイルができた後、Windows側でエラーがでてしまい、ここでも結構悩んだ。


次に3Dモデルを読み込む「ObjectLoader」クラスの改変。ブレンダーで作成した3Dモデルは「.obj」ファイルでエクスポートしている。これの実際の中身はテキスト。オブジェクトを読み込むメソッドの先頭部分より。まずは元のソースより。

package shooting.ObjectLoader;

/**
*  渡されたファイルパスの OBJ ファイルを読み込み、Model インスタンスとして返す
*/
public static Model load(String filePath) throws IOException {
Model           model = new Model(filePath, DEFAULT_IMAGES_DIR);
DisplayList     displayList = null;
File            file = new File(filePath);
BufferedReader  br = null;
String          line;

//  テキストファイルなので 1 行ずつ読み込んでパースしていく
try {
br = new BufferedReader(new FileReader(file));

・・・・・・・・

次に改変したもの。

package shooting.ObjectLoader;

/**
*  渡されたファイルパスの OBJ ファイルを読み込み、Model インスタンスとして返す
*/
public static Model load(String filePath) throws IOException {
filePath = "models/" + filePath;
Model           model = new Model(DEFAULT_IMAGES_DIR);
DisplayList     displayList = null;
File            file = new File(filePath);
BufferedReader  br = null;
String          line;

//  テキストファイルなので 1 行ずつ読み込んでパースしていく
try {
br = new BufferedReader(new InputStreamReader (ObjectLoader.class.getResourceAsStream(filePath),"UTF-8"));

・・・・・・・・・・・・・・・・・

一番最後のBufferdReader()に与える引数として、

  • 改変前ではFileReader(File arg0)を使っているのに対して
  • 改変後ではInputStreamReader(InputStream arg0, String arg1) を使っている。

さらにFileReaderとInputStreamReaderでは与える引数が違ってくる。前者に与える引数はFileなのでFileオブジェクトを作って与えるが後者ではInputStreamなので後者ではStringであるfilePathとなる。後者でもFileオブジェクトを作っているが、後で読み込みメソッド本体で使用されているのでここはそのまま。

また、このプログラムはLinux環境で作られているのでテキストはUTF-8形式になるのでInputStreamReader()には”UTF-8″も引数として与えている。これはWindows環境での実行の際に必要になる。

オブジェクト本体の他に「.mtl」ファイル(マテリアルを設定するファイル)も読み込むので同じように改変した。

メインクラスは以下の通りでパスに指定されたファイルを読み込める。自機のファイルを読み込む部分。

package shooting.Main;

public Model myRocket;
・・・・・・・・・・・・・・・・

textureLoader = new TextureLoader();
・・・・・・・・・・・・・・・・

//  ファイルのパス指定で画像を読み込む
myRocket = ObjectLoader.load("MyRocket3.obj");
myRocket.compile(textureLoader);

 

さて、次はサウンドの読み込み。改変後のソース、SoundManager2クラスより、addSoundメソッド。つまりリソースを読み込む部分。

package shooting.SoundManager2;

public int addSound(String path) throws Exception {
// Generate 1 buffer entry
scratchBuffer.rewind().position(0).limit(1);
AL10.alGenBuffers(scratchBuffer);
buffers[bufferIndex] = scratchBuffer.get(0);

// load wave data from buffer
//WaveData wavefile = WaveData.create(path);元々のコードはこうだった。この一行を下のように変更すると動いた。
//どうもコードが古かったようだ。
//WaveData wavefile = WaveData.create(new BufferedInputStream(new FileInputStream(path)));
//さらに上記のコードからjarにした時を考慮してgetResourceAsStream()を使用するコードに変更した。
InputStream is = SoundManager2.class.getResourceAsStream(path);
WaveData wavefile = WaveData.create(new BufferedInputStream(is));
// copy to buffers
AL10.alBufferData(buffers[bufferIndex], wavefile.format, wavefile.data, wavefile.samplerate);
//generate data from the file (binary data?)

// unload file again
wavefile.dispose();

// return index for this sound
return bufferIndex++;
}

WaveData wavefile = WaveData.create(path);

元々のチュートリアルではこうなっていた部分だが、エクリプスにコピペしたところエラーがでた。なので以下のように変更した。BufferedInputStream()を使うように変更。(そりゃそうだよな・・・・大きめのファイルだもんな・・・)このスペースインベーダーが動かなくて困っている人がいればここがポイントです。

WaveData wavefile = WaveData.create(new BufferedInputStream(new FileInputStream(path)));

さらに変更。2行に分ける(わざわざInputStreamオブジェクトを作る)必要はなかったかもしれないけど。

InputStream is = SoundManager2.class.getResourceAsStream(path);
WaveData wavefile = WaveData.create(new BufferedInputStream(is));

・・・ほぼソースに書いてあるままです。

Screenshot_from_2015-03-09 14:56:53今回のプロジェクトではリソースはこういうふうに配置してある。srcフォルダに全部いれるかそれともプロジェクトのフォルダにいれとくのかどっちかにすれば良さそうな感じだがうまくいかなかった(かなり苦労した)ww

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です