uka.apple のすべての投稿

3日坊主アプリ開発 255日目 対人戦の実装


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄


  • 今回の作業

(´・ω・`)ショボーン
今日は以下項目の実装をしました。

・対人戦の勝敗確定前処理の調整
・サーバの負荷を減らすため、例えば5分以内に再度サーバアクセスがあったら、前回のキャッシュを使うなどの工夫。

残作業は以下となります。

・対人戦の勝者に経験値とロジックパネルを加算する処理 ※9割方完成
・各対人ステージの対人戦歴を参照する機能(履歴取得時、サーバ負荷を減らすため、古い対戦履歴を削除)
・対人ステージのバリエーション増加
・サーバ負荷を抑えるため、対人戦は1日に特定回数までに抑制する。[NEW!]
・対人戦で特定条件を満たしたユーザに特別なロジックパネルをプレゼントする処理 ※バランス崩壊するから中止するかも

1項目増えました!
作業終えても終えても追加実装したい内容が出てきて終わりません・・・
(´・ω・`)ショボーン

  • 次回の予定

対人戦の勝者に経験値とロジックパネルを加算する処理

3日坊主アプリ開発 241〜254日目 対人戦の調整調整調整・・・


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 今回の作業

対人戦の調整

 

もうCATAMAのアプリ開発日記つけ始めてから、254日経ったんだな・・・長くかかったな・・・

今回は、対人戦に関していろいろ作業をしていました。バグから、対人バランス調整から、本当にいろいろ作業していました。
もう一息で完成する気配がそこはかとなくしている段階です。本当だってば!

残作業は以下となります。

・対人戦の勝敗確定前処理の調整
・サーバの負荷を減らすため、例えば5分以内に再度サーバアクセスがあったら、前回のキャッシュを使うなどの工夫。
・対人戦の勝者に経験値とロジックパネルを加算する処理 ※9割方完成
・各対人ステージの対人戦歴を参照する機能(履歴取得時、サーバ負荷を減らすため、古い対戦履歴を削除)
・対人ステージのバリエーション増加
・対人戦で特定条件を満たしたユーザに特別なロジックパネルをプレゼントする処理 ※バランス崩壊するから中止するかも

まだ結構あるな・・・

  • 次回の予定

対人戦の作業

3日坊主アプリ開発 237〜240日目 対人戦の残作業洗い出し


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 今回の作業

対人戦の実装

 

今回は

・各対人スデージに支配者情報を初期登録する処理
・対人戦で相手のロジックパネルを見えないようにマスクする処理
・”ID”がばれるとデータを乗っ取られてしまうので、IDを他の人に見せないように注意書きをする
・対戦相手のレベルを表示

を行いました。

残作業は以下となります。

・対人戦の対戦内容と勝敗結果をサーバに格納 完了
・対人戦の勝者に経験値とロジックパネルを加算する処理
・対人戦で特定条件を満たしたユーザに特別なロジックパネルを加算する処理
・各対人ステージの対人戦歴を参照する機能
・サーバの負荷を減らすため、例えば5分以内に再度サーバアクセスがあったら、前回のキャッシュを使うなどの工夫。
・サーバの負荷を減らすため、古い対戦履歴を定期削除
・対人戦の勝敗確定前処理の調整 9/3 追加
・対人ステージのバリエーション増加 9/3 追加

9/3追記 いろいろ足りない処理に気づいて、作業増やしました。バグも随時修正しています。中々減らない・・・

  • 明日の予定

対人戦の対戦内容と勝敗結果をサーバに格納

3日坊主アプリ開発 230〜236日目 夏休み


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 今回の作業

夏休み

 

私の一番の休息は、アプリを開発することです。

というわけで、夏休みはアプリを開発して遊びました。会社爆発しないかなぁ。

今回は、対人戦のステージ情報を格納するサーバサイドのDB実装と、そのDBを読み取り、クライアントであるiOSアプリに読み込む処理を作っていました。

残り作業は以下となります。もうスタンドアローンとしては完成しているアプリですが、もう一息で対人戦も含めて実装してリリース出来そうです。

・対人戦の勝敗結果と勝者の情報をサーバサイドに格納
・対人戦での相手のロジックパネルを見えないようにマスクする処理
・対人戦の勝者に経験値と相手のロジックパネルを加算する処理、敗者は奪われる処理
・対人戦で特定条件を満たしたユーザに特別なロジックパネルを加算する処理
・各対人ステージの対人戦歴を参照する機能
・”ID”がばれるとデータを乗っ取られてしまうので、IDを他の人に見せないように注意書きをする
・対戦相手のレベルを表示
・サーバの負荷を減らすため、例えば5分以内に再度サーバアクセスがあったら、前回のキャッシュを使うなどの工夫。

  • 明日の予定

対人戦の勝敗結果と勝者の情報をサーバサイドに格納

3日坊主アプリ開発 210〜229日目


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 今回の作業

セーブ/ロード機能の実装

 

Simulator Screen Shot 2016.08.09 23.57.39

クラウドでのセーブ/ロード機能を実装しました。
AppleのiCloudではなく、独自サーバ上(さくらのVPS)に実装しています。

