simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity: Behavior Designer のノードの勉強

基本的なことはこちら
simplestar-tech.hatenablog.com

ビヘイビアツリーを構成しているノードは三つに分けられる、それぞれ Task, Composite, Decorator である。

・Task はツリーのリーフ要素で、1フレームに許された計算時間で条件判定やアクションを実行し、戻り値として1)Running(まだタスクが残っている), 2)Failure(条件に合わない、または失敗), 3)Success(条件一致、または成功) の三つのいずれかを返す。
・Composite は子ノードの実行を制御するノードで、シーケンスとセレクターの二種類が存在する。
シーケンスは最初の子ノードが実行完了するまで待機し、成功を返した場合に、次の子ノードを実行し、この操作を繰し、最後の子ノードが成功を返したら、シーケンスノードも成功を返す。一度でも子ノードが失敗を返したら、残りの子ノードは無視して失敗を返す。子ノードが実行中を返すようなら、シーケンスも実行中を返す。
セレクターは優先度順に子ノードの選択を行い、選択したノードを実行する。シーケンスと違う点は、どれか一つの子ノードが成功を返した時点で、まだ実行していない子ノードがあっても、成功を返す点である。
セレクターには指定した割合の確率で選択する Probability Selector や、1フレームにすべての子ノードを実行する Parallel がある。
・Decorator ノードは特質系で、子ノードを一つしかとることができない。ノードのタイプによって振る舞いはさまざまで、たとえば n 回子ノードを繰り返し実行してから成功を返したり、子ノードがn秒以上実行中だったら成功失敗に関わらず強制的に失敗を返したりする。

これから Behavior Tree を活用していくので、使い方に慣れるため、各ノードの動きを確認しながら、道具として使えるようになるため、覚えていこうと思います。

## Parallel

子タスクを一斉に並列実行し、いずれかが失敗を返したらすべての子タスクを停止して失敗を返す
実行中の子タスクが残るなら、実行中を返し
すべての子タスクが成功したとき、成功を返す
イメージとしては並列実行の And 条件 if 文って感じですかね

f:id:simplestar_tech:20210703130241p:plain
Parallel

## Parallel Complete

子タスクを一斉に並列実行し、最も早く答えを出した子タスクの答えを返す
全部成功していて、残る実行中のものがあったとしても、最初に成功を返した子タスクを見つけた時点で成功を返す
イメージとしては並列実行の Or 条件 if 文って感じですかね、False を返すのが先だと False が返るところが全然違う

f:id:simplestar_tech:20210703131331p:plain
Parallel Complete

## Parallel Selector

子タスクを一斉に並列実行し、すべて失敗となるまで待つが、いずれかが成功を返した瞬間に子タスクをすべて停止して成功を返す
イメージとしては並列実行の Or 条件 if 文って感じですね
f:id:simplestar_tech:20210703131954p:plain

## Priority Selector

float GetPriority(); を Action Task 側で実装して返す必要がある
この値が大きい順で子タスクを順番に実行して、先に成功を返したタスクが生まれたら成功を返すもの

f:id:simplestar_tech:20210703142958p:plain
Task class

## Random Selector

ランダムに子タスクの順番を決めて Selector として、成功を見つけるまで続け、成功を返す子タスクによって、続く子タスクの実行を停止して、成功を返す
イメージとしては順番実行の Or 条件 if 文って感じで、評価順がランダムというものですね

f:id:simplestar_tech:20210703143258p:plain
Random Selector

## Random Sequence

ランダムに子タスクの順番を決めて Sequence として、すべてが成功するまで続け、失敗を返す子タスクによって、中断して子タスクの実行を停止して、失敗を返す
最後の子タスクが成功したときにやっと成功を返すという
イメージとしては順番実行の And 条件 if 文って感じで、評価順がランダムというものですね

f:id:simplestar_tech:20210703150525p:plain
Random Sequence

## Selector

いずれかが成功したら成功を返し、残りの実行を中断する
イメージとしては順番実行の Or 条件 if 文って感じ

