Browsing: Andengine

シューティングゲーム開発_Vol3



2機同時操作のシューティングゲーム

前回までは
・機体表示
・機体から弾発射
・機体操作のコントローラ
です。

今回は
・機体からの弾一旦停止ボタン
・敵を1機表示
・当たり判定
をやっていきたいと思います。

機体からの弾一旦停止ボタン

この機能がなんで必要かというと
今回のシューティングは味方の攻撃でも当たったらアウトにしようとしているので
自動で弾の撃ちっぱなしだと不便だと思うので実装します。
[java]
MainScene.java

private boolean weponLeftFlg = true;
private boolean weponRightFlg = true;

@Override
public void init(){

・・・省略

// 左側の弾停止ボタン ※1
weponLeftButton = ResourceUtil
.getInstance(getBaseActivity())
.getButtonSprite("weponOnButton.png", "weponOnButton.png");
weponLeftButton.setPosition(75 – weponLeftButton.getWidth() / 2, 240);
attachChild(weponLeftButton);
registerTouchArea(weponLeftButton);
weponLeftButton.setOnClickListener(new ButtonSprite.OnClickListener() {
public void onClick(ButtonSprite pButtonSprite,
float pTouchAreaLocalX, float pTouchAreaLocalY) {
if(weponLeftFlg) {
weponLeftButton.detachSelf();
weponLeftButton.dispose();
weponLeftButton = null;

weponLeftButton = ResourceUtil
.getInstance(getBaseActivity())
.getButtonSprite("weponOffButton.png", "weponOffButton.png");
weponLeftButton.setPosition(75 – weponLeftButton.getWidth() / 2, 240);
attachChild(weponLeftButton);
registerTouchArea(weponLeftButton);

weponLeftFlg = false;
}else{
weponLeftButton.detachSelf();
weponLeftButton.dispose();
weponLeftButton = null;

weponLeftButton = ResourceUtil
.getInstance(getBaseActivity())
.getButtonSprite("weponOnButton.png", "weponOnButton.png");
weponLeftButton.setPosition(75 – weponLeftButton.getWidth() / 2, 240);
attachChild(weponLeftButton);
registerTouchArea(weponLeftButton);

weponLeftFlg = true;
}
}
});

// 右側の弾停止ボタン
weponRightButton = ResourceUtil
.getInstance(getBaseActivity())
.getButtonSprite("weponOnButton.png", "weponOffButton.png");
weponRightButton.setPosition(725 – weponRightButton.getWidth() / 2, 240);
attachChild(weponRightButton);
registerTouchArea(weponRightButton);
weponRightButton.setOnClickListener(new ButtonSprite.OnClickListener() {
public void onClick(ButtonSprite pButtonSprite,
float pTouchAreaLocalX, float pTouchAreaLocalY) {
if(weponRightFlg) {
weponRightButton.detachSelf();
weponRightButton.dispose();
weponRightButton = null;

weponRightButton = ResourceUtil
.getInstance(getBaseActivity())
.getButtonSprite("weponOffButton.png", "weponOffButton.png");
weponRightButton.setPosition(725 – weponRightButton.getWidth() / 2, 240);
attachChild(weponRightButton);
registerTouchArea(weponRightButton);

weponRightFlg = false;
}else{
weponRightButton.detachSelf();
weponRightButton.dispose();
weponRightButton = null;

weponRightButton = ResourceUtil
.getInstance(getBaseActivity())
.getButtonSprite("weponOnButton.png", "weponOnButton.png");
weponRightButton.setPosition(725 – weponRightButton.getWidth() / 2, 240);
attachChild(weponRightButton);
registerTouchArea(weponRightButton);

weponRightFlg = true;
}
}
});
}

// ※2
private void weponMainLeftSprite(){
mainLeftSprite.registerEntityModifier(new SequenceEntityModifier(
new DelayModifier(0.5f, new IEntityModifier.IEntityModifierListener() {
@Override
public void onModifierFinished(
IModifier<IEntity> pModifier, IEntity pItem) {

if(!weponLeftFlg){
weponMainLeftSprite();
return;
}
final Rectangle rect = new Rectangle(mainLeftSprite.getX() + mainLeftSprite.getWidth(), mainLeftSprite.getY(), 5, 5, getBaseActivity().getVertexBufferObjectManager());
attachChild(rect);

rect.setColor(CommonUtil.convertRGB(200), CommonUtil.convertRGB(200), CommonUtil.convertRGB(200));
rect.setPosition(mainLeftSprite.getX() + mainLeftSprite.getWidth() / 2, mainLeftSprite.getY());
rect.setZIndex(10);

weponLeftList.add(rect);

weponMainLeftSprite();
return;
}

@Override
public void onModifierStarted(IModifier<IEntity> pModifier,
IEntity pItem) {
}
})));
}

