「Unity」と一致するもの

[Unity] Android App Bundle(AAB)どこで設定するの?

  • 投稿日:
  • by
  • カテゴリ:

APKではなくAABにしたいのだけど、Unity 2018.4のBuild Settingsを見ても
あるはずのBuild App Bundle (Google Play) の項目が見当たらない。

検索してもわからず時間を取られてしまったけど結論としては、
一つ上の項目であるBuild Systemを「Gradle」にすると出てきた!
Internalだと表示されない。

[Unity] Android App Bundle(AAB)どこで設定するの?

  • 投稿日:
  • by
  • カテゴリ:

APKではなくAABにしたいのだけど、Unity 2018.4のBuild Settingsを見ても
あるはずのBuild App Bundle (Google Play) の項目が見当たらない。

検索してもわからず時間を取られてしまったけど結論としては、
一つ上の項目であるBuild Systemを「Gradle」にすると出てきた!
Internalだと表示されない。

Visual Effect Graphのカスタムアトリビュート

  • 投稿日:
  • by
  • カテゴリ:

Unity VFX Graphのサンプルを見ていると
検索しても出てこないアトリビュートブロック("Base Position"とか"Offset 1" とか)を使っている事があり
いったいどうやって使うのか調べた結果、
下記のチェックボックスをONにすると使えるようになるという事だった。
Preferences -> Visual Effects > Experimental Operators/Blocks

ONにすると Set Custom AttributeブロックとGet Custom Attributeノードを作成できるようになる。
作成するとデフォルトはfloatだが、ブロックを選択してInspectorから型を変えたり、ランダムにしたり、Attribute名を変えたりできる。

PlayFabのログインユーザーの識別IDとして、SystemInfo.deviceUniqueIdentifierを使用している状況で。
アプリのアンインストールなどせずに、ただAppStoreでアップデートしただけなのにIDが変化して新規ユーザー扱いになってしまう事象が2件発生した。
ユーザー端末は、いずれもiPhone8で iOS 11.3 と iOS 12.2 だった。
iOSのバグなのか不明だが非常に困る。

ひとまず対策として
初回のみ SystemInfo.deviceUniqueIdentifierを取得してPlayerPrefsに保存し、
2回目以降はPlayerPrefsに保存しておいた値を使うことにした。
アンインストールしない限りPlayerPrefsは残っているはずなのでたぶん大丈夫だと思う。

対策としてKeychainにIDを保存することにした。
Keychainに保存したデータは、たとえアンインストールしても残るのでPlayerPrefsより安心。
有償アセットiOS Keychain Pluginを使ったが、ネイティブ部分はunity-ios-keychain-pluginと同じだったので買わなくても良かったかも。
有償の方は、iOS以外の場合にpersistentDataPathに暗号化したファイルとして保存するようになっていた。

PlayFabのローカライズ

  • 投稿日:
  • by

言語ごとに表示内容を変える方法について。
ニュース、メール、プッシュ通知については管理画面で各言語ごとに文章を登録できるようになっている。
これらはプレイヤーのLanguage設定に従って自動で表示文章が選ばれる。
プレイヤーのLanguageが未設定だったり、一致する言語がない場合はデフォルト言語で表示される。
デフォルト言語は管理画面のSettings>Default languageで設定する。

クライアントからプレイヤーの言語を登録する方法は下記のような感じで、Profile.VersionNumberが必要になるので先に取得してから、SetProfileLanguage APIを呼ぶ。


//言語を登録する
    public static void SetLanguage(string language)
    {
        //先にプロフィールのバージョン番号を取得する
        GetProfile((version) => {
            //取得したバージョン番号を用いて言語登録
            SetProfileLanguage(language, version,(success)=> {
                if(success){
                    Debug.Log("登録完了");
                }else{
                    Debug.Log("登録失敗");
                }
            });
        });
    }
//プロフィールのバージョン番号を取得する
    private static void GetProfile(System.Action callback) {
        PlayFabProfilesAPI.GetProfile(new GetEntityProfileRequest{}, result => {
            callback.Invoke(result.Profile.VersionNumber);
        },error => {});
    }
//言語登録APIを実行する
    private static void SetProfileLanguage(string language,int profileVersion, System.Action callback)
    {
        var request = new SetProfileLanguageRequest
        {
            Language = language,
            ExpectedVersion = profileVersion
        };
        PlayFabProfilesAPI.SetProfileLanguage(request, res =>
        {
            callback.Invoke(true);
        }, error=> {
            callback.Invoke(false);
        });
    }

