JavaFXで図形や画像を使用する場合,1つの方法はCanvasというコントロールを用います.Canvasには図形や画像を描くことが出来て,Canvasを1つのノードとしてSceneGraphに設置します.ここでは,Canvasを使って,図形や画像を用いたプログラムを作ってみましょう.
Canvasは画像を表示する領域を表し,実際の図形などはCanvas上のGraphicsContext(画用紙のようなもの)に描かれます.
具体的な使用法は,まず,次のようにCanvasを作成し,そのGraphicsContextを取得します.
//canvasという名前で横400縦300の描画領域を作成 Canvas canvas = new Canvas(400,300); //canvasのGraphicsContextをgcという名前で取得 GraphicsContext gc = canvas.getGraphicsContext2D();
また,Canvasを使用する場合は次のimport文が必要です.
import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext;
GraphicsContextに様々な図形を描くためのメソッドが用意されています.
| 基本図形 | |
|---|---|
| メソッド | 使用法 |
| fillRect(x,y,w,h) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形を塗りつぶす. |
| fillRoundRect(x,y,w,h,aw,ah) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」で幅「aw」高さ「ah」の弧状の角の四角形を塗りつぶす. |
| fillOval(x,y,w,h) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形に内接する楕円を塗りつぶす. |
| fillArc(x,y,w,h,s,t,ArcType) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形に内接する楕円のうち,\(x\)方向を0度として中心角が反時計回りに「s」度の位置から「t」度分の部分を塗りつぶす. ArcType:null,ArcType.CHORD(始点と終点を直線で結ぶ),ArcType.OPEN(始点と終点を結ばない),ArcType.ROUND(中心と始点,中心と終点を直線で結ぶ)のどれか |
| strokeLine(x1,y1,x2,y2) | 始点 \((x1,y1)\),終点 \((x2,y2)\) の直線を描く. |
| strokeRect(x,y,w,h) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形を描く. |
| strokeRoundRect(x,y,w,h,aw,ah) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」で幅「aw」高さ「ah」の弧状の角の四角形を描く. |
| strokeOval(x,y,w,h) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形に内接する楕円を描く. |
| strokeArc(x,y,w,h,s,t,ArcType) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形に内接する楕円のうち, \(x\)方向を0度として中心角が反時計回りに「s」度の位置から「t」度分の部分を描く.ArcTypeはfillArcと同じ. |
| clearRect(x,y,w,h) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形の領域を消去する. |
| fillPolygon(x[ ], y[ ], n) | それぞれ n 個の x 座標の配列「x[ ]」と y 座標の配列「y[ ]」によって決まる n 個の頂点からなる n 角形を塗りつぶす. |
| strokePolygon(x[ ], y[ ], n) | それぞれ n 個の x 座標の配列「x[ ]」と y 座標の配列「y[ ]」によって決まる n 個の頂点からなる n 角形を描く. |
| strokePolyline(x[ ], y[ ], n) | それぞれ n 個の x 座標の配列「x[ ]」と y 座標の配列「y[ ]」によって決まる n 個の点を結ぶ折れ線を描く. |
| テキスト | |
| fillText("文字列",x,y) | 座標 \((x,y)\) に「文字列」を描く. |
| strokeText("文字列",x,y) | 座標 \((x,y)\) に「文字列」を描く. |
| パス(経路) | |
| beginPath() | 現在のパスを空にリセット. |
| moveTo(x,y) | 現在のパスから座標 \((x,y)\) へ移動する. |
| lineTo(x,y) | 現在のパスに座標 \((x,y)\) への直線のパスを追加. |
| quadraticCurveTo(cx,cy,x,y) | 現在のパスに座標 \((x,y)\) まで,制御点を \((cx,cy)\) とした2次曲線のパスを追加. |
| bezierCurveTo(cx1,cy1,cx2,cy2,x,y) | 現在のパスに座標 \((x,y)\) まで,制御点を \((cx1,cy1)\),\((cx2,cy2)\) とした3次ベジエ曲線のパスを追加. |
| arc(x,y,rx,ry,s,t) | 現在のパスに座標 \((x,y)\) を中心とした \(x\)方向の半径が\(rx\),\(y\)方向の半径が\(ry\)の楕円の \(x\)方向から反時計回りに角度 \(s\)度を始点として,終点が \(t\)度に成るような弧状のパスを追加. |
| arcTo(x1,y1,x2,y2,r) | 現在のパスに座標 \((x1,y1)\) から \((x2,y2)\) までの半径 \(r\) の円弧を追加. |
| closePath() | パスを閉じる. |
| rect(x,y,w,h) | 左上の座標が \((x,y)\) で幅が「w」高さが「h」の四角形のパスを構成する. |
| fill() | パスを塗りつぶす. |
| stroke() | パスを描く. |
| clip() | 現在のパス領域が設定され,この後に描かれる図形は,この設定された領域で切り抜かれた部分だけが表示される. |
| イメージ(画像) | |
| drawImage(Image,x,y,w,h) | プログラムに読み込まれた画像(Image)を,左上の座標が \((x,y)\) で幅が「w」高さが「h」の領域に表示する. |
| 消去 | |
| clearRect(x,y,w,h) | 表示されているグラフィックの左上の座標が \((x,y)\) で幅が「w」高さが「h」の領域を消去する. |
図形などを表示する時の色や線の太さなどの属性は,はじめは決まった値が設定されていますが,それらを使用したいものに変えることが出来ます.
設定の仕方は,図形などを描くメソッドを使用する前に次のような命令を使用します.GraphicsContextを gc とすると,
gc.setLineWidth(線の太さ); //線の太さを数値で指定する gc.setStroke(色の指定); //線の色を指定する gc.setFill(色の指定); //塗りつぶす色を指定する
色の指定の仕方は次で説明します.
JavaFXには色を表す「クラス」が用意されていて,それを使用して色を操作できます.
使用する場合は次の import文が必要です.
import javafx.scene.paint.Color;
人間は,光の3原色(赤,緑,青)が様々な強度で混ざることで色の違いを認識しています.従って,色を指定するための方法の多くは,3原色をどの位の割合で混合するかを指定することで色を設定します.
これらの色の指定を上記の描画属性の設定に使用することで,プログラムの中において,設定の後に記述された図形や文字の色が変更されます.
| 指定の仕方 | 書き方 | 説明 |
|---|---|---|
| 予め決められている名前 | Color.(名前) | 具体的な名前は後に記す |
| 直接オブジェクトを作成 | new Color(red,green,blue,alpha) | red,green,blueとalphaに0〜1の実数値を設定する.alphaは不透明度を表す(0が透明) |
| colorメソッドを使用 | Color.color(red,green,blue) | red,green,blueに0〜1の実数値を設定する. |
| colorメソッドを使用 | Color.color(red,green,blue,alpha) | red,green,blueとalphaに0〜1の実数値を設定する. |
| rgbメソッドを使用 | Color.rgb(RED,GREEN,BLUE) | RED,GREEN,BLUEに0〜255の整数値を設定する. |
| rgbメソッドを使用 | Color.rgb(RED,GREEN,BLUE,alpha) | RED,GREEN,BLUEに0〜255の整数値,alphaは0〜1の実数値を設定 |
| hsbメソッドを使用 | Color.hsb(色相,彩度,明度,alpha) | 色相には0〜360の実数値を設定.彩度,明度,alphaは0〜1の実数値を設定. |
| hsbメソッドを使用 | Color.hsb(色相,彩度,明度) | 色相には0〜360の実数値を設定.彩度,明度は0〜1の実数値を設定. |
| webメソッドを使用 | Color.web(色指定文字列,alpha) | 色指定文字列には,色名かrgb,hsbによる指定を文字として設定.alphaは0〜1の実数値を設定. |
| webメソッドを使用 | Color.web(色指定文字列) | 色指定文字列には,色名かrgb,hsbによる指定を文字として設定. |
名前が与えられている色の代表的なものは以下のものです.右の数値は各色のRGBを0〜255までの数値で表したときの値です.自分で色を合成する場合は,この数値を参考にして下さい.これ以外のものを含めた色名の一覧.
| 色 | 名前 | 赤 | 緑 | 青 |
|---|---|---|---|---|
| 黒 | BLACK | 0 | 0 | 0 |
| 暗い灰色 | DARKGRAY | 64 | 64 | 64 |
| 灰色 | GRAY | 128 | 128 | 128 |
| 明るい灰色 | LIGHTGRAY | 192 | 192 | 192 |
| 白 | WHITE | 255 | 255 | 255 |
| 青 | BLUE | 0 | 0 | 255 |
| シアン | CYAN | 0 | 255 | 255 |
| 緑 | GREEN | 0 | 255 | 0 |
| 黄 | YELLOW | 255 | 255 | 0 |
| 赤 | RED | 255 | 0 | 0 |
| マゼンダ | MAGENTA | 255 | 0 | 255 |
| オレンジ | ORANGE | 255 | 200 | 0 |
| ピンク | PINK | 255 | 175 | 175 |
Canvas上に表示する文字の字体を変更するためには,GraphicsContextに対して以下のような設定をします.
GraphicsContextを gc とした場合,文字を表示する命令を記述する前に次のように書くことで文字のサイズやスタイルを変更できます.
gc.setFont(Font.font(フォントの指定));
また,次のimport文が必要です.
import javafx.scene.text.Font;
フォントの指定は,上記でimportした「Fontクラス」に以下のようなフォントの情報が記述されているので,その情報を使用して指定します.
| 項目 | 意味 | 設定できる値 | family | 論理フォント名:一般的なフォント名 (コンピューター毎に対応する物理フォント名に自動的に変換) |
SERIF,SANS-SERIF,CURSIVE,FANTASY,MONOSPACE を" "に挟んで指定 |
|---|---|---|
| 物理フォント名:フォントの実際の名前 (コンピューター毎に異なる) |
"Osaka","Arial","Times"など(名前を" "に挟んで指定) | |
| weight | 太さ | FontWeight. に続けて,BLACK, BOLD, EXTRA_BOLD, EXTRA_LIGHT, LIGHT, MEDIUM, NORMAL, SEMI_BOLD, THIN を指定 |
| posture | スタイル | FontPosture.ITALIC, FontPosture.REGULAR |
| size | 大きさ | ポイント数を指定 |
また,「FontWeight」,「FontPosture」を使う場合は,次のimport文が必要です.
import javafx.scene.text.FontWeight; import javafx.scene.text.FontPosture;
フォントの指定は,次のようにします.全部の項目を指定する必要はありませんが,weight や posture を指定する場合は family と size を指定します.
Font.font(family, weight, posture, size)
最初の例として,フォントや色の変更と幾つかの図形を表示するプログラムを作ってみましょう.
/************************************************** 文字と図形を表示する **************************************************/ import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.ArcType; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.text.Font; public class FirstCanvas extends Application{ public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { stage.setTitle("Application FirstCanvas"); VBox root = new VBox(); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(10,10,10,10)); Canvas canvas = new Canvas(600,400); //Cavasの作成 GraphicsContext gc = canvas.getGraphicsContext2D(); //GraphicsContextの取得 //描画属性の設定 gc.setLineWidth(5.0); //線の太さ gc.setStroke(Color.RED); //線の色 gc.setFill(Color.RED); //塗りつぶす色 //テキストの描画 gc.fillText("こんにちは JavaFX!",20,20); //図形の描画 gc.strokeRect(200, 10, 50, 30); //長方形 gc.fillOval(300, 10, 50, 30); //塗りつぶし楕円 //フォントの変更 gc.setFont(Font.font("SERIF",48)); //描画属性の変更 gc.setLineWidth(2.0); gc.setStroke(Color.BLUE); gc.setFill(Color.rgb(200, 50, 255,0.5)); //図形の描画 gc.strokeText("こんにちは JavaFX!",20,100); gc.strokeRoundRect(20, 130, 80, 50, 10, 10); //角丸長方形 gc.fillArc(150, 130, 80, 50, 30, 250, ArcType.ROUND); //塗りつぶし円弧(中心線) gc.strokeArc(280, 130, 80, 50, 30, 250, ArcType.CHORD); //円弧(弦) //頂点座標の設定 double x[] = {400, 450, 500, 475, 425}; double y[] = {170, 120, 170, 220,220}; //塗りつぶし多角形 gc.fillPolygon(x, y, 5); //描画属性の変更 gc.setLineWidth(7.0); gc.setStroke(Color.rgb(0,150,255)); gc.setFill(Color.GREEN); //パスによる描画 gc.beginPath(); //パスの初期化 gc.moveTo(10, 300); //描画点の移動 gc.lineTo(70, 330); //直線のパスを追加 gc.lineTo(140, 300); gc.moveTo(200, 300); gc.quadraticCurveTo(270, 250, 350, 300); //2次曲線のパス gc.stroke(); //線状のパスを表示 gc.beginPath(); gc.moveTo(400, 300); gc.bezierCurveTo(450, 350, 500, 250, 550, 300); //3次ベジエ曲線のパス gc.closePath(); //パスを閉じる gc.fill(); //閉じたパスを塗りつぶす root.getChildren().addAll(canvas); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } }
Canvas には文字や図形を表示する他に,画像を表示することが出来ます.
画像を表示するためには,まずPCのプログラムと同じ場所に画像ファイル用意します.プログラムではそれを読み込んで表示するように記述します.
具体的には,「momiji.jpg」という画像ファイルがあるとすると,次のような命令を使用します.この記述で,読み込んだ画像ファイルをプログラム内では img という名前で扱えるようになります.
Image img = new Image(getClass().getResource("momiji.jpg").toExternalForm()); //画像の読込
gc.drawImage(img,20,100); //画像の表示
また,次の import文が必要です.
import javafx.scene.image.Image;
Canvasのもう一つの例として,イメージファイルを表示するプログラムを作ってみます.
/************************************************** 画像を表示するプログラム **************************************************/ import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.text.Font; import javafx.scene.image.Image; public class SecondCanvas extends Application{ public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) throws Exception { stage.setTitle("Application SecondCanvas"); VBox root = new VBox(); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(10,10,10,10)); Canvas canvas = new Canvas(480,350); //Cavasの作成 GraphicsContext gc = canvas.getGraphicsContext2D(); //GraphicsContextの取得 //描画属性の設定 gc.setLineWidth(5.0); //線の太さ gc.setStroke(Color.ORANGE); //線の色 gc.setFill(Color.RED); //塗りつぶす色 //フォントの変更 gc.setFont(Font.font("SERIF",32)); //テキストの描画 gc.fillText("そろそろ秋!",150,35); //図形の描画 gc.fillRect(65, 50, 350, 280); gc.setStroke(Color.YELLOW); gc.strokeRect(65, 50, 350, 280); //画像の表示 Image img = new Image(getClass().getResource("momiji.jpg").toExternalForm()); //読み込み gc.drawImage(img, 80, 70, 320, 240); //表示 root.getChildren().addAll(canvas); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } }
上の2つの例題を参考にして,様々な図形の組合せや画像が表示されるプログラムを作りなさい.クラス名は「Ex10_1」とせよ.
ロジスティック写像は,生物の個体数の変動を表すモデルの1つです.このモデルでは,単純な個体数の増加だけではなく,個体間の競争や個体数の増加による環境の悪化による減少も考慮しています.
\(n\)世代目の個体数にある係数を掛けて0〜1の間の数値に変換したものをを \(X(n)\) とすると,次の世代の個体数が \(X(n+1) = a X(n)(1−X(n))\) のように求められます.ここで, \(a\) は生物の増殖率を表し,2項目が競争や環境の影響(これは個体数が多いほど激しいので個体数に比例する.)による個体数の減少を表します.
ここでは, \(a\) を変化させた時に起こる現象を調べるプログラムを作ります.個体数は,最終的には \(a\) によって定まる一定の値になると想像されますが,実際にはどうなるでしょう.
/**************************************************
Logistic Map
***************************************************/
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class LogisticMap extends Application{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("Application Logistic Map");
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10,10,10,10));
Canvas canvas = new Canvas(640,480); //Cavasの作成
GraphicsContext gc = canvas.getGraphicsContext2D(); //GraphicsContextの取得
//描画属性の設定
gc.setLineWidth(1.0); //線の太さ
gc.setStroke(Color.RED); //線の色
//ロジスティック写像の計算
double a0, da, a, x1, x2;
int xp;
a0 = 2.8; //最初の a の値
x1 = 0.5;
da = (4.0 - a0)/600; // a の変化量:横軸を600にした
for(int n = 0; n < 600; n++) {
a = a0 + da*n; // n 回目の a の値
//最初の500回は安定していないので捨てる
for(int i = 0; i < 500; i++) {
x2 = a*x1*(1.0 - x1);
x1 = x2;
}
//その後の5000回をプロット
for(int j = 0; j < 5000; j++) {
x2 = a*x1*(1.0 - x1);
xp = 450 - (int)(x2*400); //プロットする点の y 座標
gc.strokeLine(20+n, xp, 20+n, xp); //点(短い線)を描く
x1 = x2; //計算結果を x1 として次の回の計算へ
}
}
gc.setStroke(Color.BLACK);
gc.strokeRect(20, 50, 600, 400);
root.getChildren().addAll(canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
以下のどれかのプログラムを作る.
簡単な計算を何回も繰り返し,その結果を適当な条件で色分けして表示します.非常に複雑で,カラフルな図形ができます.
まず,\(z(0),z(1),A\)をそれぞれ複素数とし,次のような計算をします.
\(z(1) = z(0)*z(0) + A\)
さらに,こうして得られた\(z(1)\)に対し,\(z(2)\)を同様に計算します.
\(z(2) = z(1)*z(1) + A\)
このように何度も同じ計算を繰り返していきます.そして,\(z\)の絶対値が2より大きくなったときの計算回数を求めます. (100回繰り返しても2より大きくならなければ計算をやめます.)
この計算を\(A\)を少しずつ変えながら実行し,それぞれの\(A\)の値における計算回数に応じて色を付けた点を画面に表示していきます.
複素数は,2つの実数 \(x,y\) を用いて \(z = x + \imath y\) ( \(\imath\) は虚数単位)のように書けるものです.複素数の計算は,実数 \(x,y\) に対して計算するプログラムとして書きます.
また,画面に表示する場合は,\(x\)を横軸,\(y\)を縦軸として2次元平面上に表します(複素平面).
具体的には次のように書きます.
/**************************************************
Mandelblot set
**************************************************/
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
public class Mandelblot extends Application{
public static void main(String[] args) {
launch(args);
}
double cvz, cvi;
double scale;
Canvas canvas = new Canvas(540,580);
GraphicsContext gc = canvas.getGraphicsContext2D();
TextField tf1 = new TextField("-1.0");
TextField tf2 = new TextField("0.0");
TextField tf3 = new TextField("1.0");
TextField tf4 = new TextField("10");
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("Application Logistic Map");
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10,10,10,10));
root.setSpacing(10.0);
HBox pane1 = new HBox();
pane1.setAlignment(Pos.CENTER);
pane1.setPadding(new Insets(10,10,10,10));
pane1.setSpacing(5.0);
Label label1 = new Label("実軸の中心座標:");
Label label2 = new Label("虚軸の中心座標:");
Label label3 = new Label("拡大率:");
Label label4 = new Label("色の数:");)
tf1.setAlignment(Pos.CENTER_RIGHT);
tf1.setMaxWidth(50);
tf2.setAlignment(Pos.CENTER_RIGHT);
tf2.setMaxWidth(50);
tf3.setAlignment(Pos.CENTER_RIGHT);
tf3.setMaxWidth(50);
pane1.getChildren().addAll(label1,tf1,label2,tf2,label3,tf3);
gc.setLineWidth(1.0);
gc.setStroke(Color.BLACK);
gc.setFill(Color.BLACK);
gc.fillRect(20, 60, 500, 500);
tf1.setOnAction(event -> updateMap());
tf2.setOnAction(event -> updateMap());
tf3.setOnAction(event -> updateMap());
root.getChildren().addAll(pane1,canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private void updateMap() {
gc.fillRect(20, 60, 500, 500);
String s1 = tf1.getText();
String s2 = tf2.getText();
String s3 = tf3.getText();
String s4 = tf4.getText();
cvz = Double.parseDouble(s1);
cvi = Double.parseDouble(s2);
scale = Double.parseDouble(s3);
c = Integer.parceInt(s4);
double rs = cvz - 1.0/scale, is = cvi - 1.0/scale;
double rc,ic;
double rz,iz;
double Rz,Iz;
double d = 1.0/Math.abs(scale*250);
for(int i = 0; i < 500; i++) {
rc = rs + d*i;
for(int j = 0; j < 500; j++) {
ic = is + d*j;
rz = 0.0;
iz = 0.0;
for(int n = 0; n < 500; n++) {
Rz = rz*rz - iz*iz + rc;
Iz = 2*rz*iz + ic;
if(Rz*Rz+Iz*Iz > 4.0) {
gc.setStroke(Color.hsb(n%c*360/c, 1.0, 1.0)); //色は hsb 形式で与えた
gc.strokeLine(20+i,560-j,20+i,560-j);
break;
}
rz = Rz;
iz = Iz;
}
}
}
}
}
このプログラムを実行すると,窓の中に数値を入力する場所が3箇所表示されます.最初の2つには表示する領域の中心の座標を入力し,最後のものには表示する倍率を入力します.
上のプログラムを参考にして,
\(Z(1) = Z(0)*Z(0)*Z(0) - 2*Z(0) + A\)
という写像に対して同様のプログラムを作って実行してみましょう.クラス名は 「Ex10_3」 にします.
四角形の領域を設定し,その領域の内部にランダムに道を作ることによって迷路を作成します.
具体的には,領域内の場所の状態を表す2次元の配列変数を用意し,その値が1の時は壁,0の時は道のように決めておき,
領域内の任意の位置から毎回ランダムに方向を決めて道を作成して行き,道が作れなくなるまで続けます.
例として,任意の場所から道を伸ばして行くプログラムを作ってみます.
/**************************************************
RoadBuild
**************************************************/
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
public class RoadBuild extends Application{
public static void main(String[] args) {
launch(args);
}
int size = 10;
Canvas canvas = new Canvas(20*size+20,20*size+20);
GraphicsContext gc = canvas.getGraphicsContext2D();
TextField tf = new TextField("10");
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("Application Road Build");
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10,10,10,10));
root.setSpacing(2.0);
HBox pane1 = new HBox();
pane1.setAlignment(Pos.CENTER);
pane1.setPadding(new Insets(10,10,10,10));
pane1.setSpacing(5.0);
tf.setAlignment(Pos.CENTER_RIGHT);
tf.setMaxWidth(50);
Label label = new Label("Size : ");
pane1.getChildren().addAll(label,tf);
gc.setLineWidth(1.0);
tf.setOnAction(event -> createRoad(stage)); //メソッドに引数として stage を指定
root.getChildren().addAll(pane1,canvas);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
//Stage を引数として持つメソッドを定義(窓の大きさを変えるため)
private void createRoad(Stage stage) {
String s = tf.getText();
size = Integer.parseInt(s);
stage.setWidth(20*size+30); //stage の幅を設定
stage.setHeight(20*size+110); //stage の高さを設定
canvas.setWidth((double)(20*size+20)); //canvas の幅を設定
canvas.setHeight((double)(20*size+20)); //canvas の高さを設定
//ブロックの位置を2次元配列として作成
int[][] site;
site = new int[2*size+1][2*size+1];
//ブロックの初期化
for(int i = 0; i < 2*size+1; i++) {
for(int j = 0; j < 2*size+1; j++) {
if(i==0 || i==2*size || j==0 || j==2*size) {
site[i][j] = 0; //外周のブロックは通路に設定
}
else {
site[i][j] = 1; //内部は全て壁
}
}
}
//掘り始める位置をランダムに設定
int x = 2*(int)(Math.random()*(size-1))+2;
int y = 2*(int)(Math.random()*(size-1))+2;
site[x][y] = 0; //最初の位置を通路にする
//位置(x,y)から前後左右どちらか2ブロックが壁ならば通路にする
while(site[x][y-2]==1 || site[x][y+2]==1 || site[x-2][y]==1 || site[x+2][y]==1) {
switch((int)(Math.random()*4)) { //通路にする方向はランダムに選ぶ
case 0: if(site[x][y-2]==1) {
site[x][y-1] = 0;
site[x][y-2] = 0;
y = y-2;
}
break;
case 1: if(site[x+2][y]==1) {
site[x+1][y] = 0;
site[x+2][y] = 0;
x = x+2;
}
break;
case 2: if(site[x][y+2]==1) {
site[x][y+1] = 0;
site[x][y+2] = 0;
y = y+2;
}
break;
case 3: if(site[x-2][y]==1) {
site[x-1][y] = 0;
site[x-2][y] = 0;
x = x-2;
}
break;
}
}
//通路と壁を色分けして表示する
for(int i = 0; i < 2*size+1; i++) {
for(int j = 0; j < 2*size+1; j++) {
if(site[i][j]==1) {
gc.setFill(Color.BLUE);
gc.fillRect(10*i+5, 10*j+10, 10, 10);
}
else {
gc.setFill(Color.RED);
gc.fillRect(10*i+5, 10*j+10, 10, 10);
}
}
}
}
}
例を参考にして,道が作れなくなったら,既に道になっていて道が作れる場所を選び,同様に道を作っていくという作業を繰り返します.最終的に道を作れる場所がなくなると迷路が出来ます.クラス名は,「Ex10_4」とする.