二次元配列を覚えて欲しい!
こんにちは「つけらっとゲームス」プログラム担当のとちです。
これまでも何度か記事にしているのでご存知の方もいらっしゃると思いますが、ゲームを作る以外にも専門学校で非常勤講師をやっていまして、担当科目は「システム設計」です。
わたしが担当している時間は、設計部分(設計書、仕様書、アルゴリズム等々)の勉強がメインとなりますが、アルゴリズムの学習をするためには変数や配列を理解する必要があります。
今回は二次元配列(2次元配列)について書いてみようと思います。
同テーマの関連記事として以下の2つ記事を先に読むと、今回のお話も理解しやすいです。
まずは配列の復習!
いわゆる配列(一次元配列)は図で表現すると変数が横(または縦)に一列に並んでいるイメージです。
はい、この時点で何を言っているかわかりませんね…
ということで図で表現してみます。
配列 sNo には、とあるお店で扱っている商品番号が格納されています。
sNo[0]には商品番号 11 が、sNo[4]には商品番号 22 が入っています。
sNoの後の括弧に添字を書くことで配列の何番目を見ているのかを示しているんですね。
では、商品番号だけでなく価格の配列も追加してみます。
以後、配列の図は縦に並べます。
配列 sKakaku を追加しました。
配列 sNo の添字に対応しているものとします。
つまり、sNo[3] の商品の価格は sKakaku[3] に入っています。
こんな感じで配列を幾つか作っても良いのですが、扱うデータが複雑な場合はどうでしょうか?
データ例として…
少々複雑なデータを用意しました。商品番号、武器名、価格、攻撃力、そして5つある職業の装備可否が表になっています。
装備可否は「1」であれば装備可、「0」であれば不可とします。
このデータをこれまでの配列の考え方で図にすると以下のようになります。
ゲームに登場する職業が5つなのでこれで済んでいますが、もっと種類があったら、配列の名前を考えるだけで結構大変ですよね。
ゲームによって異なりますが、この他にも命中率とか、重量とか、必要スキルといったデータも存在するかも…
となると、もうちょっと楽に管理したいところです。
上図をよく見ると、配列 wName 以外は全て整数で管理されています。というわけで、この整数データを二次元配列化してみます!
※武器名は文字列なので配列 wName をそのまま使用します。
二次元配列化
データ例の整数部分を二次元配列 wTbl としました。
二次元配列は変数が縦横のグリッド状に並んでいるイメージになります。
これまでの「添字」は縦に並んでいるだけでしたが、横にも並んでいます。
横の添字の意味を以下に箇条書きします。
0.商品No
1.価格
2.攻撃力
3.装備可否・戦士
4.装備可否・盗賊
5.装備可否・神官
6.装備可否・魔術師
7.装備可否・狩人
例えば、商品No 21 の価格はいくらかを知りたい場合は…
wTbl[3,0] に商品No 21 が入っています。価格は横の添字の 1 なので、
wTbl[3,1] に入っている 90 となります。
フローチャートで表現すると
二次元配列が何なのか、なんとなく理解できましたよね?
では問題。280Gの武器を探して商品データを画面表示しましょう!
まずは処理の仕方(アルゴリズム)を思い浮かべましょう。
線形探索についてはこちらに詳しい記事があります。
線形探索で商品データを表示するフローチャートはこんな感じになります。
内容は同じですがループ記号を使用すると、こんな感じでしょうか、
おおまかな形はこれまでの配列(一次元配列)を使った場合と、そんなに違いがありません。では、どうして二次元配列が存在するのでしょうか?
それは…一次元配列化した表を再掲します。
そして一次元配列用に書き直すと違いがわかります。
赤文字が違うところです。
データ項目が多くなるほど配列名(変数名)を考える量が増え、更には記述量も増えるのがわかりますね。
プログラムコードで表現すると
フローチャートをプログラムコードにします。
今回はExcel(VBA)と、Unity(c#)でコード化しました。
まずはVBAで…
実際に線形探索している部分は20行にも満たないのですが、データを二次元配列に入れている準備部分が長いですね。
Option Explicit
Private Sub UserForm_Initialize()
Dim wTbl(10, 7) As Integer '【配列定義】データ各項目
Dim wName(10) As String '【配列定義】武器名
Dim wLbl(8) As String '【配列定義】表示メッセージ用ラベル
'---------------------------------------------------------------------
' 二次元配列 wTbl データ格納
'---------------------------------------------------------------------
wTbl(0, 0) = 11: wTbl(0, 1) = 50: wTbl(0, 2) = 2: wTbl(0, 3) = 1: wTbl(0, 4) = 1: wTbl(0, 5) = 0: wTbl(0, 6) = 1: wTbl(0, 7) = 1
wTbl(1, 0) = 12: wTbl(1, 1) = 150: wTbl(1, 2) = 6: wTbl(1, 3) = 1: wTbl(1, 4) = 1: wTbl(1, 5) = 0: wTbl(1, 6) = 0: wTbl(1, 7) = 0
wTbl(2, 0) = 13: wTbl(2, 1) = 280: wTbl(2, 2) = 9: wTbl(2, 3) = 1: wTbl(2, 4) = 0: wTbl(2, 5) = 0: wTbl(2, 6) = 0: wTbl(2, 7) = 0
wTbl(3, 0) = 21: wTbl(3, 1) = 90: wTbl(3, 2) = 9: wTbl(3, 3) = 1: wTbl(3, 4) = 1: wTbl(3, 5) = 0: wTbl(3, 6) = 0: wTbl(3, 7) = 1
wTbl(4, 0) = 22: wTbl(4, 1) = 360: wTbl(4, 2) = 12: wTbl(4, 3) = 1: wTbl(4, 4) = 0: wTbl(4, 5) = 0: wTbl(4, 6) = 0: wTbl(4, 7) = 0
wTbl(5, 0) = 31: wTbl(5, 1) = 100: wTbl(5, 2) = 4: wTbl(5, 3) = 1: wTbl(5, 4) = 0: wTbl(5, 5) = 0: wTbl(5, 6) = 0: wTbl(5, 7) = 1
wTbl(6, 0) = 32: wTbl(6, 1) = 240: wTbl(6, 2) = 8: wTbl(6, 3) = 1: wTbl(6, 4) = 0: wTbl(6, 5) = 0: wTbl(6, 6) = 0: wTbl(6, 7) = 0
wTbl(7, 0) = 41: wTbl(7, 1) = 70: wTbl(7, 2) = 2: wTbl(7, 3) = 1: wTbl(7, 4) = 0: wTbl(7, 5) = 1: wTbl(7, 6) = 0: wTbl(7, 7) = 0
wTbl(8, 0) = 42: wTbl(8, 1) = 260: wTbl(8, 2) = 6: wTbl(8, 3) = 1: wTbl(8, 4) = 0: wTbl(8, 5) = 1: wTbl(8, 6) = 0: wTbl(8, 7) = 0
wTbl(9, 0) = 51: wTbl(9, 1) = 120: wTbl(9, 2) = 4: wTbl(9, 3) = 0: wTbl(9, 4) = 0: wTbl(9, 5) = 0: wTbl(9, 6) = 0: wTbl(9, 7) = 1
wTbl(10, 0) = 52: wTbl(10, 1) = 280: wTbl(10, 2) = 7: wTbl(10, 3) = 0: wTbl(10, 4) = 0: wTbl(10, 5) = 0: wTbl(10, 6) = 0: wTbl(10, 7) = 1
'---------------------------------------------------------------------
' 配列 wName データ格納
'---------------------------------------------------------------------
wName(0) = "ダガー"
wName(1) = "ショートソード"
wName(2) = "ロングソード"
wName(3) = "ハンドアクス"
wName(4) = "バトルアクス"
wName(5) = "ジャベリン"
wName(6) = "スピア"
wName(7) = "メイス"
wName(8) = "フレイル"
wName(9) = "ショートボウ"
wName(10) = "ロングボウ"
'---------------------------------------------------------------------
' 配列 wLbl データ格納
'---------------------------------------------------------------------
wLbl(0) = "商品No."
wLbl(1) = " / "
wLbl(2) = "G / 攻撃 +"
wLbl(3) = " / 戦 "
wLbl(4) = " / 盗 "
wLbl(5) = " / 神 "
wLbl(6) = " / 魔 "
wLbl(7) = " / 狩 "
'---------------------------------------------------------------------
' 線形探索
'---------------------------------------------------------------------
Dim ChkGold As Integer '【変数定義】探索条件(価格)
Dim ChkLen0 As Integer '【 〃 】二次元配列 wTbl 縦の件数
Dim ChkLen1 As Integer '【 〃 】二次元配列 wTbl 横の件数
Dim ChkText As String '【 〃 】コンソールに表示するメッセージ
Dim ix As Integer '【 〃 】縦方向用添字
Dim ix2 As Integer '【 〃 】横方向用添字
'
ChkGold = 280 ' 探索条件(価格)は280G
ChkLen0 = UBound(wTbl, 1) ' wTbl 縦件数
ChkLen1 = UBound(wTbl, 2) ' wTbl 横件数
'
For ix = 0 To ChkLen0 ' ■ カードの全枚数分だけループ
If ChkGold = wTbl(ix, 1) Then ' ├◆探索条件(価格)と対象データの価格が同じ場合
ChkText = wLbl(0) & wTbl(ix, 0) & " / " + wName(ix) ' │├文字列編集 - 商品Noと武器名
For ix2 = 1 To ChkLen1 ' │├▼二次元配列 wTbl 横方向に全件ループ
ChkText = ChkText & wLbl(ix2) & wTbl(ix, ix2) ' ││└文字列編集 - データ各項目
Next ' │├ループ終端
MsgBox ChkText ' │└編集したメッセージの表示
End If ' │
Next ' └ループ終端
End Sub '
これを実行すると
引き続き、c#でコードを書くとどうなるでしょう?
using UnityEngine;
public class CArray2Test : MonoBehaviour
{
void Start()
{
int[,] wTbl = new int[11,8]; //【配列定義】データ各項目
string[] wName = new string[11]; //【配列定義】武器名
string[] wLbl = new string[8]; //【配列定義】表示メッセージ用ラベル
//---------------------------------------------------------------------
// 二次元配列 wTbl データ格納
//---------------------------------------------------------------------
wTbl[ 0,0] = 11; wTbl[ 0,1] = 50; wTbl[ 0,2] = 2; wTbl[ 0,3] = 1; wTbl[ 0,4] = 1; wTbl[ 0,5] = 0; wTbl[ 0,6] = 1; wTbl[ 0,7] = 1;
wTbl[ 1,0] = 12; wTbl[ 1,1] = 150; wTbl[ 1,2] = 6; wTbl[ 1,3] = 1; wTbl[ 1,4] = 1; wTbl[ 1,5] = 0; wTbl[ 1,6] = 0; wTbl[ 1,7] = 0;
wTbl[ 2,0] = 13; wTbl[ 2,1] = 280; wTbl[ 2,2] = 9; wTbl[ 2,3] = 1; wTbl[ 2,4] = 0; wTbl[ 2,5] = 0; wTbl[ 2,6] = 0; wTbl[ 2,7] = 0;
wTbl[ 3,0] = 21; wTbl[ 3,1] = 90; wTbl[ 3,2] = 9; wTbl[ 3,3] = 1; wTbl[ 3,4] = 1; wTbl[ 3,5] = 0; wTbl[ 3,6] = 0; wTbl[ 3,7] = 1;
wTbl[ 4,0] = 22; wTbl[ 4,1] = 360; wTbl[ 4,2] = 12; wTbl[ 4,3] = 1; wTbl[ 4,4] = 0; wTbl[ 4,5] = 0; wTbl[ 4,6] = 0; wTbl[ 4,7] = 0;
wTbl[ 5,0] = 31; wTbl[ 5,1] = 100; wTbl[ 5,2] = 4; wTbl[ 5,3] = 1; wTbl[ 5,4] = 0; wTbl[ 5,5] = 0; wTbl[ 5,6] = 0; wTbl[ 5,7] = 1;
wTbl[ 6,0] = 32; wTbl[ 6,1] = 240; wTbl[ 6,2] = 8; wTbl[ 6,3] = 1; wTbl[ 6,4] = 0; wTbl[ 6,5] = 0; wTbl[ 6,6] = 0; wTbl[ 6,7] = 0;
wTbl[ 7,0] = 41; wTbl[ 7,1] = 70; wTbl[ 7,2] = 2; wTbl[ 7,3] = 1; wTbl[ 7,4] = 0; wTbl[ 7,5] = 1; wTbl[ 7,6] = 0; wTbl[ 7,7] = 0;
wTbl[ 8,0] = 42; wTbl[ 8,1] = 260; wTbl[ 8,2] = 6; wTbl[ 8,3] = 1; wTbl[ 8,4] = 0; wTbl[ 8,5] = 1; wTbl[ 8,6] = 0; wTbl[ 8,7] = 0;
wTbl[ 9,0] = 51; wTbl[ 9,1] = 120; wTbl[ 9,2] = 4; wTbl[ 9,3] = 0; wTbl[ 9,4] = 0; wTbl[ 9,5] = 0; wTbl[ 9,6] = 0; wTbl[ 9,7] = 1;
wTbl[10,0] = 52; wTbl[10,1] = 280; wTbl[10,2] = 7; wTbl[10,3] = 0; wTbl[10,4] = 0; wTbl[10,5] = 0; wTbl[10,6] = 0; wTbl[10,7] = 1;
//---------------------------------------------------------------------
// 配列 wName データ格納
//---------------------------------------------------------------------
wName[ 0] = "ダガー";
wName[ 1] = "ショートソード";
wName[ 2] = "ロングソード";
wName[ 3] = "ハンドアクス";
wName[ 4] = "バトルアクス";
wName[ 5] = "ジャベリン";
wName[ 6] = "スピア";
wName[ 7] = "メイス";
wName[ 8] = "フレイル";
wName[ 9] = "ショートボウ";
wName[10] = "ロングボウ";
//---------------------------------------------------------------------
// 配列 wLbl データ格納
//---------------------------------------------------------------------
wLbl[0] = "商品No.";
wLbl[1] = " / ";
wLbl[2] = "G / 攻撃 +";
wLbl[3] = " / 戦 ";
wLbl[4] = " / 盗 ";
wLbl[5] = " / 神 ";
wLbl[6] = " / 魔 ";
wLbl[7] = " / 狩 ";
//---------------------------------------------------------------------
// 線形探索
//---------------------------------------------------------------------
int ChkGold = 280; //【変数定義】探索条件(価格)
int ChkLen0 = wTbl.GetLength(0); //【 〃 】二次元配列 wTbl 縦の件数
int ChkLen1 = wTbl.GetLength(1); //【 〃 】二次元配列 wTbl 横の件数
string ChkText = ""; //【 〃 】コンソールに表示するメッセージ
for(int ix = 0; ix < ChkLen0; ix++) // ■二次元配列 wTbl 縦方向に全件ループ
{ // │
if(ChkGold == wTbl[ix,1]) // └◆探索条件(価格)と対象データの価格が同じ場合
{ // │
ChkText = wLbl[0] + wTbl[ix,0]+ " / " + wName[ix]; // ├文字列編集 - 商品Noと武器名
for(int ix2 = 1; ix2 < ChkLen1; ix2++) // ├▼二次元配列 wTbl 横方向に全件ループ
{ // ││
ChkText += wLbl[ix2] + wTbl[ix,ix2]; // │└文字列編集 - データ各項目
} // │
Debug.Log(ChkText); // └編集した文字列をコンソールに表示
} //
} //
} //
} //
実行するとコンソールにメッセージが表示されます。
どちらも価格280Gの武器が2つ検出されました。
正しく動いているようですね!
おわりに
いかがでしたでしょうか? 「二次元配列」は理解できたでしょうか?
これ、説明されると難しくはないんですが、図や表がない状態だと混乱するんですよね。
資格試験の勉強で二次元配列を用いた問題が出てきた場合は、メモ書きでも何でもいいので自分で図表を書いてみるといいでしょう。
もうちょっと勉強したいなーって方にはコチラがオススメです。
これらの勉強に使えそうな関連記事は以下にまとめてます!(↓)