カテゴリー別アーカイブ: アプリ開発

Unityでのダウンロードを高速化するアセットの販売をはじめました!

Unityのゲームを作っていて、ダウンロード速度が思ったほど出ないと感じること、ありませんか?

例えば以下のようなケース
■シナリオの進み具合に応じて登場するキャラクターが、衣装や装備品を身につけていて、新しいキャラクターが出てくるたびにダウンロードが発生!
■育成画面でユーザーごとに保有するカードを 種類数分表示するようになってて、スクロールするたびにダウンロードが発生!
■10連ガチャをまわして、出た内容に応じてダウンロードが発生!

通信環境が悪いと結構な頻度でユーザーに待ち時間を発生させてしまいます……
そんなUnityでも、極力シンプルにダウンロードを高速化するアセットを作りました!!

[[ Simple H2 downloader – Unity Asset Store ]]
https://assetstore.unity.com/packages/tools/network/simpleh2downloader-173745

このアセット、Unityが使っている通信機能(WWW、UnityWebRequest)を使わず、代わりに最新の通信プロトコルで実装された通信機能によってダウンロードすることで、高速化を図っています。

Unityの通信機能では、HTTPバージョン1.1(以下、HTTP1)までしか使えません。この HTTP1 では、1回の通信で同時に1つしかダウンロードできないため、複数ダウンロードを並行で行うにはそれなりのマシンパワーが必要になります。また HTTP1 自体の仕様が古く(1995年制定)、テキスト解析にも比較的高いマシンパワー使います。スマホだけでなくサーバーの負荷も高いため、みだりに並行数を増やすことはマナー違反とされています。他にも通信を開始するための準備(DNS解決、暗号方式の擦り合わせ、暗号鍵の生成)も遅いです。もちろんそれらを動かすにはそれなりに複雑な実装が必要になります。

一方、このアセットでは内部で用いる通信プロトコルとして、HTTPバージョン2(以下、HTTP2)を使っています。
こちらを使うことで、1回の通信がさらに細かく制御できるようになり並行して取得できるようになってます、しかも従来の1つ分のマシンパワーで。また マシン負荷が低く抑えられるような工夫が HTTP2 に盛り込まれており、省電力でハイパフォーマンスなダウンロードが行えるプロトコルということもあり、今回採用に至りました。

