Skip to main content

Migrating from Unity

If you have built projects in Unity, much of PlayCanvas will feel familiar. The PlayCanvas Editor has a hierarchy, an inspector, an assets panel and a 3D viewport. Scenes are trees of entities, entities are made of components, and behavior is written in scripts with lifecycle methods such as initialize and update.

This guide maps what you know from Unity to PlayCanvas: the Editor's panels and shortcuts, the names of common concepts, C# APIs and their JavaScript equivalents, and the places where PlayCanvas works differently. Each section links to the full documentation for its topic.

Key Differences​

  • Everything runs in the browser. The Editor is a web application, your project is stored online, and what you publish is a web app. There is no WebGL build to wait for: your app runs on the same engine as the Editor's viewport.
  • Scripts are JavaScript. You write scripts as JavaScript modules instead of C# classes, and nothing is compiled: save a script and launch.
  • Scripts share one component. In Unity, each MonoBehaviour is a component of its own. In PlayCanvas, an entity has one Script component, which holds all of its scripts.
  • One component of each type per entity. An entity can have one camera, one light, one collision component and so on, and each script once. When you need two of something, such as two colliders, use child entities.
  • The coordinate system is right-handed. Forward is -Z rather than +Z. See Coordinate System and Units.
  • There is no Play Mode. Launch runs your scene in a separate browser tab that stays connected to the Editor.
  • Collaboration and version control are built in. Several people can edit the same scene at the same time, and checkpoints, branches and merging are part of the Editor.

The Editor​

The Editor's layout is much like Unity's default layout:

The PlayCanvas Editor, with the toolbar, hierarchy, viewport, inspector and assets panel numbered 1 to 5

PlayCanvasUnity
1ToolbarThe main menu, the transform tools and the toolbar
2HierarchyHierarchy window
3ViewportScene view
4InspectorInspector window
5Assets panelProject window

Other Unity windows and menus have these equivalents:

UnityPlayCanvas
Play button and Game viewThe Launch button at the top right of the viewport
Console windowThe browser's developer tools console in the launched tab. The Editor's own CONSOLE panel, at the bottom left, shows Editor messages such as script parse errors.
Project Settings and Lighting windowSettings, from the cog icon on the toolbar
Build SettingsPublish / Download on the toolbar
Animator windowThe anim state graph editor, which opens when you select an anim state graph asset
Asset StoreASSET STORE at the top right of the assets panel, with PlayCanvas Store and Sketchfab assets
Visual Studio or RiderThe built-in Code Editor, or VS Code with the VS Code extension
Unity Version Control or GitBuilt-in version control

Launching Instead of Play Mode​

Unity plays your game inside the Editor. PlayCanvas opens it in a new browser tab, the Launch Page, which runs your scene just as it will run when published. Click Launch, or press Ctrl+Enter.

The Launch Page stays connected to the Editor. Changes to entities, components, script attributes, assets and scene settings show up in the running app as you make them, and unlike edits made in Play Mode, they are not thrown away when you stop: the Editor saves every change as you make it. Place the two tabs side by side to work this way. Code changes need a reload, unless the script supports hot reloading.

Scripts never run in the Editor's viewport, so there is nothing like Unity's [ExecuteInEditMode]: anything a script creates or changes appears only in the launched app.

The Launch button's menu has options for testing: choose the graphics API, open the Profiler, MiniStats or Inspector, and tick Debug to log the engine's warnings and errors to the console.

ActionUnityPlayCanvas
OrbitAlt + left-dragLeft-drag
Look aroundRight-dragRight-drag
PanMiddle-dragMiddle-drag, or Shift + left-drag
ZoomMouse wheelMouse wheel
FlyHold the right mouse button and press W, A, S, D, Q, EPress W, A, S, D, Q, E, with no mouse button held (hold Shift to go faster)
Frame the selectionFF
Rectangle selectionLeft-drag over empty spaceCtrl + left-drag
Add to or remove from the selectionShift+click or Ctrl+clickCtrl+click

Keyboard Shortcuts​

