コンテンツにスキップ

16.配列を使おう2(二次元配列)

これまで学んできたのは添字(インデックス)、つまり[]が一つだけの 一次元配列 と呼ばれる配列です。配列には 二次元配列 もあり、添字が二つあって[]も二つ並べて表示します。二次元配列は、ブロック崩しやパックマンゲーム、ロールプレイングゲームのマップなど、縦横のある平面の情報を格納するのによく使われます。

このページではまず一次元配列の復習をして、その後に二次元配列のイメージや宣言・初期化・代入のしかたを説明しています。

Ⅰ 一次元配列(復習)

1.配列のイメージ

配列は、番号付きの箱があり、その中に値が入っているイメージです。

numbers[0] に 7
numbers[1] に 5
numbers[2] に 3

2.配列宣言・初期化

配列を使うときには、ほかの変数と同じように、まず 宣言(せんげん) をします。また、 初期化(しょきか) といって、いくつデータが入るかを決める必要があります。

(1)配列宣言

データ型の後に鍵かっこ[]を書いて、その次に書かれている変数が配列であることを宣言します。(箱はまだできていません)

形式
データ型[] 配列変数名;
int[] numbers; //int(整数)型の配列を「numbers」という名前をつけて使うことを宣言

(2)初期化

配列宣言の後に、配列の長さ(箱の個数)を指定する初期化をします。初期化するときには、 new(ニュー) を使います。

形式
配列変数名 = new 型[個数];
numbers = new int[3]; //配列numbersは整数の入る3つの新しい箱とする

(3)配列宣言と初期化を一緒に行う

配列宣言と初期化は、まとめて書くこともできます。一般的には、この形式が最もよく使われます。

形式
型[] 配列変数名= new 型[個数];
int[] numbers = new int[3];

3.配列への代入

(1)添字を指定してひとつずつ代入

添字とは、配列の箱を指定する番号です。配列変数名の後の[]の中に書かれます。添字は0から始まります。

int[] numbers = new int[3]; //まず配列の宣言・初期化をする。個数は3個
numbers[0] = 7; // 1番目の要素
numbers[1] = 5; // 2番目の要素
numbers[2] = 3; // 3番目の要素

(2) for文で順次代入

for文で配列の添字を順次変化させて代入することもできます。

int[] numbers = new int[3]; //まず宣言・初期化をする
for(int i = 0; i < 3; i++){
numbers[i] = i * 2; //各要素にi*2を代入する
println(i, numbers[i]);
}

(3) 配列宣言と初期化と代入を一緒に行う

中かっこ{}を使って配列宣言と初期化と代入をいっぺんに書くこともできます。

形式
型[ ] 変数名 = \{値1, 値2, 値3, …\};
int[] numbers = {7, 5, 3};

4.配列の内容の表示

(1) for文で順次表示

for文を使えば、配列の内容を簡単に表示することができます。

int[] numbers = {7, 5, 3};
for(int i = 0; i < 3; i++){
println(numbers[i]);
}
出力結果
7
5
3

5.よくある使い方

(1)おみくじ

配列は、同じものがたくさんあるときに使うと便利です。配列の中身は整数(int型)だけではなく、文字(String型)とすることもできます。

// おみくじ
size(300,300);
textSize(48);
String[] luck = {"Very Good", "Good", "Bad"};
int result = int(random(3));
text(luck[result], 30, 100);

配列に "Very Good""Good""Bad" の3つを入れておき、実行するたびに乱数を使ってどれか1つを画面に表示します。

random(3) で、0から2までの数字がランダムに選ばれますが、float(小数)型であるため、そのままでは配列の添え字に使えません。配列の添え字には、int(整数)型にする必要があります。float(小数)型をint(整数)型に変更するには キャスト(型変換) という方法を使います。

// キャストの書き方
int(変換したい値)