もちろん、クライアント(objective-c)の開発と共に、サーバ側のサーブレット(java)やユーザテーブル(Mysql)の作成も同時に行いました。

画像の「Change ID」ボタンは、スマホを買い替えた場合などデータを端末間で移動する場合に使用します。元の端末で表示されているIDを、新しい端末で「Change ID」ボタンにより入力すると、データが引き継げます。

iPadにも対応予定のゲームですが、iPhoneやiPad間でIDを同じに設定しておけば、どちらでも同じデータで遊ぶような使い方も可能です。

素晴らしい!

長いIDを入力しなきゃならないこと以外は!!

ID長い方が、何か有り難みがあるよね。うん。だから長くても大丈夫。

  • 明日の予定

対人戦画面の実装

3日坊主アプリ開発 209日目


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 本日の作業

DB接続管理クラスの作成

 

sql投げるだけで実行してくれるメソッドを持つクラスを作成しました。
コネクションを意識せずに使えるタイプと、自分でコネクションを渡せる2タイプを用意しました。

例外発生時のロギングには、昨日作成した自作クラスを使っています。

使うとき意味なくインスタンス化する必要あるし、ResultSet呼び出し元でcloseしなきゃいけないし、いまいちなので、もうちょっと改造します。

package servlet;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.ResultSet;
import java.sql.Statement;
import servlet.Logging;

public class DBManager {

        Connection getConn() {
                Connection conn = null;
                try {
                    Class.forName(“com.mysql.jdbc.Driver”);
                    // MySQLに接続
                    conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/catama”, “user”, “password”);
                } catch (ClassNotFoundException | SQLException e) {
                    Logging.out(e);
                }
                return conn;
        }

        void releaseConn(Connection conn) {
                if (null == conn) {
                        Logging.out(“connection is null.”);
                        return;
                }
                try {
                        conn.close();
                } catch (SQLException e) {
                        Logging.out(e);
                }
        }

        ResultSet query(String sql) {
                Connection conn = null;
                ResultSet rs = null;
                try {
                        conn = this.getConn();
                        rs = this.query(sql, conn);
                } finally {
                        releaseConn(conn);
                }
                return rs;
        }

        ResultSet query(String sql, Connection conn) {
                if (null == conn) {
                        Logging.out(“connection is null.”);
                        return null;
                }
                ResultSet rs = null;
                Statement st = null;

                try {
                        st = conn.createStatement();
                        rs = st.executeQuery(sql);
                } catch(SQLException e) {
                        Logging.out(e);
                } finally {
                        if (null != st) {
                                try {
                                        st.close();
                                } catch (SQLException e) {
                                        Logging.out(e);
                                }
                        }
                }
                return rs;
        }

        int queryExec(String sql) {
                int result = -1;
                Connection conn = null;
                try {
                        conn = this.getConn();
                        result = this.queryExec(sql, conn);
                } finally {
                        releaseConn(conn);
                }
                return result;
        }

        int queryExec(String sql, Connection conn) {
                int result = -1;
                if (null == conn) {
                        Logging.out(“connection is null.”);
                        return result;
                }
                Statement st = null;

                try {
                        st = conn.createStatement();
                        result = st.executeUpdate(sql);
                } catch (SQLException e) {
                        Logging.out(e);
                } finally {
                        if (null != st) {
                                try {
                                        st.close();
                                } catch (SQLException e) {
                                        Logging.out(e);
                                }
                        }
                }
                return result;
        }
}

  • 明日の予定

サーバにログインする処理を実装する。サーバに各ユーザの識別ID※とユーザ名を格納する。

※appleの推奨するidentifierForVendor(IDFV)を識別IDとして使います。

3日坊主アプリ開発 208日目


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 本日の作業

ロギングクラスの作成

 

スタックトレースやクラス名/メソッド名/エラー発生行番号を記録してくれるロギングクラスを作っていました。

package servlet;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Logging {

  static void out(Exception e) {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat(“yyyy/MM/dd HH:mm:ss”);

    System.out.println(“— ” + sdf.format(date) + ” —“);
    e.printStackTrace();
    System.out.println(“— ” + sdf.format(date) + ” —“);
  }

  static void out(String msg) {
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat(“yyyy/MM/dd HH:mm:ss”);
    StackTraceElement throwableStackTraceElement = new Throwable().getStackTrace()[1];

    System.out.println(“— ” + sdf.format(date) + ” —“);
    System.out.println(msg + “at ” + throwableStackTraceElement.getClassName() + “#” + throwableStackTraceElement.getMethodName() + “(” + throwableStackTraceElement.getLineNumber() + “)”);
    System.out.println(“— ” + sdf.format(date) + ” —“);
  }
}

outメソッドに例外を渡すと、

— 2016/07/19 23:29:41 —
java.sql.SQLException: Field ‘prize_panels’ doesn’t have a default value
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:963)
〜略〜
at servlet.DBManager.queryExec(DBManager.java:96)
at servlet.DBManager.queryExec(DBManager.java:79)
at servlet.Login.doGet(Login.java:38)
〜略〜
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
— 2016/07/19 23:29:41 —