ActionUnityPlayCanvas
Move toolW1
Rotate toolE2
Scale toolR3
Rect tool (UI elements)T4
Toggle local and world gizmo spaceXL
Frame the selectionFF
PlayCtrl+PCtrl+Enter (Launch)
Create an empty childAlt+Shift+NCtrl+E
DuplicateCtrl+DCtrl+D
RenameF2F2
DeleteDeleteDelete
Undo / redoCtrl+Z / Ctrl+YCtrl+Z / Ctrl+Y
SaveCtrl+SNot needed, because every change is saved. Ctrl+S opens the form for creating a version control checkpoint.
Maximize the viewShift+SpaceSpace (hides the Hierarchy, Inspector and Assets panels)
Front, right and top viewsClick the arms of the Scene GizmoNumpad 1, 3 and 7 (add Ctrl for the opposite side), or click the axes of the view cube
Toggle orthographic and perspectiveClick the center of the Scene GizmoNumpad 5

On macOS, use Cmd in place of Ctrl. Unity's View tool (Q) has no equivalent, because navigating the viewport never needs a tool. The full list is on the Controls & Keyboard Shortcuts page.

Concepts and Terminology​

UnityPlayCanvasNotes
GameObjectEntity
ComponentComponentComponents are properties of the entity: entity.camera, entity.light.
TransformPart of every entityEntities have a position, rotation and scale of their own. See Hierarchy & Transforms.
MonoBehaviourScriptAttached through an entity's Script component.
PrefabTemplateSee Prefabs and Templates.
SceneScene
TagTagsAn entity can have any number of tags.
LayerLayersRendering only. Physics uses collision groups and masks.
Mesh Filter and Mesh RendererRender componentSkinned meshes use the Render component too.
MaterialMaterial
CameraCamera component
LightLight componentA point light is called an omni light.
RigidbodyRigid Body component
ColliderCollision componentWithout a rigid body, a collision component is a trigger. See Physics.
Audio SourceSound componentOne component plays any number of sounds, called slots.
Audio ListenerAudio Listener component
Particle SystemParticle System component
Animator and Animator ControllerAnim component and anim state graph asset
CanvasScreen component
RectTransform, Image and TextElement component
Sprite RendererSprite component
ScriptableObjectJSON assetOr a JavaScript module that exports the data.
Resources and AddressablesThe asset registrySee Assets.

Coordinate System and Units​

Both engines use Y as up, measure distances in meters and take angles in degrees. The difference is handedness: Unity's coordinate system is left-handed with +Z forward, while PlayCanvas's is right-handed with -Z forward. Cameras look down their negative Z axis, entity.forward returns the negative Z axis, and lookAt points the negative Z axis at its target.

Unity's left-handed axes with +Z forward beside PlayCanvas's right-handed axes with -Z forward

This has a few consequences when you port code and content:

  • Moving forward means moving along -Z. transform.Translate(0, 0, speed * Time.deltaTime) becomes this.entity.translateLocal(0, 0, -this.speed * dt).
  • Positive rotations turn the other way. A positive rotation about Y turns an entity to its left (counter-clockwise seen from above), and a positive rotation about X tilts it up. In Unity they turn right and down, so a camera that looks down at the scene has a negative X rotation in PlayCanvas.
  • Euler angles are applied in a different order. PlayCanvas applies the X rotation first, then Y, then Z, while Unity applies Z, then X, then Y. Together with the flipped axis, this means rotations you read off Unity's Inspector don't carry over. Bring layouts across in exported files instead, which convert them. See Bringing Your Content Over.
  • Lights shine down -Y. Directional and spot lights point along their local negative Y axis, so an unrotated light shines straight down. In Unity they shine along +Z.
  • Models usually face +Z. glTF defines +Z as the front of a model, and models exported from Unity keep facing +Z, which is backward in PlayCanvas. If a script moves a character along entity.forward, put the model inside a parent entity and rotate it 180° about Y, so that the model faces the parent's forward direction.
  • The default field of view is narrower. Both engines measure the field of view vertically, but a new PlayCanvas camera starts at 45° rather than 60°.

Scripting​

PlayCanvas scripts are JavaScript classes that extend Script. Each lives in a .mjs file in the assets panel, and you attach it to an entity by adding a Script component and choosing the script. See Scripting for the complete guide.

A Script Side by Side​

Here is a Unity script that spins its GameObject and switches a light on and off with the space bar:

using UnityEngine;

public class Spinner : MonoBehaviour
{
// Degrees per second
[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}");
}
}
}

