メインコンテンツまでスキップ

Unityからの移行

Unityでプロジェクトを作ったことがあれば、PlayCanvasの多くはなじみのあるものに感じられるはずです。PlayCanvas Editorには、ヒエラルキー、インスペクター、アセットパネル、3Dビューポートがあります。シーンはエンティティのツリーで、エンティティはコンポーネントで構成され、振る舞いはinitializeやupdateなどのライフサイクルメソッドを持つスクリプトで記述します。

このガイドでは、Unityで身につけた知識をPlayCanvasに対応付けます。エディターのパネルとショートカット、共通する概念の名前、C#のAPIとそれに相当するJavaScript、そしてPlayCanvasの動作が異なる点を取り上げます。各セクションから、そのトピックの詳しいドキュメントにリンクしています。

主な違い​

  • すべてがブラウザーで動作します。 エディターはWebアプリケーションで、プロジェクトはオンラインに保存され、公開するものもWebアプリです。WebGLのビルドを待つ必要はありません。アプリは、エディターのビューポートと同じエンジンで動作します。
  • スクリプトはJavaScriptで書きます。 スクリプトはC#のクラスではなくJavaScriptのモジュールとして書きます。コンパイルは不要で、スクリプトを保存したらそのまま起動できます。
  • スクリプトは1つのコンポーネントを共有します。 Unityでは、MonoBehaviourのそれぞれが独立したコンポーネントです。PlayCanvasでは、エンティティに1つのScriptコンポーネントがあり、そのエンティティのすべてのスクリプトを保持します。
  • 各タイプのコンポーネントはエンティティごとに1つです。 エンティティが持てるカメラ、ライト、Collisionコンポーネントなどはそれぞれ1つで、各スクリプトも1回しか追加できません。コライダーを2つ使いたい場合など、同じものが2つ必要なときは子エンティティを使います。
  • 座標系は右手系です。 前方は+Zではなく-Zです。座標系と単位を参照してください。
  • Playモードはありません。 シーンを起動すると、エディターに接続されたままの別のブラウザータブで実行されます。
  • コラボレーションとバージョン管理が組み込まれています。 複数の人が同じシーンを同時に編集でき、チェックポイント、ブランチ、マージはエディターの機能です。

エディター​

エディターのレイアウトは、Unityのデフォルトのレイアウトとよく似ています。

ツールバー、ヒエラルキー、ビューポート、インスペクター、アセットパネルに1から5の番号を付けたPlayCanvas Editor

PlayCanvasUnity
1ツールバーメインメニュー、トランスフォームツール、ツールバー
2ヒエラルキーHierarchyウィンドウ
3ビューポートSceneビュー
4インスペクターInspectorウィンドウ
5アセットパネルProjectウィンドウ

Unityのその他のウィンドウやメニューに相当するものは、次のとおりです。

UnityPlayCanvas
PlayボタンとGameビュービューポートの右上にあるLaunchボタン
Consoleウィンドウ起動したタブで開く、ブラウザーの開発者ツールのコンソール。エディター左下にあるエディター自体の CONSOLE パネルには、スクリプトの解析エラーなど、エディターのメッセージが表示されます。
Project SettingsとLightingウィンドウツールバーの歯車アイコンから開く設定
Build Settingsツールバーの Publish / Download
Animatorウィンドウanim state graphアセットを選択すると開くanim state graphエディター
Asset StorePlayCanvasストアとSketchfabのアセットがある、アセットパネル右上の ASSET STORE
Visual StudioまたはRider組み込みのコードエディター、またはVS Code拡張機能を入れたVS Code
Unity Version ControlまたはGit組み込みのバージョン管理

Playモードの代わりに起動する​

Unityはゲームをエディターの中で再生します。PlayCanvasは新しいブラウザータブで起動ページを開き、公開したときと同じようにシーンを実行します。Launch をクリックするか、Ctrl+Enterを押してください。

起動ページはエディターに接続されたままです。エンティティ、コンポーネント、スクリプト属性、アセット、シーン設定への変更は、加えたそばから実行中のアプリに反映されます。Playモード中の編集とは違い、停止しても変更は失われません。エディターはすべての変更をその都度保存するからです。この使い方をするには、2つのタブを横に並べて配置してください。コードの変更は、スクリプトがホットリロードに対応していない限り、再読み込みが必要です。

スクリプトがエディターのビューポートで実行されることはないため、Unityの[ExecuteInEditMode]に相当するものはありません。スクリプトが作成したり変更したりするものは、起動したアプリにしか現れません。

Launchボタンのメニューには、テスト用のオプションがあります。グラフィックスAPIを選んだり、プロファイラー、MiniStats、Inspectorを開いたりできます。Debug にチェックを入れると、エンジンの警告とエラーがコンソールに記録されます。

操作UnityPlayCanvas
周回(オービット)Alt + 左ドラッグ左ドラッグ
見回す右ドラッグ右ドラッグ
パン中ドラッグ中ドラッグ、またはShift + 左ドラッグ
ズームマウスホイールマウスホイール
飛行右ボタンを押しながらW、A、S、D、Q、Eを押すマウスボタンを押さずにW、A、S、D、Q、Eを押す(Shiftを押すと速く移動)
選択対象にフォーカスFF
矩形選択何もない場所を左ドラッグCtrl + 左ドラッグ
選択への追加と選択からの除外Shift+クリックまたはCtrl+クリックCtrl+クリック

