ラベル Unity の投稿を表示しています。 すべての投稿を表示
ラベル Unity の投稿を表示しています。 すべての投稿を表示

2016年8月8日月曜日

Unity関係のTwitter postまとめ(~2016/08/05ぐらい)

今日のUnityTipsを中心としたUnity関係のツイートまとめ

ポーズ処理のスクリプト


GetComponentsInChildrenでMonobehaviourを全部無効にするみたいな処理。ので、コルーチンは止まらない。コルーチンを一時的に止める方法があったら教えて欲しい。


一度に複数のInspectorを表示する


InspectorからInspectorへのドラッグアンドドロップができるので作業効率が上がる。ゲームオブジェクトやアセットを右クリックしたときに新しいInspectorをLock状態で表示する拡張が欲しい。気が向いたら作る。


StandaloneビルドやWebGLビルドでマウス入力を完全無視する


StandaloneInputModuleの代わりに使うスクリプト。StandaloneInputModuleからマウス関係の処理を機械的に削除しただけ。


2D用接触判定スクリプト


[Unity][2D]接地判定するスクリプトの強化版。
上下左右にBoxcastする。


Editor拡張でRichTextを使う


Editor拡張でも使えるよっていう機能紹介。


uGUIオブジェクトの透明化



CanvasGroupの機能紹介とAlphaをスライダーでいじれるようにするエディタ拡張。


軸に沿ったオブジェクトの反転


スケールに-1掛けると反転する。回転を使うよりも簡単で精度がいい。


スナップ移動、回転、拡大縮小



機能紹介。


RectToolのショートカット


Sceneビューでごりごり編集するときは作業効率が上がる。


Inspectorのデバッグモード


privateな変数の中身を参照するためによく使う。あとスクリプトからRectTransformを触るときに対応がわからなくなることがあるのでそのときも使う。


配列、リストのInspector操作


最近まで知らなくて不便を感じていた。知らないと真ん中らへんにある要素を削除するのがとても大変。


テストを書く


zunko is Kawaii が書きたかっただけ。
ゲームプログラミングではインターフェース周りの処理とロジックが密に結びついているケースが多くてあまりテストを書く習慣が無い印象。
実際私は趣味プログラミングではめんどくさくて書かない。


uGUIのアウトラインとシャドー



アウトラインは1pxまでしかまともに機能しないと思っていい。あと重い。
改善版アウトラインは探せば出てくる。


SpritePacker



機能紹介。実際効果的。SetPassのコールが目に見えて減る。
AssetBundleとの相性はあまり良くないと聞いたことがあるけど良く分からない。
Spriteを1枚だけ読み込むことができなくなるからかな?


GetComponet系メソッドで基底クラスの指定



ちゃんとクラス設計する意味はある。
型を指定するタイプのメソッドは似たようなことができるのでScriptableObjectとかもちゃんとクラス設計しよう。


オブジェクトを点滅させるスクリプト


ダメージ受けたときとかに使うやつ。
GetComponetsInChildrenでRenderer系を取ってきて点滅させる。


Shurikenのスケーリング


ScalingModeを設定してなくて上手くスケーリングできずに半日悩んだ。


UnityEvent



空クラスを実装することで引数付きイベントもInspectorで扱える。但し引数のクラスはシリアライズできる必要がある。
確か4引数ぐらいまでいけたはず。


TrailRenderer


TrailRendererの機能紹介。プロトタイプ段階ではよく使う。


Phisic Material, Phisics 2D Material




摩擦と反発の機能紹介。知らずに1から開発しようとしたことがある。
知った後でも自前で摩擦を実装したりしているけど...

2016年6月27日月曜日

[Unity][2D]接地判定するスクリプト

ローグライクの製作に疲れて息抜きにアクションゲームを作っているので、アクションゲーム用です。
キャラクターが空中に居るかどうかを判定する接地判定のメモとスクリプト。

前提

  • 接地判定用のBoxCollider2Dが存在する(キャラクターの当たり判定の流用でもOK)
  • 地形のColliderにLayerが設定されている

手順

