20210829のUnityに関する記事は11件です。

SIMPLYGONを使ってUnity上でローポリ化してLoDする。

はじめに Unityでポリゴン数の多いモデルをそのままインポートしていたら、だんだん性能が遅くなってきたのでローポリ化&LoD(Level of Detail)をやろうと思いたったのでやりました。 SIMPLYGONのインストール Unityでローポリ化する方法を検索するとSIMPLYGONが良さそうな感じです。 環境構築はこちらの記事をほぼそのまま実施すればよいです。私はUnity 2020.3.15f2、SIMPLYGON 9.1、USD3.0.0-exp.1 で試しています。こちらの記事ではUSD2.0.0-exp.1が推奨されていますが、2021/8/29現在ではUSD3.0.0-exp.1で問題ないようです。 Simplygon.Unity.EditorPlugin.dll のコピー先をAssets直下ではなく Editor/Plugins もしくは Plugins/Editorにすることで、ビルド時のエラーを回避することができます。これは、using UnityEditor; を含むDLLを作るときに気をつけること を参考にしました。 ポリゴンの削減 正しくインストールできていれば Windows>Simplygon というメニューが存在するはずです。これを開いて、ポリゴンを削減したいオブジェクトを選択します。Add LOD Componentボタンを押してTemplete>Basic>Reductionを選択し、下の方のボタン(黄色い半円2つ)を押すとポリゴンの削減処理が走ります。Run Modeが Run in New Processだと何故かエラーで止まるので、Run in This Processに変更しておきましょう。 うまく変換できるとこうなります。左が1.9M Trianglesのモデルで、右がポリゴン削減後の0.94M Trianglesのモデルです。正直Wireframeで見ても違いはよくわかりません。 変換時に気を付けるべきこと 変換にはものすごく時間がかかりますので、あらかじめ失敗するケースを知っておくほうがよいでしょう。例えば、選択したオブジェクトの中に、MeshRendererの表示を外したオブジェクトが含まれている場合、Simplygonは何故か表示設定を無視して変換するため、下図のような変換結果になってしまいます。ということで、変換前に不要なMeshRendererを削除することをお勧めします。 劇的にローポリ化したい場合 Templete>Basic>Remeshing with material baking を選択して変換することで、大まかな形状とマテリアルを維持したまま、非常に軽量なモデルに変換することができます。下図の例では変換後は13858 Triangles、マテリアルは1つに圧縮されています。 穴が開いているモデルの場合 Templete>Basic>Remeshing with material baking だと穴がふさがってしまうため、Templete>Advanced>Remeshing を用いて、Remeshing>Hole Filling をDisabledにすることで穴を維持することができます。 例えば、本体の真ん中にボタン用の穴を残したい場合は、下図のように変換したあと、 ローポリ化していないボタンをはめ込むことができます。このケースでは穴の中にLightを置いてLEDを再現するような実装をしていたので、こういう方法で変換する必要がありました。 LoDの設定 UnityでLoDを行うには LOD Groupを使います。親となるGame ObjectにLOD Groupを設定し、その下に変換したモデルを_LOD0、_LOD1、_LOD2のように名前をつけて管理します。_LOD0を LOD GroupのLOD0にドラッグドロップし、同様に_LOD1、_LOD2もドラッグドロップすることでどのLODの時に何を描画するのかをUnityに伝えます。Gismosを表示することでどのLODを表示しているかが分かるので、あとはカメラからの距離に応じてどのLODを表示すべきかを適切に設定してください。 LOD1を80%としたときに、LOD0からLOD1に切り替わるのはだいたいこのくらいの大きさです。 LOD Groupの注意点 LODのモデルが完成してから設定するのであれば問題は起こらないのですが、中途半端な状態でLODの設定をした場合にカメラをいくら近づけてもLOD0にならないとか、逆にLOD0から変わらないというような問題が起こるときがあります。その場合は LOD GroupのRecalculate Boundsボタンを押すと治ることが多いです。Recalculate Boundsで設定した大きさを使って選択すべきLODを判定しているので当たり前なのですが、意外と気づきづらいです。 LOD GroupをアタッチすべきGame Objectにも注意が必要です。RigidBodyで動かす物体の場合は、RigidBodyの親につけてしまうとBoundsが変更されず、LODがおかしくなるのでRigidBodyがついているGame Objectに設定してください。 さいごに SIMPLYGONとLODに関しては意外に纏まった情報が無かったのと、罠がいっぱいあったので記事として残しておきます。参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【UniRx】Subjectをコンポーネントのインスタンス変数にした場合の寿命

