UnityとVite-React(webpackになった)でゲームを作ったときに困ったこと
ロープの実装が大変、というか暴れる
ロープを実装するために、ロープを分割してそれぞれをjointで接合しているが、それぞれの角運動の計算を簡略化しているため時間がたつほどに実際の数字と乖離していく
ただほかの剛体物理エンジンのBox2Dとかではちゃんと計算してくれていてjointが強靭らしい(unityはデフォルトではPhysX)
が、Unity 2Dでの計算ではBox2Dを使っているらしい
今は2Dのゲームを作っているが、まだ恩恵を預かっている感がない
Polygon Collider 2Dの設定がだるい
Polygon Collider 2Dが初期だと解像度によってPathがめっちゃおおくなる
こんなシンプルなオブジェクトが
こんなに複雑になる、とくにかごは中が開いているのでとてつもなく多くなる
しかも、このGUI上ではポイントを消すことができない、InspectorのPointsをちまちま消していくしかない...うむむポイント
本当はこのくらいシンプルにしたい
ただ、sprite editorでGUI上で分かりやすい操作ができるらしい(かなり前の話)、Scene上でもできるようにしてほしいでやんすね...
レイヤーが違くてもオブジェクトが重なってるとクリックできひん
ドラックアンドドロップをonMouseDown()で実装してたが、ある時を境にクリックしても反応しない時がでてきた(うける)
根気強く、クリック連打して反応する場所としない場所を調べたら、
isTriggerだから通り抜けるけどColliderは設定してあるオブジェクトのところが反応しない事に奇跡的に気が付いた
色々調べてたら、解決法っぽいのがでてきた
EventSystemを使った、タッチされたオブジェクトの取得方法を以前紹介しましたが、この方法では1度に1つのオブジェクトしか取得できません。
そのためオブジェクト同士が重なっていると、最初にEventを取得した1つのオブジェクトしか取得することができません。また厄介なことに、取得できるオブジェクトは一番手前のオブジェクトであるとは限らないです。
なので違うアプローチで複数同時タッチに対応します。
https://pengoya.net/unity/object-touch/
Layerが上だったら勝手にクリックできるものだと思っていたが、そういうことではないらしい
大人しくRaycastを飛ばして、大人しく自分のオブジェクトだけを取得する実装に変えたらできた(うける)
void OnMouseDown()
{
isDragging = true;
rb.gravityScale = 0; // 重力を無効にする
rb.freezeRotation = true; // 回転を固定する
offset = transform.position - Camera.main.ScreenToWorldPoint(Input.mousePosition);
}
void Update()
{
// クリックが押されたら
if (Input.GetMouseButtonDown(0))
{
// 貫通Raycastビームを飛ばして、ぶつかったオブジェクトを検索
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D[] hits = Physics2D.RaycastAll(mousePosition, Vector2.zero);
foreach (RaycastHit2D hit in hits)
{
if (hit.collider != null && hit.collider.gameObject == gameObject)
{
OnMouseDown();
break;
}
}
}
if (Input.GetMouseButtonUp(0))
{
if (isDragging)
{
OnMouseUp();
}
}
if (isDragging)
{
OnMouseDrag();
}
}
Layer Collision Matrixが効かないお
Edit > Project Settings > PhysicsでLayer Collision Matrixしてるのに効かないお...(^ω^ )
Physicsの方を見ている愚かな人間
って思ってたら、3Dの方を見てた
Phyisics 2Dをみたら、綺麗なMatrixが!!
Physics 2D
われわれは、賢明になるためには、まず馬鹿にならなければならない。己れを導くためには、まず盲目にならなければならない。
モンテーニュ 「随想録」
参考:https://discussions.unity.com/t/collision-matrix-not-working/794089
二次元配列をinspectorで設定できない
配列の配列とかを扱いたいときに、insepctorからは設定できない
なのでListのClassのListを使って設定する
public class WeaponShelfManager : MonoBehaviour
{
[SerializeField]
private List<WeaponSet> weaponSets; // 武器のセットを管理するリスト
...
}
// 武器のセットを表すクラス
// Unityのinspectorでは2次元配列の設定ができないため、Listのクラスのリストを使用
[System.Serializable]
public class WeaponSet
{
public List<GameObject> weapons; // 武器のリスト
}
こんなかんじになる
型安全にいきたい
Stringで処理を分けているところがよくでてくるんだけど、もっと型安全にやる方法はないのかな
classとか作ればいけるか、普通に
いずれにせよ、ゲームの仕様書作成とかってまじで大変そう、やってる人すごいな
if (child.CompareTag("Weapon") && child.gameObject.layer ==
LayerMask.NameToLayer("WeaponInFront"))
{
shouldMoveWeapon = false;
break;
}
オブジェクトのサイズの把握が大変
Inspectorに書いてあるのってscaleで、オブジェクトのサイズって書いてないよね
Sceneにグリッドはあるのに...定規で測るか...
※スクリプトで出すときはこれらしい
GameObject sqObj = GameObject.Find("Square"); // 目的のスプライトのオブジェクトを取得
SpriteRenderer sqSr = sqObj.GetComponent<SpriteRenderer>();//目的のスプライトのSpriteRendererを取得
Debug.Log("四角のサイズは " + sqSr.bounds.size + " です"); // 四角のサイズは (1.0, 2.0, 0.2) です
Debug.Log("四角の横の長さは " + sqSr.bounds.size.x + " です"); // 四角の横の長さは 1 です
Debug.Log("四角の縦の長さは " + sqSr.bounds.size.y + " です"); // 四角の縦の長さは 2 です
Debug.Log("中心からの距離は " +sqSr.bounds.extents+ " です");//中心からの距離は (0.5, 1.0, 0.1) です
Debug.Log("中心の座標は " + sqSr.bounds.center + " です");//中心の座標は (-0.5, 0.2, 0.0) です
Debug.Log("右上の座標は " + sqSr.bounds.max + " です");//右上の座標は (0.0, 1.2, 0.1) です
Debug.Log("左下の座標は " + sqSr.bounds.min + " です");//左下の座標は (-1.0, -0.8, -0.1) です
参考:https://qiita.com/No2DGameNoLife/items/d631b27676c5cd7e9def
Ignore Raycastレイヤーってそうなの?
タップ禁止のゾーンを作って、Raycastでそのゾーンのレイヤーが含まれていたら何も動作しないようにしたかった
タップ禁止ゾーン
「どんなレイヤー名にしようかな~」と思っていたら、デフォルトでなんかっぽい名前のレイヤーを発見!!
使ってみたら、Raycastが反応しなくなった
foreach (RaycastHit2D hit in hits)
{
// Ignore Raycast レイヤーにヒットしたか確認
if (hit.collider != null && hit.collider.gameObject.layer == LayerMask.NameToLayer("Ignore Raycast"))
{
hitIgnoreRaycast = true;
break; // Ignore Raycast レイヤーにヒットした場合はクリックを無視
}
// 武器にヒットした場合の処理
if (hit.collider != null && hit.collider.gameObject == gameObject)
{
hitWeapon = true;
}
}
名前だけかと思ったら、そういうロジックがあるのね
レイヤーを使って、レイキャストが交差できるゲームオブジェクトを指定することができます。レイキャストが特定のゲームオブジェクトを無視するようにするには、そのゲームオブジェクトを Ignore Raycast レイヤーに割り当てるか、レイキャスト API 呼び出しに レイヤーマスク を渡します。
大人しく、違う名前のレイヤーにした。ドキュメントはよく読もう
参考:https://docs.unity3d.com/ja/2022.3/Manual/use-layers.html
InspectoreでDictionary型を使うために工夫
がいるらしい、まあ、そうだろう
[Serializable]
public class SerializableKeyPair<TKey, TValue>
{
[SerializeField] private TKey key;
[SerializeField] private TValue value;
public TKey Key => key;
public TValue Value => value;
}
[SerializeField] private SerializableKeyPair<bool,Sprite>[] _menuSprites = default;
Instantiateした時にオブジェクトに(clone)って入っちゃう
意図はわかるんだけど、nameで判断したいときにちょっと不便
設定で変えられたらいいのに(それとも、そもそもnameで判断するのが間違ってる??あるかも)
var clone = Instantiate(source);
clone.name = source.name;
Unityへの機能リクエストとして投げられているみたいだ
UnityでHTMLを見たいけど、windowsだとWeb-Viewが使えない
unity-webview is a plugin for Unity 5 that overlays WebView components on Unity view. It works on Android, iOS, Unity Web Player, and Mac (Windows is not supported for now).
https://github.com/gree/unity-webview
😇
export packagesでおとなしく、Macに移行することにした
UnityでVite-Reactアプリを見ようとしたら大苦戦
Vite-Reactで作ったアプリをデプロイせずにUnity-WebViewで見ようとしたら大苦戦した
- まずは普通にアプリを作る(参考:https://qiita.com/teradonburi/items/fcdd900adb069811bfda)
- ビルドする(下記のような出力になる)
dist/
├── index.html
└── assets/
├── hogehoge.js
└── hogehoge.css
- Unityで
Assets/StreamingAssets
にこれをセッティング - Unity-WebViewを用いてこのファイルを取得して表示する
using UnityEngine;
public class LocalWebViewSample : MonoBehaviour
{
private WebViewObject webViewObject;
void Start()
{
// WebViewObjectの作成
webViewObject = new GameObject("WebViewObject").AddComponent<WebViewObject>();
// WebViewの初期化
webViewObject.Init(
cb: (msg) => Debug.Log(string.Format("CallFromJS[{0}]", msg)), // JavaScriptからのコールバック
err: (msg) => Debug.LogError(string.Format("Error[{0}]", msg)), // エラー時のコールバック
ld: (msg) => Debug.Log(string.Format("Loaded[{0}]", msg)) // ページが読み込まれたときのコールバック
);
// ローカルファイルへのパスを取得
string url = System.IO.Path.Combine(Application.streamingAssetsPath, "dist/index.html");
// Androidの場合、ファイルのアクセスには "file://" が必要
if (Application.platform == RuntimePlatform.Android)
{
url = "file:///android_asset/dist/index.html"; // Androidでは特別なパスが必要
}
else
{
url = "file://" + url.Replace(" ", "%20"); // その他のプラットフォームでは通常のファイルパス
}
// WebViewのサイズを設定(左上X, 上Y, 右X, 下Y)
webViewObject.SetMargins(10, 100, 10, 200);
// ローカルのHTMLファイルをロード
webViewObject.LoadURL(url);
// WebViewを表示
webViewObject.SetVisibility(true);
}
void OnGUI()
{
// WebViewを閉じるボタン
if (GUI.Button(new Rect(10, 10, 150, 100), "Close WebView"))
{
webViewObject.SetVisibility(false); // WebViewを非表示に
}
}
}
- GameObjectにこのスクリプトをアタッチ
- ゲームを動かす
- 画面が真っ白😇😇😇😇😇😇
こういう時は大体パスがおかしい、という短年の感からtest.html
という単一ファイルを試してみたら表示された。
そこで、Viteで出力されるアプリを単一ファイルにすれば解決できそうだがそんな都合のいいプラグインなんて...
_人人人人人人人人人人人人_
> vite-plugin-singlefile <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y  ̄
こりゃあいい
vite.config.ts
を変更してbuildしたらindex.html
だけ出力された!!
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { viteSingleFile } from "vite-plugin-singlefile";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), viteSingleFile()],
base: "./",
});
そのindex.html
をAssets/StreamingAssets
に移動したら見れた...
原因はわからなかったがひとまずこの方針で行く
あとは、Unityにイベントを送る&受け取る実装が待ち構えているが、すでに嫌な予感しかしない...😇
vite-react で画像を読むのが大変
vite-plugin-singlefileだとpublic下の画像が取れない
Static resources in public folder (like favicon) are not inlined by Vite, and this plugin doesn't do that either. BUT the output single HTML file CAN work together with these resouces, using relative paths.
https://www.npmjs.com/package/vite-plugin-singlefile
だから、画像自体をビルドしたファイルに用意すれば読み取れはする
SSRだと動的にimageのurlを取得できない
function getImageUrl(name) {
return new URL(./dir/${name}.png
, import.meta.url).href
}
SSR では動作しません
ブラウザーと Node.js で import.meta.url のセマンティクスが異なるため、 このパターンは Vite をサーバーサイドレンダリングで使用している場合には動作しません。サーバーバンドルは事前にクライアントホストの URL を決定することもできません。
諦めてimport方式でやる
import TitleLogo from "./images/title.png";
UnityでVite-Reactアプリを見るのをやめた
UnityのWebViewでViteでビルドしたアプリが見れない問題について、type=module
が原因っぽかった。
参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules
<script type="module" crossorigin src="main.tsx" />
WebViewで実装しているエンジンでES6が対応していない?理由は分からない...
type="module" 属性がESモジュールを使用するために追加されたのは、2015年にECMAScript 6(ES6)仕様として標準化されたタイミングです。しかし、ブラウザでのサポートは少し遅れて登場しました。以下に主要ブラウザでのサポート開始時期をまとめます。
type="module" のサポート開始時期:
Google Chrome:
サポート開始: Chrome 61
リリース日: 2017年9月
Mozilla Firefox:
サポート開始: Firefox 60
リリース日: 2018年5月
Microsoft Edge:
サポート開始: Edge 16
リリース日: 2017年10月
(ChromiumベースのEdgeの場合、Chromeと同じタイミングでサポート)
Safari:
サポート開始: Safari 11
リリース日: 2017年9月
Opera:
サポート開始: Opera 48
リリース日: 2017年9月
ひとまずWebpackで行うことにしたらできた。
参考:https://zenn.dev/spacemarket/articles/23e5401a074ccc#3.-typescriptを導入する
Text Mesh Proで光の当て方が分からん
URPのLight 2Dのというものがあるらしく使ってみたら、割といい感じだったのだが...
Text Mesh Proを使っている文字だけ表示されてしまった
当然使っているText Mesh Proは2D限定ではないので、理由は分かるが、今度はこいつらを真っ暗にする方法がわからなくなってしまった
やってみたこと
- Text Mesh Pro > Inspector > Mesh Renderer > Lighting > Cast Shadows > On
- これやってゲーム実行すると勝手にoffになる😇
- SDF Material > Lighting
- なんか若干文字の色が暗くなった
多分だけど、ちゃんとLightの使い方を知っていれば解決する気がする
Unity詳しい人を探す旅に出る...
Unityroomに投稿しようとした時に起きたアレコレ(解決編)
UNITY_EDITORのせいでビルドにこける
UNITY_EDITORはUNITY_EDITORでしか動かない、それはそう
//#if UNITY_EDITOR // この行を削除
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;
using System.Linq;
public class WeaponDataLoader : MonoBehaviour
{
// クラス内容はそのまま
}
//#endif // この行を削除
AssetDatabase使っているせいでビルドにこける2
AssetDatabaseはUnityエディター専用のAPIであり、WebGLや他のプラットフォーム向けにビルドされたアプリケーションでは動作しません。
// 修正前
// string[] prefabGUIDs = AssetDatabase.FindAssets("t:Prefab", new[] { prefabFolderPath });
// foreach (string guid in prefabGUIDs)
// {
// string assetPath = AssetDatabase.GUIDToAssetPath(guid);
// GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
// if (prefab != null)
// {
// prefabs.Add(prefab);
// }
// }
// 修正後
GameObject[] prefabs = Resources.LoadAll<GameObject>("Weapons");
WebGLでFile.ReadAllTextができない
JSOSファイルを読み込もうとしたら、FileNotFoundException
になった
StreamingAssetsに配置したファイルはIO.Fileで取得できない、らしいのでWebリクエストを送るように修正
参考:https://qiita.com/kazuki_kuriyama/items/3155606bb6cb5861ce68#ファイル読み書き関連
// 修正前
// string jsonText = File.ReadAllText(jsonFilePath);
// 修正後
// WebGLではUnityWebRequestを使用
UnityWebRequest request = UnityWebRequest.Get(jsonFilePath);
await request.SendWebRequest();
// JSON内容を取得
string jsonText = request.downloadHandler.text;
unity-webviewでエラー発生
なんとかbuildは通ったが、今度はunity-webviewを使ったオブジェクトが表示されない
ひとまず公式のReadMeを読んでできそうな事をやる
- WebGL Templatesを作る
- デフォルトのテンプレートを持ってくる
$ cp -a /Applications/Unity/Hub/Editor/2022.3.45f1/PlaybackEngines/WebGLSupport/BuildTools/WebGLTemplates/Default/TemplateData Assets/WebGLTemplates/unity-webview-2020
- /unity-webview/dist/package/Assets/WebGLTemplates/unity-webview-2020にある
index.html
とunity-webview.js
をWebGL Templatesに持ってくる
Unityroomに投稿しようとした時に起きたアレコレ(未解決編)
AddressablesとWebGL Templates
今2つの問題が起きている
- unity-webviewでローカルファイルを読み込むときに、StreamingAssetsを使っているが、UnityroomではStreamingAssetsが使えないので、StreamingAssetsが使いたかったら別のサーバーで配信するしかない
url = "file://" + System.IO.Path.Combine(Application.streamingAssetsPath, "index.html").Replace(" ", "%20");
// ローカルのHTMLファイルをロード(再利用時もリロードする)
webViewObject.LoadURL(url);
- unity-webviewを使うためにWebGL Templatesを用いているが、Unityroomでは見ていないので、どうにか*.loader.jsに含める必要がありそう
思いつく解法
1番
a. unity-webviewの中を書き換えて、StreamingAssetsではなくResourceに向くようにする
→難しそう、やってみる価値はある
b. StreamingAssetsの中を配信する
→配信の中身はただのReactnoアプリなので、Vercelでもherokuでもなんでもデプロイできそうだが、「Unity側にメッセージを送信」部分をどうやってデプロイした環境で実行できるのかが分からない(window as any).Unity.call(json);
2番
下記を参照してやる
参考:https://zenn.dev/uimss/articles/9912f9d7147853#webglでのaddressable(assetbundle)読込の流れ
ひとまず
1番のaでやってみる
ちなみにitch.ioならこんなことしなくてもいいらし、次は英語対応のゲームを作ろうと心に誓った
やってみた結果
2番はいけた
1-a
よく分からんかった、unityObject.cs
の下記のどこかで、Resouseから読み込む処理を追加したらいけると思ったが、そんなことはなかった
public void LoadURL(string url)
{
if (string.IsNullOrEmpty(url))
return;
#if UNITY_WEBGL
#if !UNITY_EDITOR
_gree_unity_webview_loadURL(name, url);
#endif
#elif UNITY_WEBPLAYER
Application.ExternalCall("unityWebView.loadURL", name, url);
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX || UNITY_SERVER
//TODO: UNSUPPORTED
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IPHONE
if (webView == IntPtr.Zero)
return;
_CWebViewPlugin_LoadURL(webView, url);
#elif UNITY_ANDROID
if (webView == null)
return;
webView.Call("LoadURL", url);
#endif
}
1-b
- VercelでNext.jsのアプリとしてデプロイした
- CORSの設定書いた
- scriptに
displayMessageFromUnity
書いた
でもエラー出まくり、unity-webviewのobjectはiframeの中に表示されるが、そのiframeの関数を実行する方法が分からないし、クロスオリジンだと出来なさそう
import { NextRequest, NextResponse } from "next/server";
const allowedOrigins = [
"https://unitryroom.com",
"https://57521.play.unityroom.com",
];
export function middleware(request: NextRequest) {
// Check the origin from the request
const origin = request.headers.get("origin") ?? "";
const isAllowedOrigin = allowedOrigins.includes(origin);
// Handle preflighted requests
const isPreflight = request.method === "OPTIONS";
if (isPreflight) {
const preflightHeaders = {
...(isAllowedOrigin && { "Access-Control-Allow-Origin": origin }),
};
return NextResponse.json({}, { headers: preflightHeaders });
}
// Handle simple requests
const response = NextResponse.next();
if (isAllowedOrigin) {
response.headers.set("Access-Control-Allow-Origin", origin);
}
return response;
}
"use client";
import "./index.css";
import Provider from "./provider";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja">
<head>
<script>
{`
function displayMessageFromUnity(msg) {
window.unityMessage = msg;
}`}
</script>
</head>
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}
結論
unity-webviewから、普通のunityのgameobjectに置き換える
😭
Resoucesファイルって非推奨なのね
3.1. Best Practices for the Resources System
Don't use it.
https://learn.unity.com/tutorial/assets-resources-and-assetbundles#5c7f8528edbc2a002053b5a7
草だこれ
理由としては、要はメモリ管理とか最適化が甘い、とかよくある理由らしい
このように強く推奨する理由はいくつかあります:
Resources フォルダを使用すると、きめ細かなメモリ管理が難しくなる
Resources フォルダの不適切な使用は、アプリケーションの起動時間とビルドの長さを増加させる
Resources フォルダの数が増えると、フォルダ内の Assets の管理が非常に困難に
なる Resources システムは、特定のプラットフォームにカスタム コンテンツを配信するプロジェクトの能力を低下させ、コンテンツのインクリメンタル アップグレードの可能性を排除する
AssetBundle Variants は、デバイスごとにコンテンツを調整するための Unity の主要なツールである。
代わりにAddressableを使おう
そうは言われてもUnityroomで投稿する時にはResourcesに入れないと、外部配信必要になってる気がするからケースバイケースって感じ
多分🤔