UnityにはPhysics2D.BoxCastというメソッドが存在します。
このメソッドは特定の方向に四角いあたり判定を飛ばして、衝突を検出するメソッドです。Raycastのぶっとい版です。
(類似品にCircleCastや3D用のPhysics.BoxCast,Physics.SphereCastなどもあります)
このメソッドのdistanceパラメータで飛ばす距離を指定できるので、ごく短い距離のBoxCastを垂直下向きに飛ばして地面と接触したかを見れば接地判定できます。

但しひとつ注意点があって、Castを飛ばす元がすでに地形と接触している場合、BoxCastが反応せずにすり抜けてしまいます。
この現象はテラシュールブログさんの記事に詳しいです。
これを回避するために、BoxCastはBoxColliderの中心ではなく少し上から発射するように設定します。

スクリプト

というわけでスクリプトです。
このスクリプトをアタッチするとお手軽に接地判定ができるぞ。

2016年5月16日月曜日

[UnityEditor拡張]Inspectorの配列のラベルをenumに差し替える

Inspectorに配列やリストを表示すると要素のラベルが「Element 0」「Element 1」...になるのをenumで置き換えるエディタ拡張を作りました。
上を下に置き換える。


使い方

配列のフィールド属性にEnumLabelを付けるだけ。引数はtypeof(enumの型)。


ソースコード

配列要素のlabelの「Element 0」の数字部分をパースして対応するenumに置き換えているだけ。


問題点

sizeの入力を縛っていないので、enumの定義数と配列の要素数に関係がありません。
要素数がenumの定義より多い場合はこうなります。

PropertyDrawer.OnGUIの引数のpropertyで取れるのが配列要素のpropertyなので、配列全体のGUIを制御することができずこの実装となりました。
配列やリスト全体の表示を描き換える方法ってないんですかね?

2017/12/23追記

コメントにて指摘をいただきましたので反映いたしました。ありがとうございます!
EditorGUILayout#PropertyField()はbool値で子要素を展開するかを指定できるようです。常にtrueでいいような...
ちなみにこの記事の内容は古くて最新のUnityに対応しているかはわかりません。ちゃんと動作させた環境の情報は書こうな...

2016年5月9日月曜日

[Unity]ミニマップ(レーダー)をuGUIとして表示する

RenderTextureを使ってミニマップをuGUIとして表示するおはなし。
少し引っかかったのでメモ。
例は2Dだけど3Dにも適用できるはず。

ミニマップっていうのはコレのこと。

手順

仕組みはすごく単純です。
ミニマップ用のカメラを用意して、カメラでレンダリングされたイメージをテクスチャとして持っておき、RawImageで表示するというだけです。
  1. ミニマップ用のカメラにだけ写るオブジェクトを作る
  2. RenderTextureを用意する
  3. ミニマップ用のカメラを用意し、TargetをRenderTextureに設定する
  4. CanvasにRawImageを追加してTextureをRenderTextureに設定する

ミニマップ用のカメラにだけ写るオブジェクトを作る

ミニマップに表示したいオブジェクトの子要素として新しいGameObjectを作ります。
GameObjectに各種Rendererをアタッチしてカメラに写るようにします。
また、GameObjectのLayerにミニマップ用のレイヤーを追加して設定しておきます。
メインカメラのCullingMaskをからミニマップ用のレイヤーのチェックを外しておきます。

RenderTextureを用意する

プロジェクトビューからAssetsに新しいRenderTextureを追加します。
RenderTextureのサイズは表示したいミニマップの解像度を設定します。(今回の例では表示領域が400x300と決まっているので400x300に設定)

ミニマップ用のカメラを用意し、TargetをRenderTextureに設定する

ミニマップ用のカメラを作ります。
このカメラにはミニマップ用のレイヤーのみが写るようにCullingMaskを設定します。
TargetTextureを作成したRenderTextureに設定しておきます。このカメラでレンダリングした結果がRenderTextureに保持されるようになります。
ちなみに、カメラのアスペクト比はRenderTextureのサイズで決まるようです。

CanvasにRawImageを追加してTextureをRenderTextureに設定する

UIオブジェクトを追加し、RawImageをアタッチします。
RawImageのTextureに作成したRenderImageを設定します。

まとめ

  • ミニマップ用のカメラを用意しレンダリング結果をRenderTextureに描き出す
  • RawImageでRenderTextureを表示する

2016年5月2日月曜日

[Unity]ポリモーフィズムなScriptableObjectのJSONシリアライズに挑戦する