はじめに Component.UpdateAsObservable()等のUniRxの拡張メソッドは、そのコンポーネントが付与されたGameObjectのDestroyのタイミングでComplete状態に移行するようになっています。 このため、以下のようにAddTo(this)は不要です。 public class SomeComponent : MonoBehaviour { private void Start() { this.UpdateAsObservable() .Subscribe(_ => DoSomething()); // .AddTo(this) // ↑ GameObjectのDestroyでCompleteになるのでAddTo(this)は不要 } } では自作のSubjectはどうなのでしょうか? ストリーム停止条件を記述しない場合 ストリーム停止条件を記載しない書き方 public class EventFiringComponent : MonoBehaviour { private readonly Subject<bool> someSubject = new Subject<bool>(); public IObservable<bool> SomeObservable => this.someSubject; } public class SubscribingComponent : MonoBehaviour { public void SubscribeEvents(EventFiringComponent component) { component.SomeObservable.Subscribe(_ => DoSomething()); } } まずはシンプルに書いた場合の挙動を考えます。 この場合、ストリームが停止するのは、Subjectを持つコンポーネントが付与されたGameObjectがDestroyされた後にGCが走るタイミングとなります。 それはDestroy後すぐかもしれないし、アプリケーション終了のタイミングかもしれません。 メモリ使用制限が厳しい実行環境ではこれがネックとなる可能性はありますが、記述量の少なさや装飾が少ないことによる可読性の高さといった利点を考えると、この書き方を採用する場面も往々にしてあると思います。 コンポーネントと寿命を同じにする方法 Component.UpdateAsObservable()等と同じようにコンポーネントの付与されたGameObjectのDestroyでストリームが停止するようにするためには、以下のように書くことになります。 ストリームソースの寿命をコンポーネントに合わせる書き方 public class EventFiringComponent : MonoBehaviour { private readonly Subject<bool> someSubject = new Subject<bool>(); public IObservable<bool> SomeObservable => this.someSubject; private void OnDestroy() { this.someSubject.OnCompleted(); } } public class SubscribingComponent : MonoBehaviour { public void SubscribeEvents(EventFiringComponent component) { component.SomeObservable.Subscribe(_ => DoSomething()); } } これでDestroyのタイミングでストリームソースがComplete状態に移行することになり、Component.UpdateAsObservable()等と同じライフサイクルになります。 「UniRxの拡張メソッドとライフサイクルが同じ」、「コンポーネントの寿命とストリームソースの寿命は同じ」という仕様のわかりやすさは非常に魅力的です。 また、UniRx内ではComplete状態になると不要になった内部の参照を切るように工夫されており、メモリ効率が上がることも期待できます。 しかし、1行のみとは言え、Subjectを多く保有しているクラスでは記述量の増加が結構辛いのと、宣言箇所と停止条件を書く場所が離れるので、記述漏れが発生しそうで怖いです。 シーン内の一部品では過剰設計となるかもしれませんが、実行環境のメモリ使用制限が厳しい場合やフレームワークなどの汎用部品では使えそうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】1文字ずつメッセージを表示する

UNDERTAL●やどうぶつの●のようにキャラクターがしゃべる時のアレです。 コピペ用 そのままコピペしてプロジェクトのどこかにおいてください。 MessageText.cs MessageText.cs using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; [RequireComponent(typeof(Text))] public partial class MessageText : MonoBehaviour { /// セリフ音 public AudioClip audioClip; Button _parentButton; AudioSource _audioSource; Text _text; float _charIntervalSec = 0f; bool _isWriting = false; int _lineIndex = 0; List<Line> _allLines; List<char> _muteChars; virtual public float charIntervalSec { get => 0.02f; } public void Start() { _audioSource = gameObject.AddComponent<AudioSource>(); _audioSource.playOnAwake = false; _audioSource.clip = audioClip; _text = GetComponent<Text>(); _allLines = GetAllLines(); _muteChars = GetMuteChars(); _parentButton = transform.parent.gameObject.GetComponent<Button>(); _parentButton?.onClick.AddListener(OnClickParentButton); ClearLines(); OnAppear(); } } public partial class MessageText { virtual public List<Line> GetAllLines() { return new List<Line>() { }; } virtual public List<char> GetMuteChars() { return new List<char>() { }; } virtual public void OnAppear() { WriteLine(); } virtual public void OnComplete() { Destroy(this.gameObject); } virtual public void OnClickParentButton() { PushAction(); } public void WriteLine() { _charIntervalSec = charIntervalSec; StartCoroutine(IEWrite(_allLines[_lineIndex].text)); } void ClearLines() { _text.text = ""; } void makeNewLine() { var line = _allLines[_lineIndex]; switch (line.lineType) { case LineType.SkipOn: _text.text += "\n"; break; case LineType.SkipOff: _text.text += "\n"; break; case LineType.Clear: break; } _lineIndex += 1; } void ReadNext() { makeNewLine(); var line = _allLines[_lineIndex]; switch (line.lineType) { case LineType.SkipOn: WriteLine(); break; case LineType.SkipOff: WriteLine(); break; case LineType.Clear: ClearLines(); // 再帰呼び出し ReadNext(); break; } } public void PushAction() { if (_allLines.Count - 1 <= _lineIndex) { OnComplete(); return; } if (_isWriting) { if (_allLines[_lineIndex].lineType == LineType.SkipOn) { _charIntervalSec = 0f; } } else { ReadNext(); } } System.Collections.IEnumerator IEWrite(string lineText) { _isWriting = true; foreach (char c in lineText) { _text.text += c; if (!_muteChars.Contains(c)) { _audioSource.Play(); } yield return new WaitForSeconds(_charIntervalSec); } _isWriting = false; } } public partial class MessageText { public enum LineType { SkipOn, SkipOff, Clear, } public interface Line { LineType lineType { get; } String text { get; } } public class SkipOn : Line { public LineType lineType { get => LineType.SkipOn; } public String text { get; } public SkipOn(String text) { this.text = text; } } public class SkipOff : Line { public LineType lineType { get => LineType.SkipOff; } public String text { get; } public SkipOff(String text) { this.text = text; } } public class Clear : Line { public LineType lineType { get => LineType.Clear; } public String text { get => null; } } } 使い方 Unityの画面で Button を作り、その子として自動生成される Text に以下のサンプル Sample.cs を貼り付けます。 Sample.cs using System.Collections.Generic; class Sample : MessageText { // 文字と文字の間隔秒数 (短いほど早い) override public float charIntervalSec { get => 0.02f; } // 表示するメッセージ override public List<Line> GetAllLines() { // SkipOn: スキップできるテキスト // SkipOff: スキップできないテキスト // Clear: ここで文字を全部消す return new List<Line>() { new SkipOn("おはようございます、これは1番目のメッセージです"), new SkipOn("こんにちは、これは2番目のメッセージです"), new Clear(), new SkipOff("こんばんは、これは3番目のメッセージです"), new SkipOn("おやすみなさい、これは4番目のメッセージです"), new SkipOn("これでメッセージの表示を終わります"), }; } // 音を出さない文字 override public List<char> GetMuteChars() { return new List<char>() { ' ', ' ', ',' , '、', '。', '「', '」', '!', '?', }; } // 表示されたとき override public void OnAppear() { // 最初の1行を書き始める WriteLine(); } // 最後のメッセージ表示した後 (クリック後) override public void OnComplete() { // 自分を削除 Destroy(this.gameObject); } // 親ボタンをクリックした時 override public void OnClickParentButton() { // アクションを進める PushAction(); } } この時点でゲームスタートすれば動きます。 サンプルの中身は好きなように変更してください。 Unityのインスペクタから Text を見ると音源をはめ込むところがあります。ここにセリフ音(mp3もしくはwavファイル)を設定してください。 画像やテキストの見た目も通常通りボタンの画像やテキストを変更すればOKです。 今回は以上です。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VFXGraphに複数のパラメータを渡して状態を切り替えるやつ

はじめに VisualEffectGraph12から別の方法(GraphicsBufferを用いる)でもできるようです。 そちらを調べる前に一旦今自分の知っているやり方を整理する目的で書いてます。 参考文献 動画 グラフの中身 軽く解説 上記画像の2枚目が重要です。 スクリプトからStateMapというテクスチャをこのVFXGraphに渡して、そこからパラメータを取り出すということをやっています。 今回の例ではパラメータは1コしか与えていませんが(タイトル詐欺)、1枚のテクスチャにつき4つのパラメータまで渡すことが出来ます。もっと多くのパラメータを与えたい場合はテクスチャの数を増やしていけばよいです(StateMap2とか)。 今回はStateという要素数9のintの値を取り出して、それにより色とサイズを変化させています。 テクスチャを渡す部分も一応コードを載せておきます。(ほとんど参考文献と同じですが...) ↓vfx側のスクリプト using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.VFX; public class MagicOrb : MonoBehaviour { VisualEffect vfx; //Orbの状態を表す  //0:緑色 1:赤色 2:青色 int[] orbState = new int[9]; public int[] OrbState { get { return orbState; } set { orbState = value; } } private void Start() { vfx = GetComponent<VisualEffect>(); } Color State2Color(int state) { return new Color(state, 0, 0); } Texture2D State2Map() { float r = Mathf.Sqrt(9); int size = (int)Mathf.Ceil(r); var tex = new Texture2D(size, size, TextureFormat.RGBAFloat, false); tex.filterMode = FilterMode.Point; tex.wrapMode = TextureWrapMode.Clamp; Color[] StateColor = new Color[size * size]; for (int i = 0; i < 9; i++) { StateColor[i] = State2Color(orbState[i]); } tex.SetPixels(StateColor); tex.Apply(); tex.name = "StateMap"; return tex; } public void SetMap2VFX() { vfx.SetTexture("StateMap", State2Map()); } } ↓キューブ側(VRコンテンツの手のイメージ)のスクリプト using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.VFX; public class OrbInteractor : MonoBehaviour { [SerializeField] bool left = false; [SerializeField] MagicOrb magicOrb; bool change = false; private void OnTriggerEnter(Collider other) { if (other.gameObject.CompareTag("Orb")) { //object名の数字部分を切り出す string name = other.gameObject.name; int num = int.Parse(name.Substring(4)); change = false; if (left && magicOrb.OrbState[num] != 2) { magicOrb.OrbState[num] = 2; change = true; } else if (!left&&magicOrb.OrbState[num] != 1) { magicOrb.OrbState[num] = 1; change = true; } if (change) { magicOrb.SetMap2VFX(); } } } } ちょっと補足すると、スフィアのエフェクトのある部分にRoot0,Root1...みたいな空のオブジェクトがあり、それらがコライダーをもっています。Root0とかのオブジェクトはエフェクトの初期位置を与えるのにも使用しています。(VFX Property BinderのMultiple Position Binder) 終わりに パラメータをテクスチャを介して渡す部分は参考文献が素晴らしかったので特に迷うことは無かったのですが、 グラフの方でpositionとして直接利用するのではなく、enum的にエフェクトの状態を切り替えるやり方はちょっと悩みました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フリーのUnityアセットを使って道路自動生成ツールを作成してみた

概要 フリーのUnityアセットを利用して,道路を自動生成するツール作成してみましたので,その内容を紹介します. フリーのUnityアセットでは,様々な道路パーツがあります.本ツールでは,道路パーツを組み合わせて,様々な走行環境を構築することを目指しています. 前提とする環境 Unity 本ツールは,Unityを前提としています. バージョンは,2021.1.16f 以降を想定しています. 道路パーツ 道路のフリーのUnityアセットとして,今回は以下を利用しています. インストール手順 道路生成ツール用 Unity パッケージのダウンロード 以下のサイトから道路生成ツール用のファイルをダウンロードしてください. ダウンロード対象ファイルは以下の通りです. hakoniwa-road-creator.unitypackage road_map.json road_parts_type.json Unityパッケージのインポート 新規Unityプロジェクトを作成し,hakoniwa-road-creator.unitypackageをインポートします. インポート成功すると,以下のように,Scenesフォルダ配下に,RoadCreatorというシーンが現れますので,それをダブルクリックしましょう. 道路パーツのインストール Webブラウザで,以下のサイトにアクセスしてください. 上記のOpen in Unityをクリックしましょう. 上図のimportボタンをクリックして,Unityアセットをインストールします. 成功すると,下図のように,CityVoxelPackというフォルダが作成されます. 道路パーツの微調整 インストールした道路パーツですが,道路自動生成するには,若干,位置の微調整が必要となります. 微調整が必要となるパーツは以下のものです(対象パーツを利用しない場合は対応不要です). CityVoxelPack/Assets/roads/medium/Prefabs road2/road2 微調整内容:x=-2.5, z=2.5 CityVoxelPack/Assets/roads/tall/Prefabs road12 微調整内容:x=-2.0, z=2.0 road12bis 微調整内容:x=-2.0, z=2.0 road12bisbis 微調整内容:x=2.0, z=2.0 以下,road2の設定例です.画面右のTransform/Positionのxとzの位置を変更します. これで準備完了です. 利用手順 道路パーツのコンフィグファイルの配置 まずは,作成したUnityプロジェクト直下に,さきほどダウンロードした以下の2ファイルを配置します. road_map.json road_parts_type.json 道路生成する UnityのWindowメニューから,道路生成用のメニューをクリックしてください. 成功すると,下図のような画面がポッポアップされます. なお,Parentは,かならずRoadsを指定してください. CREATEボタンを押下すると,以下のように道路が現れます. この道路オブジェクトは,HierachyビューのRoads配下に配置されています. なお,各オブジェクトには,コライダが割り当てられており,このままシーンビューを見ると,コライダが割り当たっていることがわかります. 各種コンフィグ情報の説明 road_map.json ここで作成した道路ですが,road_map.jsonでその形状を定義します. 御覧の通り,2種類の道路パーツ(road1, road2)だけで構成されています. { "entries": [ { "prefab_name": "road2", "rotation": 0.0, "connect_direction": null, }, { "prefab_name": "road1", "rotation": 0.0, "connect_direction": "+z", }, { "prefab_name": "road2", "rotation": 180.0, "connect_direction": "+z", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "+x", }, { "prefab_name": "road2", "rotation": 270.0, "connect_direction": "+x", }, { "prefab_name": "road1", "rotation": 0.0, "connect_direction": "-z", }, { "prefab_name": "road2", "rotation": -270.0, "connect_direction": "-z", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "+x", }, { "prefab_name": "road2", "rotation": 0.0, "connect_direction": "+x", }, { "prefab_name": "road1", "rotation": 0.0, "connect_direction": "+z", }, { "prefab_name": "road1", "rotation": 0.0, "connect_direction": "+z", }, { "prefab_name": "road2", "rotation": 270.0, "connect_direction": "+z", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "-x", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "-x", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "-x", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "-x", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "-x", }, { "prefab_name": "road1", "rotation": 90.0, "connect_direction": "-x", }, { "prefab_name": "road2", "rotation": 180.0, "connect_direction": "-x", }, { "prefab_name": "road1", "rotation": 0.0, "connect_direction": "-z", }, { "prefab_name": "road1", "rotation": 0.0, "connect_direction": "-z", }, { "prefab_name": "road2", "rotation": 90.0, "connect_direction": "-z", }, { "prefab_name": "road1", "rotation": 90.0, "scale": 1.285, "connect_direction": "+x", }, ] } 以下,各項目の説明です. prefab_name 今回使用したフリーのUnityアセットの道路パーツの名前です rotation 道路パーツの回転角度を90度単位で指定します. connection_direction 一個前の道路パーツと接続する方向を指定します. +x:+x軸方向に接続します. -x:-x軸方向に接続します. +z:+z軸方向に接続します. -z:-z軸方向に接続します. scale 道路パーツのスケールを指定します. 未指定の場合は1.0倍です. road_parts_type.json 今回使用したフリーのUnityアセットの道路パーツの定義体です. 道路パーツを増やす場合や,異なるUnityアセットを利用する場合はこの定義体をアップデートすれば拡張できるようになってます. 詳細は追々説明していきますので乞うご期待ください.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity コンポーネントを持っているかを調べる

0.0 はじめに ゲームオブジェクトに特定のコンポーネントが含まれているか調べる 1.0 結論 GetComponent<>()の返り値で判断可能です。 例えばゲームオブジェクトにRigidbodyコンポーネントが含まれていればGetComponent< Rigidbody>()の返り値はtrue、含まれてなければ返り値はfalseとなります。、 Test.cs if (GetComponent<Rigidbody2D>()) { Debug.Log("Rigidbody2D含まれてるよ"); } else { Debug.Log("Rigidbody2D含まれてないよ"); gameObject.AddComponent<Rigidbody2D>(); // 含まれてなければ加える } 2.0 おまけ 下記のように[RequireComponent (typeof ())]のアトリビュートを付けているとコンポーネントを自動に追加してくれます。 ポスト typeof ()の()中にコンポーネント名を入れる。 Test.cs [RequireComponent (typeof (Rigidbody))] public class Test : MonoBehaviour { }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity]3点から得られる平面にQuad/Planeオブジェクトを移動する

小ネタ。 表題の問題を解こうとネットを検索するとたいてい平面の数式に遭遇し、オブジェクトの回転に変換する方法を見つけにくいので書いてみる。大した話ではなく、メモ程度。 要件として ここでは 「ある物体の角度に応じて、その物体の正面の障害物を壁で覆うようにQuadオブジェクトを配置したい」 とする。 なお Plane を使う場合は面の方向が90度異なるので、以下の説明に補正が必要になる。「正面」でない場合も同様。 作戦は 「物体から正面に3点のレイキャストを発行し、ぶつかった3点に沿うように Quad オブジェクトを移動する」 としよう。このとき、検出された3点に沿うように矩形オブジェクトの姿勢を指定するのが今回のテーマ。 外積から法線を得る 3点あると三角形ができるが、そのうちの二辺のベクトルで外積を作ると、平面に垂直なベクトルを得られる。じっさい平面の式というのは法線ベクトルそのものなので、これを知ってればこの問題は解けたも同然。 LookRotation Unity にはベクトルに向かうための Quaternion.LookRotation という関数があるので、先程の法線に適用すれば完成。なお安定性のため、第二引数の up ベクトルも指定する。法線ベクトルとワールド空間の右ベクトルの外積で fwd というベクトルを作っておく(状況に応じて対応は分かれる)。 code 以下。Quadのpositionは3点の重心とした。 using System.Collections; using System.Collections.Generic; using UnityEngine; public class Wall : MonoBehaviour { public Transform Target; void FixedUpdate() { var orgPoints = new Vector3[] { new Vector3( 1f, 1f, 0f), new Vector3(-1f, 1f, 0f), new Vector3( 0f,-1f, 0f), }; var hitPoints = new Vector3[3]; bool existsCollider = true; for (var i = 0; i < orgPoints.Length; ++i) { var org = Target.TransformPoint(orgPoints[i]); var dir = Target.TransformDirection(Vector3.forward); RaycastHit hit; if (Physics.Raycast(org, dir, out hit, Mathf.Infinity, 1 << 3 /* layerMask */)) { hitPoints[i] = hit.point; } else { existsCollider = false; } } if (existsCollider) { var norm = Vector3.Cross(hitPoints[1] - hitPoints[0], hitPoints[2] - hitPoints[0]).normalized; var up = Vector3.Cross(norm, Vector3.right); var center = (hitPoints[0] + hitPoints[1] + hitPoints[2])/3f; var rot = Quaternion.LookRotation(norm, up); transform.position = center; transform.rotation = rot; } } } このスクリプトを Quad オブジェクトに適用し、Target には判定するためのプレイヤーなどの物体を入れておく。レイキャストを投じる対象はレイヤー3を指定。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【unity】

DynamicBoneでボーン変形するオブジェクト 回転するオブジェクトの先端に子オブジェクトとしてつけた時と、独立させてscriptで座標を合わせた時、 その動作はほとんど変わらない 角度等参照せず、座標だけで動作を制御してるっぽい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】materialがリークするけど、sharedMaterialにも問題がある場合

Unityエディタを操作中、次のようなエラーが出ました。 Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead. 翻訳すると 編集モード中にrenderer.materialを呼び出したことが原因で、マテリアルをインスタンス化します。これはシーンにマテリアルをリークすることになります。代わりにrenderer.sharedMaterialを使用することをお勧めします。 materialを使わず、sharedMaterialを使えということなのですが、sharedMaterialを使うと元のアセットのプロパティを書き換えてしまうという問題があります。 そこでsharedMaterialを使うけど、元のアセットも書き換えない方法です。 var tempMaterial = new Material(renderer.sharedMaterial); tempMaterial.mainTexture = texture; renderer.sharedMaterial = tempMaterial; 追記 この方法を使ってもエラーが出ないだけでリークしていて、Hierarchyに勝手にインスタンスを作ってしまうことがあるようです。 なのでmaterialを取得したら自分で破棄した方がいいのかも知れません。 【Unity】Renderer.materialで取得したマテリアルは自分で破棄しないとリークする話
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】好きな補間関数で追従するカメラ

はじめに ネットで調べても意外にもヒットしなかったのでとりあえず作ってみた。 なんかかなり行き当たりばったりで作ったので、色々雑です。 本当は追従対象が動いている時は別で処理をすべきでなんですが、まあなんか面倒なので更新されたベクトルを使って 適当に補間させてます。よくない。 あと例によって私は素人です。 ソース using UnityEngine; namespace MyTool { public class FollowUpCamera : MonoBehaviour { [SerializeField] GameObject _mainCamera; [SerializeField] GameObject _target; [SerializeField] AnimationCurve _animationCurve; [SerializeField] float _followUpTime = 3.0f; private float _elapsedTime; private Vector3 _relativeFollowingPosition; private Vector3 _currentTargetPosition; private Vector3 _currentCameraPosition; void Awake() { _relativeFollowingPosition = _mainCamera.transform.position - _target.transform.position; _currentTargetPosition = _target.transform.position; _currentCameraPosition = _mainCamera.transform.position; } void LateUpdate() { //動いているときだけ更新する if(_currentTargetPosition!=_target.transform.position) { _currentTargetPosition = _target.transform.position; _currentCameraPosition = _mainCamera.transform.position; _elapsedTime = 0f; } _elapsedTime += Time.deltaTime; _mainCamera.transform.position = Vector3.Lerp(_currentCameraPosition, _currentTargetPosition + _relativeFollowingPosition, InterpolationFunction(_elapsedTime/_followUpTime)); } private float InterpolationFunction(float rate) { return _animationCurve.Evaluate(rate); } } } _followUpTime秒で補間されます。 別に真新しいことはしていないです。補間関数にAnimationCurveを指定しているというだけです。 ぐにゃぐにゃな関数を指定するとぐにゃぐにゃ動きます。 独り言 巷じゃ、カメラが滑らかに追従するテンプレが出回ってるとおもうんですが、あれってどうなんだろう(今更) なんか、Lerpの引数が毎フレーム変わるのって出力が明示的じゃないからあんまよくない気がする。 でもいい感じに動くのは確か。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Blenderトラブルシューティング

fbxファイルにエクスポートしたオブジェクトをUnityでインポートするとオブジェクトが白くなる デフォルトの設定だとテクスチャ情報がエクスポートされないことが原因 以下の箇所をcopyに変更、右側のフォルダをクリックして青くするとテクスチャの出力が有効になる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む