スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

ランダムキラーの解説

…えーと、タイトルはこれでいいのかな?

ランダムキラーというのは本家のマリオで"特定の面に一定のタイミングでかつ、出現する高さが毎回異なるキラー"のことです。
確か本家だと表面の5-3、裏面の1-3や6-3とかに出現しましたよね。今回はそれを解説させていただこうかと思います。

Screenshot_2015-03-06-01-56-16.png

以前「敵キャラクターの紹介」にて紹介したキラー(青)は、このゲームではランダムな位置に出現する敵キャラとして紹介しました。

ランダムといってもところかまわず出現して良いわけではなく、ちゃんと画面内に出現し、かつ1ブロック分の高さである32*32に固定するようにします。

public TimerHandler キラー出現レート = new TimerHandler(
3, true, new ITimerCallback() {
public void onTimePassed(TimerHandler pTimerHandler) {
SoundPlay.seBulletBill.stop();
SoundPlay.seBulletBill.play();
// キラーの出現ポイント
int appPoint = 0;
int r = (int)(Math.random() * 11);
switch (r) {
case 0:
appPoint = 384; // 下から3マス
break;
case 1:
appPoint = 352; // 下から4マス
break;
case 2:
appPoint = 320; // 下から5マス
break;

// 省略…

case 10:
appPoint = 64; // 下から13マス
break;
}
bulletBills.enemyBulletBillBlue = getBaseActivity().getResourceUtil()
.getAnimatedSprite("enemy_04_bullet_bill_blue.png", 1, 2);
bulletBills.enemyBulletBillBlue.setTag(
BulletBills.TAG_ENEMY_04_BULLET_BILL_BLUE);
// 画面右外に追加。y座標(appPoint)はランダム
bulletBills.enemyBulletBillBlue.setPosition(getBaseActivity().getEngine()
.getCamera().getWidth() + bulletBills.enemyBulletBillBlue
.getWidth(), appPoint);
bulletBills.enemyBulletBillBlue.animate(ani並);
attachChild(bulletBills.enemyBulletBillBlue);
bulletBills.enemyBulletBillBlue.setZIndex(レイヤー04敵キャラ用);
sortChildren();
}
});


そういえば、久々のAndEngineですね。
「public TimerHandler キラー出現レート = new TimerHandler」はAndEngineが持つ「ハンドラークラス」を継承しています。
(ハンドラーは過去の記事で紹介したような気がしますが…まあ、せっかくなのでここで詳しく解説します。)

すぐ下の行にある「3」という数字は、このクラスが持つ1つ目の引数で、ここで「何秒後にキラーが出現するのかをチェック」しています。
今回は3秒を指定しているのでひとまずこのコードからは「ゲームがスタートして3秒後に画面右端からキラーが出現する」ということになりますね!

続いてすぐ右隣にある第2引数の「true」(トゥルー)。
これはこのハンドラーが「繰り返し呼び出されるかどうかをチェック」しています。
「true」に設定すると一度ハンドラーが呼び出された後も3秒後(正確には第一引数で指定した値。今回は3を指定したため。)にもう一度同じハンドラーが繰り返し呼び出され、逆に「false」にすると1回きりの呼び出しハンドラーとなります。

ランダムキラーは繰り返し呼び出さないと全く効果がないので、今回は「true」に設定しキラーが画面左端へ隠れたら再度呼び出されるようにします。
…実際動画を見てみないとわからないと思いますが、2秒ちょっとでキラーが画面左端へ隠れるので、ちょっと長めの3秒に設定して少し余裕を持たせているというわけです。(たぶん本家のタイミングもこれくらいじゃないかな…。)

以上がタイムハンドラーの仕組みです。

…ちなみに上記のハンドラーコードはサンプル(NinjaRan)で敵キャラが出現するときに使用されていたやつをキラー用に改良したものです。

Screenshot_2014-06-30-02-03-27 (2)

「NinjaRan」では一定のタイミングで敵キャラが出現するのを攻撃でやっつけたり、ジャンプでかわしたりしながらハイスコアを目指すエンドレスタイプのゲームで、敵キャラを出現させるのに上記のハンドラーコードが使われておりました。
サンプルでは「1秒おき」に出現していますが、これをキラーでやるとさすがにうっとうしいので、約3間秒に一回呼び出されるように書き換え、キラーが完全に画面左へ消えてから新たなキラーが出現するように調整しています。