キーボードショートカット​

操作UnityPlayCanvas
移動ツールW1
回転ツールE2
スケールツールR3
矩形ツール(UI要素)T4
ギズモのローカル空間とワールド空間の切り替えXL
選択対象にフォーカスFF
再生Ctrl+PCtrl+Enter(Launch)
空の子オブジェクトを作成Alt+Shift+NCtrl+E
複製Ctrl+DCtrl+D
名前の変更F2F2
削除DeleteDelete
元に戻す / やり直しCtrl+Z / Ctrl+YCtrl+Z / Ctrl+Y
保存Ctrl+Sすべての変更が保存されるので不要です。Ctrl+Sを押すと、バージョン管理のチェックポイントを作成するフォームが開きます。
ビューの最大化Shift+SpaceSpace(ヒエラルキー、インスペクター、アセットパネルを隠す)
正面、右側面、上面のビューシーンギズモの軸をクリックテンキーの1、3、7(Ctrlを加えると反対側)、またはビューキューブの軸をクリック
正投影と透視投影の切り替えシーンギズモの中央をクリックテンキーの5

macOSでは、Ctrlの代わりにCmdを使います。UnityのViewツール(Q)に相当するものはありません。ビューポートの操作にツールは必要ないためです。すべてのショートカットは操作とキーボードショートカットのページにあります。

概念と用語​

UnityPlayCanvas備考
GameObjectエンティティ
Componentコンポーネントコンポーネントはエンティティのプロパティです(entity.camera、entity.light)。
Transformすべてのエンティティの一部エンティティはそれ自体が位置、回転、スケールを持ちます。階層とトランスフォームを参照してください。
MonoBehaviourスクリプトエンティティのScriptコンポーネントを通じてアタッチします。
PrefabテンプレートPrefabとテンプレートを参照してください。
Sceneシーン
Tagタグ1つのエンティティにいくつでもタグを付けられます。
Layerレイヤーレンダリング専用です。物理演算ではコリジョングループとマスクを使います。
Mesh FilterとMesh RendererRenderコンポーネントスキンメッシュにもRenderコンポーネントを使います。
Materialマテリアル
CameraCameraコンポーネント
LightLightコンポーネントポイントライトはオムニライトと呼ばれます。
RigidbodyRigid Bodyコンポーネント
ColliderCollisionコンポーネントRigid Bodyがない場合、Collisionコンポーネントはトリガーになります。物理演算を参照してください。
Audio SourceSoundコンポーネント1つのコンポーネントで、スロットと呼ばれる任意の数のサウンドを再生できます。
Audio ListenerAudio Listenerコンポーネント
Particle SystemParticle Systemコンポーネント
AnimatorとAnimator ControllerAnimコンポーネントとanim state graphアセット
CanvasScreenコンポーネント
RectTransform、Image、TextElementコンポーネント
Sprite RendererSpriteコンポーネント
ScriptableObjectJSONアセットまたは、データをエクスポートするJavaScriptモジュール。
ResourcesとAddressablesアセットレジストリアセットを参照してください。

座標系と単位​

どちらのエンジンもYを上方向とし、距離をメートル、角度を度で扱います。違いは座標系の向きです。Unityの座標系は+Zが前方の左手系ですが、PlayCanvasは -Zが前方 の右手系です。カメラは自身の負のZ軸の方向を向き、entity.forwardは負のZ軸を返し、lookAtは負のZ軸をターゲットに向けます。

Unityの左手座標系(+Zが前方)とPlayCanvasの右手座標系(-Zが前方)

このため、コードやコンテンツを移植するときには、次の点に注意が必要です。

  • 前進は-Z方向への移動です。 transform.Translate(0, 0, speed * Time.deltaTime)はthis.entity.translateLocal(0, 0, -this.speed * dt)になります。
  • 正の回転の向きが逆になります。 Y軸まわりの正の回転はエンティティを左に向け(上から見て反時計回り)、X軸まわりの正の回転は上に傾けます。Unityではそれぞれ右と下に回るため、シーンを見下ろすカメラのX回転は、PlayCanvasでは負の値になります。
  • オイラー角を適用する順序が異なります。 PlayCanvasはX、Y、Zの順に回転を適用しますが、UnityはZ、X、Yの順に適用します。軸の向きの反転と合わせて考えると、UnityのInspectorで読み取った回転の値はそのままでは使えません。レイアウトは、変換を行ってくれるエクスポートファイルで移行してください。コンテンツの移行を参照してください。
  • ライトは-Y方向を照らします。 ディレクショナルライトとスポットライトはローカルの負のY軸の方向を向くため、回転していないライトは真下を照らします。Unityでは+Z方向を照らします。
  • モデルは通常+Zを向いています。 glTFは+Zをモデルの正面と定めており、Unityからエクスポートしたモデルも+Zを向いたままです。これはPlayCanvasでは後ろ向きになります。スクリプトでキャラクターをentity.forwardの方向に動かす場合は、モデルを親エンティティの中に入れてY軸まわりに180°回転させ、モデルが親の前方を向くようにします。
  • デフォルトの視野角が狭くなっています。 どちらのエンジンも視野角を垂直方向で測りますが、PlayCanvasの新しいカメラは60°ではなく45°から始まります。

スクリプト​

