シャープハッカソン2012春に行ってきた&アプリの技術的なお話

シャープハッカソンお疲れ様でした。
呼んでいただけて嬉しかったです。
主催のシャープさん、運営のブリリアントサービスの皆さんお疲れ様でした。非常にいい経験が出来ていい刺激になりました。

ハッカソン初参加で何ができるかわからなかったけど、いい感じにアプリが完成できてよかったです。あのレベルのアプリが2日で作れるんだから、参加者みんな凄まじい技術力ですよね。

ウチのチームで作ったアプリは「プリ♡シェア」っていう複数台の端末で同時に写真をデコれるアプリ。
え、おれ? 俺はアイディア出し以外はほとんど何もしてないお( ー`дー´)キリッ
アイディア出しの段階ではみんな「パンツ!」「パンツ!」連呼してどうなるかわからなかったけど(;´Д`)
チーム名も「かわいいはパンツ」だしな!!

ぶっちゃけ、「優勝はもらったな・・・!」って思ってたんだけど、3位ですた(´・ω・`)

4チームあって得点がそれぞれ、1位37pt、2位32pt、3位31pt(2チーム)だった。

俺が担当した部分はお絵かきActivityの部分。bluetoothへ送受信するデータ構造とかもやってた。通信自体の難しいところは全部bluetooth班に投げて、俺は何もしてないお。
なのでお絵かき部分の技術的なお話を少しだけ。

まず、描画は毎度おなじみOpenGL ES+オレオレライブラリであるeglibrary(https://github.com/eaglesakura/eglibrary)で実装されてる。

2日間で実装できた機能

  1. 線の描画(色・太さ指定あり)
  2. スタンプの描画(画像指定あり)
  3. 美白設定(美白の強さ指定あり)

ちなみに「美白」エフェクトは美白と言うよりも漂白に近い。画面全体が。

お絵かき部分

描画部分の技術的な課題はの1つは、複数端末で同じ描画データを共有する関係上、可能な限り同じ見た目を目指したいということ。
基本的にはあんまり難しく考えず、onTouchイベントで入ってくるXYのタッチ座標(ピクセルベース)を、画像に対して縦横0.0〜1.0の値=UV値に変換してお互い転送することで、座標を一元的に扱うことができる。

画像のnピクセル目って座標に変換しなかったのは、UV値のほうが分解能が良くて画像の解像度・アスペクト比に左右されない上、実際のGL正規化座標系(-1.0f〜1.0f)にマッピングしやすいから(それぞれu * 2 - 1.0f、-(v * 2 - 1.0f)で変換できる)

画面上の描画座標はglViewportで指定もできるし、画像にFITするような秒が行列を作ってもいい。余白をキャンバス風に埋めたかったから、今回は後者。

タッチ座標とUV座標のマッピングはオレオレライブラリに入ってるから、それを利用。

線描画

線の描画自体はGLのライン描画を利用。ポイントスプライトでも良かったんだけど、そっちだと点と点の間をある程度補完しなければいけないからコーディング時間のコストが高かったから見送り。問題点としてライン描画は太さの最大値がGPUによって決まっていることだけど、そこらへんはあんまり気にしないようにして、24pixの太さが最大にするとかして諦めた。
ライン描画のライブラリはなかったから、ここは専用コードを書くことで対応した。

スタンプ描画

スタンプの描画はタッチ座標を中心としたポイントスプライト描画として最初に実装。
してはみたけど、ポイントスプライト描画は最大サイズが決まっていて、画像サイズが不定になるスタンプ描画には使えないと判明。
スタンプの最大解像度に制限を加えるのはツライ。
ということで仕方なく、ポリゴンを使ったスプライト描画へ実装を変更。この描画はSpriteManagerクラスとBitmapTextureImageクラスが大活躍。

美白(漂白)機能

漂白設定は単純に画面全体を加算ブレンディングでFILLしているだけだから、難しいことはない。ただし、美白設定をMAXにしても画面全体が白飛びしないように、適当な補正はかけてあげてる。
これはSpriteManagerクラスとOpenGLManagerクラスで実装。

描画部分は書いたみたいな感じで、さほど難しくないから初日のハッカソン+ホテルで2:30くらいには出来てた。美白設定だけは最後に欲張って実装してみた。

残念だったのはフォトフレームが間に合わなかったこと。
実装したいなあ。


通信部分のお話

このアプリのキモは、全員でリアルタイムに落書きできるということ。
bluetooth通信部分はデイジーチェーン接続されている以外は全くわからない。通信部分は素人なんだよ、俺。
俺に使えるようにしてもらった機能は3つ。

  1. 自分以外の全端末へ「文字列」を送信する
  2. 自分以外のどれかの端末から「文字列」を受信したことが通知される
  3. 「誰か」がbluetoothのチェーンに追加されたことが通知される

送受信されるデータは全て文字列。
送信される文字列は2種類あって、必ずどちらかを送受信する仕様にした。

  1. 制御メッセージ
  2. 描画データ

制御メッセージ

他の端末から文字列を受け取ると「それが制御メッセージか否か」を判断して、制御メッセージならそれに応じた処理、違うならデータとして扱ってJSONのパースに移る。
制御メッセージは結果的に1つだけで、「BT接続したから画像くれ」というもの。
もちろん、送信先は「自分以外の全端末」だから、制御メッセージは画像を撮影したユーザー(メイン端末)も画像を撮影していないユーザー(サブ端末たち)も全員受け取ることになる。
受け取ったら、「自分が画像を持っている場合、画像をJSONデータ化して全端末へ送信する」って処理をしてる。
もちろん、デイジーチェーン接続だから末端へ届くまでには全端末を経由するため、非常に効率は悪い。多分、参加端末が増えたら一気に転送時間が増えていく。効率化できる部分だけど、ソコまで複雑なプログラムを書く余裕はなかったため今回は華麗にスルー。
なにせ、通信でメッセージを受け取れるようになったのが終了2時間前という慌ただしさだからね(;´Д`)

