UnityでAndroid In App Billing Pluginを使ってアプリ内課金をためす方法

prime31Asset Store – Android In App Billing PluginがIABを使うときによさそうなので、このAssetの使い方をメモしていきます。

Google Play Developer Consoleにアプリを登録する

アプリをいったん仮に登録しないと、アイテム画面に行けないのでまずはこちらをアップします。

こちらからgmailアカウントをもとに登録していきます。
すべてのアプリ – Google Play Developer Console
$25かかってしまうのですが、これをしないとアプリ内課金の仕組みが使えません。

間違えやすいのですが、Google Developers Consoleではないよ。

ここでUnityからapkファイルを作成して、アップする必要があるのですが、公開用の署名が付いていないのとアップ時にエラーになってしまいます。
Unityではこの鍵を作るところもサポートしてくれています。

BuildSettings → AndroidのPlayerSettingsを開く。
Publushing SettingsでCreate New Keystoreにチェックを入れる。
BrowseKeystoreでKeystoreの保存場所を選択する。
Passwordを入力する。
AliasプルダウンからCreateNewKeyを選択する。
Aliasには仮にtestと入力しました。
Passwordを入力する。
CreateKeyで作成する。
作成したあとに、Aliasで今作成したAliasを選択する。

via 【Unity】keystoreの生成 Android – Litのプログラムとかアプリ開発とか。

では、Buildしてapkファイルを作成し、Consoleにアップしましょう!
ちなみに、製品版にアップしてもただちに公開されるわけではないので大丈夫です。

他にベータ版・アルファ版とアップ先がありますが、ここでは製品版にアップしてみました。

Google Play Developer Consoleに課金用のアイテムを登録する

アプリ内アイテムで新しいアイテムを追加をクリックすると、
以下から選ぶようにいわれます。

  1. 管理対象の商品
  2. 管理対象外の商品
  3. 定期購入

この違いは、以下のサイトから引用させていただきます。

1.はアカウント単位で1回購入できるアイテム(電子書籍とか)。appleでいうNon-consumable
2.は同一アカウントで何回も購入できるアイテム(回復系アイテムとか)。appleでいうConsumable
3.これはappleのAuto-renewable subscriptionsかな?

via: エンジニアブログはじめました: アンドロイドでアプリ内課金(v3)のテスト環境

今回は魔法石のように何度でも買えるようにするために、管理対象外の商品を選択します。

アイテムを作成したら、プルダウンから有効にしておきます。
これを忘れると、いっこうにアイテム課金ができません。(ぼくはこれで結構ハマった!)

UnityでAndroid In App Billing Pluginを使ってみる

Android In App Billing Pluginをimportするとdemoがあるので、そのシーンに移動する。

以下、[Unity][Android] (In App Billing ver3版) prime31 Android IAB Pluginを使用したアプリ内課金の実装 : West Hill 開発メモこちらの記事を参考に、必要そうな処理も追記しています。

GoogleIABEventListener.cs

そもそもHierarchyに追加したgameObjectが破棄されることがあるのか調査できていないのですが、これを追加。

void Start ()
{
    // このオブジェクトが破棄されないようにする
    DontDestroyOnLoad (gameObject);
}

IABUIManager.cs

ConsoleのサービスとAPIから、ライセンスキーをinitの中に記述します。
アプリケーションが終了した場合にunbindされるようにしておきます。

void OnApplicationQuit() {
    Debug.Log("OnApplicationQuit");
    GoogleIAB.unbindService();
}

以下のボタンのところを追加したアイテムIDに変更しておきます。
注意:消費系のアイテムの場合、GoogleIAB.purchaseProductしたあとにGoogleIAB.consumeProductをしないと再度買うことができません。

if( GUILayout.Button( "Purchase Test Product" ) )
{
    GoogleIAB.purchaseProduct( "magic_stone" );
}


if( GUILayout.Button( "Consume Test Purchase" ) )
{
    GoogleIAB.consumeProduct( "magic_stone" );
}

ただ、IAB側の問題なのか調査できていないのですが、このボタンを順番に押した時に、GoogleIAB.consumeProductが失敗する場合がありました。
ログを見ながら押していったのですが、GoogleIAB.consumeProductするのが速すぎたのかな。

なので、GoogleIABEventListener.csの購入が成功したイベントハンドラでGoogleIAB.consumeProductするようにしてみました。
実際にはこうゆうコードでやることになるのかな。

// 購入が正常に終わると呼ばれる
void purchaseSucceededEvent( GooglePurchase purchase )
{
    Debug.Log( "purchaseSucceededEvent: " + purchase );
    GoogleIAB.consumeProduct(purchase.productId);
}

引数のGooglePurchaseに購入レシート情報が入っています。こちらを見て、何を買ったのかをアプリケーション・サーバーに通知するのかな。

テスト課金って書いてあってもなんか怖いねw

参考リンク

AndroidBilling – 開発資料
Google Play In-App Billing テスト方法 | Androidアプリ開発の紆余曲折
android/アプリ内課金 – 初心者エンジニアの簡易メモ
エンジニアブログはじめました: アンドロイドでアプリ内課金(v3)のテスト環境

見れていないのですが、
In-App Purchasing Unity Plugin for Android Tutorial – YouTube
こちらの動画も参考になるかも。
自前でやっているっぽいですが。

iPhone&Androidアプリ内課金プログラミング完全ガイド (Smart Mobile Developer)
佐藤 航陽 加藤 勝也 瀬戸 健二 日高 正博
翔泳社
売り上げランキング: 182,508

UnityのAssetBundleを使うときのお作法

AssetBundleについてはまだ勉強を始めたばかりで検証中なのですが、あまりにも情報がいっぱいあるがそれぞれでやり方が違う印象があるので、
いろいろ書いていって最適解を出したいと思います。

なので、この記事はまだ(仮)です。

AssetBundleについて

スマホアプリはインストール時にサイズを50MBにに抑えないとWifi経由でのインストールしかできなくなります。
それだけではなく、オフラインのときでも使えるようにローカルにキャッシュしてほしいなど、制限が結構あります。

そこでAssetBundleを使って、追加コンテンツとしてアプリ内にダウンロードできる仕組みです。

Unityを使って何かしらのスマホアプリを作るなら、割りと使うことになりそうな仕組みですね。

AssetBundleに含められるのは、

  • GameObject
  • Material
  • Texture
  • Prefab

などなど。

AssetBundleをiOS・Android用にビルドするにはiOS Pro・Android Proのライセンスが必要になります。
なので、これらを使わなければお金は掛からないのですが、使うとなると途端に結構な金額のライセンスが必要です。

デメリットとしてメモリを多く消費するという点があるようです。
なので、このAssetBundleをどうゆう単位で作り、いつ破棄するかが結構重要になりそうですね。

また、iOSとAndroidで別に書き出す必要があり、その書き出しのタイミングで最適化がされているようです。
別々のファイルになってしまうので、Unity側でどのAssetBundleを取りに行くかの分岐が必要です。

注意点として、ScriptはAssetBundleに含められません。
iPhone側の話しですが、動的にScriptを生成することを許可していないからです。

AssetBundleを使ってみる

UnityのAssetBundleをじっくり理解する手順をまとめてみた。【アセットバンドル】 – NAVER まとめ
こちらの記事が一番読みやすかったので、これにそって試してみました。

まずはAssetBundleを作成するためのEditor拡張を入れます。
BuildPipeline.BuildAssetBundle
こちらにあるコードを参考にしてたんですが、書いてあるコードにはiOS・Android用のビルドが書かれていなかったので、
Unity AssetBundle Examples.を参考にしました。

// C# の例
// プロジェクト ウィンドウの選択されたオブジェクトからアセットバンドルを作成
// コンパイルした後は "Menu" -> "Assets" へ移動して選択肢から一つを選択して
// アセットバンドルをビルド
using UnityEngine;
using UnityEditor;

public class ExportAssetBundles
{
    [
     MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")]
    static void ExportResource ()
    {
        // 保存ウィンドウのパネルを表示
        string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
        if (path.Length != 0) {
            // アクティブなセレクションに対してリソースファイルをビルド

            Object[] selection =
                Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);

            // via https://gist.github.com/yaeda/5410868
            // require iOS Pro, Android Pro Lisence
            // for Android
            BuildPipeline.BuildAssetBundle(Selection.activeObject,
                                           selection, path + ".android.unity3d",
                                           BuildAssetBundleOptions.CollectDependencies |
                                           BuildAssetBundleOptions.CompleteAssets,
                                           BuildTarget.Android);

            // for iPhone
            BuildPipeline.BuildAssetBundle(Selection.activeObject,
                                           selection, path + ".iphone.unity3d",
                                           BuildAssetBundleOptions.CollectDependencies |
                                           BuildAssetBundleOptions.CompleteAssets,
                                           BuildTarget.iPhone);

            // for WebPlayer
            BuildPipeline.BuildAssetBundle(Selection.activeObject,
                                           selection, path + ".unity3d",
                                           BuildAssetBundleOptions.CollectDependencies |
                                           BuildAssetBundleOptions.CompleteAssets,
                                           BuildTarget.WebPlayer);

            Selection.objects = selection;
        }
    }