f:id:simplestar_tech:20210703150847p:plain
Selector

## Selector Evaluator

実行中を返す Action が子タスクの優先度の低いものであったなら、それより高い優先度の Action の評価をもう一度行う様子
実行中のタスクより低い子タスクの実行はせず
優先度が高いタスクは常にチェックしたいといったギミックに良いのかも たとえばプレイヤーを視認していること という条件に使って、続くアクションに追いかけるといった running ステータスを返す何かが挟まるなど
イメージとしては順番実行の Or 条件 if 文って感じだが、どういうわけかさっき調べた条件をもう一度しらべいにく
いくつもの Task のうち、最初に成功を返した Task の結果をもって成功を返す

f:id:simplestar_tech:20210703151759p:plain
Selector Evaluator

## Sequence

順次実行して、失敗が返ると即子タスクを停止して失敗を返す
イメージとしては順番実行の And 条件 if 文って感じ

f:id:simplestar_tech:20210703153116p:plain
Sequence

## Utility Selector

float GetUtility の値が大きいものを常に選んで実行する
子タスクすべてを見ていて、Utility 値が実行中に小さくなったなら、他の Utility 値が大きいタスクの実行へと移る
いずれかが成功を返したら、他のタスクを停止して成功を返す でも、いずれの子タスクも Running を返すようにする使い方がメインなのかな
Priority Selector との違いは、それまで最高Utility だったタスクが Running 中でも、他の Utility が高まったら中断するところかな

f:id:simplestar_tech:20210703153501p:plain
GetUtility
f:id:simplestar_tech:20210703154804p:plain
Utility Selector

Unity:非VRからVRモード切替機能とその逆切り替えをするScript記述方法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Management;

namespace CubeWalk
{
    /// <summary>
    /// VRモード切替器
    /// </summary>
    public class VRSwitcher : MonoBehaviour
    {
        /// <summary>
        /// 非VR時にのみアクティブになるオブジェクト群
        /// </summary>
        [SerializeField] List<GameObject> keyMouseObjects;
        /// <summary>
        /// VR時にアクティブになるオブジェクト群
        /// </summary>
        [SerializeField] List<GameObject> vrObjects;
        /// <summary>
        /// VR アセット位置調整用
        /// </summary>
        [SerializeField] Transform vrAssets;
        /// <summary>
        /// プレイヤーオブジェクト発見用
        /// </summary>
        [SerializeField] Transform players;

        public void EnableVR()
        {
            StartCoroutine(this.CoEnableVR());
        }

        public void Enable2D()
        {
            XRGeneralSettings.Instance.Manager.StopSubsystems();
            XRGeneralSettings.Instance.Manager.DeinitializeLoader();
            foreach (var item in this.vrObjects)
            {
                item.SetActive(false);
            }
            foreach (var item in this.keyMouseObjects)
            {
                item.SetActive(true);
            }
        }
        
        IEnumerator CoEnableVR()
        {
            yield return XRGeneralSettings.Instance.Manager.InitializeLoader();
            while (!XRGeneralSettings.Instance.Manager.isInitializationComplete)
            {
                yield return null;
            }
            XRGeneralSettings.Instance.Manager.StartSubsystems();

            // VR開始位置をプレイヤー位置に調整
            foreach (Transform vrm in this.players)
            {
                if("Player" == vrm.tag)
                {
                    this.vrAssets.position = vrm.position;
                    break;
                }
            }

            foreach (var item in this.keyMouseObjects)
            {
                item.SetActive(false);
            }
            foreach (var item in this.vrObjects)
            {
                item.SetActive(true);
            }
        }
    }
}

CubeArtWorld:ユーザーショップ機能

長らく作ってる姿をお見せしてきたゲームは、ついに昨年クリスマスにリリースしました!
booth.pm

ひそかにブログを見守ってくれたそこのあなたに、ぜひ買って遊んで、口コミで世に広めてもらいたいです。ご協力お願いします。

今回は、そのゲームタイトル「 Cube Art World 」に入る大型アップデート:ユーザーショップ機能について構想から記していきます。

