20211129のUnityに関する記事は10件です。

今年Houdini関係で登壇して公開した資料まとめ

今年Houdini関係で登壇して公開した資料まとめ 今年登壇した「ボーンデジタル様主催のセミナー」「Unity道場」「CGWORLDクリエイティブカンファレンス」で発表時に使用し、一般公開している資料や動画の紹介及びまとめになります。 皆様のHoudiniの勉強や業務にお役立て頂ければ幸いです。 Houdiniで作るNPRエフェクト&After Effectsでのコンポジット実践解説オンラインセミナー 登壇資料はこちら CGWORLD様によるセミナー内容のまとめ記事はこちら 当日の登壇動画は、ボーンデジタル様とAUP契約を結んでいるユーザー様は申請すればご覧になることができます。 Unity道場 Houdini編 『UnityとHoudiniで作るRealtimeVFX実践解説』 登壇資料はこちら Unity Learning Materials : 前編の動画及び資料 Unity Learning Materials : 後編の動画及び資料 CGWORLD 2021 クリエイティブカンファレンス 【TechDEMO“OKURIGAMI” : VFX Breakdown】Houdini x Unity x AfterEffectsを用いたRealtimeVFXテクニックや苦労話紹介 登壇資料はこちら ArtStationにアップしてある各項目のArtDumpはこちら セッション紹介ページはこちら 登壇のアーカイブ動画は2021年12月20日まで配信されている予定です。2021年のクリエイティブカンファレンスに登録した方は誰でも視聴可能です。 おわりに 如何だったでしょうか? 業界活性化や次に続く人たちのためにも、出来るだけ資料はオープンな形で公開するようにしてきているので、これらの資料が少しでも皆様のHoudiniの勉強や業務のお役に立てたのなら幸いです。 最後までお目通し頂きありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アバターの物を出し入れできるようにするEditorを作る(Animation編)|VRC用の拡張Editorを作ってみよう(5)

この記事は前回の続きになります VRC用の拡張Editorを作ってみよう(4) 目次 1.準備 2.OnGUIを作る 3.Animationを作るコードを書く 4.AddAnimationを呼び出す 5.動作確認する 6.次回 1.準備 Animationを保管するフォルダーを作る アニメーションを保管するためのフォルダーを作っておきます 場所は自分の好きな場所で大丈夫です これでアニメーションフォルダーは完成しました Editorフォルダーに新しくC#スクリプトを作る 新しくEditorを作りたいので、最初に作ったEditorフォルダーに、新しくC#スクリプトを作ります 名前は分かりやすい名前で作りましょう 今回はAnimationEditorにしました これでC#スクリプトは完成しました Editorの初期設定をする usingを追加する Editorとして機能させるためのusingと、今回使うusingを追加していきます 今回はこの二つを追加します using UnityEditor; using VRC.SDK3.Avatars.Components; これでusingの追加ができました Menuアイテムを作る メニューバーに表示するMenuアイテムを作ります 自分の名前など、好きなように設定してもらって大丈夫です 今回私はAnimationEditor/AnimationEditorで作ることにしました [MenuItem("AnimationEditor/AnimationEditor")] これでメニューバーに表示されるようになりました Windowを取得する publicな、値を返さない静的メソッドを作ります 名前はShowWindowなどにしておきます メソッドが出来たら、EditorWindow.GetWindowをつかって、(今回の場合)AnimationEditorを取得します public static void ShowWindow() { EditorWindow.GetWindow<AnimationEditor>(true, "AnimationEditor"); } これでウィンドウがひらけるようになりました Hierarchyからパスを取得できるようにする 後でアニメーションを作るときに、アバターの階層が取得できなかったので、それ用のメソッドを作りました 今回はそれを使用するために、コピー&ペーストしていきます private static string GetHierarchyPath(GameObject targetObj) { List<GameObject> objPath = new List<GameObject>(); objPath.Add(targetObj); for (int i = 0; objPath[i].transform.parent != null; i++) objPath.Add(objPath[i].transform.parent.gameObject); string path = objPath[objPath.Count - 2].gameObject.name; //今回の場合avatar(先頭のオブジェクトが不要)なのでCount - 2にする。必要な場合は - 1 に変更 for (int i = objPath.Count - 3; i >= 0; i--) //こっちもCount - 3にする。必要な場合は - 2にする path += "/" + objPath[i].gameObject.name; return path; } 空いているところにコピーができたら使えるようになります コードの説明はこちら これで準備は終わりました 2.OnGUIを作る OnGUIメソッドを作る privateな値を返さないメソッド、OnGUIを作ります private void OnGUI() { } これでOnGUIメソッドは完成しました 変数を宣言する 今回宣言する変数は、 アバター,(VRCAvatarDescriptor),[avatar] アニメーションの名前,(string),[animationName] オンオフするオブジェクト,(GameObject),[targetObj] アニメーションフォルダーのパス,(string),[animFolderPath] アニメーションフォルダーのパスは、さっき作ったAnimationフォルダーのパスをコピーしてきましょう これらを全てprivateで作っていきます (もちろん今回もOnGUIの上に書きますよ) private VRCAvatarDescriptor avatar; private string animationName; private GameObject targetObj; private string animFolderPath = "Assets/test/Animation"; これで変数は完成しました 変数に代入する 上で作った変数に、値を代入していきます avatarとtargetObjは、いつも通りObjectFieldを使いますが、animationNameだけTextFieldを使用します TextFieldの使い方はこうです EditorGUILayout.TextField("label", "text"); これを使って代入していきます TextFieldに、キャストは必要ありません (OnGUI内に書きます) avatar = EditorGUILayout.ObjectField("Avatar", avatar, typeof(VRCAvatarDescriptor), true) as VRCAvatarDescriptor; animationName = EditorGUILayout.TextField("animationName", animationName); targetObj = EditorGUILayout.ObjectField("TargetObject", targetObj, typeof(GameObject), true) as GameObject; これで変数への代入ができました ボタンを作る まずはIF文を書いていきます (OnGUIの中に書きます) avatarがnullではない時かつ、アニメーションの名前がnullではない時かつ、ターゲットオブジェクトがnullではない時、を、条件にします if (avatar && animationName != null && targetObj) { } IF文が完成しました 次に、ボタンを作っていきます (IF文の中に作ります) ボタンの名前は何でも大丈夫です 今回私はAnimation作成にしました if (avatar && animationName != null && targetObj) { if (GUILayout.Button("Animation作成")) { } } これでボタンが完成しました 3.Animationを作るコードを書く コードを書くためのメソッドを作る privateな、値を返さないメソッドを作ります メソッド名はわかりやすい名前にします 今回私は、AddAnimationにしました private void AddAnimation() { } これでメソッドが完成しました メソッドを完成させる まずは、CreateAssetを使って、新しくAnimationClipを作るようにします 最初に作った変数、animFolderPathをパスとして使用します 作成するときの名前は、変数animationNameを使います アニメーションを二つ、OnとOffを作成したいので、animationNameにOnとOffを付けくわえて 二つアニメーションを作成します AssetDatabase.CreateAsset(new AnimationClip(), animFolderPath + "/" + animationName + "On.anim"); AssetDatabase.CreateAsset(new AnimationClip(), animFolderPath + "/" + animationName + "Off.anim"); 次に、今作ったアニメーションを、AnimationClip型の変数animOnとanimOffを宣言して代入します 代入する時は、LoadAssetAtPathを使用します AnimationClip animOn = AssetDatabase.LoadAssetAtPath(animFolderPath + "/" + animationName + "On.anim", typeof(AnimationClip)) as AnimationClip; AnimationClip animOff = AssetDatabase.LoadAssetAtPath(animFolderPath + "/" + animationName + "Off.anim", typeof(AnimationClip)) as AnimationClip; これで代入ができました targetObjの現在のパスを取得する GetHierarchyPathを呼び出して、string型の変数に代入します 変数の名前はpathにします string path = GetHierarchyPath(targetObj); これでパスを取得できました アニメーションにKeyFrameを追加する KeyFrameを追加するために、AnimationCurveを使用します Animationcurve型の変数を宣言して、new AnimationCurve(new Keyframe( , ))を代入します keyframeの使い方はこの通りです Keyframe("フレーム数", "Value") これを使用して、新しくcurveOnとcurveOffを宣言します curveOnは、ゲームオブジェクトを有効化するため、Valueを1にします curveOffは、ゲームオブジェクトを有効化するため、Valueを0にします フレーム数は二つとも0です AnimationCurve curveOn = new AnimationCurve(new Keyframe(0f, 1f)); AnimationCurve curveOff = new AnimationCurve(new Keyframe(0f, 0f)); 次にこれをAnimationClipにセットしていきます セットにはAnimationClip.SetCurveを使用します 使い方はこの通りです AnimationClip.SetCurve("relativePath", "type", "propertyName", "curve"); これをanimOnと、AnimOff用に作っていきます animOn.SetCurve(path, typeof(GameObject),"m_IsActive", curveOn); animOff.SetCurve(path, typeof(GameObject), "m_IsActive", curveOff); これでアニメーションにKeyFrameを追加することができました 4.AddAnimationを呼び出す 最初に作ったボタンから、AddAnimationを呼び出せるようにします ボタンの中(IF文)に書いていきます if (avatar && animationName != null && targetObj) { if (GUILayout.Button("Animation作成")) { AddAnimation(); //ここに追加 } } これで呼び出せるようになりました 5.動作確認する オブジェクトを用意します VRCAvatarDescriptorが入ったAvatarと、適当に追加したゲームオブジェクトを用意します 用意ができたらEditorを開きます 追加していきましょう 追加ができたら、Animation作成を押してみましょう Animationフォルダーに追加されていれば成功です アニメーションは[ウィンドウ/アニメーション/アニメーション]で確認できます 6.次回 次回はAnimator編です VRC用の拡張Editorを作ってみよう(6) coming soon
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】超高解像度映像「KNOT ISLAND 2155」をつくりました