…これはローカルネタなので分かる人だけでいいです^^; 失礼しました。

~オマケ~(第一引数を0.1fに設定してみた。すごい弾幕の嵐ですね(笑))

Screenshot_2015-05-21-01-56-32.png

現在コースを製作中…。
リリースまでまだ期間がありますが、もうしばらくお待ちくださいね。
スポンサーサイト

レイヤーの設定

画面上にオブジェクトが増えてくるとレイヤーを設定したくなりますよね。
今回はAndengineがもつレイヤー機能(setZIndexメソッド)について解説致します。

Screenshot_2014-12-14-22-48-20.png

まずレイヤーとは"日本語で階層"という意味で、ゲームでは"オブジェクト同士の重なり順"を意味します。
2Dのゲームでは3Dと違い一点の視点からしかオブジェクトを見ることができないので、
オブジェクトには必ず優先順位を設定してあげないといけません。

構文だけじゃ分かりづらいと思うので画像を用意しました。ご覧ください。

Screenshot_2014-12-14-22-38-25 (2)

マリオと草が重なっているのが分かりますよね。
これは「マリオは草よりも手前に表示させなさい。」とsetZIndexメソッドを使って命令しているからです。
もし、この命令を忘れてしまうと、(コードにもよりますが…)スタート地点にある草はマリオより奥に表示されても
画面スクロールによって新たに生成された草ではマリオよりも手前にきてしまいます。(理由は後述。)

もちろんマリオと草だけではなく、ゲーム中に登場するオブジェクトには
みんなみ~んなレイヤーを設定してあげないといけないということもお忘れなく!

…ちょっとめんどくせーっと思ったかもしれませんが、
変数と組み合わせてやれば簡単に実装できるので是非やってみましょう!

まずは変数の宣言からです。

private int レイヤー01タイル用		= 1;
private int レイヤー02アイテム用 = 2;
private int レイヤー03敵キャラ用 = 3;
private int レイヤー04プレイヤー用 = 4;
private int レイヤー05アイコン用 = 5;
private int レイヤー06テキスト用 = 6;
private int レイヤー07デバッグ用 = 7;

(懲りずに日本語変数ですね^^;ハイ…)
これを見てもう分かったと思いますが、一つ一つのオブジェクトに優先順位を設定するのではなく、
タイルセットのレイヤーや敵キャラセットのレイヤーなど複数のジャンルに絞込むとかなり楽チンです。

setZIndexメソッドは中の設定値が高いほど手前に表示される仕組みで、この設定値で行くと…、
「(設定無し)<タイル<アイテム<敵キャラ<マリオ<…」の優先順位でオブジェクトが表示されるようになります。

「(設定無し)」というのはオブジェクトの生成時に"setZIndexメソッド"を付けなかったときの場合です。
この場合は自動的に一番最下層のレイヤーとして表示されます。
ちなみに、大体の2Dゲームではこの順番で成り立っているので、
オブジェクトの優先順位に迷ったらとりあえずはこちらの値を指定してみてくださいね。

ではいよいよsetZIndexメソッドの登場です。

playerMario = getBaseActivity().getResourceUtil()
.getAnimatedSprite("player_01_mario_set_1.png", 1, 30);
playerMario.setPosition(64, 480);
attachChild(playerMario);
// ここでレイヤーの設定をする
playerMario.setZIndex(レイヤー04プレイヤー用);
sortChildren();

コードの6行目にやっとsetZIndexメソッドが登場しましたね。
特に解説することはありませんが、setZIndexメソッドは必ずオブジェクトの画像を読み込んでから設定してくださいね。
それとsetZIndexメソッドを使う場合は7行目のsortChildrenメソッドも必要になるみたいなので、
この2つをセットで宣言するのをお忘れなく!

ちなみに、この"setZIndexメソッド"を使ってレイヤーを設定してやらないと、
いったいどのように表示されるのかというと…。
Andengineでは自動的に"一番最後に生成したオブジェクトが手前にくるよう"設定されているので、
新たにオブジェクトが生み出されるたびに手前に来るなどやりたい放題になります。
(「画面スクロールによって新たに生成された草ではマリオよりも手前される」というのはその為。)
よって、一つの画面内で遊ぶ簡単なゲームではいちいちレイヤーを設定する必要はありませんので、
時と場合によって使い分けてみてください。