PlayCanvasのスクリプトは、Scriptを継承するJavaScriptのクラスです。各スクリプトはアセットパネル内の.mjsファイルにあり、エンティティにScriptコンポーネントを追加してスクリプトを選ぶとアタッチできます。詳しくはスクリプティングを参照してください。

スクリプトの比較​

次のUnityスクリプトは、GameObjectを回転させ、スペースキーでライトのオンとオフを切り替えます。

using UnityEngine;

public class Spinner : MonoBehaviour
{
// 1秒あたりの回転角度(度)
[SerializeField] float speed = 90f;
[SerializeField] Light lamp;

void Start()
{
lamp.enabled = false;
}

void Update()
{
transform.Rotate(0f, speed * Time.deltaTime, 0f);

if (Input.GetKeyDown(KeyCode.Space))
{
lamp.enabled = !lamp.enabled;
Debug.Log($"Lamp on: {lamp.enabled}");
}
}
}

同じスクリプトをPlayCanvas用にspinner.mjsというファイルで書くと、次のようになります。

import { Entity, KEY_SPACE, Script } from 'playcanvas';

export class Spinner extends Script {
static scriptName = 'spinner';

/**
* 1秒あたりの回転角度(度)。
*
* @attribute
*/
speed = 90;

/**
* @attribute
* @type {Entity}
*/
lamp;

initialize() {
this.lamp.light.enabled = false;
}

update(dt) {
this.entity.rotateLocal(0, this.speed * dt, 0);

if (this.app.keyboard.wasPressed(KEY_SPACE)) {
this.lamp.light.enabled = !this.lamp.light.enabled;
console.log(`Lamp on: ${this.lamp.light.enabled}`);
}
}
}

注目すべき違いは、次のとおりです。

  • scriptNameがスクリプトの名前になります。 エディターはこの名前でスクリプトを表示し、他のスクリプトはこの名前でスクリプトにアクセスします(entity.script.spinner)。
  • [SerializeField]の代わりに@attributeを使います。 JSDocコメントで@attributeタグを付けたクラスフィールドがインスペクターに表示され、コメントの最初の文がフィールドのツールチップになります。インスペクターのフィールドを参照してください。
  • Entity属性は、コンポーネントではなくエンティティを参照します。 Entity型の属性はエンティティを保持するので、this.lamp.lightのように、そのエンティティを通じてコンポーネントにアクセスします。
  • this.entityはGameObjectとそのTransformに相当します。 this.appはアプリケーションで、入力デバイス、シーンの階層(this.app.root)、アセットレジストリなどにアクセスできます。
  • dtはTime.deltaTimeに相当します。 エンジンは、フレーム時間を秒単位でupdateに渡します。

ライフサイクルメソッド​

UnityPlayCanvas備考
Awake、Startinitialize()スクリプトとそのエンティティが初めて両方とも有効になったときに1回呼ばれます。初期化にクラスのコンストラクターは使わないでください。
別のスクリプトのAwakeに依存するStartpostInitialize()すべてのスクリプトのinitializeが実行された後に呼ばれるので、他のスクリプトの準備はできています。
Updateupdate(dt)
LateUpdatepostUpdate(dt)すべてのスクリプトのupdateが実行された後に呼ばれます。
FixedUpdateなし物理エンジンは独自に固定レートでステップします。物理演算を参照してください。
OnEnable、OnDisableenableとdisableイベントthis.on('enable', ...)で購読します。
OnDestroydestroyイベントthis.once('destroy', ...)で購読し、そこで他のイベントの購読を解除します。
OnValidate属性のセッター属性の変更に応答するを参照してください。
OnCollisionEnter、OnTriggerEnterなどCollisionコンポーネントのイベント物理演算を参照してください。

詳しくはスクリプトのライフサイクルを参照してください。

インスペクターのフィールド​

スクリプト属性は、PlayCanvasにおけるシリアライズされたフィールドです。エディターはコードから属性を読み取るため、属性を追加または変更したら、スクリプトアセットまたはScriptコンポーネントの Parse をクリックしてインスペクターを更新します。

UnityPlayCanvas
[SerializeField] float speed = 5f;/** @attribute */ speed = 5;
[Tooltip("...")]属性のJSDocコメントの最初の文
[Range(0, 10)]@range [0, 10]
[Min(0)]@range [0]
[HideInInspector]、または[SerializeField]なし@attributeのないフィールド
GameObjectまたはTransformのフィールド@type {Entity}
Texture2D、Material、AudioClipのフィールド@type {Asset}と、@resource texture、material、audio
Prefabのフィールド@type {Asset}と@resource template
Color、Vector3のフィールドcolor = new Color()、offset = new Vec3()
AnimationCurveのフィールド@type {Curve}
配列またはList<T>のフィールド@type {Entity[]}などの配列型
enumのフィールド@enumオブジェクト、または@type {'low' | 'high'}のようなリテラルのユニオン
[Serializable]クラスのフィールド@interfaceクラスまたは@typedef

エディターで設定した属性の値は、initializeが実行される前に適用されます。すべてのタグについては、属性のリファレンスを参照してください。

主なAPIの対応​

以下の表で、entityはスクリプト内のthis.entityなど任意のエンティティを表し、Vec3やKEY_Wなどはplaycanvasからインポートします。

エンティティとコンポーネント​