はじめに 長崎にある軍艦島デジタルミュージアムの巨大プロジェクタで上映する 映像作品「KNOT ISLAND 2155」をUnityで製作しました。 カラコレ・編集以外は全部一人でやりまして、製作期間は基本日曜に作業して3ヶ月くらい。 編集や音楽のおかげもあって、かなり良い映像になりました。 軍艦島デジタルミュージアムで上映される「KNOT ISLAND 2155」をつくりました。点群データをUnityのVFX Graphで映像にしました。 pic.twitter.com/EFtmeuKgDP— ekunish (@ekunish) November 29, 2021 製作について 手順は下記の通りです。 モデルデータを間引き 建物ごとにモデルを切り出し 切り出したモデルをクレンジング ワイヤーフレーム表示用にモデルデータ書き換え Unityに取り込み VFX Graphで表示 TextMeshProでUI表示 CinemachineとTimelineでカメラワーク調整 Rendermonsterでpng書き出し ffmpegでHapに変換 モデルデータを間引き Meshlabを使い、モデルデータのvertexが1000万程度となるように調節しました。 最終的にはUnity上で25fpsくらいとほぼリアルタイムに処理できました。 建物ごとにモデルを切り出し ここは自動でやる手段が思いつかなかったので、手動です。 軍艦島の建物数十棟を目検で分けていくのは辛い作業でしたが、 Meshmixerだとモデルの形状に合わせてある程度自動で範囲選択してくれるので、 思ったより時間は掛かりませんでした。 切り出したモデルをクレンジング 手動で切り出すとどうしても細かいポリゴンが残ってしまったため、 再びMeshlabのisolation機能を使い、孤立しているパーツを削除しました。 ワイヤーフレーム表示用にモデルデータ書き換え 元々、点群っぽい表現にしたいと思い、UnityのVFX Graphの使用を想定していました。 表現の方向性を検討していく上で、ワイヤーフレーム的な表現を使うことになり、 色々調べたのですが、Particle Stripなどはあるものの、 VFX Graphでワイヤーフレーム表示をする機能は見つかりませんでした。 とりあえず、点群データのフォーマットであるplyをasciiコードで見てみると、 全頂点位置と色情報が並び、その次に各ポリゴンを形成する頂点の組み合わせが並んでいる 割とシンプルな構造であることがわかりました。 Particle Stripは、頂点データを上から順番に読んでいくので、 ポリゴンの順に頂点を並び替えたplyを作ってやれば良さそうです。 本当はunityのプラグインを書けば良いのでしょうが作法がよく分からなかったので pythonでスクリプトを書きました。 二重に被る点が出てきちゃいますが、動けば良いの精神です。 input_dir = 'インプットディレクトリ' output_dir = 'アウトプットディレクトリ' os.makedirs(output_dir, exist_ok=True) for filename in os.listdir(input_dir): base, ext = os.path.splitext(filename) if ext == '.ply': print(filename) f = open(os.path.join(input_dir, filename), 'r') linelist = f.readlines() f.close() is_header = True header = [] vertexes = [] tri_indexes = [] sorted_vertexes = [] for line in linelist: if (is_header): header.append(line) if (line == 'end_header\n'): is_header = False else: elem_num = len(line.split()) if (elem_num == 7): vertexes.append(line) elif(elem_num == 4): tri_indexes.append(line) # sort for tri_index in tri_indexes: indexes = tri_index.split() sorted_vertexes.append(vertexes[int(indexes[1])]) sorted_vertexes.append(vertexes[int(indexes[2])]) sorted_vertexes.append(vertexes[int(indexes[3])]) sorted_vertexes.append(vertexes[int(indexes[1])]) # output w = open(os.path.join(output_dir, filename), 'w') w.write('ply\n') w.write('format ascii 1.0\n') w.write('comment python converted\n') w.write('element vertex ' + str(len(sorted_vertexes)) + '\n') w.write('property float x\n') w.write('property float y\n') w.write('property float z\n') w.write('property uchar red\n') w.write('property uchar green\n') w.write('property uchar blue\n') w.write('property uchar alpha\n') w.write('element face 0\n') w.write('property list uchar int vertex_indices\n') w.write('end_header\n') w.writelines(sorted_vertexes) w.close() Unityに取り込み @keijiro さんのプラグインを使わせていただきました。 読み込んだら、タイプをTextureに変更。 VFX Graphで表示 前述の通り、Particle Stripを使いました。 スクリプトで処理するため、Blackboardでパラメータを外に出しておきます。 注意点はParticleのCapacityです。 建物ごとにVFX Graphデータをつくるにあたり、 最初は、全部のCapacityを1000万とかにしてたのですが、 3棟ほど動かしたあたりでUnityが動かなくなりました。 1日くらい原因が分からず、そもそもVFX Graphの限界なのではと焦ったのですが、 どうやらCapacityが確保するメモリやらの値のようで、 モデルに合わせてやることで解決しました。 TextMeshProでUI表示 文字の色やサイズを変更するため、TextMeshProをつかいました。 htmlっぽいタグで書けるので楽チン。 CinemachineとTimelineでカメラワーク調整 最初はスクリプトでどうにかできるかなと思っていたのですが、 ダイナミックなカメラワークにするため、Cimemachineを使いました。 軌道を設計するにあたってはSmooth pathが使いやすかったです。 各種パラメーターの一部はTimelineで処理してます。 グラフィカルに変数を調整できるのが良いです。 もう少しAfterEffectっぽい感覚に近づけるともっと使いやすくなりそうです。 Render Monsterでpng書き出し 今回のプロジェクタが8K超えの横長超高解像度で、 Unity Recorderでは書き出せませんでした。 また、そもそもmp4も規格として4Kだか8Kまでしか対応していないようで、 Render Monsterを使い、連番のpngで書き出しました。 シーン再生は25FPSくらい出てたんですが、書き出しは1FPSほどでかなり時間掛かりました。 ffmpegでHapに変換 動画は普通ならProResコーデックで良いと思いますが、 TouchdesignerではProResが読み込めませんでした。 調べてみると、Hapには対応しているようだったので、ffmpegで変換しました。 最初再生したらどうやってもカクカクで、どうしようかと思いましたが、 Perfome Modeだと滑らかに動いたので良かったです。 最後に 点群を扱うのも初めてですが、Unityで初めて本格的な映像をつくってみました。 やることも多かったですが、学ぶことが多かったです。 1000万超えの点群をリアルタイムで処理できるVFX Graphはすごいです。 Cinemachineも初めて使ってみましたが、かなり直感的にカメラワークを設計できました。 20万で作ったWindowsを有効活用できてよかったです。 しばらくは常設で上映されると思うので、軍艦島デジタルミュージアムにぜひ行ってみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityのGC AllocateとWaitForSeconds