解説は以上です!多忙な時期なので年内最後の更新かもしれませんが、またちょくちょくと見に来てくださいね。
それでは、来年もよろしくお願いします。

Screenshot_2014-12-14-22-50-59.png

データ画面の実装~完~

だいぶ前にちょこっとだけ紹介したような気がする"データ画面の実装"です。

前回の段階ではハイスコアを獲得した時にしかスコアは保存されませんでしたが、それだけだとちょっと物足りないので5位までのスコアを記録できるようにしました。

~前回の画面~

Screenshot_2014-02-11-08-16-13.png

↓↓↓↓↓

~実装後の画面~

Screenshot_2014-10-06-09-10-38.jpg

今回は少しコードを解説していきますね。

if (一位データ.getInstance(getBaseActivity()).get一位() == 0) {
一位テキスト = new Text(352, 96, font, "- - - - -",
20, new TextOptions(HorizontalAlign.LEFT),
getBaseActivity().getVertexBufferObjectManager());
一位テキスト.setColor(1, 1, 1);
一位テキスト.setScale(1);
attachChild(一位テキスト);
} else {
一位テキスト = new Text(352, 96, font, ""
+ 一位データ.getInstance(getBaseActivity()).get一位(),
20, new TextOptions(HorizontalAlign.LEFT),
getBaseActivity().getVertexBufferObjectManager());
一位テキスト.setColor(1, 1, 1);
一位テキスト.setScale(1);
attachChild(一位テキスト);
}

ここはゲームのタイトル画面全般を管理する"InitialSceneクラス"です。
普段は"MainSceneクラス"内のゲームのメイン部分のコードを解説していたため、タイトルシーンに関してはあまり触れることはありませんでした。
今回はここでAndengineが持つテキスト型の変数"一位テキスト"に文字データを代入してやります。
ちなみに、ここでifとelseに分岐させ、スコアの値が0のときにわざわざ"ハイフン(-)"を表示させているのは、単に"0"と表示させるのはカッコ悪いと思ったからです。

…それより重要なのがこの"一位データ"というクラス。
(1行目で呼び出してる"if (一位データ.getInstance(getBaseActivity()).get一位() == 0) {"の部分。)
ここでスコアの保存データを管理しています。(ちょっとのぞいてみましょう!)

public class 一位データ {

// 自身のインスタンス
private static 一位データ instance;
// シングルトン
public static synchronized 一位データ getInstance(Context context) {
if (instance == null) {
instance = new 一位データ(context);
}
return instance;
}

private static SharedPreferences settings;
private static SharedPreferences.Editor editor;

private 一位データ(Context context) {
settings = context.getSharedPreferences("shared_preference_1.0", 0);
editor = settings.edit();
}

public int get一位() {
return settings.getInt("一位", 0);
}

public void set一位(int value) {
editor.putInt("一位", value);
editor.commit();
}
}

…失礼ながら例のテキストのサンプル(元SPUtilクラス)をそのままコピペしたものです。
まだ良く分からないのでテキストを使って解説してしたいと思います。

例のテキストによると…「基本的なクラスですので解説は不要ですね。」



…^^;



…た、確かに基本的過ぎる内容ですね;
というわけで次に行きましょうか;

ここはゲームの大元となっている"MainSceneクラス"の内部です。