And here is the same script for PlayCanvas, in a file called spinner.mjs:

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

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

/**
* Degrees per second.
*
* @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}`);
}
}
}

The differences to note:

  • scriptName names the script. The Editor lists the script under this name, and other scripts use it to reach the script: entity.script.spinner.
  • @attribute takes the place of [SerializeField]. Class fields tagged with @attribute in a JSDoc comment appear in the Inspector, and the comment's first sentence becomes the field's tooltip. See Inspector Fields.
  • Entity attributes reference entities, not components. An attribute of type Entity holds the entity, so you reach its components through it, as in this.lamp.light.
  • this.entity is the GameObject and its transform. this.app is the application, which gives you input devices, the scene hierarchy (this.app.root), the asset registry and more.
  • dt is Time.deltaTime. The engine passes the frame time, in seconds, to update.

Lifecycle Methods​

UnityPlayCanvasNotes
Awake, Startinitialize()Called once, the first time the script and its entity are both enabled. Don't use the class constructor for setup.
Start that depends on another script's AwakepostInitialize()Called after initialize has run on every script, so other scripts are ready.
Updateupdate(dt)
LateUpdatepostUpdate(dt)Called after update has run on every script.
FixedUpdateNoneThe physics engine steps at a fixed rate on its own. See Physics.
OnEnable, OnDisableenable and disable eventsSubscribe with this.on('enable', ...).
OnDestroydestroy eventSubscribe with this.once('destroy', ...), and unsubscribe from other events there.
OnValidateAn attribute setterSee Responding to Attribute Changes.
OnCollisionEnter, OnTriggerEnter and so onCollision component eventsSee Physics.

See Script Lifecycle for the details.

Inspector Fields​

Script attributes are PlayCanvas's serialized fields. The Editor reads them from your code, so after you add or change one, click Parse on the script asset or the Script component to update the Inspector.

UnityPlayCanvas
[SerializeField] float speed = 5f;/** @attribute */ speed = 5;
[Tooltip("...")]The first sentence of the attribute's JSDoc comment
[Range(0, 10)]@range [0, 10]
[Min(0)]@range [0]
[HideInInspector], or no [SerializeField]A field without @attribute
GameObject or Transform field@type {Entity}
Texture2D, Material, AudioClip field@type {Asset} with @resource texture, material or audio
Prefab field@type {Asset} with @resource template
Color, Vector3 fieldcolor = new Color(), offset = new Vec3()
AnimationCurve field@type {Curve}
Array or List<T> fieldAn array type, such as @type {Entity[]}
enum fieldAn @enum object, or a union of literals such as @type {'low' | 'high'}
[Serializable] class fieldAn @interface class or a @typedef

Attribute values set in the Editor are applied before initialize runs. See the attributes reference for every tag.

Common API Equivalents​

In these tables, entity stands for any entity, such as this.entity in a script, and imports such as Vec3 and KEY_W come from playcanvas.

Entities and Components​

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, which reads false when an ancestor is disabled
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() copies an entity's components and children, but its scripts start again from the attribute values they were created with. Values that a script has changed while the app runs are not copied, whereas Unity's Instantiate copies the current values of serialized fields.

Transforms​

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) (world space: translate)
transform.Rotate(x, y, z)entity.rotateLocal(x, y, z) (world space: rotate)
transform.LookAt(target)entity.lookAt(target.getPosition())
transform.forward, right, upentity.forward, entity.right, entity.up (forward is -Z)
transform.parententity.parent
transform.SetParent(parent)entity.reparent(parent), which keeps the local transform, so the entity moves with its new parent
transform.GetChild(i), childCountentity.children[i], entity.children.length

Time, Math and Debugging​

UnityPlayCanvas
Time.deltaTimeThe dt argument of update
Time.timeScalethis.app.timeScale
Time.timeNone. Add up dt in update.
Vector3, Quaternion, ColorVec3, Quat, Color
Vector3.up, Vector3.forwardVec3.UP, Vec3.FORWARD (which is (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)), because lerp doesn't clamp t
Mathf.Clamp, Mathf.Lerpmath.clamp, math.lerp
Mathf.Deg2Radmath.DEG_TO_RAD
Random.Range(min, max) with floatsmath.random(min, max)
Random.Range(min, max) with integersMath.floor(math.random(min, max)), because math.random always returns a float
Debug.Log, LogWarning, LogErrorconsole.log, console.warn, console.error
Debug.DrawLine(start, end, color)this.app.drawLine(start, end, color)