描画データ

描画データは文字列でしか送受信できないから、フォーマットはJSONに決定。
クラスオブジェクト <--> 文字列の変換はJSONICに全て頑張ってもらった。そのため、それに関わるDataクラスとPenクラスのメンバは全てpublicで実装されてる。アクセサメンドイし、publicのほうがいろいろ確実だし。
全てのデータ(画像もベクタも)は同じDataクラスで管理してる。これは、パース先のクラスを判別するコストを省きたかったから。もちろん、プログラミング時間のコストね。
パースされたら専用のリスナでその通知を受け取って、DataクラスからOpenGL管理用の描画クラスに再度変換されて再描画される。
描画用のクラスは線・スタンプでそれぞれ専用に作りこんである。共通の抽象クラスだけは定義してて、描画元はforループでdrawを行うだけ。

不安だったのはテキストベースのやり取りだからパースに時間がかかることと、通信に時間がかかること。
これは実際のところ、ほとんど気にする必要がないレベルだった。最新機種ってスゴイね。
ほぼリアルタイムで全端末のお絵かきデータがアップデートされるのを見た瞬間は最高だったwwww

その他

他に技術的に気になる部分があったらTwitterあたりで聞いてくれれば答えるお。

茨城のドライバーはなぜアクセルを踏むのか

ここに書いてあることは自分の偏見と経験則のみが元になっているので、正確性については保証しません。
あくまで、個人的な偏見と経験則のみに基づいています。
自分の生活範囲、家族、友人にのみ基づいています。

茨城の交通事情について

茨城のドライバーは非常にマナーが悪い。
twitterにも書いたけど、50km制限の道を50kmで走ると、必ずといっていいほど「さっさとスピード出せ」って感じで後続車が車間距離を詰めてくる。