PONOS Advent Calendar 2021の1日目の記事です。 はじめに ご存知の通りUnityにおけるメモリ管理は基本的にGCにおまかせになっています。 詳しい説明は省略しますが、一般的にこうした環境下でのメモリの領域は、大きくわけてヒープ領域とスタック領域という二つに分類されます。 ローカル変数として宣言された値型やstructはスタック領域にメモリが確保され、スコープを抜けたタイミングで解放されるわけですが、ヒープ領域に確保されたメモリは特定のタイミングでGCによって解放されます(世代別GCなどの話は省略)。 このヒープ解放はそれなりに負荷がかり、時にはスパイクとなってしまうことがあり注意が必要です。 Unityのコルーチンで用いられるWaitForSecondsはClassインスタンスを生成するため、ヒープ領域にメモリが割り当てられます。 つまり、ループ毎に new WaitForSeconds(xxx) を行ってしまうと、理論上はその度にヒープ領域にゴミのようなメモリ割り当てを発生させてしまうことになります。 今回はProfilerを使用して、実際にnew WaitForSeconds(xxx) がどの程度メモリ割り当てを発生させているかを確認してみたいと思います。 検証内容 検証する環境は以下の通りです。 Unity: 2020.3.23f1 フレームレート: 60FPS Scene上にはデフォルトのカメラとライト以外、テスト用のGameObjectのみとする 極力他の影響を避けるため、デバッグログの出力なども含めた余計なコードは一切かかない 動作はUnityエディタ実行時で確認する そして以下の動作を確認します。 何もしない状態で毎フレームどの程度のメモリー割り当てが発生しているかを確認します。 new WaitForSeconds(xxx)を行うコルーチンを100個生成し、同様の確認を行います。 new WaitForEndOfFrame()を行うコルーチンを100個生成し、同様の確認を行います。 WaitForSecondsを再利用する形に変更し、同様の確認を行います。 検証する 1. プレーンな状態 テスタースクリプトを作る のちのちWaitForSecondsを使用するテスタースクリプトを作ります。 ここではプレーンな状態を想定しているので実際にはそれを使用しませんが、FPSだけ固定しておきます。 public class Tester : MonoBehaviour { // Start is called before the first frame update void Start() { Application.targetFrameRate = 60; } } このスクリプトをヒエラルキーのGameObjectにアタッチしておきます。 結果を見る Window > Analysis を開き、この状態でエディタ実行してしばらく様子をみます。 Memoryの項目のGC Allocated In Frameという項目を見ると、1フレームあたりに割り当てられた個数とデータ量が確認できます。 この結果から、プレーンな状態では0/0Bであることがわかりました。 2. new WaitForSeconds(xxx)を行うコルーチンを100個生成 スクリプトを編集する 0.01f秒のwaitを返すコルーチンを100個生成するようにしてみました。 public class Tester : MonoBehaviour { // Start is called before the first frame update void Start() { Application.targetFrameRate = 60; for (var i = 0; i < 100; i ++) { StartCoroutine(Hoge()); } } private IEnumerator Hoge() { while (true) { yield return new WaitForSeconds(0.01f); } } } 結果を見る Memoryの項目のGC Allocated In Frameという項目を見ると、毎フレーム100個/2.0KBのメモリ割り当てが発生していることが確認できます。 60FPSなので1秒間で120KB, 1分間に約7MBの割り当てを行なっている計算です。 また、WaitForSeconds一つあたり、20Bのメモリが割り当てられるようです。 3. new WaitForEndOfFrame()を行うコルーチンを100個生成 スクリプトを編集する 次はWaitForEndOfFrameの場合をみてみます。 コルーチンを書き換えます。 private IEnumerator Hoge() { while (true) { yield return new WaitForEndOfFrame(); } } 結果を見る Memoryの項目のGC Allocated In Frameという項目を見ると、毎フレーム100個/1.6KBのメモリ割り当てが発生していることが確認できます。 60FPSなので1秒間で96KB, 1分間に約5.6MBの割り当てを行なっている計算です。 また、WaitForSeconds一つあたり、16Bのメモリが割り当てられるようです。 4. WaitForSecondsを再利用する ここまでのことでわかる通り、このループのコードでは常にメモリ割り当てが発生してしまっています。 これを発生させないために再利用するコードに変えてみます。 スクリプトを編集する コルーチンの処理を書き換えます。 private IEnumerator Hoge() { var wait = new WaitForSeconds(0.01f); while (true) { yield return wait; } } 結果を見る Memoryの項目のGC Allocated In Frameという項目を見ると、0/0Bになっており、メモリ割り当てが発生していないことが確認できます。 まとめ このように、よく使うWaitForSecondsも、ヒープ領域が割り当てられることが確認できました。 1インスタンスあたりの割り当てられるメモリは決して多くありませんが、同様のオブジェクトが多数あったり、1秒間あたりの実行回数が多い場合は無視できないデータ量になることもあり、それによってGCのためのスパイクを発生させる要因の一つとなりえます。 ここではインスタンスの再利用という簡単なコードでそれを回避しました。 WaitForSecondsに限らず、小さいデータであったとしてもヒープ領域へのアロケーションが必要なコードをループに記述する場合、極力その回数を減らせられるように、注意が必要です。 明日は@e73ryoさんです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】図解で分かる!アニメ調シェーダ基礎

この記事はサムザップ Advent Calendar 2021 12/2の記事です! 昨日の記事はオオバさんによる「【Unity】失敗しないUI開発3つのポイント 」でした! この記事について この記事ではアニメ調シェーダで使われている輪郭線の仕組みについての解説を挟みながら、画像のような最も簡単な輪郭線を作れるシェーダとモデル(オブジェクト)の話をします。 輪郭線とは 輪郭線とは端的に言えばモデルに対して周囲を取り囲むように表示される線の事です。輪郭線を引くことで造詣がはっきりとし、モデルを際立たせることができます。また、UnityのSceneビューでオブジェクトを選択した際に輪郭に沿って表示されるオレンジ色の線など、身近にありふれている表現方法です。 この輪郭線をUnityで表現する場合は主にシェーダを用いて表現されることがあり、アウトラインシェーダや輪郭線シェーダと呼ばれています。 輪郭線はアニメ調の表現でもよく使われており、例えばUnityちゃんでも使われています。 下の画像ではUnityちゃんの一部の輪郭線の有り無しを比較したもので、Unityちゃんのあご付近を見ると右の画像では輪郭部分に濃い線が引かれていますね。 これがUnityちゃんの輪郭線です。(Unityちゃんの他の部分にもたくさん使われています。) もっとも簡単な輪郭線 最も簡単に輪郭線を表示する方法は、モデルを一回り大きく輪郭線の単色で描写しその上にモデルを描写する方法です。 例として、ある四角いアイコンのUIで輪郭線が作られるか見てみましょう。 このようにベース画像と単色の拡大画像を合わせることで輪郭線を表示させることができます。 それでは早速シェーダに落とし込んでみましょう。コード内でも同様に、1パス目でベース画像を拡大したものを描画し、2パス目でべース画像自体を描画することで輪郭線を表示するようにします。特別なことはしておらず、最小限の構成にしています。 シェーダコード NormalOutLine.shader Shader "Unlit/NormalOutLine" { Properties { _MainTex ("Texture", 2D) = "white" {} _BaseColor("BaseColor", Color) = (1, 1, 1, 1) _OutlineColor("Color", Color) = (1, 1, 1, 1) _OutlineWidth("Width", float) = 1 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 // _OutlineColorの色で_OutlineWidthの分だけ大きく描画を行う Pass // 1 { // 裏面のみの描写にする cull Front CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; }; half _OutlineWidth; half4 _OutlineColor; v2f vert (appdata v) { v2f o; // _OutlineWidthの分だけ法線方向に拡大させている o.vertex = UnityObjectToClipPos(v.vertex + v.normal * _OutlineWidth / 100); return o; } fixed4 frag (v2f i) : SV_Target { return _OutlineColor; } ENDCG } // 特別な処理を行わず、そのまま描画を行う Pass // 2 { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; half4 _BaseColor; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { return _BaseColor; } ENDCG } } } 法線拡大法の問題点 それではこのシェーダをキューブやスフィアにアタッチしてみましょう。 実際にコードを実行していくつかのオブジェクトにアタッチしてみると分かるのですが、実はこの方法では問題があり、ある基準を満たしたモデルでないと輪郭線が途切れてしまいます。 上記の画像では左のキューブに上記のシェーダをアタッチしたところ右の様になりました。右のキューブは輪郭線が切れてしまっています。 これは頂点が持っている法線データが原因で起こってしまっています。 今回のシェーダでは法線方向に拡大をしているため、モデルの法線の方向が意図したものでないと正常に表示されません。 法線データ 頂点が持っている法線データはどう確認すればよいでしょうか? 一度法線データを確認したいですがUnityでは手軽に法線データを確認できないため、今回はBlenderでモデルの法線データ確認してみます。 上記の画像では、各頂点から法線をピンク色の線で表示してみました。確かに面から垂直に法線が引かれているため法線方向に拡大すると角や辺の付近にはアウトラインが生成されることはなさそうです。 Blender上で板ポリをおいて実際に法線が垂直な場合をシミュレートした結果がこちらです。 確かにUnity上と同じく、辺や角の付近が切れている状態になりました。 次は頂点の法線データをBlender編集してUnityに持っていき、想定しているアウトラインが出るか確認します。 簡略化のため、一時的にキューブの1面だけで考え、予想されるアウトラインをオレンジの板て表現しています。 現在は下の画像のように面に対して垂直な法線になっているため、アウトラインが角までカバーできていない状態です。 そこで下記のように角の法線を関連する面の平均ベクトルになるように編集しました。 この法線を使って、法線方向に拡大すれば輪郭線は以下の画像の様に拡大されるため途切れることなく表示されるはずです。 それではUnityで確認をしてみましょう。 調整した右のキューブでは、無事に途切れることがない輪郭線を作ることができました。 つまり原因は法線データが面から垂直になため、角や辺の部分にアウトラインが生成できていないという事でした。 これで無事にモデルとシェーダの調整を終えたため、輪郭線を描画できるようになりました! まとめ この記事ではもっとも簡単な輪郭線シェーダの解説を行いました。 この方式ではモデル自体に制約を課してしまいますが、その分単純なコードで輪郭線を描画することが可能です。 この記事が初めてアニメ調シェーダを書こうとする方、輪郭線について知りたい方の一助になれば幸いです! また、日々の出来事や開発記録をこちらのブログで公開しています。 よろしければご覧になって下さい! 次回は@shiimanさんの「【Golang】OAuth2.0を実装する方法/Google認証」です! 参考サイト ライセンス表記 © Unity Technologies Japan/UCL
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity入門】ゲーム開発ができるUnityをインストール

はじめに ゲーム開発ができるUnityを使い始めるにあたって行った内容メモ Unityの説明 ゲームやツールを開発できるC++で作成されたゲームエンジン 非常に幅広いプラットフォームに使用できる iOSやAndroidのようなスマホ向けアプリ Windows、MacOS、Linux向けのデスクトップアプリ PlayStationやXbox、WiiUなどの家庭用ゲーム機 ゲームエンジンであると同時に、開発用プラットフォームでもある。 ゲーム制作用の補助ツールやビジュアルプログラミングツールなどが含まれる。 2Dゲーム, 3Dゲームどちらも作ることができる。 直感的にゲームを作ることができる。 AssetStore(アセットストア)が提供されている。 ◆ ゲームエンジン ゲーム開発における一般的な処理(映像処理や音響処理など)を効率よく実装できるツール よく使われる処理が共通化されているため、実装作業の工数を減らすことができる。 プログラミング言語の知識が浅いクリエイターでもゲーム開発が進められるように作られている ◆ ビジュアルプログラミングツール 直感的な操作を用いてコーディングレス開発を行うツール ◆ AssetStore(アセットストア) 3Dモデルなどのゲームデータを購入できる場所(有料販売だけでなく、無料配布されている) 下記例 3Dモデル(人型、ロボット、クリーチャー) キャラクターのイラスト アイコン画像 音楽 エフェクト プログラム処理 無料で利用する条件 個人利用(ただし過去 12 か月の収益や調達した資金が 10 万米ドルを下回る方が対象) 無料のUnityプラン「Personal」 ダウンロードとインストール Unityを公式ページ(https://unity3d.com/jp/get-unity/download) から「Unity Hub をダウンロード」を押してダウンロード。 ↓ インストール ↓ (UnityHubを日本語化する) Preferences > Appearance > Languages 使い始めと試しにテンプレートで新しいプロジェクトを作ってみる UnityID作成 ↓ 「Personal」を選択 ↓ インストール > 「エディタをインストール」 ※結構時間かかる ↓ 新しいプロジェクト作成 ↓ テンプレートを選ぶ ↓ テンプレートをダウンロード ↓ プロジェクト作成 ↓ しばらく経過するとUnityが起動し 作成したプロジェクトを開く ※ ここでUnityとUnityHubは別アプリなのかと気がつく Unityを日本語化する 日本語化は「UnityHub」から行う。 UnityHubを起動し、「インストール」 > 「アイコン」をクリック > 「モジュールを加える」> 言語パック「日本語」を選んでチェックを入れ > 「インストール」 ↓ Unity再起動 ※再起動しないと表示されない ↓ Unity > Preferences > Languages > 「English」→「Japanese」に変更 ※ Unityを再起動する ※ 上記はMacの場合 Unityの説明 画面(枠の色) 説明 シーン(赤) オブジェクトなどを配置する画面。マウスのドラッグなどで画面を回転することができる。 インスペクタ(黄色) シーンやオブジェクトのプロパティを表示 ヒエラルキー(緑) 左側の画面:現在編集しているシーンに存在しているオブジェクトの一覧 プロジェクト(水色) コンソール 下部の画面:今開いているプロジェクトの構成。 シーンやパッケージ等のフォルダが表示される。 役に立つカメラ視点の操作方法 上の方のボタンを教えてもできそうだけど、効率を考えると覚えたが良さそう。 (Blenderを調べたときも誰かが似たようなこと言っていた) ※たまたま見つけただけなのでオブジェクトのアップのショートカットは不明 ※ 2点ドラッグも目のアイコンになるけどよくわからん 操作 方法 中心を支点に回転 option(手から目のアイコンに変化する) + ドラッグ 拡大 と 縮小 2点で上下スライド(スクロールの時のやつ) カメラ視点移動 option + command + 1点ドラッグ オブジェクトのアップ ヒエラルキーのオブジェクトダブルクリック 最後に 所感、Blenderのときは実行できないのでつまらなかったが、こっちは実行できるので面白かった
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Beta版のUnity Gaming Servicesを導入して軽く触ってみる

本記事はCraft Egg Advent Calendar 2021の12/4の記事です。 12/3の記事は@Tomy_0331さんの「新卒2年目までに学んだ、コーディングで意識すること」でした。 はじめに 株式会社Craft EggでUnityクライアントエンジニアをしている山元です。 アドベントカレンダーを書くということで何かいいネタがないかと探していたところ、最近 Unity Gaming Services のBeta版がリリースされていたので触ってみた記事になっております。 Unity Gaming Services とは 「バックエンド、マネタイズ、ユーザー獲得、プレイヤーエンゲージメントなど、ゲームクリエイターが必要とするすべての機能を提供する新しいプラットフォーム」のようです。 11/22にBeta版がリリースされた新機能になっておりマルチプレイ機能、Vivoxよる音声通話など様々な機能が入っています。 早速触って試してみる 今回はUnity Gaming Services を使用してプレイヤーが Lobby に参加するところまでを試してみようと思います。 検証環境 Mac Catalina Unity 2021.1.0b12 Unity Authentication 1.0.0-pre.6 Unity Lobby 1.0.0-pre.6 UniTask 2.2.5 Gaming Servicesの準備 PackageManager, Unity Editor, Unity DashBoard にてそれぞれ準備を行います。 PackageManager Unity Editor で Gaming Services の機能を利用するため manifest.json に以下のPackageを追加します。 "dependencies": { "com.unity.services.authentication": "1.0.0-pre.6", "com.unity.services.lobby": "1.0.0-pre.6", } Unity Editor Gaming Services の Unity Project ID を作成します。 手順: ・ UnityEditor で Project Settings > Services を開く ・ Organizations から自分のアカウントを選択する ・ Create Project ID ボタンをクリック ・ 一番下の項目にある13歳以下の子供を対象にしているかの設定で Yes No どちらかを選ぶ ・ 右下にあるSaveボタンを押して完了 Gaming Services を利用するための Unity Editor 側の準備は以上になるので Unity DashBoard 側の準備に移ります。 Unity DashBoard ダッシュボードでは Lobby 内でプレイヤーがマッチングするために必要な機能を有効にする設定を行います。 手順: ・ Unity Dashboard 側で先ほど作成した Unity Project を選択する ・ 今回はプレイヤーのマッチング機能を試すので左タブ下の方にある Multiplayer を選択する ・「Lobby」「Relay」機能それぞれ Get Started から手順通りに進み機能を ON にする これでダッシュボード側の準備も完了しました。 これから実際にコードを書いてロビーを作成、参加していこうと思います。 実装 サインイン Unity Gaming Services は何をするにしても Unity Authentication を使用して認証を通す必要があります。 なのでまずはUnity Authentication で匿名サインイン処理を行っていきましょう。 private async void Start() { // Unity Gaming Services の初期化処理 await UnityServices.InitializeAsync(); Debug.Log(UnityServices.State); await SignInAnonymouslyAsync(); } /// <summary>匿名サインインする処理</summary> private async UniTask SignInAnonymouslyAsync() { try { await AuthenticationService.Instance.SignInAnonymouslyAsync(); Debug.Log("匿名サインイン成功!"); } catch (AuthenticationException ex) { // 認証エラー時の例外 Debug.LogException(ex); } catch (RequestFailedException exception) { // リクエストエラー時の例外 Debug.LogException(exception); } } サインイン成功,サインイン失敗,サインアウト時のコールバックをそれぞれ登録することもできます。 AuthenticationService.Instance.SignedIn AuthenticationService.Instance.SignInFailed AuthenticationService.Instance.SignedOut ロビーの作成 private async void Start() { // Unity Gaming Services の初期化処理 await UnityServices.InitializeAsync(); Debug.Log(UnityServices.State); await SignInAnonymouslyAsync(); var lobby = await CreateLobbyAsync("test lobby", 2); if (lobby == null) return; Debug.Log($"ロビー作成成功! Id:{lobby.Id}, Name:{lobby.Name}"); } /// <summary>ロビーを作成する処理</summary> /// <param name="lobbyName">ロビー名</param> /// <param name="maxPlayers">ロビーの最大プレイヤー数</param> /// <returns>作成したロビー</returns> private async UniTask<Lobby> CreateLobbyAsync(string lobbyName, int maxPlayers) { try { return await Lobbies.Instance.CreateLobbyAsync(lobbyName, maxPlayers); } catch (LobbyServiceException ex) { Debug.LogException(ex); } return null; } 今回はお試しなので指定していませんが Lobbies.Instance.CreateLobbyAsync の第三引数に CreateLobbyOptions という細かいオプションを渡すこともできます。 public class CreateLobbyOptions { // プライベート用のフラグ public bool? IsPrivate { get; set; } // ロビー製作者の情報 public Player Player { get; set; } // ゲーム固有のプロパティも設定できる public Dictionary<string, DataObject> Data { get; set; } } ロビーの入室 Unity Editorだけでは検証することができないのでアプリケーションをビルドしてロビーを作成した状態で検証します。 private async void Start() { // Unity Gaming Services の初期化処理 await UnityServices.InitializeAsync(); Debug.Log(UnityServices.State); await SignInAnonymouslyAsync(); var lobby = await QuickJoinLobbyAsync(); if (lobby != null) { Debug.Log($"ロビー入室成功! Id:{lobby.Id}, Name:{lobby.Name}"); return; } // ロビーに参加できなかったので作成する lobby = await CreateLobbyAsync("test lobby", 2); if (lobby == null) return; Debug.Log($"ロビー作成成功! Id:{lobby.Id}, Name:{lobby.Name}"); } /// <summary>条件に応じた適当な部屋に素早く入室する処理</summary> /// <returns>入室したロビー</returns> private async UniTask<Lobby> QuickJoinLobbyAsync(QuickJoinLobbyOptions options = null) { try { return await Lobbies.Instance.QuickJoinLobbyAsync(options); } catch (LobbyServiceException ex) { Debug.LogException(ex); } return null; } ロビーに入室できた時 ロビーに入室できなかった時 無事ロビーに入室するところまで実装することができました。 まとめ 今回は Unity Gaming Services を使って、ロビー作成、参加というマルチプレイの基本的な部分の実装を行ってみました。 Unity Gaming Services はマネタイズ、プレイヤーエンゲージメントなど広い領域をサポートするようなので今後のアップデートに注目していこうと思います。 今回のLobby実装はUnity公式にサンプルプロジェクトがあるので気になった方は触ってみると良いかもしれません! ※サンプルプロジェクトでは Vivox が使用されているのでダッシュボードにて「Vivox」を ON にする必要があります ここまで読んで頂きありがとうございました! Craft Egg Advent Calendar 2021 明日は@ryousangataの記事になります!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

同人プロジェクトマネジメント入門:仕様書作成編その1

同人プロジェクトマネジメント入門の目次はこちら はじめに 仕様書作成編その1では、そもそもどこに仕様書を書くかについて書いていきます。 仕様書ツールリスト 仕様書管理のジレンマ 初心者マネージャーが「プロジェクト管理ツール」「タスク管理ツール」などを見ているうちは「いいツール導入してうまくプロジェクトをまわすぞ~!」と思ってしまいがちですが、逆です。まず先に、ツールがないとプロジェクトが回しにくいだけの状況、ツールを導入する理由がある環境があって初めてツールが機能します。極論、2人で1か月で動かすプロジェクトの仕様なんて、ラインで送ればメンバー全体に確実に周知されますよね。こんな状況で複雑な変更承認プロセスなどを入れても非効率的です。 つまり、ツールを導入すればきっちりした管理が出来るが、ツール独自の管理コストが発生するというジレンマがあります。 加えて、同人プロジェクトだと、「ちゃんとツールを使わない人」が出てきます。技術的に使えない人、性格が雑で使わない人などです。さらに、そのようなメンバーには腐ったみかん理論が適用されます。つまり、ちゃんと使わない人がいると、周りがちゃんと仕様書にする意味が薄れるということです。情報がまとまっていない仕様書には意味がないですからね。したがって、ツールを厳格化すれば管理は簡単になるが、厳格なツールは怠惰な運用を生み、結果的に管理がより難しくなるというジレンマがあります。 では、以下、各仕様書管理ツールの紹介とその特徴・評価について紹介していきます。 Line 特徴 lineに仕様がある、というのは実質的に仕様書が存在していない状況です。これは止めた方がいいです。 「開発メンバーでLineグループを作って仕様を会話しながら決めて、そのTLを後から確認して実装を決める。決めたことをまとめてアナウンスする」みたいな雑な開発形態、やりがちではないでしょうか? 仕様を探すのに長時間上スクロール Lineの感覚で途中で雑談を挟んでしまい仕様会議が中断する 仕様のアナウンスを忘れて仕様が消滅する(というかアナウンスの個数制限がある) 正式に書式化されておらず認識に齟齬が発生しても気づけない 複数の仕様についての会話が同じTLに流れる など、ほぼ仕様書としての体を成しません。 しかしながら、開発メンバーに仕様書を見るモチベーションがなく怠惰な仕様決定をすると、まず間違いなくここまで堕ちます。 評価:止めた方がいい Lineのプロジェクトでの使用は止めましょう。Lineは友達とどうでもいい雑談するためのツールです。 Slack 特徴 基本的にはLineと同じです。 もちろん、Slackの方が高機能なのでマシになっている部分はあります。 スレッド機能があるので、複数の仕様について同一のTLに流れない ピン止め機能がLineより強いのであとからトピックの結論にさかのぼりやすい 検索機能が充実しているのであとから見つけやすい 評価:2人のプロジェクトなら十分、でも出来れば止めた方がいい ただ、Slackはチャットツールです。仕様を決める会話をSlackで行うのはいいですが、その結論は別の場所に書きましょう。 怠惰なメンバーがいると仕様書にせずにSlackの機能で済まそうとするので、心を鬼にして仕様書にまとめましょう。 Google Documents 特徴 もっとも単純なドキュメント共有方法だと思います。 仕様を別の場所に書いてまとめている、というだけで圧倒的にえらいです。 しかし、ドキュメントを作るたびに共有が必要であることや、ドキュメント間のリンクが出来ないことなどから、1ファイルが肥大化しやすく、仕様が巨大になってくると厳しいです。 評価:小さなプロジェクトなら十分 体感ですが、開発メンバーが3人程度で仕様書の骨子が10ページ以内に収まるような場合はこれで十分事足りると思います。 むしろ、下手に本格的なツールを使って開発メンバーがチャットツールに流れて仕様書が骨抜きになるより100倍マシです。 Scrapbox 特徴 ScrapBox気軽な無料wikiツールです。 誰でも気軽に編集ができる点 リンクが張りやすく、ドキュメントをたどりやすい点 ハッシュタグ機能によってドキュメントをまとめやすい点 全文検索も可能で記事を見つけやすい などの特徴から、Google Documentsに比べて圧倒的に複数ページを作りやすいです。 Google Documentsならば仕様書を複雑にするゴミ扱いだった細かい仕様のメモなども、すべてScrapBox上で一元管理できるのも嬉しいポイントです。 欠点としては、ページが増えまくるので、仕様の追加に一定人数の確認が必要になってくるような大きいプロジェクトでは、非常に高確率で認識に齟齬が生まれます。 総括すると、ScrapBoxは情報をばらして情報同士のつながりで整理することに特化していて、トップダウン・一覧性といった機能に弱みを持っています。 評価:「雑に作って十分機能する」というのは同人プロジェクトにピッタリ 体感ですが、開発メンバーにある程度仕様の裁量があるようなプロジェクトであれば、大抵うまく機能します。 個人の開発メモと、全体の仕様が一元管理できると、ツールの切り替えコストも減るので良いです。 実際の運用方法としては、トップダウン型の管理が必要なものだけはハッシュタグをつけてもらい、その他は各メンバーが自分の書いた記事に関連がある記事にリンクを張っていけばよいです。 同人プロジェクトにおいて一番素晴らしい点は「雑な運用をする1メンバー」の悪影響が少ない点だと思います。 また、Google Documentsと同様の運用をしたときに比べても追加で発生する手間がありません。機能が向上して手間が増えないのであれば、積極的に使っていけばいいと思います。 Github 特徴 プロジェクトの参加人数が増えてきた場合には、Githubによる編集が有効です。 その理由は、pull requestによって、仕様書の変更を見てほしい人に通知を送り、認識を合わせることが出来ることです。 また、「エンジニア部門の仕様書変更は、メインプログラマとマネージャーを必ずレビュワーに含めなければならない」というルールを設定すれば、勝手な仕様変更が防げます。 さらに、仕様決定の会話などはissueのテキストチャットで行えるので、slackやlineよりもTLが分散しません。トピックごとの一覧性を高く維持できます。 さらにシステムがGitなので任意のフォルダ構成に任意のファイルを入れることが出来ます。 テキストや画像だけでない様々な情報を管理する仕様書の場合は便利です。 評価:本格的過ぎて同人プロジェクトには向いていないかも… 充実した機能が魅力のGithubですが、しかし欠点もあります。小さなプロジェクトではあまりissue単位の会話などは発生しないことや、1対1でラインすればいいことをわざわざpull requestを投げてごちゃごちゃする作業が面倒なことや、自由な構成が出来るがゆえにルールを決めて守らせないと秩序が崩壊して可用性が下がることなどです。 同人プロジェクトでは、厳格なルールを守らせるのが難しいです。また、ラフな使い方をするメンバーがいても無理に矯正させられません。加えて、そもそもGitが分からないメンバーがいることも多いですし、勉強させるのも難しいです。さらに、レビューする人間が明確になる状況というのは、メンバ間の上下関係がきっちり決まっていて、かつ複雑な組織図になるときです。なぜなら、マネージャーだけが仕様を確認するだけでいいというならば、別にpull requestなどせずとも、単にマネージャーが変更差分を全部読めばいいだけだからです。仕様書においてpull requestが有効になるのは「複数の上司がいて、それぞれ担当部署の変更差分を読む」場合だと思います。通常のコード管理の場合なら、コードレビューやバグ防止などの役割を持ちますが、仕様書はコードほど厳密ではないです。 TL仕様書脱却ガイド 「TL仕様書」とは、LineやSlackなど、流れていくツールに仕様を書いている状況です。 ここからの脱却方法、防止方法を解説します。 こまめな声かけ 「じゃあこれ仕様書に書いておいてくれる?」とか「ちょっと仕様書見といて」とか「仕様書のリンク送るね」とかの発言を心がけましょう。 なるべくTLで質問に答えない マネージャーは開発メンバーに仕様を聞かれても、すぐに答えるのではなく、何らかの形で仕様書を作って「ここを見ろ!」と言いましょう。心を鬼にする必要があります。 アナウンス・ピン止め・メッセージの削除 マネージャーが定期的にTL上のスレッドやピン止めを見て、まとめが必要だと思われる部分については仕様書に書き出して、TLを消します。(Lineだとアナウンスを消せますし、Slackならメッセージごと消せます) そして代わりに、仕様書へのリンクを張り付けておきましょう。 TLにノイズ(雑談なりなんなり…)を流す 焦土作戦です。当然悪影響はありますが、メンバーが嫌々でも「仕様書の方を見る方が楽だ」と認識してくれればこっちのものです。 TLと仕様書の連携を簡単にする Slack Botを使って、「誰かが仕様書を編集したら通知が飛ぶ」を実装すれば、「仕様の相談をしたいから通知を飛ばすためにチャットを送信して、TLで(しかもDMで)相談して、結局TLが仕様書となる」という状況をある程度防げます。また、仕様書側に「○○どうなったか書いて!」と書き込み、その通知を送ることで強制的に仕様書に追加させる運用が出来ます。 Githubを使うなどしない限りは、「固定化された仕様書と会話して仕様を決めるチャット」の2極体制をとらざるを得ないのですが、仕様書変更で通知が飛んでくれれば多少行き来が楽になると思います。 分野ごとに運用を切り分ける 例えば、デザイン部門が小さくてプログラム部門が大きいプロジェクトだった場合、「全体的な仕様・プログラムの仕様はScrapBoxに書いて、デザインの仕様はSlackに書く」といった運用が出来ます。小さい部署に大きな管理コストを持たせるのは良くないという判断です。怠惰な運用をされる仕様書管理ツールを減らすのが最優先です。 おわりに メンバーの実力が分からない時点でのツール選定はかなり難しいです。 実際の運用としては、まずはSlackあたりから始めて、徐々にレベルを上げていくことをおすすめします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityでセーブ:ScriptableObjectもDictionaryもシリアライズしたい

やりたいこと セーブデータをファイルとして保存したい。 そのために、下記をシリアライズしたい。 Vector3などのUnity組み込みクラス Dictionary ※今回はシリアライズに関してなので、暗号化やファイル保存、逆アセンブル対策などは扱いません こまったこと Unityで利用できるシリアライズ方法は、主に2通り .Netで一般的な方法(例:System.Text.Json等)は、 Vector3やScriptableObjectなど、Unity組み込みクラスに対応しない UnityEngine.JsonUtility Unity組み込みクラスに対応しているが、Dictionaryを保存できない でもUnity組み込みクラスとDictionaryを両立したい! 解決方法 Dictionaryを含んだクラスは別に切り分け、byte[]としてシリアライズする ※JsonとかでもOK ※サンプルではNugetとか不要なBinaryFormatterを使用 サンプルコード using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using UnityEngine; [Serializable] public class SaveData { public Vector3 position; public byte[] dictionaryClassBinary; } [Serializable] public class DictionarySaveData { // フラグとかを想定 public Dictionary<string, bool> data1; } public class SaveTest : MonoBehaviour { public void Save() { var saveData = new SaveData(); saveData.position = transform.position; var dictionarySaveData = new DictionarySaveData(); // 適当にデータセット dictionarySaveData.data1= new Dictionary<string, bool>(); dictionarySaveData.data1["test"] = true; using (var ms = new MemoryStream()) { var bf = new BinaryFormatter(); bf.Serialize(ms, dictionarySaveData); saveData.dictionaryClassBinary = ms.ToArray(); } string saveDataJson = JsonUtility.ToJson(saveData); byte[] saveDataBinary = Encoding.UTF8.GetBytes(saveDataJson); // saveDataBinaryを暗号化してファイル保存 } public void Load() { string saveDataJson = /* ファイルを開いて復号、jsonを読み取ってくる */; SaveData saveData = JsonUtility.FromJson<SaveData>(saveDataJson); using (var ms = new MemoryStream(saveData.dictionaryClassBinary)) { var bf = new BinaryFormatter(); var dictionarySaveData = (DictionarySaveData)bf.Deserialize(ms); // trueと表示される! Debug.Log(dictionarySaveData.data1["test"]); } // Unity組み込みクラスも読み込めている transform.position = saveData.position; } } まとめ EasySaveみたいな優良有料アセットだと、こういう悩みとは無縁なのかなぁ… でもプログラム系のアセットは自分でなんとかしたい よきゲーム制作を!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity]ゲームオブジェクトのメッシュをobjで保存するスクリプト

はじめに なぜかみつからなかったので投稿.書くほどのことか?と思うが誰かの役に立ちそうなのでメモ. 色の保存はvertexColorでテクスチャではない.なおUnityは初期設定ではobjに記載されたvertexColorを反映できないので,保存した.objファイルを他のunityプロジェクトに流用する際はこの方法は向かない. 環境 Unity 2019.4 Windows 10 コード _.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; public class [classname] : MonoBehaviour { //保存対象オブジェクト(unity側で指定してください) GameObject model; // この関数を実行で保存.ボタンの関数等に設定してください public void objSaveButtonFunc(){ Mesh mesh = model.GetComponent<MeshFilter>().mesh; List<Vector3> _vertices = new List<Vector3>(mesh.vertices); List<Color> _colors = new List<Color>(mesh.colors); List<int> _triangles = new List<int>(mesh.triangles); objWriter(_vertices, _colors, _triangles); } private void objWriter(List<Vector3> list_v, List<Color> list_c, List<int> list_t){ string path = Application.persistentDataPath + "/test.obj"; using (var fs = new StreamWriter(path, true, System.Text.Encoding.GetEncoding("UTF-8"))) { print(path); // ファイルの文頭から頂点情報を入れると反応しないレンダリングソフトがあるのでコメント挿入 fs.Write("### created by unity script") for(int t = 0; t < list_v.Count; t++){ // 色つきで保存する場合はこのコメントを外して,下のfs.Writeをコメントアウト // fs.Write("v " + list_v[t].x + " " + list_v[t].y + " " + list_v[t].z + // " " + list_c[t].r + " " + list_c[t].b +" "+ list_c[t].g + "\n"); fs.Write("v " + list_v[t].x + " " + list_v[t].y + " " + list_v[t].z + "\n"); } for(int t = 0; t < list_t.Count/3; t++){ string f1 = (list_t[t * 3 + 0] + 1)+""; string f2 = (list_t[t * 3 + 1] + 1)+""; string f3 = (list_t[t * 3 + 2] + 1) +""; fs.Write("f "+f1+"//"+f1 +" "+ f2+"//"+f2+" "+f3+"//"+f3+"\n"); } fs.Write("# vertex: "+list_v.Count + " face: "+ list_t.Count); } } 解説 Unityの頂点番号は配列などと同様で0,1,2...n-1と保存されているが,.objのフォーマットでは1,2,3...nと記載するため,1を足している MeshLab2020.12 for Windowsでは文頭から頂点情報を書き込むと文頭の1点が無視されることを確認..objフォーマットの規則なのか文字コードが悪いのかバグなのか不明.詳しい人情報ください. 参考 hiramine氏ブログ-objフォーマット- Wikipedia -Wavefront_.obj-
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む