UnityPlayCanvas
new GameObject("Box")const box = new Entity('Box'); this.app.root.addChild(box);
Instantiate(original)const copy = original.clone(); this.app.root.addChild(copy);
Instantiate(prefab)const instance = templateAsset.resource.instantiate(); this.app.root.addChild(instance);
Destroy(gameObject)entity.destroy()
gameObject.SetActive(false)entity.enabled = false
gameObject.activeInHierarchyentity.enabled(祖先が無効なときはfalseになります)
GetComponent<Light>()entity.light
GetComponent<Spinner>()entity.script.spinner
GetComponentInChildren<Light>()entity.findComponent('light')
GetComponentsInChildren<Light>()entity.findComponents('light')
AddComponent<Light>()entity.addComponent('light', { type: 'omni' })
Destroy(GetComponent<Light>())entity.removeComponent('light')
GameObject.Find("Player")this.app.root.findByName('Player')
GameObject.FindGameObjectsWithTag("Enemy")this.app.root.findByTag('enemy')
transform.Find("Arm/Hand")entity.findByPath('Arm/Hand')
CompareTag("Enemy")entity.tags.has('enemy')

clone()はエンティティのコンポーネントと子をコピーしますが、スクリプトは作成時の属性値から始め直します。アプリの実行中にスクリプトが変更した値はコピーされません。一方、UnityのInstantiateは、シリアライズされたフィールドの現在の値をコピーします。

トランスフォーム​

UnityPlayCanvas
transform.positionentity.getPosition()、entity.setPosition(x, y, z)
transform.localPositionentity.getLocalPosition()、entity.setLocalPosition(x, y, z)
transform.rotationentity.getRotation()、entity.setRotation(quat)
transform.eulerAnglesentity.getEulerAngles()、entity.setEulerAngles(x, y, z)
transform.localScaleentity.getLocalScale()、entity.setLocalScale(x, y, z)
transform.Translate(x, y, z)entity.translateLocal(x, y, z)(ワールド空間ではtranslate)
transform.Rotate(x, y, z)entity.rotateLocal(x, y, z)(ワールド空間ではrotate)
transform.LookAt(target)entity.lookAt(target.getPosition())
transform.forward、right、upentity.forward、entity.right、entity.up(forwardは-Z)
transform.parententity.parent
transform.SetParent(parent)entity.reparent(parent)。ローカルのトランスフォームが保たれるため、エンティティは新しい親に合わせて移動します
transform.GetChild(i)、childCountentity.children[i]、entity.children.length

時間、数学、デバッグ​

UnityPlayCanvas
Time.deltaTimeupdateの引数dt
Time.timeScalethis.app.timeScale
Time.timeなし。updateでdtを足し合わせます。
Vector3、Quaternion、ColorVec3、Quat、Color
Vector3.up、Vector3.forwardVec3.UP、Vec3.FORWARD((0, 0, -1))
Quaternion.Euler(x, y, z)new Quat().setFromEulerAngles(x, y, z)
rotation * vectorrotation.transformVector(vector, result)
Vector3.Distance(a, b)a.distance(b)
Vector3.Lerp(a, b, t)result.lerp(a, b, math.clamp(t, 0, 1))(lerpはtをクランプしないため)
Mathf.Clamp、Mathf.Lerpmath.clamp、math.lerp
Mathf.Deg2Radmath.DEG_TO_RAD
浮動小数点数のRandom.Range(min, max)math.random(min, max)
整数のRandom.Range(min, max)Math.floor(math.random(min, max))(math.randomは常に浮動小数点数を返すため)
Debug.Log、LogWarning、LogErrorconsole.log、console.warn、console.error
Debug.DrawLine(start, end, color)this.app.drawLine(start, end, color)

入力​

UnityPlayCanvas
Input.GetKey(KeyCode.W)this.app.keyboard.isPressed(KEY_W)
Input.GetKeyDown(KeyCode.W)this.app.keyboard.wasPressed(KEY_W)
Input.GetKeyUp(KeyCode.W)this.app.keyboard.wasReleased(KEY_W)
Input.GetMouseButton(0)this.app.mouse.isPressed(MOUSEBUTTON_LEFT)
Input.mousePositionマウスイベントのxとy。左下ではなく、キャンバスの左上から測ります
Input.GetAxis("Mouse X")mousemoveイベントのdx
Cursor.lockState = CursorLockMode.Lockedthis.app.mouse.enablePointerLock()
Input.touchesthis.app.touchのタッチイベント
Physics.Raycastthis.app.systems.rigidbody.raycastFirst(start, end)

入力軸やアクションマップはありません。デバイスを直接読み取るか、独自のスクリプトでアクションに対応付けてください。ブラウザーは、クリックやキー入力に応じた場合にのみポインターロックを許可します。

Webプラットフォーム​

UnityPlayCanvas
PlayerPrefsブラウザーのlocalStorage
UnityWebRequestfetch
JsonUtilityJSON.parseとJSON.stringify
Application.OpenURLwindow.open

ベクトルはオブジェクトです​