Input​

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.mousePositionThe x and y of mouse events, measured from the top-left of the canvas rather than the bottom-left
Input.GetAxis("Mouse X")The dx of a mousemove event
Cursor.lockState = CursorLockMode.Lockedthis.app.mouse.enablePointerLock()
Input.touchesTouch events on this.app.touch
Physics.Raycastthis.app.systems.rigidbody.raycastFirst(start, end)

There are no input axes or action maps: read the devices directly, or map them to actions in a script of your own. Browsers only allow pointer lock in response to a click or key press.

Web Platform​

UnityPlayCanvas
PlayerPrefsThe browser's localStorage
UnityWebRequestfetch
JsonUtilityJSON.parse and JSON.stringify
Application.OpenURLwindow.open

Vectors Are Objects​

Unity's Vector3 and Quaternion are structs, copied whenever you assign them. PlayCanvas's Vec3 and Quat are classes, so assignment shares one object, and JavaScript has no operator overloading. Three habits follow:

  • Treat returned vectors as read-only. Getters such as entity.getPosition() return a vector that the entity owns and reuses: its value changes when the entity moves, and writing to it does not update the entity properly. Call clone() to keep a value, and use setters such as setPosition() to change one.
  • Use methods instead of operators. Methods such as add, sub and mulScalar change the vector they are called on and return it. add2(a, b) and similar methods write a result into the vector they are called on.
  • Avoid allocating in update. Creating vectors every frame gives the garbage collector work that can cause stutters. Create them once and reuse them.
import { Script, Vec3 } from 'playcanvas';

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

/** @attribute */
speed = 5;

// Created once, and reused every frame
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);
}
}

Events and Messages​

C# events, UnityEvent and SendMessage all map to PlayCanvas events. The application, entities and scripts can fire events and listen for them:

// Anywhere: fire an event on the application
this.app.fire('player:died', this.entity);

// In another script: listen for it, and stop listening when the script is destroyed
initialize() {
this.app.on('player:died', this.onPlayerDied, this);
this.once('destroy', () => {
this.app.off('player:died', this.onPlayerDied, this);
});
}

Always remove listeners you add to other objects when your script is destroyed. Otherwise they keep running, and keep the script in memory.

Coroutines and Timers​

PlayCanvas has no coroutines or Invoke. For gameplay timers, count down in update: the timer then honors app.timeScale, pauses while the script is disabled and stops when the entity is destroyed.

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

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

The browser's setTimeout, setInterval and async functions with await are useful for loading and other work that is not tied to game time, but they run in real time and keep running after the script is destroyed, so cancel them in a destroy handler.

Execution Order​

Scripts on the same entity run in the order they are listed in its Script component, which you can change by dragging. Instead of depending on the order between entities, as Unity's Script Execution Order settings allow, use postInitialize and postUpdate, or events.

Sharing Code and Using Packages​

Scripts are ES modules, so they share code through import and export. A module's top-level variables are shared by every script that imports it, which makes modules the place for what you would write as static classes and singletons in C#. They also survive scene changes.

To use a package from npm in the Editor, map its name to a CDN URL with an import map. Projects built without the Editor install packages from npm like any other web project.

Prefabs and Templates​

Templates are PlayCanvas's prefabs. They work much the same way:

UnityPlayCanvas
Drag a GameObject into the Project windowRight-click an entity in the Hierarchy and select Template > New Template
Prefab instanceTemplate instance, marked by its icon in the Hierarchy
OverridesOverrides, shown in the Inspector, with a View Diff button
Overrides > Apply All / Revert AllApply All / Revert All in the Inspector of the instance's root
Nested prefabsNested templates
Prefab ModeEdit any instance in a scene, then apply its overrides
Unpack PrefabTemplate > Unlink From Template
Instantiate(prefab)templateAsset.resource.instantiate()

There are no prefab variants: a template instance with overrides is the closest equivalent. Importing a model creates a template of its hierarchy, much as Unity creates a model prefab.

You cannot undo applying overrides to a template asset. To instantiate a template at runtime, its asset must be loaded: reference it from a script attribute with @resource template and either mark it to preload or load it first.

