2012年01月16日

phpfogにstatusnetを導入してみた このエントリーをはてなブックマークに追加 はてなブックマーク - phpfogにstatusnetを導入してみた

393360_232625983483051_100002069543204_565840_1793723291_n.jpg
つわりを共有できる妊婦のためのミニブログサイト
Twatter」(つわったー)をリリースしました!
twatは禁句らしいのでかなりのレベルの下ネタになってしまいました

ネットのニュースとかみていたら、
海外のオープンソースのTwitterクローン「statusnet」が
なかなか本格的な機能を備えているようで面白そうでした。
ただ、PHPの必要バージョンが5.3以上とかで今手持ちの空きサーバが無いし
ローカル環境でやるのも面白くないし、追加でVPSとか契約するほどでもないのです。
そこで、仕事の合間の息抜きに
最近気になってたPHPとMysqlが動作して無料のPaas「phpfog」を使ってみました。

phpfogをセットアップ

phpfogにSignUp
確認画面もメール存在確認もなくいきなり登録完了&ログイン状態になってびびる!
簡単で良い。

ApplicationsかFrameworksを選ぶだけでサーバが立ち上がるなんですごいなー
WordPressのサーバほしいならほんと簡単にできちゃいますね。
今回はstatusnetの導入が目的なので、Custom Appを選択します。

あとは適当にサブドメイン名を決めたらURLが決まります。
今回は「twatter」と入力したのでURLは「twatter.phpfogapp.com」となりました。

次にGitの設定をします。ここがちょっと変わってるところかも?
phpfogではGitのpushで必要なファイルをサーバへデプロイします。

ローカル環境へgit(windows版)のインストール

http://git-scm.com/
インストール途中で色々聞かれますが、
「Git Bash Here」「Git GUI Here」は便利なのでチェックしといていいかも。
その他はデフォルト状態で進んでいいのでは。(お好みで)

phpfogへssh-keyの設定

「Git GUI」を起動し、ヘルプ⇒SSHキーを表示し、鍵を生成をクリック。
設定するパスワードはテスト用なら短くていい気がします。
適当に進むと公開鍵の文字列が表示されますのでコピーして
phpfogの管理画面にある「SSH Keys」に張り付けて登録してください。
ローカル環境のWindows上に適当な名前のフォルダ作成して、
フォルダを右クリックして「Git Bash」を起動し、
phpfogの「Source Code」ページでコピーできるコードを入力してみましょう。
下記はコードの例です。
$ git clone git@git01.phpfog.com:xxxxx.phpfogapp.com
フォルダ内にphpfogにデフォルトで設置してあるindex.phpなどのファイルが落ちてきます。
一度、xxxxx.phpfogapp.com へブラウザでアクセスし、
index.phpなどの内容をテキストエディタで変更した後、
再度フォルダを右クリックして「Git GUI」を起動し、
「再スキャン」⇒「変更をコミット予定に入れる」⇒「書名」⇒「コミット」⇒「プッシュ」⇒「パスワード入力」
とポチポチ押していけばデプロイされます。
再度、xxxxx.phpfogapp.com へブラウザでアクセスし、変更内容が反映されていれば完了です。
うまく「Git GUI」からプッシュできない場合は、一度「Git Bash」を起動し、
「git commit」⇒「git push」などのコマンドを試してみてください。

phpfogへstatusnetのインストール

下記からパッケージをダウンロードし(バージョンは1.0.1でした)、
さきほど作成したフォルダ内に解凍して
テキストエディタで.gitignoreを開き、config.phpと*.mo(.htaccessも?)が無視されるようになってるので
削除し、Gitでphpfogへデプロイしてください。
このままだと xxxxx.phpfogapp.com/statusnet-1.0.1/ でアクセスしないといけないので、
DocumentRootを変更します。
phpfogの「Setting」ページのBase Directoryに「statusnet-1.0.1/」と入力して保存。
これで xxxxx.phpfogapp.com でアクセスできるようになったかブラウザで確認してください。

次に、xxxxx.phpfogapp.com/install.php にアクセスしてください。
いくつかのフォルダのパーミッションを下げろというエラーが表示されるので、
phpfogの「Permissions」で下記のように設定します。
statusnet-1.0.1/avatar/
statusnet-1.0.1/background/
statusnet-1.0.1/file/
再度ブラウザでインストール画面にアクセスするとエラーが消えていることを確認できます。

次に、各種情報の入力です。
fancyURLsを有効にしたい場合は、「htaccess.sample」をテキストエディタで開き、
RewriteBase /mublog/ ⇒ RewriteBase /
へ編集してから「.htaccess」へ保存してデプロイし直しておいてください。
Mysqlの情報はphpfogの「Database」ページ下部、
「Database Connection Info」から取得できるのでコピペしましょう。
その他の情報は適当に。
インストールすると、class/Config.php がおかしい的なエラーが発生します。
ここで一度詰まったんですが、どうやらPEAR経由で別のconfig.phpを読んでいるような挙動・・・
力技で解決してみます。

db/core.php 79行目
'Config',⇒'Configs',へ変更
classes/Config.php の名前を Configs.php へ変更
classes/Configs.php 内の「Config」という文字列をテキストエディタなどで「Configs」へ一括置換
※「config」も変換してしまわないように大文字/小文字を区別するように変換してください
phpfogの「Database」のLaunch phpMyAdminからphpMyAdminを開いて、
Configs.phpの「schemaDef」関数の中身を見ながら手動で「Configs」テーブルを作成