小数を整数にキャストすると、小数点以下が切り捨てられます。キャストはfloat型からint型への変換だけではなく色々ありますので、調べてみると良いでしょう。

(2)図形を並べる

図形を横に並べてみましょう。また、配列のデータの内容によって色を変えて表示しましょう。

int[] field = {0, 1, 1};
void setup() {
size(150, 150);
}
void draw() {
for (int j = 0; j < 3; j++) {
if (field[j] == 0) {
fill(0, 0, 255); // 青
} else if (field[j] == 1) {
fill(0, 255, 0); // 緑
}
rect(j*50, 0, 50, 50);  //jは横方向なのでx座標になることに注意
}
}

実行結果:

やってみよう1

図形を縦に並べてみよう。

やってみよう2

マウスでクリックしたところのマスの色が反転(青⇔緑)するようにしてみよう。

(3)星スクロール

宇宙旅行をしているみたいな動きを作りましょう。

このプログラムは、課題集の「雨を降らせてください」などに応用できます。

float[] stars = new float[100];
void setup(){
size(600,400);
for(int i = 0; i < 100; i++){
stars[i] = random(0,600);
}
fill(255);
stroke(255);
}
void draw(){
background(0);
for(int i = 0; i < 100; i++){
ellipse(stars[i], i*4,2,2);
stars[i] += 2;
if(stars[i] > width){
stars[i] = 0;
}
}
}

実行結果:

星々が一斉に右に動いていきます

(解説)

setup()部分

まず、星100個のX座標を入れる配列starsを作ります。星は右に動いていくので、Y座標は変わらないためY座標の配列はいりません。 setup() では、for文でそれぞれの星の最初のX座標を決めています。自然な並びになるように random() を使います。 fill(255)stroke(255) で、星のりんかくと中身の色を白にしています。

draw()部分

ここでは、一個ずつの星を表示しています。星は ellipse で描きます。このとき、X座標は stars の値を使って、Y座標はたてに4ドットおきに均等に置きます。画面の縦の長さが400なので、4ドットおきに表示すればちょうど100個の星が描けます。

星をスクロールさせる

stars[i] += 2; で、星のX座標を増やしています。なので、drawが呼ばれるたびに星が右に2ドットずつ動いていきます。 if(stars[i] > width) で、星が右まで行ったときに星のX座標を0に戻しているので左からまた始まります。

(4)複数のボールの落下

setup()の中でfor文を使って初期値を設定し、draw()の中で使っています。配列で、それぞれのボールのy座標の位置を持っています。

このプログラムは、課題集にある「複数のボールキャッチゲームを作ってください」などに応用できます。

float[] ball_y = new float[5];
void setup() {
size(600, 400);
for (int i = 0; i < 5; i++) {
ball_y[i] = random(-400); //ボールはy座標が-400~0の位置から落ち始めます
}
}
void draw() {
background(200);
fill(255, 255, 0);
noStroke();
for (int i = 0; i < 5; i++) {
ball_y[i] += 5;
if (ball_y[i] > 400) {
ball_y[i] = random(-400);
}
ellipse(i*100+100, ball_y[i], 20, 20);
}
}

※random(-400)について:ボールが画面上辺から現れるように、-400にしています。ここをrandom(400); に変えてみて動作の違いを確かめてみると良いでしょう。

実行結果:

Ⅱ 二次元配列

1.配列のイメージ

二次元配列は、行・列に並んだ箱があるイメージです。行番号(i)は下方向に増えていき、列番号(j)は右方向に増えていきます。

2.配列宣言・初期化

一次元配列と同様、配列宣言と初期化が必要です。一般的には、(3)配列宣言と初期化を一緒に行うの形式が最もよく使われます。

(1)配列宣言

型名の後に鍵かっこ[][]を書いて、その次に書かれている変数が配列であることを宣言します。(箱はまだできていません)

形式
型[][] 配列変数名;
int[][] field; //int(整数)型の2次元配列を「field」という名前をつけて使うことを宣言