Scenes​

UnityPlayCanvas
SceneManager.LoadScene("Level 2")this.app.scenes.changeScene('Level 2')
LoadSceneMode.Additivethis.app.scenes.loadSceneHierarchy('Level 2', callback)
Unloading an additive sceneDestroy the root entity that loadSceneHierarchy passed to its callback
DontDestroyOnLoadNone. See below.
Lighting settings saved with each sceneScene settings, applied by changeScene or loadSceneSettings

changeScene destroys every entity under the application root, including entities that your scripts created, before it adds the new scene. To keep entities alive from one level to the next, don't change scenes: load each level additively and destroy the previous level's root entity. Data that only scripts need can live in modules, which changing scenes doesn't affect. See Loading Scenes.

The Editor opens one scene at a time, so there is no equivalent of Unity's multi-scene editing. Loading scenes additively is something your app does when it runs.

Assets​

The assets panel stores files much like the Project window, but projects live on PlayCanvas's servers rather than in a folder on your disk, so there are no .meta files. Each asset has an ID and an Inspector of its own for its import settings.

UnityPlayCanvas
Resources.Load<Texture2D>("logo")this.app.assets.find('logo', 'texture'), then load it
Addressables and AssetBundlesAssets that do not preload, loaded when you need them, found by name or tag
ScriptableObjectA JSON asset, whose resource is the parsed object
Texture import settingsThe texture inspector, including texture compression
Model import settingsAsset Import settings

Assets marked Preload are downloaded before the application starts. New assets preload unless you turn off Assets default to preload in the Asset Import settings, so clear the option on large assets that only some scenes need.

Rendering​

PlayCanvas has one renderer, so there is no render pipeline to choose. It renders with WebGPU or WebGL 2, and its clustered lighting keeps scenes with many omni and spot lights fast.

Materials and Shaders​

The PlayCanvas material is a physically based material like Unity's Standard shader and URP's Lit shader, with metalness and specular workflows:

UnityPlayCanvas
Albedo or Base MapDiffuse
MetallicMetalness
SmoothnessGlossiness (higher is smoother in both)
Normal MapNormal
OcclusionAmbient Occlusion
EmissionEmissive
Rendering Mode: CutoutAlpha Test
Rendering Mode: Transparent or FadeBlend Type, with Opacity
Tiling and OffsetTiling and Offset, for all maps or for each map
Shader Graph and custom shadersShaders written in GLSL and WGSL

Unity's Metallic map holds metalness in its red channel and smoothness in its alpha channel. In PlayCanvas, assign the same texture to the Metalness map with channel R and to the Glossiness map with channel A. Check that the glossiness Invert option is off, because it makes the map act as a roughness map.

Lighting​

UnityPlayCanvas
Directional lightDirectional light
Point lightOmni light
Spot lightSpot light. Its Inner and Outer Cone Angles are measured from the light's axis, so they are half of Unity's Spot Angle.
Area lightA light with a Rectangle, Disk or Sphere Shape, once Area Lights Enabled is on in Rendering Settings
Lighting window environment settings, and fogRendering Settings: skybox, ambient color, exposure and fog
Reflection probesThe skybox, which lights every material, and per-material cubemaps with box projection
Light probesNone. Use lightmaps and environment lighting.
Progressive LightmapperThe lightmapper, run from Bake on the toolbar, or in the running app

See Lighting.

Cameras and Layers​

UnityPlayCanvas
Culling MaskLayers
Depth, or URP camera stackingPriority (lower values render first)
Clear FlagsClear Color Buffer and Clear Depth Buffer
Viewport RectViewport
Target TexturerenderTarget, set from a script
Orthographic SizeOrtho Height

A Unity GameObject sits on exactly one layer, and layers drive rendering, physics and ray casts alike. PlayCanvas layers are for rendering only. They decide which cameras draw a mesh and in what order: each camera renders the layers in its list, in the order set in Settings > Layers, and components that draw meshes can be on more than one layer. Physics filters with collision groups and masks instead. See Multiple Cameras.

Post-Processing​