    [
     MenuItem("Assets/Build AssetBundle From Selection - No dependency tracking")]
    static void ExportResourceNoTrack ()
    {
        // 保存ウィンドウのパネルを表示
        string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
        if (path.Length != 0) {
            // アクティブなセレクションに対してリソースファイルをビルド

            BuildPipeline.BuildAssetBundle (
                Selection.activeObject,
                Selection.objects, path);
        }
    }
}

■gist
AssetBundle用のエディター拡張

Assets/Build AssetBundle From Selection – No dependency trackingのほうはまだ使えていないので、複数OS分のコードは書いていません。

このクラスを、

Assets/Editor

に配置します。

適当なPrefabを作成し、Prefabを選択した状態で右クリックから、Build AssetBundle From Selection – Track dependenciesを実行します。
すると、

  • Asset.unity3d.android.unity3d
  • Asset.unity3d.iphone.unity3d
  • Asset.unity3d.unity3d

のように、WebPlayer用・android用・iphone用の3つができました。
これをDropboxに適当に配置し、URLをメモしておきます。

■参考リンク
アセットバンドルのビルド / Building AssetBundles

それでは、Unity側でAssetBundleを使ったコードを書いていきましょう。

using System;
using UnityEngine;
using System.Collections;

public class CachingLoadExample : MonoBehaviour
{
    void Start ()
    {
        // Clear Cache
        Caching.CleanCache();

#if   UNITY_ANDROID && !UNITY_EDITOR
        string url = "https://dl.dropboxusercontent.com/Asset.unity3d.android.unity3d
#elif UNITY_IPHONE  && !UNITY_EDITOR
        string url = "https://dl.dropboxusercontent.com/Asset.unity3d.iphone.unity3d
#else
        string url = "https://dl.dropboxusercontent.com/Asset.unity3d.unity3d?dl=1
#endif

        StartCoroutine (DownloadAndCache ("Particle System",
                                          url,
                                          1));
        StartCoroutine (DownloadAndCache ("Sprite",
                                          url,
                                          1));
    }

    public IEnumerator DownloadAndCache (string assetName, string url, int version = 1)
    {
        // キャッシュシステムの準備が完了するのを待ちます
        while (!Caching.ready)
            yield return null;

        // 同じバージョンが存在する場合はアセットバンドルをキャッシュからロードするか、またはダウンロードしてキャッシュに格納します。
        using (WWW www = WWW.LoadFromCacheOrDownload (url, version)) {
            yield return www;
            if (www.error != null) {
                throw new Exception ("WWWダウンロードにエラーがありました:" + www.error);
            }

            AssetBundle bundle = www.assetBundle;
            if (assetName == "")
                Instantiate (bundle.mainAsset);
            else
                Instantiate (bundle.Load (assetName));
            // メモリ節約のため圧縮されたアセットバンドルのコンテンツをアンロード
            bundle.Unload (false);

        } // memory is freed from the web stream (www.Dispose() gets called implicitly)

        Debug.Log(Caching.IsVersionCached(url, 1));
        Debug.Log("DownloadAndCache end");
    }
}

■gist
AssetBundleを使うサンプル

WWW.LoadFromCacheOrDownloadを使うのがミソっぽいです。

■参考リンク
アセットバンドルのダウンロード / Downloading AssetBundles

こちらの記事を読んでみると、WWW.LoadFromCacheOrDownloadを使うのがよさそうですね!

AssetBundleの読み込み方法
  • 1番オススメ:LoadFromCacheOrDownload
    • ダウンロードしたものが展開された状態でストレージに保存される
      • 実行時の読み込みの際に、展開用のメモリが必要ない
      • その分、ストレージの容量は圧迫する
  • 2番目にオススメ:CreateFromFile
    • 圧縮していないAssetBundleしか使えない
    • 圧縮できないのでストレージの容量は圧迫する
  • 非おすすめ:WWW、CreateFromMemory
    • 展開に使う分のメモリが必要
    • 圧縮したAssetBundleが使える → ストレージ容量は節約できる
  • 過去の遺物:Resources
    • AssetBundleと比べて効率が悪い。今から作るならAssetBundleを使ったほうが良い

via: Unite Japan 2013 の1日目に行ってきたメモ – 好き勝手に・げーあにん?

これで、Buildすると各OSでもAssetBundleが使えます。
でも、これでやっと使えるレベル、どう使う?単位は?という道はまだほど遠い。

StreamingAssetsに配置してロードしてみる

StreamingAssetsは簡単に言えば、ファイルをそのままアプリに持っていけるフォルダだ。そこに置いたものは何であれビルド時に余計なエンコードされること無くアプリ内に配置することができる。

via: テラシュールウェア [Unity3D] StreamingAssetsについて

いまいちまだStreamingAssetsをどうゆうときに使うのか分かっていないが、普通にFileアクセスはできずWWW経由でアクセスするようだ。
これはAssetBundleにアクセスする方法と同じだから特に理解には困らないが、StreamingAssetsに置くなら、普通にAssetsディレクトリの下にPrefabとかを置くでもよいのかなーと思っていたり。
ここは次第に実感が湧いてくるんだろう。

このディレクトリにアクセスするなら、Streaming AssetとPersistent DataPathの注意点 Android、iOS Unity – 万年素人からGeekへの道こちらの記事が参考になる。

では実際にファイルをロードするコードを書いてみよう。

// StreamingAssetsからオブジェクトを取得する場合
public IEnumerator LoadStreamingAssets(string assetName, string fileName, int version = 1) {
#if   UNITY_ANDROID && !UNITY_EDITOR
    string url = "jar:file://" + Application.dataPath + "!/assets/" + fileName;
    Debug.Log("AndroidUrl: " + url);
#elif UNITY_IPHONE  && !UNITY_EDITOR
    string url = "file://" + Application.dataPath + "/Raw/" + fileName;
#else
    string url = "file://" + Application.streamingAssetsPath + "/" + fileName; // Application.dataPath + "/StreamingAssets/" + fileName
#endif
    Debug.Log("AssetBundleUrl: " + url);

    // キャッシュシステムの準備が完了するのを待ちます
    while (!Caching.ready)
        yield return null;

    // 同じバージョンが存在する場合はアセットバンドルをキャッシュからロードするか、またはダウンロードしてキャッシュに格納します。
    using (WWW www = WWW.LoadFromCacheOrDownload (url, version)) {
        yield return www;
        if (www.error != null) {
            throw new Exception ("WWWダウンロードにエラーがありました:" + www.error);
        }

        AssetBundle bundle = www.assetBundle;
        if (assetName == "")
            Instantiate (bundle.mainAsset);
        else
            Instantiate (bundle.Load (assetName));
        // メモリ節約のため圧縮されたアセットバンドルのコンテンツをアンロード
        bundle.Unload (false);

    } // memory is freed from the web stream (www.Dispose() gets called implicitly)

    Debug.Log(Caching.IsVersionCached(url, 1));
    Debug.Log("DownloadAndCache end");
}

各パスの説明は、ストリーミングアセット / Streaming Assetsに記載されている。

あと、どうもversionを上げてもキャッシュを見に行ってしまうことがあるようで、その場合はurlの後ろにnow的なものを付けてアクセスするのが良さそうです。

System.DateTime.Now.ToString("yyyyMMddhhmmss")

こんな感じをQueryStringに含めるのがいいんですかね。

アセット依存関係の管理 / Managing asset dependencies

Unity – Unity Manual

複数のAssetBundleに同じオブジェクトやシェーダーが含まれてしまう場合は、まったく別でメモリ確保されてしまうため、
依存関係を管理することができるようです。

BuildPipeline.PushAssetDependencies();
BuildPipeline.PopAssetDependencies();

このメソッドを使うようですが、結構ハードコーディングが必要になりそうだなーという印象です。

keijiroさんのgithubリポジトリがすごく参考になります。
keijiro/unity-shader-bundle

参考リンク

「もののけ大戦“陣”」製作事例
Unity での asset bundle による追加コンテンツの扱い方
Asset bundleなどの、Unity3d基礎知識

こちらは間違いなく見たほうがよい。
[Unite Japan 2013]シーン/メモリ/アセットバンドル

UnityでAndroidNativePluginを作るときのお作法

AndroidNativePluginの作り方

ググると、いろんな記事がありそれぞれやり方が違ったので、なるべく新しい記事や動画を見て作ってみました。
以下の動画がシンプルで一番参考になったので、こちらとおり作ってみます。