同乗者も、「さっさとスピード出せ!」って言ってくる。
茨城の道路において、制限速度とは一切意味をなしていない。いかなる道路も最低時速60kmは出さないと侮蔑と嘲笑の視線に晒される。

↓を参考にすると、制限速度は法定速度に優先され、守らなければならない。
http://t-words.jp/w/E588B6E99990E9809FE5BAA6.html
その規制を突破するには緊急車両になるしかないらしいが、どう考えても茨城の車全てが緊急車両になっているとは言えない。
http://t-words.jp/w/E7B78AE680A5E887AAE58B95E8BB8A.html

つまり茨城のドライバーは法律違反をすることが当然であり、法律を守ることは悪と考える傾向にある。
茨城のドライバーは「田舎のドライバー」「DQNドライバー」と言い換えてもいいかもしれない。

地元に帰って車を運転するたびに、DQNパッシングと後部座席からの怒号に晒されるのが非常に納得行かないので、なぜ茨城のドライバーはアクセルを踏みぬくのか考察してみた。


この考察は自分の観測範囲のみが基準となってるから、その他はわからない。
ちなみに実家があるのは、土浦駅から制限速度を守った車で1時間くらいかかる場所。
DQNドライビングで何分掛かるかはわからぬ。

俺は18歳まで茨城で過ごして、運転免許を取得して、翌月には上京してる。茨城ドライバー歴は非常に少ない。

茨城の車事情

↑に書いたみたいに、実家がある当たりは駅から遠い。バスもほとんど通っていない。公共交通機関がほぼ死滅している地域だから、移動には車が必須となる。

一家に一台車なんて生ぬるい。
一人一台、もしくは仕事用でそれ以上を所有している場合が多い。

車を使わない日が無いため、ドライバーはほぼ全員が「車の運転に慣れている」と考えているだろう。

茨城の県民性

田舎の人間は特出することを嫌っている。日本人的な「出る杭打たれる」という考え方。つまり和は美しくなければならず、見だすことを嫌っている。
そのため、「皆がスピードを上げているから自分もスピードを上げなければならない」「車の流れに乗らなければならない」という強迫観念が存在する。
「皆が制限速度を守ってないから、自分も守ってはいけない」という考え。
どちらかと言えば「誰かだって制限速度を守ってないから、俺も守らない」という考え。
ついでに、「あいつだけが制限速度を守らずに得をしている」という考えも持っている。

つまり、誰か一人が得をすることを望まず、自分も得をしたいと考えている。

運転における損得

人間の行動理念として一番わかり易いものは損得だと思う。
茨城のドライバーにとっての損得を考えると、多分答えが見えるのでは無いだろうか。

  • 速度制限を破ることで得になること
    • 目的地に早く到着する
    • 車を抜かすことは快感である
    • 自分のドライビングテクニックを他者に見せつけることができる
    • いわゆる厨二病と同じく、法律を守ることは恥であるため、恥をかかずに済む
    • 初心者と思われない
  • 速度制限を破ることで損になること
    • 事故を起こした場合に自分が不利になる
    • 警察が網を貼っていた場合、捕まる恐れがある
  • 制限速度を守ることで得になること
    • 最低限の安全を確保できていると考えられる
  • 速度制限を守ることで損になること
    • 道路事情によって到着速度が制限される
    • つまり、出発時刻が制限される
    • 時間に大きく拘束される
    • 車列を作り出してしまう

ドライバーの時間的な拘束

ざっと考えてみたが、「制限速度を守ることで得になること」があまり無い。
逆の場合、わかりやすい利益として「時間」がある。
制限速度を守るということは目的地へ辿り着くためのMIN値がほぼ固定されてしまうということだからだ。
制限速度を排除することで、アクセルを踏めば踏むだけ目的地へ早く到達することが出来、時間的な拘束を少なくできる。
これは茨城のドライバーにとって大きなメリットである。
なにせ、彼らが時間通り出発することはほぼありえない。