のように、日時とスタックトレースを出力します。

outメソッドに文字(エラーが発生しましたよ)を渡すと

— 2016/07/19 23:29:41 —
エラーが発生しましたよ at servlet.DBManager#getConn(13)
— 2016/07/19 23:29:41 —

のように、日時と発生クラス名/メソッド名/行番号を出力します。

メソッドの中身を書き換えて”コンパイル”すれば、120%自由自在にカスタマイズできます。

シンプル!簡単!分かり易い!

え?commons-loggingとかlog4jとかslf4j-apiとかjcl-over-slf4jとかlogback-classicとか使え?

“設定”覚えるのがめんどいんで結構です。

  • 明日の予定

サーバにログインする処理を実装する。サーバに各ユーザの識別ID※とユーザ名を格納する。

※appleの推奨するidentifierForVendor(IDFV)を識別IDとして使います。

3日坊主アプリ開発 207日目


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 本日の作業

サーバにログインする処理を実装する。サーバに各ユーザの識別ID※とユーザ名を格納する。
※appleの推奨するidentifierForVendor(IDFV)を識別IDとして使います。

 

DBにユーザ情報を格納する前に、DBコネクションの管理や、SQL実行を簡単にする独自DBマネージャクラスを作成していました。様々な処理のベースとなるクラスなのでちょっと時間をかけて作っています。

viコマンドでゴリゴリ書いて、javacコマンドでクラスパス通してコンパイルしています。

eclipseを使え? それほど大量のクラスを作るつもりはないので、手動コンパイルで十分です。たぶん。

  • 明日の予定

サーバにログインする処理を実装する。サーバに各ユーザの識別ID※とユーザ名を格納する。

※appleの推奨するidentifierForVendor(IDFV)を識別IDとして使います。

3日坊主アプリ開発 206日目


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 本日の作業

サーバにログインする処理を実装する。サーバに各ユーザの識別ID※とユーザ名を格納する。

 

ちょっとトラブったので、今日はあまり進捗ありませんでした。やったことを書き殴ります。

まず、mysqlに接続するために、jdbcドライバダウンロードしました。

http://dev.mysql.com/downloads/connector/j/

このページから持ってきたtarファイルを解凍して、中身の

mysql-connector-java-5.1.39-bin.jar

を、webapps/catama/WEB-INFの下にlibディレクトリ作って、そこにコピーしました。

これで、この”catama”のWebアプリケーション内で使えるようになります。

次に、javaのサーブレットから接続してみます。

Connection con = null;
String dbResult = “start MySQL connection.”;
String newLine = “
“;
try {
Class.forName(“com.mysql.jdbc.Driver”);
// MySQLに接続
con = DriverManager.getConnection(“jdbc:mysql://localhost:3306/catama”, “USER”, “PASSWORD”);
dbResult += newLine + “connected to MySQL.”;
} catch (ClassNotFoundException | SQLException e) {
dbResult += newLine + e.toString();
} finally {
if (con != null) {
try {
con.close();
dbResult += newLine + “closed MySQL.”;
} catch (SQLException e) {
dbResult += newLine + e.toString();
}
}
}
System.out.println(dbResult);

しかし、なぜか接続に失敗します。

さんざん悩みまくった挙句、netstatコマンドを使うと、mysqlの待ち受けポートである3306がリストに出てきません。

そこでmy.cnfを確認すると、以下の記述を見つけました。

skip-networking

この行が原因でした。この行をコメントアウトして、無事接続できました。

  • 明日の予定

サーバにログインする処理を実装する。サーバに各ユーザの識別ID※とユーザ名を格納する。

※appleの推奨するidentifierForVendor(IDFV)を識別IDとして使います。

3日坊主アプリ開発 205日目


    ∩∩
   (´・ω・)
   _| ⊃/(___
 / └-(____/
  ̄ ̄ ̄ ̄ ̄ ̄ ̄

  • 本日の作業

iOSアプリからサーブレットにアクセスする際、どちらが単位時間当たりの処理数が多いか確認する。

1.tomcatのサーブレットにアクセスする。
2.nginx経由でtomcatのサーブレットにアクセスする。

 

1つのリクエストが完了したら即座に次のリクエストを投げ、合計100回繰り返す”直列方式”と、0.01秒間隔で100リクエストを投げる”並列方式”の2パターン計測しました。

結果は以下の通りです。

1.tomcatのサーブレットにアクセス

直列:21.186秒
並列:0.501秒

2.nginx経由でtomcatのサーブレットにアクセス

直列:15.162秒 (1の72%)
並列:0.352秒 (1の70%)

nginxを経由した方が3割分ほど速くなりました。尚、リクエストはキャッシュされないよう100個全て異なるものを投げています。nginxの設定はほぼデフォルトです。誰かnginxの方が速くなる理由を教えやがって下さい。

  • 明日の予定

サーバにログインする処理を実装する。サーバに各ユーザの識別ID※とユーザ名を格納する。

※appleの推奨するidentifierForVendor(IDFV)を識別IDとして使います。