eclipseでAndroid Application Projectを作成。
package名はあとあとUnityから呼び出すときに重要なので、それっぽく入力しておく。

targetなどのバージョンはこちらを読むと理解できる。
[Android] Androidアプリ開発における Target SDK, Minimum Required SDK, Compiler SDK の違い

Finishでプロジェクト作成。

メニューのデバッグボタンをクリックし、Android Applicationを選択、繋がっているAndroidデバイスを選んでOK。
Package Explorerからプロジェクトを右クリックし、Propertiesを選択。
PropertiesウィンドウでAndroidを選択、右のペインでIs Libraryにチェックを入れる。

すると上の画像のようにjarファイルが出来上がってる。

では、実際にJavaクラスを作成していく。
該当のパッケージに、Classを追加する。
ここでは、Hogeクラスを作成。

package com.test.androidnativeplugin;

import android.util.Log;

import com.unity3d.player.UnityPlayer;

public class Hoge {

  // 引数あり
  public void FuncA(final String msg) {
    Log.d("Android", "call FuncA");
    Log.d("Android", msg);
  }

  // 戻り値あり
  public String FuncB(final String msg) {
    Log.d("Android", "call FuncB");
    return "Back" + msg;
  }

  // callbackあり
  public void FuncC(final String gameObjName, final String msg) {
    Log.d("Android", "call FuncC");
    Log.d("Android", gameObjName);
    // 第一引数:GameObject名、第二引数:メソッド名
    UnityPlayer.UnitySendMessage(gameObjName, "onCallBack", msg);
  }
}

使いそうな3パターンを作ってみた。

ここからは、Unity上での作業。
以下のようにディレクトリを作成する。

Assets/Plugins/Android

ここにbinディレクトリにあるjarファイルを置く。

次は、

Assets/Plugins

に、Androidのメソッドを呼び出すクラスを作成する。

using UnityEngine;
using System.Collections;

public class HogeAndroidPlugin : MonoBehaviour
{
    public static void CallFuncA (string msg)
    {
#if UNITY_ANDROID
        AndroidJavaObject obj = new AndroidJavaObject("com.teamlab.androidnativeplugin.Hoge");
        obj.Call("FuncA", msg);
#endif
    }

    public static string CallFuncB (string msg)
    {
        string returnVal = null;
#if UNITY_ANDROID
        AndroidJavaObject obj = new AndroidJavaObject("com.teamlab.androidnativeplugin.Hoge");
        returnVal = obj.Call<string>("FuncB", msg);
#endif
        return returnVal;
    }

    public static void CallFuncC (string msg)
    {
#if UNITY_ANDROID
        AndroidJavaObject obj = new AndroidJavaObject("com.teamlab.androidnativeplugin.Hoge");
        obj.Call ("FuncC", "Main", msg);
#endif
    }
}

それぞれのメソッドの中には、Android上での実行かどうかのプリプロセッサを書いている。
iOS側のプラグインを書く場合にも、同様にifで実行環境ごとに分けるのがいいだろう。

ここまででプラグインの準備は完了だ。
あとは、これを呼び出す処理をテスト的に書いてみよう。

EmptyなGameObjectを作成し、MainというC#クラスをアタッチする。

using UnityEngine;
using System.Collections;

public class Main : MonoBehaviour
{
    void OnGUI ()
    {
        if (GUI.Button (new Rect (0, 0, 100, 100), "Push Me")) {
            // FuncA
            HogeAndroidPlugin.CallFuncA (" call CallFuncA ");

            // FuncB
            string returnVal = HogeAndroidPlugin.CallFuncB (" call CallFuncB ");
            Debug.Log ("Unity: " + returnVal);

            // FuncC
            HogeAndroidPlugin.CallFuncC (" call CallFuncC ");
        }
    }

    public void onCallBack (string msg)
    {
        Debug.Log ("Call From Native. (" + msg + ")");
    }
}

特にFuncCは使うシチュエーションがありそうだが、使いすぎると保守がしにくくなりそうだ。
UnityからAndroidに処理がわたり、Androidで処理が終わったタイミングでUnityのコードをCallbackする。(でも便利!)

ここまでで、Androidでjarを作成し、AndroidのNativePluginをUnityから呼び出すコードが書けたわけだ。
あとはBuild & Runを実行するのみ!

ビルドが完了すると実機でUnityで作ったアプリが起動しているはず。

ひとつ問題なのが、デバッグがし難いという点。
eclipseでLogCatビューを表示しておいて、Unityというtagの部分を発見するとちゃんとログがでている。

04-16 16:33:21.374: D/Android(11900): call FuncA
04-16 16:33:21.374: D/Android(11900): call CallFuncA
04-16 16:33:21.384: D/Android(11900): call FuncB
04-16 16:33:21.384: I/Unity(11900): Unity: Back call CallFuncB
04-16 16:33:21.384: I/Unity(11900):
04-16 16:33:21.384: I/Unity(11900): (Filename: ./artifacts/AndroidManagedGenerated/UnityEngineDebug.cpp Line: 53)
04-16 16:33:21.384: D/Android(11900): call FuncC
04-16 16:33:21.384: D/Android(11900): Main
04-16 16:33:21.394: I/Unity(11900): Call From Native. ( call CallFuncC )
04-16 16:33:21.394: I/Unity(11900):
04-16 16:33:21.394: I/Unity(11900): (Filename: ./artifacts/AndroidManagedGenerated/UnityEngineDebug.cpp Line: 53)

参考リンク

Unity3D – Androidネイティブ開発のすすめ – Qiita

実践 Android Developer Tools
実践 Android Developer Tools
posted with amazlet at 14.04.16
Mike Wolfson
オライリージャパン
売り上げランキング: 43,217

Alfredを使ってMissionControlのウィンドウ切り替えをしてみる、そしてWorkflowへ

はじめに

MissionControlの←→とウィンドウを切り替える機能は、毎日すごく使うのですが、キーボードショートカットの割り当てを

  • Ctrl + ←
  • Ctrol + →

としているんですが、どうしても十字キーが遠い…

そこで

  • Ctrl + H
  • Ctrol + L

としてた時期もあるんですが、Vimのウィンドウ切り替えとバッティングしてしまい、いまいちでした。

そこでAlfredを使ってなんとか出来ないかなーと思っていたんですが、サボってサボって。
そしてAppleScriptを使えばできるんじゃないかなーと思って、ちょっとやってみました。
その過程をメモメモ。

ターミナルからデスクトップ切り替えしてみる

こんなのがあったんですが、1を2、3と切り替えてみてもデスクトップ切り替えにはなりませんでした。

/Applications/Mission\ Control.app/Contents/MacOS/Mission\ Control 1

via Terminal commands for “Application Windows” and “Show Desktop” in Mountain Lion – Ask Different

AppleScriptを書いてみる

職場のチャットで話ししていて、こんなAppleScriptを教えてもらいました。

←に移動

tell application "System Events"
  key down control
  keystroke (ASCII character 28)
  key up control
end tell

■gist
MissionControllLeft.applescript

→に移動

tell application "System Events"
  key down control
  keystroke (ASCII character 29)
  key up control
end tell

■gist
MissionControllRight.applescript

そして、これをAlfredのAction → Terminal Commandから実行するようにしてみた。

osascript MissionControllRight.applescript

そして、InputのKeywordを適当に作って実行したところ、うまくいった!

Alfredのみで完結してみる(AppleScriptを外部ファイルにしない)

どうもこちらを見ていると、雑多の備忘録 – AppleScript/osascriptAppleScriptはShellとしても呼び出せそう。
なので、AlfredのAction → Run Scriptを使って、Languageのところを/usr/bin/osascriptし、上述したAppleScriptをペタリ。

これで、外部ファイル使わずにAlfred環境だけでいけた!!

Workflowにしておきました

hisasann/MissionControlMoveSpace

おまけ

MissionControllのdurationを変更できそうなので、ためしに以下のコマンドを実行してみましたが、
ウィンドウ切り替えのdurationは変更できませんでした。

defaults write com.apple.dock expose-animation-duration -float 0.15; killall Dock

via Mission Controlが表示されるアニメーションのスピードを調節する方法 – Macの手書き説明書

はじめてのUnity – インタラクティブサイネージができるまで

Unityをインストールはしていたけど、実際に使ったことがない状態から試行錯誤して、やりたい事が実現し、
ハマったところをメモしていたので、ここに残しておきます。
何かの参考になれば幸いです!

はじめ

そもそもサイネージなどのハードウェアと密接なお仕事を経験したことがなかったので、
終盤までかなり苦しめられました。

やろうとしていたことが、

  1. 巨大なタッチパネルを2枚繋げたデュアルディスプレイで、且つフルスクリーンでマルチタッチをしたい!
  2. 複数人で楽しめてゲーム性を持たせたい!

というものでした。

そこで、まずはUnity自体を触ってみるということで以下の本をとりあえず1日掛けてやってみました。

