2013年8月25日日曜日

RPG作成中・バックアップ、引継ぎ機能

α版に、スキルをすべて習得した状態でさらに習得すると、アプリがクラッシュするバグがありました。申し訳ありませんが、修正はβ版以降とさせていただきたく思います。

スキルについて、発動に必要な能力値を設定しました。ステータス成長や、増加アイテムの利用に、若干の戦略性が出るのではないかと思います。

補助機能では、バックアップと復元の機能を作成しました。前作同様に、ファイルに保存する形式となります。
また、前作データの引継ぎ機能を、解析部分について実装しました。前作のバックアップファイルを読み込む形式としました。どんな引継ぎ特典を設けるかは、シナリオとともに検討したいと思います。可能かどうかわかりませんが、クラスチェンジシステムを導入し、クラスチェンジ用アイテムを特典とすることなどを考えています。

カタログ機能等はリリース後の追加も簡単ですので(内部でのフラグ管理はすでに行っています)、いよいよシナリオ(モンスター、アイテム)を検討、実装していきます。

2013年8月18日日曜日

RPG作成中・職業、スキル周りを調整

未実装だったスキル、魔法の実装を完了しました(難しいもの、あまり意味がなさそうなものは廃止しました)。

また、職業ごとの攻撃判定等にいまひとつ納得いかない部分があったので手直ししました(職業ごとの物理攻撃能力に差が出にくくなっていました)。上級職の導入も視野に入れつつ、職業別に、どのような能力が成長するかを表現するような感じにしてみました。たとえば戦士と僧侶ではこんな感じです。

スキルについても、各能力別に習得できるようにしようと思います。

引き続きシナリオの検討と、バックアップとリストア、引継ぎ機能等を作成していきます。

2013年8月10日土曜日

RPG作成中・α版を更新

いくつかのバグを修正いたしました。戦闘スキルの使用率が低い問題は、ご指摘いただいていましたが、なかなか原因に気付けませんでした。失礼いたしました。
また、今回より3人でなくても、探索できるようにしました。ただ、3人決め打ちにしている箇所で、バグがあるかも知れません。

α版の更新は、何もなければここまでにしたいと思います。

スキル導入によって、AI処理の作り込みが難しいため、バランスが非常に取りづらくなりました。深層階に着くころには、たいていMPが尽きているのはどうかと思っています。MPなどの概念をなくして、戦闘ごとにリセットすれば、ずいぶん楽なのですが……(他の放置型RPGはそんな感じかと思います)。

いろいろ考えてみたいと思います。

2013年8月4日日曜日

RPG作成中・シナリオ検討、未実装機能の実装など

シナリオやシステムを検討しつつ、少しずつ未実装スキルの実装等を進めています。

シナリオについては、前作もほぼ一本道で、たいした謎解きもありませんでしたが、今回もあまり凝った物でなくてもいいのかな、と思っています。いわゆる、ありがちな感じでいいかなと。
それよりも、放置型RPGとしての面白さを出すことを目指したいと思います。たとえば、ログのバリエーションを増やすなどしてそれらしい雰囲気を出したり、システム面での機能強化を図ったり(アイテムの強化要素や罠・泉以外のイベント、使い勝手の向上など)、意外性のあるダンジョンを用意したり、といったことです。

とはいえあまりやりすぎると、いつまでも完成しなくなりますので、難しいです。今までにいただいたコメントなどを参考に、検討していきたいと思います。よろしくお願いいたします。

アプリ作成中にハマった点など

先日、α版をリリースしましたが、いくつもバグを出してしまいました(申し訳ありません)。いくつかはまった点を書いておきます。多少ともどなたかの参考になれば幸いです。

Activityのライフサイクルを確認するときは開発者オプションを使う

最もバグが出てしまったのは、Activityの状態管理の部分です。

このゲームでも、Androidの仕様通り、ActivityのonCreateやonPause等で、BundleにActivityの状態を保存しています(所持金や所持品、パーティ情報など)。この処理は最初から組み込んであったのですが、テスト環境(エミュレータおよび実機)では、ほとんど動作していなかったようです。テスト中にはそのアプリしか動かさないので、Activityがずっと保持されていたわけです。

開発者オプションの「アクティビティを保持しない」にチェックを入れると、Activityから離れたときに、すぐにGCされるようになるようです。リリース前に必ずこの状態でテストしたほうがよいでしょう。

ParcelクラスのreadParcelableに指定するクラスローダ

Parcelから、Parcelableのオブジェクトを取り出すときに使うのがreadParcelableメソッドですが、この引数に指定するクラスローダは、アプリのクラスローダにする必要があるようです(Androidのクラスローダにはシステムクラスローダとアプリのクラスローダがある、参考)。readParcelable(null)とすると、システムクラスローダが使用され、ClassNotFoundExceptionになります。

以下のようにするといいようです。
dungeonContext = source.readParcelable(this.getClass().getClassLoader());

Activity間で共有するインスタンスとライフサイクルに注意する

Activityの遷移時に大きめのデータを渡すとき、以下のページにあるようにpublic static fieldでデータを共有しています。


このとき、前のActivityは遷移後もなくなったわけではなく、次のActivityが生成された後で、onPauseやonDestroyが呼ばれることがあります。そこで、共有したオブジェクトを操作してしまうと(たとえばBundleに保存するなど)、わかりにくいバグが起きる可能性があります。

渡した後は触らないようにするか、素直にIntentを使うのがよさそうです。

ダイアログ表示ボタンを連打するとDialogFragmentが二重に表示される

ボタン連打でダイアログが二重に表示される問題は以前からありました。前作ではDialogFragmentは使っていませんでしたので、「表示する処理(ボタンの処理)で表示中かどうか判定する」という泥臭い処理を行っていました。

今回はダイアログの数が増えていますので、すべてにその処理をいれるのは面倒です。また、DiagloFragmentを使うようにしたので、共通化ができそうです。

そこで、以下のブログの記事を参考にさせていただきました。有用な記事をありがとうございます。


ただ、これでも「二つのボタンを同時押しする」と二重に表示されてしまいます。実機だと、二本指でタップすることで、容易に再現できます。

ですので、アプリのダイアログ基底クラスに、以下のように表示中フラグを導入しました。とりあえず問題なく動作しているようです。

private static boolean dialogShowing = false; // 表示中フラグ
private static Object lock = new Object(); // ロック用(いらないかも)

public void show(FragmentManager manager) {
    synchronized (lock) {
        if (dialogShowing) {
            return;
        }
        dialogShowing = true;
    }

    dismissPreviousDialog(manager, ALERT_DIALOG_FIXED_TAG);
    super.show(manager, ALERT_DIALOG_FIXED_TAG);
}

@Override
public void onResume() {
    super.onResume();
    synchronized (lock) {
        dialogShowing = false;
    }
}

private void dismissPreviousDialog(FragmentManager manager, String tag) {
    AlertDialogFragment prev = (AlertDialogFragment) manager.findFragmentByTag(tag);
    if (prev != null) { // フラグメントが表示されていなければ処理なし
        Dialog dialog = prev.getDialog();
        if (dialog != null) { // ダイアログがなければ処理なし
            if (dialog.isShowing()) { // ダイアログが表示されていなければ処理なし
                prev.dismiss(); // ダイアログ消去
            }
        }
    }
}
コードが読みにくくて申し訳ありません。