ループとしてはこうなる

  1. 予定より遅く出発する
  2. 目的地に到着したい時間が固定されているため、アクセルを踏む
  3. 結果として間に合う
  4. 次回の出発予定時刻が遅くなる
  5. 最初に戻る

ドライバーの厨二病について

茨城のドライバーにとって、「初心者」と思われることは何より苦痛である。
なぜなら、ドライバーほぼ全員が「慣れている」と自負している県民性のため、そこから外れるわけにはいかないのである。

一番わかり易い「初心者」とは、間違いなく教習車だ。教習車は当然ながら制限速度を守って走っている(少なくとも、自分が通った教習所では制限速度厳守だった)。

ここで、法定速度を守る=教習車=初心者という等式が成立する。
法定速度を守ることは、初心者だと大々的に宣伝することになる。

また、教習車は車列を作ることが多い。数十台の車を引っ張るとか、ままある。それも「初心者」と関連付けされ、「車列を作ることが恥」という性質につながる。

周囲の車両に「運転初心者」と思われたくないため、アクセルを踏み込む。
ちなみに、駐車場であればバックで駐車しなければならない。

茨城とはそういうお土地柄だ。

警察に対するリスク

安全運転を心がけていようが、猛スピードを出していようが、比率はともかく事故は起こる。

だけど、猛スピードを出している場合にのみ背負うのが警察に捕まるリスク。
ところが茨城ではこのリスクが少ない。
少なくとも、厳守するほどの理由にはならないようだ。

何故ならば、警察が取り締まる速度は「制限速度+20km」のため、それが茨城ドライバーにとってのしきい値となる。

茨城のドライバーが「50km」の標識を見た場合、脳内では「70km」に変換される。さらに、10%程度は誤差として認識しているため、約80km程度まで加速することとなる。

算数が得意なドライバーは+10%の誤差を認識した上で+15km程度(時速65km程度)の速度で運転している。

ちなみに法定速度が「60km」だと知っている場合、彼らの脳内では「いかなる道路も60kmまでは出していい」と都合よく解釈されている。

制限速度を厳密に処理した場合、茨城の道路はそれこそ入れ食い状態なのだろうが、年度末のキャンペーン期間中くらいしか取り締まられたって話を聞かない。

ちなみに、取り締まられた場合決まって「ふざけんな!みんな出してんだろうが!!」って言う。あと、「くそ、ついてねぇ!!」って言う。

反省?なにそれおいしいの?

交通事故が起こった場合のリスク

大抵のことは金と人脈で解決できる。保険屋さんとかそういう意味ではなく、仮に無免許運転だとしても警察内部の知り合いに話を通してどうにかしてしまう。

地獄の沙汰も金次第である。

どうかしてる。というか、無免許運転はさっさとブタ箱へお入りください。
免許不携帯でなく、無免許運転ね。
無免許でもあのへんはどうにかなるって、どんな世紀末だよほんとに・・・

対人だけは多分どうしようもないけど、対物はどうにでもなる社会。




安全運転とは

彼らがいう「安全」とは、自身の得の上にのみ存在する。
目的地へ遅れる「損」の前に他者の安全は優先されない。もちろん、自身の安全も優先されない。

「事故ったらどうするんだ?」って聞いた場合、彼らは「事故らないから大丈夫」と答える。もしくは「制限速度を守ったほうが事故る」と答える。
彼ら(も自分も)統計を確認したわけではないだろうが、間違いなく制限速度は悪であり損と捉えている。

彼らに制限速度を守らせる、もしくは守ったほうが得と思わせるには、リスクを大きくする必要があるだろう。


制限速度一発免停とか、そういうレベルのリスクを与えない限り、多分彼らは今日もアクセルを踏み続ける。