Unity4入門   最新開発環境による簡単3Dゲーム製作
浅野 祐一 荒川 巧也 森 信虎
ソフトバンククリエイティブ
売り上げランキング: 1,550

この手の新しいことをやるときに、難しいところから入るとテンションが上がらないので、まずは入門書。
こちらの本は入門書なんですが、初心者がハマるところをよく練りこまれていて、この本以外は結局読まずに最後まできてしまいました。

あとは、ググったり、Unityのdocsを中心に見ていきました。そしてStackOverFlow様!
Unity – Unity Manual

これ以外で解決できないものは、会社のインタラクティブチームにヒアリングして教えてもらってという感じです。
(ほんとこのチームがなかったら出来てなかった、、、)

今回使ったもの

実行機のOS
Windows8
Unity
4.2
GestureWorks
マルチタッチライブラリ
Uni2D
Unity用2Dライブラリ

この後の、Unity4.3で2Dを作る仕組みが入ったようです。

GestureWorksはマルチタッチを検出してくれるライブラリで、dllをUnityから読み込んで使います。
ここはTouchScriptでも代用できるようですが、
今回はトライという意味も含めてGestureWorksで遊んでみました。

Unity編

物理エンジン

ゲームのベースとなる部分は物理演算をメインに使いたいと思っていたので、Unityは最高でした。
とくにコードレスでモノを落下させたり地面でバウンスさせたりが楽でした。

物理演算を有効にするには、GameObjectにRigidbodyを付与します。
するとどこまでもどこまでも落ち続けるオブジェクトが完成します。
Unity – Unity Manual

さらに当たり判定が必要な場合は、何かしらのColliderを付与します。

ColliderはBox ColliderやMesh Colliderなど様々ありますが、最終的にパフォーマンスを考慮して、
Box Collider、Sphere Collider、Capsule Colliderの3種類のみを使いました。

Mesh Colliderをはじめのうち使っていたのですが、画面内にオブジェクトが100個や200個などに増えた場合、
FPSが極端に落ちてしまい、今回のゲームでは使えなそうでした。
確かに、オブジェクトの形状と同じようにColliderを作り、当たり判定を計算するのはむちゃくちゃ重そうですね。

物理演算をいじる場合、物理特性マテリアルをいじるといい感じになりました。
これはPhysics Materialを作成し、GameObjectのColliderにアタッチします。
Unity – Unity Manual
デフォルトだと全然バウンスしてくれないので、Bouncinessを1とかにすると楽しいです。

あと、落下スピードが速かったので、もう少しゆるい世界観にするために、物理特性マネージャを使ってGravityのYを少し大きくしました。
デフォルトは-9.81なので、-8とか。
Unity – Unity Manual

オブジェクトを吹っ飛ばす

rigidbodyに力を加えることで吹っ飛ばすことが可能です。
以下の2種類を使ってみました。

AddForceで力を加える

流星とかを作るときに便利でした。

gameObject.rigidbody.AddForce (new Vector3 (power, power, 0), ForceMode.Impulse);

AddExplosionForceで周囲に爆発の力を加える

周囲を吹き飛ばす爆弾とかに便利でした。

Rigidbody body = gameObject.collider.attachedRigidbody;
body.AddExplosionForce (50.0f, transform.position, 10.0f, 3, ForceMode.Impulse);

最後の引数のForceModeは以下のとおり。

Force その質量を使用して、rigidbodyへの継続的な力を追加します。
Acceleration その質量を無視して、rigidbodyへの継続的な加速を追加します。
Impulse その質量を使用して、rigidbodyに瞬時に速度変化を追加します。
VelocityChange その質量を無視して、rigidbodyに瞬時に速度変化を追加します。

via: RigidBodyにAddForce()を行った場合のForceModeによる動作の違い – 強火で進め

モノにあたったときに何かしたい

これはもうむちゃくちゃ使ったイベントです。
ぶつかったときに何か起こるというのがメインのコンテンツなので、ここでコントロールしました。

isTriggerでない場合、つまり動いているオブジェクトの場合のイベントハンドラ

void OnCollisionEnter (Collision col) {}

isTriggerの場合、つまり動いていないオブジェクトの場合のイベントハンドラ

void OnTriggerEnter (Collider col) {}

当たり判定を無効化したい

あるオブジェクトとあるオブジェクトを通過するようにしたい場合、以下のように書くとできます。
ただ、本当にキレイに通過するというよりは、一瞬だけぶつかったような感じになってから通過する気がします。
これは状況によるかもしれないので、要検証。

Physics.IgnoreCollision (gameObject.collider, col.gameObject.collider);

参考リンク

Pushing rigidbody with FPS Controller – Unity Answers

レンダリング

初めハマったのがこのレンダリングの順番です。
UnityではZ軸を奥のほうに配置したからといって、手前のオブジェクト→後ろのオブジェクトとレンダリングされるとは限らないようです。
なので、奥にいるんだけど、手前としてレンダリングされてしまう。

これを回避するために、レンダリングの順番はかなり細かく設定しました。

using UnityEngine;
using System.Collections;

[AddComponentMenu("Custom/Scripts/Render/RenderQueue")]
public class RenderQueue : MonoBehaviour
{
  public int queue = 1;

  // Use this for initialization
  void Start ()
  {
    if (!gameObject.renderer) {
      return;
    }

    gameObject.renderer.material.renderQueue += queue;
  }

  // Update is called once per frame
  void Update ()
  {

  }
}

このプログラムをGameObjectにアタッチして、数値をコントロールしてレンダリングの順番を調整しています。

レンダリングする順番を調整する

子どもたち全部を同じRenderQueueにする場合は、

レンダリングする順番を調整する(子供たちも)

とか使ってます。

CharacterControllerを使ってみた

Unity – Unity Manual

CharacterController(以下CC)のvelocity.magnitudeで運動をしているかが取れたり、isGroundedで地面に着地したのかどうかが取れたり、
結構便利なんですが、CCを使いこなすのはなかなか大変でした。
あと、CC独自の仕様なのか他のオブジェクトとの違いが結構あり、ある程度割り切って使ってみました。

ひじょうにベーシックなCCのUpdateはこんな感じかと思います。
これにいろいろ肉付けしていくと、動きの感じがキャラクターっぽくなります。

void Update ()
{
  // 地面に着いている場合
  if (CC.isGrounded) {
    moveDirection = new Vector3 (1, 0, 0);
    moveDirection = transform.TransformDirection (moveDirection);
    moveDirection *= walkSpeed;
    moveDirection = moveDirection  * 2.0f;

    moveDirection.y = 0.0f;
  } else {
    moveDirection.y -= gravity * Time.smoothDeltaTime;
  }

  // どこの面にぶつかったかの判定
  CollisionFlags flag = CC.Move (moveDirection * Time.smoothDeltaTime);
  if (flag == CollisionFlags.None) {
  } else if ((flag &amp; CollisionFlags.Above) == CollisionFlags.Above) {
  } else if ((flag &amp; CollisionFlags.Sides) == CollisionFlags.Sides) {
  } else if ((flag &amp; CollisionFlags.Below) == CollisionFlags.Below) {
  }

  // 右に移動している
  if (moveDirection.x &gt; 0.0f) {
    transform.localScale = origLocalScale;
  }

  // 左に移動している
  if (moveDirection.x &lt; 0.0f) {
    // テクスチャを反転
    transform.localScale = new Vector3 (-origLocalScale.x, origLocalScale.y, origLocalScale.z);
  }
}

CC同士が衝突するときにZ軸が動いてしまう

今回は2Dベースなので、基本的にZ軸には移動しません。
ただ、CCはSphere Colliderなので、キャラクター同士がぶつかった場合は、ずるっとZ軸側に移動してしまうことがあります。

これに関してはググると、ParentにBox ColliderなGameObjectを用意して、キャラクターがぶつかっても、Z軸方向にずれないようにする、
みたいな記事もあったんですが、ややこしく、変化に対応できなさそうなのでやめました。

結果、

transform.position = new Vector3 (transform.position.x, transform.position.y, 初期Z位置);

をUpdateメソッドの中に書くことにしました。
つまり、Z軸を強制的にある位置に固定する。
これでZ軸はズレなくなり、特にパフォーマンスもFPSも落ちていなかったです。

AddForceしても吹っ飛ばない

RigidbodyFPSWalker – Unify Community Wikiにあるような自作キャラクターを作り、
AddForceするとなんですかね。
要検証。

CC同士の衝突をを検知できない

こちらの記事が参考になりそうです。
これもほんとハマりました。

