tanaka's Programming Memo

プログラミングについてのメモ。

Unity2021LTSでAndroidビルドのエラー

Voxelorer BirdをUnity2021LTSにバージョンアップしてAndroidでビルドしてみたのですが諸々エラー。Cross Platform Native Pluginsを入れるとエラーになるのですが、原因はUnityがインストールしたAndroid SDKのライセンスが通ってないという感じでした。

Android Studioを起動して、SDK Managerで以下をインストール。

上記をインストールした先のパスを、UnityのPreferencesのExternal ToolsのSDKパスとして設定しました。詳しくは以下の通り。

youdoyou-motto.com

エラー

発生したエラーは以下のような感じでした。

1つ目

Starting a Gradle Daemon, 1 incompatible and 6 stopped Daemons could not be reused, use --status for details

> Configure project :launcher
WARNING: The option setting 'android.enableR8=false' is deprecated.
It will be removed in version 5.0 of the Android Gradle plugin.
You will no longer be able to disable R8
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools\30.0.2\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-29\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-30\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools\30.0.2\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-29\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-30\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools\30.0.2\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-29\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-30\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\tools\package.xml. Probably the SDK is read-only
File C:\Users\YuTanaka\.android\repositories.cfg could not be loaded.
Checking the license for package Android SDK Build-Tools 29.0.2 in C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\licenses
Warning: License for package Android SDK Build-Tools 29.0.2 not accepted.
Checking the license for package Android SDK Platform 31 in C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\licenses
Warning: License for package Android SDK Platform 31 not accepted.

UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

2つ目

FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':unityLibrary:com.voxelbusters.essentialkit.androidlib:compileReleaseAidl'.
> Failed to install the following Android SDK packages as some licences have not been accepted.
     platforms;android-31 Android SDK Platform 31
     build-tools;29.0.2 Android SDK Build-Tools 29.0.2
  To build this project, accept the SDK license agreements and install the missing components using the Android Studio SDK Manager.
  Alternatively, to transfer the license agreements from one workstation to another, see http://d.android.com/r/studio-ui/export-licenses.html
  
  Using Android SDK: C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 10s
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8

UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)

3つ目

CommandInvokationFailure: Gradle build failed. 
C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\OpenJDK\bin\java.exe -classpath "C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib\gradle-launcher-6.1.1.jar" org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx4096m" "assembleRelease"