(2)初期化

配列宣言の後に、配列の大きさを指定する初期化をします。二次元配列の場合は、行の長さ、列の長さをそれぞれ指定します。

形式
配列変数名 = new 型[行の長さ][列の長さ];
field = new int[3][3];

(3)配列宣言と初期化を一緒に行う

配列宣言と初期化は、まとめて書くこともできます。一般的には、この形式が最もよく使われます。

形式
型[][] 配列変数名= new 型[行の長さ][列の長さ];
int[][] field = new int[3][3];

3.配列への代入

(1)添字を指定してひとつずつ代入

一次元配列と同様、添字を指定して代入することができます。

int[][] field = new int[3][3]; //まず宣言・初期化をする
field[0][0] = 1;
field[0][1] = 2;
:

(2) for文で順次代入

for文で配列の添字を順次変化させて代入することもできます。この場合、for文を2重にします。

int[][] field = new int[3][3]; //まず宣言・初期化をする
int n = 1;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
field[i][j] = n;
println(i, j, field[i][j]);
n++;
}
}

(3) 配列宣言と初期化と代入を一緒に行う

中かっこ{}を使って配列宣言と初期化と代入をいっぺんに書くこともできます。この場合、途中で改行しても続けても構いませんが、行要素ごとに改行した方が見やすいでしょう。

int[][] field = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

4.配列の内容の表示

(1) for文で順次表示

for文を使えば、配列の内容を簡単に表示することができます。この場合も、for文を2重にします。

int[][] field = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
print(field[i][j]);
}
println();
}
出力結果
123
456
789

5.よくある使い方

(1)マップの表示

二次元配列で地図データを設定し、それを画面に表示します。(教材のパックマン風ゲームや横スクロールゲームなど)

int[][] field = {
{0, 1, 1},
{0, 0, 1},
{1, 0, 0}
};
void setup() {
size(150, 150);
}
void draw() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (field[i][j] == 0) {
fill(0, 0, 255); // 青
} else if (field[i][j] == 1) {
fill(0, 255, 0); // 緑
}
//iは行(縦方向に増減)なのでy座標、jは列(横方向に増減)なのでx座標になることに注意
rect(j*50, i*50, 50, 50);
}
}
}

実行結果:

やってみよう3

マップの大きさを3×3よりももっと大きくしてみよう。例えば、4×5とかにしてみよう。もしくは逆に、2×2とかに小さくしてみたりしてみよう。もしエラーが出たら、そのエラーをよく見て覚えておこう。誰もがよくやる間違いで、今後何度もお目にかかるでしょう。

やってみよう4

マウスでクリックしたところのマスの色が反転(青⇔緑)するようにしてみよう。

(2)ブロックの設置

規則的にブロックを並べます。setup()の中で2重のfor文で配列の添字を順次変化させて各ブロックの設置位置(ブロックの左上頂点のx座標、y座標)を設定し、draw()の中で表示するのに使っています。(教材のブロックくずしゲームなど)

ここで特に注意する点は、ブロックのx座標は列番号(j)から、y座標は行番号(i)から計算しているところです。

float[][] block_x = new float[3][5];
float[][] block_y = new float[3][5];
void setup() {
size(500, 500);
//ブロックの設置位置の設定
for (int i=0; i < 3; i++) { //縦方向(行)の並び
for (int j=0; j < 5; j++) { //横方向(列)の並び
block_x[i][j] = j*100; //x座標は列番号(j)から算出
block_y[i][j] = i*50; //y座標は行番号(i)から算出
}
}
}
void draw() {
//ブロックの表示
for (int i=0; i < 3; i++) {
for (int j=0; j < 5; j++) {
fill(255, 128, 0);
rect(block_x[i][j], block_y[i][j], 100, 50);
}
}
}

実行結果:(数字は説明のため追記。実際の実行画面にはありません。)