UnityのVector3とQuaternionは構造体で、代入するたびにコピーされます。PlayCanvasのVec3とQuatはクラスなので、代入すると1つのオブジェクトを共有します。また、JavaScriptには演算子のオーバーロードがありません。このため、次の3つを習慣にしてください。

  • 返されたベクトルは読み取り専用として扱います。 entity.getPosition()などのゲッターは、エンティティが所有して再利用するベクトルを返します。その値はエンティティが動くと変わり、書き込んでもエンティティは正しく更新されません。値を保持するにはclone()を呼び、値を変えるにはsetPosition()などのセッターを使います。
  • 演算子の代わりにメソッドを使います。 add、sub、mulScalarなどのメソッドは、呼び出したベクトル自体を変更して、そのベクトルを返します。add2(a, b)などのメソッドは、呼び出したベクトルに結果を書き込みます。
  • update内でのアロケーションを避けます。 毎フレームベクトルを作成すると、ガベージコレクターの負担が増え、カクつきの原因になります。一度だけ作成して再利用してください。
import { Script, Vec3 } from 'playcanvas';

export class Mover extends Script {
static scriptName = 'mover';

/** @attribute */
speed = 5;

// 一度だけ作成し、毎フレーム再利用する
step = new Vec3();

update(dt) {
// Unity: transform.position += transform.forward * speed * Time.deltaTime;
this.step.copy(this.entity.forward).mulScalar(this.speed * dt);
this.entity.translate(this.step);
}
}

イベントとメッセージ​

C#のイベント、UnityEvent、SendMessageは、いずれもPlayCanvasのイベントに対応します。アプリケーション、エンティティ、スクリプトは、イベントを発火したりリッスンしたりできます。

// どこからでも: アプリケーションでイベントを発火する
this.app.fire('player:died', this.entity);

// 別のスクリプトで: イベントをリッスンし、スクリプトが破棄されたらリッスンをやめる
initialize() {
this.app.on('player:died', this.onPlayerDied, this);
this.once('destroy', () => {
this.app.off('player:died', this.onPlayerDied, this);
});
}

他のオブジェクトに追加したリスナーは、スクリプトが破棄されるときに必ず削除してください。削除しないと、リスナーが動き続け、スクリプトもメモリに残ります。

コルーチンとタイマー​

PlayCanvasにはコルーチンやInvokeはありません。ゲームプレイのタイマーは、updateでカウントダウンしてください。こうすると、タイマーはapp.timeScaleに従い、スクリプトが無効な間は止まり、エンティティが破棄されると終了します。

// Unity: Invoke(nameof(Spawn), 2f);
initialize() {
this.spawnTimer = 2;
}

update(dt) {
if (this.spawnTimer > 0) {
this.spawnTimer -= dt;
if (this.spawnTimer <= 0) {
this.spawn();
}
}
}

ブラウザーのsetTimeout、setInterval、awaitを使うasync関数は、読み込みなど、ゲーム内の時間に結びつかない処理に便利です。ただし、実時間で動き、スクリプトが破棄された後も動き続けるため、destroyハンドラーでキャンセルしてください。

実行順序​

同じエンティティ上のスクリプトは、Scriptコンポーネントに並んでいる順に実行されます。この順序はドラッグで変更できます。UnityのScript Execution Order設定のようにエンティティ間の順序に頼るのではなく、postInitializeとpostUpdate、またはイベントを使ってください。

コードの共有とパッケージの利用​

スクリプトはESモジュールなので、importとexportでコードを共有します。モジュールのトップレベルの変数は、そのモジュールをインポートするすべてのスクリプトで共有されるため、C#で静的クラスやシングルトンとして書くものは、モジュールに置くのが適しています。モジュールはシーンを切り替えても残ります。

エディターでnpmのパッケージを使うには、インポートマップでパッケージ名をCDNのURLに対応付けます。エディターを使わずに作るプロジェクトでは、他のWebプロジェクトと同じように、npmからパッケージをインストールします。

Prefabとテンプレート​

テンプレートは、PlayCanvasにおけるPrefabです。ほぼ同じように機能します。

UnityPlayCanvas
GameObjectをProjectウィンドウにドラッグヒエラルキーでエンティティを右クリックし、Template > New Template を選択
Prefabインスタンスヒエラルキーのアイコンで区別されるテンプレートインスタンス
オーバーライドインスペクターに表示されるオーバーライド(View Diff ボタンで一覧を確認できます)
Overrides > Apply All / Revert Allインスタンスのルートのインスペクターにある Apply All / Revert All
ネストされたPrefabネストされたテンプレート
Prefabモードシーン内の任意のインスタンスを編集し、そのオーバーライドを適用
Unpack PrefabTemplate > Unlink From Template
Instantiate(prefab)templateAsset.resource.instantiate()

Prefabバリアントはありません。オーバーライドを持つテンプレートインスタンスが、最も近いものです。モデルをインポートすると、Unityがモデルのプレハブを作るのと同じように、その階層のテンプレートが作成されます。

テンプレートアセットへのオーバーライドの適用は、元に戻せません。実行時にテンプレートをインスタンス化するには、そのアセットが読み込まれている必要があります。@resource templateを指定したスクリプト属性から参照し、プリロードするように設定するか、先に読み込んでください。

シーン​

UnityPlayCanvas
SceneManager.LoadScene("Level 2")this.app.scenes.changeScene('Level 2')
LoadSceneMode.Additivethis.app.scenes.loadSceneHierarchy('Level 2', callback)
加算的に読み込んだシーンのアンロードloadSceneHierarchyがコールバックに渡したルートエンティティを破棄
DontDestroyOnLoadなし。以下を参照してください。
シーンごとに保存されるライティング設定シーン設定。changeSceneまたはloadSceneSettingsで適用されます