クラス階層構造のあるScriptableObjectをJSONシリアライズしたいというお話。
もっといい方法があるなら教えてください。切実に。

シリアライズされたオブジェクトは型情報を含まない

含みません。ということはデシリアライズするときに意図した型に復元することはできません。
Itemクラスを継承したWeaponクラスのインスタンスをシリアライズしたとして、
復元時にはそのアイテムが武器なのか消費アイテムなのか判断がつかないということです。

ゲーム中に変更の無い情報はアセットのパスをシリアライズすれば事足りる

考えた構造がこちら。
SerializableItemクラスはシリアライズ専用のクラスです。
ItemのインスタンスをResourcesに保持しておき、そのパスをアイテムに持たせておくという構造です。
これによって、ScriptableObject.Instantiate(Resources.Load(assetPath))で型情報を復元することができます。

型名をシリアライズしておいてリフレクションで復元するという方法もありますが、固定の情報(ここでいうとIronSwordのpower)のリストアが手間だったりするので、こちらのほうがスマートでしょう。

インスタンスにいちいちassetPathを設定するのが面倒と思われるかもしれませんが、エディタ拡張で自動化すれば手間にはなりません。
projectウィンドウが更新されたときにすべてのItem型アセットを更新して保存するようにします。


ゲーム中で動的に変化する情報はどうする?

例えば武器に強化値があったとします。この情報はゲーム内で動的に変化するため、アセットに持たせるわけにはいかず、シリアライズの必要があります。
これに対してはいい方法が思いついていません。一応動作する実装は可能ですが...

現状の実装はこんな感じになっています。
サブクラスに固有のフィールドをExtensionクラスのインスタンスとkeyで紐付けて保持する。
で、シリアライズするときはオーバーライドされたCreateExtensionsでExtensionのリストを作ってSerializableItemに持たせ、デシリアライズするときはオーバーライドされたRestoreExtensonsで各フィールドに値を設定しなおすってことをやってます。
typeのenumはint, float, bool, stringの4種類で事足りるはず。(足りないものはJson化してstringとして保持することが可能なはず)

まとめ

  • アセットのパスをシリアライズしとけば型を復元できるよ
  • もっといい方法があれば教えてください

2016年4月19日火曜日

[Unity]ScriptableObjectをモデルクラスとして使う

ScriptableObjectでデータを定義しつつそのままモデルクラスとして扱ってしまおうというお話。
ゲームロジックと表示部分の分離ができるようになります。

何はともあれ作ってみる

突然ですが、敵を表現するクラスをScriptableObjectで作ります。ScriptableObjectで作るとEditor上でレベルデザインができるようになります。*1
敵はそれぞれがHPとPowerを持っているとします。...できました。なんとなくダメージを食らうメソッドを持ってます。


早速3種類のデータを作ってみました。

さて、ここで作ったのは敵の種類であるため、HPが減ったからといってアセットファイルを書き換えたくはありません。*2
また、それぞれの種類の敵は複数居るかもしれません。スライム2匹とか。
なので、これらのScriptableObjectは複製して使用することにします。ScriptableObject.Instantiate()で複製が可能です。複製した後はHPが減ろうがどうなろうが好き放題です。
とてもいいですね。

GameObjectにくっつける

これをGameObjectにくっつけて使いたい場合はこうします。汎用ジェネリクスクラス。

こっちは実際に利用する用のクラス。

GameObjectのインスペクタにアセットファイルを指定すると、OnAwakeで複製して、以降modelからインスタンスにアクセスできるようになります。
m_modelにアクセスするとアセットが書き換えられちゃうので注意です。
すでに複製されたオブジェクトを取り扱う場合にはmodelをpublicにして露出させちゃえばOKです。

MonoBehaviourとScriptableObjectが別々のスクリプトになるので、ゲームのロジックと表示処理を分離することが可能です。これがやりたかった。

複製されたオブジェクトに固有の動的データを取り扱う

静的なデータだけでなく動的なオブジェクトの状態を持たせることもできます。
例として、攻撃を受ける度に強くなるダルマという敵を考えて見ます。ダルマの仕様はこんな感じ。
  • ダメージを受けるとPowerが1上昇する
  • 3回ダメージを受けると残り体力が0になる