Redmine REST APIとgitの連携をしてみた

一年ぶりのブログ記事。
転職直後はあんまり使ってなかったけど、このごろRedmineによるタスク管理の重要性を再確認して、使う機会が多くなった。


けど、利用頻度が多くなってきたらいちいちEclipse <==> ターミナル <==> ブラウザを行き来してやり取りするのがメンドウになってきた。
という訳で、Redmineを自分好みにターミナルから使えるようにRedmine REST APIでgitとの連携をできるようにしてみた。


コマンド名はgit+redmineだからgitmineという安直なネーミングで。


結果は、まだあんまり使ってないからわからないからわからぬ(;´Д`)
とりあえずAPIの勉強になったから良しとする。


近いうちにさくらのVPSを借りて自分のRedmineを立てないなぁ。


gitのフックスクリプトと同時に利用するのが前提。
要件としてはターミナルからRedmineのチケット確認・更新が行えること。
リポジトリとの関連付けはフックスクリプトにお任せ。


というか、git-redmineを導入しようとして失敗したんだよね(´・ω・`)


公開するかはわからんけど、とりあえず自分のメモを兼ねて使い方。


初期設定

このへんの設定はgit-redmineの仕様に合わせた。
一応共存できるような配慮で。
そもそも、git-redmineが使えれば問題ないわけだし(´・ω・`)

git config redmine.apiKey 自分のAPIキー
git config redmine.projectUrl リポジトリに関連付けてあるプロジェクトのURL



Basic認証設定

もしチケットの更新を行う場合はAPIキーの他にBasic認証用の値を設定する必要がある。
GETだけだったらこの設定はいらない。
PUTの使い方間違えてるのか、APIの使い方を間違えるのか、イマイチわからんけど、自分しか使わんからまぁいいかな(;´Д`)


Basic認証の値は他人にバレるとマズいから、同期されないように.git/.gitmineに暗号化して保存してる。
暗号の安全性は全く保証されてないがな( ー`дー´)キリッ

gitmine config --basic "user:passwordをbase64した文字列"



Redmineのチケットを見る

gitフックスクリプトの規約に合わせて、id/チケット番号のブランチにいる場合、gitmineを実行するとチケットのサブジェクトが表示される。
オプションで--ticket チケット番号を付与すれば好きなチケットを参照できる。--ticketオプションは大抵の命令と共存可能。

gitmine
〜チケット番号 :: チケットのサブジェクトを表示〜

gitmine --ticket 3103
〜3103番のチケットサブジェクトを表示〜

Redmineのチケット一覧を見る

オプションで-aを付加すると、未解決のチケット一覧が表示される

gitmine -a
〜チケット番号 :: チケットのサブジェクトを表示〜
〜チケット番号 :: チケットのサブジェクトを表示〜
〜チケット番号 :: チケットのサブジェクトを表示〜
...

Redmineのチケット詳細を見る

オプションで--detailを付加すると、チケットの詳細が表示される。

gitmine --detail
〜チケット番号 :: チケットのサブジェクト〜
〜作成日時〜
〜更新日時〜
〜進捗率〜
〜内容〜

チケットの進捗率を操作する

オプションで--doneを付加すると、チケットの進捗率を更新できる。


進捗率を50%に変更する場合

gitmine --done 50

チケットの状態を変更する

オプションでstatusを指定すると、チケットの状態を変更できる。
・・・ようにする予定(´・ω・`)


チケットを終了させる

gitmine --status close



チケットを再開する

gitmine --status open