CharacterController 同士は、何も設定しなくても互いにぶつかり、相手の身体を滑ったりも出来る便利なものですが、その代わり現在の仕様上、ぶつかった相手が CharacterController だった場合、衝突情報を取得することができないようです。
 処理上はちゃんと衝突してても、OnControllerColliderHit には、CharacterController タイプのオブジェクト情報だけ送られてきません(その他のコライダは当然取得出来ます)。

 回避策としては、子オブジェクトに CharacterController 付属のコライダと同じくらいの大きさの CapsuleCollider をアタッチすることが挙げられます。
 この子オブジェクトに、CharacterController を搭載した親オブジェクトと同じ tag や layer を設定しておけば、大抵の用は済みます。
 必要であれば、衝突した子オブジェクトの parent を参照すればよいでしょう。

via: 紫雪PのUnity備忘録 CharacterController 同士の衝突が検知できないんだが?

それでもやはり自前で実装しないといけないんですね。

CCは使えそうですが、少し特殊なので、使うシーンを見極める必要があると感じました。
今回は特に大きな役割をしないキャラクターの実装時に使ってみました。

動画を再生する

ざっくりですが、以下のような感じです。
MovieTextureには動画、AudioClipには動画の音を設定。

public MovieTexture movieTexture;
public AudioClip audioClip;

private AudioSource audioSource;

// Use this for initialization
void Start () {
  // movie
  gameObject.renderer.material.mainTexture = movieTexture;
  movieTexture.Play();

  // sound
  audioSource = gameObject.GetComponent<AudioSource>();
  audioSource.clip = audioClip;
  audioSource.Play();
}

動画をループさせたい場合は、

movieTexture.loop = true;

をつける。

実際にはもっとコード量が多いのですが、この方法はなかなかうまくいきませんでした。
動画に音声もある場合、mov + mp3のようにUnity側で作られるのですが、複数回再生していると変な音が入ることがあったり、
動画が始まる瞬間、真っ黒だったりと違和感がありました。
(mov + mp3だとロードが重いからなんですかね)

なので、動画には音なしにして、movとmp3を完全に分けてUnityで管理していたのですが、今度は映像のループと音のループにずれが生じました。
どうも、このあたりはまだ不安定のようで、自前で映像が(きっと)終わったタイミングなどで音の再再生をInvokeなどで呼び出すとかがよさそうです。
これまた自前InvokeRepeating!

WindowsでMovie Texturesにmp4をアタッチしてもなんかmissingになってしまう

Unity – Unity Manual
を読みながら、QuickTime入れたりしたんですが、Macでmp4をMovie TexturesにアタッチしてWindowsでUnityを起動すると動画ファイルが外れた状態になってしまいました。
なので、Windows上でビルドができなかったんです。

movにしたら外れなかった!
よって、mov採用!

音を再生する

こんなコードを書いて使っています。
if (audioClip && audioSource) { なんてif文があるのは、再現率はかなり低いのですが、ヌルポが発生してしまうケースがあったための回避です。

using UnityEngine;
using System.Collections;

public class EventObjectSound : MonoBehaviour
{
  public AudioClip audioClip;
  private AudioSource audioSource;

  public void PlaySound ()
  {
    audioSource.Stop ();

    if (audioClip &amp;&amp; audioSource) {
      audioSource.Play ();
    }
  }

  public void PlaySoundOneShot ()
  {
    audioSource.Stop ();

    if (audioClip &amp;&amp; audioSource) {
      audioSource.PlayOneShot (audioClip);
    }
  }

  public void StopSound ()
  {
    audioSource.Stop ();
  }

  // Use this for initialization
  void Start ()
  {
    audioSource = gameObject.GetComponent<AudioSource> ();
    audioSource.clip = audioClip;

  }

  // Update is called once per frame
  void Update ()
  {

  }
}

Unityで音を再生する

カメラを動かす

カメラにスクリプトを追加し、Updateの中で以下のようなコードを実行

  float x = Mathf.Sin(Time.time) * 0.02f;
  float y = Mathf.Cos(Time.time) * 0.02f;
  gameObject.transform.position -= new Vector3 (x, y, 0.0f);

カメラもGameObjectなので、他のGameObjectと同じ要領ですね。

GameObjectを透過させる、そしてExtensionMethodsで既存のオブジェクトを拡張する

Unityで透過の処理を探していたら、意外と面倒でしたが以下の方法で可能でした。
material.alpha = 0.1
とかしたいのにー

ExtensionMethodsでMaterialを拡張し、SetAlphaメソッドを追加しています。
こうゆうのすんごく便利!だけど、あとあと分からなくならないように程々にねっ!

using UnityEngine;
using System.Collections;

public static class ExtensionMethods
{
  public static void SetAlpha (this Material material, float value)
  {
    Color color = material.color;
    color.a = value;
    material.color = color;
  }
}

呼び出し方は以下。

gameObject.renderer.material.SetAlpha (value);

Debug.Logをリリース時(exe実行時)に無効にしたい、でも既存のソースはそのままがいい!

ここに解決策が書かれていました。
すばらしい!
Unity3D – Debug.Logを開発環境以外で無効化 – Qiita [キータ]

どうしてもDebug.Logは多用してしまい、本来ならラッパーを作って条件によってLogを出す出さないをすればいいんですが、
なんとなく後回しになったりして、気がついたら結構Debug.Logを埋め込んでしまったという状況はあります。

方法は、

やり方としては、自分で同名の「Debug」クラスを定義する。
UnityのDebugクラスは、UnityEngineというnamespaceにあるので、UnityEngine.Debugとして定義されている。なので、ネームスペースを使わずに同名クラスを定義した場合は、そちらが優先される・・・というC#の仕様を利用する。

via: Unity3D – Debug.Logを開発環境以外で無効化 – Qiita [キータ]

のようで、なかなか面白いですねー

ぼくの場合、Uni2DがDebug.Log以外にも使っていたので、少し足してあげる必要がありました。

#if !UNITY_EDITOR
#define DEBUG_LOG_OVERWRAP
#endif

using UnityEngine;

#if DEBUG_LOG_OVERWRAP
public static class Debug
{
    static public void Break(){
        if( IsEnable() )    UnityEngine.Debug.Break();
    }

    static public void Log( object message ){
        if( IsEnable() ){
            UnityEngine.Debug.Log( message );
        }
    }
    static public void Log( object message, Object context ){
        if( IsEnable() ) {
            UnityEngine.Debug.Log( message, context );
        }
    }

    static public void LogWarning( object message){
        if( IsEnable() ) {
            UnityEngine.Debug.LogWarning( message );
        }
    }

  static public void LogError( object message){
        if( IsEnable() ) {
            UnityEngine.Debug.LogError( message );
        }
    }

  static public void LogError( object message, Object context ){
        if( IsEnable() ) {
            UnityEngine.Debug.LogError( message, context );
        }
    }

  static bool IsEnable(){ return UnityEngine.Debug.isDebugBuild; }
}
#endif

UnityのDebug.Logをラップする via http://qiita.com/rodostw/items/39183e62ed2a1f52f690

マウスポインタを無効にする

タッチが前提の場合マウスカーソルは邪魔です。
なので、以下で非表示に。

Screen.showCursor = false;
Screen.lockCursor = true;

Unity3D の Web Player でマウスカーソルをロックする | Lonely Mobiler

ただし、これをしてしまうと、Developmentでビルドしエラーコンソールが出た場合、タップしても反応しません。
つまり、何行目でエラーが出ているかを見ることができませんでした。

なので、Developmentなときはカーソルを表示するようにして回避しました。

Hierarchyやリソースからオブジェクトを探す

Unity3D – 【Unity】スクリプトからオブジェクトやコンポーネント操作まとめ – Qiita [キータ]

・Hierarchyから探す

GameObject.Find("MyObject");
GameObject.FindWithTag("MyObjectTag");

・Projectのリソースから探す

Resource.Load("hoge");

・子どもたちを探す

Transform[] allChildren = parentObject.GetComponentsInChildren<Transform> ();
foreach (Transform child in allChildren) {
  // ゴニョゴニョ
}

これでだいたい事足りる。

タイマーを使って遅延したり、リピートしたりしたい!

StartCoroutine、WaitForSeconds、そしてyield | ひささん日記の記事でコルーチンを取り上げたんですが、
ぼくは、コルーチンはあまり使わず、Invokeをかなり使いました。

setTimeoutのように使えるので、直感的で使いやすかったです。
さらに、InvokeRepeatingで何度も実行する場合は、これが使えます。
止めたければ、CancelInvokeを実行すればOK。

// InvokeRepeating("関数名",初回呼出までの遅延秒数,次回呼出までの遅延秒数)
InvokeRepeating("Logging", 10, 10);
void Logging ()
{
  Debug.Log("1");
  Invoke("Logging2", 2);
}

void Logging2 ()
{
  Debug.Log("2");
}

オブジェクトが数秒間だけ生存するスクリプトを書く

この方法、Materialの削除が行われていないので、結局使いませんでした。
Materialのリーク怖い!

//ゲームオブジェクトを5秒後にDestroy
Destroy(gameObject, 5);

GameObjectをコードから破棄する

Monobehaviourを継承していないクラス内で破棄する場合

GameObject.DestroyObject (material);
GameObject.DestroyObject (gameObject);

Monobehaviourを継承しているクラス内で破棄する場合

Destroy (material);
Destroy (gameObject);

上記コードで注目したいのがmaterialを破棄していることです。
これをしないとmaterial数はどんどん上昇しメモリリークを引き起こす。
GameObjectを破棄したからといって、materialは自動で破棄されてはくれない
これはすごくハマった。

子オブジェクトを持っているGameObjectの場合は子もちゃんと破棄すること。
もちろんmaterialもねっ!

private void DestroyObject ()
{
  foreach ( Transform t in transform ){
    Destroy( t.gameObject.renderer.material );
    Destroy( t.gameObject );
  }
  Destroy(gameObject.renderer.material);
  Destroy(gameObject);
}

ScreenCaptureする

JPEG、PNGで保存する方法を以下のリンクにまとめておきました。

Unity/ScreenCapture at master · hisasann/Unity

また、Application.CaptureScreenshotを使ってキャプチャするとUIが一瞬ロックされてしまいました。
画面の状態を一定時間でScreenCaptureし、Facebookに上げるなどの場合にこのロックがかなりネックになるので、以下のコードで対応できるかもしれません。

さらに、デュアルディスプレイでUnityをEditorモードで実行していると、片面しかキャプチャできなかったので、
次の項目で説明しているpopupwindow引数を付けてexeを実行しScreenCaptureするのがよいでしょう。

Unityのプログラムをサブディスプレイをまたいで表示する方法

exeの上でShift押しながらクリックし、WindowedモードでいったんUnityを起動する。
一度Unityを閉じる。
その後popupwindowの引数を付けて、コマンドラインから起動するとフルスクリーンになる。
どうもこれはWindows限定のパラメータのようですが、なので、MacMiniとかでフルスクリーンにするのどうするんだろう、、、

hoge.exe -popupwindow -screen-width 3840 -screen-height 1080

フルHDを2枚使った場合の引数の付け方。

Unity – Unity Manual

条件付きコンパイル

環境ごとに処理を変える場合

こんな感じでかなり細かく指定できるようです。

UNITY_EDITOR Define for calling Unity Editor scripts from your game code.
UNITY_STANDALONE_OSX Platform define for compiling/executing code specifically for Mac OS (This includes Universal, PPC and Intel architectures).
UNITY_DASHBOARD_WIDGET Platform define when creating code for Mac OS dashboard widgets.
UNITY_STANDALONE_WIN Use this when you want to compile/execute code for Windows stand alone applications.
UNITY_STANDALONE_LINUX Use this when you want to compile/execute code for Linux stand alone applications.
UNITY_STANDALONE Use this to compile/execute code for any standalone platform (Mac, Windows or Linux).
UNITY_WEBPLAYER Platform define for web player content (this includes Windows and Mac Web player executables).
UNITY_WII Platform define for compiling/executing code for the Wii console.
UNITY_IPHONE Platform define for compiling/executing code for the iPhone platform.
UNITY_ANDROID Platform define for the Android platform.
UNITY_PS3 Platform define for running PlayStation 3 code.
UNITY_XBOX360 Platform define for executing Xbox 360 code.
UNITY_NACL Platform define when compiling code for Google native client (this will be set additionally to UNITY_WEBPLAYER).
UNITY_FLASH Platform define when compiling code for Adobe Flash.
UNITY_BLACKBERRY Platform define for a Blackberry10 device.
UNITY_WP8 Platform define for Windows Phone 8.
UNITY_METRO Platform define for Windows Store Apps (additionally NETFX_CORE is defined when compiling C# files against .NET Core).
UNITY_WINRT Equivalent to UNITY_WP8 | UNITY_METRO

via: Unity – Platform Dependent Compilation

[via]
Unity – Platform Dependent Compilation

使い方はこんな感じ。

#if UNITY_STANDALONE_OSX
  Debug.Log("Stand Alone OSX");
#endif

#if UNITY_STANDALONE_WIN
  Debug.Log("Stand Alone Windows");
#endif

OSごとに処理を変える場合

void Start () {
  if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer) {
    Debug.Log("WindowsPlayer");
  } else if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) {
    Debug.Log("OSXPlayer");
  }
}

