テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】スティックを倒した方向にキャラクターを向ける

f:id:tsubaki_t1:20170703230905g:plain

昨日に引き続き、キャラクターの向きを変える奴です。
今回はマウスの位置ではなく、スティックを倒した方向にキャラクターを向けてみます。

tsubakit1.hateblo.jp

スティックを倒した方向にキャラクターを向ける

スティックを倒した方向にキャラクターを向けてみます。これはトップビュータイプのゲームをコンソールゲーム機やモバイルで操作する際によく見る手法の一つです。

 試しに作ってみました。

スティックを用意

まずスティック操作を用意します。今回は出来る限り手を抜きたいので、CrossPlatformInputを使用してサクッと用意します。

最近の傾向だと「DualTouchControls」の方がなじみが深いと思いますが、今回は面倒なので「MobileSingleStickControl」を採用します。

左にあったJoyStickを右にも持っていき、HorizontalAxisNameを”Horizontal2”、Vertical AxisNameを”Vertical2”とか設定します。
これで CrossPlatformInputManager.GetAxis ("Vertical2"); とかでスティックの値が取得できます。

f:id:tsubaki_t1:20170703231424j:plain

f:id:tsubaki_t1:20170703232025j:plain

f:id:tsubaki_t1:20170703231606g:plain

スティックの方向を向く、移動する

次にスティックを倒した方向に移動したり、向きを変えたりしてみます。前回はクリックの位置から向きを取得していましたが、今回はそのまま使ってみます。

gist.github.com

f:id:tsubaki_t1:20170703232720g:plain

これで向きを向いた方向に移動できました。ただコレが正常に動作するのはZが奥を向いてるときのみで、カメラを回転させると移動や向きが破たんします。

f:id:tsubaki_t1:20170703232830g:plain

カメラの視点も考えて方向を決める

カメラの向きも考えて方向を決めます。

tsubakit1.hateblo.j

これで下のように、カメラの位置が変わってもスティックを上に倒せば、画面的に上の方向をキャラクターが向くようになります。

 

gist.github.com

関連

tsubakit1.hateblo.jp

qiita.com

tsubakit1.hateblo.jp

【Unity】キャラクターの向きをマウスのある方向に向ける(2D/3D)

f:id:tsubaki_t1:20170702215521g:plain

今回は上から視点で、マウスのある方向にキャラクターを向けようと思います。

マウスの位置にキャラクターの向きを変える

上から見下ろす感じの視点(トップビュー)でゲームをする場合、キャラクターの向きを変えるのは大体はジョイスティックもしくはマウスの位置です。

これは既にゲームの文脈として登録されており、普通に使われる操作です。古事記にもそう書いてあります。

 

特に「照準」をマウス(右手)で行い、移動はキーボードWASD(左手)の文脈では、かなりよく使われる手法です。*1

今回はそのやり方について、少しメモします。

本当はコレの感じのゲームをUnity1Weekに出したかった

サバイバルシューターから学ぶ

まずはゲーム内のマウス位置を取得します。

その辺りはUnityの初心者向けチュートリアル「Serival Shooter」を見てみます。

unity3d.com

やり方はカメラから見たマウスの位置と地面との交点の方向を向く方法です。

Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);

でカメラからマウスの位置に対しての光線(Ray)を

Physics.Raycast (camRay, out floorHit, camRayLength, floorMask)

で実際にレイを飛ばしています。

後は「地面に接触した部分」が実際の地面という事みたいです。

f:id:tsubaki_t1:20170702221309j:plain

後はキャラクターをRayの交点の方向に向けるだけです。

Vector3 playerToMouse = floorHit.point - transform.position;  Quaternion Quaternion newRotation = Quaternion.LookRotation (playerToMouse);

地面からColliderを取っ払う

なんとなくコレだけの為にColliderを作るのは負けた気がしなくもないです。また、手前に高い建物等があるとヒットしてしまう可能性を回避するためマスクを設定するのも、なんとなく負けた気がします。

