ラベル アプリ開発 の投稿を表示しています。 すべての投稿を表示
ラベル アプリ開発 の投稿を表示しています。 すべての投稿を表示

2012年8月5日日曜日

fps記録

Android2.3~4.0の場合、機種によってVSYNCの間隔が区々なようなので、リプレイデータにプレイした時のFPS平均値を記録&表示するようにしてみました。Androidの場合、VSYNC間隔はプログラム側で弄れ無さそうなので、せめて「どの程度の環境でプレイした記録か」という情報を保持する必要があります。
SLOT1~3は、仕様変更前のリプレイデータだから、0.00fps。
SLOT4がテストで記録してみたもの。
ちなみに、Windows版の場合、DirectX9以降に対応したGPUを積んだ環境であれば、60.00fpsジャストになります。少し端数の具合を確認したかったので、プレイ中にわざとウィンドウを動かしてみたりして、端数が出るようにしました。

あと、計測対象は、ゲームメイン処理のプレイ中でプレイ開始時点の最初の約1秒(正確には1000ミリ秒~1ミリ秒の範囲)を除いた状態からの平均フレーム数で、ポーズ中やクリア時、エンディング時は測定しない仕様です。(※最初の約1秒を記録しないのは、秒単位で総フレーム数と総プレイ時間(秒単位)を測定するので、厳密な測定をする為に必要)

無駄に厳密な測定。

2012年8月4日土曜日

生産状況(8/4)

本日(4-Aug 2012)時点のSHOT04の開発規模を測定。

5月中旬ごろ(体験版が完成した時)の実行行数が7.7ksぐらいで、現時点で12.3ks。
2.5ヶ月ぐらいで+4.6ksといったところですか。
「単独製作なのに、2.5ヶ月でたった4.6ksかよ!?」
と、思われるかもしれませんが、基本休日(週休2日として約20人日)しかプログラムを組む時間が無いから、こんなもんです。
あと、ずっとプログラムを作る作業だけやっている訳ではなく、絵を描くのと音楽や効果音を作るのにもそれなりに時間が掛かります。(むしろ、プログラムよりも絵や音楽の方が専門外なので大変)

思ったよりもプログラム規模が膨れました。
5月時点では、スコアランキングやリプレイなどの機能が未実装(体験版として公開できる最低限度の実装をした状態)だったというのが増加要因なので、あとは完成するまで、あまり大きな増加は無かろう・・・と、思っていますが、あと、3ksぐらいは増えるかも。一応、一番デカいゲームの本編処理(game.c)と、VGEエミュレータ(vge*.*とvgs.c)の増加傾向は止まりつつありますが、敵のアルゴリズム(enemy.c)の規模が依然として増加し続けているので。

敵アルゴリズムは、内部で細かく部品化して実装量を抑えるようにしていますが、再利用率があまり高くないです。似たような敵でも、ついつい細かい演出の違いとかを入れたくて、結局再利用せずに新規で起こしたりしているので。これなら、最初から部品化をしない仕組み(=非オブジェクト指向設計)にしておいた方が実装規模を抑えることができたかも。私はそこら辺(オブジェクト化するorしない)のバランス調整がかなり上手い方なので、通常は実装規模を最小限に抑えることができるのですが、この敵システムの作りこみに関しては、ミスった感が否めません。

ちなみに、SHOT03の時の実装規模は、10.2ks。
だいたい、10ksを超えだすと、ソース内に魔物が棲み始めます。
だから、プログラムの規模は少なければ少ないほど良いです。
(SHOT03の時は完成時ジャスト10ks程度になるように狙って作っていました)

SHOT04に関しては、今から10ks以下にするようにリファクタするのは期間的に不可能です。
なので、魔物を棲ませた状態で作り続けざるを得ません。
ロックロールせざるを得ません。

2012年7月29日日曜日

/mnt/sdcard/ディレクトリのアクセス権限

ウォークマン(Zシリーズ)で、SHOT04(NokogiRider)をデバッグしていて機種依存の盲点をひとつ発見。

SHOT04の場合、SDカード(/mnt/sdcard)の下に専用のディレクトリを掘って、そこにプレイデータ(スコアとか)やリプレイデータを保存する仕様にしていたのですが、Zシリーズの場合、/mnt/sdcardにアクセス権限が無いようでした。

確かに、Androidの仕様として、/mnt/sdcardの有無は機種依存に成り得ます。
Androidの仕様として、アプリケーションが(ストレージパーミッションで)読み書きが保障されているのは、基本的に 標準のアプリケーションディレクトリ(/data/data/パッケージ名) 配下のみのようです。

という訳で、/mnt/sdcardにアクセス権限が無い場合、標準のアプリケーションディレクトリ(/data/data/パッケージ名)にデータファイルを保存する仕様にしておきました。


なお、/mnt/sdcardにアクセス権限がある場合は、SDカードに保存するようにします。
一般的にSDカードの方が空き領域が大きいので、標準のアプリケーションディレクトリに配置するデータは可能な限り小さくする方が、ユーザにとって親切だと思うし、リプレイデータとかを独自にバックアップしたりするのも楽なので。(/data/dataディレクトリの場合、パス決めウチで打たないとファイルへアクセスできないという...)


これは、InvaderBlock2も同じ仕様にしておく必要がありそうです。
まぁ、このバグに気付けただけでも、Zシリーズを購入した甲斐がありました。
Zシリーズ設備投資分のペイバックは無いでしょうけど。

しかし、Androidの機種依存には困ったものです。
SHOT04は、かなりその辺を柔軟に作ってましたが、それでもこういう手落ちは出てくる・・・


追記(8/4):
Xアプリで、音楽データを転送したら/mnt/sdcardに書き込み権限ができたっぽい?
とりあえず、SHOT04は、/mnt/sdcardに書き込み権限があれば、/mnt/sdcardにデータ保存して、なければ/data/dataにデータ保存する仕様にしておきます。

Z1050導入

SonyのZシリーズ(Z1050)を購入。
手持ちのスマホをAndroid4.0へアップしてしまったので、2.3スマホのデバッグ機として。
今年に入ってもう3台目のAndroid機。(iPodTouchを含めれば4台目か)


使ってみた感じでは、かなりの良品です。
附属のヘッドフォンがかなり良いです。
ノイズキャンセルがついているのに、イヤホン側に電源不要。
内蔵型ですかね。

あと、保護シールが最初っから貼ってあるのも良いです。
なかなか滑らかにスライドができる素材のものなので、張替え等は不要だと思います。

ネックは値段とSDカード非対応ということぐらい。
iPodTouchと戦うには戦力不足。
iPodTouchと同価格でSDカードを装備していれば・・・という感じ。

海外で発表されているZシリーズの後継機(NWZ-F800)もSDカードは付かないようです。
ただ、ノイズキャンセルは後継機ではついていないから、その分、価格は安くなるかも。
個人的には、値段よりもSDカード対応の方が重要だと思いますが。

2012年7月28日土曜日

Windows版の扱い

現在開発中のSTG(SHOT04)ですが、Android版については無料のLITE版を配布&有料(100~200円程度の予定)の製品版を販売する予定ですが、Windows版の扱いをどうしようか、全然決めてなかったな。。。

LITE版相当の無料版は適当に(ベクターとかで)配るつもりですが。
せっかくなので、アレンジ付きのサウンドトラックと製品版相当のWindows版をセットで同人ショップあたりで販売しようかという案があります。
DL販売ではなく物理媒体の販売で。

