Andengineで使うスプライトアニメーション素材作成

皆様はじめまして、今月からGeNERACEに参画してます。マークアップエンジニアのユージローです。

さて、いくつか前のポストでも紹介されてる通り、弊社ではAndengineを使用して開発を行っています。

Andengine = java

マークアップエンジニア = html,css,javascript,etc

仕事が無い!(待て

そんなこんなで慣れないjavaを少し触りつつ、僕は現在キャラクターなどの画面素材を担当しています。

Andengineはjavaですので、アニメーションの実装方法もcssアニメーションなどではなく、javaを用いてのアニメーションが主になりますが、スプライトアニメーションは昔ながらのフィルムビデオと同じ形式です。

全てのコマを一枚の画像として書き出さなければなりません。

スプライトシートは縦横に繰り返すものもありますが、単純なものであれば
縦もしくは横のみの繰り返しで済みます。

このスプライトシートですがアナログな作業でやるのは効率的ではないのでツールを使います。
作成ツールも下記のように幾つかあるのですが、Flash Pro CS6からFlashでもスプライトシートの作成が可能になりました。

インストールタイプのツール:http://www.codeandweb.com/texturepacker
ブラウザアプリ:http://draeton.github.com/stitches/

ということで今回はFlash Pro CS6を使用した場合のスプライトシート作成手順です。

ステップ1:Flashアニメの書き出し

Flashアニメの作成方法は端折るとして、ここでは作成したFlashアニメをpng画像に書き出します。キャプチャのようにメニューの『ファイル』➡『書き出し』➡『ムービーの書き出し』を選択します。

ss2013-02-22-12.40.08

フォーマットは今回背景有の為jpegでもgifでも良いのですが、変更で透過させる可能性も考えてpngシーケンスを選択しました。ss2013-02-22-12.58.30

保存するとタイムラインに沿った画像が全て吐き出されこのようになります。
無事に書き出せました。

スクリーンショット-2013-02-22-13.07.54

ステップ2:ライブラリへの取り込み

ここでまたFlash Proで新規ファイルを作成します。
新規ファイルを作成したら、メニューの『ファイル』➡『読み込み』➡『ライブラリに読み込み』を選択し、先ほど書き出したpng画像を全て読み込みます。
スクリーンショット-2013-02-22-13.22.39

ステップ3:スプライトシートの生成

読み込みが完了したら、今度はライブラリウインドウから全ての素材を選択して右クリック➡『スプライトシートを生成』を選択します。

スクリーンショット-2013-02-22-13.55.21

最後です。
スプライトシートの出力と書かれている部分でシートの幅と高さや、素材間の距離、背景色などの調整を行い書き出して完了です。
スクリーンショット-2013-02-22-14.01.50

シートの大きさなどは実際の実装にも影響する為、事前に認識のずれが無いかなどの確認が必要です。

また今回は新規ファイルを作成しましたが、Flashアニメーションの任意のキーフレームシンボルからスプライトを生成することも可能になっています。

ご覧いただきありがとうございました。

Andengineを使ってみる その1

GeNERACE laboを御覧の皆様、はじめまして。

ピンキリエンジニアのひろゆきです。

先日からAndengineを使っていますが、日本語の紹介があんまりないので書いてみます。

少しでもお役に立てば幸いです。

本家はここ→ http://www.andengine.org/

用意したものはAndroid開発環境があるMac、Eclipse、DLしたAndengine。

プロジェクト作成などはこちらを参考にしてください。

では、簡単ではありますが、サンプルを記載します。

※SampleScene.javaを作成して動かしています

文字を出力する。

// フォントを指定
Texture texture = new BitmapTextureAtlas(activity.getTextureManager(), 480, 800, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
Font font = new Font(activity.getFontManager(), texture, Typeface.DEFAULT_BOLD, 30, true, Color.WHITE);
activity.getTextureManager().loadTexture(texture);
activity.getFontManager().loadFont(font);

// 出力する文字を設定
String str = "hello andengine!!";
Text text = new Text(0, 0, font, str, str.length(), new TextOptions(HorizontalAlign.LEFT), activity.getVertexBufferObjectManager());
attachChild(text);

まずはTextureの生成から使用したいFontを指定します。
それをActivityに設定して、Textにて文字を生成します。

画像を表示する

// 使用する画像名
String fileName = "button.png";

// 使用する画像の設定
BitmapTextureAtlas bta = new BitmapTextureAtlas(activity.getTextureManager(), 100, 100, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
activity.getEngine().getTextureManager().loadTexture(bta);
ITextureRegion btr = BitmapTextureAtlasTextureRegionFactory.createFromAsset(bta, activity, fileName, 0, 0);
Sprite sprite = new Sprite(0, 0, btr, activity.getVertexBufferObjectManager());
sprite.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
attachChild(sprite);

ボタン画像を表示する

// 使用する画像名
String btnName = "button.png";
String btnPressName = "button_press.png";

// 使用する画像の設定
BuildableBitmapTextureAtlas bta = new BuildableBitmapTextureAtlas(activity.getTextureManager(), 100, 200);
ITextureRegion trBtn = BitmapTextureAtlasTextureRegionFactory.createFromAsset(bta, activity, btnName);
ITextureRegion trPresseBtn = BitmapTextureAtlasTextureRegionFactory.createFromAsset(bta, activity, btnPressName);
try {
    bta.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 0, 0));
    bta.load();
} catch (TextureAtlasBuilderException e) {</em>
    Debug.e(e);
}

// 通常時と押された時の画像を指定する
ButtonSprite buttonSprite = new ButtonSprite(0, 0, trBtn, trPresseBtn, activity.getVertexBufferObjectManager());
buttonSprite.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
attachChild(buttonSprite);

ボタン画像を押した時に処理を行う

public class SampleScene extends Scene implements ButtonSprite.OnClickListener {

ButtonSprite.OnClickListenerを定義します。

// 識別タグ
buttonSprite.setTag(1);
// リスナー
buttonSprite.setOnClickListener(this);
// 認識するエリア登録
registerTouchArea(buttonSprite);

上記で表示したボタン画像に対して、この処理を加えます。

@Override
public void onClick(ButtonSprite pButtonSprite, float pTouchAreaLocalX, float pTouchAreaLocalY) {
    if (pButtonSprite.getTag() == 1) {
        // ボタン画像を大きくする
        buttonSprite.setScale(1.2f);
    }
}

どのボタン画像が押されたかはTagで識別します。

画像を動かす

// Handloerを定義する
public TimerHandler timeHandler = new TimerHandler(1f / 60f, true, new ITimerCallback() {
    @Override
    public void onTimePassed(TimerHandler pTimerHandler) {
        // Y軸に移動させる
        sprite.setPosition(sprite.getX() ,sprite.getY() + 1);
        // 拡大させる
        sprite.setScale(sprite.getScaleX() * 1.01f, sprite.getScaleY() * 1.01f);
        // 透明にする
        sprite.setAlpha(sprite.getAlpha() * 0.99f);
    }
});

これだけでは画像は動きません。
Handlerは登録する必要があります。

registerUpdateHandler(timeHandler);

動きを止めたいときは

unregisterUpdateHandler(timeHandler);

とします。
慣れてないときは、これを忘れて動かない!ということが多々ありました。

Sceneを遷移させる

NextScene scene = new NextScene(activity);
activity.getEngine().setScene(scene);

SceneからActivityを遷移させる

activity.startActivity(new Intent(activity, NextActivity.class));
activity.finish();

現在のActivityを終了して、次のActivityを呼び出します。

他にAndengineの機能は多々ありますが、調べきれていない部分などもありますので、次回に続きたいと思います。

ご覧頂きありがとうございました。

AndEngineでAndroidネイティブのView操作をSceneから行う

こんにちは
Zynga Japanを12月に退職し、GeNERACEという作りたての会社でCTOやってます、村松です。
GeNERACEではAndroidのネイティブゲーム開発にAndEngine(gituhub)を使ってます。

AndEngineはとっても便利ですが、ネイティブのViewと連携させるとより便利です。
今開発中のゲームもネイティブのViewをいくつかのSceneで使っています。
その際にScene1ではいらないけど、Scene2では使いたいとかいう場合ありますよね。
これ、単純にActivityのインスタンスを取得し、対象のViewにsetVisibilityで制御出来るようで実は出来ない。

何故か。

Viewはシングルスレッドモデルです。
AndEngineのSceneはActivityとは別スレッド。
SceneでネイティブのView操作を行うとアプリが強制終了します。

その為、こんな感じに実装してみました。

  • StageScene.java(ゲーム画面のクラス)
  • public class StageScene extends GeneraceAndEngineBaseScene implements ButtonSprite.OnClickListener, IOnSceneTouchListener, IOnAreaTouchListener{
    	private void showResult() {
    		// ネイティブのViewを表示状態にしたいYO!
    		getBaseActivity().visibleNativeViewFromId(R.id.edit_text_result);
    	}
    }
    
  • GeneraceAndEngineBaseScene.java(ゲーム画面の親クラス)
  • public abstract class GeneraceAndEngineBaseScene extends Scene implements ButtonSprite.OnClickListener{
    	private GeneraceAndEngineBaseActivity baseActivity;
    	public GeneraceAndEngineBaseActivity getBaseActivity() {
    		return this.baseActivity;
    	}
    }
    
  • GeneraceAndEngineBaseActivity.java(アクティビティ制御クラス)
  • public abstract class GeneraceAndEngineBaseActivity extends SimpleLayoutGameActivity {
    	Handler goneNativeViewHandler = new Handler();
    	Handler visibleNativeViewHandler = new Handler();
    	Handler invisibleNativeViewHandler = new Handler();
    	public void goneNativeViewFromId(final int id) {
    		new Thread(new Runnable() {
    			public void run() {
    				goneNativeViewHandler.post(new Runnable() {
    					public void run() {
    						findViewById(id).setVisibility(View.GONE);
    					}
    				});
    			}
    		}).start();
    		
    	}
    	
    	public void invisibleNativeViewFromId(final int id) {
    		new Thread(new Runnable() {
    			public void run() {
    				invisibleNativeViewHandler.post(new Runnable() {
    					public void run() {
    						findViewById(id).setVisibility(View.INVISIBLE);
    					}
    				});
    			}
    		}).start();
    		
    	}
    	
    	public void visibleNativeViewFromId(final int id) {
    		new Thread(new Runnable() {
    			public void run() {
    				visibleNativeViewHandler.post(new Runnable() {
    					public void run() {
    						findViewById(id).setVisibility(View.VISIBLE);
    					}
    				});
    			}
    		}).start();
    	}
    }
    
  • activity_main.xml
  • <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	xmlns:tools="http://schemas.android.com/tools"
    	android:layout_width="match_parent"
    	android:layout_height="match_parent" >
    
    	<org.andengine.opengl.view.RenderSurfaceView
    		android:id="@+id/renderview"
    		android:layout_width="fill_parent"
    		android:layout_height="fill_parent" />
    	<EditText
    		android:id="@+id/edit_text_result"
    		android:gravity="left|top"
    		android:inputType="textMultiLine"
    		android:layout_marginLeft="10dip"
    		android:layout_marginRight="10dip"
    		android:layout_marginTop="300dip"
    		android:visibility="gone"
    		android:layout_width="match_parent"
    		android:layout_height="100dip"/>
    </RelativeLayout>
    

    非同期処理とかで使う実装に似た感じ。
    なんかイマイチ綺麗じゃないなー、俺Handlerあんま好きじゃないんだよなーとか思うけど、もう今日は眠いのでこれで限界。(3:30 AMなう)
    より良い実装が浮かんだら書き換えよう。そうしよう。

    そういえば、AndEngineってZyngaのNicolas Gramlich(facebook)さんが作ってるですよね。
    Zynga Japan辞めてもなんやかんやでZyngaにお世話になることはまだまだありそうです。

    GitとGitHubを使いこなすためのメモ

    こんにちは。2月から入った新人のあらいみきです。今日から「爆速」で開発できるエンジニアになるために頻繁にメモを書き残します。
    第一弾のテーマはGitです!

    macでGitを使いこなすための簡単なメモ。

    1. 下準備

    ☆用意するもの

    • Xcodeがインストールされていて、Command Line Toolsが入っているmacbook

    ☆インストールするもの

    • Tower

    ☆GitHubの設定をする。

    • GitHub https://github.com/

    ☆Git coreをmac portでダウンロード

    $ sudo port install git-core
    

    ☆Gitのバージョンを確認

    $ git version
    

    2. Gitを使う

    ☆登場人物

    • 中央リポジトリ…メインユーザーが持つプロジェクトのリポジトリ。
    • リモートリポジトリ…各人が中央リポジトリからforkしたリポジトリ。メインユーザーではなく各人のリポジトリとなる。
    • ローカルリポジトリ…リモートリポジトリからローカルへクローンして作るリポジトリ。

    ☆Gitのおさらい

    • fork…自分のGitのリポジトリに任意のGitのリポジトリをコピーする。
    • pull…任意のリモートリポジトリの変更を自分のところに反映させる。
    • push…自分のリモートリポジトリに、自分のローカルでの変更を反映させる。commitとセットで行うとわかりやすい。
    • commit…自分がローカルで行った変更を確定させる。commitしただけだと変更が自分のリモートリポジトリ反映されないことに注意!(svnとは違う)
    • pull request…自分のリモートリポジトリの変更を中央リポジトリに反映させたい場合は、中央リポジトリに対してpull requestを送る。
    • master…リポジトリの大本命。ここを簡単に変えられないように枝分かれのリポジトリ(=branch)を作る。
    • 中央リポジトリの設定について…Git Flowに似た運用をしているので、中央リポジトリの更新用にリモートリポジトリを追加します。

      中央リポジトリの設定について詳細は後述

    ☆ローカルにgitのリポジトリを置くディレクトリを作る。

    $ mkdir Git用ディレクトリ名
    

    ☆GitHubでコピーしたいリポジトリをforkする。

    ☆forkしたリポジトリのクローンをローカルに作る。

    $ cd Git用ディレクトリ名
    $ git clone git@xxx/yyy.git
    $ git checkout -b ブランチ名
    

    ※今回はSSHの鍵認証を使ってるので、HTTPではなくSSHを選択しました。

    ☆中央リポジトリの設定を行う。
    今回は中央リポジトリのリモートブランチとして、”upstream”という名前のリモートブランチを作成しました。

    $ git remote add upstream git@フォークしたリポジトリの元のURL
    

    ☆ローカルで、クローンしたリポジトリ内のどこかに変更を加える。

    ☆自分の変更をまずはコミットさせる。

    • Towerの左側のBRANCHESは常にチェックすること。ターゲットになってるブランチだったら、ブランチ名の横に(HEAD)と書いてある。
    • コミットさせたい変更したファイルを選ぶ方法→Statusタブを開いて、任意のファイルのStagedをチェックする。チェックしたら、上部にあるチェック印の”Commit”ボタンをクリック。

    これで「コミットは完了」!、、だけどローカルの変更は自分のリモートリポジトリに反映されていない。pushをして、ローカルでの変更を自分のリモートリポジトリに反映させる。

    ☆変更をpushする。
    Towerの上部のpushボタンをクリックし、ブランチの場所を要確認してokボタンをクリックする。
    これで自分のリモートリポジトリにも変更した内容が反映させる。
    今回はTowerでpushしましたが、ターミナルを使う場合は以下のコマンドを打ちます。

    $ git push origin ブランチ名/プロジェクト名
    

    ※引数なしのpushをすると、カレントブランチに関わらずローカルブランチと同名のブランチがリモート上にある場合はそれらを一気にpushします。

    ☆中央リポジトリの最新版を取り込む。

    $ git pull upstream ブランチ名
    

    ☆pull requestを送る。
    GitHub上で自分がフォークしたリポジトリのページに遷移し、Commitsというタブを選択し、自分のpush内容が反映されていることを確認し、pull requestボタンをクリックする。

    • pull(変更を)する側…master
    • pullするもの…pull requestを送る側が変更した箇所

    これで中央リポジトリの管理者にpull requestが送信される。

    ※pull requestを押した後、Tower上で空pushをする。これで自分が行った変更+中央リポジトリの変更を自分のリモートリポジトリに反映することができる。

    ☆mergeしたいときは?

    自分がpull requestを送ったと同時にほかの誰かもpull requestを送ったとき、エラーや競合が起きない範囲で変更を反映させたいところです。

    そこでGitHub上でmerge pull requestボタンを押すと、簡単にmergeを行うことができます。さらにmergeされた箇所に対してコメントを書くこともできます。これでソースレビューとマージが同時にできます!

    ※自分が行った変更と他の人が行った変更がコンフリクトした場合。
    例えば他の人と同じファイルを変更していて、pullした際にeclipse上などでファイルを確認したときにエラーが起こっている場合は、自分でeclipe上で変更分を手動でマージする必要があります。その後改めて変更分をpushし、pull requestを送る必要があります。

    今回はここまでです。ご覧いただきましてありがとうございます。

    参考記事一覧