private void weponMainRightSprite(){
mainRightSprite.registerEntityModifier(new SequenceEntityModifier(
new DelayModifier(0.5f, new IEntityModifier.IEntityModifierListener() {
@Override
public void onModifierFinished(
IModifier<IEntity> pModifier, IEntity pItem) {
if(!weponRightFlg){
weponMainRightSprite();
return;
}

final Rectangle rect = new Rectangle(mainRightSprite.getX() + mainRightSprite.getWidth(), mainRightSprite.getY(), 5, 5, getBaseActivity().getVertexBufferObjectManager());
attachChild(rect);

rect.setColor(CommonUtil.convertRGB(200),CommonUtil. convertRGB(200), CommonUtil.convertRGB(200));
rect.setPosition(mainRightSprite.getX() + mainRightSprite.getWidth() / 2, mainRightSprite.getY());
rect.setZIndex(10);

weponRightList.add(rect);

weponMainRightSprite();
return;
}

@Override
public void onModifierStarted(IModifier<IEntity> pModifier,
IEntity pItem) {
}
})));
}

[/java]

まず※1ですが
停止ボタンの画像を読み込んで、画像がタッチされた場合に「ON」「OFF」を切り替えるように設定します。
下記で画像のタッチを検知して、フラグを見て画像を切り替えます。
[java]
weponLeftButton.setOnClickListener(new ButtonSprite.OnClickListener() {
public void onClick(ButtonSprite pButtonSprite,
float pTouchAreaLocalX, float pTouchAreaLocalY) {
[/java]
(右も同様)

次に※2で
弾を発射するために再帰的に呼び出している処理で
フラグを見て、ボタンの状態が「ON」か「OFF」かを判断して
「OFF」の場合には、弾を発射しない分岐に入れてスキップして再帰的に呼び出します。
[java]
if(!weponLeftFlg){
weponMainLeftSprite();
return;
}
[/java]

これだけ。これで弾が発射されたままになったり、停止したりします。

ついでに必殺のボタンだけ配置しちゃいましょう。
[java]
fireLeftButton = ResourceUtil
.getInstance(getBaseActivity()).getSprite("fireButton.png");
fireLeftButton.setPosition(75 – fireLeftButton.getWidth() / 2, 50);
attachChild(fireLeftButton);
registerTouchArea(fireLeftButton);

fireRightButton = ResourceUtil
.getInstance(getBaseActivity()).getSprite("fireButton.png");
fireRightButton.setPosition(725 – fireRightButton.getWidth() / 2, 50);
attachChild(fireRightButton);
registerTouchArea(fireRightButton);
[/java]

敵機の表示

次はいよいよ敵を表示します!
[java]
MainScene.java

private List<Enemy> enemyList = new ArrayList<Enemy>();

@Override
public void init(){

・・・省略

// 敵をセット
entryEnemy(stage);
}

// ※3
private void entryEnemy(int stage){
TimerHandler timerHandler = new TimerHandler(5, false, new ITimerCallback() {
public void onTimePassed(TimerHandler pTimerHandler) {
Sprite enemy1 = ResourceUtil.getInstance(getBaseActivity()).getSprite("enemy1.png");
attachChild(enemy1);

enemyList.add(new Enemy(enemy1));
}
});
registerUpdateHandler(timerHandler);

}

Enemy.java

private Sprite enemySprite;

public Enemy(Sprite sprite){
this.enemySprite = sprite;
setMoveType(0);
}

// ※4
public void setMoveType(int type){
enemySprite.setColor(CommonUtil.convertRGB(200), CommonUtil.convertRGB(200), CommonUtil.convertRGB(200));
enemySprite.setPosition(400, -50);
enemySprite.setZIndex(10);

enemySprite.registerEntityModifier(new SequenceEntityModifier(
new MoveModifier(2f, enemySprite.getX(),enemySprite.getX(), enemySprite.getY(),50)
,new DelayModifier(1f)
,new LoopEntityModifier(
new SequenceEntityModifier(
new MoveByModifier(1f, 100, 0)
,new MoveByModifier(1f, – 100, 0)
,new MoveByModifier(1f, – 100, 0)
,new MoveByModifier(1f, 100, 0)))
));
}
}

[/java]

敵の表示はinit処理で呼びだして
登場する敵の情報は最初に全て定義しちゃおうと思います。
※3、ここではゲーム開始後5秒後に敵が登場するようにしています。
敵の画像のSpriteを生成してEnemyクラスのコントラクタに渡します。
(今後敵を追加するときは、ここに何秒後に敵を追加するか指定します。)

※4、EnemyクラスでSpriteを保持します。
setMoveTypeメソッドで敵の移動を制御します。
・new SequenceEntityModifier(
 以降のModifierを順番に実行する
・new MoveModifier(2f, enemySprite.getX(),enemySprite.getX(), enemySprite.getY(),50)
 対象のSpriteを2秒かけて、現時点X軸に0、Y軸に+50移動する。
・,new DelayModifier(1f)
 1秒待機
・,new LoopEntityModifier(
 次のModifierを繰り返し処理をする。
・new SequenceEntityModifier(
 以降のModifierを順番に実行する
・new MoveByModifier(1f, 100, 0)
 対象のSpriteを1秒かけて、現時点X軸に+100、Y軸に0移動する。
・,new MoveByModifier(1f, – 100, 0)
 対象のSpriteを1秒かけて、現時点X軸に-100、Y軸に0移動する。
・,new MoveByModifier(1f, – 100, 0)
 対象のSpriteを1秒かけて、現時点X軸に-100、Y軸に0移動する。
・,new MoveByModifier(1f, 100, 0)))
 対象のSpriteを1秒かけて、現時点X軸に+100、Y軸に0移動する。

上記の処理で、上から出てきて、ずっと右左に移動する動きをします。
setMoveTypeメソッドの引数にtypeがあるのは、いろんな敵の動きを実装して引数によって動きを変えたいからです。
今後この引数は使用する予定です。

あたり判定

最後に、当たり判定の実装
(今回は判定のみで、クラッシュ時のエフェクトは実装しません。)

[java]
MainScene.java

private Sound enemyCrashSound;
private Sound mainCrashSound;

※音の初期化
@Override
public void prepareSoundAndMusic() {
// 効果音をロード
try {
enemyCrashSound = SoundFactory.createSoundFromAsset(
getBaseActivity().getSoundManager(), getBaseActivity(),
"enemyCrash.wav");
mainCrashSound = SoundFactory.createSoundFromAsset(
getBaseActivity().getSoundManager(), getBaseActivity(),
"mainCrash.mp3");
} catch (IOException e) {
e.printStackTrace();
}
}

TimerHandler timerHandler = new TimerHandler(1 / 60f, true, new ITimerCallback() {
public void onTimePassed(TimerHandler pTimerHandler) {
List<Rectangle> removeLeftWepon = new ArrayList<Rectangle>();
for(Rectangle rect : weponLeftList){
if(rect.getY() < -10){
removeLeftWepon.add(rect);
rect.detachSelf();
rect.dispose();
}else {
rect.setPosition(rect.getX(), rect.getY() – 3);
// ※5
if(isEnemyCrash(rect, true)){
removeLeftWepon.add(rect);
rect.detachSelf();
rect.dispose();
}
}
}
weponLeftList.removeAll(removeLeftWepon);

List<Rectangle> removeRightWepon = new ArrayList<Rectangle>();
for(Rectangle rect : weponRightList){
if(rect.getY() < -10){
removeRightWepon.add(rect);
rect.detachSelf();
rect.dispose();
}else {
rect.setPosition(rect.getX(), rect.getY() – 3);
if (isEnemyCrash(rect, false)){

}
}
}
weponRightList.removeAll(removeRightWepon);

// ※6
for(Sprite enemy : enemyList){
if(enemy.collidesWith(mainLeftSprite)){
crashMain(mainLeftSprite);
}
if(enemy.collidesWith(mainRightSprite)){
crashMain(mainRightSprite);
}
}

if(mainLeftSprite.collidesWith(mainRightSprite)){
crashMain(mainLeftSprite);
crashMain(mainRightSprite);
}
}
});

// ※7
private boolean isEnemyCrash(Rectangle rect, boolean isMainLeftSprite){

for(Sprite enemy : enemyList){
if(enemy.collidesWith(rect)){
enemyCrashSound.play();
return true;
}
}
return false;
}

// ※8
private void crashMain(Sprite mainSprite){
mainCrashSound.play();
}
[/java]

※5、※7で機体からの、弾と敵の当たり判定をしてます。
弾Spriteと敵Spriteを「collidesWith」で判定
trueの場合はSprite同士が重なっているので衝突と判断しします。
今回までは、衝突音と当たった弾の消去をしてます。

※6、※8で機体と敵、機体と機体の当たり判定をしてます。
同じように判定したいSprite同士で「collidesWith」処理をして
trueの場合に衝突音を実装してます。

静止画じゃ分かりづらいですが、現在のゲーム画像はこんな感じです。
↓↓↓
vol5_3

vol5_4

敵がでてきたことで、少しだけシューティングっぽくなりましたね。

次回以降は
・敵の攻撃実装
・衝突時のエフェクト
・敵の追加
をしたいと思います。

ちょっと処理が冗長だったり、うまく分割できていないように感じますが
それは追々余裕があればやりたいと思います。

でわでわ、つづきをお楽しみに。

{ Add a Comment }

ブロック崩し&シューティングゲーム案



私は最近スマホアプリ開発に興味をもっていて
会社の勉強会や家でこっそりアプリを作っているわけですが。

よけろ!ききいっぱつ!:会社の勉強会で開発したアプリ
よけろ!かんいっぱつ!:家で開発したアプリ

上記二つは既に開発が完了しリリースもしています。

んで、次に「自分で2機操作するシューティング」を”会社の勉強会”で開発しようと考えています。

私は飽きっぽいので、一つのアプリだけの開発に時間がかかっていると
モチベーションが下がってきてしまうので
会社の勉強会のアプリと家で作るアプリの2本立てで進めて行こうと思ってます。

次に開発する”会社の勉強会”のアプリは決まっているので
家で作るアプリは何にしようかと考えていました。

考えた結果、、

「ブロック崩しとシューティングを混ぜたゲームにしよう!」

と思いついたので、
「自分で2機操作するシューティング」
と平行して
「ブロック崩し&シューティング」
を進めて行こうと思います!

ざっくりとした仕様ですが下記を考えています。

——
仮タイトル:バトルサッカープレイヤー
仮仕様:
・横画面
・ゲームテーマ:サッカー、シューティング、ブロック崩し
・サッカー選手を操作する。
 操作方法は、タップした時点とドラッグしている箇所の位置関係から、サッカー選手を上下左右に操作。
・敵は、野球・バスケ・テニスなどのプレイヤー(上部に出現)
 インベーダーのように横移動しながら、ボールで攻撃してくる。
・サッカー選手はサッカーボールを操作する。
 サッカーボールをブロック崩しのように跳ね返して操作
・アイテムの実装
 ボールや移動スピードがあがったり、大きくなったり、ボールが増えたりする。ライフのアイテムなど。
・必殺ボタン
・敵を全て倒したら後からどんどんでてくる。
——

基本的な仕様はこんな感じで、割と今持っている知識とAndengineを使用すればできそうかなと考えています。

不安要素は
・キャラデザイン
・サッカー選手にサッカーボールが当たって跳ね返す際の角度

「キャラデザイン」は今は自分でデザインできないので、フリー素材から探そうと思いますが
アニメーションになっているものがほぼ無いのと、イメージに合う画像があるかが問題ですね。
誰か無料でキャラデザインしてくれないかなー
自分ができるようになるのが一番いいんだけど。。。

「サッカー選手にサッカーボールが当たって跳ね返す際の角度」
これはAndengineの物理演算系のライブラリ(AndEnginePhysicsBox2DExtension?)を使えばできるかな。
当たった位置によって跳ね返す角度が想定どおりになればいいんだけど。。
まぁ、これは実装してみてから考えますか。

と、こんな感じで考えてます。

物理演算系のライブラリは使えるようになると、色んな発想が生まれそうですね。
落ちもの系とか、積み重ね系とかとか。

「自分で2機操作するシューティング」/「ブロック崩し&シューティング」
と二つともシューティング系ですが、操作方法・実装方法はかなり違うと思うので
飽きずにやれるといいなー。

まだまだ色々作りたいアプリあるけど、時間とスキルが追いついて行かないですね。。。

また、随時状況の報告をしていきます。

{ Add a Comment }

シューティングゲーム開発_Vol2



前回の記事を見直したら、ソース記述しただけで
特に説明を記載してませんでした。。

すみません。

攻撃実装

今回は2機のミサイルと、ミサイルの移動を実装します。

■MainScene
[java]
@Override
public void init(){

・・・省略・・・

weponMainLeftSprite();
weponMainRightSprite();
registerUpdateHandler(timerHandler);

}

// ※2
TimerHandler timerHandler = new TimerHandler(1 / 60f, true, new ITimerCallback() {
public void onTimePassed(TimerHandler pTimerHandler) {
List<Rectangle> removeLeftWepon = new ArrayList<Rectangle>();
for(Rectangle rect : weponLeftList){
if(rect.getY() < -10){
removeLeftWepon.add(rect);
rect.detachSelf();
rect.dispose();
}else {
rect.setPosition(rect.getX(), rect.getY() – 3);
}
}
weponLeftList.removeAll(removeLeftWepon);

List<Rectangle> removeRightWepon = new ArrayList<Rectangle>();
for(Rectangle rect : weponRightList){
if(rect.getY() < -10){
removeRightWepon.add(rect);
rect.detachSelf();
rect.dispose();
}else {
rect.setPosition(rect.getX(), rect.getY() – 3);
}
}
weponRightList.removeAll(removeRightWepon);
}
});

// ※1
private void weponMainLeftSprite(){
mainLeftSprite.registerEntityModifier(new SequenceEntityModifier(
new DelayModifier(0.5f, new IEntityModifier.IEntityModifierListener() {
@Override
public void onModifierFinished(
IModifier<IEntity> pModifier, IEntity pItem) {

final Rectangle rect = new Rectangle(mainLeftSprite.getX() + mainLeftSprite.getWidth(), mainLeftSprite.getY(), 5, 5, getBaseActivity().getVertexBufferObjectManager());
attachChild(rect);

rect.setColor(convertRGB(200), convertRGB(200), convertRGB(200));
rect.setPosition(mainLeftSprite.getX() + mainLeftSprite.getWidth() / 2, mainLeftSprite.getY());
rect.setZIndex(10);

weponLeftList.add(rect);

weponMainLeftSprite();
}

@Override
public void onModifierStarted(IModifier<IEntity> pModifier,
IEntity pItem) {
}
})));
}

private void weponMainRightSprite(){
mainRightSprite.registerEntityModifier(new SequenceEntityModifier(
new DelayModifier(0.5f, new IEntityModifier.IEntityModifierListener() {
@Override
public void onModifierFinished(
IModifier<IEntity> pModifier, IEntity pItem) {

final Rectangle rect = new Rectangle(mainRightSprite.getX() + mainRightSprite.getWidth(), mainRightSprite.getY(), 5, 5, getBaseActivity().getVertexBufferObjectManager());
attachChild(rect);

rect.setColor(convertRGB(200), convertRGB(200), convertRGB(200));
rect.setPosition(mainRightSprite.getX() + mainRightSprite.getWidth() / 2, mainRightSprite.getY());
rect.setZIndex(10);

weponRightList.add(rect);

weponMainRightSprite();
}

@Override
public void onModifierStarted(IModifier<IEntity> pModifier,
IEntity pItem) {
}
})));
}

private float convertRGB(int val){
return val / 255f;
}

[/java]

■説明!!
※1
initメソッドからweponMainLeftSprite / weponMainRightSpriteを呼び出します。
weponMainLeftSpriteメソッドで、
・mainLeftSprite.registerEntityModifier(Spriteへの変更処理を設定します。)
・SequenceEntityModifier(指定した変更処理を順番に処理するように設定します。)
・DelayModifier(0.5f, new IEntityModifier.IEntityModifierListener(0.5秒たってからメソッド内の処理をするように設定します。)

// 5dp四方の画像を描画します。
final Rectangle rect = new Rectangle(mainLeftSprite.getX() + mainLeftSprite.getWidth(), mainLeftSprite.getY(), 5, 5, getBaseActivity().getVertexBufferObjectManager());
attachChild(rect);

// 色を指定
rect.setColor(convertRGB(200), convertRGB(200), convertRGB(200));

// ポジションの指定をします。
// 【mainLeftSprite.getX() + mainLeftSprite.getWidth() / 2】でSpriteのX座標の中央値を指定します。
rect.setPosition(mainLeftSprite.getX() + mainLeftSprite.getWidth() / 2, mainLeftSprite.getY());
rect.setZIndex(10);

// 機体のミサイルをリストで保持します。(ミサイルの画像を移動するのに使用します。)
weponLeftList.add(rect);

// 再帰的に呼び出して常に0.5秒事にミサイルを発射するようにする。
weponMainLeftSprite();

※2
initメソッドからtimerHandlerをregisterUpdateHandlerにセットして毎秒60回(1 / 60f)呼び出すようにします。
ここのメソッドで敵(未実装)の移動やミサイルの移動、あたり判定を行う予定です。

[java]
// 削除用のリストを用意(for文で回している最中に要素を削除するとエラーとなるので削除対象を保持する)
List<Rectangle> removeLeftWepon = new ArrayList<Rectangle>();

// 左機のミサイルを移動する。画面から外れたミサイルは削除する。
for(Rectangle rect : weponLeftList){
// 画面上部から-10のところまでミサイルがきたら不要なので、削除用リストに保持
if(rect.getY() < -10){
removeLeftWepon.add(rect);
// 画像をクリア
rect.detachSelf();
rect.dispose();
}else {
// 画像を移動する。上方向に-3の速度で移動
rect.setPosition(rect.getX(), rect.getY() – 3);
}
}
// 削除対象をリストから除去
weponLeftList.removeAll(removeLeftWepon);
[/java]

■上記の処理をすると2機からミサイルが発射されます!

こんな感じ
wepon

次回は自動で発射されるミサイルの一時停止と必殺ボタンを実装したいと思います。

{ Add a Comment }

第2弾、第3弾避けゲーリリース!



会社の勉強会で開発していたAndengineというゲームエンジンを利用して作成したゲーム。

リリースしましたー!

■よけろ!ききいっぱつ!
こちら2016/01から開発を開始して、2016/4にようやく完了しました!
基本的には「AndEngineでつくるAndroid 2Dゲーム」という参考書に記載のある技術のみで
完結しています。

■よけろ!かんいっぱつ!
これは「よけろ!ききいっぱつ!」がほぼ完成してから着手しました。
ベースのアプリを改造して開発したので、約10時間程度でできたと思います。

やっぱり1本ゲームアプリを作ると色々と感覚がつかめてきて
「こうしたい!」と思ったときに「じゃあこうすればいいかなー」と色々と考えれるので
まずは地道に小さいアプリから作ってみるのがアプリ開発を学ぶ近道かなと感じました。

いきなり凝ったアプリを開発しようと思うと、途中で行き詰ったりして
モチベーションがあがらずに結局完成しないという事になりかねないですね。

私も普段アプリの妄想してて、かなり凝ったイメージがあるのですが
それは追々頑張ってみようと思います。

みなさん暇つぶしにGoogle Playでダウンロードしてみてください。

でわでわ!

{ Add a Comment }

シューティングゲーム開発_Vol1



傾きセンサーを使用した2つのゲームは、ほぼ作り終えたので。
次はシューティングゲームを作ってみようと思います!

タイトルは
【ツインスペースヒーロー!!】

Andengineの「AnalogOnScreenControl」を2つ配置して

二つの機体を同時に操作するシューティングです。
今回はコントローラの配置します。
※ここで記載する内容は「AndEngineでつくるAndroid 2Dゲーム」の参考書の
プロジェクト構成を前提としています。

[java]
public class MainActivity extends MultiSceneActivity implements
SensorEventListener {

// 画面のサイズ。
private int CAMERA_WIDTH = 800;
private int CAMERA_HEIGHT = 480;

public static Camera camera;

public EngineOptions onCreateEngineOptions() {
this.camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);

EngineOptions eo = new EngineOptions(true,
ScreenOrientation.LANDSCAPE_FIXED,
new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera);

// ふたつコントローラを使用するのでマルチタッチの設定をする。
eo.getTouchOptions().setNeedsMultiTouch(true);
}
}

public class MainScene extends KeyListenScene implements
ButtonSprite.OnClickListener {
private BitmapTextureAtlas mOnScreenControlTexture;
private ITextureRegion mOnScreenControlBaseTextureRegion;
private ITextureRegion mOnScreenControlKnobTextureRegion;

private AnimatedSprite mainLeftSprite;
private AnimatedSprite mainRightSprite;

@Override
public void init(){
// 背景設定
setBackground(new Background(convertRGB(0), convertRGB(0), convertRGB(0)));

// 左側のコントローラエリア作成
final Rectangle rect = new Rectangle(0, 0, 150, 480, getBaseActivity().getVertexBufferObjectManager());
attachChild(rect);

rect.setColor(convertRGB(50), convertRGB(50), convertRGB(50));
rect.setPosition(0, 0);
rect.setZIndex(controlAreaZIndex);
sortChildren();

// 右側のコントローラエリア作成
final Rectangle rect2 = new Rectangle(700, 0, 150, 480, getBaseActivity().getVertexBufferObjectManager());
attachChild(rect2);

rect2.setColor(convertRGB(50), convertRGB(50), convertRGB(50));
rect2.setPosition(650, 0);
rect2.setZIndex(controlAreaZIndex);
sortChildren();

// コントローラの画像登録
this.mOnScreenControlTexture = new BitmapTextureAtlas(getBaseActivity().getTextureManager(), 256, 128, TextureOptions.BILINEAR);
this.mOnScreenControlBaseTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mOnScreenControlTexture, getBaseActivity(), "onscreen_control_base.png", 0, 0);
this.mOnScreenControlKnobTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mOnScreenControlTexture, getBaseActivity(), "onscreen_control_knob.png", 128, 0);
this.mOnScreenControlTexture.load();

// コントローラ描画メソッド
drawAnalogControls();
}

// コントローラとコントローラの操作対象の機体を描画
private void drawAnalogControls(){

// 左の機体を描画
mainLeftSprite = getBaseActivity().getResourceUtil().getAnimatedSprite("main1.png", 1, 2);
mainLeftSprite.setZIndex(mainZIndex);
mainLeftSprite.setPosition(200, 450);

attachChild(mainLeftSprite);

// 左の機体操作用のハンドラー
final PhysicsHandler physicsHandler1 = new PhysicsHandler(mainLeftSprite);
mainLeftSprite.registerUpdateHandler(physicsHandler1);

// 右の機体を描画
mainRightSprite = getBaseActivity().getResourceUtil().getAnimatedSprite("main2.png",1,2);
mainRightSprite.setZIndex(mainZIndex);
mainRightSprite.setPosition(600, 450);

attachChild(mainRightSprite);

// 右の機体操作用のハンドラー
final PhysicsHandler physicsHandler2 = new PhysicsHandler(mainRightSprite);
mainRightSprite.registerUpdateHandler(physicsHandler2);

sortChildren();

// leftControl
final float x1 = 20;
final float y1 = getBaseActivity().getEngine().getCamera().getHeight() – this.mOnScreenControlBaseTextureRegion.getHeight() – 30;
final AnalogOnScreenControl leftControl = new AnalogOnScreenControl(x1, y1, MainActivity.camera, this.mOnScreenControlBaseTextureRegion, this.mOnScreenControlKnobTextureRegion, 0.1f, getBaseActivity().getVertexBufferObjectManager(), new AnalogOnScreenControl.IAnalogOnScreenControlListener() {
@Override
public void onControlChange(final BaseOnScreenControl pBaseOnScreenControl, final float pValueX, final float pValueY) {

// 機体の移動制限
System.out.println("mainLeftSprite:" + mainLeftSprite.getX() + "/" + mainLeftSprite.getY());
boolean backMove = false;
if(mainLeftSprite.getX() &lt; 155) {
physicsHandler1.setVelocity(50, 0);
backMove = true;
}
if(645 &lt; mainLeftSprite.getX() + mainLeftSprite.getWidth()){
physicsHandler1.setVelocity(-50, 0);
backMove = true;
}
if(mainLeftSprite.getY() &lt; 5) {
physicsHandler1.setVelocity(0, 50);
backMove = true;
}
if(475 &lt; mainLeftSprite.getY() + mainLeftSprite.getHeight()){
physicsHandler1.setVelocity(0, -50);
backMove = true;
}

// 機体の移動
if(!backMove){
physicsHandler1.setVelocity(pValueX * 100, pValueY * 100);
}
}

@Override
public void onControlClick(final AnalogOnScreenControl pAnalogOnScreenControl) {
/* Nothing. */
}
});
leftControl.getControlBase().setBlendFunction(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
leftControl.getControlBase().setAlpha(0.5f);

this.setChildScene(leftControl);

// rightControl
final float y2 = y1;
final float x2 = getBaseActivity().getEngine().getCamera().getWidth() – this.mOnScreenControlBaseTextureRegion.getWidth() – 20;
final AnalogOnScreenControl rightControl = new AnalogOnScreenControl(x2, y2, MainActivity.camera, this.mOnScreenControlBaseTextureRegion, this.mOnScreenControlKnobTextureRegion, 0.1f, getBaseActivity().getVertexBufferObjectManager(), new AnalogOnScreenControl.IAnalogOnScreenControlListener() {
@Override
public void onControlChange(final BaseOnScreenControl pBaseOnScreenControl, final float pValueX, final float pValueY) {
System.out.println("mainRightSprite:" + mainRightSprite.getX() + "/" + mainRightSprite.getY());
boolean backMove = false;
if(mainRightSprite.getX() &lt; 155) {
physicsHandler2.setVelocity(50, 0);
backMove = true;
}
if(645 &lt; mainRightSprite.getX() + mainRightSprite.getWidth()){
physicsHandler2.setVelocity(-50, 0);
backMove = true;
}
if(mainRightSprite.getY() &lt; 5) {
physicsHandler2.setVelocity(0, 50);
backMove = true;
}
if(475 &lt; mainRightSprite.getY() + mainRightSprite.getHeight()){
physicsHandler2.setVelocity(0, -50);
backMove = true;
}
if(!backMove){
physicsHandler2.setVelocity(pValueX * 100, pValueY * 100);
}
}

@Override
public void onControlClick(final AnalogOnScreenControl pAnalogOnScreenControl) {
/* Nothing. */
}
});
rightControl.getControlBase().setBlendFunction(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
rightControl.getControlBase().setAlpha(0.5f);

leftControl.setChildScene(rightControl);
}

private float convertRGB(int val){
return val / 255f;
}
}

[/java]

vol5_2

※重要な部分のみコード記載してますので、変数宣言とか一部抜けているかも知れませんがご了承。。

次回以降も随時シューティングゲームの途中経過を記載していきます!

{ Add a Comment }

ゲームエンジン(Andengine)について



文字通り3日坊主でサボってました。
浅野です。

子供達の進級・卒園・入学などでバタバタしてまして。。
と言い訳しておきます。

さて、今日はAndengineというスマホアプリのゲームエンジンの話をします。

ゲーム(スマホアプリ)を作成するにはゲームエンジンを使用すると格段と効率がよくなります。
ゲーム中でよく使用する機能をゲームエンジンが吸収してくれるのでとても助かりますね。
(音楽・画像表示・画像移動などなど)

スマホのゲームエンジンというと、私がパッと思いつくのが以下4つ。
・Unity(C#,Unityscript)
・Cocos2dx(C++)
・Andengine(Java)
・enchant.js(Javascript)

Unity・Cocos2dx・enchant.jsなんかはマルチプラットフォーム対応しているので
魅力的ですね。

ただ、私は普段仕事でJavaを使用しているので、できればJavaを使用したゲームエンジンを探して
Andengineにたどり着きました。
(UnityはC#で開発できるので、割とすんなり馴染めるかと思いますが、まずはAndengineで。)

Andengineで勉強・開発をスムーズにする上で必要なものが3つあります。
1.給料袋
2.堪忍袋
3.お袋



すいませんウソですw

ホントは下記です。
1.参考書(AndEngineでつくるAndroid 2Dゲーム)
2.サンプルアプリ(AndEngine Examples)
3.サンプルソース(AndEngine Examples)

【1.参考書(AndEngineでつくるAndroid 2Dゲーム)】
こちら非常に分かりやすいです。
別に参考書の宣伝をするわけじゃないので、詳細は省きますが
サンプルアプリを3つほど作りながら勉強できます。

私の場合は、適当に流し読みして、本の出版元から
完成したサンプルアプリをDLして、Android Studioで動くようにしました。
実際に動くアプリとソースを用意して、動作の確認&ソースを変えた時の挙動を確認して勉強しました。

【2.サンプルアプリ(AndEngine Examples)】
Google PlayでAndengineのサンプルアプリ集をDLできます。(Google PlayでAndEngine Examplesで検索)
実際にどういうことができるかをサンプルを見て確認し、
じゃあ自分はどういうアプリを作ろうかというのをイメージしました。
※「3.」のソースより若干古いアプリになってますが、まぁ別に支障はないでしょう。
気になるようでしたら、ソースをDLしてAndroid Studioで動かせるようにして確認してみるといいかも。

【3.サンプルソース(AndEngine Examples)】
「2.」でサンプルアプリを確認して、自分が使いたい機能があれば、実際にソースを確認します。
非常にたくさんのサンプルがあるので、必ず役に立つと思います。
(WEBでAndEngine Examplesで検索)

私も現在、「1.」のサンプルアプリの「ゾンビから逃げるゲーム」を参考に
二つほどゲームを作成してます。
(近日公開)

Andengineで勉強してみて、思ったことは(Andengineに限らずですが)
いくら本や、参考サイトをみても、実際に自分でアプリを実装しないと理解できません!

百聞は一見にしかず
百見は一触にしかず

ってことでみなさんバンバンAndengine触っていきましょー

今後はAndengineの実装についてちょこちょこ触れていきます。

{ Add a Comment }