ただし、そうなると赤字の可能性が・・・まぁ、一種の道楽なので、多少の赤字は問題無いですが。
とりあえず、適当なプレス屋さんで値段を調べてみました。

Pケース:3week
枚数300枚500枚700枚1000枚1500枚2000枚
料金89,70095,000100,100105,000157,500206,000
1枚当たりの単価299190143105104103
枚数2500枚3000枚3500枚4000枚4500枚5000枚
料金247,500294,000339,500384,000427,500470,000
1枚当たりの単価999897969594
[参考] http://www.inv.co.jp/~popls/

コストパフォーマンスで見ると、1,000枚~が概ね最低ラインですかね。
Android版でちゃんと原価分の利益が出てくれれば、何も躊躇することなくトライできますが。
仮に、Android版を150円で販売した場合、Googleの手数料(30%=45円)を差し引いて、1,000本ぐらい売れればトライできる感じですかね。(1,000本も売れる気がしませんが)

2012年7月27日金曜日

デバッグ機の選定

Android2.3のデバッグ機(スマホ)を探していますが、Sonyのウォークマン(Zシリーズ)が良いかも。
http://www.sony.jp/walkman/products/NW-Z1000_series/index.html

白ロムで買ってもどうせ3Gは不要(3Gは手持ちのスマホだけで十分)なので。

昔これを買おうと思っていた時期があったのですが、デバッグ用途としては不向きと判断して断念。
店頭で「PCからアプリ転送できませんよ」という張り紙がしてあったことが致命傷。
あと、SDカードが付いていないこと等がネック。

ですが、これを選定していた当時は、色々と知識不足でした。(Android端末を持ってなかったので)
スマホで実機デバッグする時、SDカードはまず使いません。
店頭表示の意味はイマイチよく分かりませんが、2ch情報だとadbでapkを転送するのは普通にできるようです。
ちなみに、私の場合、FTPでPCからapkを内部ストレージへ転送して、インストールしてます。
なので、仮に2ch情報がガセだったとしても問題なし。

概ね、Zシリーズ導入の方向で確定しつつあります。
ただ、仮に白ロムで買うとすれば、SonyEricssonのMiniProあたりが(値段+サイズが)手ごろで良い感じ

2012年7月26日木曜日

3ボス完成

3面のボスがとりあえず完成。
デカめのレーザー。
発射前にはちゃんと集気的な感じの前振り+効果音で知らせるので、初見殺しではないです。
3面はステージの難易度が高めだったので、ボスの難易度は緩くしておきました。

しかし、現時点では最高難度(Ninja)で私が3面を越せないという状況。
このままではマズイので、調整が必要です。
ただ、全体バランスというのもあるので、とりあえず完成を優先する方が吉な気がしてきました。

あと、ボス戦でグダグダと稼ぐゲームにはしたくないので、タイムボーナスを高くする方向で調整中。
スコアシステムは、体験版(公開中)から大分弄ったかも。
あまりにも弄りすぎてしまい、私自信が細かい要素を忘れつつあります。

だいたい、ソースコードが10ks(10,000行)を超えだすと、自分自身でも全体を把握し切れない|-)
全体規模で10ksは余裕で超えていますが、そろそろゲーム本編処理のみで10ksを超えそうな感じ。

2012年7月25日水曜日

開発状況(3面ボス作成中)

3面ボスを作成中。
怒首領蜂(無印)の2面ボスあたりをオマージュした感じでしょうか。
珍しく、DoGAに殆ど頼らずドット絵を自分で描いてみました。
(DoGAで描いているのは、左右の副砲のみ)

色々と予定が遅延方向になる事由があったので、進捗は悪いです。
夏中に完成は厳しいかも。
というより、難易度の調整が厄介です。
3面のボスが一通り完成したら、ちょっとやり込んで調整する必要があります。
無理ゲーというほどではないのですが、現行の3面の難易度が鬼畜すぎるので、易しくする方向で。2面は、殆ど調整不要で良い感じにできたのですが、3面は色々と自重しなかった所為で、色々と吹っ切れ過ぎてしまいました。


今にして思えば、全5面というのがオーバースペックだったかも。。。
まぁ、音楽も作ってしまったので、全5面で作りますが。
ただ、次回作からは全3面で良いかも・・・と、思いつつあります。
次回作を作る予定は、今のところありませんが。
構想めいたものはありますが、どの程度売れるかが疑心暗鬼なので。
(SHOT04が想定よりも売れたら作る予定)

とりあえず、調整し易いように残機MAXでデバッグしてますが、Practiceはこのままで良いかも。
あと、画面構成を少し変えました。
例の問題のこともあるので、FPS表示をする方向で。

2012年7月24日火曜日

効果音の作り方

アマチュアがゲームを作るとき、効果音を自作している人はどれぐらい居るのでしょうか?
ふと、疑問に思いました。
割と有名なフリー素材があり、そこから拝借・・・という手があります。
しかし、私の経験上、自分で作った方が楽です。
素材だと、気に入った音を探すのが大変だし、ライセンス(使用許諾)の縛りも発生するので。
つまり、技術的にも権利云々(著作権とか)でも、自作した方が楽です。

(0)必要な機材

必要なのは、PCと波形エディタソフトだけです。
マイクとかは不要です。
自然音を加工して使いたいのであれば別ですが、ゲームの場合、自然音を使うと逆に不自然。

私は、3種類の波形エディタを使っています。(全部フリーウェア)
効果音エディタ_D
wavy
③Tiny Wave Editor


(1)音を作る


一番大事なことは、「効果音エディタ_Dで目的の音を自在に作れること」だと思います。
適当に弄っていても作れますが、音色の仕組みを理解すれば、ある程度狙って作れます。

a) サイン波
最も基本的な音色はサイン波(下図)です。

効果音エディタ_Dで波形(音色)のところに上図のような線を描き、再生してみてください。
たぶん、「ぽーーー」という感じの丸い音が鳴ると思います。
笛系の楽器の音がだいたいサイン波です。
マイクを持っている人は、笛の音を録音して、1周期(凸と凹の1セット)を見れば分かります。



b) 三角波

サイン波を少し変化させた感じの音色に三角波(下図)があります。
三角波は、概ねサイン波と同じような感じの音です。
ただ、ちょっと鋭い感じになります。
「ぺーーー」という感じでしょうか。
ちなみに、ファミコンのベース音でよく使われるのが、この三角波です。


c) 矩形派

嘗て、電子音といえば矩形派でした。
理由は波形を作るための計算が物凄く簡単なので。
つまり、安価なマイクロチップで処理できます。
AY-3-8190とかのPSG(Programmable Sound Generator)は矩形派ですね。

「ピーーー」という感じの音が鳴ります。
楽器でいうとクラリネットとかよく言われますが、個人的には「?」です。

ポイントは、
・サイン波=ポーーー
・三角波=ペーーー
・矩形波=ピーーー
です。

相違(図では黒線の位置)の変化が大きいと、音が鋭くなるという感じでしょうか。
それだけ抑えておけば、だいたいどんな音でも狙って作れます。
あとは、デューティー比(プラスの相違とマイナスの相違の比率)を調整したり、合成したり等々。


d) ノコギリ波
メロディーとしてよく使われる音色がノコギリ波ですね。
ちょっと、分かり易くするために2周期分のノコギリ波を描いてみました。
サイン波や三角波との決定的な違いは、1周期終わった後の変化量です。
上から下まで一気に降下しているのが分かると思います。
この大きな変化量が、非常に心地よい違和感を与えてくれます。
楽器でいうと、ストリングスとかの音になります。