changeSceneは、新しいシーンを追加する前に、アプリケーションのルートの下にあるすべてのエンティティを破棄します。スクリプトが作成したエンティティも例外ではありません。レベルをまたいでエンティティを残すには、シーンを切り替えずに、各レベルを加算的に読み込み、前のレベルのルートエンティティを破棄します。スクリプトだけが使うデータはモジュールに置けます。モジュールはシーンの切り替えの影響を受けません。シーンの読み込みを参照してください。

エディターで開けるシーンは一度に1つなので、Unityのマルチシーン編集に相当するものはありません。シーンを加算的に読み込むのは、実行時にアプリが行うことです。

アセット​

アセットパネルはProjectウィンドウと同じようにファイルを保存しますが、プロジェクトはディスク上のフォルダーではなくPlayCanvasのサーバー上にあるため、.metaファイルはありません。各アセットにはIDがあり、インポート設定を編集する専用のインスペクターがあります。

UnityPlayCanvas
Resources.Load<Texture2D>("logo")this.app.assets.find('logo', 'texture')で見つけてから読み込む
AddressablesとAssetBundlesプリロードしないアセットを必要なときに読み込み、名前やタグで見つける
ScriptableObjectresourceが解析済みのオブジェクトになるJSONアセット
テクスチャのインポート設定テクスチャ圧縮を含むテクスチャのインスペクター
モデルのインポート設定アセットインポート設定

Preload が設定されたアセットは、アプリケーションの開始前にダウンロードされます。アセットインポート設定の Assets default to preload をオフにしない限り、新しいアセットはプリロードされます。一部のシーンでしか使わない大きなアセットでは、このオプションを外してください。

レンダリング​

PlayCanvasのレンダラーは1つなので、レンダーパイプラインを選ぶ必要はありません。WebGPUまたはWebGL 2でレンダリングし、クラスタードライティングによって、オムニライトやスポットライトが多いシーンでも高速に描画できます。

マテリアルとシェーダー​

PlayCanvasのマテリアルは、UnityのStandardシェーダーやURPのLitシェーダーと同様の物理ベースのマテリアルで、メタルネスとスペキュラーの両方のワークフローに対応しています。

UnityPlayCanvas
AlbedoまたはBase MapDiffuse
MetallicMetalness
SmoothnessGlossiness(どちらも値が大きいほど滑らか)
Normal MapNormal
OcclusionAmbient Occlusion
EmissionEmissive
Rendering Mode: CutoutAlpha Test
Rendering Mode: TransparentまたはFadeBlend TypeとOpacity
TilingとOffsetTilingとOffset(すべてのマップ共通、またはマップごと)
Shader GraphとカスタムシェーダーGLSLとWGSLで書くシェーダー

UnityのMetallicマップは、赤チャンネルにメタルネス、アルファチャンネルにスムースネスを格納します。PlayCanvasでは、同じテクスチャを Metalness マップ(チャンネルR)と Glossiness マップ(チャンネルA)に割り当てます。Glossinessの Invert オプションはマップをラフネスマップとして扱うので、オフになっていることを確認してください。

ライティング​

UnityPlayCanvas
Directional Lightディレクショナルライト
Point Lightオムニライト
Spot Lightスポットライト。Inner Cone AngleとOuter Cone Angleはライトの軸から測るため、UnityのSpot Angleの半分になります。
Area Lightレンダリング設定で Area Lights Enabled をオンにしてから、Shape をRectangle、Disk、Sphereのいずれかにしたライト
Lightingウィンドウの環境設定とフォグレンダリング設定(スカイボックス、アンビエントカラー、露出、フォグ)
Reflection Probeすべてのマテリアルを照らすスカイボックスと、ボックス投影を使うマテリアルごとのキューブマップ
Light Probeなし。ライトマップと環境ライティングを使います。
Progressive Lightmapperツールバーの Bake から、または実行中のアプリで実行するライトマッパー

ライティングを参照してください。

カメラとレイヤー​

UnityPlayCanvas
Culling MaskLayers
Depth、またはURPのカメラスタッキングPriority(値が小さいほど先にレンダリング)
Clear FlagsClear Color BufferとClear Depth Buffer
Viewport RectViewport
Target Textureスクリプトから設定するrenderTarget
Orthographic SizeOrtho Height

UnityのGameObjectは1つのレイヤーにだけ属し、レイヤーはレンダリング、物理演算、レイキャストのすべてに使われます。PlayCanvasのレイヤーはレンダリング専用で、どのカメラがメッシュを描画するか、どの順序で描画するかを決めます。各カメラは、リストにあるレイヤーを Settings > Layers で設定した順序でレンダリングします。メッシュを描画するコンポーネントは、複数のレイヤーに属することができます。物理演算では、代わりにコリジョングループとマスクでフィルタリングします。複数のカメラを参照してください。

ポストプロセス​

Unityのポストプロセスボリュームに相当するのはCameraFrameで、ブルーム、アンビエントオクルージョン、被写界深度、アンチエイリアス、カラーグレーディング、ビネットなどをカメラに追加します。エディターでは、カメラのエンティティにCameraFrameスクリプトを追加します。他のスクリプトと同じく起動したアプリでしか実行されないため、エフェクトはビューポートには表示されません。CameraFrameを使わない場合、トーンマッピングとガンマはCameraコンポーネントのプロパティです。

パフォーマンス機能​