ユーザーショップデータ

プレイヤーがキャンバスキューブと呼ばれる、所有権を有するキューブにアイテムと価格設定を加えて在庫とともに陳列するというデータをサーバー側にて刻むとすると
さて、このデータをどこに置くか どうやって他プレイヤーがアクセスするかについて考えていきたいと思います

現在のキャンバスキューブのメタデータにショップアイテム配列を追加する というのもありかもしれません。
別に用意して、データを取りに行ってあるかどうか確かめる方法もあるのですが、これは UI について決めていくと、そのハマり具合がわかり、決まりそうです。

ということで、現在のキャンバス UI から見ていきましょう。

f:id:simplestar_tech:20210214135022p:plain
キャンバス閲覧時

こちら、編集することができる者だけ Edit ボタンを押して次の編集UI に移行することができます。

f:id:simplestar_tech:20210214135206p:plain
キャンバス所有者のみが開ける編集画面

既存の UI はそのままに、ここにユーザーアイテムショップ機能をつけるとして、発想としてはショップを開くボタンを置いてみるとします。

メインの Cube Type を Canvas として、これをドロップダウンリストで Shop に切り替えられるとしましょう
この考え方の良いところは、既存の UI 設計を崩さず、新しい UI に切り替えて提供できる点です。

今の状態をそのままに、リストディフォルトを Canvas とします。データに無い場合はこれ
そして、Shop に切り替えたときに、既存の UI はすべて消え、新しいショップ用 UI に切り替わります。

そこにはアイテム選択と価格、在庫数を入れるフォームを用意し、Add ボタンでリストに追加
既存リスト項目の編集、削除が行えることにします。前後移動も可能な左右やじるしボタンも用意

項目はサムネイルとアイテム名 キャンバスの場合はタイトル 簡易説明が付く 価格 GD と、販売予定個数

あと、今後購入履歴を過去 5件ほど記録して、これをプレイヤーが認識できることとします

保存する情報はどこか
メタデータには ドロップダウンの選択項目のみ記録して、配置パスは分けます
キャンバスIDで管理はされますので、キャンバスから参照、編集には困らない

タイプがキャンバスの場合は既存のフロー
ショップの場合は先行してショップ情報を引き出して、これを使って、ショップ用の UI を表示することにします。

ドロップダウンはこの通り

f:id:simplestar_tech:20210214151853p:plain
CubeType選択

UI の行き来を作ってみましょう

販売項目追加において、いったいプレイヤーは何のアイテムを売ることができるのか?
これについて、インベントリ内の運営規定IDのキャンバスと、自身が所有権を持つキャンバスに値を付けて販売できるものとします。

インベントリアイテムリストを絞り込めるかチェックしてみます

リストを選ぶ UI を先に作っていきました

f:id:simplestar_tech:20210214181904p:plain
陳列項目追加 UI

UI の切り替えがうまくいったところで、まずはキャンバスのキューブタイプ情報を既存のメタデータに追加します。
これを保存できるようにして、さらに読み込んでから UI をそのタイプで初期化して表示するように作り替えてみましょう。

続いて、自身の所持金額を左上に表示します
もろもろ初期表示まわりがそろいましたので、続いて、 Add ボタンを押したときに 未設定の陳列アイテム 選びなさい UI が現れるようにします。
ここでSelect を押したとき、インベントリのようで、そうではない所持アイテム一覧のうちフィルターされた ショップ陳列可能なアイテムが並ぶようにしてみます

陳列項目を追加、編集するという UI だけできてきました。

f:id:simplestar_tech:20210216000941p:plain
追加操作を提供 不正があれば理由を示します。

こちらの機能は
simplestar-game.booth.pm

リリースされました!!
経済という概念、プレイヤー同士が金銭でアイテムをやり取りし出すことがはじまります

CubeWalk:ゲーム開始までの流れをどう実装するか案

これまた下書きのままずっと放置されていた記事
Unity のコンポーネントが Start や Awake で好き放題やるのは、引き継いだり後から見直したときに
全体で何しているかわからないので、例えばオンラインのログイン完了後に処理してほしいとかそういうの指示するとき困るよね
これをどうするか考えた記事