今のところ自分で使う専用で考えてるから適当実装。
Redmineのチケットにコメントを付けるAPIが見つからないけど、どうやるんだろ(´・ω・`)

海外パケホーダイと中国のケータイ事情

今月の2日〜5日の間、上海万博へ行ってきたのでその時の感触。

まず、今月1日からスタートした海外パケホーダイ
出国の数週間前に発表されてすぐスタートしたので非常に助かりました。
持っていった端末はXperiaHT-03A(CM5)です。

Docomo店員の話では特に設定する必要ないというムチャぶり(おそらく彼らも分かっていない)で海外パケホのパンフレットだけ持って現地へ乗り込んだわけですが、案の定使えませんでした。

海外パケホを使うためには当然いくつか設定変更が必要で、以下はペリアの場合
1・Android設定->通話設定->ネットワークオペレータでオペレータを検索し、「China Unicom」か「CU-GSM」を選択する。
 ※この時、全く同じ地点(ホテルの自室)でチェックしましたが、ペリアは「China Unicom」の設定を捕まえ、HT-03Aでは「CU-GSM」の設定を捕まえていました。

 この設定で出来るのは通話だけで、APN設定はパンフにもHPにも載っていません(少なくとも海外パケホ関連のページには)

なので

2・APN設定に「mopera.net」を追加。

 これで3G回線を捕まえてくれるようになります。
 MyDocomoの明細をチェックしましたが、ちゃんと1日1480円のパケホ代金で収まっていました。

 また、HT-03A(CM5)を利用したUSBテザリングも行ないましたが、海外パケホの範囲で収まっています。

 上海にてNexusOneを購入してそちらも試しましたが、ペリアと全く同じ構成で利用できました。
 速度チェックはしていませんが、Twitterを楽しむ程度には問題ない速度です。しかし、2Mピクセル程度の画像のアップロードは1〜2分かかる等、大容量通信には当然向いていません。

 思ったよりも電波の入りはよく、ホテルの中・街中、共に圏外になることはありませんでした。


 残念なことに大連・上海市内・万博会場全てでAndroidユーザーを自分以外見かけませんでしたが、端末自体の販売は行われています。

 現地での価格は
 Motorora Droid 4400元 > Nexus One 4100元 > HTC Desire 3500元
 です。
 俺が行ったときは1万円=800人民元というレートでした。

 また、現地でパケット定額のSIMを購入した場合、月額1500元程度だそうです(親父の話なので、間違っているかもしれません)。なので、10日以上いる場合は海外パケホを使うよりもトクになると思います。

Android1.x系列のIntent不具合について

もしくははじめから仕様として設計されてるのかもしれないけど、巨大なメモリをIntentで扱った場合のAndroidの挙動不審についてメモ書き。


ShakeDroidの画像トリミングは標準の画像トリミングActivityを呼んで、切り取った画像をIntentで受け取ることで実現しています。

しかし、高解像度で画像をやり取りしようとすると、本来
startActivityForResult() -> onActivityResult() -> onRestart()
の順で呼ばれるはずのライフサイクルが
startActivityForResult() -> onRestart()
って感じでonActivityResult()をスルーしてしまうようです。

ShakeDroidの「メモリ節約」はこの挙動を回避するために設けられています。

理想は受け取る画像解像度=画面解像度。無駄な画像加工は画質を落とすだけなので内部的になるべく避ける方向で実装してあります。
ただし、その方法で画像を受け取るには、Intentでかなりの大きさのBMPデータをやり取りしなければなりません。
試算すると、ビット深度・RGBA8888 × 854 × 480 ≒ 1.6MB。
ビット深度はトリミングActivityのソースコードを見る限りRGBA8888で固定です。

ここから先はSHARPの方に問い合わせた結果わかったことですが、Intentのデータをやり取りするために扱えるメモリが約100KBらしいです。
参考URL:
http://groups.google.co.jp/group/android-developers/browse_thread/thread/866301365d7a353d

それを超える値(どの程度が限界かは、おそらく端末ごと・ドライバごとに違います)は正常にやりとりが行えません。

アプリ側が出来ることは、可能な限りIntentでやり取りするデータを軽量化すること。

ShakeDroidの場合は画像をファイルとして保存してもらうことで回避する方向です(個人的に、一時ファイルを置くのは非常に気持ち悪い動作なのですが・・・)


巨大なデータをIntentの連携で扱うアプリの場合、Intentの挙動に気を配ったほうがよさそうです。

Androidの無料アプリ・有料アプリを切り替えてビルドする方法

おそらくもっとスマートな方法があると思いますが、ShakeDroid/ShakeDroid Advanceで使用した無料・有料アプリのプロジェクト設定とビルド方法です。

Androidの問題点として、同じプロジェクトから有料・無料アプリの切り替えが非常に面倒(packageの制限やManifestの書き換えなど)であるため、別プロジェクトとして用意するのが普通だと思います。
違うプロジェクトにした場合はgenフォルダ内のpackageが切り替わってしまうため、ソースに変更が必要です。
いくつかの制限はありますが、プロジェクトに細工を行えば同じソース・リソースで有料・無料プロジェクトのビルドが可能です。
※もっと便利な方法があると思いますが、自分ではこれ以上自動化できませんでした。

開発環境はWindows Vista(Home)+Eclipse 3.5を利用していますが、他のOSでも問題ないと思います。
以下、見づらくてすいません。




1:基本となる無料版プロジェクト(ここでは基底プロジェクトと呼びます)を作成する

俺の場合だと基底プロジェクト名はShakeDroid、生成されるapkはShakeDroid.apkです。
このプロジェクトは基本的に普通のAndroidアプリとして作成して構いません。
ライブラリjarやプロジェクトも普通に設定して構いません。
ただし、2で説明する派生プロジェクトのリソースもresフォルダに含めなければなりません。




2:派生先の有料版プロジェクト(派生プロジェクト)を作成する

俺の場合だとShakeDroidAdvance、生成されるapkはShakeDroidAdvance.apkです。
このプロジェクトに仕掛けを行うことで同じソース・リソースを利用してビルドが可能になります。
ただし、AndroidManifest.xmlは使いまわせません(ShakeDroidでは特に不都合はありませんでした)ので、各々のプロジェクトで用意します。
起動用のActivityは基底プロジェクトのActivityを単純継承して、何もオーバーライドしません。

                                                • -

package eagle.android.app.shakeadvance;

import eagle.android.app.shake.ShakeDroid;
import android.app.Activity;
import android.os.Bundle;

//! 継承してクラスパッケージのみを分離する。
public class ShakeDroidAdvance extends ShakeDroid {
/** Called when the activity is first created. */
}

                                                • -


3:派生プロジェクトのフォルダを削除する。
 基底プロジェクトから引き継ぎたいフォルダ(res/assets)を派生プロジェクトから削除します。
 次に、派生プロジェクトの新規->フォルダを選択します。

 フォルダ名を指定せず、拡張->ファイル・システム内のフォルダーにリンク->参照から、基底プロジェクトのresフォルダを指定します。

 完了を選択すると、基底プロジェクトとの間にシンボリックリンクが作成され、リソースが共有されるようになります。
 無料版にも有料版のリソースを含める必要がありますが、特に不都合はないと思います。
 assetsも必要であれば同じ手順でシンボリックを作成してください。
 ソースコードも同じようにフォルダ/ファイル単位のシンボリックを作成して、ファイルを共有します。そうすることで、編集漏れやバージョン違いを防ぐことができます。




4:genフォルダの中身をコピーする
 レイアウト等のリソースから画面を生成するのに生成されたRクラスを使用してると思いますが、packageが違うため通常はソースコードの編集が必要です。
 生成されたRクラスはfinal属性であるため、継承によってダミーのクラスを作ることもできません。
 そのため、基底プロジェクトから派生プロジェクトにRクラスをコピーするバッチを作成します。
 バッチ自体はこんな感じです。

                                                              • -

@echo off
SET PARENT_PROJECT=ShakeDroid
SET CURRENT_PROJECT=ShakeDroidAdvance
REM 定義ファイルをコピーする
cd ..\%PARENT_PROJECT%\
REM genフォルダをコピーする
xcopy /s /e /y .\gen ..\%CURRENT_PROJECT%\gen
cd ..\CURRENT_PROJECT\

                                                              • -

 これを実行すればRクラスがコピーされ、派生プロジェクトでも同一ソースを利用出来るようになります。
 ただ、いちいち手動実行では非効率的ですので、プロジェクトの設定を変更します。
 派生プロジェクトのプロパティ->ビルダー->新規から、先程作成したバッチを呼び出す起動構成を作成します。

 これでビルドの度に自動的に基底プロジェクトからgenフォルダが同期されます。




5:無料・有料のアプリ内での切り分け
 これは個々のアプリごとに設計が異なると思いますが、ShakeDroidの場合はeagle.android.app.appinfoパッケージを意図的にシンボリックリンクせず、別々の中身にすることで同一ソースで有料・無料を切り分けています。
 切り分けが必要な箇所ではAppInfomationをnewし、フラグやクラスを取得することで切り分けを実現しています。
 具体的にはこんな感じです。
AppInfomation.java
無料版----------------------------------
/**
*
* @author eagle.sakura
* @version 2010/06/05 : 新規作成
*/
package eagle.android.app.appinfo;

import com.admob.android.ads.AdView;

import android.app.Activity;
import android.view.View;
import eagle.android.appcore.IAppInfomation;

/**
* @author eagle.sakura
* @version 2010/06/05 : 新規作成
*/
public class AppInfomation implements IAppInfomation
{

/**
* @author eagle.sakura
* @param activity
* @return
* @version 2010/06/05 : 新規作成
*/
@Override
public View createAdView(Activity activity)
{
// TODO 自動生成されたメソッド・スタブ
AdView ad = new AdView( activity );
ad.setVisibility( View.VISIBLE );
ad.setKeywords("Android application");
ad.bringToFront();
ad.requestFocus();
ad.invalidate();
return ad;
}

/**
*
* @author eagle.sakura
* @return
* @version 2010/06/05 : 新規作成
*/
@Override
public boolean isSharewareMode()
{
//! 無料モードであるため、falseを返す。
// TODO 自動生成されたメソッド・スタブ
return false;
}

}
有料版----------------------------------
/**
*
* @author eagle.sakura
* @version 2010/06/05 : 新規作成
*/
package eagle.android.app.appinfo;

import android.app.Activity;
import android.view.View;
import eagle.android.appcore.IAppInfomation;
import eagle.util.EagleUtil;

/**
* @author eagle.sakura
* @version 2010/06/05 : 新規作成
*/
public class AppInfomation implements IAppInfomation
{

/**
* @author eagle.sakura
* @param activity
* @return
* @version 2010/06/05 : 新規作成
*/
@Override
public View createAdView(Activity activity)
{
EagleUtil.log( "Not Admob" );
// TODO 自動生成されたメソッド・スタブ
return null;
}

/**
*
* @author eagle.sakura
* @return
* @version 2010/06/05 : 新規作成
*/
@Override
public boolean isSharewareMode()
{
EagleUtil.log( "this is shareware" );
//! 有料モードであるため、trueを返す。
// TODO 自動生成されたメソッド・スタブ
return true;
}

}

                                                                                                                            • -


設定項目が多く面倒ですが、ある程度の回数アップデートするなら設定しておいて損はないかと思います。

土下寝だ! ああ、土下寝だ!

fbx -> OpenGLへ独自形式で持ってくるためのコンバータを作成中。

SDKC++のみの提供だから、中間ファイルをC++(VisualStudio2010EE)から吐き出して、それをJavaにて必要な形式に最適化・出力。

頂点情報は中間ファイルではfloat[]で保持して、コンバート時にGLFixedに直す。将来的にはXNAとかに使い回したいな。