余談ですが、Webページを表示するブラウザ(ChromeやMicrosoftEdge)では、当たり前のように使われている通信プロトコルがHTTP2なのですが、Unityでも使えるようになるのは、もう数年かかりそうとのこと……
参考:  [[ .NET Standard 2.1 platform support #1330 ]]
https://github.com/dotnet/standard/issues/1330#issuecomment-510495179

ということで、興味を持たれた方は、Unityアセットストアへどうぞ。
速度比較の参考動画もアップロードしてます。

[[ Simple H2 downloader – Unity Asset Store ]]
https://assetstore.unity.com/packages/tools/network/simpleh2downloader-173745

iPhoneプッシュ通知(APNs)の証明書を作る

iPhoneアプリでプッシュ通知するには、
対象のアプリのプッシュ通知証明書を毎年更新する必要がある。
Appleから取得できる証明書は「.cer」だが、サーバーで使うのは「.pem」なので、変換が必要になる。
やりかたを毎回忘れるのでその時に備えたメモ。

事前に済ませておくこと
・鍵ペアを作成する
・証明書の作成に必要な証明書署名要求を作る
・iOS Dev Centerで証明書を作成する
・キーチェーンアクセスに証明書を登録する

以下の手順でやる

・キーチェーンアクセスから証明書と秘密鍵の書き出しを行う
『証明書』からプッシュ通知用の証明書を右クリックして書き出す
apns1

ファイル名は「apns-dev.crt.p12」で保存
apns2

パスワード入力画面では、便宜上、空欄のままOKを押す
apns3

証明書に紐づく秘密鍵を右クリックして書き出す
apns4

ファイル名は「apns-dev.key.p12」で保存
apns5

パスワード入力画面では、便宜上、空欄のままOKを押す
apns6

証明書をPKCS12形式からPEM形式に変換する

cat apns-dev.crt.p12 | openssl pkcs12 -passin pass: -clcerts -nokeys > apns-dev.crt.pem

秘密鍵をPKCS12形式からパスワード付きのPEM形式に変換する(空パスワードは指定できない)

cat apns-dev.key.p12 | openssl pkcs12 -passin pass: -passout pass:PASS -nocerts > apns-dev-with-password.key.pem

秘密鍵からパスワードを除去する

cat apns-dev-with-password.key.pem | openssl rsa -passin pass:PASS > apns-dev.key.pem

証明書と秘密鍵を合成する

cat apns-dev.crt.pem apns-dev.key.pem > apns-dev.crt+key.pem

2015.9.6
NECO

cocos2d-xでゲーム画面を縦に変更

よく忘れるので、備忘録も兼ねてメモ

cocos2d-xでアプリを作る際、初期設定は横向きになっている。
iPhoneで縦画面としてレイアウトするには、以下のように行う。

(1) xcodeのナビゲータエリアから、xcodeプロジェクトファイルを選択し、エディタエリアからGeneral > Deployment Info > Device Orientation から、Portrait のみチェックを入れる。

layout

(2) $PROJ_ROOT/proj.ios_mac/ios/RootViewController.mmの
shouldAutorotateToInterfaceOrientationメソッドの戻り値を下記のように書き換える。

// Override to allow orientations other than the default portrait orientation.
// This method is deprecated on ios6
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return UIInterfaceOrientationIsPortrait( interfaceOrientation );
}

これにより、iPhoneで縦向きで表示される。

layout_portrait

ちなみに、macでも縦向きで表示するには、以下のファイルを編集する。試してないけどwindowsでもイケるはず。
$PROJ_ROOT/cocos2d/cocos/platform/desktop/CCGLViewImpl-desktop.cpp

GLViewImpl* GLViewImpl::create(const std::string& viewName)
{
    auto ret = new (std::nothrow) GLViewImpl;
//  if(ret && ret->initWithRect(viewName, Rect(0, 0, 960, 640), 1)) {
    if(ret && ret->initWithRect(viewName, Rect(0, 0, 320, 568), 1)) {
        ret->autorelease();
        return ret;
    }

    return nullptr;
}

これにより、macでも縦向きになる。

layout_portrait_mac

解像度がまちまちだったりとか、いろいろ問題もあるけど、
まずは縦画面にできた。

cocos2d-xでプロジェクトを作成

Mac環境での作業を想定。
事前にxcodeとpythonのインストールを済ませておく。

cocos2d-xのダウンロード
http://cocos2d-x.org/download

cocos2d-x-3.3.zipを任意のフォルダに展開

セットアップ
setup.py

新規プロジェクトの作成
cocos new [ゲーム名] -p [パッケージ名] -l cpp -d .

ビルドと実行
cocos run -p ios
cocos run -p android

ビルドが成功すれば、iOSシミュレータが起動し、アプリが起動する。
hello_cocos

その他のコマンドはヘルプを参照
cocos -help

Android の WebViewのキャッシュについて

本情報は、Android 2.3までのもので、3.0以上は未確認です。

WebViewアプリを作るうえで、
パフォーマンス向上のため、WebViewキャッシュに関して調べた。
その時のメモ。

◆キャッシュDBの場所
AndroidのWebViewのキャッシュは、dataフォルダ内に収められている。
WebViewCache.dbにsqlite形式で保存される、

/data/data/【パッケージ名】/databases/webviewCache.db

>sqlite3 webviewCache.db
sqlite> .schema
CREATE TABLE cache (

_id INTEGER PRIMARY KEY

, url TEXT, filepath TEXT

, lastmodify TEXT

, etag TEXT

, expires INTEGER

, expiresstring TEXT

, mimetype TEXT

, encoding TEXT

, httpstatus INTEGER

, location TEXT

, contentlength INTEGER

, contentdisposition TEXT

, crossdomain TEXT

, UNIQUE (url) ON CONFLICT REPLACE
);
sqlite> select * from cache;
_id|url|filepath|lastmodify|etag|expires|expiresstring|mimetype|encoding|httpstatus|location|contentlength|contentdisposition|crossdomain
1|http://www.darkdrive.net/***/style.css|7abbf48b|Mon, 18 Feb 2013 16:43:21 GMT||1365353616876||text/css||200||10055||
2|http://www.darkdrive.net/***/arrow.gif|2b518e19|Sun, 16 Sep 2012 06:55:56 GMT||1368039066000||image/gif||200||70||

◆ファイルの実体
また、キャッシュファイルの実体の保存場所は、
webviewCacheディレクトリ下になる。
ファイル名は、さきほどのDBに保存された名前(filepath)になっている。

/data/data/【パッケージ名】/cache/webviewCache
※ディレクトリ

>adb shell
# cd /data/data/***/cache/webviewCache
# ls -l
-rw——- app_xxx app_xxx 10055 2013-02-18 16:43 7abbf48b
-rw——- app_xxx app_xxx

70 2012-09-16 06:55 2b518e19

感想として、
WebViewのキャッシュは端末本体に保存されるため、保存領域や容量上限などで取り扱いづらい。
キャッシュデータを直接読み込めると、
ディスクIOの回数が減るので、全体的なパフォーマンス向上になったりするのかなぁと。
Android 3.0以降の調査がほとんどできてないので、そちらも調べてみたい。

2013.5.9
NECO

年末年始は無料恋愛ノベルアプリ「KIRIKA」を読もう!(女性向けです!)

年末年始って家にいる時間が多く、暇な時間を持て余すことがありますよね。
・・・ってそんな奴、私だけかもしれませんが。

ぜひ、そんな暇で退屈な時間がありましたら、
Playストアにて好評配信中の恋愛ノベルアプリ「KIRIKA~同じ人間がいる、もう一つの世界~」を
読んでみてください(><)

↓↓↓DLはこちらからどうぞ↓↓↓
https://play.google.com/store/apps/details?id=net.darkdrive.android.kirika

全70話(全部読むのに10~15時間ぐらいかかる、圧倒的ボリュームです!)を無料で配信しています。

本作品のジャンルは「恋愛ファンタジー」です。
30話ぐらいまでは恋愛要素は薄く、30話以降は恋愛面が強調されていきます。
そして、50話以降になるとキスシーンが多いです!

イベントシーンまでもが表情が切り替わり、立ち絵の表情パターンも尋常じゃないくらいに多いです!!

さらに、エンディングはマルチエンディングになっていまして、
一本道ノベルですが、最後だけ分岐します。
そして、一番読者から人気があり、且つルート投票で50票集まったものが、
無料で続編化されます!

ぜひ、読んで1票を投じていただけるとうれしいです。(選挙日だけに(笑))

ここまで読んでいただき、ありがとうございました。

2012.12.16
神森 真昼

※KIRIKAって何?
→全70話、エンディングまで完全無料の恋愛ノベルアプリです!

※内容の詳細、ダウンロードはこちら!
https://play.google.com/store/apps/details?id=net.darkdrive.android.kirika

検索用:恋愛ゲーム、小説、ノベル、女性向け、恋愛シミュレーション、ノベルゲーム、乙女ゲーム

【Androidアプリ作成】HTML5 Application Cacheを使ってみる

HTML5の「Application Cache」を使うと
WEBページのオフライン表示を比較的容易に実現できる。

この機能は、初めてアクセスした時に表示に必要なファイルを全部
ダウンロードし、次回以降は不必要な通信を行われずに
表示されるといった挙動となる。

オフラインでも表示できるのが最大のメリット。

いくつか対応に手間取ったので、備忘録も兼ねてメモ。

■HTMLにmanifestを記載
——————————
<html lang="ja" manifest="cache.appcache">
<head></head>
<body></body>
</html>
——————————
※便宜上「~.appcache」としているが別指定も可能

■cache.appcacheを作成
アクセスするディレクトリ直下にcache.appcacheを作る
——————————
CACHE MANIFEST

# Version 201207170123

## Cache Files
CACHE:
index.html
style.css
data/data.json
js/script.js

## Network Files
NETWORK:
*
——————————
一行目はCACHE MANIFEST固定
「CACHE:」以降にローカル保存するファイルを記載
「NETWORK:」以降は毎回取りに行くファイルを記載

■Content-Typeのmanifest対応
アクセスするディレクトリ直下に.htaccessを作り
以下の内容を記載。
——————————
AddType text/cache-manifest .appcache
——————————

■apacheの設定変更(必要に応じて)
——————————
<Directory />

Options All

AllowOverride All
</Directory>
——————————
※環境によっては'/'ではないことも。適宜 $DOCUMENT_ROOT に読み替える。
apache再起動も忘れずに。

■WebView対応
AndroidからWebView経由でアクセスする場合は追加で必要。
——————————
public class MainActivity extends Activity {

@Override

public void onCreate( Bundle savedInstanceState ) {

super.onCreate( savedInstanceState );

WebView webView = new WebView( this );

webView.getSettings().setAppCacheEnabled( true );

webView.getSettings().setAppCacheMaxSize( 32 * 1024 * 1024 );

webView.getSettings().setCacheMode( WebSettings.LOAD_CACHE_ELSE_NETWORK );

webView.loadUrl( "http://www.darkdrive.net/index.html?from=blog" );

ViewGroup vg = (ViewGroup)findViewById( R.id.vgApp );

vg.addView( webView );

}
}
——————————

ファイル更新時は、忘れずに cache.appcache を適宜書き換える。

本機能により通信しなくてもWEB表示できるようにはなるが、
あらかじめダウンロードを行う分、
最初のネットワークの使用量が多くなってしまう。

利便性を考慮すると、ユーザーが設定で切り替えられるように
したほうがいいかも。

2012.7.17
NECO

【Androidアプリ作成】SQLiteを使うには?(後編)

前回のページからの続きです。
見てない方は下記をご参照ください^^
http://blogs.yahoo.co.jp/hiro5_188/21863869.html

前回に引き続き、SQLiteのDBファイルからの読み込みです。
大体以下のようになります。

[1] アプリ起動時

(1-1) DBが既にあれば何もしない

(1-2) DBがなければ空のDBを作る

(1-3) assetsからコンテキストデフォルトのDBパスへコピー

(1-4) DB作成に失敗したらDB削除

[2] SQLite使用時

(2-1) DBを開く

(2-2) 自由に操作

(2-3) DBを閉じる

上記の処理を実際にプログラムすると
以下のような感じになります。
長いですが、全部書きます。

[1] アプリ起動時

public static boolean onCreate( Context context, String dbname )

{

if ( isExistsDataBase( context, dbname ) )

// (1-1)

{

// 既にDBが存在するなら何もしない

return true;

}

try

{

createEmptyDataBase( context, dbname );

// (1-2)

copyDataBaseFromAsset( context, dbname );

// (1-3)

return true;

}

catch ( IOException e )

{

try

{

deleteDataBase( context, dbname );

// (1-4)

}

catch ( Exception ignore ) {}

return false;

}

}

(1-1) DBが既にあれば何もしない

private static boolean isExistsDataBase( Context context, String dbname )

{

SQLiteDatabase con = null;

try

{

con = openDataBase( context, dbname );

// (2-1)

return true;

}

catch ( IOException ignore )

{

// 例外は飲み込む

return false;

}

finally

{

if ( con != null )

{

try { con.close(); } catch ( Exception ignore ) {}

con = null;

}

}

}

(1-2) DBがなければ空のDBを作る

private static void createEmptyDataBase( Context context, String dbname ) throws IOException

{

SQLiteOpenHelper openHelper = new SQLiteOpenHelper( context, dbname, null, 1 )

{

@Override

public void onUpgrade( SQLiteDatabase sqlitedatabase, int i, int j ) {}

@Override

public void onCreate( SQLiteDatabase sqlitedatabase ) {}

};

SQLiteDatabase db = null;

try

{

// このメソッドを呼ぶことで、空のデータベースが

// アプリのデフォルトシステムパスに作られる

db = openHelper.getReadableDatabase();

}

finally

{

if ( db != null )

{

try { db.close(); } catch ( Exception ignore ) {}

db = null;

}

}

}

(1-3) assetsからコンテキストデフォルトのDBパスへコピー

private static void copyDataBaseFromAsset( Context context, String dbname ) throws IOException

{

InputStream in = null;

OutputStream out = null;

try

{

// asset 内のデータベースファイルにアクセス

in = context.getAssets().open( dbname + ".sqlite" );

// デフォルトのデータベースパスに作成した空のDB

String dbpath = context.getDatabasePath( dbname ).getPath();

out = new FileOutputStream( dbpath );

// コピー

byte[] buffer = new byte[ 4 * 1024 ];

int size;

while ( ( size = in.read( buffer ) ) >= 0 )

{

out.write( buffer, 0, size );

}

out.flush();

}

finally

{

if ( out != null )

{

try { out.close(); } catch ( Exception ignore ) {}

out = null;

}

if ( in != null )

{

try { in.close(); } catch ( Exception ignore ) {}

in = null;

}

}

}

(1-4) DB作成に失敗したらDB削除

private static void deleteDataBase( Context context, String dbname )

{

String dbpath = context.getDatabasePath( dbname ).getPath();

File db = new File( dbpath );

if ( db.exists() )

{

db.delete();

}

}

[2] SQLite使用時
(2-1) DBを開く

public static SQLiteDatabase openDataBase( Context context, String dbname ) throws IOException

{

// Open the database

SQLiteDatabase db;

try

{

String dbpath = context.getDatabasePath( dbname ).getPath();

db = SQLiteDatabase.openDatabase( dbpath, null, SQLiteDatabase.OPEN_READONLY );

}

catch ( SQLiteException e )

{

// あえて検査例外で投げ直す

throw new IOException( e.toString() );

}

return db;

}

(2-2) 自由に操作

Cursor rs = con.rawQuery( "select ITEM_ID, ITEM_NAME from ITEM_INFO", null );

rs.moveToFirst();

int len = rs.getCount();

ArrayList<ItemDao> list = new ArrayList<ItemDao>( len );

while ( 0 <= (–len) )

{

int offset = 0;

int id = rs.getInt( offset++ );

String name = rs.getString( offset++ );

list.add( new ItemDao( id, name ) );

rs.moveToNext();

}

return list;

(2-3) DBを閉じる

db.close();

結構手軽に扱えるので、お試しください^^

2011.12.23
NECO

【Androidアプリ作成】SQLiteを使うには?(前編)

大量のデータを使う際にはSQLを使うのが便利。
WEBサービスではデータの定義や永続化にSQLを使うことが多いと思われるが、
Androidでも小規模ながらSQLを使うことができる。

■データの定義
データの定義はPCからできる。
今回はWindowsで行った。

sqlite自体はAndroidSDKに付属してる。
バージョンは 3.6.22 のようです。("sqlite3 -version"で確認できる)
その気になればコマンドラインからでも操作できますが、
今回はめんどくさいので管理ツールを使う。

■各種インストール
・Apache
http://httpd.apache.org/
2.0.63 入れた。
[httpd.conf]
LoadModule php5_module C:\php5\php5apache2.dll

<IfModule mod_php5.c>
AddType application/x-httpd-php .php
PHPIniDir "C:/php5"
</IfModule>

・PHP
http://www.php.net/
5.3.1 入れた。

[php.ini]
必要なextentionは3つ
・php_mbstring.dll
・php_pdo_sqlite.dll
・php_sqlite3.dll

・SQLiteManager
http://www.sqlitemanager.org/
1.2.4 入れた。
ついでに日本語化。

■データ作成
・DBの作成
以下のコマンドで作れる

>sqlite3 test.sqlite

".quit"で抜けれる。

SQLiteManagerへの登録
SQLiteManagerのdocbaseと同じ階層に
DBファイルを置く。
ブラウザから名前、パスにDBファイル名を指定。
アップロードは非チェック。
私の環境ではなぜかアップロード出来なかった。
よくわからない。

・テーブルの作成
自由に CREATE TABLE すればいいと思う。

Android用に、別途
android_metadataなるテーブルが必要。
無いとアプリ起動時に例外が出る。
CREATE TABLE android_metadata ( locale text default 'en_US' );

・データの登録
自由に INSERT INTO すればいいと思う。

■データをプロジェクトディレクトリにコピー
SQLiteManagerのdocbaseからAndroidプロジェクトディレクトリに
DBファイルをコピーするバッチファイル作る。
コピー先は$PROJECT/assets以下とかで。

…とりあえず読み込む準備ができたので、
Androidから読み込む処理はまた今度。

2011.12.20
NECO

【Androidアプリ作成】@stringをHTMLタグを使ってデコレーションする

@stringのデータに一部のHTMLタグが使えます。
ただフォントサイズによっては効いているのかわかりづらいです。
(私の環境では<b>の効果が全く分かりませんでした。。)

【android developers:String-resource】
http://developer.android.com/intl/ja/guide/topics/resources/string-resource.html

サンプルソース)
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="welcome">Welcome to <b>神森真昼の小説と波の世界</b>!</string>
</resources>

その他にも<U>(アンダーライン)と<I>(イタリック)が使えます。
ぜひ試してみてください^^>

2011.7.24
神森 真昼