ので、PhysicsRaycastは止めて、Plane.Raycastを使ってみます。

docs.unity3d.com

gist.github.com

これで高さの異なる場所でも、かつ手前に障害物があっても問題なく回せます。PerspectiveでもOrthographicでもどちらでも。

f:id:tsubaki_t1:20170702224002g:plain

f:id:tsubaki_t1:20170702233659g:plain

2Dの場合でも同じようにしてみる

2Dの場合も基本は同じアプローチで出来ます。

gist.github.com

f:id:tsubaki_t1:20170702230436g:plain

2Dって画面だけで向きとれるんじゃね…?

ふと思いました。あれ、これ別にPlane使う必要なくね…?

何せ2Dの場合は上から視点、距離は固定ですしなんか問題なく使えそうです。

ということで、Rayを飛ばすのではなく、キャラクターの位置をScreenSpaceに直して回転を求めてみます。

gist.github.com

f:id:tsubaki_t1:20170702230940g:plain

これはこれで…

*1:同人系は逆(右手カーソル移動の、左手ZXC操作)

【Unity】アプリ内課金やAssetBundle、ランキングの例を含むサンプルゲーム、Endless Runner Sample Game

f:id:tsubaki_t1:20170620224446j:plain

https://www.assetstore.unity3d.com/#!/content/87901

少し前に、Unity公式の完成プロジェクト「Endless Runner Sample Game」が公開されてました。

Endless Runner Sample Gameといふゲーム

Endless Runner Sample Gameは、モバイルで良く見たエンドレスラン系のサンプルみたいです。

ゲームを開始すると、割と良い感じのアニメーション(猫がねずみを追い回す)が始まり、キャラクターセレクトっぽい画面、そしてエンドレスランのゲーム画面に遷移します。

f:id:tsubaki_t1:20170620225402j:plain

割と作り込んでいて、IAPやランキングの機能が含まれてるっぽいです。

 

 

ダウンロードはこちら:https://www.assetstore.unity3d.com/#!/content/87901

AssetBundleでキャラクター・ステージの切替

このゲーム、キャラクターの読込はAssetBundleで行われており、多分自分で追加したりする事も出来るんじゃないかなと。

f:id:tsubaki_t1:20170620230450j:plain

ただAssetBundleの分割は割と適当で、重複アセットありまくりです。

まぁ複数のキャラクターを同時に読む事は無いので問題は無いのですが、キャラクター切替等にアセット再読込が走るので、割とロードは間抜けです。

(黄色い▲が付いてるアセットが他のAssetBundleと重複して読み込まれる可能性のあるアセット)

f:id:tsubaki_t1:20170620231647j:plain

ちなみにAssetBundleにキャラクターが格納されているので、AssetBundle ManagerのSimulater ModeでAssetBundleの振る舞いをシュミレーションするか、Local AssetBundle Serverでローカルサーバー立ててAssetBundleをダウンロードする形にしないと、動作しません。

f:id:tsubaki_t1:20170620232002j:plain

多分ステージもAssetBundleです。

昼と夜のデータを持つと思われるScriptableObjectがAssetBundleに格納されています。中にはオブジェクトの生成パターンや、空・フォグの色等が格納されていました。

f:id:tsubaki_t1:20170620235031j:plain

Shaderで色々な画面効果

このプロジェクト、結構シェーダーを使いまくっています。

例えばオブジェクトの回転は頂点シェーダーでぶん回していますし、

f:id:tsubaki_t1:20170620230809g:plain

遠方を表示しない(地球の表面のように歪んで見える)もシェーダーでやっています。湾曲率はShader.SetGlobalFloatで一括制御。クール

f:id:tsubaki_t1:20170620231028j:plain

また幾つかの箇所は頂点カラーで色を塗っているっぽいです。

ちゃんと見てないですが、下の壁とかは「頂点カラーでレンガの色を塗る」「レンガの模様はテクスチャを繰り返して実現」といった感じでしょう。多分

f:id:tsubaki_t1:20170620231315j:plain