public void GameOver() {
ゲームオーバーかどうか = true;
// Sceneのタッチリスナーを解除
setOnSceneTouchListener(null);
// 距離を更新時は保存
if (現在のスコア > 一位データ.getInstance(getBaseActivity()).get一位()) {
// 四位の記録を五位に…
五位データ.getInstance(getBaseActivity()).set五位(
四位データ.getInstance(getBaseActivity()).get四位());
// 三位の記録を四位に…
四位データ.getInstance(getBaseActivity()).set四位(
三位データ.getInstance(getBaseActivity()).get三位());
// 二位の記録を三位に…
三位データ.getInstance(getBaseActivity()).set三位(
二位データ.getInstance(getBaseActivity()).get二位());
// 一位の記録を二位に…
二位データ.getInstance(getBaseActivity()).set二位(
一位データ.getInstance(getBaseActivity()).get一位());
// ここで一位の距離を保存
一位データ.getInstance(getBaseActivity()).set一位(現在のスコア);
} else if (現在のスコア > 二位データ.getInstance(getBaseActivity()).get二位()) {
// 四位の記録を五位に…
五位データ.getInstance(getBaseActivity()).set五位(
四位データ.getInstance(getBaseActivity()).get四位());
// 三位の記録を四位に…
四位データ.getInstance(getBaseActivity()).set四位(
三位データ.getInstance(getBaseActivity()).get三位());
// 二位の記録を三位に…
三位データ.getInstance(getBaseActivity()).set三位(
二位データ.getInstance(getBaseActivity()).get二位());
// ここで二位の距離を保存
二位データ.getInstance(getBaseActivity()).set二位(現在のスコア);
} else if (現在のスコア > 三位データ.getInstance(getBaseActivity()).get三位()) {
// 四位の記録を五位に…
五位データ.getInstance(getBaseActivity()).set五位(
四位データ.getInstance(getBaseActivity()).get四位());
// 三位の記録を四位に…
四位データ.getInstance(getBaseActivity()).set四位(
三位データ.getInstance(getBaseActivity()).get三位());
// ここで三位の距離を保存
三位データ.getInstance(getBaseActivity()).set三位(現在のスコア);
} else if (現在のスコア > 四位データ.getInstance(getBaseActivity()).get四位()) {
// 四位の記録を五位に…
五位データ.getInstance(getBaseActivity()).set五位(
四位データ.getInstance(getBaseActivity()).get四位());
// ここで四位の距離を保存
四位データ.getInstance(getBaseActivity()).set四位(現在のスコア);
} else if (現在のスコア > 五位データ.getInstance(getBaseActivity()).get五位()) {
// ここで五位の距離を保存
五位データ.getInstance(getBaseActivity()).set五位(現在のスコア);
}
// 解説範囲外のため省略…
}

1行目にある"GameOver()メソッド"は文字通り、プレイヤーがゲームオーバーになったときに呼び出されるメソッドです。
…つまり、ゲームが終了したタイミングでスコアを保存しているというわけですね!

では、6行目から見ていってください。
"if (現在のスコア > 一位データ.getInstance(getBaseActivity()).get一位()) {"とありますがこれはゲーム内で獲得したスコアがハイスコアよりも大きいかを調べています。

既に登録済みのハイスコアよりもゲーム中で獲得したスコアの方が大きい場合は…、
20行目の処理"一位データ.getInstance(getBaseActivity()).set一位(現在のスコア);"で先ほどの"一位データクラス"に獲得したスコア値を代入してあげます。
もちろんスコアが登録されていない場合も同じですね。

あ…!待ってください!
それだけだと、ただスコアが保存されるだけの処理になってしまい、以前に獲得したスコアはそのまま上書きされ消えてしまうので7行目~18行目の処理を行い、古いハイスコアは下位のランクに落としてあげるようにします。

これで一件落着ですね!

壊れるブロックの実装

今回はマリオシリーズに登場するレンガブロック「tile_33_brick_block_small.png」を実装してみました。

このゲームには通常のレンガブロックと、一回りサイズの大きいデカレンガブロックの2種類が登場します。

Screenshot_2014-07-03-20-33-40.png

コードを解説する前に本家マリオに登場するレンガブロックの性質をおさらいすると…。

1.ブロック全体に接触判定があり、マリオが上に乗っかることができる。
2.スーパーマリオ、もしくはファイアマリオの状態で下から触れると割ることができる。
3.中には割れない"当たりのブロック"もあり、その場合はコインなどのアイテムが隠されている。

大まかにこの3つがマリオシリーズ共通のレンガブロックの性質ですが、
3番の「中には割れない"当たりのブロック"もあり、その場合はコインなどのアイテムが隠されている。」は"諸事情"により今回はスルーします。
(最も本家とは少し違う方法でアイテムを隠すようにはしたいですが…。)

ですので、今回の実装は1番の「ブロックの接触判定」と、2番の「下から触れた際の割れる挙動」ですね。

まず、レンガブロックの下…つまりマリオの頭部分の赤いラインにレンガブロックが触れたときの処理を見てみましょう!