gitでpushしてpullするといろいろおかしくなる場合

Project Settings → Editor
Version Control ModeをMeta Files
Asset Serialization ModeをForce Text

にすると幸せになれます。

(Unityは画像や動画などファイルサイズが大きいものを使うので、gitにpushしまくっているとcloneが激重になるんですが、ここはしょうがないのかな。
やはり、
Unity – Asset Server Guide
このあたりを使って管理するべきなのかな。)

解像度を指定する

Project Settings → Edit
Project Settings → Player

から

Default Is Native Resolutionのチェックを外すとWidthとHeightが出てくるので入力するすると、GameビューのStandaloneから選べるようになる。

Textureがどうもジャギるなーという場合

TextureのAniso Levelを最大にしてみた。
(そこまでパフォーマンスは落ちないらしい)

TrailRendererはtimeで指定してもGameObjectが破棄されない場合がある

どうもUnityのバグっぽくて、TrailRenderer自体は画面上から消えるのに、GameObject数が下がらない現象がありました。
なので、ポーリングして、すでに消えているはずのGameObjectがいる場合、破棄するように自前でコードを書きました。

それでも、TrailRendererは見た目もよくかっこよく演出できるので使いたいところですねー

タッチしたところにあるオブジェクトを判別して特定のスクリプトを呼び出すパターン

UnityではC#のInterfaceが使えるようなんですが、Editor上で作ることができなくて、
かつ、いろんな記事を読んでいるとうまくいかないケースがあるかもみたいなことが書かれていたので、
使うのをやめました。

そこで、タッチした場所に存在しているオブジェクトをRayを使って特定し、且つ、そのオブジェクトのメソッドを呼び出し、
個々の動作をさせたいということを考えて以下のようになりました。

つまり、if文をあまり使いたくなかったので、SendMessageを使ってそれっぽく実現してみました。

Main.cs

Rayを使って、対象のオブジェクトを取得するコード。

RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay (new Vector3 (pEvent.Position.X, screenHeight - pEvent.Position.Y, 0));

// Ray
if (Physics.Raycast (ray, out hit, Mathf.Infinity)) {
  // hit object
  GameObject obj = hit.transform.gameObject;

  new TouchObject (hit, obj, x, y);
}

TouchObject.cs

targetにRayで取得したオブジェクトが入ってくる。

TouchObjectProvider provider = target.GetComponent&lt;TouchObjectProvider&gt; ();
if (provider != null) {
  provider.EventFire ();
}

TouchObjectProvider.cs

SendMessageを使って対象のオブジェクトが持つ、特定のメソッドをリフレクション呼び出し。

using UnityEngine;
using System.Collections;

[AddComponentMenu("Custom/Scripts/TouchObject/TouchObjectProvider")]
public class TouchObjectProvider : MonoBehaviour
{
  public void EventFire ()
  {
    SendMessage ("SendEventFire");
  }

  // Use this for initialization
  void Start ()
  {

  }

  // Update is called once per frame
  void Update ()
  {

  }
}

TouchObjectEvent.cs

このクラスにあるSendEventFireメソッドが重要。
このメソッドがSendMessageによってFireされる。

なので、クラス名は対象オブジェクトを表す名前とかにしておけば、管理がしやすい。

using UnityEngine;
using System.Collections;

public class TouchObjectEvent : MonoBehaviour
{
  public void SendEventFire ()
  {
    // ここにイベントの処理
  }

  // Use this for initialization
  void Start ()
  {
  }

  // Update is called once per frame
  void Update ()
  {
  }
}

MonoDevelop

キーボードショートカット

よく使うのは以下でした。

Rename

Command + R

定義場所にジャンプ

Command + Y

以下のリンク先にショートカットがまとまっています。

Monodevelop keyboard shortcuts | Kureus

文字化けを直す

/Applications/Unity/MonoDevelop.appを右クリックし、パッケージの内容を表示する。

「Contents/Frameworks/Mono.framework/Versions/Current/etc/gtk-2.0/gtkrc」ファイルをvimなどで開き、

font = “Lucida Grande 14″

の一行下に、以下を追加する。

font_name =”Hiragino Kaku Gothic Pro 10″

あとはPreferenceから画像のように設定すれば、再起動後に文字化けは直ってる。

ただし、コメント入力は相変わらず日本語が打てない。
(他のエディターや、ペーストで日本語を貼り付けることができるが、どうも特定の日本語の場合にビルドに失敗するというのを
他の記事で読んだ記憶があるので、まだあまり日本語コメントはしないほうがいいのかな)

Windows側の設定

Windows8のタッチ時の視覚効果を無効にする