再度インストールし、無事完了したら下記のように設定を戻します。
classes/Configs.php の名前を Config.php へ戻し
classes/Config.php 内の「Configs」という文字列をテキストエディタなどで「Config」へ一括置換。
phpfogの「Database」のLaunch phpMyAdminからphpMyAdminを開いて、
「Configs」テーブルの名前を「Config」へ変更

サーバ上のファイルエクスプローラを導入

phpfogはGitで管理するので、サーバ上で生成されたファイルを削除したりしにくそうですので、
下記がよさげだったので導入しました。
PHP Navigator 4.41

これを使って、statusnetインストール後に生成される「config.php」をサーバ上からダウンロードし、
statusnet-1.0.1/ 配下へ配置しましょう。

日本語化

さきほど配置した「config.php」へ下記を追記
$config['site']['timezone'] = ‘Asia/Tokyo’;
$config['site']['language'] = ‘ja_JP’;
「lib/language.php」の下部へ下記を追記
'ja' => array('q' => 0.5, 'lang' => 'ja', 'name' => 'Japanese', 'direction' => 'ltr'),

日本語テキストを変更したい場合は「poedit」を
http://www.poedit.net/download.php
インストールして「locale/ja」内の
poファイルをテキストエディタで編集⇒poeditで開いて保存⇒デプロイ
してください。

これで多分一通り動くハズ。
どんな機能があるかなど気になる方は http://twatter.phpfogapp.com/ に登録して適当に遊んでみてください。

所感

statusnetは高機能で結構良いけど、日本語周りが怪しすぎるのが難点・・・
あとHTML修正しにくすぎ
モバイル対応は現在Titaniumで開発中のandroid/iphone版があるらしく、今後に期待。
phpfogは無料でphpとmysqlが使えて良いけど、
無料版だからなのかstatusnetのせいなのか重い・・・
ping打ってみるとどうやらamazon-ec2使ってるみたいなんだけど
海外のlocationらしく、レイテンシとかも関係してるのかな?
とりあえず、いろいろと息抜きと勉強になりました。
社内イントラネット向けのTwitterクローン案件とかがあっても対応できそうかな?
posted by BBキング∞金×金∞ at 11:17 | Comment(0) | TrackBack(0) | WEB開発 | このブログの読者になる | 更新情報をチェックする

2011年12月07日

引っ越してました このエントリーをはてなブックマークに追加 はてなブックマーク - 引っ越してました

いつの間にか埼玉に引っ越してました。(10月1日くらいから)

自社メディアの制作を行うようになり受託を減らしたら
ほとんど遠隔のやりとりだけで業務遂行可能となってしまったので
初台駅徒歩1分のオフィスとかで狭くて高い賃料払ってる意味がなくなり、
タイミング良くオフィス兼自宅として借りられる良物件を見つけたためです。

・9月末まで
笹塚アパート:自宅
初台オフィス:オフィス

・10月から
埼玉アパート2F:自宅
埼玉アパート1F:オフィス

こうなりました。超効率化しました。
以前は出社するのに笹塚から初台まで2駅もあるので大変でしたが、
これからは2Fから1Fへ降りるだけなので、通勤時間も短縮できそうです。
外出する際、自宅から駅までが遠い(徒歩24分)のが唯一ネックですが、
自然、環境、経費、どれをとっても抜群に良くなりました。
東京駅まで電車で30分なのでわりと交通の便も良いです。
パソコンさえあればなんとかなり、在庫もいらないIT商売は身軽で好きです。

ただ、周りにお店が少ないのと良いものが食いたいので東都生協で食料品とか注文してます。
毎週自宅まで届けてくれるので便利です。有機野菜BOXとかいうのが美味い。

これからもがんばっていこうと思う。
posted by BBキング∞金×金∞ at 23:07 | Comment(0) | TrackBack(0) | 近況 | このブログの読者になる | 更新情報をチェックする

2011年06月24日

Flash CS5.5ならプログラマじゃなくてもAndroidアプリが作れちゃう! このエントリーをはてなブックマークに追加 はてなブックマーク - Flash CS5.5ならプログラマじゃなくてもAndroidアプリが作れちゃう!

はじめに