上図だと、2周期分のノコギリ派を鳴らしますが、単位時間あたりの波の周期の数(周波数)が多くなると、音が高くなります。1秒間に440周期で鳴らせば、オクターブ4のラの音になります。
880周期鳴らせば、オクターブ5のラですね。

効果音エディタ_Dでは、音色と同様、周波数の波(時系列での変化)も編集できます。
あとは、音量の波も編集できます。
そして、これらを弄って目的の音を作ります。
左右の出力バランスも調整できますが、私は効果音は全てモノラルで作るので使いません。

e) ノイズ
あとは、相違をランダムに変化させれば、ノイズになります。
ちなみに、ファミコンで使えるプリセット波形は、三角波、矩形波、ノコギリ派、ノイズの4種類です。

(2)音を加工する

wavyなどのフィルターツールを使い、適当な加工をします。
加工のコツですが、「加工し過ぎないこと」です。
実はこれは結構重要です。
だから、私の場合、加工フェーズで使うフィルターは概ねエコーのみです。


(3)周波数などの調整


私の場合、ゲームで使う効果音は、22050Hzのモノラルと決めています。
そういった周波数の調整をするのに、Tiny Wave Editorが役立ちます。
Tiny Wave Editorは、かつてヤマハのサイトで無料で入手できました。
ただ、最近は配っていないようです。

海外のヤマハサイトを巡回すれば見つかるかもしれません。
ちなみに、私は3年ぐらい前にカナダのヤマハサイトで見つけました。
(一応、まだ無いかチェックしてみましたが、もう無いようです)


(4)効果音を入れるコツ

細かい全ての動作に対して効果音を入れると良いです。
過剰に設定するぐらいがちょうど良いです。
ちょっと邪魔っぽい感じだったら、音を鳴らさなくする前に、音を小さくしてみると良いかも。
ちなみに、SHOT04(NokogiRider)の体験版で使っている効果音は、34種類。
現段階(3面途中)では40種類。
完成版では、少なくとも50種類ぐらいになると思います。

2012年7月19日木曜日

今後の対応について

今後のアプリ開発の方向性について、Androidの扱いをどうするか真剣に考える必要がありそうです。
とりあえず、前の記事に書いたパフォーマンス劣化の問題が酷すぎるので。
最初から、iPhoneをメインを据えるべきだったか。。。

ただ、この問題が私の使った端末(Razr)固有のものだったら、まだ救いはあります。
たぶん、2.3から4.0へアップデートできる端末はそんなにはないので、そのケースの地雷を踏んだだけかも。
そうであれば、まだAndroidを切るという判断は早い。

更に、SHOT04(NokogiRider)の体験版を既にAndroidMarketで無料配布していて、尚且つ1,000本近いダウンロードもして頂いたので、ここで頓挫してしまったら楽しみにして頂いている方たちに申し訳ないです。まだ、日本の方からは感想は貰ってませんが、海外の方からはメールで感想を貰っていて、楽しみにしているとのことでしたし。


でも、私のメイン端末でデバッグができないのは痛すぎますが。
その点は、2.3系のスマホを別途(白ロムで)入手して、何とかしようと思います。
一応、2.3系のタブレットは持ってますが、やはりメインターゲットはスマホなので、スマホのデバッグ機は1台は欲しい。

まさか、1年で3台買うハメになるとはね。。。orz
開発環境のコストはタダ同然(最初に25$必要なだけ)だから、最初は(iPhoneではなく)Androidにしたのですが、もしかすると裏目だったかも。

2012年7月16日月曜日

Android 4.0で発生するパフォーマンス劣化の問題について

下記の記事で記載している性能劣化の問題ですが、Razrに対して2012年9月に提供されたシステムアップデートを適用することで、だいぶ改善されることを確認しました。ただし、まだまだ不安定です。概ね60fps程度で動くようになったのですが、バラツキが非常に激しいため、Android2.3の頃のような安定度はまだありません。
2012年9月7日・記

前の記事に書いている内容と重なる部分があるかと思いますが、Android4.0(3.0を含む?)で発生する、アプリケーションのパフォーマンス劣化に関する問題について、今、私が把握している情報を纏めておきます。

(1)どんな問題?

Android4.0(4.0.4)では、SurfaceViewCanvasを使って画面描画を行っているアプリケーションの動作性能(パフォーマンス)が極端に劣化する問題が発生します。
私はこの現象を、Motorola社のRazrという機種で確認しました。

SurfaceViewCanvasとは、2Dゲームなどでよく使われる「高速な描画」を行うための機能です。
私(SuzukiPlan)が開発しているアプリの場合、次のようなロジックでこれを利用しています。
/*
 *============================================================================
 * サーフェース・ビュー
 *============================================================================
 */
class VgeSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable {
    public Thread thread;
    public boolean isAttached;
    private final int XSIZE=240;
    private final int YSIZE=320;
    private Bitmap vram;
    private Rect vramRect;
    private Rect screenRect;
    private Paint screenPaint;
    /*
     *------------------------------------------------------------------------
     * コンストラクタ
     *------------------------------------------------------------------------
     */
    public VgeSurfaceView(Context context) {
        super(context);
        vram=Bitmap.createBitmap(XSIZE,YSIZE,Bitmap.Config.RGB_565);
        vramRect=new Rect(0,0,XSIZE,YSIZE);
        getHolder().addCallback(this);
    }
    /*
     *------------------------------------------------------------------------
     * サーフェース変更の検出
     *------------------------------------------------------------------------
     */
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        int xPos=0;
        int yPos=0;
        float wX=(float)width;
        float wY=(float)height;
        if((float)YSIZE*(wX/XSIZE)<wY) {
            wY=YSIZE*(wX/XSIZE);
            yPos=0;
            height=(int)wY;
        } else {
            wX=XSIZE*(wY/YSIZE);
            xPos=(width-(int)wX)/2;
            width=(int)wX;
        }
        Log.i("SHOT04-J","Width="+width);
        Log.i("SHOT04-J","Height="+height);
        screenRect=new Rect(xPos,yPos,width+xPos,height+yPos);
        screenPaint=new Paint();
    }
    /*
     *------------------------------------------------------------------------
     * サーフェース作成の検出 (スレッド起動)
     *------------------------------------------------------------------------
     */
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        isAttached=true;
        thread = new Thread(this);
        thread.start();
    }
    /*
     *------------------------------------------------------------------------
     * サーフェース破棄の検出 (スレッド停止)
     *------------------------------------------------------------------------
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isAttached = false;
        while (thread.isAlive());
    }
    /*
     *------------------------------------------------------------------------
     * メインループ (スレッド)
     *------------------------------------------------------------------------
     */
    @Override
    public void run() {
        SurfaceHolder holder=getHolder();
        Canvas canvas;
        int rc=0;
        while (isAttached) {
            rc=SHOT04.setVram2(vram);
            canvas=holder.lockCanvas();
            if(null!=canvas) {
                canvas.drawBitmap(vram,vramRect,screenRect,screenPaint);
                holder.unlockCanvasAndPost(canvas);
            }
            if(rc!=0) break;
        }
        if(0!=rc) {
            System.exit(0);
        }
    }
}