UnityPlayCanvas
静的バッチングと動的バッチングバッチグループ
GPUインスタンシングハードウェアインスタンシング
オクルージョンカリングなし。カメラは視野外のメッシュをカリングします。
LOD Groupメッシュにはなし
Profilerデバッグとプロファイリングを参照

物理演算​

PlayCanvasの物理演算は、物理エンジンBulletのWebAssembly版であるammo.jsで動作します。オプトイン方式なので、プロジェクトに追加するには、Settings を開いて PHYSICS を展開し、IMPORT AMMO をクリックします。重力は同じセクションで、シーンごとに設定します。物理演算を参照してください。

UnityPlayCanvas
RigidbodyDynamicタイプのRigid Bodyコンポーネント
Is Kinematic を有効にしたRigidbodyKinematicタイプのRigid Body
RigidbodyのないコライダーCollisionコンポーネント と StaticタイプのRigid Body
Is Trigger を有効にしたコライダーRigid BodyのないCollisionコンポーネント
AddForce(force)entity.rigidbody.applyForce(force)
AddForce(force, ForceMode.Impulse)entity.rigidbody.applyImpulse(force)
AddTorqueapplyTorque、applyTorqueImpulse
velocity(Unity 6ではlinearVelocity)linearVelocity
Dynamicなボディの位置の設定entity.rigidbody.teleport(position)
KinematicなボディのMovePositionエンティティの位置を設定(Kinematicなボディはエンティティに追従します)
Constraints(位置や回転の固定)Linear FactorとAngular Factor
Physic MaterialRigid BodyのFrictionとRestitution
OnCollisionEnter、OnCollisionStay、OnCollisionExitCollisionコンポーネントのcollisionstart、contact、collisionendイベント
OnTriggerEnter、OnTriggerExitトリガーのCollisionコンポーネントと、トリガーに入ったRigid Bodyで発生するtriggerenterとtriggerleaveイベント
レイヤーの衝突マトリックススクリプトから設定するコリジョングループとマスク
Physics.RaycastraycastFirstとraycastAll
Character Controllerなし。スクリプトでDynamicなRigid Bodyを動かします。

Unityの開発者の多くが、次の違いでつまずきます。

  • 静的なコライダーにもRigid Bodyが必要です。 Collisionコンポーネントだけではトリガーボリュームになり、ボディを検出しますが、止めることはしません。床や壁には、StaticタイプのRigid Bodyコンポーネントを追加してください。
  • プリミティブのコリジョン形状はスケールを無視します。 ボックス、球、カプセルの形状は、エンティティをどうスケールしてもHalf ExtentsやRadiusのままです。例外はメッシュ形状です。スケールを参照してください。
  • FixedUpdateはありません。 シミュレーションは1秒に60回の固定レートでステップし、力はupdateから加えます。衝突イベントは、物理ステップごとに1回発生します。
  • Dynamicなボディは物理エンジンが所有します。 各ステップの後、エンジンはすべてのDynamicなボディの位置と回転をそのエンティティに書き込みます。そのため、Dynamicなボディは、エンティティのトランスフォームを設定するのではなく、力、速度、teleportで動かします。

ユーザーインターフェース​

PlayCanvasのキャンバス内UIは、UnityのUI(uGUI)とほぼ同じように機能します。ユーザーインターフェースを参照してください。

UnityPlayCanvas
Screen Space - OverlayのCanvasScreen Space を有効にしたScreenコンポーネント
World SpaceのCanvasScreen Space を無効にしたScreenコンポーネント
Canvas Scaler: Scale With Screen SizeScale Mode をBlendにし、Ref Resolution と Scale Blend を設定
RectTransformアンカー、ピボット、マージンを持つElementコンポーネント
Image、Raw Imageテクスチャまたはスプライトを表示する、ImageタイプのElement
Text、TextMeshProTTFまたはOTFファイルから作成したフォントアセットを使う、TextタイプのElement
ButtonButtonコンポーネント
Horizontal、Vertical、Grid Layout GroupLayout Groupコンポーネント
Layout ElementLayout Childコンポーネント
Scroll Rect、ScrollbarScroll ViewとScrollbarコンポーネント
Mask、Rect Mask 2DMask を有効にしたElement。マスクを参照してください。
EventSystemとGraphic Raycaster入力を受け取るElementで Use Input を有効にする
Toggle、Slider、DropdownElementで組み立てます。よく使うウィジェットを参照してください。
UI ToolkitHTMLとCSS

アプリはWebページの中で動作するので、インターフェースのどの部分でも、HTMLとCSSで作ってキャンバスの上に重ねることができます。テキストの入力欄やアクセシブルなメニューは、この方法の方が簡単に作れます。

アニメーション​

UnityPlayCanvas
AnimatorコンポーネントAnimコンポーネント
Animator Controllerステート、トランジション、パラメーター、レイヤーを持つanim state graphアセット
Animation Clipモデルのインポート時に作成されるアニメーションアセット
animator.SetFloat("Speed", 2f)entity.anim.setFloat('Speed', 2)
SetBool、SetInteger、SetTriggersetBoolean、setInteger、setTrigger
Avatar Maskレイヤーマスク
Blend Treeコードで作成したステートグラフでのブレンドツリー(1D、2D、Direct)。エディターのステートグラフエディターにはありません。
Root Motionなし。スクリプトでエンティティを動かします。
Animation EventアニメーションアセットのAnimイベント
Animator Override Controller1つのanim state graphを使い、エンティティごとに異なるアニメーションアセットをステートに割り当てる