public void tileAndPlayerAboveContact() {
if (((Sprite) getChildByIndex(タイル)).collidesWith(
contactLines.playerContactLineAbove)) {
float 判定遊び = 0;
float 接触距離 = Math.abs((getChildByIndex(タイル)
.getY() + ((Sprite) getChildByIndex(タイル))
.getHeight() / 2) - (contactLines.playerContactLineAbove
.getY() + (contactLines.playerContactLineAbove
.getHeight() / 2)));
// 15.地面ブロック(短い)
if (getChildByIndex(タイル).getTag() ==
GroundBlocks.TAG_TILE_15_GROUND_BLOCK_SHORT
// ※ブロックはジャンプ時のみ判定
&& プレイヤーのダッシュフラグ == false && プレイヤーのジャンプフラグ == true
&& プレイヤーの上昇移動フラグ == true) {
判定遊び = 0;
if (接触距離 < contactLines.playerContactLineAbove.getHeight()
/ 2 + ((Sprite) getChildByIndex(タイル)).getHeight()
/ 2 - 判定遊び) {
tileAndContactLineAboveDecision();
}

// 省略…

// 33.レンガブロック(小さい)
} else if (getChildByIndex(タイル).getTag() ==
BrickBlocks.TAG_TILE_33_BRICK_BLOCK_SMALL
// ※ブロックはジャンプ時のみ判定
&& プレイヤーのダッシュフラグ == false && プレイヤーのジャンプフラグ == true
&& プレイヤーの上昇移動フラグ == true) {
// ライフが2以上のとき、ブロックを破壊できる
if (現在のライフ >= 2) {
SoundPlay.seBrickBlock.stop();
SoundPlay.seBrickBlock.play();
収納用タイル.add((Sprite) getChildByIndex(タイル));
}
判定遊び = 0;
if (接触距離 < contactLines.playerContactLineAbove.getHeight()
/ 2 + ((Sprite) getChildByIndex(タイル)).getHeight()
/ 2 - 判定遊び &&
// ライフが1のときは落下判定
現在のライフ == 1) {
tileAndContactLineAboveDecision();
}

// 省略…

// 68.はり(下)
} else if (getChildByIndex(タイル).getTag() ==
Needles.TAG_TILE_68_NEEDLE_BELOW
// ※ブロックはジャンプ時のみ判定
&& プレイヤーのダッシュフラグ == false && プレイヤーのジャンプフラグ == true
&& プレイヤーの上昇移動フラグ == true) {
判定遊び = 0;
if (接触距離 < contactLines.playerContactLineAbove.getHeight()
/ 2 + ((Sprite) getChildByIndex(タイル)).getHeight()
/ 2 - 判定遊び) {
tileAndContactLineAboveDecision();
if (状態フラグ点滅 == false) {
// はりに触れた場合、敵キャラのこうげきと同じ処理
enemyAndPlayerNormalDecision();
}
}
}
}
}

tileAndPlayerAboveContact()メソッドはマリオの頭上判定…つまり、マリオの上に付いている赤いラインとメソッド内のオブジェクトが触れたとき、指定された処理内容が実行されます。

では、「// 33.レンガブロック(小さい)」に注目してください。
ここで注意しなければいけないことは、スーパーマリオ(と、ファイアマリオ)の状態で触れるとレンガブロックを割れることですよね。
「現在のライフ」という変数は以前解説しましたが、マリオの状態を管理している変数です。

1 == ならマリオ(チビ)
2 == ならスーパーマリオ
3 == ならファイアマリオ

ここでは「現在のライフ >= 2」つまり、スーパーマリオの状態でレンガブロックに触れた場合はレンガブロックが割れるようにしていますが、ここでも本家のマリオとは少し違う点があります。
本家のマリオはレンガブロックをジャンプで割った後、即座に地面へ落下しますが、このゲームはレンガブロックをジャンプで割っても画面から指を離すまで(もしくは、ジャンプ力が限界値に達するまで)マリオは落下しません。

Screenshot_2014-07-05-15-23-15.png

↓↓↓↓↓↓↓↓↓↓

Screenshot_2014-07-05-15-23-53.png

自動スクロールゲームではプレイヤーは停止できませんよね。
ですからこの場合一つ一つ割っていくより一気に破壊できた方が効率が良いのです。
無理に本家マリオを再現するより、遊びやすさを優先的に作っていった方が良いですもの。

