[STYLY] アセットを作って組み立てる。制作効率化のコツ [Unity]

これは [STYLYアドベントカレンダー](https://qiita.com/advent-calendar/2021/styly) の4日目の記事です！
#はじめに
STYLYでのコンテンツ制作は、ブラウザで動作するStudioで「シーン」に「アセット」を配置して行います。
アセットには多数のプリセットの他、画像や3Dモデルをアップロードして利用したり、Unityでアセットを自作し利用する事も出来ます。

本記事では、Unityで小さな機能単位でアセットを制作し、STYLY上でそれらを連携することで効率よく制作する方法を実例を通して説明します。

## 参考作品 [The Sword of STYLY](https://gallery.styly.cc/scene/397fe351-2fae-4a6e-b1ce-9432f55c6831)
2020年のNEWVIEW CYPHER Gameで制作したアクションRPG風VRゲーム。
ステージを徘徊するザコキャラを剣で倒しながら進み、ボスキャラを倒すとクリア。

<iframe width="560" height="315" src="https://www.youtube.com/embed/S4qjerWOQkc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
https://gallery.styly.cc/scene/397fe351-2fae-4a6e-b1ce-9432f55c6831

## 制作効率を上げるために
大きなプロジェクトをUnityでまとめて制作すると、STYLYへのアップロードに時間がかかります。
1回だけなら待てば良いかもしれませんが、ゲーム制作では後々までアセット配置などの調整をしたくなるもので、そのたびにアップロードに長い時間がかかるのでは非効率です。
そこで、Unityでは小さな要素に分割して制作し、Studio上で組み立てることで効率化を図ります。

## シーン構成
Unity Projectはアセットサイズが大きなステージ用とキャラクターAI＆ゲームロジック開発用の2つに分けて開発しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/5d9f72c1-9faf-f5b6-ea50-a767b93fe01f.png)

構成要素

* ステージ
  * Unityシーン。NavMeshでザコキャラの移動範囲を設定。
* ザコキャラ
* 巡回ギミック
* ボスキャラ
  * 攻撃を受けるとプレイヤーを吹き飛ばし、ザコキャラを召喚してゆく手を阻む。
* ゲームロジック
 * Kill数、時間管理
 * Player死亡時のゲームオーバー演出
 * ボス撃破時のクリア演出
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/c64f7e1b-52bc-81a6-f6f4-4b6b33cb3f55.png)

ザコキャラをステージに多数配置して巡回ルートを設定しています。
特に巡回ルートをStudio上で設定したかったので、「プラレールを並べてその上を走らせる」ような発想でアセットを作りました。

## ザコキャラの巡回ギミック
ザコキャラはステージ内を巡回。プレイヤーを発見すると攻撃。
NavMeshAgentを使ってステージ内を徘徊させます。
後述の巡回ギミックと組み合わせる事で、STYLY Studio上で巡回ルートを組み立てます。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">今日は <a href="https://twitter.com/hashtag/NEWVIEWCYPHER?src=hash&amp;ref_src=twsrc%5Etfw">#NEWVIEWCYPHER</a> もくもく会<br>Monsterがこちらを発見して攻撃してくるようになった。<br>離れると巡回ルートに戻る。<a href="https://twitter.com/hashtag/STYLY?src=hash&amp;ref_src=twsrc%5Etfw">#STYLY</a> <a href="https://t.co/etdqM6Lpo4">pic.twitter.com/etdqM6Lpo4</a></p>&mdash; afjk(エーエフ) (@afjk01) <a href="https://twitter.com/afjk01/status/1320233959750553600?ref_src=twsrc%5Etfw">October 25, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 巡回ギミック
ザコキャラが開始点踏むと一定距離先の目的地まで誘導するアセット。
TRIGGER_EVENTによりザコキャラとのHitを判定。ザコキャラのNavMeshAgentのDestinationに行く先をセットする事で誘導します。
このアセットを複数配置し、開始点と目的地を連続させることで巡回経路を組み立てます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/4c657a6d-c1ee-efd9-e11a-4053059cc84f.png)