最近Androidアプリ作りました
FLASH CS5.5 で「かわいいねこちゃん」というちょうおもしろいゲームアプリを作ってAndroid Marketにリリースしました。(AIRで出力したのでPCでも遊べます
FLASH自体は作り易かったのですが、とても情報が少なく苦労したところが結構ありましたので
備忘録およびこれからFLASHでアプリ作ってみようと思っている方向けに記事を書こうと思いました。
この記事の想定対象外読者
    • 「Android!うん知ってる!CMでやってた!AUの新しい携帯電話のことだよね!」って言う人

  • Androidアプリ作ろうぜ!
    全国1億人のプログラマ以外の方々こんにちは。
    そしてプログラマの方々こんな記事みなくても大丈夫なのでさようなら!!
    現在、Androidのアプリを作ろうとする場合、選択肢は大きく2つです。

    @全てはGoogle様の御心のままに・・・Eclipse+JAVAで開発する!
    ⇒これは最もスタンダードなやり方ですね。スタンダードってサイコウだよ!
    だってドキュメントとか情報が充実してるし、大手ソーシャルメディア(○REEとか)のSDK組み込めるし、他社の広告配信プラットフォーム使いたい時だってちゃんとSDKとかライブラリが用意されてるから、すごく安心感がありますね!
    しかし、プログラマのように困難を快感に感じることのできる変態新人類でないと、Eclipseの環境構築するだけで途中で投げ出してしまいそうもし投げ出さなかったらプログラマになれ!プロかつグラマーになれ!

    AJAVAってJavascriptのことですか?HTML5+Javascriptで作ったものをアプリの形式にコンバートする!
    ⇒最近かなり注目されてきているアプリの作り方ですね!Windows8ではアプリケーションをHtml5で作れるようになるそうですし、Html書いたことあるしJavascriptもコピペで使ったことありますよ!程度の僕のような技術者でもアプリが作れちゃう敷居の低さも凄い。最近ではDream Weaver CS5.5 が phonegap というフレームワークと連携する機能を備えてます。有名なのは titanium ですかね。簡単さと将来性は最強です!
    しかし、HTML5+Javascriptですから、WEB画面のようなページ遷移が頻繁に発生するようなアプリであれば良いのですが、ゲームのようにキャラクターを動かしたり、アニメーションしたり、という仕様を実現するのがちょっと苦手です。ぶっちゃけHTML5で作れる範囲の表現をアプリに固めてなんの意味があるのか、まあ確かに加速度とかアプリじゃないと扱えないものはあるんでしょうけど・・・そのままブラウザでアクセスできる場所にアップロードしたほうがお手軽だしサーバ連携も簡単だし、どうなの?とも思います。
    第3の選択肢 Flash CS5.5
    プログラムとかよくわかんないんだけど、Androidアプリの簡単なゲームとかアニメとか作ってみたいという方にはFlash CS5.5です。実はFlash builder4.5でも作れるのですが、こちらは完全にAS3.0(パフォーマンスアップ!でもJAVA的な書き方してね!)的な作り方が必要となる点と、iphone用のアプリも出力したい時にiphone SDKとApplication Loaderが必要になるのでMAC(開発には性能的にairではなくproがオススメらしい)を買わないとあかん・・・。(VMWAREで仮想MACOSをWindowsPCに導入するという方法でWindowsのみでのiphoneアプリ開発に成功している方もいるようですが・・・MACOSは価格が¥3,000とかで安いのでやってみる価値はあるのかも)
    じゃあFLASH CS5.5はどうなのかというと、iphone SDKがなくてもiphoneアプリを出力するところまではできちゃいます。結局Application Loaderがないとアップロードはできないのですが、アップロードだけは友達のMAC借りてやるとかね!個人であればそんな運用でもよいかと・・・。

    かわいいねこちゃんの作り方

    企画
    何事もまずはじめは企画(やりたいこと)を考えて、妄想をふくらませることですね!
    今回は社内の人(2人しかいないけど)に「Andoroidアプリの企画書作ってみて!」という的確な指示を出してみました。
    すると5分後、「名称未設定-2.gif」というファイル名の画像がskypeで(無言で)送られてきた!



    ・・・



    ・・・



    ・・・



    ・・・



    ・・・



    こいつ・・・動くぞ!



    かわいいねこちゃん



    やったぞー!企画書の完成だ!
    (GIFアニメで企画書提出とは・・・こやつ・・・やりおるわ・・・)

    準備
    ・FLASH CS5.5 起動
    ・テンプレートから作成「AIR for Android」をクリック
    ・「800 x 480 空白」を選択してOK
    ・企画書によると横長画面なので、「修正」⇒「ドキュメント」で幅と高さの数値を入れ替えてOK
    ・「ファイル」⇒「AIR for Android 設定」クリック
    出力ファイル名「cute_cat.apk」
    アプリケーション名「かわいいねこちゃん」
    アプリケーションID「jp.co.penet.cutecat」
    起動時の縦横比「横長モード」
    ・「デプロイ」タブクリック
    証明書「作成」クリック(パスワードとか適当に設定)
    証明書「参照」から作った証明書を選択
    「パスワード」にさきほどのパスワードを入力して「このセッション中はパスワードを保存する」にチェック
    ※FLASHを再起動した場合、再度この画面でパスワードを入力する必要があります
    ・「権限」タブクリック
    「アプリケーション記述ファイルへの...」チェックしてOK
    ※これをやっておかないとxmlの設定ファイルへの修正をFLASHに上書きされてしまいがちになります

    制作
    AS3.0で作りますが、classとかは使わずに、
    フレームに直接actionscriptを書いていく形で
    直感的にわかりやすいAS2.0気分で作っていきます。

    1フレーム目

    var version:Number = 1;
    var debug:Boolean;
    //デバッグモード
    debug = true;

    if( debug == true )
    {
    import net.hires.debug.Stats;
    addChild( new Stats() );
    }

    var webView:StageWebView;
    var ADMOB_URL:String = "http://xxx.com/xxx.html";
    var ad_width:int = 468;
    var ad_height:int = 60;

    function view1_initializeHandler():void
    {
    var target:DisplayObject = this;
    while(target.parent){
    target = target.parent;
    }
    var s:Stage = target as Stage;
    webView = new StageWebView();
    webView.addEventListener(ErrorEvent.ERROR,onIoerror);
    webView.stage = s;
    webView.viewPort = new Rectangle((s.stageWidth-ad_width)/2,480-ad_height,ad_width,ad_height);
    webView.loadURL(ADMOB_URL);
    webView.addEventListener(Event.COMPLETE,onComplete);
    }

    function onIoerror(event:ErrorEvent):void
    {
    webView.dispose();
    }

    function onComplete(event:Event):void
    {
    webView.addEventListener(LocationChangeEvent.LOCATION_CHANGE,update);
    }

    var update_i:Boolean;
    function update(event:LocationChangeEvent):void
    {
    if(webView.location != ADMOB_URL){
    if( update_i == false )
    {
    event.preventDefault();
    navigateToURL(new URLRequest(event.location));
    update_i = true;
    }
    else
    {
    event.preventDefault();
    webView.historyBack();
    update_i = false;
    }
    }
    }

    view1_initializeHandler();

    import be.boulevart.as3.security.Base64;
    Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;

    //BGMと効果音
    var start_sou:Sound = new start_mp3();
    var ue_sou:Sound = new ue_mp3();
    var shita_sou:Sound = new shita_mp3();
    var bgm_sou:Sound = new bgm_mp3();
    var mouse_sou:Sound = new mouse_mp3();
    var bird_sou:Sound = new bird_mp3();
    var dog_sou:Sound = new dog_mp3();

    Global.soundFlag = true;
    sou_on_btn.visible = false;

    //Twitter連携用date関数
    function date14()
    {
    var now:Date = new Date();
    var m:String = "";
    var d:String = "";
    var H:String = "";
    var i:String = "";
    var s:String = "";
    var Y:int = now.fullYear;
    var mp:int = now.month + 1;
    if( mp < 10 ){ m += "0";}
    m += mp;
    var dp:int = now.date;
    if( dp < 10 ){ d += "0";}
    d += dp;
    var Hp:int = now.hours;
    if( Hp < 10 ){ H += "0";}
    H += Hp;
    var ip:int = now.minutes;
    if( ip < 10 ){ i += "0";}
    i += ip;
    var sp:int = now.seconds;
    if( sp < 10 ){ s += "0";}
    s += sp;
    return Y+m+d+H+i+s;
    }

    //AIR用自動更新
    import air.update.ApplicationUpdaterUI;
    import flash.filesystem.File;

    var appUpdater:ApplicationUpdaterUI;
    appUpdater = new ApplicationUpdaterUI ;
    appUpdater.configurationFile = new File("app:/config/update-config.xml");
    appUpdater.initialize();

    var timer_air:Timer = new Timer(1000,1);
    timer_air.addEventListener(TimerEvent.TIMER_COMPLETE,function(e:TimerEvent){
    appUpdater.checkNow()
    });
    timer_air.start();
    そしてここまで書いて自分のコードの酷さに愕然とした。
    えー、まず広告を表示するためにStageWebViewを使ってます。
    表示しているのはGoogle Adsenseのスマートフォン用広告です。
    広告が表示されるHTMLを作成し適当なサーバにアップロードして、そのURLを指定してください。
    ちなみにこの方法で問題がないかどうかわかりませんので、参考にされる場合は自己責任でお願いします。
    下記サイトの情報を参考にさせていただきましたが、
    http://help.adobe.com/ja_JP/as3/dev/WS901d38e593cd1bac3ef1d28412ac57b094b-8000.html
    http://today-only.net/flash_android_admob/
    AdMobだと広告が表示される頻度が少なかったため、Adsenseにしてみました。
    また、ローカルのHTML読み込みではAdsenseだとクロール対象のHTMLではないためか、
    広告が表示されなかったため、サーバにアップロードしたものを読み込ませています。
    HTML内のテキスト文字列に連動した広告が表示されます。(この場合はタイトルの「かわいいねこちゃん」に連動してペット関係の広告が表示されました)

    広告表示用HTMLの内容


    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>かわいいねこちゃん</title>
    <style type="text/css">
    *{margin:0; padding:0; border:0;}
    </style>
    </head>
    <body bgcolor="000000" STYLE="overflow: hidden;">
    <script type="text/javascript"><!--
    google_ad_client = "ca-pub-xxxxxxxxxxxxxxx";
    /* apps_pc */
    google_ad_slot = "xxxxxxxxxx";
    google_ad_width = 468;
    google_ad_height = 60;
    //-->
    </script>
    <script type="text/javascript"
    src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
    </script>
    </body>
    </html>
    BGMと効果音を一括で読み込んでます。
    date14関数はTwitterのoAuth認証でランキングも作ったので、
    そのためにアプリから14桁の形式で日時をサーバへ送信するためのものです。
    最後にアプリリリース後にバージョンアップしたときに
    自動アップデートするための仕組みをいれときます。

    app:/config/update-config.xml の内容



    http://xxx.com/update-descriptor.xml
    1









    「ファイル」⇒「AIR for Android 設定」の「含めるファイル」で「config」フォルダを追加しましょう

    適当なサーバに update-descriptor.xml を設置

    update-descriptor.xml の内容



    1.0.0
    http://xxx.com/cute_cat.air


    これで、サーバに versionNumber の数値を上げた update-descriptor.xml と、
    新しく出力した cute_cat.air をアップロードするだけで、アプリが自動更新されます。
    (もちろんAndroid Marketのアプリも更新しときましょう)

    2フレーム目

    import flash.display.MovieClip;
    import flash.utils.getDefinitionByName;

    Global.stopFlag = false;
    Global.resetFlag = false;

    if( Global.soundFlag == true )
    {
    sou_on_btn.visible = false;
    sou_off_btn.visible = true;
    }
    else
    {
    sou_on_btn.visible = true;
    sou_off_btn.visible = false;
    }

    var cat_timer:Timer = new Timer(500, 0);
    cat_timer.addEventListener( TimerEvent.TIMER, cat_timerFunc );
    function cat_timerFunc( evt:TimerEvent ):void {
    if ( cat_mc.smile == true )
    {
    cat_mc.gotoAndStop("smile");
    cat_mc.smile = false;
    }
    else if ( cat_mc.currentLabel == "loop" )
    {
    cat_mc.gotoAndStop("loop2");
    }
    else
    {
    cat_mc.gotoAndStop("loop");
    }
    }

    //距離計測
    var distance_i:int = 0;
    var distance:Timer = new Timer(1000, 0);
    distance.addEventListener( TimerEvent.TIMER, distanceCount );
    function distanceCount( evt:TimerEvent ):void {
    distance_i++;
    distance_mc.distance_txt.text = distance_i + " m";
    if( Global.stopFlag == true )
    {
    distance_mc.distance_txt.text = distance_i + " m";
    distance.stop();
    }
    }

    //背景移動(=ねこちゃん移動)
    var backSpeed:Number = 5;
    function backMove(e:Event):void {
    back_mc.x += backSpeed;

    if( Global.stopFlag == true )
    {
    back_mc.removeEventListener( Event.ENTER_FRAME , backMove );
    }

    if( back_mc.x > -100 )
    {
    back_mc.x = -200;
    }
    }

    //動物出現
    var i:int = 0;
    var pop:int = 0;
    var mcArray:Array = new Array();
    var timer:Timer = new Timer(1000, 0);
    var mouse_y:int = 0;
    var num:int = 0;
    var num2:int = 0;
    var syu:String;
    var dogSpeed:Number = 6;
    var mouseSpeed:Number = 7;
    var birdSpeed:Number = 9;
    timer.addEventListener( TimerEvent.TIMER, mousePop );
    function mousePop( evt:TimerEvent ):void {
    num = xGetRandomInt(0,9);
    if ( distance_i < 20 )//0〜19m
    {
    switch ( num ) {
    case 0: syu = "mouse";break;
    case 1: syu = "mouse";break;
    case 2: syu = "mouse";break;
    case 3: syu = "mouse";break;
    case 4: syu = "bird";break;
    case 5: syu = "dog";break;
    default: syu = "not";
    }
    }
    else if ( distance_i < 50 )//20〜49m
    {
    switch ( num ) {
    case 0: syu = "mouse";break;
    case 1: syu = "mouse";break;
    case 2: syu = "mouse";break;
    case 3: syu = "mouse";break;
    case 4: syu = "mouse";break;
    case 5: syu = "mouse";break;
    case 6: syu = "bird";break;
    case 7: syu = "dog";break;
    default: syu = "not";
    }
    }
    else if ( distance_i < 100 )//50〜99m
    {
    switch ( num ) {
    case 0: syu = "mouse";break;
    case 1: syu = "mouse";break;
    case 2: syu = "mouse";break;
    case 3: syu = "mouse";break;
    case 4: syu = "mouse";break;
    case 5: syu = "mouse";break;
    case 6: syu = "mouse";break;
    case 7: syu = "bird";break;
    case 8: syu = "dog";break;
    case 9: syu = "dog";break;
    default: syu = "not";
    }
    }
    else if ( distance_i < 300 )//100〜299m
    {
    switch ( num ) {
    case 0: syu = "mouse";break;
    case 1: syu = "mouse";break;
    case 2: syu = "mouse";break;
    case 3: syu = "mouse";break;
    case 4: syu = "mouse";break;
    case 5: syu = "bird";break;
    case 6: syu = "bird";break;
    case 7: syu = "dog";break;
    case 8: syu = "dog";break;
    case 9: syu = "dog";break;
    default: syu = "not";
    }
    }
    else
    {
    switch ( num ) {
    case 0: syu = "mouse";break;
    case 1: syu = "bird";break;
    case 2: syu = "bird";break;
    case 3: syu = "bird";break;
    case 4: syu = "bird";break;
    case 5: syu = "bird";break;
    case 6: syu = "bird";break;
    case 7: syu = "bird";break;
    case 8: syu = "bird";break;
    case 9: syu = "dog";break;
    default: syu = "not";
    }
    }

    if( syu != "not" )
    {
    i++;
    pop++;
    var mc_name:Class = getDefinitionByName( syu + "_mc" ) as Class;
    var mc = new mc_name();
    mc.name = syu;
    mcArray[i]= mc;
    mcArray[i].x = -200;
    num = xGetRandomInt(1,3);
    switch ( num ) {
    case 1://上に出現
    mouse_y = 80;
    break;
    case 2://真ん中に出現
    mouse_y = 190;
    break;
    case 3://下に出現
    mouse_y = 300;
    break;
    }
    mcArray[i].y = mouse_y;
    if( syu == "dog" )
    {
    mcArray[i].speed = dogSpeed;
    addChildAt( mcArray[i], 1 );
    mcArray[i].addEventListener( Event.ENTER_FRAME , dogAction );
    mcArray[i].addEventListener( Event.ENTER_FRAME , dogAction_reset );
    }
    else if( syu == "mouse" )
    {
    mcArray[i].speed = mouseSpeed;
    addChildAt( mcArray[i], 2 );
    mcArray[i].addEventListener( Event.ENTER_FRAME , mouseAction );
    mcArray[i].addEventListener( Event.ENTER_FRAME , mouseAction_reset );
    }
    else
    {
    mcArray[i].speed = birdSpeed;
    addChildAt( mcArray[i], 2 );
    mcArray[i].addEventListener( Event.ENTER_FRAME , birdAction );
    mcArray[i].addEventListener( Event.ENTER_FRAME , mouseAction_reset );
    }
    }
    else
    {
    pop = 0;
    }

    num = xGetRandomInt(1,10);
    switch ( num ) {
    case 1://花出現
    back1Pop();
    break;
    case 2://空き缶出現
    back2Pop();
    break;
    default://出現しない
    }

    }

    //犬衝突処理&削除
    function dogAction(e:Event):void {
    var mc:MovieClip = e.target as MovieClip;
    mc.x += mc.speed;

    if( mc.hitTestPoint( cat_mc.x, cat_mc.y + cat_mc.height/2, true ) && mc.endFlag != true )
    {
    if( Global.soundFlag == true)
    {
    dog_sou.play();
    }

    if( distance.delay >= 100 )
    {
    mc.gotoAndStop(2);
    mc.dog2_mc.gotoAndPlay("up");//犬縮小化&速度UPアニメ
    backSpeed *= 1.05;//ねこちゃん&背景移動速度上昇
    cat_timer.delay *= 0.95;//ねこちゃんの足の動きも早めたい
    timer.delay *= 0.95;//動物出現速度上昇
    distance.delay *= 0.95;//距離計測上昇

    dogSpeed *= 1.05;
    mouseSpeed *= 1.05;
    birdSpeed *= 1.05;

    zokanum *= 1.05;
    }
    else
    {
    mc.gotoAndStop(2);
    mc.dog2_mc.gotoAndPlay("max");//犬縮小化&速度MAXアニメ
    }
    cat_mc.smile = true;
    mc.endFlag = true;
    }
    }

    function dogAction_reset(e:Event):void {
    var mc:MovieClip = e.target as MovieClip;

    if( mc.x > 1000 || Global.resetFlag == true )
    {
    mc.removeEventListener( Event.ENTER_FRAME , arguments.callee );
    this.removeChild( mc );
    }
    }

    //動物衝突処理&削除
    function mouseAction(e:Event):void {
    var mc:MovieClip = e.target as MovieClip;
    mc.x += mc.speed;

    if( mc.hitTestPoint( cat_mc.x, cat_mc.y + cat_mc.height/2, true ) && Global.stopFlag == false )
    {
    if( Global.soundFlag == true)
    {
    mouse_sou.play();
    channel.stop();
    }
    Global.stopFlag = true;
    mc.gotoAndStop(2);//動物巨大化
    mc.removeEventListener( Event.ENTER_FRAME , arguments.callee );
    }
    }

    function mouseAction_reset(e:Event):void {
    var mc:MovieClip = e.target as MovieClip;

    if( mc.x > 1000 || Global.resetFlag == true )
    {
    mc.removeEventListener( Event.ENTER_FRAME , arguments.callee );
    this.removeChild( mc );
    }
    }

    function birdAction(e:Event):void {
    var mc:MovieClip = e.target as MovieClip;
    mc.x += mc.speed;

    if( mc.hitTestPoint( cat_mc.x, cat_mc.y + cat_mc.height/2, true ) && Global.stopFlag == false )
    {
    if( Global.soundFlag == true)
    {
    bird_sou.play();
    channel.stop();
    }
    Global.stopFlag = true;
    mc.gotoAndStop(2);//動物巨大化
    mc.removeEventListener( Event.ENTER_FRAME , arguments.callee );
    }
    }

    function back1Pop():void {
    num = xGetRandomInt(1,2);
    num2 = xGetRandomInt(0,50);
    var mc = new back1_mc();
    mc.x = -200;
    if( num == 1 )
    {
    mc.y = num2;
    }
    else
    {
    mc.y = 380 + num2;
    }
    addChildAt( mc, 1 );
    mc.addEventListener( Event.ENTER_FRAME , backAction );
    mc.addEventListener( Event.ENTER_FRAME , backAction_reset );
    }

    function back2Pop():void {
    num = xGetRandomInt(1,2);
    num2 = xGetRandomInt(0,50);
    var mc = new back2_mc();
    mc.x = -200;
    if( num == 1 )
    {
    mc.y = num2;
    }
    else
    {
    mc.y = 380 + num2;
    }
    addChildAt( mc, 1 );
    mc.addEventListener( Event.ENTER_FRAME , backAction );
    mc.addEventListener( Event.ENTER_FRAME , backAction_reset );
    }

    function backAction(e:Event):void {
    var back:MovieClip = e.target as MovieClip;
    back.x += backSpeed;

    if( Global.stopFlag == true )
    {
    back.removeEventListener( Event.ENTER_FRAME , arguments.callee );
    }
    }

    function backAction_reset(e:Event):void {
    var back:MovieClip = e.target as MovieClip;

    if( back.x > 1000 || Global.resetFlag == true )
    {
    back.removeEventListener( Event.ENTER_FRAME , arguments.callee );
    this.removeChild( back );
    }
    }

    //ランダム数値生成関数
    function xGetRandomInt(n0:int=0, n1:int=1):int {
    var nMax:int = Math.max(n0, n1);
    var nMin:int = Math.min(n0, n1);
    var nRandom:int = Math.floor(Math.random()*(nMax-nMin+1))+nMin;
    return nRandom;
    }

    start_btn.addEventListener(MouseEvent.CLICK, fl_ClickToGoToNextFrame);
    function fl_ClickToGoToNextFrame(event:MouseEvent):void
    {
    gotoAndPlay("main");
    }

    play_btn.addEventListener(MouseEvent.CLICK, fl_ClickToGoToAndStopAtFrame_4);
    function fl_ClickToGoToAndStopAtFrame_4(event:MouseEvent):void
    {
    gotoAndStop("play");
    }

    sou_on_btn.addEventListener(MouseEvent.CLICK, fl_ClickToGoToAndStopAtFrame_6);
    function fl_ClickToGoToAndStopAtFrame_6(event:MouseEvent):void
    {
    if( Global.soundFlag == true )
    {
    Global.soundFlag = false;
    sou_on_btn.visible = true;
    sou_off_btn.visible = false;
    }
    else if( Global.soundFlag == false )
    {
    Global.soundFlag = true;
    sou_on_btn.visible = false;
    sou_off_btn.visible = true;
    }
    }

    sou_off_btn.addEventListener(MouseEvent.CLICK, fl_ClickToGoToAndStopAtFrame_7);
    function fl_ClickToGoToAndStopAtFrame_7(event:MouseEvent):void
    {
    if( Global.soundFlag == true )
    {
    Global.soundFlag = false;
    sou_on_btn.visible = true;
    sou_off_btn.visible = false;
    }
    else if( Global.soundFlag == false )
    {
    Global.soundFlag = true;
    sou_on_btn.visible = false;
    sou_off_btn.visible = true;
    }
    }

    end_btn.addEventListener(MouseEvent.CLICK, fl_ClickToGoToAndStopAtFrame_8);
    function fl_ClickToGoToAndStopAtFrame_8(event:MouseEvent):void
    {
    NativeApplication.nativeApplication.exit();
    }
    なんかごちゃごちゃ書きました!
    MouseEvent.CLICK はタップでも反応するので
    別途TOUCHイベントを用意する必要はなさそうです。
    いろいろアホな書き方がされていますが
    背景の空き缶とかまで無駄に動的に生成しているのが特にアホですね。
    背景の無限スクロールとかもすごく適当なのがおわかりいただけるであろうか。

    4フレーム目

    stop();
    なんかこれだけ書いてあった。血迷ったか。

    5フレーム目

    var cat_mc:cat_base = new cat_base();
    cat_mc.x = 563;
    cat_mc.y = 200;
    addChildAt( cat_mc, 1 );

    removeEventListener(Event.ENTER_FRAME, backMove);
    var location:String = "center";

    //フラグ待機
    cat_mc.addEventListener( Event.ENTER_FRAME, catAction );
    function catAction( e:Event ):void {
    if( Global.stopFlag == true )
    {
    cat_timer.stop();
    removeEventListener( Event.ENTER_FRAME , cursorMoveUp );
    removeEventListener( Event.ENTER_FRAME , cursorMoveDown );
    removeChild( cursor_top_mc );
    removeChild( cursor_bottom_mc );
    cat_mc.gotoAndPlay("lose");
    cat_mc.removeEventListener( Event.ENTER_FRAME , catAction );
    gotoAndStop("end");
    }
    }

    cat_mc.addEventListener( Event.ENTER_FRAME, catAction_reset );
    function catAction_reset( e:Event ):void {
    if( Global.resetFlag == true )
    {
    cat_mc.gotoAndStop(1);
    cat_mc.y = -200;
    cat_mc.removeEventListener( Event.ENTER_FRAME , catAction_reset );
    removeChild( cat_mc );
    }
    }

    if( Global.soundFlag == true )
    {
    start_sou.play();
    }
    ゲームスタートの瞬間です!!!
    なんでこんな風に書いたかは忘れました。


    ゲーム中のフレーム

    stop();

    timer.start();
    distance.start();
    cat_mc.gotoAndStop("loop2");
    cat_timer.start();
    addEventListener(Event.ENTER_FRAME, backMove);

    if( Global.soundFlag == true )
    {
    var channel = bgm_sou.play(0,999);
    }

    var lastIndex1:int = numChildren - 1;
    var cursor_top_mc:cursor_top = new cursor_top();
    cursor_top_mc.x = 544;
    cursor_top_mc.y = 80.80;
    addChildAt( cursor_top_mc, lastIndex1 );

    var lastIndex2:int = numChildren - 1;
    var cursor_bottom_mc:cursor_bottom = new cursor_bottom();
    cursor_bottom_mc.x = 544;
    cursor_bottom_mc.y = 312.95;
    addChildAt( cursor_bottom_mc, lastIndex2 );

    var zokanum:Number = 5;
    var zokabun:int = 0;
    function cursorMoveUp(e:Event):void {
    zokabun += zokanum;
    cursor_top_mc.y -= zokanum;
    cursor_bottom_mc.y -= zokanum;
    cat_mc.y -= zokanum;
    if( zokabun >= 100 )
    {
    switch ( location )
    {
    case "top":
    cat_mc.y = 100;
    break;
    case "center":
    cat_mc.y = 200;
    break;
    case "bottom":
    cat_mc.y = 300;
    break;
    }
    zokabun = 0;
    cursor_top_mc.visible = true;
    cursor_bottom_mc.visible = true;
    removeEventListener( Event.ENTER_FRAME , cursorMoveUp );
    }
    }

    function cursorMoveDown(e:Event):void {
    zokabun += zokanum;
    cursor_top_mc.y += zokanum;
    cursor_bottom_mc.y += zokanum;
    cat_mc.y += zokanum;
    if( zokabun >= 100 )
    {
    switch ( location )
    {
    case "top":
    cat_mc.y = 100;
    break;
    case "center":
    cat_mc.y = 200;
    break;
    case "bottom":
    cat_mc.y = 300;
    break;
    }
    zokabun = 0;
    cursor_top_mc.visible = true;
    cursor_bottom_mc.visible = true;
    removeEventListener( Event.ENTER_FRAME , cursorMoveDown );
    }
    }

    cursor_top_mc.addEventListener(MouseEvent.CLICK, fl_ClickToGoToAndStopAtFrame);
    function fl_ClickToGoToAndStopAtFrame(event:MouseEvent):void
    {
    if( location == "center" )
    {
    if( Global.soundFlag == true )
    {
    ue_sou.play();
    }
    addEventListener(Event.ENTER_FRAME, cursorMoveUp);
    cursor_top_mc.visible = false;
    cursor_bottom_mc.visible = false;
    location = "top";
    }
    else if( location == "bottom" )
    {
    if( Global.soundFlag == true )
    {
    ue_sou.play();
    }
    addEventListener(Event.ENTER_FRAME, cursorMoveUp);
    cursor_top_mc.visible = false;
    cursor_bottom_mc.visible = false;
    location = "center";
    }
    }

    cursor_bottom_mc.addEventListener(MouseEvent.CLICK, fl_ClickToGoToAndStopAtFrame_2);
    function fl_ClickToGoToAndStopAtFrame_2(event:MouseEvent):void
    {
    if( location == "top" )
    {
    if( Global.soundFlag == true )
    {
    shita_sou.play();
    }
    addEventListener(Event.ENTER_FRAME, cursorMoveDown);
    cursor_top_mc.visible = false;
    cursor_bottom_mc.visible = false;
    location = "center";
    }
    else if( location == "center" )
    {
    if( Global.soundFlag == true )
    {
    shita_sou.play();
    }
    addEventListener(Event.ENTER_FRAME, cursorMoveDown);
    cursor_top_mc.visible = false;
    cursor_bottom_mc.visible = false;
    location = "bottom";
    }
    }
    上下矢印をタッチすると移動するという仕組みを書いてます。
    ちなみにボタンクリックイベントはボタンMCを選択して、
    「ウィンドウ」⇒「コードスニペット」から設定すると簡単です。

    ゲーム終了フレーム

    stop();

    if( Global.httpFlag == false ){
    score_btn.visible = false;
    retry_btn.y = 245.3;
    }

    removeEventListener(Event.ENTER_FRAME, backMove);
    timer.stop();
    distance.stop();

    retry_btn.addEventListener(MouseEvent.CLICK, fl_ClickToGoToAndStopAtFrame_3);
    function fl_ClickToGoToAndStopAtFrame_3(event:MouseEvent):void
    {
    gotoAndPlay("init");
    }


    score_btn.addEventListener(MouseEvent.CLICK, fl_ClickToGoToWebPage);
    function fl_ClickToGoToWebPage(event:MouseEvent):void
    {
    var url:String = "http://xxx.com/twitteroauth/redirect.php";
    var request:URLRequest = new URLRequest(url);
    var variables:URLVariables = new URLVariables();
    var YmdHis:String = date14();
    //version_YmdHis_score
    variables.s = encrypt(YmdHis+"_"+distance_i,"penetpenet");
    request.data = variables;
    request.method = URLRequestMethod.POST;
    navigateToURL( request );
    }

    function encrypt(str:String, key:String = '%key&'):String {
    var result:String = '';
    for (var i:int = 0; i < str.length; i++) {
    var char:String = str.substr(i, 1);
    var keychar:String = key.substr((i % key.length) - 1, 1);
    var ordChar:int = char.charCodeAt(0);
    var ordKeychar:int = keychar.charCodeAt(0);
    var sum:int = ordChar + ordKeychar;
    char = String.fromCharCode(sum);
    result = result + char;
    }
    return Base64.encode(result);
    }

    function decrypt(str:String, key:String = '%key&'):String {
    var result:String = '';
    var str:String = Base64.decode(str);

    for (var i:int = 0; i < str.length; i++) {
    var char:String = str.substr(i, 1);
    var keychar:String = key.substr((i % key.length) - 1, 1);
    var ordChar:int = char.charCodeAt(0);
    var ordKeychar:int = keychar.charCodeAt(0);
    var sum:int = ordChar - ordKeychar;
    char = String.fromCharCode(sum);
    result = result + char;
    }
    return result;
    }
    ゲーム終了時にTwitterのアカウントでスコアランキングに登録できるようになってます。
    AIRだとPOSTできないのでGET値を暗号化&有効期限設定してサーバへ送信しています。
    サーバ側はPHP+twitteroauth+Mysqlで実現しています。

    リトライ

    Global.resetFlag = true;
    これだけで初期化されてもう一度遊べるようになってます。
    あっ!Globalは普通はAS3.0では使えませんでした。
    こちらのサイト様などを参考にしていただければ。。。
    http://clockmaker.jp/blog/2010/05/as3-global/

    そしてGoogleに$25支払ってGoogleMarketにapkファイルアップロードすればリリース完了。
    ほとんど作り方忘れてることもあって力尽きた。

    あとはなんか困ったら cute_cat-app.xml をテキストエディタで修正しよう!

    cute_cat-app.xmlの内容



    jp.co.penet.cutecat
    1.0.0

    cute_cat


    かわいいねこちゃん



    cute_cat.swf
    standard
    false
    true
    true
    landscape
    auto
    false


    false
    false


    ]]>
















    ]]>



    ポイントは「<data android:scheme="cutecat"/>」と設定することで、ブラウザで「<a href="cutecat://">アプリ起動</a>」リンクを踏むとアプリを起動させることができるようになるところ。
    それと「<manifest android:installLocation="preferExternal"/>」でSDカードにインストール可能になる設定かな!

    ね、簡単でしょ?
    posted by BBキング∞金×金∞ at 18:15 | Comment(0) | TrackBack(0) | WEB開発 | このブログの読者になる | 更新情報をチェックする