stderr[

FAILURE: Build failed with an exception.

* What went wrong:
Could not determine the dependencies of task ':unityLibrary:com.voxelbusters.essentialkit.androidlib:compileReleaseAidl'.
> Failed to install the following Android SDK packages as some licences have not been accepted.
     platforms;android-31 Android SDK Platform 31
     build-tools;29.0.2 Android SDK Build-Tools 29.0.2
  To build this project, accept the SDK license agreements and install the missing components using the Android Studio SDK Manager.
  Alternatively, to transfer the license agreements from one workstation to another, see http://d.android.com/r/studio-ui/export-licenses.html
  
  Using Android SDK: C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 10s
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
]
stdout[
Starting a Gradle Daemon, 1 incompatible and 6 stopped Daemons could not be reused, use --status for details

> Configure project :launcher
WARNING: The option setting 'android.enableR8=false' is deprecated.
It will be removed in version 5.0 of the Android Gradle plugin.
You will no longer be able to disable R8
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools\30.0.2\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-29\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-30\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools\30.0.2\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-29\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-30\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools\30.0.2\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-29\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-30\package.xml. Probably the SDK is read-only
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\tools\package.xml. Probably the SDK is read-only
File C:\Users\YuTanaka\.android\repositories.cfg could not be loaded.
Checking the license for package Android SDK Build-Tools 29.0.2 in C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\licenses
Warning: License for package Android SDK Build-Tools 29.0.2 not accepted.
Checking the license for package Android SDK Platform 31 in C:\Program Files\Unity\Hub\Editor\2021.3.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\licenses
Warning: License for package Android SDK Platform 31 not accepted.
]
exit code: 1
UnityEditor.Android.Command.WaitForProgramToRun (UnityEditor.Utils.Program p, UnityEditor.Android.Command+WaitingForProcessToExit waitingForProcessToExit, System.String errorMsg) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.Command.Run (System.Diagnostics.ProcessStartInfo psi, UnityEditor.Android.Command+WaitingForProcessToExit waitingForProcessToExit, System.String errorMsg) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.Command.Run (System.String command, System.String args, System.String workingdir, UnityEditor.Android.Command+WaitingForProcessToExit waitingForProcessToExit, System.String errorMsg) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.AndroidJavaTools.RunJava (System.String args, System.String workingdir, System.Action`1[T] progress, System.String error) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.GradleWrapper.Run (UnityEditor.Android.AndroidJavaTools javaTools, Unity.Android.Gradle.AndroidGradle androidGradle, System.String workingdir, System.String task, System.Action`1[T] progress) (at <089eca9a0a77417a901dda0093fda7f3>:0)
Rethrow as GradleInvokationException: Gradle build failed
UnityEditor.Android.GradleWrapper.Run (UnityEditor.Android.AndroidJavaTools javaTools, Unity.Android.Gradle.AndroidGradle androidGradle, System.String workingdir, System.String task, System.Action`1[T] progress) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.PostProcessor.Tasks.BuildGradleProject.Execute (UnityEditor.Android.PostProcessor.PostProcessorContext context) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.PostProcessor.PostProcessRunner.RunAllTasks (UnityEditor.Android.PostProcessor.PostProcessorContext context) (at <089eca9a0a77417a901dda0093fda7f3>:0)
Rethrow as BuildFailedException: Exception of type 'UnityEditor.Build.BuildFailedException' was thrown.
UnityEditor.Android.PostProcessor.CancelPostProcess.AbortBuild (System.String title, System.String message, System.Exception ex) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.PostProcessor.PostProcessRunner.RunAllTasks (UnityEditor.Android.PostProcessor.PostProcessorContext context) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.PostProcessAndroidPlayer.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args, AndroidPlayerBuildProgram.Data.AndroidPlayerBuildProgramOutput buildProgramOutput) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.Android.AndroidBuildPostprocessor.PostProcess (UnityEditor.Modules.BuildPostProcessArgs args, UnityEditor.BuildProperties& outProperties) (at <089eca9a0a77417a901dda0093fda7f3>:0)
UnityEditor.PostprocessBuildPlayer.Postprocess (UnityEditor.BuildTargetGroup targetGroup, UnityEditor.BuildTarget target, System.Int32 subtarget, System.String installPath, System.String companyName, System.String productName, System.Int32 width, System.Int32 height, UnityEditor.BuildOptions options, UnityEditor.RuntimeClassRegistry usedClassRegistry, UnityEditor.Build.Reporting.BuildReport report) (at <31b86d204baf45de8328f2d1261a79f7>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)

参考URL

ぺんそろ~Unity1週間ゲームジャム「そろえる」

ぺんそろ

ぺんそろ | フリーゲーム投稿サイト unityroom

unityroom.com

2022年05月02日(月) 00:00〜2022年05月08日(日)のGW中に開催されたUnity1週間ゲームジャム「そろえる」に参加しました。完成作品はこちらの「ぺんそろ」です。

ざっくり振り返りです。

目次

評価や結果

まずは終わったばかりで感覚を忘れないうちに結果から。いただいた評価は以下の通りでした。

みんなの評価

  • 楽しさ 3.952
  • 絵作り 4.048
  • サウンド 3.476
  • 操作性 3.571
  • 雰囲気 3.905
  • 斬新さ 3.714

楽しさ、絵作り、雰囲気が良い感じで、だ氏さんの参加の効果が発揮されています!!コメントでも「あぶな~い!!」システムが好評でした^^楽しさで44位にランクインできました。

楽しさ44位!!

遊んで評価を入力してくださったり、コメントいただいた皆様、ありがとうございました!!

今回の自分のテーマ

今回も開発関連のテーマ主体でした。

  • オブジェクト指向や設計、インターフェースの練習
  • URPとShaderGraphをいじる
  • アイテムなどにバリエーションを作る
  • 共同制作

自己完結できるテーマなので一通り達成できて収穫がありました。このあたりはまた別ブログで。

企画の振り返り

インターフェースを使ったり拡張性が出せそうなもので、苦手としているカードゲームや3マッチゲームのアレンジ辺りから考えてみたのですがまとめられず。共同制作だから確実に完成させられるように、床の高さを揃えてキャラクターを先に進める案で進めることにしました。これなら2~3日ぐらいでゲームは動くかな?と思ったのですが甘かったです^^;

クライマックス要素を入れたくて無敵アイテムで床まで壊しまくれるように考えました。発動すると面白いのですが、狙ってできないし、発生頻度も低いので不発だった気がします。

無敵!!

その代わりにお助けシステムが救済になりました。これによってキャラクター性が出せて、これまでの自分の作品にはない味わいが出ました。

あぶなーい!!

折角縦画面で作ったのでスマホアプリ化もなんとなく検討しています。が、スクロール型でいいのかとか、メイン要素が足りない気がするとか、検討中です。

次回に向けて

強そうな企画案が思い浮かばなかったので今回も開発練習が主体になりましたが、思い浮かびさえすれば企画主導のものも作ってみたいというのがあります。参加者の皆様の作品を見て感じたこと、自分でも試みてみたいことを次回に向けて挙げておきます。

  • 1画面にメインゲームを収める
  • 非リアルタイムで疲れない、癒し
  • 操作で変わった体験ができる
  • 操作やタイミングを失敗に直結させない
  • ランキングは最初に名前登録して自動送信(ただしランキングは無くても大丈夫)
  • (なんちゃって)リアルタイム順位表示
  • 動画撮影時は効果音のみにして、BGMは動画編集で入れる

今回も実り多い1週間ゲームジャムでした。ちゃんと収穫しないと・・・

参加の皆様、お疲れさまでした。遊んで下さった皆様、ありがとうございました!また次回の1週間ゲームジャムで!!

ジェネリッククラスの継承。抽象、インターフェイスも添えて

ジェネリッククラスを継承するための定義の書き方で難儀したのでメモ。

以下、簡易シングルトンをジェネリックで扱いつつ、それを更に解消しつつ、インターフェイスも実装。

    public abstract class ResqueActionBase<T> : SimpleSingleton<T> where T : IResqueAction, new()
    {
        // ...
    }

whereを継承元側に書くのか。未だにジェネリックは書き方を見慣れていないので難儀する。

インターフェイスを親に実装したら、子側にも実装を書く必要があるので注意。改めて考えれば当たり前だけど忘れてたので・・・。

参考URL

docs.microsoft.com

ClusterGAMEJAME 2022 in SPRINGに参加した48時間

天翔ける腕輪

天翔ける腕輪|メタバースプラットフォーム cluster(クラスター)

 

Cluster GAMEJAM 2022 Springの参加記念メダルが届きました!!

参加記念メダル

書かねば書かねばと思いつつなかなか手が付けられず開催から1ヶ月以上過ぎてしまいましたが、テーマ発表から公開までの48時間の記録です。

 

目次

 

一人企画会議(3/18 20:30-25:00)

開会式への参加からスタート!

テーマは「かける」!!翔ける、架ける、掛けるなど色々解釈ができて楽しそうなお題です。ちなみにこの時はデフォルトアバターでの参加でした。

 

テーマが判明した21時ごろから一人企画会議の開始です。Notionに思いついたことを箇条書きしたり、iPadのメモ帳に落書きしながら企画を模索しました。

  • 虹を動かす、あるいは発生させて橋をかけて、ワールドを巡ってスタンプ帳を完成させる
  • クレーンで橋を操作する
  • アイテムを使って橋を出現させる

準備段階で考えていた巨大ロボを動かしたいとか、ステージを大きく動かすところから着想。他のプレイヤーごと、ステージの地形をごっそり動かしたいとか、俯瞰でステージを見たいとか、ざっくりとしたイメージは沸くのですが、今ひとつ形にならず。引き続きアイディア出し。

  •  雨雲を操作して、雨が去ったら虹の橋がかかるので、それを歩いてゴールを目指す
  • クマを操作して、木を倒して橋をかけて移動する
  • クレーンを操作して、クレーンの先に吊り下げられているやかんからステージに置いてあるカップ麺や、他のプレイヤーにお湯をかけるというインパクト系のやつ

最終形までイメージが広がらないし、プレイヤーが移動している状態と何かを操作している状態の2つの状態があるとプレイ感が散漫になりそうです。乗り物に乗っている状態だけで完結することを考えて、靴を乗り物にする 案が出ました。

天翔ける腕輪の最初のイメージ

スタート地点から夜空を見上げると、他のプレイヤーが跳んでいる靴からパーティクルとトレイルが放たれて虹がかかっているように見える、そんなイメージです。操作方法、やること、見せたい画面のイメージがハッキリと思い浮かびました。この案はいけそうです。

 

当初は奇抜で驚けて笑えるものにしたかったのですが、アイディアが降ってこないのでは仕方ありません。それはまたの機会ということで、降りてきてくれたこの案を48時間で形にします!!

 

仮タイトルを「天翔ける靴」として、Notionに全作業を書き出して、大項目をツイート。

18日の21時から25時の4時間で企画と作業リストを作成して、少しUnityのプロジェクトの整理などをして初日を終えました。

制作1日目 3/19

画面作り -9:20

画面作りから制作開始です。予め調査していたPureNatureの岩のプレハブをシーンに配置して足場を作ります。

assetstore.unity.com

パーティクルやトレイルを奇麗に見せるために夜のステージにしたかったので、以下のようなアセットからよさげな星空を探しました。

PureNatureの空はデフォルトのSkyboxに雲のシェーダーで構成されていました。上空の設定なので星空が欲しいので不可です。Clusterのサンプルの星空も良い味わいなのですがディテールが合わなかったので不採用。Space Graphics Planetsは星そのものを作るのがメインのアセットでした。星空もあったのですがスクリプトが必要でClusterと相性が悪そうだったので不採用。ということで、最後はいつものFantasy Skybox | 2D Sky | Unity Asset Storeに落ち着きました。以前見た時より表現が増えていてかっこよくなってました!

assetstore.unity.com

 

いい感じの夜空と舞台ができました!

画面作り完了

プレイヤーのアニメ作成、音探し、BGM再生 9:20-13:50

プレイヤーを動かすのに必要な跳ぶ時のアニメーションを作成します。作業に取り掛かったところで、Clusterの乗り物に乗った時の足の位置は、乗り物に設定したアニメから変えられないことに気づきました。跳ねる時に足が動かないのは寂しいです。制御できるのは座る位置と腕の位置。ということで、飛ぶためのギミックは足ではなく、腕につけることに変更して「天翔ける腕輪」になりました。

 

腕輪をProBuilderで作成して、乗り物としてClusterのギミックをアタッチします。腕輪を拾ったり、置いたり、浮遊、加速、というアニメを腕輪や手の位置のオブジェクトの移動で作っていきます。腕輪の位置の調整は、Unity Editor上だとアバターが表示されないので、ビルドとClusterへのアップロードを繰り返して行いました。

 

昼食を食べながらBGMと効果音探し。今回は音楽の卵さんの曲でぴったりなものが見つかりました。

ontama-m.com

 

効果音はお世話になりまくっているUniversal Sound FXです。 

assetstore.unity.com

 

14時ちょっと前にBGMを鳴らして一段落です。

 

星モデル、スタンプカードの組み込み、クレジット表示 13:50-17:10

本体の開発に入る前に、準備済みのスタンプカードの組み込みとクレジット表示を実装しました。集める星のモデルをVoxelorerBirdから持ってきたり、スタンプ欄を7つに増やしたり、カードのデザインをしたり、クレジットを書いておくUIを設置したりして17:10ぐらいに完了しました。

星とスタンプ実装

夕方になってまだゲーム本体に手がついてません^^;

 

買い出し

リフレッシュを兼ねて買い出しへ。ご飯を作る時間を節約するために弁当やおにぎり、あとは景気づけのおやつをゲット。帰宅してご飯食べて作業再開。

 

ジャンプの実装 -25:24

日没後からようやくギミック作りです。まずはジャンプから。着地している時と空中にいる時で方向キーとスペースキーの操作が変わります。Is Grounded Character Item Triggerを使えば簡単に実装できると見込んでいたのですが、着地判定が甘くて飛べないことが頻発。Clusterのポニーのギミックをカンニングすると、着地判定はタイマーを使ったりして複雑なことをやってました。

まだできたばかりの機能なのでやむなし。今後の改善を願いつつ、ポニーの着地判定を参考にして必要な動きができました。

 

次は方向キーが押された時の前後左右への加速ギミックです。この時点では、左右は旋回ではなく直に移動でした。24:00ぐらいに作業完了です。

初日終わり

Clusterは内部の値が分からないので、左上にデバッグ用に文字列を表示していました。この辺りがInspectorに表示されるようになると、より開発がしやすくなりそうです。

 

移動周りの効果音を組み込んで開発1日目終わり。残りの作業量から睡眠時間を計算して就寝しました。

 

制作2日目(最終日) 3/20

操作の実装の続き 5:00-9:35

5:00頃から作業再開。前日は左右は直移動でしたが、マウスでカメラを旋回させてもプレイヤーの向きはそのままというのに気づいて、左右キーをプレイヤーの旋回に変更します。2時間ぐらいで対応完了。

 

トレイルとパーティクルを加えて、操作が一通りできるようになったよ報告ツイート。

 

企画の狙いだった軌跡で夜空に虹をかけるがいい感じで実現できてます。企画の筋が良さそうだと感じることができました^^ 

 

ジャンプの動きやジャンプ時にパーティクルを沢山出すとか演出を調整して、寝る前に考えた想定から30分ほど早い9:35に操作の実装を完了しました。

 

星の収集からエンディングの発動、リセット 9:35-12:40

星と星の収集、収集完了後のリセットです。以下のような感じでテストステージに星を並べて、色やアニメを付けたり、スタンプ帳へ反映させたり、全て取った時にクリアのタイムラインを開始したり、状態を元に戻すギミックを作ったり。

星のギミックの開発の様子

マップ作り 12:40-16:50

前日に購入していたおにぎりを食べながらマップ作りに取り掛かります。この段階まで想定より20分早くたどり着きましたが、余裕があるのはここまででした。

 

当初の予定は、スタートの地面があって、そこから上空に向かってところどころに地面が浮いていて、星も浮いていて、スタート地点から他のプレイヤーを含めてステージ全体が見上げられる、という感じでした。しかし実際に跳んでみると、近くにオブジェクトが無いと浮遊感を感じません。ということで、浮いている岩の間を抜けながら星を集めていくという方針に変更しました。

 

7つの星をどのように配置しようか。星が7つ > 7つの傷を持つ男 > 北斗...七星...。という電波を受信。ネットで北斗七星の画像を探して、真上から見てざっくり同じような形に星を配置しました。高さや岩の配置は後回しにして、星を並べた状態で跳んでみてステージの広さを確認しました。最初は1Kmぐらい必要かな?と予想していたのですが、飛んでみたら星まで遠い^^; 調整の結果、スタートからゴールまで250mぐらいになりました。

真上から見た星の配置

広さが決まったら、岩を並べたり、星の高さで緩急をつけたり、外に出られないように見えない壁で囲ったり、PureNatureによさげな雪のパーティクルがあったのでゴール付近に配置したり、ちょっとシロクマ隠したりして、16:47頃にマップの初期版が完成しました。

 

遊んでいるとなかなか面白いし、シロクマなどの予定になかったことをしたため、ここで2時間ほど予定オーバー!ゲーム説明やアップロードにかなり多めの時間を割り当てたり、20時に完了するような余裕を持たせた見積もりだったのでまだ間に合いますが、ここからは時間との勝負です!

 

クリア演出 16:50-17:50

全ての星を取り終えた時のクリアの演出の作成です。タイムラインを使って空に星を流そうと考えていたのですが、Playerの状態を反映させられるのはPlayer UIだけ、ということに作り始めてから気付きました^^; Canvas上でアニメするか・・・とも考えたのですが、時間がありません。ここはメッセージだけ!と割り切って、それっぽいメッセージをPanelで表示する演出をして、あとはスタートに戻るワープを作って、見込み通り1時間で完了させました。

この「どうでもいい演出案」が、クリア時に星をアニメさせるとか、シロクマに乗るとか、動く星があるとか、敵がいるとか、そういったものです。無くても成立するものは省いて公開に向けて集中です。

 

ハイスコアと仕上げ 17:50-20:20

自分の記録は保存できるので、クリアまでの時間がこれまでの最短だったら記録更新を称える機能を付けました。これで予定していた最低限の機能が揃いました。まだバランス調整に使える時間が残っていたので仕上げに入ります。

 

繰り返し遊んでみて、マップの配置を調整したり、ちょっと印象的なものが欲しくなってストーンヘンジみたいなのを配置しました。また、当初からあった案の一つである動く星を1つだけ実装。

 

まだバグが残っていましたが、時間内に解決できるか分かりません。一先ず、バグの内容をスタートの後ろのパネルに操作説明と一緒に書いておいて、バンカー版として公開することにしました。

 

ラストスパート 20:20-21:00

少し時間を残して応募ができました。あとは時間が許す限り、開始時に腕輪を置く音が同時に鳴ってうるさかったり、腕輪を置いて30秒するとスタート地点に強制的に戻されるバグを潰していきました。

 

20:46

 

20:55

 

バグ修正版のアップロードが完了したのは20:59でした。応募締め切りはサーバーの負荷が大変なことになっていたと思いますが、Clusterさんのサーバーは見事持ちこたえてくれました。素晴らしい環境です。

 

21:22

 

以上で「天翔ける腕輪」の応募が完了し、Cluster GAMEJAM 2022 Springを完走しました!!

 

今後のテーマ

今回の参加の目的の一つは、自分に足りないところを知り、これから取り組むテーマを見つけることでした。以下が挙がりました。

  • 「奇抜」「驚き」「笑い」の企画を考えるための入力とイメトレ
  • 他のワールドで圧倒された演出やギミックを観察して、ノウハウを増やす

他に取り組んでいることがあれこれあってこの一ヶ月で早速できていませんが^^;

これを書きながら思い出しました。振り返り大切。

 

最後に

結果は、お陰様で乗り物部門の大賞をいただくことができました!!

 

名前が呼ばれるまでは、他の受賞者の皆さんの作品を眺めて一人反省会をしていました。参加の目的だった今後のテーマを得ることや、実際にワールドを作る経験を得ること、応募作品数を1つでも増やして大会を盛り上げることは達成することができました。これでよし!と思っていたところで受賞というボーナスが加わりました。全力で取り組んで公開すると、時々、想定していなかった良いことがあると改めて感じました。参加すること大切

 

この後、念願だったシロクマに乗れるようにしたり、操作性を調整したりしたものが、現在公開されているものです。実は、当初の目的であった他のプレイヤーの飛ぶ姿を見る、というのが叶っていません。是非、お誘いあわせの上、大勢で星を巡ってみてください!!

cluster.mu

 

授賞式の時は、MagicaVoxelで作ったボクセルキャラクターをオリジナルアバターにしました。作成手順はこちら。

am1tanaka.hatenablog.com

 

使用アセット

AssetStore以外の素材

github.com

なにはなくとも。このプロジェクトを整理して作り始めました。乗り物をはじめ、様々なギミックの応用例が見れる有用な作例の塊です。

 

ontama-m.com

プレイ中、クリア時のBGMはこちらから。ワールドの雰囲気を作り出してくれました。

 

kenney.nl

スタンプ帳の星などちょっとした素材に利用しました。CC0で一通りの素材が見つかるので重宝してます。

 

freefonts.jp

日本語用のフォントアセットはこのフォントから作成しました。

 

fonts.google.com

等幅フォントはこちらから。

 

www.mixamo.com

リング装着中の基本姿勢などはこちらから。

 

Asset Store 

assetstore.unity.com

背景の足場や構造物、雪のエフェクトに利用しました。

 

assetstore.unity.com

夜空に利用しました。空で迷ったら大抵これに行き着きます。

 

assetstore.unity.com

効果音はいつもお世話になっている自分的定番アセットです。

 

assetstore.unity.com

スタート付近のリセットゾーンや、ゴールの先にあるワープゾーンはこのパーティクルから。

 

assetstore.unity.com

シロクマ親子のモデルとアニメモーションはこちらから。本当はここから沢山動物出したかったのですが間に合わず。またの機会に。

 

assetstore.unity.com

Mixamoで入手したモーションの編集に利用しました。

 

assetstore.unity.com

星のボクセルモデルの読み込みに利用しました。作品内ではありませんが、Cluster用のカスタムアバター作成でも利用しました。

 

 

GitHub Desktopをアップデートしたら外付けドライブのリポジトリが読めなくなった

表題の通りなのですが、GitHub DesktopをアップデートしたらPCの内蔵ドライブのリポジトリは問題ないのですが、外付けドライブのものが開けなくなりました。

解決策

4/14にアップデートが出て、外付けドライブのリポジトリを開こうとすると以下のような警告が表示されるようになりました。

f:id:am1tanaka:20220414202803p:plain
警告

これのadd an exception for this directoryのリンクをクリックすると、安全なディレクトリとして指定する設定が書き込まれて開けるようになりました。

原因

原因はGitのセキュリティ設定の影響だったようです。詳しくは以下の通り。

stackoverflow.com

内蔵ドライブのリポジトリフォルダーのセキュリティ設定が、Windowsにログインしたユーザーのものであれば問題なく開けるということです。外付けドライブはFATフォーマットでフォルダーの所有設定がないため、何らかの方法で設定ファイルに安全なフォルダーとして指定を加えないとgitで管理できなくなったわけです。外付けでなくても、同様にリポジトリフォルダーや.gitフォルダーがログインユーザーのアクセスが未設定だと、設定が必要になるようです。

設定

先に示したリンクのクリックを押さなくても、直にGitの設定ファイルにリポジトリのパスを加えればアクセスできるようになるようです。

gitのグローバル設定が~/.gitconifgにあるので、そのファイルを開いて、[safe]を書き加えて、その下の行にリポジトリディレクトリーを追加します。E:/ProjectFolders/Repoフォルダーなら下の通りです。

[safe]
    directory = E:/ProjectFolders/Repo

MagicaVoxelで作ったキャラをVRM化してclusterのアバターにする

clusterのゲームジャムイベントの時にデフォルトのアバターというのもなんだな~と思ってVoxelorerBirdの主人公キャラをアバター化してみました。

VoxelorerBirdの主人公にはすでにボーンを設定していたのであっさり持っていけると思っていたのですが、ボーンの構造が単純すぎてVRM化に失敗。clusterの公式サイトで紹介されているmixamoでボーンを自動設定するのも、ボクセルキャラが人型ではなかったため失敗。やむを得ないのでVoxel Importerであらためてボーンとウェイトを設定することにしました。その際の作業の備忘録です。

目次

使用ツール

以下のツールを使いました。

このうち、Voxel Importerは有料ですが、ボクセルを扱う最高のツールです。Unityでオリジナルのボクセルキャラを使ってゲームを作るならVery Animationとまとめてゲットしておくのがおススメです。

手順

MagicaVoxelでモデリング > Unityに読み込み > Voxel Importerでボーン設定 > dae形式でエクスポートして再読み込み > VRMにエクスポート、という流れです。

モデルの作成

Magica Voxelでキャラクターを作ります。いわゆるTポーズで作ります。Magica Voxelのサンプルのchr_knightとかのままだと変なになるので、あれを使う場合は装備を外して手を伸ばしてください。

NG

こんな感じに修正

左手の先に髪の毛が繋がってて駄目っぽいですが、Voxel Importerでうまいことウェイトを設定すれば切り離すことができます。斜め接続もVoxel Imporeterなら大丈夫です。

モデルができたらMagica Voxelのデフォルト形式のvox形式で保存すればモデル作成は完了です。Unityのプロジェクトがすでにあれば、プロジェクトのAssetsフォルダー以下の任意のフォルダーに入れればすぐに使えます。

ボーンの設定

作成したvoxファイルをVoxel ImporterでUnityに読み込んでボーンを設定します。

voxのインポート

  • 読み込み直後はボクセルの一辺1m相当で読み込まれて巨大なので、Scaleで適度な大きさに調整します。例えばchr_knightは高さが15ボクセルなのでそのままだと15mに相当します。Import Scaleのx,y,zすべてを0.08にすれば1.2m相当になります
  • Import Offsetは、Setボタンをクリックして、Feetを選択すると足元の丁度よさそうな場所が設定されます。

OffsetにFeetを設定

  • Animation欄でCreate > VoxelImporter > Editor > BoneTemplates > Maximum Humanoid(Mecanim).asset を選択して、必要なボーン構造を生成します

ボーンの生成

  • 生成したボーンのうち、Jawがあると後でエラーになるので削除します

Jawを削除

他にもclusterのドキュメントにないパーツは削除して構いません。

cluster. カスタムアバターの制限

  • RigのAnimationタイプに Humanoid を設定します

このままだとボーンが巨大すぎてclusterなどに持って行った時にちゃんと表示されないので、ボーンのサイズを合わせておきます。

  • Hierarchyウィンドウでvoxを読み込んだオブジェクトを開いて、Hipsを選択します

Hipsを選択

  • Inspectorウィンドウで Edit Bone Position をクリックして選択します

Bone Positionの設定

最初はこのぐらいボーンの大きさが合っていません。

Boneの位置合わせ

  • Scaling All欄に0.5を入力してApplyボタンを押して、それらしいサイズに調整します

ざっくりサイズ合わせ

ボーン合わせとウェイト設定は慣れるまで大変なので、先に動作確認のためにVRMに出力してみましょう。

VRMの作成

VRMを作成するには、notargsさんのUniVRMを利用します。

github.com

ドキュメントに従ってUnityプロジェクトにインポートしてください。

Voxel ImporterのオブジェクトのままだとVRMに書き出せないので、一度エクスポートしてUnityの通常のアセットにします。

  • InsectorウィンドウのVoxel Skinned Animation Objectの三点アイコンをクリックして Export COLLADA(dae) File を選択します

daeファイルでエクスポート

  • Assetsフォルダー内の適当な場所にエクスポートします。チェックは全てついていてOKです
  • Projectウィンドウでエクスポートしたdaeファイルを選択します
  • InspectorウィンドウでRigを選択して、Animation TypeがHumanoidになっていることを確認します。違う場合はHumanoidにしてApplyをクリックします
  • VRM0メニューから Export to VRM 0.x を選択します

VRMエクスポート

  • 下の方にあるTitle欄にアバター名を入力します。例えばchr_knightなど
  • Versionに適当なバージョン番号(0.1.0など)を入力します
  • Authorに著作権者名入力します。自作のモデルなら自分の名前、他の人のものならその人の氏名(chr_knightならEphtracy)を入力します

以上設定したら Export ボタンをクリックして出力します。デスクトップなどの適当なフォルダーを指定して出力すれば完了です。出力したvrmファイルをclusterのアバターなどにアップロードします。

clusterのアバターにアップロード

ウェイトを設定していないのでTポーズのままですが、ワールドに入れるはずです。

とりあえずアバター化成功!!

あとは必要に応じて Voxel Skinned Animation Object の Advanced 設定をしたり、ウェイトを設定して、daeファイルのエクスポートからVRMファイルを作り直せばOKです。

Voxel Importerでのウェイト設定

苦戦したのでコツをメモしておきます。なにはともあれ、まずは公式動画を一度視聴します。

youtu.be

以下が作業のコツです。

  • 近寄って作業したいのにモデルがカメラを突き抜けてしまうとか、回転位置がおかしくて操作しにくい場合、注視点を設定することで直せます。Hierarchyウィンドウでウェイトを設定したいキャラクターをクリックして選んで、Sceneウィンドウ上にマウスカーソルを移動させたら、Shift + F キーを押します。これでズームや回転が操作しやすくなります
  • Sceneウィンドウは Iso(等角図)にした方がパースがかからずに設定しやすいと思います

Isometricモード

  • ウェイト編集モードでモデルが真っ黒になって形状が確認しにくい時は、Spaceキーを押すと元の絵が表示できます
  • Spine(背骨)など親側から設定します
  • 全てのボーンにウェイトを設定する必要はなく、Spine, Left Upper Arm, Left Lower Arm, Left Hand, Head, Left Upper Leg, Left Lower Legあたりにざっくりと設定します
  • 頂点モード(Vertex)のRectでざっくりと設定すると、内部のボクセルもまとめて塗られて楽です
  • ボクセルが隣接していたり、斜めになって頂点が共有されている場合は、VoxelモードとVertexモードを上手に使い分けて塗ると正しく分割できます

髪とくっついている手をVoxelモードでウェイト設定

手にウェイトを設定。髪の毛は真っ黒なのでウェイト0

切り離してアニメできます

  • ウェイトは1か0でめりはりをつけて設定しておく感じ
  • まずはざっくり設定して、細かいところはあとで修正

こんなところです。はじめのうちはコツが掴めず何回もやり直しましたが、動画を参考に、アニメさせながら調整するとか、ざっくり設定する感覚が把握できると、結構さくさくと設定できるようになりました。

揺れもの

UniVRMのVR Spring Boneを使うと、髪の毛やしっぽなどの揺れものを設定できます。詳しくはVirtualCastの以下のWikiにあります。

virtualcast.jp

Voxel Importerで作成した場合はドキュメント内のsecondaryに該当するものがないので、以下のような手順で下準備します。

  • ボーンにしたい位置に空のゲームオブジェクトを作成して、Voxel Importerの Voxel Skinned Animation Object Bone をアタッチしてボーンにします
  • ウェイトを設定します
  • Collada形式でエクスポートして、Unityオブジェクトに変換します
  • daeファイルをHierarchyウィンドウにドラッグ&ドロップしてシーンに読み込みます
  • シーンに読み込んだルートオブジェクトの子供にスプリング管理用の空のゲームオブジェクトを作成して、VR Spring Boneコンポーネントをアタッチします

あとは、Virtualcastのドキュメントに従って揺れ設定をすればOKです。

移動や回転が速すぎると当たり判定を設定しても反対側にすり抜けてしまって戻らなくなったりしました。スプリング(Stiffness Force)を最大に固くして、抵抗(Drag Force)を大きくして、重力を軽くして(Gravity Dirを0に近づける)、動きを抑えてなんとなく安定させました。

まとめ

MagicaVoxelで作成したvoxファイルをUnityのVoxel Importerでボーンを入れて、UniVRMでVRMファイルに出力してclusterのアバターを作成しました。

  • Magica Voxelでモデルを作成してvoxファイル化
  • Voxel ImporterでvoxファイルをUnityにインポート
  • Voxel Importerでボーンの作成とウェイト設定
  • Voxel ImporterでモデルをエクスポートしてUnityオブジェクト化
  • UniVRMでVRMファイル出力

難関はウェイト設定ですが、Voxel ImporterのVoxelモードとVertexモードを活用すると、くっついているボクセルを切り離すことができるのでモデル作成の制約が減ります。ウェイト設定時に Shift + Fキーでキャラクターに注目することと、Spaceキーで元の絵が見えることを知ると作業効率が格段に上がりました。

ボクセルならモデルが作りやすいのでモデリングが苦手な人でも自分用のオリジナルアバターが作れるのではないかと思います。

参考URL

水面やUnlitシェーダーとDepth of Fieldの不具合対策メモ

Depth of Fieldと相性が悪いもの

オブジェクト指向を勉強するための素材となる海賊をモチーフにしたスゴロクもどきゲームを開発しています。

制作中の海賊スゴロク(仮)

箱庭っぽいイメージを狙ってPost ProcessingのDepth of Field(DOF:被写界深度)を設定して、水面はフリーながらカッコいいLowPoly Waterを組み込みました。いい感じでできたと思ったのですが、水面に配置したFadeの波紋オブジェクトが消えたり、手前にある水面がボヤけたりと謎の症状が発生しました。それらの問題の原因と解決策の備忘録です。

目次

問題

問題を検証するためのプロジェクトを作成しました。手元で確認したい場合にご活用ください。セットアップ方法はリポジトリのREADME.mdにあります。

github.com

3つ並んでいるうちの真ん中のCubeは正常に描画されています。Materialの設定は、シェーダーはStandard、Rendering ModeはOpaqueです。Cutoutでも正常に描画できます。

FadeとUnlitと水面

左右のCubeも同じ奥行きに配置しているのですが、以下のような問題が起きてます。

  1. 左のCubeの下の消失。StandardシェーダーのRendering ModeをFadeにすると発生します。Transparentでも同じ状態になります
  2. 右のCubeのボヤけ。Unlitシェーダーで描画しています
  3. 真ん中のCubeの下の水面のボヤけ。遠くにあるようにボヤけていますが、水面は立方体の直下です

原因

1. FadeやTransparentが消える

この症状は、FadeやTransparentのオブジェクトと、LowPoly Waterで作成した水面オブジェクトの位置関係で発生します。

消えたり現れたり

上からみるとこんな感じ

水面の中心がCubeの中心よりカメラに近くなると不具合が発生します。

LowPoly Waterの水面のRender Queueは、FadeやTransparentと同じ3000に設定されています。Render Queueは、描画処理の仕方や描画順を決める値です。詳しくは ShaderLab: SubShader 内のタグ - Unity マニュアルレンダリング順 - Queue タグにあります。

Render Queueは描画順などを決める設定で、値が小さい方から描画します。Render Queueが同じ値の時は、2500以下かどうかでルールが変わります。2500以下は不透明扱いになり、Zバッファ(奥行きバッファ=デプスバッファ)への書き込みとテストをしながら手前から奥へ描画していきます。Zバッファより奥のものは見えないはずなので描画を省いて時間を節約します。2500より大きい場合は半透明として扱います。奥の物が透けて見える可能性があるので、Zバッファは更新せず奥から手前に上書きしていきます。

ここで問題となるのが水面のシェーダーです。LowPoly Waterは独自のWaterShadedシェーダーで水面を描画します。Zバッファを参照して、水面下の不透明物と水面の距離に応じて波を表現したり、水面下を半透明で描いたりしています。この処理が描画済みの半透明のオブジェクトを考慮していないため、FadeやTransparentのオブジェクトが奥にあって先に描画されていた場合に水面が上書きして消してしまうのです。

2. Unlitのオブジェクトがボヤける

ボヤけるのはDepth of Fieldの効果ですが、Unlitのオブジェクトの奥行きが正しく反映されずボヤけ過ぎてしまっています。よく観察してみると位置によってボヤけ方が変わっています。

水面の下を観察

赤で囲った部分とそれより下では、囲んだ範囲の方が少しだけボヤけ具合が弱くなっています。また、左のFadeのCubeの表示範囲と赤で囲った部分が一致しています。FadeやTransformはZバッファに奥行きを書き込まないので、奥にある不透明なものまでの距離に応じたエフェクトがかかっているのです。Unlitも原因は同じです。Unlitは不透明でもZバッファに奥行きが書き込まれないのです。

Zバッファに書き込まれる時の条件が書かれている公式マニュアル。

docs.unity3d.com

以下の3つの条件が成立している状態でShadow Casterパスを描画する時に、ハードウェアがZバッファに奥行きを書き込みます。

  • マテリアルのRender Queueが2500以下
  • ZWrite On
  • ShadowCasterのパスが有効であること

Unlitは光の影響を受けないので影が描画されません。つまり、Zバッファを書き込むShadowCasterパスがないのです。Depth of Fieldが正しく反映されないのはこれが理由です。

3. 水面がボヤける

水面シェーダーのRender Queueが3000なので、水面の高さはZバッファに書き込まれません。どうせ半透明の描画順を調整するので、水面シェーダーを不透明なQueueにして、Shadow Casterを有効にすれば解決と思ったのですが、別の問題が発生しました。

水面が真っ白に!

これは開発中の海賊スゴロクで水面の高さをZバッファに書き込んだ時のスクショです。ボヤけは解決しましたが水面が真っ白になっています。水面の高さをZバッファに書き込んだことで水面のすぐ下に物があると水面シェーダーが判定してしまい、波の白で塗りつぶしてしまうのです。水系のシェーダーはこの辺りの対策が必要です。

これで全ての原因が分かったので解決していきます。

解決編

FadeとTransparentの消失とボヤけ問題

消失問題はRender Queueを調整して水面を先に描画するようにすれば解決します。更にボヤけを防ぐには、Render Queueを2500以下、かつ、Shadow Casterパスの追加をしたカスタムシェーダーを作ります。

新規にUnlitシェーダーを作成して、ZBufferShaderのような名前にして以下のようにします。

Shader "Unlit/ZBufferShader"
{
    SubShader
    {
        Tags { "Queue"="AlphaTest+1"}
        LOD 100

        Pass
        {
            Tags { "LightMode"="ShadowCaster"}

            ZWrite On
            ColorMask 0
        }
    }
}
  • Render QueueはAlphaTest+1として、Cutoutより後で描画します
  • ColorMask 0は色もアルファ値も書き込まないようにする設定で、Zバッファだけ書き込みます

StandardシェーダーのFadeでの描画に加えて、このシェーダーでもCubeを描画します。Standardシェーダーに手を加えるとか、自前で後付け描画するなどのスマートな方法が考えられますが、今回はMesh Rendererに複数マテリアルを設定することにします。

このシェーダーを設定したマテリアルを作成して、FadeのCubeのMesh Rendererにテクスチャを追加します。

Zバッファ専用マテリアル

「同じメッシュを別のマテリアルでそれぞれ描画するからパフォーマンス落ちるぞ。マルチパスを推奨」と言われてます。しかし、マルチパスでRender Queueを変える方法がなさそうだったので承知の上で押し通します。

これで問題が解決です。真っ白な部分がありますが、これは水面シェーダーが原因なので後で直ります。

水面シェーダーの影響で真っ白

水面がなければ大丈夫

注意!!

不透明なQueueにしたことで、手前から奥に描画することになります。そのため、このMaterialを設定したFadeやTransparentのオブジェクト同士が重なると奥にあるものが描画されなくなります。この辺りを完全に解決する手段はなさそうで、Render Queueをいじるなどして調整するようです。

Materialに表示されている警告が気になる場合は、自前でスクリプトを作成して描画すれば表示されなくなります。そのものズバり欲しい情報がLIGHT11さんのブログにありました。

light11.hatenadiary.com

やってることはMesh Rendererに警告されたことを自前のスクリプトに移動しただけのような気がするので、警告は消えますがパフォーマンス的には同じような気がします。本題の海賊スゴロクではこの問題は対応しなかったので今回は手を出しませんでした。サブメッシュが絡んできたら必要になるかも知れませんし、実際にはパフォーマンスの違いがあるかも知れません。その辺の問題が出た時にまた調査ということにします。

Unlitシェーダーのボヤけ問題

カスタムのUnlitシェーダーを作成して、ShadowCasterを有効にしたパスを追加します。これはよくある問題のようであちこちで解決策が見つかりました。公式を参考に解決します。

docs.unity3d.com

ついでにデフォルトだと半透明に対応していないので対応させました。新規にUnlitシェーダーを作成して、UnlitZBufferという名前にして以下のコードを書きます。

Shader "Unlit/UnlitZBuffer"
{
    Properties
    {
        _Color("Main Color", Color) = (1,1,1,1)
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "AlphaTest+1" "IgnoreProjector"="True"}
        LOD 100

        Pass
        {
            Tags {"LightMode"="ShadowCaster"}
            ZWrite On
            ColorMask 0
        }

        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv) * _Color;
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

今回は追加パスとかは不要なので、既存のMaterialのシェーダーをこれに変更して解決です。

カスタムUnlitZシェーダー適用!

半透明の部分が変な感じになりますが、これも水面の問題です。中央のCubeの色が変わるのはgif化した時の影響なので実際には起きません。

水面のボヤけ問題

この解決には、水面シェーダー用のカメラが必要になります。解決のヒントは先日ゲットしたTanuki Digital - Asset StoreさんのSUIMONO Water Systemで見つけました。

assetstore.unity.com

確認したところ、水の演出のために沢山のカメラが使われていました!!

カメラ軍団!!

水面と水面下でそれぞれDOFを適用するなら別の奥行きが必要になりますし、水面下の半透明物は?とか考え出すと水系は大変だなと実感しました。優秀なアセット作者の皆様に感謝!!

水面シェーダー用のカメラの作成

水面下のZバッファを描画する水面用のカメラを作ります。メインカメラと同じものが必要なので、分かりやすくメインカメラの子供にしました。描画前に位置を合わせればいいので実際には場所はどこでも良さそうです。SUIMONOでは水面用のオブジェクトにまとめられてました。

  • HierarchyウィンドウのMain Cameraを右クリックして、Cameraを追加します
  • 追加したCameraのAudio ListenerをRemoveします
  • 新規でC#スクリプトを作成して、名前をUnderWaterCameraにして、以下のようにします
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
public class UnderWaterCamera : MonoBehaviour
{
    /// <summary>
    /// RenderTextureのサイズ
    /// </summary>
    static int renderTextureSize => 512;

    /// <summary>
    /// ビット数
    /// </summary>
    static int renderTextureDepth => 24;

    Camera sourceCamera;
    Camera underWaterCamera;
    RenderTexture underWaterTex;
    float currentAspect;

    private void Awake()
    {
        sourceCamera = transform.parent.GetComponent<Camera>();
        underWaterCamera = GetComponent<Camera>();
        underWaterCamera.CopyFrom(sourceCamera);
        underWaterCamera.cullingMask = underWaterCamera.cullingMask & (-1 ^ LayerMask.GetMask("Water"));
        underWaterCamera.clearFlags = CameraClearFlags.Depth;
        underWaterCamera.depth = -100;
        underWaterCamera.depthTextureMode = DepthTextureMode.Depth;
        underWaterCamera.targetTexture = null;
        UpdateRenderTex();
    }

    private void LateUpdate()
    {
        UpdateRenderTex();
        Shader.SetGlobalTexture("_WaterDepthTex", underWaterTex);
    }

    private void OnDestroy()
    {
        DestroyTexture();
    }

    void DestroyTexture()
    {
        if (underWaterCamera.targetTexture != null)
        {
            underWaterCamera.targetTexture = null;
        }
        if (underWaterTex != null)
        {
            DestroyImmediate(underWaterTex);
            underWaterTex = null;
        }
    }

    /// <summary>
    /// RenderTextureを生成
    /// </summary>
    void UpdateRenderTex()
    {
        if ((sourceCamera == null) || (underWaterCamera == null)) return;

        if (underWaterTex != null)
        {
            if (currentAspect != sourceCamera.aspect)
            {
                currentAspect = sourceCamera.aspect;
                DestroyTexture();
            }
            else
            {
                return;
            }
        }
        underWaterTex = new RenderTexture(
            renderTextureSize, renderTextureSize,
            renderTextureDepth,
            RenderTextureFormat.Depth,
            RenderTextureReadWrite.Linear);
        underWaterTex.dimension = TextureDimension.Tex2D;
        underWaterTex.autoGenerateMips = false;
        underWaterTex.anisoLevel = 1;
        underWaterTex.filterMode = FilterMode.Point;
        underWaterTex.wrapMode = TextureWrapMode.Clamp;
        underWaterCamera.aspect = sourceCamera.aspect;
        underWaterCamera.targetTexture = underWaterTex;
        currentAspect = sourceCamera.aspect;
    }
}
  • これを先に作成したCameraにアタッチします
  • Hierarchyウィンドウで Ocean をクリックして選択して、LayerをWaterにします

これで水面下を描画するカメラと、水面のレイヤー設定ができました。スクリプトでは、Main Cameraの設定をCopyFromでコピーしたり、Waterレイヤーを描画候補から外すためのcullingMaskの設定などをしています。描画するテクスチャは_WaterDepthTexという名前で、シェーダーにGlobalTextureで渡します。

警告やエラーが出る場合

RenderTexture.Create: Depth|ShadowMap RenderTexture requested without a depth buffer. Changing to a 16 bit depth buffer.というような警告が表示されて、grabがどうこうというエラーが出る場合があります。その時は、81行目のDepthをDefaultに変更してみてください。

// 81:
            RenderTextureFormat.Default,

これで一旦エラーが消えるので、作業を完了させてください。作業が完了したらDepthに戻します。それでとりあえず動きました。このエラーは水系の色々なアセットで発生しているようですが、今回は直す決め手を見つけることはできませんでした。

WaterShadedの改造

仕上げにLowPoly WaterのWaterShadedシェーダーに手を加えます。やることは以下の通りです。

  1. 水面下の距離を_WaterDepthTexから取得するようにします
  2. Render Queueを、不透明でFadeやUnlitより前に描画するように変更します(AlphaTest-1)
  3. 水面描画時にZWriteをOnにします。これをしないと水面が描画されません
  4. Shadow Casterパスを追加します。これでDOFを有効にします

ProjetウィンドウのAssets > LowPolyWater_Pack > Shaders フォルダーを開いて、WaterShadedシェーダーをダブルクリックして開きます。以下に従って修正してください。

  • 26行目付近の_CameraDepthTexture_WaterDepthTexに書き換えます
  • 159行目のhalf depth=から始まる行にある_CameraDepthTexture_WaterDepthTexに書き換えます
// 159:
            half depth = SAMPLE_DEPTH_TEXTURE_PROJ(_WaterDepthTex, UNITY_PROJ_COORD(i.screenPos));

これでMain CameraのZバッファではなく、こちらで作成した_WaterDepthTexから奥行きを取り出すようになります。カメラのaspectを正しく設定しておけば、正方形のRenderTextureからちゃんとアスペクト比に従って参照してくれます。

次にRender Queueを変更します。

  • 183行目付近のTagsを以下のように修正します
// 183:
    Tags {"RenderType"="Transparent" "Queue"="AlphaTest-1"}

水面描画時のZWriteをOnにします。

// 193:
            ZWrite On

207行目の}の後ろで改行して、以下のShadowCasterのパスを追加します。

// 208:

    Pass{
            Tags { "LightMode" = "ShadowCaster"}
            ZWrite On
            ColorMask 0
    }

以上で完了です。Playすると水面がくっきり表示されるようになります。

水面くっきり!

この時、島の下が真っ白になっている場合は、CameraにアタッチしたスクリプトDepthのところをDefaultに変更したのではないかと思います。元のDepthに戻してみてください。

Unlitが白くなるのを解消する

右のCubeの下半分が真っ白です。これはCubeのZバッファに水面シェーダーが反応して波を描いてしまうからです。水面と同じくこのCubeのレイヤーをWaterにすることでぱっと見では解決です。

Waterで問題を回避

よくよく見るとCubeの場所だけ水面がくっきり見えています。DOFがCubeの距離で反映するからですが、これ以上は別テーマになりそうなので今回はここまでにしておきます。

まとめ

ちょっと見栄えを良くしようと思って入れたDepth of Fieldが思わぬ問題を巻き起こしました。理屈を知ってみると、DOFと透過物との相性の悪さ、特に水面はなかなかに難儀な対応が必要ということが実感できました。これらを対策済みの水や透明系アセットの有難さが分かりました。

この調査を通じて、以下のようなことを知ることができました。

  • Render Queueは2500以下が不透明、それ以降が透明扱いになり、描画ルールが変わる
  • ZバッファはRender Queueが2500以下、ZWrite On、Shadow Casterのパスでハードウェアに書き込まれる
  • 不透明のキューでもBlend設定で半透明の描画はできる
  • ShadowCasterでZバッファを書き込む場合は、他のパスもZWrite Onにしないと描画されなかった(水面)
  • エフェクト用のカメラでRenderTextureに欲しい画像を書き込んで、Shader.SetGlobalTexture()でシェーダーに渡せる
  • カメラのaspectを設定すれば、正方形のRenderTextureからスクリーン座標で色を取り出せる
  • RenderTextureを作成する時に謎の警告とエラーがでたら、とりあえずRenderTextureFormat.Defaultで作成しておく

今回の作業では、公式ドキュメントに加えてLIGHT11さんのブログに大変お世話になりました。助かりました。

Win, Mac, WebGL, Android(Pixel3a)では動作確認しました。Pixel3aだとかなり重い感じでしたが動いてはいました。

memo:シェーダーのRender Typeについて

Render Typeは特殊なエフェクトなどのためにシェーダーを置き換えたい時に、置き換える候補を指定するのに使うとのこと。

docs.unity3d.com

Zバッファのみとか不透明なQueueで半透明にしてたりとかイレギュラーなことをしていて何を設定するのが正解か分からなかったので、今回はなんとなくで設定しています。何かのエフェクトが正しく描画されない場合はこの辺りが原因かも知れません。

参考/関連URL