Unityで取得できるOSの言語(Application.systemLanguage)をPlayFabの言語コードに変換するには下記のようになると思う。 (PlayFabのドキュメントに言語コード一覧が見当たらなかったので、管理画面のプルダウンで使用されている言語コードを使用)
※Application.systemLanguageで取得できる言語数より、PlayFabで設定できる言語数のほうが多いが割愛
※ノルウェー語やセルビアクロアチア語は、PlayFabでは2種類に分かれるが独断で選んだ。


    public static string GetLanguageCode()
    {
        switch (Application.systemLanguage)
        {
            case SystemLanguage.Afrikaans: return "af";//アフリカ語
            case SystemLanguage.Arabic: return "ar";//アラビア語
            case SystemLanguage.Basque: return "eu";//バスク語
            case SystemLanguage.Belarusian: return "be";//ベラルーシ語
            case SystemLanguage.Bulgarian: return "bg";//ブルガリア語
            case SystemLanguage.Catalan: return "ca";//カタロニア語
            case SystemLanguage.Chinese: return "zh-Hans";//中国語
            case SystemLanguage.ChineseSimplified: return "zh-Hans";//中国語簡体字(simplified)
            case SystemLanguage.ChineseTraditional: return "zh-Hant";//中国語繁体字(traditional)
            case SystemLanguage.Czech: return "cs";//チェコ語
            case SystemLanguage.Danish: return "da";//デンマーク語
            case SystemLanguage.Dutch: return "nl";//オランダ語
            case SystemLanguage.English: return "en";//英語
            case SystemLanguage.Estonian: return "et";//エストニア語
            case SystemLanguage.Faroese: return "fo";//フェロー語
            case SystemLanguage.Finnish: return "fi";//フィンランド語
            case SystemLanguage.French: return "fr";//フランス語
            case SystemLanguage.German: return "de";//ドイツ語
            case SystemLanguage.Greek: return "el";//ギリシャ語
            case SystemLanguage.Hebrew: return "he";//ヘブライ語
            case SystemLanguage.Hungarian: return "hu";//ハンガリー語
            case SystemLanguage.Icelandic: return "is";//アイスランド語
            case SystemLanguage.Indonesian: return "id";//インドネシア語
            case SystemLanguage.Italian: return "it";//イタリア語
            case SystemLanguage.Japanese: return "ja";//日本語
            case SystemLanguage.Korean: return "ko";//韓国語
            case SystemLanguage.Latvian: return "lv";//ラトビア語
            case SystemLanguage.Lithuanian: return "lt";//リトアニア語
            case SystemLanguage.Norwegian: return "nb";//ノルウェー語(ブークモール)
            case SystemLanguage.Polish: return "pl";//ポーランド語
            case SystemLanguage.Portuguese: return "pt";//ポルトガル語
            case SystemLanguage.Romanian: return "ro";//ルーマニア語
            case SystemLanguage.Russian: return "ru";//ロシア語
            case SystemLanguage.SerboCroatian: return "sr-Latn";//セルビアクロアチア語 (Serbian (Latin))
            case SystemLanguage.Slovak: return "sk";//スロバキア語
            case SystemLanguage.Slovenian: return "sl";//スロベニア語
            case SystemLanguage.Spanish: return "es";//スペイン語
            case SystemLanguage.Swedish: return "sv";//スウェーデン語
            case SystemLanguage.Thai: return "th";//タイ語
            case SystemLanguage.Turkish: return "tr";//トルコ語
            case SystemLanguage.Ukrainian: return "uk";//ウクライナ語
            case SystemLanguage.Vietnamese: return "vi";//ベトナム語
            default: return "en";
        }
    }

インベントリアイテム名などはローカライズシステムが整っていないので、CustomDataにjson形式で文言を入れておいて自前で選ぶ。

[Unity ] 透過動画(WebM VP8)を作ったときのメモ

  • 投稿日:
  • by
  • カテゴリ:

Unityでサポートしているアルファチャンネル付き動画の形式はWebM(動画コーデックはVP8、音声コーデックはVorbis)。

FFmpegを使って、PNG連番画像をWebMにするなら下記で良い。

ffmpeg -r 30 -i frames\%04d.png -auto-alt-ref 0 -c:v libvpx -b:v 5M output.webm

今回、音声コーデックがOpus形式のWebMファイルがあり、そのままではUnityで読み込めないためVorbis形式に変換する必要があった。
一発でいけるかもしれないけど、うまくできなかったので下記のような手順を踏んだ。
1)サウンドだけ抜き出す

