Scripting Introduction for Unity Developers
Last updated
Last updated
Needle EngineはUnity Editorに密接に統合されています。これにより、開発者とデザイナーの両方が慣れ親しんだ環境で協力し、高速で高性能、かつ軽量なWebエクスペリエンスを提供できます。
以下のガイドは主にUnity3Dのバックグラウンドを持つ開発者を対象としていますが、Webまたはthree.jsのバックグラウンドを持つ開発者にも役立つ可能性があります。Unityとthree.jsまたはNeedle Engineでどのように物事が行われるかに関するトピックを扱います。
もしTypescriptとJavascriptを初めて使用し、Needle Engineのスクリプト作成に深く入りたい場合は、C#とJavascript/Typescriptの違いの基本的な理解のために、を読むこともお勧めします。
コードを書きながら進めたい場合は、を開いて、ブラウザで編集できる小さなプロジェクトを作成できます⚡
Needle Engineは、上で動作する3D Webエンジンです。Three.jsは、Web向けの最も人気のある3D WebGLベースのレンダリングライブラリの1つです。Needle EngineでgameObject
を参照する場合、それは実際にはthree.jsのObject3D
、three.jsのあらゆるオブジェクトの基本タイプも指しています。これらの用語は互換的に使用できます。任意のgameObject
はObject3D
です。
これはまた、もしthree.jsに既に慣れているなら、Needle Engineを使用する上で全く問題がないことを意味します。three.jsでできることはすべて、Needle Engineでも可能です。特定のライブラリを既に使っている場合、Needle Engineベースの環境でもそれらを使用できます。
注:Needle EngineのExporterは、既存のC#コードをWeb Assemblyにコンパイルしません。 Web Assemblyを使用するとランタイムパフォーマンスが向上する可能性がありますが、Webエクスペリエンス構築における反復速度と柔軟性には大きなコストがかかります。とについて詳しくはこちらをご覧ください。
:::details Needle Engineを使用して新しいUnityプロジェクトを作成する方法は?(動画):::
Unityでは、MonoBehaviour
から派生させることで新しいコンポーネントを作成します。
一方、Needle Engineでのカスタムコンポーネントは次のように書かれます。
一部のNeedle Engineスクリプトを見たことがある場合、一部の変数が宣言の上に@serializable
でアノテーションされていることに気づいたかもしれません。これはTypescriptのDecoratorであり、コードを変更またはアノテーションするために使用できます。Needle Engineでは、例えば、glTFに保存されている生のコンポーネント情報からコンポーネントインスタンスに変換する際に、スクリプトでどのようなタイプを期待するかをコアのシリアライゼーションに知らせるために使用されます。
次の例を考えてみましょう。
これはNeedle Engineに、myOtherComponent
がBehaviour
タイプであるべきだと伝えます。これにより、シーンがロードされる際にフィールドに正しい参照が自動的に割り当てられます。someOtherObject
も同様で、ここではObject3D
参照にデシリアライズしたいと考えています。
private
、public
、protected
のようなアクセサー修飾子がないフィールドは、javascriptではデフォルトでpublic
になります。
メソッドにも同様のことが言えます。
コンポーネントから現在のシーンにアクセスするには、this.scene
を使用します。これはthis.context.scene
と同等であり、ルートとなるthree.jsシーンオブジェクトを取得できます。
コンポーネントからヒエラルキーを走査するには、オブジェクトの子をforループで反復するか、
foreach
と同等のものを使用して反復できます。
レンダリング可能なオブジェクトを反復したい場合に非常に便利な別のオプションとして、すべてのRendererコンポーネントをクエリして、次のように反復することができます。
コンポーネントの取得に関する詳細については、次のセクションを参照してください。
シーン内のコンポーネントを見つける
コンポーネントを取得するには、Unityと同様の慣れ親しんだメソッドを使用できます。以下ではAnimator
タイプを例として使用していますが、組み込みまたは自分で作成した任意のコンポーネントタイプを使用することもできます。
this.gameObject.getComponent(Animator)
GameObject/Object3D上のAnimator
コンポーネントを取得します。Animatorコンポーネントがある場合はAnimator
インスタンスを返し、オブジェクトにそのようなコンポーネントがない場合はnull
を返します。
this.gameObject.getComponentInChildren(Animator)
GameObject/Object3Dまたはその子にある最初のAnimator
コンポーネントを取得します。
this.gameObject.getComponentsInParents(Animator)
親ヒエラルキーにあるすべてのAnimatorコンポーネントを取得します(現在のGameObject/Object3Dを含む)。
これらのメソッドは、静的なGameObjectタイプでも利用可能です。例えば、渡されたGameObject/Object3D上のAnimator
コンポーネントを取得するには、GameObject.getComponent(this.gameObject, Animator)
を使用します。
シーン全体から1つまたは複数のコンポーネントを検索するには、GameObject.findObjectOfType(Animator)
またはGameObject.findObjectsOfType(Animator)
を使用できます。
一部のUnity固有のタイプは、当社のエンジンで異なるタイプ名にマッピングされています。以下のリストを参照してください。
UnityEvent
EventList
UnityEvent
はEventList
タイプとしてエクスポートされます(UnityEventをデシリアライズするにはserializable(EventList)
を使用します)。
GameObject
Object3D
Transform
Object3D
three.jsとNeedle Engineでは、GameObjectとTransformは同じです(Transform
コンポーネントはありません)。このルールの唯一の例外は、Needle EngineでもコンポーネントであるRectTransform
を参照する場合です。
Color
RGBAColor
three.jsのcolorタイプにはalphaプロパティがありません。そのため、UnityからエクスポートされるすべてのColorタイプは、カスタムのNeedle EngineタイプであるRGBAColor
としてエクスポートされます。
Transformデータは、GameObject
/ Object3D
上で直接アクセスできます。Unityとは異なり、このデータを保持する追加のTransformコンポーネントはありません。
this.gameObject.worldPosition
は、ワールドスペースのベクトル3位置です
this.gameObject.worldRotation
は、ワールドスペースのオイラー角でのオイラー回転です
this.gameObject.worldQuaternion
は、ワールドスペースのクォータニオン回転です
this.gameObject.worldScale
は、ワールドスペースのベクトル3スケールです
ここで覚えておくべき主な違いは、three.jsではposition
がデフォルトでローカルスペース位置であるのに対し、Unityではposition
がワールドスペースであることです。次のセクションでは、three.jsでワールドスペース位置を取得する方法を説明します。
three.js(したがってNeedle Engineも)では、object.position
、object.rotation
、object.scale
はすべてローカルスペース座標です。これは、position
がワールドスペースであり、明示的にローカルスペース位置を使用するためにlocalPosition
を使用することに慣れているUnityとは異なります。
Needle Engineでワールド座標にアクセスしたい場合は、オブジェクトとともに使用できるユーティリティメソッドがあります。getWorldPosition(yourObject)
を呼び出してワールド位置を計算します。回転/クォータニオン、スケールにも同様のメソッドが存在します。これらのメソッドにアクセスするには、Needle Engineからimport { getWorldPosition } from "@needle.tools/engine"
のようにインポートするだけです。
時間データにアクセスするには、this.context.time
を使用します。
this.context.time.time
は、アプリケーションが実行を開始してからの時間です
this.context.time.deltaTime
は、最後のフレームから経過した時間です
this.context.time.frameCount
は、アプリケーションが開始してから経過したフレーム数です
this.context.time.realtimeSinceStartup
は、アプリケーションが実行を開始してからのスケールされない時間です
また、this.context.time.timeScale
を使用して、例えばスローモーション効果のために時間を意図的に遅くすることも可能です。
this.context.physics.raycast()
を使用してレイキャストを実行し、交差のリストを取得します。オプションを何も渡さない場合、レイキャストは現在アクティブなmainCamera
を使用して、スクリーン空間でのマウス位置(または最初のタッチ位置)から実行されます。maxDistance
、使用するカメラ、テスト対象のレイヤーなど、様々な設定を持つRaycastOptions
オブジェクトを渡すこともできます。
上記の呼び出しは、デフォルトでは可視シーンオブジェクトに対してレイキャストを実行することに注意してください。これは、オブジェクトにヒットするために常にコライダーが必要なUnityとは異なります。デフォルトのthree.jsソリューションには利点と欠点の両方があり、主な欠点の1つは、シーンジオメトリによっては非常に遅くなる可能性があることです。スキニングされたメッシュに対してレイキャストを行う場合は特に遅くなる可能性があります。そのため、通常、UnityでSkinnedMeshRendererを持つオブジェクトをIgnore Raycast
レイヤーに設定することをお勧めします。これにより、Needle Engineでもデフォルトで無視されます。
もう1つのオプションは、シーン内のコライダーを持つヒットのみを返す物理レイキャストメソッドを使用することです。
入力状態をポーリングするには、this.context.input
を使用します。
InputEvents
enum内のイベントに、次のように購読することもできます。
この場合、すべてのケースを自分で処理する必要があることに注意してください。例えば、ユーザーがデスクトップ、モバイル、VRデバイスのいずれでWebサイトを閲覧しているかによって、異なるイベントを使用する必要がある場合があります。これらのケースはNeedle Engineの入力イベントによって自動的に処理されます(例:PointerDown
は、マウスダウン、タッチダウン、およびVRコントローラーのボタンダウンの場合の両方で発生します)。
これを機能させるには、オブジェクトの親ヒエラルキーにObjectRaycaster
またはGraphicRaycaster
コンポーネントがあることを確認してください。
注:IPointerEventHandler
は、オブジェクトをすべての可能なポインターイベントに購読します。それらのハンドラーは以下の通りです。
onPointerDown
onPointerUp
onPointerEnter
onPointerMove
onPointerExit
onPointerClick
すべてに、イベントを記述するPointerEventData
引数があります。
javascriptでのDebug.Log()
に相当するのはconsole.log()
です。console.warn()
やconsole.error()
も使用できます。
Unityでは通常、OnDrawGizmos
やOnDrawGizmosSelected
のような特別なメソッドを使用してGizmoを描画する必要があります。一方、Needle Engineにはそのようなメソッドはなく、スクリプトのどこからでも自由にGizmoを描画できます。その際、例えばデプロイされたWebアプリケーションでそれらを描画しないようにすることはあなたの責任であることに注意してください(if(isDevEnvironment))
などでフィルタリングできます)。
例えば、ワールド空間の点を視覚化するために、1秒間赤いワイヤースフィアを描画する例です。
利用可能なGizmoメソッドの一部を以下に示します。
Gizmos.DrawArrow
Gizmos.DrawBox
Gizmos.DrawBox3
Gizmos.DrawDirection
Gizmos.DrawLine
Gizmos.DrawRay
Gizmos.DrawRay
Gizmos.DrawSphere
Gizmos.DrawWireSphere
@needle-tools/engine
からインポートします(例: import { getParam } from "@needle-tools/engine"
)。
getParam()
URLパラメータが存在するか確認します。値がない場合(例: ?help
)はtrueを返し、URLに見つからないか0に設定されている場合(例: ?help=0
)はfalseを返し、それ以外の場合は値を返します(例: ?message=test
)。
isMobileDevice()
アプリがモバイルデバイスからアクセスされている場合はtrueを返します。
isDevEnvironment()
現在のアプリがローカルサーバーで実行されている場合はtrueを返します。
isMozillaXR()
isiOS
isSafari
C#では通常、1つまたは複数のプロジェクトを含むソリューションを扱います。Unityでは、このソリューションはUnityによって管理され、C#スクリプトを開くとプロジェクトが開き、ファイルが表示されます。
Unityの組み込みパッケージマネージャーを使用してパッケージをインストールすることで、Unityまたは他の開発者(チーム内やUnityのAssetStoreなど)が提供する機能を追加するのが一般的です。UnityはPackageManagerを使ってパッケージの追加と管理を容易にする優れた仕事をしています。そのため、manifest.json
のようなファイル(Unityがどのパッケージがインストールされているかを追跡するために使用するもの)を手動で編集したり、コマンドラインからコマンドを実行してパッケージをインストールしたりする必要はなかったかもしれません。
Web環境では、npm
(Node Package Manager)を使用して、依存関係/パッケージを管理します。これは基本的にUnityのPackageManagerと同じことを行います。パッケージを何らかのサーバー(この文脈では通常レジストリと呼ばれます)からインストール(ダウンロード)し、node_modules
という名前のフォルダーに入れます。
package.jsonの例を以下に示します。
デフォルトのテンプレートはViteをバンドラーとして使用しており、フロントエンドフレームワークはプリインストールされていません。Needle Engineはどのフレームワークを使用するかについて制約がないため、好きなフレームワークで自由に作業できます。Vue.js、Svelte、Next.js、React、React Three Fiberなどの人気フレームワークのサンプルを用意しています。
dependencyを含む2つのエントリがあることに気づいたかもしれません - dependencies
とdevDependencies
です。
dependencies
は、Webプロジェクトがインストールされる場合、またはライブラリを開発していてパッケージが別のプロジェクトの依存関係としてインストールされる場合に、常にインストール(またはバンドル)されます。
devDependencies
は、プロジェクトを開発する際にのみインストールされます(つまり、特定のディレクトリで直接install
を実行する場合)。それ以外の場合はプロジェクトには含まれません。
まずターミナルでnpm install @tweenjs/tween.js
を実行し、インストールが完了するまで待ちます。これにより、package.jsonに新しいエントリが追加されます。
次に、トゥイーンを使用したいスクリプトファイルのいずれかを開き、ファイルの先頭でインポートします。
ここでは* as TWEEN
と書くことでライブラリ内のすべての型をインポートしていることに注意してください。import { Tween } from @tweenjs/tween.js
のように特定の型だけをインポートすることもできます。
キューブを回転させるために、TweenRotation
という新しいコンポーネントタイプを作成します。次に、オブジェクトの回転に対するトゥイーンインスタンス、繰り返しの頻度、使用するイージング、実行したいトゥイーンを作成し、開始します。あとは毎フレームupdate
を呼び出してトゥイーンアニメーションを更新するだけです。最終的なスクリプトは次のようになります。
このページはAIによって自動的に翻訳されました。
場合によっては型を省略できることに注意してください。これはで行うことができます。これらはboolean
、number
、bigint
、string
、null
、undefined
です。
または、メソッドを使用して、すべてのオブジェクトを再帰的に迅速に反復するためのthree.js固有のメソッドを使用することもできます。
または、可視オブジェクトだけを走査するには、代わりにを使用します。
Needle Engineは、Unityのそれに類似したコンポーネントシステムを多用しています。これは、シーン内の任意のObject3D
/ GameObject
にコンポーネントを追加または削除できることを意味します。addNewComponent(<Object3D>, <ComponentType>)
を使用すると、コンポーネントがエンジンに登録されます。
アタッチされたコンポーネントのイベントメソッドは、エンジンによって自動的に呼び出されます(例:update
またはonBeforeRender
)。イベントメソッドの完全なリストは、で見つけることができます。
this.gameObject.position
は、ローカルスペースのベクトル3 です
this.gameObject.rotation
は、ローカルスペースのです
this.gameObject.quaternion
- は、ローカルスペースのです
this.gameObject.scale
- は、ローカルスペースのベクトル3 です
getWorldPosition
、getWorldRotation
、getWorldScale
のようなこれらのユーティリティメソッドは、内部的にVector3インスタンスのバッファを持っており、ローカルでのみ使用することを意図していることに注意してください。これは、コンポーネントでそれらをキャッシュすべきではないことを意味します。そうしないと、キャッシュされた値が最終的に上書きされます。ただし、同じインスタンスを再利用することを心配することなく計算を行うために、関数内でgetWorldPosition
を複数回呼び出すことは安全です。これが何を意味するか不明な場合は、のPrimitive Typesセクションを参照してください。
を使用してレイキャストを実行するには、this.context.physics.raycastFromRay(your_ray)
を使用します。
ここに編集可能ながあります。
入力を自分で処理したい場合は、(たくさんあります)に購読することもできます。例えば、ブラウザのクリックイベントに購読するには、次のように書くことができます。
Unityと同様に(参照)、コンポーネント自体で入力イベントを受け取るように登録することもできます。
Webプロジェクトを扱う際、ほとんどの依存関係はからインストールされます。これは、Webプロジェクトにとって最も人気のあるパッケージレジストリです。
npmから依存関係をインストールするには、コマンドライン(またはターミナル)でWebプロジェクトを開き、npm i <パッケージ名>
(npm install
の短縮形)を実行します。
例えば、をインストールするには、npm i @needle-tools/engine
を実行します。これにより、パッケージがpackage.json
のdependencies
配列に追加されます。
パッケージをdevDependencyとしてのみインストールするには、npm i --save-dev <パッケージ名>
を実行します。dependenciesとdevDependenciesの違いについては、以下で詳しく説明します。
セクションでは、プロジェクトディレクトリでnpm i <package_name>
を実行することで依存関係をインストールできることを学びました。package_name
はで見つけることができる任意のパッケージです。
プロジェクトにトゥイーンライブラリを追加したいと仮定しましょう。この例ではを使用します。結果だけを先に確認したい場合はが最終プロジェクトです。
これでスクリプトで使用できます。使用したいライブラリのドキュメントを参照することを常にお勧めします。tween.jsの場合、従うことができるが提供されています。通常、npm上のパッケージのReadmeページには、パッケージのインストール方法と使用方法に関する情報が含まれています。
これで、シーン内の任意のオブジェクトに追加するだけで、オブジェクトを永遠に回転させることができます。 最終的なスクリプトの動作はで確認できます。