PlayCanvasには、ヒューマノイドのリターゲットはありません。アニメーションはボーンを名前で動かすため、同じ名前のボーンを持つモデルであれば再生できますが、プロポーションの異なるスケルトンに合わせて調整されることはありません。アニメーションは、再生するスケルトン向けに作成するか、そのスケルトンにベイクしてください。

オーディオ​

UnityPlayCanvas
Audio SourceSoundコンポーネントのスロット
audioSource.Play()entity.sound.play('slot name')
Spatial Blendを3Dに設定Soundコンポーネントの Positional
Min DistanceとMax DistanceRef DistanceとMax Distance
Volume RolloffDistance Model(デフォルトはLinearで、UnityのデフォルトはLogarithmic)
Audio ListenerAudio Listenerコンポーネント
Audio Mixerなし。Web Audio APIを使います。

ブラウザーは、ユーザーがクリックやキー入力などでページを操作するまで音を再生しません。音楽などのサウンドは、最初のクリックの後に開始してください。

コンテンツの移行​

Unityのプロジェクトを自動で変換するツールはありません。アセットは標準的なファイル形式で移行し、コードは書き直します。

  • モデルとアニメーション。 UnityGLTFやUnityのglTFastパッケージなどのglTFエクスポーターでGLBファイルとして、またはUnityのFBX ExporterパッケージでFBXとしてエクスポートします。エディターがインポートするのはglTFのバイナリ形式である.glbで、.gltfではありません。ファイルをアセットパネルにドラッグすると、FBXファイルはGLBに変換され、各モデルからRender、マテリアル、テクスチャ、アニメーションのアセットと、その階層のテンプレートが作成されます。3Dモデルのインポートを参照してください。
  • マテリアル。 Unityのシェーダーは移行できません。glTFエクスポーターはStandardやURP LitのマテリアルをglTFのPBRマテリアルに変換し、それがPlayCanvasのマテリアルとしてインポートされます。FBXファイルが持つマテリアルの情報は少ないため、FBXのマテリアルは設定し直すことになります。
  • テクスチャ、オーディオ、フォント。 元のファイルをアップロードします。サポートされているフォーマットを参照してください。
  • シーンとPrefab。 シーンの静的なジオメトリを1つのGLBとしてエクスポートしてレイアウトを保ち、ライト、カメラ、物理演算、UI、スクリプトはエディターで追加します。
  • Terrain。 エクスポートする前に、Terrainをメッシュに変換します。
  • スクリプトとシェーダー。 JavaScriptと、GLSLまたはWGSLで書き直します。AIコーディングアシスタントは、MonoBehaviourの変換を得意としています。エディターのプロジェクトのスクリプトをアシスタントに扱わせるには、VS Code拡張機能でスクリプトを同期します。シーンも編集させる場合は、MCPサーバーでエディターに接続します。
  • ライトマップ。 PlayCanvasのライトマッパーでベイクし直すか、3Dモデリングツールでベイクしてモデルと一緒にインポートします。

公開​

Unityはプラットフォームごとにプレイヤーをビルドします。PlayCanvasが公開するのは、HTML、JavaScript、アセットで構成されるWebアプリです。

デバッグとプロファイリング​

アプリはブラウザーで動作するので、起動したタブで、ブラウザーの開発者ツール(F12、macOSではCmd+Option+I)を使ってデバッグします。

UnityPlayCanvas
Consoleウィンドウ開発者ツールの Console タブ。コンソールロギングを参照してください。
Visual StudioやRiderのアタッチ開発者ツールの Sources タブで設定するブレークポイント。ブラウザ開発ツールを参照してください。
Debug.Break()開発者ツールを開いているときにアプリを一時停止するdebugger;文
ProfilerLaunchボタンのメニューから使うプロファイラーとMiniStats、および開発者ツールの Performance タブ
Frame Debugger同じくLaunchボタンのメニューから使え、各フレームのレンダーパスとドローコールを表示するInspectorと、ネイティブのGPUプロファイラー
Debug.DrawLineとGizmosthis.app.drawLine()と、球やボックスなどの形状を描くWireRenderer

バージョン管理とコラボレーション​

Unityのプロジェクトは、自分でバージョン管理の下に置くフォルダーです。PlayCanvasのプロジェクトはオンラインで共有されます。チームメンバーが同じシーンを開くと、リアルタイムで一緒に編集でき、互いの変更をその場で確認できます。履歴はエディターのバージョン管理で管理します。コミットのようにチェックポイントを作成し、ブランチで作業し、競合を解決しながらマージします。

組み込みの機能がないもの​

UnityPlayCanvasでの代替手段
Terrain地形をメッシュとしてインポート
NavMeshとAI NavigationPlayCanvasに対応したrecast-navigation-jsなどのライブラリ
Character Controllerスクリプトで動かすDynamicなRigid Body、またはエンジンの一人称・三人称コントローラースクリプト
2D物理Linear FactorとAngular Factorで3つ目の軸を固定した3D物理
TimelineとCinemachineアニメーションさせる値にCurve属性を使うスクリプト
VFX GraphParticle Systemコンポーネント
Light Probeとリアルタイムのグローバルイルミネーションライトマップと環境ライティング
DOTweenなどのトゥイーンパッケージCDNからインポートするJavaScriptのトゥイーンライブラリ

次のステップ​