…以上でレンガブロックの解説を終わります…っえ?マリオが横から触れた時の解説がまだだって?

それは以前解説した当たり判定"右"の実装で解説済みですよ!
この処理をメソッド化し、当たり判定のある全てのブロックは共通でこのメソッドを呼び出しているというわけです。

コースデータとオブジェクト画像の読み込み

そういえば、コースデータの読み込みについて詳しく解説していなかったので(すぷりくとも導入したことですし…)少し解説していこうかと思います。

system_03_title_back (2)

さて、このゲームに出てくるタイルやアイテム、敵キャラ(以下オブジェクトで統一)はすべて"2次元配列"で管理していることは過去記事で簡単に説明しましたよね。
今回はハテナボックスを画面に表示させるところまで解説していこうかと思いますがその前に一言、このプログラムはあまり効率的にはよくないらしいので(詳しい方に見てもらったらもっと手っ取り早い方法があるみたいです。)あくまでも参考程度に見ていってください。

1.ではまず、オブジェクトのタグとスプライト型の変数を宣言します。

public static final int TAG_TILE_27_QUESTION_BLOCK_SHORT_COIN_YELLOW = 27;
public AnimatedSprite tileQuestionBlockShortCoinYellow;

1行目はタグというものでstatic変数として宣言しています。
タグは簡単に言うとオブジェクトの性質を決めるもので、このタグのオブジェクトは下からマリオが接触するとコインを出すというような命令を隠し持っているといった感じです。
2行目はアニメスプライトの宣言です。ここはもう解説不要ですよね。

2.次にハテナブロックメソッドを宣言する。

tile_27_question_block_short_coin_yellow.png

// 27.ハテナブロック(短い/きいろコイン)
public void tileQuestionBlockShortCoinYellow() {
    questionBlockShorts.tileQuestionBlockShortCoinYellow = getBaseActivity().getResourceUtil()
        .getAnimatedSprite("tile_27_question_block_short_coin_yellow.png", 1, 6);
    questionBlockShorts.tileQuestionBlockShortCoinYellow.setTag(
        QuestionBlockShorts.TAG_TILE_27_QUESTION_BLOCK_SHORT_COIN_YELLOW);
    if (コース初期化中 == true) {
        questionBlockShorts.tileQuestionBlockShortCoinYellow.setPosition(ヨコ * 32, タテ * 32);
    } else {
        questionBlockShorts.tileQuestionBlockShortCoinYellow.setPosition((ヨコ * 0) + 800, タテ * 32);
    }
    questionBlockShorts.tileQuestionBlockShortCoinYellow.animate(
        new long[] { 180, 180, 180, 180, 180, }, 0, 4, true);
    attachChild(questionBlockShorts.tileQuestionBlockShortCoinYellow);
    questionBlockShorts.tileQuestionBlockShortCoinYellow.setZIndex(レイヤー01タイル用);
    sortChildren();
}

5行目と6行目にsetTag()メソッドがあるのをお気づきでしょうか。
ここで先ほど宣言したアニメスプライトにタグをセットしています。

8行目はスプライトのセット位置。
わざわざ"ヨコ * 32, タテ * 32"の位置に指定しているのは画像のサイズがタテとヨコのサイズが32ピクセルのため、もっというと1ブロックまでの間は32ドット分離れているということになります。

3.オブジェクトデータのIDを指定する。

// スプライトの割り振りメソッド
public void courseDataAssign() {
    // 1.草パーツ(1/3)
    if (course1.courseData[ヨコ][タテ] == 1) {
        tileGrassPart1();
    // 2.草パーツ(2/3)
    } else if (course1.courseData[タテ][ヨコ] == 2) {
        tileGrassPart2();
    // 3.草パーツ(3/3)
    } else if (course1.courseData[タテ][ヨコ] == 3) {
        tileGrassPart3();
    // 4.山パーツ(1/6)
    } else if (course1.courseData[タテ][ヨコ] == 4) {
        tileMountPart1();
    
    // (省略…)
    
    // 26.隠しブロック(ファイアフラワー(小さい))
    } else if (course1.courseData[ヨコ][タテ] == 26) {
        tileHiddenBlockFireFlowerSmall();
    // 27.ハテナブロック(短い/きいろコイン)
    } else if (course1.courseData[ヨコ][タテ] == 27) {
        tileQuestionBlockShortCoinYellow();
    // 28.ハテナブロック(短い/あかコイン)
    } else if (course1.courseData[ヨコ][タテ] == 28) {
        tileQuestionBlockShortCoinRed();
    
    // (省略…)
    
    // 94.バルーン
    } else if (course1.courseData[タテ][ヨコ] == 94) {
        enemyBalloon();
    }
}