上記の青色の部分が問題が発生する箇所です。
問題の処理は、
 1. 240x320(QVGA)サイズのBitmap(AndroidBitmap)を更新(SHOT04.setVram2
 2. そのBitmapを表示(lockCanvasdrawBitmapunlockCanvasAndPost
という単純な処理を繰り返し実行しています。
Android2.3では、この処理では60fpsの性能を(かなりの余裕をもって)キープできていました。
しかし、Android4.0では、40fps~50fps程度の性能しか出なくなります。(※Razrの場合)

(2)原因は何?

推測ですが、Android2.3の場合、上記の描画処理でハードウェアアクセラレーション(GPU)が作用してました。
※なお、GPUを搭載していない端末の場合を除きます。(Android2.3を搭載しているスマホのほぼ全てに搭載していますが、ただし、一部の端末では搭載していない場合があるようです。GPUを搭載しているかどうかについては、あなたが所有する端末の公式サイトなどに掲載されているスペック表で確認できます)

しかし、Android4.0の場合、上記の描画処理でGPUが作用せず、ソフトウェア描画になったものと思われます。
その結果、遅延が発生したものと考えています。
その原因について調査した結果、下記のフォーラムを発見しました。
https://groups.google.com/forum/?fromgroups#!topic/android-developers/CK_jkHEQoOM

なお、上記フォーラムはAndroid3.0に関するものなので、本件とは同件では無いかもしれません。
ただし、質問者のコンディションと上記の実装が一致するので、私は同じ問題だと考えています。
上記の回答にあるGoogleのAndroid開発者、Romain Guy氏曰く、
In this case you won't get hardware acceleration.
この場合(SurfaceViewとlockCanvasを使用する場合)、ハードウェアアクセラレーションは作用しません。
We couldn't add this feature on time for Android 3.0. Sorry about this.
我々(Google)は、この機能をAndroid3.0で実装する時間がありませんでした。申し訳ありません。
とのことです。
私の和訳は若干怪しい部分があります。具体的には、on time forのところ。
Google翻訳の場合、「Android3.0の時」という風になりました。
ただ、「could not X on time Y」って「XがYに間に合わなかった」って意味では?(※カンです)
という訳で、上記のような翻訳にしておきました。

ただ、上記の記事は1年半ぐらい前のものです。
そして、Android4.0ではなくAndroid3.0に関するものです。
なので、不良ではなく仕様の可能性が高いと思っています。

追記:あと、そもそもAndroid2.3の時もこのケースではGPUは働いていなかった可能性もあります。
つまり、ソフトウェア描画だったけど、極端に性能が劣化してしまったということ。
劣化の度合いが大きいので、その可能性は低いと想定していますが。
まぁ、結局のところ、本当の原因はAndroidのソースコードを追いかけなければ分からない・・・ということ。

(3)アプリ利用者が問題を回避する手段は?

ありません。
Android4.0の場合、「設定」の「開発」で「全ての2D描画処理をGPUで行う」というモードがあります。
しかし、そのモードを設定しても、上記問題を回避することはできません。

(4)アプリ開発者が問題を回避する手段は?

■マニフェストによる回避は不可能
しばしば、android:hardwareAccelerated="trueをマニフェストに定義すれば良いんでない?」という意見を見ますが、その方法での回避は不可能です。
まず、この設定項目のデフォルト値は、"true"です。
そもそも、この設定項目は、GPUの使用を抑制するために定義するものです。
(そして、上記(3)の設定(強制GPUモード)は、falseを強制的にtrueにするものです)
なので、マニフェストの定義で、問題を解決することはできません。

[訂正]
上記については、若干間違いがありました。(マニフェストで回避不可能な点は正しいです)
デフォルト値はfalseでした。
ただし、実機確認した限り、SurfaceView+lockCanvasではtrueにしてもハードウェアアクセラレーションは作用しません。性能劣化があまりにも極端だったので、GPUが作用していたものがしなくなったものと実動作から判断しましたが、Android3.0以降(4.0以降を含む)は、有り得ないレベルで性能がデグっているだけという可能性もあります。(これについては、Androidのソースコードを追いかけて調べるしかないですが、未だ調べてません)

■GLSurfaceView(OpenGL/ES)へ移行による回避
この回避方法を用いれば、大半のアプリケーションは問題を回避できます。

しかし、(私が開発しているアプリのように)ピクセル単位で描画内容を更新するアプリの場合、この方式での問題回避は不可能です。ピクセル単位で描画内容を更新するアプリとは、例えば、エミュレータ(SNES,NES,MAME)などが該当します。

ピクセル単位で描画内容を更新するアプリがOpenGL/ESで目的とする処理を実現しようとする場合、テクスチャのフレーム単位での更新が必要になります。しかし、画像情報をテクスチャに変換する処理は(GPUがやっていますが)メチャクチャ遅い欠点があります。Android(やiPhone)のテクスチャは、GPUの内部では「PVRTC」という圧縮形式で保持する必要があるのですが、この圧縮方式への変換に極めて大きなオーバヘッドを要するため、240x320もの大きなサイズのテクスチャを60fpsで変換することはRazrで検証した限り不可能でした。(これについての詳しい検証内容は、前の記事を参照してください)

■RSSurfaceViewへの移行による回避
この方式については検証していません。
しかし、私の場合、そもそもこの方式を採用することが不可能です。
というのも、RSSurfaceViewはAPI level 11以降でないと使えません。
API level 11以降の場合、Android2.3以前での動作が不可能になります。
API level 11以降は、Android3.0以降で動作するアプリケーション専用

現在、世の中で最も普及しているAndroid2.3を切るという選択肢は、当面ありえません。
今のところ、Android3以降の機能を使いたいアプリ限定で利用可能な選択肢といえます。
少なくとも、ゲーム開発者でそれを考えている人はほぼ居ないと思います。

■結論
大半のアプリは、GLSurfaceViewへの移行で問題を解決できます。
私が検証してみた限り、けっこう大変な作業になると思いますが。。。
ガッツがある方は、がんばって移行してください。
しかし、ピクセル単位で描画内容を更新するアプリの場合、アプリ開発者でも問題の回避は不可能です。

つまり、Android4.0(or3.0)でデグってます。
Googleが修正しない限り、何ともなりません。
「Googleが修正しない限り」というのは、必ずしも正しくはないかもしれません。今では、Motorola側の作りこみ部分の不具合の可能性の方が高いかも・・・と、思っていたりします。
2012年9月7日・記
なお、この結論は、私なりにちゃんと実機で検証した上で得たものです。
しかし、私が技術的に至らないだけで、別に良い回避手段があるかもしれません。
私は、その可能性について、探り続けるしかありません。
(ですが、全然見つからないので、開発自体を投げ出しそうです...)

もちろん、Googleが本件を不良と認めて、修正するのがベストですが。
ただ、私の英語力では、Googleと交渉する自信が全くありません。
他力本願でアレですが、そこは英語が得意な人or米国のデベロッパーにお任せで。
たぶん、4.0がもっと普及すれば、米国でもすぐに叩かれる筈。

なお、この記事を英訳して、blogへ記事を転載してくれる方が居るとすれば、有り難いです...
この記事の内容の転載はご自由にどうぞ。
ただし、転載先で発生したトラブル等についての責任ついて、私が取ることはできません。
その点はご了承ください。

2012年7月14日土曜日

OpenGL/ESによる2D描画の高速化 (update-6)

VG-EngineのJava部分の描画処理をSurfaceView+CanvasからOpenGL(GLSurfaceView+GL10)に変更したところ、処理性能が大分落ちてしまった問題が直りつつあります・・・ですが、今一歩。。。

ポイントとなるonDrawFrameの処理は以下のような感じ。
public void onDrawFrame(GL10 gl10) {
// VG-Engineの描画処理を呼び出す
int rc=SHOT04.setVram(vram);
if(0!=rc) {
System.exit(0);
}

// VRAMの内容をテクスチャ領域へ転送
// GLUtils.texSubImage2D(GL10.GL_TEXTURE_2D,0,0,0,vram);
gl10.glTexSubImage2D(GL10.GL_TEXTURE_2D,0,0,0
,TXSIZE
,YSIZE
,GL10.GL_RGB
,GL10.GL_UNSIGNED_SHORT_5_6_5
,vbuf
);

// 描画
drawUv.put(vramPositions);

drawUv.position(0);
gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl10.glVertexPointer(3, GL10.GL_FLOAT, 0, drawUv);
gl10.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);

}

最初のSHOT04.setVramというのは、VG-Engine(エミュレータ)の中核描画ルーチンです。
従来、引数に渡しているvramはBitmapクラス(AndroidBitmap)でしたが、byte配列に変更。
この部分で、仮想VRAM(240x320pixel)の描画処理を行います。
AndroidBitmapのlock/unlockが無くなった分、高速になったかも。
ただ、ポイントはそこではないです。

ポイントは、「VRAMの内容をテクスチャ領域へ転送」というコメントのところです。
この部分を、GLUtils.texSubImage2DからGL10.glTexSubImage2Dに変更しました。
GLUtils.texSubImage2Dの場合、AndroidBitmapを引数に渡します。
しかし、転送するバッファサイズを指定できないから、常に全体を転送する無駄が生じます。
OpenGLの仕様上、テクスチャ画像の辺の長さが2のn乗でなければならないので、VG-EngineのVRAM仕様(QVGA=240x320)の場合、AndroidBitmapのサイズは256x512pixelという具合になり、無駄なデータ転送が発生する訳です。

GL10.glTexSubImage2Dの場合、テクスチャバッファに転送するバッファの幅と高さ情報を指定できるので、転送量を256x320にすることが可能になります。(ちなみに横方向は240にすると危なそうなので、256のままにしておきました)

これにより、転送量を262,144byteから、163,840byteに削減できます。
ほぼ半分程度の削減になりますね。(47.5%の削減
OpenGL内部でのバッファ⇒テクスチの変換処理が恐ろしく遅いので、この削減効果は極めて大きいです。

ちなみに、テクスチャやテクスチャの表示位置を設定する処理は、全てonSurfaceChangedで纏めてやってます。
つまり、変換イベントを検出しない限り、1回ポッキリ。
こうすることで、バッファリング演算の処理回数を大幅に削減できます。
大幅っていっても1テクスチャのみだから高が知れてますが。



描画時の頂点バッファ演算(2ポリゴン分)は削れませんが。(この部分も最適化の余地があります)
なお、サーフェースのクリア処理(glClearとか)は大して重くないけど、やる意味が無いから、やってません。
(テクスチャは画面全体のVRAMが1枚のみだから、こういうやり方でOK)

しかし、このやり方でやっても52~58fps程度しか出ない。(IdeaPadA1の場合)
やはり、常識的に考えて、テクスチャの書き換えというのは重過ぎる。。。
(当然ながら、ボトルネックはglTexSubImage2Dです)

ちなみに、上記はBVQ(Basic Vert Quads)という、割とオーソドックスな手法です。
DTE(Draw Texture Extension)の方が高速という噂があるので、DTEも試したのですが、結果は同程度。
(性能が同程度なら、オーソドックスな手法の方が良かろうと思います)

やはり、テクスチャの書き換えによる描画という手法自体が鬼門ですねぇ・・・。
まぁ、最初から知っていたことですが。(知っていたから、当初Canvasを使っていた訳で)
surface lockをサポートしている分、OpenGLよりはDirect3Dの方が2D向きです。
何故、OpenGLはsurface lockをサポートしていないのやら・・・
3D onlyのゲームでも普通に要ると思うのですが。

半分、諦めの境地です。
ですが、諦めるのはまだ早い。
「思い切って、フルネイティブ(NativeActivity)にしてしまえばどうだろう?」というのも、無くは無いです。
当初、Android特有の拡張機能を取り込むのに不便だから避けてましたが、必要になるとすれば広告(アフィリエイト)や何らかの通信サービスを入れる時ぐらいだろうし、そういうものを実装するつもりは今の所無いので。(でも、後からやりたくなったら困るから、中々踏み切れない)





追記

上記のonDrawFrameの処理時間を測定してみたところ、だいたい平均で9ms~10msぐらい。

個別に測定してみたところ、
・仮想VRAM作成(setVram)が3~6msぐらい(ゲーム本編の処理量による)
・テクスチャ作成’(glTexSubImage2D)が5~15msぐらい(平均すると7ぐらい)
・描画が1~2msぐらい

たぶん、これが限界性能なのかも。
もうちょっと最適化余地を探ってみますが。

あとは、JavaVM(ダルビッシュでしたっけ?)とかAndroid側の制御や、VGSの処理(OpenSL)の制御やらが色々絡んでって感じかなぁ・・・ただ、VGSは別スレッドの筈だから、そんな大したものではないです。なので、やはりNativeActivity化が一番優良の策なのかも。。。


キツイなぁ。


追記2

とりあえず、NativeActivity化する前に、onDrawFrameの処理(全部)をJNIで実行する形に改造。

↓こんな感じ
@Override
public void onDrawFrame(GL10 gl10) {
// VG-Engineの処理を呼び出す
if(0!=SHOT04.setVram(gl10)) {
System.exit(0); // Exitボタンが押された
}
}

まぁ、Javaで書く意味は無いですからねぇ。
頂点座標についても、onSurfaceChangedで、Cのバッファへコピーし、Cで作成。
ループ処理中のJavaからのデータ受け渡しは一切無いです。

こうすれば、NativeActivityで実装するのとほぼ同等性能だと思います。
たしかに、少しばかり速くなりました。
遅いタブレットマシンでだいたい55~58fpsぐらい。
Razrならほぼ60fps。

う~ん・・・今一歩です。
やっぱり、NativeActivityにするしか無いかなぁ・・・



追記3

まぁ、追記2のコードでダメだったということは、このままNativeActivity化しても効果はほぼ無いでしょう。
Javaコードは、タッチ等のイベント処理とonDrawFrameしか走らないので。
なので、圧縮テクスチャを利用してみようと思います。

一般論で、「圧縮」というと遅そうなイメージですが。
ただ、OpenGLの世界では、圧縮=色のビットレートを落とすことという意味らしい。
まぁ、圧縮であることには違いません。
データ転送量が減るから、圧縮した方が高速だということです。

16bitカラーから8bitカラーに変更した場合、glTexSubImage2Dの転送量が次のようになります。
・16bitカラーの場合 = 240×320×2 = 153,600byte
・8bitカラーの場合 = 256色×3byte+240×320×1byte = 77,568byte


VGEの仮想ハードウェアの色表示性能は8bit(256色)なので、表現性に劣化は生じません。
(VGEのハードスペックを設計した時、8bitカラーを採用しておいて本当に良かった・・・)
表現性に劣化が生じずに転送量を1/2にできるというのは美味しすぎる。
つまり、これをやらない手はないです!



追記4

ほぼ、チェックメイトになりました・・・
圧縮したテクスチャ情報を送信する場合
glCompressedTexImage2D または
glCompressedTexSubImage2D を使います。

glCompressedTexImage2Dの場合、転送する画像サイズが、テクスチャサイズと同値でないと失敗します。
・VRAMサイズ = 240x320pixel(QVGA)
・テクスチャサイズ = 256x512pixel
という差異があり、テクスチャサイズでしか転送できない場合、倍程度の無駄領域が発生してしまいます。
なので、折角圧縮して転送量が半分になるのに、無駄領域の転送により転送量が倍になる・・・
・・・要するに、完全なる無意味状態。

glCompressedTexSubImage2Dの場合、圧縮形式が通常のGL_PALETTE8_R5_G6_B5_OESとかではなく、PVRTC方式というGPU内部のアルゴリズムで圧縮しなければなりません。
一応、glext.hに以下の4種類が#defineされています。

#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG                      0x8C00
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG                      0x8C01
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG                     0x8C02
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG                     0x8C03

で、この圧縮方式ですが、けっこう複雑です。
要するに、GPU内部でこのアルゴリズムでテクスチャ情報を変換して保持しているから、テクスチャイメージの書き換えには膨大な処理時間を持っていかれてしまう訳ですね。
なので、仮に自前のCPU演算で同じ圧縮演算をしたとしても、GPU任せの変換と比較して膨大な処理時間を食う訳です。


という訳で、ほぼチェックメイトです。
「ほぼ」というのは、まだ2つほど別のアイディアがあるという意味です。

【アイディア1:ベースロジックの改造】
テクスチャ変換処理を完全に別スレッド(非同期)にして、onDrawFrameのサイクルが呼ばれる時点で完成した前フレームのテクスチャを画面に出力する(毎回1フレームの遅延が発生するけど、かなり軽くなる筈)


【アイディア2:総転送量を減らす】
テクスチャを2枚(256x256と256x64にして、組み合わせて256x320にする。
この方式はかなり危険です。
・つなぎ目の境界がボンヤリ見えてしまう可能性がある。
・2回glCompressedTexImage2Dを呼び出す必要がある。





追記5


とりあえず、【アイディア2:総転送量を減らす】を実装してみました。
つなぎ目は割りと気になりません。
しかし、やはり、2回glCompressedTexImage2Dを呼び出すオーバーヘッドが思ったよりも大きい。
一応速くなりましたがね。


IdeaPadA1で、55~56fpsで安定していたものが57~58fpsで安定する程度に。。。
もはや、ギブアップ寸前。。。

やっぱり、OpenGL/ESは使い物にならない。。。

そもそも、ポリゴンのテクスチャとは、頻繁な書き換えを想定したものではないので。
OpenGL/ESで性能を出そうと思えば、パターンデータは完全に固定化する必要があるというのが、結論かな。
しかし、2Dゲームの場合、そんなんだと使い物になりません。。。
ピクセル単位での書き込みや、スプライトのマスキング表示などが必須なので。
それらはパターンデータの加工が必須。
テスクチャを書き換えずに利用する方式だと、パターンデータの加工はできない。
よって、チェックメイト・・・。


ちなみに、onFrameDrawのC側の関数の入口と出口にログ出力処理を入れてみたところ、
I/SHOT04  (16557): 2012/07/15 19:01:37.562 start
I/SHOT04  (16557): 2012/07/15 19:01:37.578 end (4ms)
I/SHOT04  (16557): 2012/07/15 19:01:37.579 start
I/SHOT04  (16557): 2012/07/15 19:01:37.595 end (16ms)
I/SHOT04  (16557): 2012/07/15 19:01:37.597 start
I/SHOT04  (16557): 2012/07/15 19:01:37.612 end (15ms)
I/SHOT04  (16557): 2012/07/15 19:01:37.613 start
I/SHOT04  (16557): 2012/07/15 19:01:37.629 end (16ms)
I/SHOT04  (16557): 2012/07/15 19:01:37.630 start
I/SHOT04  (16557): 2012/07/15 19:01:37.646 end (16ms)
I/SHOT04  (16557): 2012/07/15 19:01:37.647 start
I/SHOT04  (16557): 2012/07/15 19:01:37.662 end (15ms)
I/SHOT04  (16557): 2012/07/15 19:01:37.664 start
I/SHOT04  (16557): 2012/07/15 19:01:37.671 end (7ms)
I/SHOT04  (16557): 2012/07/15 19:01:37.686 start
I/SHOT04  (16557): 2012/07/15 19:01:37.694 end (8ms)
という具合。

絶望的な遅さです。
何よりバラツキが激しい。
やっぱり、PVRTCが相当なクセモノです。
完全なソフトウェアレンダリングの方が高速に処理できる気がしてきたので、そっちの方向で探ってみるか。。。



追記6


そもそも、今回の問題の発端になったCanvasがICS(Android4)で劇的に性能劣化するようになった原因のGoogle公式回答が見つかりました。
https://groups.google.com/forum/?fromgroups#!topic/android-developers/CK_jkHEQoOM


Google「すまんね、lockCanvasはAndroid3以降、ハードウェアアクセラレーションしないよ。

えっ?
それだけ?

質問している方は、代替策を聞いているのに、代替策に関しては無策ということはバグですね。
ICSからのバグならまだしも、Android3の頃からのバグ(仕様欠陥)ってことは、直す気ナシか?
Googleみたいなスカタンな連中にOSを作らせた結果がコレです。

完全に推測ですが、ICS開発中のGoogle内部でのやりとりは、大方
・電池持ちについて検討
・SurfaceView+CanvasのH/Wアクセラレーションを切れば電池持ちが良くなる
・ゲームが遅くなる可能性については黙認 (デグっても問題なしと判断)
といった感じですかね。

さて、どうしたものか。
Android 2.3専用にして作り続けるか、開発自体をドロップするかの二択?
厳しいなぁ。。。

とりあえず、やる気が大分削げました。
Windowsでゲーム本編の開発を進めることはできるから、作りながら待つべきか。

2012年7月13日金曜日

Android4大幅性能劣化

Android4で、自作アプリの処理性能が大幅に劣化する問題が発生。
たぶん、CanvasがGPUを介さずに動いているのかも。

私のアプリは、Cで仮想ビデオメモリ(240x320pixel )の情報をAndroidBitmapとして作成し、それをSurfaceViewでCanvasを使って、画面サイズにあわせて拡大描画するという処理方式。
スプライト描画などは、Cで実装している完全に自前のロジックです。
その部分では問題が出る筈が無いので、問題の原因はほぼ100%、SurfaceView or Canvasのどちらか。

実は、Canvasが相当重い処理であろうことは想定済みです。
ただ、手元にあるシングルコアの陳腐なAndroid2.3機(Lenovo製)でもそのCanvas方式で60fpsを出せていたから、「問題ないのかな?」と、思っていたのですが、どうやらAndroid4だと、問題が出てしまったという訳です。

もちろん、Android4がバグっているだけという可能性も否定できません。
しかし、改善の余地はあるから、この部分の処理方式を大幅に変えてみようと思います。
とりあえず、OpenGLでテクスチャを使う方式に変更してみようか・・・。
OpenGLは苦手ですが....


しかし、こんなショボイ性能デグを起こすあたり、流石、Googleさん。
やはり、Java脳の人々は、性能に無頓着すぎる。

2012年7月9日月曜日

3面進捗が・・・

結局、NHKがらみの対応(参照)で、殆ど開発が着手できず・・・
実際にやったことといえば、テレビの処分と解約の手続きだけで昨日(7/7)全て完了していたのですが、主に法律関係の調査と理論武装などに若干の時間が掛かりました。法律は難しいですねぇ。でも、法律を勉強するのは楽しいので、あまり入れ込みすぎないように注意する必要があります。(私の場合、下手をするとネタで司法試験予備試験でも受けてみようかという発想になりかねない)

本当は、今日、3面が完成する予定だったんですが、当然ながら完成せず。
9月公開という線については、若干暗雲が立ち込めてきましたが、まだ焦るような時期じゃない。
平日が割りと暇になる頃合なので、平日にチャージできそうですし。

2012年7月3日火曜日

テスター

3面中ボス作成中。
とりあえず、夏中には完成させて9月頃にはリリースしたい。
9月~10月中旬ぐらいまでは、ゲーム開発以外に時間を費やしたいので。

しかし、問題は、テスターをどうやって確保するか。
iPhoneなら持っている人が結構沢山居ますが、Androidだと持っている人が限られる。

概ね、ドコモを使っていた人が嫌々Androidを選んで、それ以外はiPhone一択みたいな感じですかね。
ドコモのガラケーを使い続け、スマホは他キャリアのiPhoneという人すら居ます。
(その方が、ドコモのAndroidにするのよりも安いとか、なんとか)


両方の機能をよく知っていれば、iPhoneという選択肢は有り得ませんが。
iPhoneが商業的に成功している理由は、両方を良く知っていない人(最初は皆そう)が「Android or iPhoneであれば、何となくiPhone」という空気・・・ですかね。(少なくとも、機能云々が原因ではないと思います)


まぁ、私はiPhoneが今後も売れ続けようが、沈没しようがどっちでも良いです。
ただ、現時点の普及率が二分している訳なので、本気でテスターを確保しようと思えば、前倒しでiPhone移植をしてしまうのも手かもしれないと、思いつつあります。
ただ、iPhoneはステーブ・ジョブズの亡霊と共に沈む船だと思っているので、移植へ乗り切るかどうかの判断は、ギリギリ(最低でもiPhone5登場あたり)まで、遅らせたい所です。無駄な箱を買うのは勿体無いので。

2012年7月1日日曜日

曲のタイトルについて

一昨日から、ほぼぶっ通しでSHOT04(NOKOGI-Rider)で使う音楽データを作成し続けて、ようやく全曲完成。
耳が凄い疲れた・・・
電子音をブッ続けで聴くのは、なかなか応えます。。


ちなみに、製品版に収録する曲は、
・体験版で公開中の4曲(1面、1&3面ボス、ステージクリア、2面)
・VGS Chiptune Musicで公開中の2曲(3面、4面)
・未公開の4曲(5面、2&4面ボス、5面ボス、エンディング)
で、合計ジャスト10曲。
※現時点で公開中の曲は、完成時に若干修正や変更が入ります。


なお、VGS Chiptune Musicの曲は、
・3rd stage ⇒ 3面
・You must open a box ⇒ 4面
という割り振り。

4面の曲のタイトルは、本当は「その箱を開けてはならない」なんですが。
You must open a box」を直訳すれば、「あなたは、ただひとつの箱を開けなければならない」でしょうか。
もちろん、直訳で解釈することは、まず無いと思います。
ちなみに、boxは女性器という意味のスラングですね。

そんなチャレンジャーなタイトルを付けた理由は、この曲は元々、ピスコラの曲投稿サイトでやってたコンテストに出す為に作った曲で、その時のテーマがboxだった為です。そして、特にどんなboxかという指示が無かったので、この場合、テーマ=女性器と解釈するのが普通だと思います。ちなみに、私以外の参加者がそのテーマで投稿していた曲は、全般的に宇宙めいた感じのものが多かった・・・なるほど。
で、そういう意味で解釈すると、「その箱を開けてはならない」だと、人類の滅亡を望んでいるような感じのニュアンスになってしまうので、不健全かな?と思い、健康的な感じにしようと考えた結果が現状のタイトル・・・という訳です。(それでも、フェミーな方からは叩かれそうな気がしますが、、、)


そもそも私は、曲にタイトルを付ける行為自体が、ナンセンスだと思っています。
「その箱を~」みたいに、必要に迫られて命名したことは何度か有りますが。
しかし、一度として真面目に命名したことは有りません。

動機や動機が変化する時の要因として、何らかのイメージやテーマは持つとはしばしば有ります。
そして、4面の曲を作曲時の動機の変化要因がbox(=女性器)であったことは、確かです。
しかし、ソレはあくまでも創作の場面でのみ必要なものであって、一般的な作品として公開する時には不要。
コンテストとかだと、否応無くタイトルを命名されることが強いられるので、仕方なくつけますが。
だから、真面目な命名ができる訳もなく。


ただ、曲にタイトルが不要だとは思ってません。
曲のタイトルは、曲の聴き手が感じたままのもの付ければ良いと思います。
例えば、ベートーヴェンのピアノ協奏曲5番の皇帝みたいな感じで。
「皇帝」は、第三者が勝手に付けた名称です。
その呼称(俗称?)が多数の人に支持された結果、今日では正式なタイトルになっている訳です。
実際聴いてみれば、「あぁ、皇帝だね」と成るほど納得できる。(皇帝は特に分かり易い)

2012年6月28日木曜日

最適な音源スペック

SHOT04(NOKOGI-Rider)の最終ステージの道中曲を作成中。
作曲ではなく、作成中。
曲自体は去年、ピスコラで作曲済みで、ピスコラ⇒VGS(自作波形メモリ音源)への変換するため、MMLを書き起こす作業中です。ピスコラだとテキストの楽譜データに変換できないのが痛いですが、大分慣れました。

最終ステージの曲は最大4声の対位法+ベースで作ったのですが、VGS(6チャネル)で全ての声部が収まりきらないから、目立たない声部をストレッタでぶった切って別の声部に繋げ直したりする必要があり、中々面倒。

ちなみに、
・4声の対位法⇒4チャネル
・ベース⇒1チャネル
・ドラムス⇒1チャネル
という割り当てにすれば、全部収まりますが、主旋律は際立たせる為に2チャンネル使うので、どうしても1声削る必要があります。

VGSを改造して声部を増やす(7チャネル以上にする)のは簡単なんですが、それは絶対にやらない。
漠然とした拘りのようなものがあります。
私がコンピュータで作曲を始めた頃、最初に使っていた音源がヤマハのYM-2203ですが、それの仕様が6チャネル(FM音源3チャネル+SSG音源3チャネル)でした。その後、YM-2608やMIDI音源各種(SC55ST, JV1010, SC8820, MU80, MU500あたり)を触ってきましたが、何だかんだで、聴くのも作るのもYM-2203の音楽が一番楽しい。恐らく、私の好きなゲームに一番合う音楽が、6チャネルぐらいのスペックの音源の音楽なんだろう・・・と思います。


だから、音源スペックを拡張するのはダメ。
音楽を音源スペックに合わせるのが正解。

2012年6月24日日曜日

3面

SHOT04(NOKOGI Rider)の3面の作成に入る前に、想定以上に蛇行レーザーの作成に時間を取られましたが、無事、作成に入りつつあります。

レーザーは、若干角度が粗い部分もあります。。。
とりあえず、許される範囲内に落ち着くことが出来たので良しとしておきます。
静止画で見ると粗さが目立ちますが・・・
あと、低難易度のモードだと速度が遅めな分、プレイ中にも若干粗さが目立つかも。

今の所、3面は大多数の背景を海にする予定。(陸もある)
1面=陸地メイン(海もある)
2面=ダンジョン
3面=海メイン(陸もある)
4面=基地

5面=中枢
という感じにするつもり。

ただ、単純にその状態だと3面が1面とイメージが被ってしまいます。
ステージ毎に特徴を作る為、敵パターンは基本的に刷新するようにして作ろうとしていたのですが、それでも若干目新しさに欠けるように見えたから、レーザーを頑張っておいた訳です。

2012年6月23日土曜日

Laser

敵のレーザーを作成中。
真っ直ぐ飛ばすレーザーなら簡単なんですが、ホーミングやカーブは若干面倒。

どちらにしても、回転機能が無いハードで実装する場合、先ずは回転パターンを作画します。
↓こんな感じで。
で、真っ直ぐ飛ばすだけなら同じラジアン値で連射すればOK。
普通の弾を飛ばすアルゴリズムでそのまま実現できます。
ちなみに、SHOT04(NOKOGI Rider)の敵弾は、1発毎に下図の構造体で情報を管理しています。
ただ、このデータ構造だと、
(1) レーザーを目標物(自機等)に向かってホーミングさせる
(2) レーザーをカーブさせる
といったことが実現できません※。
※正確には、不可能では無いけど複雑になり過ぎて処理効率が悪い

上記(1)(2)を実現するには、トレースというアルゴリズムを実装する必要があります。
トレースというのは、一番先頭のオブジェクト(リーダー)の動作情報(ログ)を記録しておき、サブオブジェクト(メンバ)は一定間隔過去のリーダーのログと同じ動作をする・・・というアルゴリズムです。


分かり難いですね・・・分かり易く言うと、
・ドラクエのパーティーの動き
・グラディウスのオプションの動き
・インストーラーがプログラムのインストールに失敗した時に、インストール前の状態に戻す動き
・データーベースでコミット済みの状態を変更前の状態に戻す動き(ロールバック)
などなど。
これらは皆、同じトレースです。
インストーラやデータベースのロールバックはバックトレース(ログを逆順に追跡)ですが、本質は同じ事。


で、トレースを実現するのには、ログ(ジャーナルともいう)が必要。
現状の敵ショットのメンバにログを持たせても良いですが、そうなるとメモリ効率が悪いし、メモリ効率を良くすると処理効率が悪い・・・つまり、別テーブルで管理するのがベストです。
とりあえず、下図のような感じのテーブルを追加。
まだ、肝心のプログラムを作っていないので、多分上記は何かデータが不足しているかも。
あと、ログはリングバッファで管理(256フレーム分)。
それで足りるかは不明。
事前に設計できなくも無いけど、作ってから調整。


ちなみに、リングバッファの場合、インデクスの加算処理で無駄な分岐によるパイプラインハザードを抑える為、要素数は2のn乗にする必要があります。(そうすることで、AND演算だけで限界時のループができるので)





【追記】
プログラムを作成してみました。
表示したみたところ、下図のような感じ。


ちなみに、テーブルの内容は結構変えました。
移動速度のログは残さない(加速しない)仕様にして、後は座標を最大の長さ分保持したり云々。
↓こんな感じで。(当初想定よりも大分シンプル)

/* 敵レーザー */
struct ELaser {
int flag; /* フラグ兼フレームカウンタ */
int ix; /* 初期X座標 */
int iy; /* 初期Y座標 */
short fx[32]; /* X座標(*8) */
short fy[32]; /* Y座標(*8) */
int len; /* 現在の長さ */
int maxL; /* 最大の長さ */
int R; /* 先端のラジアン値(0~627) */
unsigned char S; /* 速度(1~15) */
unsigned char itR; /* ラジアン値の変更間隔(フレーム数) */
unsigned char addR; /* ラジアン値の角度増減量 */
unsigned char typeR; /* ラジアン値の変更方法(0:変更しない, 1:右回転, 2:左回転, 3:ホーミング) */
short logR[512]; /* 角度ログ(512フレーム分) */
};


レーザーの長さはMAX=32にしておきました。
32×16=512ピクセルが最大長。これだけあれば十分。
あと、10度単位で回転パターン(36パターン)を準備したのですが、若干繋ぎ目が分かり易い・・・
ただ、これは気にならないレベルなので、まぁ良いかな。
8度単位(45パターン)にしてみようか微妙なところ。


ログのサイズは256だと足りないので、512に拡張。
速度&長さがMAXの場合に必要なログ数が32×16=512なので。
速度&長さがMAXなんて、鬼畜過ぎて使うことは無いと思いますが。


ちなみに、末尾~尻尾まで全てのレーザーが画面外アウトしたら消滅する仕様。
角度がキツ過ぎると、画面に戻ってくる場合があり、それは鬼畜過ぎるので、後で仕様変更するかも。




2012年6月21日木曜日

PowerUp

STGにパワーアップって必要なんですかねぇ・・・と、常々思っています。
で、実際にSHOT04は、パワーアップアイテム無しで作っている訳です。
しかし、STGにはパワーアップが有って当たり前という固定観念みたいなものがあります。

敢えて固定観念を捨てて、パワーアップに対して何を求めるかと考えて思いつくものは、
(1) だんだん強くなる成長過程を愉しむ
(2) 攻略を有利に進める
(3) 稼ぎ要素のひとつ(最高レベルの状態でPWアイテムを取得すると得点が入る等)
の3つぐらい。

上記は全て、無ければ無いで問題無い気がしています。
(1) ⇒ プレイヤーの腕前が上達していく成長過程を愉しめば良い
(2) ⇒ パワーアップが無い前提で攻略を有利にするパターンを探る楽しみがある
(3) ⇒ 他の稼ぎ要素があれば問題無い

寧ろ、パワーアップが有ることで生じるリスクも有ります。
・パワーアップアイテムを取得した時、攻撃力や連射速度を上げるケース
 ⇒ ミスによりパワーダウンした時の復活が厳しい
   (こういうゲームの場合、1ミスでゲームオーバーで良いのに・・・と思ってしまう)
・攻撃力は(殆ど)変えずに、拡散度を上げたり、当たり判定だけ大きくするケース
 ⇒ 狙って部品や敵機を破壊するのが難しくなる
   (個人的には、狙って部品等を破壊するのが楽しいと思っているので、これは結構致命的)

ノーパワーアップの方がスコアを稼げる仕組みにして、上級者(スコアラー)はノーパワーアップ、初級者はどんどんパワーアップして遊べるようにする・・・という案もあったのですが、「パワーアップアイテムをわざと落とす」というのは微妙だったので却下。
「わざとミスをする」とか、「わざとアイテムを逃す」というのはダメだと思います。
ゲームの本質(=競技性)と逆行してるから気がするので。
ミスはしたくないし、アイテムはなるべく取りたい。

合理的ではないものを作りたい

ここ最近、実機版の東方VGSの開発が忙しくて、東方VGSの曲追加が滞っています。 東方VGS(実機版)のデザインを作りながら検討中。基本レトロUIベースですがシークバーはモダンに倣おうかな…とか pic.twitter.com/YOYprlDsYD — SUZUKI PLAN (...