Unity's post-processing volumes correspond to CameraFrame, which adds bloom, ambient occlusion, depth of field, anti-aliasing, color grading, vignette and more to a camera. In the Editor, add the CameraFrame script to your camera entity. Like any script, it only runs in the launched app, so the effects don't appear in the viewport. Without it, tone mapping and gamma are properties of the Camera component.

Performance Features​

UnityPlayCanvas
Static and dynamic batchingBatch groups
GPU instancingHardware instancing
Occlusion cullingNone. Cameras cull meshes outside their view.
LOD GroupNone for meshes
ProfilerSee Debugging and Profiling

Physics​

PlayCanvas physics runs on ammo.js, a WebAssembly build of the Bullet physics engine. It is opt-in: to add it to a project, open Settings, expand PHYSICS and click IMPORT AMMO. Gravity is set in the same section, for each scene. See Physics.

UnityPlayCanvas
RigidbodyRigid Body component of type Dynamic
Rigidbody with Is KinematicRigid Body of type Kinematic
Collider without a RigidbodyCollision component and a Rigid Body of type Static
Collider with Is TriggerCollision component without a Rigid Body
AddForce(force)entity.rigidbody.applyForce(force)
AddForce(force, ForceMode.Impulse)entity.rigidbody.applyImpulse(force)
AddTorqueapplyTorque, applyTorqueImpulse
velocity (linearVelocity in Unity 6)linearVelocity
Setting a dynamic body's positionentity.rigidbody.teleport(position)
MovePosition on a kinematic bodySet the entity's position: kinematic bodies follow their entity
Constraints (freeze position or rotation)Linear Factor and Angular Factor
Physic MaterialFriction and Restitution on the Rigid Body
OnCollisionEnter, OnCollisionStay, OnCollisionExitcollisionstart, contact and collisionend events on the collision component
OnTriggerEnter, OnTriggerExittriggerenter and triggerleave events, on the trigger's collision component and on the rigid body that enters it
Layer collision matrixCollision groups and masks, set from a script
Physics.RaycastraycastFirst and raycastAll
Character ControllerNone. Drive a dynamic rigid body from a script.

These differences catch out most Unity developers:

  • Static colliders need a rigid body. A collision component on its own is a trigger volume, which detects bodies but does not stop them. Give floors and walls a Rigid Body component of type Static.
  • Primitive collision shapes ignore scale. A box, sphere or capsule shape keeps its Half Extents or Radius however you scale its entity. Mesh shapes are the exception. See Scale.
  • There is no FixedUpdate. The simulation steps at a fixed rate of 60 times a second, and you apply forces from update. Collision events fire once per physics step.
  • Physics owns dynamic bodies. After each step the engine writes the position and rotation of every dynamic body to its entity, so move dynamic bodies with forces, velocities or teleport, rather than by setting the entity's transform.

User Interface​

PlayCanvas's in-canvas UI works much like Unity's UI (uGUI). See User Interface.

UnityPlayCanvas
Canvas in Screen Space - OverlayScreen component with Screen Space enabled
Canvas in World SpaceScreen component with Screen Space disabled
Canvas Scaler: Scale With Screen SizeScale Mode Blend, with a Ref Resolution and Scale Blend
RectTransformElement component: anchor, pivot and margins
Image, Raw ImageElement of type Image, showing a texture or a sprite
Text, TextMeshProElement of type Text, with a font asset made from a TTF or OTF file
ButtonButton component
Horizontal, Vertical and Grid Layout GroupLayout Group component
Layout ElementLayout Child component
Scroll Rect, ScrollbarScroll View and Scrollbar components
Mask, Rect Mask 2DAn element with Mask enabled. See Masks.
EventSystem and Graphic RaycasterEnable Use Input on the elements that should receive input
Toggle, Slider, DropdownBuilt from elements. See Common Widgets.
UI ToolkitHTML and CSS

Because your app runs in a web page, you can also build any part of the interface in HTML and CSS, laid over the canvas. Text input fields and accessible menus are easier to build that way.

Animation​

UnityPlayCanvas
Animator componentAnim component
Animator ControllerAnim state graph asset, with states, transitions, parameters and layers
Animation ClipAnimation asset, created when you import a model
animator.SetFloat("Speed", 2f)entity.anim.setFloat('Speed', 2)
SetBool, SetInteger, SetTriggersetBoolean, setInteger, setTrigger
Avatar MaskLayer mask
Blend TreeBlend trees (1D, 2D and direct), in state graphs created from code. The Editor's state graph editor has none.
Root MotionNone. Move the entity from a script.
Animation EventsAnim events on the animation asset
Animator Override ControllerOne anim state graph, with different animation assets assigned to its states on each entity