コントロールパネル→ハードウェアとサウンド→ペンとタッチ
タッチタブで、スクリーンをタッチしたときに視覚的フィードバックを表示するのチェックを外す。

まとめ

Unityは触れば触るほどよく出来ていて、さくっと実験してみたり気軽に遊べるのが本当に楽しい。

Unitychanのデータ早く欲しい!
UNITY-CHAN!

Unity4入門   最新開発環境による簡単3Dゲーム製作
浅野 祐一 荒川 巧也 森 信虎
ソフトバンククリエイティブ
売り上げランキング: 4,144

StartCoroutine、WaitForSeconds、そしてyield

MonoBehaviourの機能として提供されているStartCoroutineをちょっとだけ触ったので忘れないようにメモメモ。

logの1は即座に表示され、
logの2は3秒待ったあとに表示され、
logのisFlgは5秒待ったあとに表示される。

ここで、logの2とlogのisFlgのwaitTimeがもし両方とも3秒の場合は、一気に表示される。
つまりその前のコルーチンのwaitをその下に書いたコルーチンは引きづられない。

サンプルコード

using UnityEngine;
using System;
using System.Collections;

public class Coroutine : MonoBehaviour {
  // Use this for initialization
  void Start () {
    Debug.Log(1);

    StartCoroutine(wait());

    StartCoroutine(waitForCallback(5f, (bool isFlg) => {
      Debug.Log(isFlg);
    }));
  }

  // Update is called once per frame
  void Update () {
  }

  IEnumerator wait() {
    yield return new WaitForSeconds(3f);
    Debug.Log(2);
  }

  IEnumerator waitForCallback(float waitTime, Action<bool> callback) {
    yield return new WaitForSeconds(waitTime);
    callback(true);
  }
}

なかなか面白いね。
これよく見るサンプルではStartメソッドの中に書かれているけど、Updateメソッドの中で書く場合は、
フラグとかもたして、間引く?必要があるのかな。
じゃないと、フレームごとにUpdateメソッドが呼ばれて、キューにコルーチンが溜まりまくってしまいそう。
そもそもそんなタイミングで使わないのかな。

こうゆうの作っておけば便利なのかな

using UnityEngine;
using System;
using System.Collections;

public class Callback {
  public static IEnumerator waitForCallback(float waitTime, Action callback) {
    yield return new WaitForSeconds(waitTime);
    callback();
  }
}

gist

Callback.cs

参考リンク

Unity4入門   最新開発環境による簡単3Dゲーム製作
浅野 祐一 荒川 巧也 森 信虎
ソフトバンククリエイティブ
売り上げランキング: 4,554

スマホサイト作っててハマったことメモ(2013/06/13時点)

window.scrollTo(0, 1);でアドレスバーが隠れない

以下のようにJavaScriptからスクロール位置をY軸方向に1px動かすことで、アドレスバーを引っ込めることができますが、
これが動くことは動くんですが、すぐに隠れずに、4〜5秒ぐらいしてからヒョコッと隠れる。

自分で作った他のサイトでは、すぐに引っ込むのになんでだろう、、、と

(function () {
  $(window).bind("load", function (e) {
    setTimeout(doScroll, 100);
  });

  $(window).bind("orientationchange", function (e) {
    // e.orientation === "portrait" ? "縦" : "横"
    setTimeout(doScroll, 100);
  });

  function doScroll() {
    if (window.pageYOffset === 0) {
      window.scrollTo(0, 1);
    }
  }

  window.doScroll = doScroll;
})();

そもそもjQueryMobileを使っていると内部的に↑のようなことはしてくれるので、自分で実装する必要はないのですが、
jQueryMobileを使ってもアドレスバーが引っ込むタイミングが遅い!

早く引っ込むサイトとそうでないサイトを見比べると、Mobile Safariのアドレスのところに「リーダー」というのが出てる。
(あやしい、、、)

そこで、このリーダーを消す作業をしました。

  1. JavaScript疑う
  2. CSSを疑う
  3. 心が折れる
  4. htmlを削り始める

この最後でbodyの中身をスッカラカンにすると、リーダーがなくなり、最終的にarticleタグが全体を囲っているとどうもリーダーが出ちゃう可能性が高い
という結論に到達しました。
とりあえずdivにしたんですが、それでもダメな場合があるらしく、そのあたりは以下の記事がわかり易かったです。

リーダーのせいでmobile safariのアドレスバー隠しが遅れる – < /gecko >

jQueryMobileでTransitionが効かない場合がある

jQueryMobileの特徴として、次の画面をAjaxで取得しDomに追加し、アニメーションしながら遷移できる。
というのがありますが、ここでの醍醐味は、アニメーションで遷移できる点かなーと思っています。

このアニメーション効果、つまりTransitionがつかずに遷移する場合があったので、ここにメモ。

Transitionがつかないと、Ajax遷移には間違いないんですが、一瞬で次の画面に行くので、
普通に画面遷移しちゃったのかな?と思うぐらいよくわからない。

そしてこれが起きる原因は、サイトの全体の高さがある一定以上を超える場合に起きる可能性が高い。

以下にjQueryMobileのコードから抜粋してみました。

Line: 3652

Math.max( $.mobile.window.scrollTop(), toScroll ) &gt; $.mobile.getMaxScrollForTransition()

Line: 3771

defaultGetMaxScrollForTransition = function() {
  return $.mobile.getScreenHeight() * 3;
};

Line: 3797

// Set the getMaxScrollForTransition to default if no implementation was set by user
$.mobile.getMaxScrollForTransition = $.mobile.getMaxScrollForTransition || defaultGetMaxScrollForTransition;

$.mobile.getScreenHeight() * 3;ここの部分が該当の箇所だと思うのですが、
スクロール位置が、画面のScreenの高さを3倍したものを超えた状態でAjax遷移をするとTransitionが効かないという感じです。

実際にconsole.logで出力しながらやってみたら、まさしくそのとおりでした。

ここは割り切るか、画面の高さを縮めるか、という感じですかね。
個人的にはこの配慮はパフォーマンスを意識して書かれていると思うので、Transitionが効かないときがあってもいいのかなーと思っています。
普通の画面遷移よりはどのみち速いので。

jQueryMobileのローディングにアニメーションgifが表示されない

CoreなCSSだけあればよかったので、「jquery.mobile.structure-1.3.1.css」を読み込んでいたのですが、
どうもアニメーションgifの部分のCSSクラスが定義されていなく、ローディングが一瞬出るのですが中身のグルグルがない状態でした。

なので、本体のCSSから以下をコピーしてきました。

/* loading icon */
.ui-icon-loading {
  background: url(images/ajax-loader.gif);
  background-size: 46px 46px;
}

これで動いたけど、みんなどうしているのかな?

jQueryMobileとanimate.cssの相性が悪い

animate.cssに書かれている以下の内容がどうもダメっぽく、Transitionでアニメーションしたあとに少しチラつきがでちゃいます。

body { /* Addresses a small issue in webkit: http://bit.ly/NEdoDq */
  -webkit-backface-visibility: hidden;
}

なので、animate.cssはインクルードせずにSassを使って以下のように組み込みました。

@include keyframes(bounceIn) {
  0% {
    @include transform(scale(.3));
    @include opacity(0);
  }
  60% {
    @include transform(scale(1.05));
    @include opacity(1);
  }
  80% {
    @include transform(scale(.9));
  }
  100% {
    @include transform(scale(1));
  }
}

Sass楽ちん!

iOSアプリからカレンダーに予定を追加する方法

メモメモ。

#import <EventKit/EventKit.h>
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    EKEventStore *eventStore = [[EKEventStore alloc] init];
    // weakつけるとブロックの中で参照がなくなってたので、ダメみたい。でもselfは大丈夫
    // でもつけないとリークしちゃうよね?
