20191028のUnityに関する記事は5件です。

【Unity】UnityにVivox導入してみた【Sound】

Vivox ってな~に?

vivox.png
https://www.vivox.com/partners
PUBG, Fortnite, LoL, R6S, WoT, など世界的に売れている超有名ゲームで用いられている
テキスト/ボイスチャット提供ミドルウェアです。
2018年9月頃よりインディーデベロッパーでも無償で使えるようになりました(一部条件有)

日本国内ではほとんど導入事例を聞きませんが、現在ボイスチャットを導入するなら
間違いなくこれという状態なので知っておいて損は無いでしょう。

Vivox 導入フロー

アカウント作成まで

  1. 公式サイト(https://developer.vivox.com/) に行ってSDKを手に入れる心構えをする(ここ大事)
  2. 氏名とメールアドレスを登録 (ちゃんと確認用メールが届いているかチェックしよう)
  3. 組織を入力
  4. 組織の審査を待つ

審査通過後~SDK 取得まで

  1. Planを設定
    基本的にはFreePlanでOK, 商用の場合は条件をよく読みましょう
    vivox_plan.PNG

  2. Sandbox アプリケーションを作成
    vivox_dashboard.PNG

  3. アプリケーションの詳細設定
    最悪間違えてもあとでアプリの編集から再設定できるので必要最低限で大丈夫です
    vivox_make_app_01.PNG
    今回はUnityでの開発なのでUnityを選択してリリースに必要なプラットフォームを選択しましょう
    vivox_make_app_02.PNG

  4. 左側メニューのDownloadからSDKを選択
    vivox_sdk.PNG
    Android を選択していますが、UnitySDKを選択すると中にStandalone用とAndroid用とiOS 用のSDKがUnityPackageとして入っていました。とても優しいですね。

  5. DLしてきたzip ファイルを解凍するとUnityCustomPackageがあるのでこれをUnityProject側にImport

ドキュメントについて

ReadMeには「ナレッジベースの~」という説明がありますが、日本語化UIだと場所がよくわからないので説明します。
1. Developperページのところのドキュメントを選択
2. バージョンを選択
3. General Unity を選択
vivox_doclink.PNG
4. ドキュメントページへ移動
5. Vivox Core Unity: Developer First Stepsを見ながら初期設定を行う

Unityプロジェクト側の初期設定

カスタムパッケージをImport後からの手順です。
1. Assets/Vivox/Samples/TanksUnityGameSample/Scenes/MainScene.unity を開く
2. VivoxのDevelopperPortalからDashBoard 内にある先ほど作ったアプリを選択
3. InspectorにあるVivoxVoiceManager にWebページに乗っているパラメータをセット
(画像はセキュリティの都合上値を一部非表示しています)
vivox_app_config.PNG
vivox_config.PNG
4. Unity側のBuildSettingsにSceneを設定してビルド
vivox_buildSettings.PNG

まとめ

だらだらやって2~3時間でボイスチャット/テキストチャットが動きました。
Pixel3, Oculus Go, UnityEditor(PC)間で簡単にボイチャが出来るのは驚くべきことで、
謳い文句の36時間以内にボイスチャットが動くはあながち間違いじゃないなと思いました。

今回は導入フローまでですが、製品版で使うには別途審査があったりしますが、
導入ハードルの低さや性能部分に関しては文句なしといったところでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

脱出ゲームの作り方 7 特定の条件でアイテムを使用

取得した電球を使うと電気が点灯する処理が作成されたが、
LightstandPanel以外の場所でも使えてしまうので、LightstandPanelの場面で電気を使うようにする

各パネルを表示するときに、現在のパネルが何かをチェックする

GameManagerで下記を追加する

◇変数currentPanelを列挙型で作成し、ROOMパネルで初期化する

public enum PANEL
{
    ROOM,
    LIGHT_STAND,
    DRAWER,
    PC
}

    // 現在表示しているパネル
    public PANEL currentPanel = PANEL.ROOM;

◇ボタンを押したら、現在のパネルをcurrentPanelに取得し、該当するパネルを表示する

バックボタンが押されたら、currentPanelをROOMに戻し、他のパネルを非表示にする

    // ボタンを押したら該当するパネルを表示
    public void OnClickLightStandTrigger()
    {
        currentPanel = PANEL.LIGHT_STAND;
        lightStandPanel.SetActive(true);
    }
    public void OnClickDrawerTrigger()
    {
        currentPanel = PANEL.DRAWER;
        drawerPanel.SetActive(true);
    }
    public void OnClickPCTrigger()
    {
        currentPanel = PANEL.PC;
        pcPanel.SetActive(true);
    }

    // ボタンを押したらパネルを全て非表示
    public void OnClickBackTrigger()
    {
        currentPanel = PANEL.ROOM;
        lightStandPanel.SetActive(false);
        drawerPanel.SetActive(false);
        pcPanel.SetActive(false);
    }


アイテムを電気スタンドが表示されている時だけ使用する

◇現在のパネルが電気スタンドのときに、アイテムを使う
ItemBoxManagerでGameManagerを取得し、UseItem()を書き換える

    [SerializeField] GameManager gameManager;   


 // アイテムの使用
    public void UseItem(int index)
    {
        if ( gameManager.currentPanel == PANEL.LIGHT_STAND
          && itemList[index] == ITEM.LIGHT_BULB)   //現在のパネルが電気スタンドで、かつ使用するアイテムが電球だったら
        {
            lightStandManager.LightSwitch(true);            //電気をつける
            itemList[index] = ITEM.NONE; // アイテムを使用したので空にする;
            itemBoxImages[index].sprite = null;
        }
    }

InspecterでItemBoxPnelオブジェクトのItemBoxManagerスクリプトにGameManagerが出ているので、GameManagerを設定する

:interrobang:
UseItem(int index)関数は、OnClick()ではなく実行できる?

教材

Unityゲームスタジオ スタジオしまづ
【Unity】初心者からの脱出!? 脱出ゲームの作り方 その7 特定の条件でアイテムを使用
https://youtu.be/ualq_a3rnQc

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Taskじゃない非同期をTaskに変換したい(TaskCompletionSource)

はじめに

Task(UniTask)は便利です。
特にこの昨今awaitで結果を待てるので手続き処理っぽく書きつつ非同期処理が書けます。

でも、 Taskじゃない非同期処理はどうしましょう。

例えばボタンのクリック。 ボタンのクリックはユーザーが気が向いたときにするものなので非同期です。

PlayFabやNCMBといったmBaasのAPI呼び出しなどは通信中画面が止まっては困るのでコールバックで結果(もしくはエラー)を受け取るようになっています。 これもまた非同期です。

こういった「非同期だけれど、Taskではない」

をTaskに変換してくれる便利な奴がいます。 それが TaskCompletionSource(UniTaskCompletionSource)です。

※この記事では基本Task/TaskCompletionSourceを使いますが、適宜UniTask/UniTaskCompletionSourceに読みかえてください

基本形

public Task<int> GetIntAsync(){
    var tcs = new TaskCompletionSource<int>();



    return tcs.Task;
}

intを返すTask Task<int> に対応するのは TaskCompletionSource<int> ですし
floatを返すTask Task<float> に対応するのは TaskCompletionSource<float> です。

この返却したいTaskに対応した TaskCompletionSource を new して、 Task をreturn するのが基本形です。

しかし、このメソッドのTaskはいつまでたっても結果を返しません。 それはそう。 誰も結果を返してないからです。

結果のセット

結果を返すにはこのTaskCompletionSourceのインスタンスに TrySetResult を呼ぶ必要があります。
(なお、キャンセルする場合は TrySetCanceled()を呼ぶ必要がありますが、それはまたの機会に・・・)

public Task<int> GetIntAsync(){
    var tcs = new TaskCompletionSource<int>();

    tcs.TrySetResult(32768);//結果のセット

    return tcs.Task;
}

public async Task RunAsync(){
    var num = await GetIntAsync(); 
    Debug.Log(i); //→ 32768 
}

これで、このメソッドはTaskの結果として32768を返すようになります。
さて。これは非同期でしょうか。違いますね。 これでは return Task.FromResult(32768); と同じです。

しかし、ここで一つとても重要なことがあります。それはこの TrySetResultは非同期で呼んでも構わないということです。

と言われても良くわからない方もいるでしょう。
例えば。 最初に例で出した ボタンのクリック を絡めてみます。

例1 指定したボタンをクリックしたらintを返却するTask

まず、基本形を作ります

public Task<int> ButtonClickAsync(){
    var tcs = new TaskCompletionSource<int>();

    return tcs.Task;
}

「指定したボタンを」とあるので、引数でボタンを受け取ります

public Task<int> ButtonClickAsync(Button btn){
    var tcs = new TaskCompletionSource<int>();

    return tcs.Task;
}

ボタンがクリックされたら、結果を返却するようにします。 結果を返却するには TaskCompletionSourceTrySetResult を呼べば良いのでした。

public Task<int> ButtonClickAsync(Button btn)
{
    var tcs = new TaskCompletionSource<int>();

    btn.onClick.AddListener(() => tcs.TrySetResult(65536));//ボタンクリックイベントのコールバックで結果セット

    return tcs.Task;
}

完成です。これで、ボタンをクリックするとこの tcs.Task は 65536 を返却して終了するようになります。
簡単ですね。無事 Taskじゃない非同期処理をTask化できました

※これだとbtnのonClickイベントが登録されっぱなしじゃないか!と思った貴方。大正解です。本当はちゃんと解除してあげてください。

では、次はほんのちょっと応用です。 (今となってはObsoleteな)WWWクラスをTask化してみます

例2 指定したURLからページ情報を取得するTask

これまた、基本形を作ります。 ページ情報なので Task は Task<string> TaskCompletionSourceも TaskCompletionSource<string> にします。

public Task<string> WWWAsync(){
    var tcs = new TaskCompletionSource<string>();

    return tcs.Task;
}

「指定したURLから」とあるので、引数でurlを受け取ります

public Task<string> WWWAsync(string url){
    var tcs = new TaskCompletionSource<string>();

    return tcs.Task;
}

WWWクラスはコルーチンで結果を待つ仕組みなので、StartCoroutineで指定したurlからWWWでコンテンツを取得するようにします。

public Task<string> WWWAsync(string url){
    var tcs = new TaskCompletionSource<string>();

    StartCoroutine(WWWGet(url));

    return tcs.Task;
}

private IEnumerator WWWGet(string url)
{
    var www = new WWW(url);
    yield return www;
    //wwww.text; //TODO ここにダウンロードしたコンテンツが入っている
}

結果を返却するには TaskCompletionSourceTrySetResult を呼べば良いのですが、コルーチンの結果は(基本的には)取得できないので、WWWGetの引数に TaskCompletionSource<string> を追加して、WWWGetの中で結果を入れてもらうように修正します。

public Task<string> WWWAsync(string url)
{
    var tcs = new TaskCompletionSource<string>();

    StartCoroutine(WWWGet(url,tcs));

    return tcs.Task;
}

private IEnumerator WWWGet(string url, TaskCompletionSource<string> tcs)
{
    var www = new WWW(url);
    yield return www;
    tcs.TrySetResult(www.text);
}

完成です。 コルーチンによる非同期もTaskにすることが出来ました。

合体!

完全に蛇足ですが、上記二つの例を組み合わせると、「ボタンがクリックされるまで待ち、クリックされたら指定されたURLにアクセスしてコンテンツを取得する」Taskが作れます。

    private async Task RunAsync()
    {
        var i = await ButtonClickAsync(_btn);//指定したボタンがクリックされるのを待つTask
        Debug.Log(i);

        var content = await WWWAsync("http://yahoo.co.jp");//指定したURLのコンテンツを取得するTask
        Debug.Log(content);
    }

んー。すっきりしてますね!

最後に

今回は非常にシンプルな例を挙げましたが、使ってみるとっかかりにでもなれば幸いです。

Taskにしておくと結果が取得しやすいのはもちろんのこと、Task.WhenAnyTask.WhenAllを使って、複数あるうちのどれか一つのTaskが完了待ちや全てのTask完了待ちなど、ただの非同期処理では難しかった非同期処理同士の連携が簡単にできるようになったりするので(状況に応じて)Task化進めるのも良いと思います。

補足

  • 基本形として TaskCompletionSource をローカル変数で作りましたが、メンバ変数でも構いません。 というか、その方が便利な場合も多々あります。 ただし、TaskCompletionSourceの使いまわしはできません。 1回 SetResultしたらそれで終わりなので、また同様のTaskを呼ぶ場合は TaskCompletionSource を再インスタンス化する必要があります。
  • ボタンクリックや、wwwの待ち等は実は UniRX.Async を使っていればこんなことせずに await が可能です。 (ボタンクリック待ちのTaskは .OnClickAsync(); が用意されています)今回の内容はあくまでも TaskCompletionSource の使い方の簡単な説明なのであしからず。
  • TaskCompletionSource には、結果を返さないただのTaskがありません。 そのため別に返却する値が無い場合でも TaskCompletionSource<object> とし、 TrySetResult(null); とやることがよくあります。 が、 UniTaskCompletionSource には非Generics版が用意されているので、返却値が無い場合はそちらを使用しましょう。
  • イベント周りはUniRX使った方が適切なケースも多いです。適宜使い分けましょう。

よい。Taskライフを。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】DoozyUIのUIPopupの使い方

初めに

こちらはUnityのAssetのDoozyUIのUIPopupについての記事です。
DoozyUIのチュートリアル動画はだいたい理解できる内容だったのですが、UIPopupの動画だけは少しわかりにくかったので記事にしました。
DoozyUIの他の機能と共通なところはあまり説明していません。ご容赦ください。
この記事ではDoozyUIのversion3.0.c4を使用しています。

UIPopupとは

  • UIPopupはuGUIのプレハブをアニメーションさせて表示する機能です。
  • キューに登録して順番に1個づつ表示することもできます。
  • UIViewと似たようなところがありますが単体で使う時に向いてそうです。

UIPopupの使用方法

まずは最小構成で動作させてみます。

  1. UIPopupを持つuGUIのプレハブを作ります。
    Hierarchyで右クリックからDoozy>UI>UiPopupで作ります。
    UIPopupのShowとHideにアニメーションを設定をしておきます。
    UIPopupShowHide2.png

  2. メニューのTool>Control PanelからDoozyUIのウィンドウを開いて、Popupsデータベースに名前を入力してプレハブを登録します。
    UIPopupPopups.png

  3. 下記スクリプトを実行するとプレハブからインスタンスを作成して表示します。
    ボタン等のイベントに設定して実行してください。

最小構成のスクリプト

using UnityEngine;
using Doozy.Engine.UI;

public class DoozyPopupShowExample : MonoBehaviour
{
    [SerializeField]
    string PopupName;

    public void PopupShow()
    {
        UIPopup popup = UIPopupManager.GetPopup(PopupName);
        UIPopupManager.ShowPopup(popup, popup.AddToPopupQueue, false);
    }
}

表示内容の変更方法

UIPopupの例を見るとテキスト、スプライト、ボタン等の内容を変更していました。
UIPopupには変更対象を管理しやすくする仕組みが用意されていて対象のオブジェクトをリストに設定しておきます。
Popups_003.PNG

それぞれの設定数とスクリプトの関数の引数の数は合わせます。
スクリプトの関数の引数が多い場合はエラーになります。

表示内容の変更のスクリプト例

UIPopup popup = UIPopupManager.GetPopup(PopupName);

popup.Data.SetLabelsTexts(Title, Message);          //←追加 テキストの文字を変更
popup.Data.SetImagesSprites(sprite);                //←追加 画像の変更
popup.Data.SetButtonsLabels(LabelButtonOne);        //←追加 ボタンの文字を変更
popup.Data.SetButtonsCallbacks(ClickButtonOne);     //←追加 ボタンで実行したい関数を登録

UIPopupManager.ShowPopup(popup, popup.AddToPopupQueue, false);

公式の参考例E11のスクリプトでは登録したリストを取得して直接色を変更していたりする例もありました。
ここまでの流れがわかれば、あとは公式資料も参考にしていけば良いかと思います。

気になったところ

  1. DoozyUIでTextMeshProを使用する設定にしているとSetLabelsTextsで通常のTextは変更できないようです。
  2. 公式のE11のスクリプトでm_popup.Show()になっていました。 UIPopupManager.ShowPopupを使わないとAftar to Popup Quaueの設定が無視されます。
  3. GetPopup関数の処理が少し遅い気がしました。 私の環境で測定したらほぼ0.9ms程度かかっていました。 関数内の処理を見るとデータベースからプレハブを探す際にList<UIPopupLink>をforeachで2回ループしていたことと、そもそものInstantrateが遅いのが原因と思います。

※UIPopupLinkはDoozyUIデータベースが管理している名前とプレハブを持つScriptableObjectです。
※UIPopupLinkはAssets\Doozy\Engine\Resources\Data\UIPopupEngineフォルダに作成されてました。

今回考えた遅延対策

使い方次第なのでここからは蛇足かもしれませんが、下記の2点をやってみました。
1. UIPopupLinkからプレハブを取得する。
2. オブジェクトプールを使用する。

参考スクリプト例

using System.Collections.Generic;
using UnityEngine;
using Doozy.Engine.UI;
using A_rosuko.ObjectPool;

public class DoozyUIPopupInstantiate : MonoBehaviour
{
    [SerializeField]
    UIPopupLink uiPopupLink;

    public void AddMessage(string Message)
    {
        //オブジェクトプールからプレハブのインスタンスを取得
        GameObject clone = ObjectPool.Instance.GetGameObject(uiPopupLink.Prefab);
        UIPopup popup = clone.GetComponent<UIPopup>();

        //UIPopupの設定を変更する
        popup.Data.SetLabelsTexts(Message);

        UIPopupManager.ShowPopup(popup, popup.AddToPopupQueue, false);
    }
}

オブジェクトをプールに戻す処理はUIPopup側でDestroyしない設定にして、HidePopupのFinishのUnityEventsにプールする処理を登録して実行してます。
1の方は効果が少なかったですが、2の効果が大きかったので約0.08ms程度に改善しました。

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityのビルド時に見慣れない警告が出る

症状

Script attached to 'SpawningBase_SpawnableObjectPrefab' in scene '' is missing or no valid script is attached.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

Script attached to 'AuthObjPrefab' in scene '' is missing or no valid script is attached.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

Script attached to 'GetCurrentRTTCallDoesntCrashWhenUseWebSockets_PlayerPrefab' in scene '' is missing or no valid script is attached.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

Script attached to 'SpawningBase_PlayerPrefab' in scene '' is missing or no valid script is attached.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

Script attached to 'PlayerWithAuthPrefab' in scene '' is missing or no valid script is attached.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

Script attached to 'NoAuthObjPrefab' in scene '' is missing or no valid script is attached.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

Script attached to 'PlayerCallbacksOrderOnTheHost_PlayerPrefab' in scene '' is missing or no valid script is attached.
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

解決方法

PackageManagerから「Multi Player HLAPI」をバージョンアップする。

https://answers.unity.com/questions/1624149/script-attached-to-playerwithauthprefab-in-scene-i.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む