ここでは簡単のためにEnemyクラスを継承します。*3*4...こんな感じ?

privateメンバはインスペクタ上に表示されないので、敵のパラメータの設定の際に邪魔になりません。もし、damageCountを外のクラスに公開したい場合は、HideInInspector属性や自動実装プロパティを使うといい感じです。
ダルマが2匹居てもそれぞれが別の状態を持つことができます。

初期化

いろいろ実装してHPが回復できるようになったとします。前のままではHPは際限なく回復させることができるのでEnemyクラスに最大HPを保持させることにしましょう。現在のHPは最大HPで初期化したいですよね?
OnEnableを書いておけばInstantiateのタイミングで初期化されます。但し、UnityObjectの例に漏れずパラメータ付初期化はできません。


まとめ

  • ScriptableObjectをモデルとして使うときはInstantiateで複製しましょうね
  • ゲームロジックと表示処理を分離できるよ
  • パラメータとして設定したいメンバはInspecterに表示して動的なメンバは隠しましょう
  • OnEnableで初期化できるよ

*1: 他の方法としてはCSVとかExcelに敵を定義していく方法やPrefabを使う方法がある。他にもあるかも? それぞれ利点があるので一概にどれが良いとはいえない。

*2: Rsources.Load()で読み込んだScriptableObjectはスクリプト上からファイルを書き換えることが可能(但しゲーム起動時にリセットされる)。この性質を利用してシーン間をまたがるデータをScriptableObjectに持たせて管理する方法があったりする。

*3: ScriptableObjectは継承してポリモーフィズムを利かせることができるのが強みのひとつ。今回のケースではSubEnemyはEnemyと同等に扱うことができる。

*4: 後々の拡張を考えた場合、継承は安易に使うべきではないというのが俺ルール。継承だと複数の特性を組み合わせたりするのが難しいし。今回のケースだと「敵特性」から派生したダルマ特性ってScriptableObjectを作成して、Enemyは複数の敵特性を持っているっていう感じの実装にすると思う。最良の選択など無い。

2016年4月14日木曜日

[UnityEditor拡張]Editor上でスクリーンショットを連続で撮れるようにする

前回の発展形、UnityEditor上で再生中のゲームのスクリーンショットを撮影しまくるエディタ拡張です。



つかいかた

メニューの Extension -> Sequence Screenshot を選択すると上の画面が開きます。
あとはゲームの再生中に「Capture Start」を押すと連続でスクリーンショットを撮ってくれます。
ボタンとゲーム再生の手順は逆でもOKです

Start Capture / Stop Capture
撮影の開始と終了を指示するボタンです。

Output Folder
キャプチャ画像の出力先フォルダのパスです。
下の「Select Folder」ボタンでフォルダ選択ウィンドウが開きます。

Filename Prefix
キャプチャ画像のファイル名の前につける文字列です。

Filename Suffix
キャプチャ画像のファイル名の後につける文字列です。

Interval
撮影の時間間隔です。単位は秒。
例えば、5を指定すると5秒おきにキャプチャされます。

Max Capture Count
最大撮影枚数です。0以下を指定すると無限に撮影し続けます。
指定された枚数の撮影が終わると自動的に「Stop Capture」が押された状態になります。

Identify Method
撮影されたスクリーンショットを識別する方法です。
  • 「Timestamp」ファイル名にタイムスタンプを使用します。
  • 「SerialNumber」ファイル名に連番を振ります

Timestamp Format
ファイル名にタイムスタンプを使用するとき、タイムスタンプの形式を指定します。

Offset
ファイル名に連番を使用するとき、最初の番号を指定します。

Reset Serial Number
押すと次に振られる連番の番号を「Offset」の番号に変更します。

Current Serial Number
次に振られる連番の番号です。

Filename Sample
ファイル名はだいたいこんな感じになるよーっていう見本です。


ソースコードはこちら。

2016年4月12日火曜日

[UnityEditor拡張]Editor上でスクリーンショットを撮れるようにする

UnityEditorで再生中のゲームのスクリーンショットが撮りたかったので
初のEditor拡張にチャレンジ...っていうほどでもなく簡単にできた。



このメニューをクリックすると、再生中でなければ何も起きず、
再生中ならばプロジェクトのルートディレクトリに現在の日時.pngを生成します。

ソースコードはこちら。3行。