Animatorは割とシンプル

Animatorは割とシンプルです。

基本的に右側のジャンプ・スライディング・ランはBOOLで管理しています。これはボタンを連打した時もトリガーがつけっぱなしにならない為でしょう。

HITが呼ばれたらANY STATEからHITが呼ばれて、HPが無ければDEATHに移行する等。

f:id:tsubaki_t1:20170620232740j:plain

基本的に RUN LOOPで走りますが、ジャンプすると着地後 run startから再スタートする点、スライディングは速やかにrun loopに戻る点は中々面白いです。

ちなみにRun LoopがBlend Treeで作られてる辺りに、努力の跡を感じます。

f:id:tsubaki_t1:20170620233026j:plain

その他諸々

オーディオは普通に音を鳴らしたり、UIから直接呼び出したりしてました。

f:id:tsubaki_t1:20170620233707j:plain

UIはソコソコちゃんとレイアウトシステム使って組んでます。

f:id:tsubaki_t1:20170620233957j:plain

感想

割と中々悪くないんじゃないでしょうか。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】ScriptableObjectを使用した、コンポーネント・シーン間でのデータ共有について

今回はScriptableObjectを使用したComponentやScene間でのデータ共有についてです。なお、この記事は30回近く書き直した*1という曰く付きの記事で、場合によっては消えるかもしれません。

ScriptableObject

ScriptableObjectはScene・Prefab以外のUnityエディターで値を調整する事が可能なオブジェクトです。Unityエディター上で値を調整する場合、ScriptableObjectやScene・Prefabを使用すると割と簡単に調整、値の利用が可能です。

ソレ以外の場合は、自前のシリアライザを用意する必要があります。

ScriptableObjectはアセットでありインスタンスである

ScriptableObjectのアセットは「アセット」ですが同時に「インスタンス」でもあります。この特徴は少し面白く、例えば複数のPrefabやSceneから参照された場合でも、勝手に複製されず単一のインスタンスとして保持されます。

 

例えば、Scene AとScene Bという二つのシーンがあり、それぞれOBJ 1とOBJ 2というオブジェクトを持っているとします。
ここでOBJ 1とOBJ2が同じScriptableObjectを参照していた場合、OBJ1が参照しているScriptableObjectとOBJ2が参照しているScriptableObjectは同一のものです。

f:id:tsubaki_t1:20170619214942j:plain

この仕組みを利用して別SceneのAudio SourceとButtonのEventTriggerを接続したのが、以前紹介した「ボタンを押して音を鳴らす方法」でした。
このアプローチでは、FindやStatic等は一切使用しておらず、動的にコンポーネントコンポーネントを紐付けています。

https://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20170614/20170614000458.jpg

tsubakit1.hateblo.jp

 

二つのオブジェクトでデータを共有する

実際にデータを共有してみます。

f:id:tsubaki_t1:20170619220901j:plain

まずは赤と青の二つのText、そして白のButtonを用意しました。動作としては

  • 赤と青のTextはScriptableObject内のカウントを監視し、テキストに表示
  • 「ADD」Buttonを押すと、ScriptableObjectのカウントが増える

という単純な物です。うまくいくと、ADDボタンを押すたびに赤と青の両方のカウントが1ずつ増えます。

 

なお今回紹介する内容は、その気になればstaticやsingletonで代用出来ると思います。強いて違いを上げるとするならば

  • Findやstaticを使用せずオブジェクトを参照出来る
  • エディターで値が調整出来る
  • インスタンスの破棄が出来る
  • UnityEventベースで色々出来る

辺りでしょうか。

実際にScriptableObjectにデータを格納して共有する

まずはScriptableObjectと、ScriptableObjectを監視してTextを更新するコンポーネントを作ります。

gist.github.com

作成したコードから、ScriptableObjectのアセットを作成しておきます。

f:id:tsubaki_t1:20170619221649j:plain

 

後はClickCountUpdaterのコードに作成したClickCountを設定、設定した物を赤と青のTextオブジェクトに貼り付けます。