4行目でようやく2次元配列が登場しましたね。
ここではオブジェクトを2次元配列にセットするためにそれぞれID番号を振り分けております。
2次元配列の番号が1番、2番、3番ならそれぞれ草パーツ「tile_01_grass_part_1.pngtile_02_grass_part_2.pngtile_03_grass_part_3.png」が読み込まれ、
27番ならハテナブロック「tile_27_question_block_short_coin_yellow (2)」が読み込まれるようにそれぞれ番号で指定してやってるというわけです。

さらに正確に言うと、12行目に先ほど宣言したtileQuestionBlockShortCoinYellow()メソッドがあり、二次元配列の番号27が読み込まれると上記メソッドが呼び出され、オブジェクトの画像データやタグのセッティングなど行っているわけです。

ではここで2次元配列の中身を見てみましょう!

public int[][] courseData = {
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    
    { 0, 0, 6, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 4, 5, 8, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 4, 5, 7, 9, 8,     0, 0, 0, 0, 0,     0, 1, 2, 2, 2,     3, 4, 5, 8, 0,     0, 0, 0, 1, 2,},
    {18, 0, 0, 0, 0,    18, 0, 0, 0, 0,    18, 0, 0, 0, 0,    18, 0, 0, 0, 0,    18, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
};

この配列データを画面に表示してみる。

system_03_title_back (3)

ちなみに雲は背景としてスクロールさせており、配列で管理していません。
ここで配列番号の27(ハテナボックス)を追加してみます。

public int[][] courseData = {
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0,27, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0,27, 0, 0,     0, 0, 0, 0, 0,     0, 0,27, 0, 0,     0, 0, 0, 0, 0,},
    
    { 0, 0, 6, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
    { 0, 4, 5, 8, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 6, 0, 0,     0, 0, 0, 0, 0,},
    { 4, 5, 7, 9, 8,     0, 0, 0, 0, 0,     0, 1, 2, 2, 2,     3, 4, 5, 8, 0,     0, 0, 0, 1, 2,},
    {18, 0, 0, 0, 0,    18, 0, 0, 0, 0,    18, 0, 0, 0, 0,    18, 0, 0, 0, 0,    18, 0, 0, 0, 0,},
    { 0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,     0, 0, 0, 0, 0,},
};

配列の27番と画像を見比べてみてください。

system_03_title_back (4)

それでこのcourseData配列をどこで呼び出しているのかというと…ゲーム開始時にinit()メソッドの中から参照しています。

4.2次元配列を読み込む。

// ゲームの初期化
public void init() {
    // 最初に背景画像を追加する
    attachChild(getBaseActivity().getResourceUtil().getSprite("back_01_overworld.png"));
    
    // (省略…)
    
    // コースの読み込み
    // 読み込みフラグ:ON
    コース初期化中 = true;
    // コースデータ上下の読み込み。(初回時)
    for (タテ = 0; タテ < 15; タテ++) {
        // コースデータ左右の読み込み。
        for (ヨコ = 0; ヨコ < 26; ヨコ++) {
            // スプライトの割り振り
            courseDataAssign();
        }
    }
    
    // (省略…)
    
}


これで画面上にオブジェクトが表示されるようになるはずです。
これを常時読み込みたい(スクロールさせたい)場合、上記のコードをハンドラーに中に記述する必要がありますが、説明すると長くなるので今回はここまでにします。

プロフィール

岡本 賢治

Author:岡本 賢治
 
☆☆☆☆☆☆☆☆☆☆☆☆
 
Android専用アクションゲーム
「スーパーけんじラン」好評配信中!
 
↓↓↓↓↓↓↓↓↓↓↓↓

 
・公式サイトはこちらから
http://okamotodo.jimdo.com/

最新トラックバック

アクセスカウンター

オンラインカウンター

現在の閲覧者数:

検索フォーム

ブロとも申請フォーム

QRコード

QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。