ffmpeg -i original.webm -acodec libmp3lame -aq 4 sound.mp3

2)映像をいったん連番PNGに書き出す

ffmpeg -vcodec libvpx -i original.webm frames\%04d.png

3)連番PNGをWebMファイルに変換(その際フレームレートを60から30に変換、解像度を640x360に変更、ビットレートは20000kb/s)

ffmpeg -framerate 60 -i frames3\%04d.png -vf fps=30 -s 640x360 -auto-alt-ref 0 -c:v libvpx -pix_fmt yuva420p -metadata:s:v:0 alpha_mode="1" -b:v 20000k -r 30 noSound.webm

4)無音のWebMにサウンド(Vorbisコーデック)をつける

ffmpeg -i noSound.webm -i sound.mp3 -c:v copy -c:a libvorbis -map 0:v:0 -map 1:a:0 final.webm


なお、Androidで再生する場合は
Unityの映像ファイルのImport SettingsでTranscodeをオンにしてCodecをVP8にする必要がある。

映像をPlaneとかに貼り付けて透過表示するにはマテリアルのシェーダーをUnlit/Transparentにすれば良い。

アドフリくんのUnity動画広告SDK Ver.2.20.1から
下記のAndroidパーミッション要求が自動で追加されるようになった。

・SDカードのコンテンツの読み取り(SmaAdで使用)
・SDカードのコンテンツを修正/削除する(SmaAdで使用)
・Wi-Fiからの接続と切断(Tapjoyで使用)

これらのパーミッションを取り除くためには、
/Assets/Plugins/Android/AndroidManifest.xmlに下記を追記する必要があるとの事。

・SDカードのコンテンツの読み取り
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove"/>
・SDカードのコンテンツを修正/削除する
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove"/>
・Wi-Fiからの接続と切断
 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" tools:node="remove"/>
 <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" tools:node="remove"/>

Unity + PlayFabで課金処理

  • 投稿日:
  • by
  • カテゴリ:

App Store,Google Play Storeの課金処理はUnity IAPで行って、レシートチェックとアイテム付与をPlayFabAPIで行う。
以下、要所だけ載せます。


//////////////////////////
// Unityの課金システム構築
StandardPurchasingModule module = StandardPurchasingModule.Instance();
ConfigurationBuilder builder = ConfigurationBuilder.Instance(module);
//商品ID登録(ストアに登録したIDとPlayFabのカタログアイテムIDを統一しておくと面倒がない)
builder.AddProduct("MyItemID", ProductType.Consumable);
// 非同期の初期化を開始
UnityPurchasing.Initialize(this, builder);


//////////////////////////
//Unity IAP 初期化完了時
//////////////////////////
void OnInitialized(IStoreController controller, IExtensionProvider extensions){
	//ストアの表示価格は下記のようにして取得
	Product p=controller.products.WithID("MyItemID");
	Debug.Log(p.metadata.localizedPriceString);
}


//////////////////////////
//購入ボタン押した時
//////////////////////////
controller.InitiatePurchase("MyItemID");


//////////////////////////
//購入完了時の呼ばれる関数
//////////////////////////
public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e){
	//プロダクト不明のため無視
	if (e.purchasedProduct == null){
		return PurchaseProcessingResult.Complete;
	}
	//レシートがないので無視
	if (string.IsNullOrEmpty(e.purchasedProduct.receipt)){
		return PurchaseProcessingResult.Complete;
	}
	//レシート検証&アイテム付与。iOSとAndroidで分岐
	if (Application.platform == RuntimePlatform.Android){
		ValidateAndroidPurchase(e.purchasedProduct);
	} else {
		ValidateIosPurchase(e.purchasedProduct);
	}
	//PlayFabでレシート検証完了するまでペンディングにする
	return PurchaseProcessingResult.Pending;
}

//////////////////////////
//Androidのレシート検証&アイテム付与
//////////////////////////
private void ValidateAndroidPurchase(Product purchasedProduct) {
	var googleReceipt = GooglePurchase.FromJson(purchasedProduct.receipt);
	PlayFabClientAPI.ValidateGooglePlayPurchase(new ValidateGooglePlayPurchaseRequest()
	{
		CurrencyCode = purchasedProduct.metadata.isoCurrencyCode,
		PurchasePrice = (uint)(purchasedProduct.metadata.localizedPrice * 100),
		ReceiptJson = googleReceipt.PayloadData.json,
		Signature = googleReceipt.PayloadData.signature
	}, result =>
	{
		//購入処理が完了したものとする
		controller.ConfirmPendingPurchase(purchasedProduct);
		//TODO:インベントリ更新するなど
	},
	error =>
	{
		//使用済みレシートが残っていた場合は完了扱いにする
		if (error.ErrorMessage == "Receipt already used"){
			controller.ConfirmPendingPurchase(purchasedProduct);
		}
	}
	);
}