先にコードに登録しておくと、コンポーネントを登録するときに自動的にScriptableObjectの参照が完了するので、少し楽です。

f:id:tsubaki_t1:20170619222128j:plain

 

tsubakit1.hateblo.jp

 

後はClickCountのAdd()メソッドをButtonのEvent等から呼び出してやれば、Buttonから赤と青のTextの数字を更新出来ます。

f:id:tsubaki_t1:20170619222358j:plain

f:id:tsubaki_t1:20170619220617g:plain

 

このアプローチでは、オブジェクトは生成した瞬間にScriptableObjectを通して参照を構築します。なので、別々にインスタンス化しても問題なく動作します。

下の図では、青いTextとButton、赤いTextの順番に追加しています。特に参照を行うコードは無いですが、問題なく動作しています。

f:id:tsubaki_t1:20170620001246g:plain

ResourcesからLoadしたインスタンスは一つ、ただAssetBundleは注意

今回のサンプルでは依存関係をエディターで設定しましたが、Resourcesから取り出した場合も同様に単一のインスタンスとして扱われます。

例えば下の青と赤のテキストは個別にResources.LoadでScriptableObjectを取得しますが、値が同じです。Resourcesから取り出したインスタンスは、破棄されない限り同一だと分かります。

gist.github.com

f:id:tsubaki_t1:20170620002743g:plain

AssetBundleの場合も基本的には同じですが、暗黙的参照でインスタンスを参照していた場合、異なるアセットとして扱われるかもしれません。複数のAssetBundleをまたぎそうな場合は、別AssetBundleに明示的に格納しておくことが良さそうです。

Find Reference In SceneでScriptableObjectを参照するオブジェクトを見つけ出す

シーンでScriptableObjectへの参照を持つアセットを探したい場合、Find Reference In Sceneで見つけられます。

ただPrefab化したオブジェクトが参照していた場合、Prefabに含まれる全てのオブジェクトがヒットしてしまうので使い所が難しいです。

f:id:tsubaki_t1:20170620001933g:plain

エディターでアセットのインスタンスに書き込むと、ファイルも更新される問題

さて、アセットのインスタンスを用いてデータを共有しましたが、実はこの方法は一つ問題があります。アセットの値を更新するという事は、ファイルも更新されるという点です。言い換えれば、Sceneの再生を終了しても数値は元に戻りません。

 

下の例では、シーンを一度停止しているにも関わらず、再度再生すると途中から数値の加算が行われています。プレイヤーでは問題はないのですが、エディターだと問題になります。

f:id:tsubaki_t1:20170619222818g:plain

案1:Inspectorで使用する初期値と実際の値を分ける

この問題を回避する方法で一番まともなのは、ScriptableObjectにInspector表示用の値と実際にゲームで使用する値の二種類を用意し、OnEnableのタイミングで値を流し込む事です。

gist.github.com

またOnValidateを定義しておくことで、再生中にもInspectorから値を書き換えられるようになっています。

案2:再生停止時にScriptableObjectをUnloadする

案2はScriptableObjectがアセットでありインスタンスである事を利用します。ゲームの再生停止時にScriptableObjectをUnloadしてしまえば、再生中に変更した値は無かったことになり、ScriptableObjectの値がファイルに書き込まれる事は無いという事です。

 

まずはResettableScriptableObjectをプロジェクトに配置します。これはシーンの再生停止時に自身のインスタンスをアンロードする…という挙動を持ったScriptableObjectの派生クラスです。

これを継承する事で、シーン再生時にScriptableObject値が最後にProject Saveした際の値まで巻き戻ります。

gist.github.com

要するに、もし値を確定させたい場合は、Project Saveを実行しないと、巻戻ります。これはResettableScriptableObjectコメントアウトしてる部分を戻せば回避出来るのですが、どうもUnityエディターを巻き込んでクラッシュする事がある(条件は不明)ので、Project Save推奨です。

f:id:tsubaki_t1:20170619224839g:plain