//    __weak EKEventStore *__eventStore = eventStore;
    __weak ViewController *__self = self;
    [eventStore requestAccessToEntityType:EKEntityTypeEvent
      completion:^(BOOL granted, NSError *error) {
          // なので、ブロックの中で再生成してみた
          EKEventStore *__eventStore = [[EKEventStore alloc] init];

          NSLog(@"__eventStore = %@", __eventStore.description);
          NSLog(@"__self = %@", __self.description);

          // カレンダーへのアクセスが許可された場合
          // 一度拒否を選んでしまうと二度とアクセスできないっぽい
          // ユーザーに設定からリセットしてもらう必要がある
          // via http://d.hatena.ne.jp/appbakery/20130327/p2
          // via http://program.station.ez-net.jp/special/handbook/objective-c/iphone/grant/reset.asp
          if (granted == NO) {
              // ここはmainキューでないので、UIの操作はdispatch_get_main_queueを使ってmainキューの中でやる必要がある
              // via http://d.hatena.ne.jp/zentoo/20121028/1351443185
              dispatch_async(dispatch_get_main_queue(), ^{
                  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"お知らせ" message:@"カレンダー操作の許可をくださいちょんまげ"
                                        delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
                  [alert show];
              });
              return;
          }

          //アクセス許可後の処理はここで
          EKEvent *event = [EKEvent eventWithEventStore:__eventStore];
          event.title = @"ひささんピースDay";
          event.timeZone = [NSTimeZone defaultTimeZone];
          event.notes = @"ひささんを楽しませる会";
          event.startDate = [[NSDate alloc] init];
          event.endDate = [[NSDate alloc] initWithTimeInterval:6000 sinceDate:event.startDate];
          event.calendar = __eventStore.defaultCalendarForNewEvents;
          [event setCalendar:[__eventStore defaultCalendarForNewEvents]];

          NSError *err;
          BOOL result = [__eventStore saveEvent:event span:EKSpanThisEvent error:&err];
          NSString *message = nil;
          if (result) {
              message = [NSString stringWithFormat:@"イベントの保存が完了しました。"];
          } else {
              message = [NSString stringWithFormat:@"イベントの保存ができませんでした。 : %@", error];
          }
          __weak NSString *__message = message;
          dispatch_async(dispatch_get_main_queue(), ^{
              UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:__message
                                    delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
              [alert show];
          });
      }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

iOSのWebViewで外部リンクをネイティブブラウザで開く場合の注意点

以下のメソッド内で外部リンクかどうか判断し、外部リンクの場合はネイティブブラウザを開くようにできるのですが、iOSのWebViewはややこしいことにiframeのリクエスト時もここに入ってきてしまいます。

  - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:  (NSURLRequest *)request navigationType:  (UIWebViewNavigationType)navigationType {-

これだと計測用のiframeのリクエストもこのメソッド内に入るので、外部URLだった場合にネイティブブラウザで開くという挙動を入れると、このiframeのリクエスト時にも反応してしまいます。

そこで方法としては、クリックした場合にのみ外部リンクかどうかを判断するようにするという方法があります。

シンプルなサンプルですが、以下のような雰囲気。

  NSString *absoluteUrl = request.URL.absoluteString;

  // 外部リンクをクリックした場合には、デバイスのデフォルトブラウザを起動する
  NSRange range = [absoluteUrl rangeOfString:@'domain'];
  if (range.location == NSNotFound) {
      if (navigationType == UIWebViewNavigationTypeLinkClicked) {
          [[UIApplication sharedApplication] openURL:request.URL];
          return NO;
      }
  }

そして、WebView側のコードに以下のようにクリックイベントをfireするコードで置き換えます。
もしもアンカーリンクなどをクリックする場合は以下のコードは不要ですが、何かしらJavaScriptでURLを作ってlocation.hrefに入れている場合には置き換えが必要になります。

  $("#hoge")[0].dispatchEvent(click());

  function click(){
    var event = document.createEvent("HTMLEvents");
    event.initEvent('click', true, false);
    return event;
  };

AndroidのWebViewはiframeなどはshouldOverrideUrlLoadingメソッドの中に入ってこないので、楽なのですが、
iOSは結構ややこしいですね。

iPhoneアプリの申請方法、iTunesConnectからXCodeへ(2013/03/18時点)

だいたいの手順はアプリ申請まとめ(2012/3現在)|iPhoneアプリ開発日誌 -ZERO-が参考になりました。
また、iTunesConnectでの作業はiTunes ConnectでiPhone/iPadアプリの新規登録を行う手順(2013年1月現在)が良い感じ。
ただ、やはりiTunesConnectでの入力項目はしょっちゅう変わるようなので、以下に2013/03/18時点の内容を書いておきます。

iTunesConnect

iTunesConnectでアプリ情報を入力する。

iTunes Connect

アプリケーション情報の入力

Default Language
App Name
SKU Number(UTF-8の英数字で表されたアプリケーションの一意の識別子)
Bundle ID(AppId)

地域、価格、公開の設定

Availability Date
公開日は先にしておかないとリリース日順検索で上位に表示されないらしい。
リジェクトされた場合に、公開日がすぎると過去日になってしまうので新着で引っかからない可能性がある。
なので、少し先にしておいて公開後に調整すればよいみたい。
[via]
iPhoneアプリ申請時に知っておくと良いこと – されど空の青さを知る
【開発】利用開始日(Availability Date)の設定は戦略的に!申請したiPhoneアプリ「e-スペル」がReady for Sale(販売中)に – creativi.tea

Price Tier
Discount for Educational Institutions
Custom B2B App
App Store Worldwide

バージョン情報の入力

Version Number
Copyright
Primary and Secondary Category(カテゴリは選んだらあとで修正できないと聞いたことがある)
Subcategory
Rating

メタデータ

Description
Keywords
Support URL(ティザーサイトでなくてもよい)
Marketing URL(非必須)
Privacy Policy URL(非必須)

App Review Information

レビュー後のAppleからの連絡用。

First Name
Last Name
Email Address
Phone Number
Review Notes(非必須)

画像のアップロード

Large App Icon
1024 x 1024

3.5-Inch Retina Display Screenshots(iPhone4)
960×640, 960×600, 640×960 or 640×920
3枚設定した

4-Inch Retina Display Screenshots(iPhone5)
1136×640, 1136×600, 640×1136 or 640×1096
3枚設定した

iPad Screenshots
1024×768, 1024×748, 768×1024, 768×1004, 2048×1536, 2048×1496, 1536×2048 or 1536×2008
3枚設定した

Routing App Coverage File(非必須)

Done!!

iTunesConnectでView Detailをクリック。

Ready to Upload Binaryをクリック。

暗号化についての回答

ここで、ややこしい英語が出てきた。
ググりながら回答した。

「Is your product designed to use cryptography or does it contain or incorporate cryptography?」

httpsを使っている場合はYESを選択。

「Does your product qualify for any of the exemptions provided in Category 5 part 2?

You are responsible for the proper classification of your product; make certain that it meets the criteria of the exemption (listed here). Otherwise you may be in violation of the US export laws and could be subjected to penalties including delisting of your app from App Store. Please go through the FAQ page thoroughly before attempting to answer the question.

You can answer “YES” to question #2, if the encryption in your app is: (a) is specially designed for medical end-use; (b) is limited to intellectual property or copyright protection; (c) is limited to authentication, digital signature or the decryption of data or files; (d) is specially designed and limited for banking use or ‘money transactions’; (e) is limited to “fixed” data compression or coding techniques; or (f) if your app meets the descriptions provided in Note 4 to Category 5 Part 2.

Please visit the FAQ for additional guidance on the exemptions.」

YESを選択。

[via]
HTTPS を使ってるアプリを AppStore や Android Market で配信するときの輸出手続きについて(その2) – 規制対象になるかどうかの判断 – むらかみの雑記帳

Saveをクリック。

アプリのStatusがWaiting For Uplaodになっているのを確認する。

XCodeからアプリをアップロードする

Archiveでビルドする
Organizerが立ち上がるので、Distributeをクリック。

「Submit to the iOS App Store」をチェック。

iTunesConnectでログインしたアカウントを入力する。

アプリのDistribution用のプロビジョニングプロファイルを選択する。
(注意:ここでAdHoc用のプロビジョニングプロファイルも選べるので間違えないように、というかAppStoreへの申請なのになんでAdHocのほうが出てくるんだ!)

Nextをクリック。

15分ほど掛かる。

No issues were found in “アプリ名”がでたらOK!

Finishをクリック。

オーガナイザ画面のアプリのStatusが、Submittedになっていることを確認します。

iTunesConnectを見てみると「Waiting For Upload」から「Waiting For Review」に変わります。

やったーーーー!

おつかれさまでした!

.dSYMファイルを控えておく

あとあとクラッシュログを読めるようにしておくため、
「~/Library/Developer/Xcode/DerivedData」の下の該当のアプリディレクトリで、
.dSYMファイルを検索し、リリースビルドしたファイルをバックアップしておく。

via iOSデバイスのクラッシュログを読むには – Awaresoft

余談 – お客さんのiTunesConnectアカウントにログインして申請を代行する方法

iPhoneアプリの申請を自分のAppleアカウントからやる場合は、ごくごく普通にiTunesConnectから行えばいいのですが、
お客さんのiOS Developer Centerに登録されたアカウントからやる場合はちょっとややこしい。

まず、iOS Developer Programに登録されていないAppleアカウントを作る必要がある。
つまり普通にiTunesを使う用途としてAppleアカウントを作成して、そのアカウントをお客さんのiTunesConnectにAddしてもらう。
これで、お客さんのiTunesConnectにログインできて申請作業が行える。

こんな方法があるなんて初めて知りました!

よくわかるiPhoneアプリ開発の教科書【iOS 5&Xcode 4.2対応版】
森 巧尚
マイナビ
売り上げランキング: 7,831