開始フロー

調べてみると、各コンポーネントがスタート時に自由に開始していました。

やりたいことはゲームのタイトル情報、プレイヤーの現在の状況を読み込んでから

タイトルメニューで情報表示
プレイヤーの現在の状況から再スタート

バージョンとIDの表示だが

まずプレイヤーIDとは?

なるほどGenericIDとして、これを使ってサーバーAPIでPlayFabIDを問い合わせることができる

GenericIDの実態はサービス名とIDのペア

これを見てひらめいたのは、ゲーム内のキューブにGenericIDを刻み込み
サービス名はワールド名とし、IDにはワールド内の位置インデックス0~1700万くらいFFFFFFとする
ぎりぎり
その位置とキャラクターを結ぶようにGenericIDを加える

そうすれば、キューブ内のデータに描画用の情報を詰めてもよいことになる。
これがプレイヤー復活のためのメモリーキューブで、開始時にプレイヤーはしゃがんだ状態でキューブと交換するようにして現れることになる

開始フローを決める前に、まだまだ、そういうことしたいと思っていて要素がそろいきっていないことがわかった。

結論

最終的に GameManager が各種イベント接続をするコードを担当
そこを見ればゲームの処理の流れが追えるようになりました。

あとからどういうゲーム実装だっけ?と思って実装を読んだときに GameManger だけ読めば漏れなくわかる
というのを目指します。

React x Typescript で index.html を独自ドメインで世界公開

まえがき

Unity Asset Store に例の

f:id:simplestar_tech:20200209162015j:plain
例のアセット
を提出したんだけど、リジェクトされてしまった。

どうも初回投稿なら、技術力を見せるサイトを持ってないとダメな様子
仕方なく、作ることにします。
とりあえず index.html にアセットの画像がごろごろあればいいんかな

Node 環境を整える

Web サイトか…

今なら React x Typescript かな?

ということで node パッケージマネージャーを使えるようにしましょう。
Windows 環境はインストーラから入れるなど
nodejs.org

React x Typescript のサンプルをローカルで作る

こちらの技術書を読んで、サンプルを動かしながらコードを理解してみる
oukayuka.booth.pm

typescript で、見ただけでデータの動きがアニメで見えることが大事
以下のような単語で絵が動けばいい

分割代入
スプレッド演算子

読み切れば Redux + Saga の概念の理解と関数コンポーネントによる React の具体的実装がわかる

独自ドメインで世界に公開する

https://unityassets.elastprism.com/

具体的な手順はこちら
qiita.com

1.yarn build でできた build フォルダの中身を S3 にアップロード
2.Cloudfront でディストリビューションを作って、データオリジンに S3 を選ぶ
参考記事
simplestar-tech.hatenablog.com
3.バージニア北部にドメイン指定で証明書を作る
4.Route53で独自ドメインからCloudfrontへのAlias作る

以上の手順で世界からの独自ドメインリクエストするとCloudfrontへルートができて、各エッジサーバーからReact の SPA のアクセスが有効になる

肝となるのが、上記の Qiita の紹介にあるとおり Cloudfront の Custom Error Response で /index.html を 403 や 404 のときに返すようにする
これで、どの Route 先ページでも F5 でリロードしても S3 アクセス拒否という 403 エラーは返ってこない

そういえば昔ホームページの作り方を書いたことあったけど5年もすると Cloudfront で SPA を配信できるようになる(腕を上げたな)
simplestar-tech.hatenablog.com

ところでアセット紹介ページがまだぜんぜん形になってないのです。

追記:

単純に yarn build の成果物を公開すると .map ファイルが同梱されるためソース内容(ts, tsx)実装が同時に公開されてしまいます。
.env ファイルを直下に置いて
GENERATE_SOURCEMAP=false
を書き込んでから yarn build することで without sourcemap で build でき、それを公開した場合は実装がばれずに公開することができました。

情報元はこちら
qiita.com