詳細はこちらのツイートで解説しています。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/STYLYTips?src=hash&amp;ref_src=twsrc%5Etfw">#STYLYTips</a> キャラクター巡回ルートの作り方<br>敵キャラクターやステージの賑やかしに使える<br>ギミックを解説します（続きます）<a href="https://twitter.com/hashtag/STYLY?src=hash&amp;ref_src=twsrc%5Etfw">#STYLY</a> <a href="https://twitter.com/hashtag/NEWVIEW?src=hash&amp;ref_src=twsrc%5Etfw">#NEWVIEW</a> <a href="https://twitter.com/hashtag/NEWVIEWCYPHER?src=hash&amp;ref_src=twsrc%5Etfw">#NEWVIEWCYPHER</a> <a href="https://t.co/Ua5g3wMl2y">pic.twitter.com/Ua5g3wMl2y</a></p>&mdash; afjk(エーエフ) (@afjk01) <a href="https://twitter.com/afjk01/status/1320870165685309442?ref_src=twsrc%5Etfw">October 26, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## アセット配置のコツ
多くのザコキャラや巡回経路をステージ形状に沿って配置するにあたり、一つ一つ移動回転していては非常に手間がかかってしまいます。

### STYLY StudioのD&Dを活用する
ヒエラルキー（左のアセット一覧のパネル）からアセットを選択して右へドラッグすると、アセットをコピーすることができます。
さらに、そのアセットはドラッグ中は奥行き方向のコライダーに沿って自動で位置が調整されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/0ef5b7f9-3790-55bb-426a-32541674d934.png)

### STYLY VR Edit modeで直感的に配置する
巡回経路の組み立てはキャラ配置よりも複雑です。
開始点→目的地→開始点→目的地と連続するように配置する必要があり、StudioのD&Dでも手間がかかってしまいます。

STYLYのVR Edit modeでは、VR内でアセットを掴んで直感的に配置する事が出来ます。
<blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">STYLYのVR Edit modeを使って目的地指示ギミックを配置すると、VR内で直感的に巡回ルートを組めるので便利です。<a href="https://twitter.com/hashtag/STYLYTips?src=hash&amp;ref_src=twsrc%5Etfw">#STYLYTips</a> <a href="https://t.co/On0SQGz2vr">pic.twitter.com/On0SQGz2vr</a></p>&mdash; afjk(エーエフ) (@afjk01) <a href="https://twitter.com/afjk01/status/1467050875168636929?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
操作方法は[こちらの記事](https://styly.cc/ja/feature/vreditor/)を参考にしてください。

### STYLYでNav Mesh を使う場合の注意点
 * Nav Mesh AgentはNavMeshの範囲にしか置けません。これはSTYLY Studioでもそうなってしまいます。
 * 場合によってはNav Meshなしバージョンのステージを用意しておき、配置はNavMeshなしステージで行うなど対策してください。

## アセット間のイベント・データ連携
アセットを分割した制作では、アセット間でイベントやデータのやり取りをしたくなる場合があります。
例えば以下のような場合

* ザコキャラを倒したらゲームロジックでKill数をカウントアップする。
* ボスキャラがプレイヤーを吹き飛ばす

アセット間でのイベント、データのやり取りにはPlaymakerの[Send Event by Name](https://hutonggames.fogbugz.com/default.asp?W834)アクションが使えます。
この仕組みにより、別々に作られたアセットでも連携が可能になります。

### 例：ボスキャラがプレイヤーを吹き飛ばす際の連携
 * 送信側

プレイヤーを吹き飛ばす位置を指定
[SetEventData](https://hutonggames.fogbugz.com/default.asp?W531)アクションでEventDataに情報をセットし、Send Event By NameでMovePlayerイベントをBroadcastAllで通知します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/83f5cc34-712c-ae57-1c88-c91447602f05.png)

 * 受信側

MovePlayerイベントを受け、[GetEventInfo](https://hutonggames.fogbugz.com/default.asp?W530)でEventDataから移動先の位置を取得。プレイヤーを移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43734/641f85ff-0c7e-8ffb-d796-b7c7bc03f8da.png)
 
## おわりに
小さな機能単位でアセットを制作し、STYLY上でそれらを配置・連携してコンテンツを制作するコツを説明しました。
作例では、Studioで制作の大部分を行える事で、調整→VRでの確認を繰り返しスムーズに行う事が出来ました。
また、よく使う汎用的な機能（例えば銃など）をアセットにしておけば、別のシーン制作に再利用できるのでさらに制作スピードが上がります。
ぜひ挑戦してみてください！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">現実にオーク召喚。<a href="https://twitter.com/hashtag/STYLY?src=hash&amp;ref_src=twsrc%5Etfw">#STYLY</a> <a href="https://t.co/BJSEHLmGkH">pic.twitter.com/BJSEHLmGkH</a></p>&mdash; afjk(エーエフ) (@afjk01) <a href="https://twitter.com/afjk01/status/1467115206040244231?ref_src=twsrc%5Etfw">December 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>