山傘のプログラミング勉強日記

プログラミングに関する日記とどうでもよい雑記からなるブログです。

JavaFX でゲームプログラミング超入門 その2

フィールドに関するプログラム

前回の記事では、モンスターの画像を表示するプログラムを作りました。今回はマップでキャラクタの移動に関するプログラムを作りました。
f:id:yamakasa3:20180314183850p:plain

タイルセットつかってマップを作る

Tiledというフリーソフトを使ってマップを作ります。
解説記事として、
タイルマップ(Tiled)でステージを作成!:Cocos Creator - モーリーのメモ
が参考になります。タイルセットは、
opengameart.org
のものを使わせて貰いました。
Tiledで作ったマップを保存して、メモ帳などのエディタから開くと、f:id:yamakasa3:20180314185405p:plain
のようになります。この中で、マップを数値化した二次元配列をつくるために、dataをコピーして新しいテキストファイルを作ります。僕の環境では、メモ帳ではなく、TeraPadでマップの二次元配列元のテキストファイルを作成しています。

コードの作成

コードの説明や疑問?

TeraPadでテキストファイルを保存した理由ですが、
public int getMapData()内のmapData[i][j] = Integer.parseInt(array[j]);
でエラーを出したためです。テキストファイルを読み込むとき、一行ごとに読み込んでいるんですが、" , "を取り除くために、split()を使い、行ごとに配列が作成されます。この配列はString型なので、int型に変換したかったんですが、エラーが出ました。
ファイルを読み込むときに文字コードを指定したり、trim()で文字列に含まれる空白を取り除いても改善しなかったので、改めて、TeraPadで保存してからやるとうまくいきました。
また、メモ帳で保存しても、いったんString型の二次元配列に格納してから、文字列を読み込むwhile文の外でint型に変換すると成功しました。
getBytes()で文字を調べてみましたが、その先が分からずに断念しました。解決したら、また記事にしたいと思います。

歩行グラフィックは、押されたボタンに対応してプレイヤのImageViewを変えています。
移動が可能かどうかの判定は、プレイヤの座標を取得し、その値を32で割り、二次元配列で表現できる領域に変換しています。マップは320*320(ピクセル?)のサイズで、32*32のタイルを100個埋めているので、座標を32で割ることで、配列に対応させています。そして、移動の可否は、配列の数字で判定しています。

コード

package application;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;

public class Maze extends Application{
	public static void main(String[] args) {
		launch(args);
	}
	public ImageView player1ImgView = new ImageView();
	public ImageView playerImgView =  new ImageView();;
	public Image playerMoveImg[][] =  new Image[4][3];
	@Override
	public void start(Stage stage) throws Exception{
//		stage.setHeight(360);
//		stage.setWidth(360);
		stage.setTitle("TheMaze");

		// マップの画像を読み込む
		Path mapPath = Paths.get("image/map.png");
		Image mapImg = new Image(mapPath.toUri().toString());
		ImageView mapImgView = new ImageView(mapImg);

		// プレイヤのタイルセットから歩行グラフィックを切り取る
		Path playerPath1 = Paths.get("image/player1.png");
		Image playerImg1 = new Image(playerPath1.toUri().toString());
		int width = 32;
		int height = 32;
		for(int i = 0; i < 4; i++) {
			for(int j = 0; j < 3; j++) {
				playerMoveImg[i][j] = new WritableImage(playerImg1.getPixelReader()
						,width * j, height * i, width, height);
			}
		}
		// プレイヤの初期位置と画像を設定
		player1ImgView = new ImageView(playerMoveImg[0][1]);
		player1ImgView.setLayoutX(32);
		player1ImgView.setLayoutY(32);

		Group group = new Group();
		group.getChildren().addAll(mapImgView,player1ImgView);
		Scene scene = new Scene(group);

		int[][] mapData = getMapData();
		scene.setOnKeyPressed(event -> moveEvent(event, mapData));

		stage.setScene(scene);
		stage.show();
	}
		public void moveEvent(KeyEvent event, int[][] mapData) {
			String s = event.getCode().toString();
			//System.out.println(s);
			if(s.equals("DOWN")) {
				player1ImgView.setImage(playerMoveImg[0][0]);
				if( checkMove(mapData, 1, 0)) {
					player1ImgView.setLayoutY(player1ImgView.getLayoutY() + 32);
				}
			}else if(s.equals("UP")) {
				player1ImgView.setImage(playerMoveImg[3][0]);
				if( checkMove(mapData, -1, 0)) {
					player1ImgView.setLayoutY(player1ImgView.getLayoutY() - 32);
				}
			}else if(s.equals("LEFT")) {
				player1ImgView.setImage(playerMoveImg[1][0]);
				if( checkMove(mapData, 0, -1)) {
					player1ImgView.setLayoutX(player1ImgView.getLayoutX() - 32);
				}
			}else if(s.equals("RIGHT")) {
				player1ImgView.setImage(playerMoveImg[2][1]);
				if( checkMove(mapData, 0, 1)) {
					player1ImgView.setLayoutX(player1ImgView.getLayoutX() + 32);
				}
			}
		}
		public boolean checkMove(int[][] mapData, int i, int j) {
			int col = (int)player1ImgView.getLayoutX()/32;
			int row = (int)player1ImgView.getLayoutY()/32;
			if(mapData[row + i][col + j] == 29) {
				return true;
			}else if(mapData[row + i][col + j] == 82){
				return true;
			}else {
				return false;
			}
		}

		// texファイルからmap情報を取得し、int型の配列を返す
		public int[][] getMapData() {
			int [][]mapData = new int[10][10];
			Path path = Paths.get("maptext/mapdata1.txt");
			File file = path.toFile();
			try {
				FileReader filereader = new FileReader(file);
				BufferedReader br = new BufferedReader(filereader);
				String str;
				int i = 0;;
				while((str = br.readLine()) != null) {
					String[] array = str.split(",");
					for(int j = 0; j < array.length; j++) {
						mapData[i][j] = Integer.parseInt(array[j]);
					}
					i++;
				}
			br.close();
			}catch(FileNotFoundException e) {
				System.out.println(e);
			}catch (IOException e) {
				System.out.println(e);
			}
			return mapData;
		}
}

また、プレイヤの画像は、
ぴぽや
から使っています。