ScriptableObjectの保持範囲は、アセットが開放されるまで

少し気になるのが、ScriptableObjectを保持する範囲です。

これは単純に「ScriptableObjectのアセットが開放されるまで」となります。

例えば、シーンをロードした際に、次のシーンでも同一インスタンスのScriptableObjectを参照していた場合、ScriptableObjectの値は保持されます。

またUnloadUnusedAssetsを呼ばれた際に、どこからかScriptableObjectへの参照がある場合、同様にScriptableObjectの値は保持されます。

f:id:tsubaki_t1:20170619230259j:plain

なお、エディターだとシーンを跨いだ場合でもScriptableObjectが開放されない事があります

アセットを勝手に開放させない方法

シーンを跨いだり、どこからも参照されない状態でUnloadUnusedAssetsを呼ばれるとアセットが開放される事があります。

アセットを開放させない方法は、思いつく限り3つあります。

  • 何処からか常に参照する。例えばstaticにでも登録してやれば開放されない。
  • PreLoadAssetsに登録する。挙動的にどうも開放されないらしい
  • HideFlags.DontUnloadUnusedAssetを設定す

積極的なアセットの開放は、Resources.Unload。しかしC#インスタンスが開放される訳ではない

もし値を積極的にリセットしたい場合、シーンをロードする直前でResources.Unloadでインスタンスを開放してやると、次のシーンでマッサラな(アセットからデシリアライズしたての)ScriptableObjectを受け取れます。

 

例えば下の図では、二つのシーンを行き交っています。この際、連続してScriptableObjectを参照しているため、二つのシーン間で値を保持していますが、Unloadを押すとScriptableObjectが開放され、次のシーンでは値は0に巻き戻っています。

f:id:tsubaki_t1:20170619231515g:plain

この時、C#のScriptableObjectインスタンスが開放される訳では無い点には注意が必要です。ScriptableObjectは一旦破棄されますが、ScriptableObjectがデシリアライズして生成されたC#インスタンスはそのまま保持されます。

つまり、EventやC#的に直接参照している物は、Unload後もアクセス出来る事を意味します。

下の図では、左がButtonからイベント呼出、右はスクリプトC#)がScriptableObjectへの参照をキャッシュして呼び出しています。左はアンロード時に押せなくなりますが、右はアンロード後も押せます。f:id:tsubaki_t1:20170619233052g:plain

追記:ScriptableObjectをCreateInstanceすべきか、クラスを使うべきか

今回ScriptableObjectについて書きましたが、こうなると動的なクラス生成の運用でも、単純なクラスの使用は止めてScriptableObjectを使用すべきか考えるかもしれません。

個人的な考えでは、

  • エディターでパラメータを修正するようなケース
  • ホットリロードを行うようなケース

以外では、動的なインスタンスは通常のクラスを使用する方が良いのではないかなと思います。

逆に、エディターで既にインスタンスを作成しており、複数シーンやオブジェクトで共有するようなケースの場合、ScriptableObjectはかなり良いです。

感想

ScriptableObjectを用いたデータの共有でした。

アンロード(アセットの開放)周りが少しややこしくなるので、面倒なら避けるのは正しい選択かもしれません。

 

強いていうならば、ボタンを押したら音が出る…を、出来る限りコードを書かずに実現する で紹介したような、オブジェクト間の接続では割と有益かもしれません。このアプローチでは「送信先・送信元」のどちらも存在する場合に使われるので、

  • 意図的にUnloadしない限りScriptableObjectはアンロードされない
  • どちらも無い場合はアンロードされる
  • 再ロード時も参照関係しか保持しないのでパラメータが失われる等の問題は無い

と、悪くないです。

 

もしくはデータはシーン内のGameObjectが持ち、メッセージのやり取りのみを行わせる等でしょうか。

関連

www.slideshare.net

tsubakit1.hateblo.jp

今回の話を考えたきっかけ

www.urablog.xyz

 
 

*1:(同タイトルの下書き記事も4つある)