PlayCanvas has no humanoid retargeting. An animation drives bones by name, so it plays on any model whose bones have the same names, but nothing adapts it to a skeleton with different proportions. Author or bake animations for the skeleton they play on.

Audio​

UnityPlayCanvas
Audio SourceA slot on a Sound component
audioSource.Play()entity.sound.play('slot name')
Spatial Blend set to 3DPositional on the Sound component
Min Distance and Max DistanceRef Distance and Max Distance
Volume RolloffDistance Model, which defaults to Linear where Unity defaults to Logarithmic
Audio ListenerAudio Listener component
Audio MixerNone. Use the Web Audio API.

Browsers do not play sound until the user has interacted with the page, for example by clicking or pressing a key, so start music and other sounds after the first click.

Bringing Your Content Over​

There is no automatic converter from Unity projects. Assets come across through standard file formats, and code is rewritten.

  • Models and animations. Export them as GLB files with a glTF exporter such as UnityGLTF or Unity's glTFast package, or as FBX with Unity's FBX Exporter package. The Editor imports the binary .glb form of glTF, not .gltf. Drag the files into the assets panel: FBX files are converted to GLB, and each model produces render, material, texture and animation assets, plus a template of its hierarchy. See Importing 3D Models.
  • Materials. Unity shaders do not carry over. glTF exporters convert Standard and URP Lit materials to glTF's PBR materials, which import as PlayCanvas materials. FBX files carry less, so expect to set up FBX materials again.
  • Textures, audio and fonts. Upload the source files. See Supported Formats.
  • Scenes and prefabs. Export the static geometry of a scene as a single GLB to keep its layout, then add lights, cameras, physics, UI and scripts in the Editor.
  • Terrain. Convert terrain to a mesh before you export it.
  • Scripts and shaders. Rewrite them in JavaScript and in GLSL or WGSL. AI coding assistants are good at translating MonoBehaviours. To let one work on your Editor project's scripts, sync them with the VS Code extension, and connect it to the Editor with the MCP Server to let it edit scenes too.
  • Lightmaps. Bake them again with the PlayCanvas lightmapper, or bake them in a 3D modeling tool and import them with the model.

Publishing​

Unity builds a player for each platform. PlayCanvas publishes a web app, made of HTML, JavaScript and your assets:

Debugging and Profiling​

Your app runs in the browser, so you debug it with the browser's developer tools (F12, or Cmd+Option+I on macOS) in the launched tab.

UnityPlayCanvas
Console windowThe Console tab of the developer tools. See Console Logging.
Attaching Visual Studio or RiderBreakpoints in the Sources tab of the developer tools. See Browser Dev Tools.
Debug.Break()A debugger; statement, which pauses the app while the developer tools are open
ProfilerThe Profiler and MiniStats, from the Launch button's menu, and the Performance tab of the developer tools
Frame DebuggerThe Inspector, also from the Launch button's menu, which shows the render passes and draw calls of each frame, and native GPU profilers
Debug.DrawLine and Gizmosthis.app.drawLine(), and WireRenderer for spheres, boxes and other shapes

Version Control and Collaboration​

A Unity project is a folder that you put under version control. A PlayCanvas project is shared online: when teammates open a scene, they edit it together in real time and see each other's changes as they happen. History is kept with the Editor's version control: create checkpoints as you would commits, work on branches, and merge them with conflict resolution.

Features Without a Built-in Equivalent​

UnityAlternative in PlayCanvas
TerrainImport terrain as meshes
NavMesh and AI NavigationA library such as recast-navigation-js, which supports PlayCanvas
Character ControllerA dynamic rigid body driven by a script, or the engine's first-person and third-person controller scripts
2D physics3D physics with the third axis locked by Linear Factor and Angular Factor
Timeline and CinemachineScripts, with curve attributes for animated values
VFX GraphThe Particle System component
Light probes and realtime global illuminationLightmaps and environment lighting
Tweening packages, such as DOTweenA JavaScript tweening library, imported from a CDN

Next Steps​