//////////////////////////
//iOSのレシート検証&アイテム付与
private void ValidateIosPurchase(Product purchasedProduct){
	Dictionary receipt=PlayFabSimpleJson.DeserializeObject>(purchasedProduct.receipt);
	var request = new ValidateIOSReceiptRequest
	{
		CurrencyCode = purchasedProduct.metadata.isoCurrencyCode,
		PurchasePrice = (int)(purchasedProduct.metadata.localizedPrice * 100),
		ReceiptData = (string)(receipt["Payload"])
	};
	PlayFabClientAPI.ValidateIOSReceipt(request, result =>
	{
		//購入処理が完了したものとする
		controller.ConfirmPendingPurchase(purchasedProduct);
		//TODO:インベントリ更新するなど
	},
	error =>
	{
		//使用済みレシートが残っていた場合は完了扱いにする
		if (error.ErrorMessage == "Receipt already used"){
			controller.ConfirmPendingPurchase(purchasedProduct);
		}
	}
	);
}

Photon Unity NetworkingのPhotonTransformViewで微妙にずれる

  • 投稿日:
  • by
  • カテゴリ:

位置同期をPhotonTransformViewで行う際、
補間処理(Interpolate Option)は「Estimated Speed」を使うのが良さそうなのだが
移動停止した時の座標が完全に同期せずわずかにずれたままになる現象がある。

これを解消するには、PhotonTransformViewPositionControl.csの91〜94行目あたりを下記のようすると良さそう。

float estimatedSpeed = (Vector3.Distance(m_NetworkPosition, GetOldestStoredNetworkPosition()) / m_OldNetworkPositions.Count) * PhotonNetwork.sendRateOnSerialize;
if (estimatedSpeed < 1f)
{
currentPosition = targetPosition;
}
else
{
// move towards the targetPosition (including estimates, if that's active) with the speed calculated from the last updates.
currentPosition = Vector3.MoveTowards(currentPosition, targetPosition, Time.deltaTime * estimatedSpeed);
}

要は、推定速度がほぼ0になってしまうと最後まで移動してくれないので
速度 1f未満なら強制的に目標値ぴったりに移動させている。

Photonを使い始めるにあたっては下記の記事がすごく丁寧で分かりやすい。
【Unity】僕もPhotonを使いたい #01〜#14

[Unity] AndroidのWebViewでHTML5 videoを表示

  • 投稿日:
  • by
  • カテゴリ:

AndroidのWebViewでYoutubeなどvideoタグを使った映像再生をしようとしてはまった点をメモ。
WebViewプラグインはgree/unity-webviewを使用。

1)まず、映像が表示されない現象。(音だけ流れて、画面表示は黒か白のまま)
AndroidManifestのandroid:hardwareAccelerated="true"は正しく記述されているのに発生した。
これについてはUnityのバージョンを上げたら解消。
2017.4.10→NG
2018.1.1 →OK

2)映像再生前にとてもダサい再生ボタンが表示される問題。

これはvideoタグのposter属性がない場合にデフォルトで差し込まれるらしい。
Chromeブラウザでは表示されないのにWebViewで見ると表示される。
消すにはWebViewPlugin.javaを書き換えて.jarをビルドしなおす必要がある。

plugins/Android/src/net/gree/unitywebview/CWebViewPlugin.javaを開き、117行目あたりを下記のように変更。

webView.setWebChromeClient(new WebChromeClient() {
 View videoView;
 //透明の画像を返す処理を追記
 @Override
 public Bitmap getDefaultVideoPoster() {
  return Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888);
 }

あとはビルドして出来上がったplugins/Android/bin/WebViewPlugin.jarをUnityのAssets/Pluginsに持っていくだけ。
ビルド手順はgree/unity-webviewに書かれているが、JDK10だと下記のエラーが出てビルドが通らない。
Could not determine java version from '10.0.1'
Gradleのバージョンの問題らしいが、Gradleのバージョンを変えてうまくビルドできなかったので、
JDK8をインストールすることで回避。