安装
npx skills add https://github.com/freshtechbro/claudedesignskills --skill playcanvas-engine
- PlayCanvas Engine Skill
- Lightweight WebGL/WebGPU game engine with entity-component architecture, visual editor integration, and performance-focused design.
- When to Use This Skill
- Trigger this skill when you see:
- "PlayCanvas engine"
- "WebGL game engine"
- "entity component system"
- "PlayCanvas application"
- "3D browser games"
- "online 3D editor"
- "lightweight 3D engine"
- Need for editor-first workflow
- Compare with:
- Three.js
-
- Lower-level, more flexible but requires more setup
- Babylon.js
-
- Feature-rich but heavier, has editor but less mature
- A-Frame
-
- VR-focused, declarative HTML approach
- Use PlayCanvas for: Game projects, editor-first workflow, performance-critical apps
- Core Concepts
- 1. Application
- The root PlayCanvas application manages the rendering loop.
- import
- *
- as
- pc
- from
- 'playcanvas'
- ;
- // Create canvas
- const
- canvas
- =
- document
- .
- createElement
- (
- 'canvas'
- )
- ;
- document
- .
- body
- .
- appendChild
- (
- canvas
- )
- ;
- // Create application
- const
- app
- =
- new
- pc
- .
- Application
- (
- canvas
- ,
- {
- keyboard
- :
- new
- pc
- .
- Keyboard
- (
- window
- )
- ,
- mouse
- :
- new
- pc
- .
- Mouse
- (
- canvas
- )
- ,
- touch
- :
- new
- pc
- .
- TouchDevice
- (
- canvas
- )
- ,
- gamepads
- :
- new
- pc
- .
- GamePads
- (
- )
- }
- )
- ;
- // Configure canvas
- app
- .
- setCanvasFillMode
- (
- pc
- .
- FILLMODE_FILL_WINDOW
- )
- ;
- app
- .
- setCanvasResolution
- (
- pc
- .
- RESOLUTION_AUTO
- )
- ;
- // Handle resize
- window
- .
- addEventListener
- (
- 'resize'
- ,
- (
- )
- =>
- app
- .
- resizeCanvas
- (
- )
- )
- ;
- // Start the application
- app
- .
- start
- (
- )
- ;
- 2. Entity-Component System
- PlayCanvas uses ECS architecture: Entities contain Components.
- // Create entity
- const
- entity
- =
- new
- pc
- .
- Entity
- (
- 'myEntity'
- )
- ;
- // Add to scene hierarchy
- app
- .
- root
- .
- addChild
- (
- entity
- )
- ;
- // Add components
- entity
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- }
- )
- ;
- entity
- .
- addComponent
- (
- 'script'
- )
- ;
- // Transform
- entity
- .
- setPosition
- (
- 0
- ,
- 1
- ,
- 0
- )
- ;
- entity
- .
- setEulerAngles
- (
- 0
- ,
- 45
- ,
- 0
- )
- ;
- entity
- .
- setLocalScale
- (
- 2
- ,
- 2
- ,
- 2
- )
- ;
- // Parent-child hierarchy
- const
- parent
- =
- new
- pc
- .
- Entity
- (
- 'parent'
- )
- ;
- const
- child
- =
- new
- pc
- .
- Entity
- (
- 'child'
- )
- ;
- parent
- .
- addChild
- (
- child
- )
- ;
- 3. Update Loop
- The application fires events during the update loop.
- app
- .
- on
- (
- 'update'
- ,
- (
- dt
- )
- =>
- {
- // dt is delta time in seconds
- entity
- .
- rotate
- (
- 0
- ,
- 10
- *
- dt
- ,
- 0
- )
- ;
- }
- )
- ;
- app
- .
- on
- (
- 'prerender'
- ,
- (
- )
- =>
- {
- // Before rendering
- }
- )
- ;
- app
- .
- on
- (
- 'postrender'
- ,
- (
- )
- =>
- {
- // After rendering
- }
- )
- ;
- 4. Components
- Core components extend entity functionality:
- Model Component
- :
- entity
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- ,
- // 'box', 'sphere', 'cylinder', 'cone', 'capsule', 'asset'
- material
- :
- material
- ,
- castShadows
- :
- true
- ,
- receiveShadows
- :
- true
- }
- )
- ;
- Camera Component
- :
- entity
- .
- addComponent
- (
- 'camera'
- ,
- {
- clearColor
- :
- new
- pc
- .
- Color
- (
- 0.1
- ,
- 0.2
- ,
- 0.3
- )
- ,
- fov
- :
- 45
- ,
- nearClip
- :
- 0.1
- ,
- farClip
- :
- 1000
- ,
- projection
- :
- pc
- .
- PROJECTION_PERSPECTIVE
- // or PROJECTION_ORTHOGRAPHIC
- }
- )
- ;
- Light Component
- :
- entity
- .
- addComponent
- (
- 'light'
- ,
- {
- type
- :
- pc
- .
- LIGHTTYPE_DIRECTIONAL
- ,
- // DIRECTIONAL, POINT, SPOT
- color
- :
- new
- pc
- .
- Color
- (
- 1
- ,
- 1
- ,
- 1
- )
- ,
- intensity
- :
- 1
- ,
- castShadows
- :
- true
- ,
- shadowDistance
- :
- 50
- }
- )
- ;
- Rigidbody Component
- (requires physics):
- entity
- .
- addComponent
- (
- 'rigidbody'
- ,
- {
- type
- :
- pc
- .
- BODYTYPE_DYNAMIC
- ,
- // STATIC, DYNAMIC, KINEMATIC
- mass
- :
- 1
- ,
- friction
- :
- 0.5
- ,
- restitution
- :
- 0.3
- }
- )
- ;
- entity
- .
- addComponent
- (
- 'collision'
- ,
- {
- type
- :
- 'box'
- ,
- halfExtents
- :
- new
- pc
- .
- Vec3
- (
- 0.5
- ,
- 0.5
- ,
- 0.5
- )
- }
- )
- ;
- Common Patterns
- Pattern 1: Basic Scene Setup
- Create a complete scene with camera, light, and models.
- import
- *
- as
- pc
- from
- 'playcanvas'
- ;
- // Initialize application
- const
- canvas
- =
- document
- .
- createElement
- (
- 'canvas'
- )
- ;
- document
- .
- body
- .
- appendChild
- (
- canvas
- )
- ;
- const
- app
- =
- new
- pc
- .
- Application
- (
- canvas
- )
- ;
- app
- .
- setCanvasFillMode
- (
- pc
- .
- FILLMODE_FILL_WINDOW
- )
- ;
- app
- .
- setCanvasResolution
- (
- pc
- .
- RESOLUTION_AUTO
- )
- ;
- window
- .
- addEventListener
- (
- 'resize'
- ,
- (
- )
- =>
- app
- .
- resizeCanvas
- (
- )
- )
- ;
- // Create camera
- const
- camera
- =
- new
- pc
- .
- Entity
- (
- 'camera'
- )
- ;
- camera
- .
- addComponent
- (
- 'camera'
- ,
- {
- clearColor
- :
- new
- pc
- .
- Color
- (
- 0.2
- ,
- 0.3
- ,
- 0.4
- )
- }
- )
- ;
- camera
- .
- setPosition
- (
- 0
- ,
- 2
- ,
- 5
- )
- ;
- camera
- .
- lookAt
- (
- 0
- ,
- 0
- ,
- 0
- )
- ;
- app
- .
- root
- .
- addChild
- (
- camera
- )
- ;
- // Create directional light
- const
- light
- =
- new
- pc
- .
- Entity
- (
- 'light'
- )
- ;
- light
- .
- addComponent
- (
- 'light'
- ,
- {
- type
- :
- pc
- .
- LIGHTTYPE_DIRECTIONAL
- ,
- castShadows
- :
- true
- }
- )
- ;
- light
- .
- setEulerAngles
- (
- 45
- ,
- 30
- ,
- 0
- )
- ;
- app
- .
- root
- .
- addChild
- (
- light
- )
- ;
- // Create ground
- const
- ground
- =
- new
- pc
- .
- Entity
- (
- 'ground'
- )
- ;
- ground
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'plane'
- }
- )
- ;
- ground
- .
- setLocalScale
- (
- 10
- ,
- 1
- ,
- 10
- )
- ;
- app
- .
- root
- .
- addChild
- (
- ground
- )
- ;
- // Create cube
- const
- cube
- =
- new
- pc
- .
- Entity
- (
- 'cube'
- )
- ;
- cube
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- ,
- castShadows
- :
- true
- }
- )
- ;
- cube
- .
- setPosition
- (
- 0
- ,
- 1
- ,
- 0
- )
- ;
- app
- .
- root
- .
- addChild
- (
- cube
- )
- ;
- // Animate cube
- app
- .
- on
- (
- 'update'
- ,
- (
- dt
- )
- =>
- {
- cube
- .
- rotate
- (
- 10
- *
- dt
- ,
- 20
- *
- dt
- ,
- 30
- *
- dt
- )
- ;
- }
- )
- ;
- app
- .
- start
- (
- )
- ;
- Pattern 2: Loading GLTF Models
- Load external 3D models with asset management.
- // Create asset for model
- const
- modelAsset
- =
- new
- pc
- .
- Asset
- (
- 'model'
- ,
- 'container'
- ,
- {
- url
- :
- '/models/character.glb'
- }
- )
- ;
- // Add to asset registry
- app
- .
- assets
- .
- add
- (
- modelAsset
- )
- ;
- // Load asset
- modelAsset
- .
- ready
- (
- (
- asset
- )
- =>
- {
- // Create entity from loaded model
- const
- entity
- =
- asset
- .
- resource
- .
- instantiateRenderEntity
- (
- )
- ;
- app
- .
- root
- .
- addChild
- (
- entity
- )
- ;
- // Scale and position
- entity
- .
- setLocalScale
- (
- 2
- ,
- 2
- ,
- 2
- )
- ;
- entity
- .
- setPosition
- (
- 0
- ,
- 0
- ,
- 0
- )
- ;
- }
- )
- ;
- app
- .
- assets
- .
- load
- (
- modelAsset
- )
- ;
- With error handling
- :
- modelAsset
- .
- ready
- (
- (
- asset
- )
- =>
- {
- console
- .
- log
- (
- 'Model loaded:'
- ,
- asset
- .
- name
- )
- ;
- const
- entity
- =
- asset
- .
- resource
- .
- instantiateRenderEntity
- (
- )
- ;
- app
- .
- root
- .
- addChild
- (
- entity
- )
- ;
- }
- )
- ;
- modelAsset
- .
- on
- (
- 'error'
- ,
- (
- err
- )
- =>
- {
- console
- .
- error
- (
- 'Failed to load model:'
- ,
- err
- )
- ;
- }
- )
- ;
- app
- .
- assets
- .
- load
- (
- modelAsset
- )
- ;
- Pattern 3: Materials and Textures
- Create custom materials with PBR workflow.
- // Create material
- const
- material
- =
- new
- pc
- .
- StandardMaterial
- (
- )
- ;
- material
- .
- diffuse
- =
- new
- pc
- .
- Color
- (
- 1
- ,
- 0
- ,
- 0
- )
- ;
- // Red
- material
- .
- metalness
- =
- 0.5
- ;
- material
- .
- gloss
- =
- 0.8
- ;
- material
- .
- update
- (
- )
- ;
- // Apply to entity
- entity
- .
- model
- .
- material
- =
- material
- ;
- // With textures
- const
- textureAsset
- =
- new
- pc
- .
- Asset
- (
- 'diffuse'
- ,
- 'texture'
- ,
- {
- url
- :
- '/textures/brick_diffuse.jpg'
- }
- )
- ;
- app
- .
- assets
- .
- add
- (
- textureAsset
- )
- ;
- app
- .
- assets
- .
- load
- (
- textureAsset
- )
- ;
- textureAsset
- .
- ready
- (
- (
- asset
- )
- =>
- {
- material
- .
- diffuseMap
- =
- asset
- .
- resource
- ;
- material
- .
- update
- (
- )
- ;
- }
- )
- ;
- // PBR material with all maps
- const
- pbrMaterial
- =
- new
- pc
- .
- StandardMaterial
- (
- )
- ;
- // Load all textures
- const
- textures
- =
- {
- diffuse
- :
- '/textures/albedo.jpg'
- ,
- normal
- :
- '/textures/normal.jpg'
- ,
- metalness
- :
- '/textures/metalness.jpg'
- ,
- gloss
- :
- '/textures/roughness.jpg'
- ,
- ao
- :
- '/textures/ao.jpg'
- }
- ;
- Object
- .
- keys
- (
- textures
- )
- .
- forEach
- (
- key
- =>
- {
- const
- asset
- =
- new
- pc
- .
- Asset
- (
- key
- ,
- 'texture'
- ,
- {
- url
- :
- textures
- [
- key
- ]
- }
- )
- ;
- app
- .
- assets
- .
- add
- (
- asset
- )
- ;
- asset
- .
- ready
- (
- (
- loadedAsset
- )
- =>
- {
- switch
- (
- key
- )
- {
- case
- 'diffuse'
- :
- pbrMaterial
- .
- diffuseMap
- =
- loadedAsset
- .
- resource
- ;
- break
- ;
- case
- 'normal'
- :
- pbrMaterial
- .
- normalMap
- =
- loadedAsset
- .
- resource
- ;
- break
- ;
- case
- 'metalness'
- :
- pbrMaterial
- .
- metalnessMap
- =
- loadedAsset
- .
- resource
- ;
- break
- ;
- case
- 'gloss'
- :
- pbrMaterial
- .
- glossMap
- =
- loadedAsset
- .
- resource
- ;
- break
- ;
- case
- 'ao'
- :
- pbrMaterial
- .
- aoMap
- =
- loadedAsset
- .
- resource
- ;
- break
- ;
- }
- pbrMaterial
- .
- update
- (
- )
- ;
- }
- )
- ;
- app
- .
- assets
- .
- load
- (
- asset
- )
- ;
- }
- )
- ;
- Pattern 4: Physics Integration
- Use Ammo.js for physics simulation.
- import
- *
- as
- pc
- from
- 'playcanvas'
- ;
- // Initialize with Ammo.js
- const
- app
- =
- new
- pc
- .
- Application
- (
- canvas
- ,
- {
- keyboard
- :
- new
- pc
- .
- Keyboard
- (
- window
- )
- ,
- mouse
- :
- new
- pc
- .
- Mouse
- (
- canvas
- )
- }
- )
- ;
- // Load Ammo.js
- const
- ammoScript
- =
- document
- .
- createElement
- (
- 'script'
- )
- ;
- ammoScript
- .
- src
- =
- 'https://cdn.jsdelivr.net/npm/ammo.js@0.0.10/ammo.js'
- ;
- document
- .
- body
- .
- appendChild
- (
- ammoScript
- )
- ;
- ammoScript
- .
- onload
- =
- (
- )
- =>
- {
- Ammo
- (
- )
- .
- then
- (
- (
- AmmoLib
- )
- =>
- {
- window
- .
- Ammo
- =
- AmmoLib
- ;
- // Create static ground
- const
- ground
- =
- new
- pc
- .
- Entity
- (
- 'ground'
- )
- ;
- ground
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'plane'
- }
- )
- ;
- ground
- .
- setLocalScale
- (
- 10
- ,
- 1
- ,
- 10
- )
- ;
- ground
- .
- addComponent
- (
- 'rigidbody'
- ,
- {
- type
- :
- pc
- .
- BODYTYPE_STATIC
- }
- )
- ;
- ground
- .
- addComponent
- (
- 'collision'
- ,
- {
- type
- :
- 'box'
- ,
- halfExtents
- :
- new
- pc
- .
- Vec3
- (
- 5
- ,
- 0.1
- ,
- 5
- )
- }
- )
- ;
- app
- .
- root
- .
- addChild
- (
- ground
- )
- ;
- // Create dynamic cube
- const
- cube
- =
- new
- pc
- .
- Entity
- (
- 'cube'
- )
- ;
- cube
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- }
- )
- ;
- cube
- .
- setPosition
- (
- 0
- ,
- 5
- ,
- 0
- )
- ;
- cube
- .
- addComponent
- (
- 'rigidbody'
- ,
- {
- type
- :
- pc
- .
- BODYTYPE_DYNAMIC
- ,
- mass
- :
- 1
- ,
- friction
- :
- 0.5
- ,
- restitution
- :
- 0.5
- }
- )
- ;
- cube
- .
- addComponent
- (
- 'collision'
- ,
- {
- type
- :
- 'box'
- ,
- halfExtents
- :
- new
- pc
- .
- Vec3
- (
- 0.5
- ,
- 0.5
- ,
- 0.5
- )
- }
- )
- ;
- app
- .
- root
- .
- addChild
- (
- cube
- )
- ;
- // Apply force
- cube
- .
- rigidbody
- .
- applyForce
- (
- 10
- ,
- 0
- ,
- 0
- )
- ;
- cube
- .
- rigidbody
- .
- applyTorque
- (
- 0
- ,
- 10
- ,
- 0
- )
- ;
- app
- .
- start
- (
- )
- ;
- }
- )
- ;
- }
- ;
- Pattern 5: Custom Scripts
- Create reusable script components.
- // Define script class
- const
- RotateScript
- =
- pc
- .
- createScript
- (
- 'rotate'
- )
- ;
- // Script attributes (editor-exposed)
- RotateScript
- .
- attributes
- .
- add
- (
- 'speed'
- ,
- {
- type
- :
- 'number'
- ,
- default
- :
- 10
- ,
- title
- :
- 'Rotation Speed'
- }
- )
- ;
- RotateScript
- .
- attributes
- .
- add
- (
- 'axis'
- ,
- {
- type
- :
- 'vec3'
- ,
- default
- :
- [
- 0
- ,
- 1
- ,
- 0
- ]
- ,
- title
- :
- 'Rotation Axis'
- }
- )
- ;
- // Initialize method
- RotateScript
- .
- prototype
- .
- initialize
- =
- function
- (
- )
- {
- console
- .
- log
- (
- 'RotateScript initialized'
- )
- ;
- }
- ;
- // Update method (called every frame)
- RotateScript
- .
- prototype
- .
- update
- =
- function
- (
- dt
- )
- {
- this
- .
- entity
- .
- rotate
- (
- this
- .
- axis
- .
- x
- *
- this
- .
- speed
- *
- dt
- ,
- this
- .
- axis
- .
- y
- *
- this
- .
- speed
- *
- dt
- ,
- this
- .
- axis
- .
- z
- *
- this
- .
- speed
- *
- dt
- )
- ;
- }
- ;
- // Cleanup
- RotateScript
- .
- prototype
- .
- destroy
- =
- function
- (
- )
- {
- console
- .
- log
- (
- 'RotateScript destroyed'
- )
- ;
- }
- ;
- // Usage
- const
- entity
- =
- new
- pc
- .
- Entity
- (
- 'rotatingCube'
- )
- ;
- entity
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- }
- )
- ;
- entity
- .
- addComponent
- (
- 'script'
- )
- ;
- entity
- .
- script
- .
- create
- (
- 'rotate'
- ,
- {
- attributes
- :
- {
- speed
- :
- 20
- ,
- axis
- :
- new
- pc
- .
- Vec3
- (
- 0
- ,
- 1
- ,
- 0
- )
- }
- }
- )
- ;
- app
- .
- root
- .
- addChild
- (
- entity
- )
- ;
- Script lifecycle methods
- :
- const
- MyScript
- =
- pc
- .
- createScript
- (
- 'myScript'
- )
- ;
- MyScript
- .
- prototype
- .
- initialize
- =
- function
- (
- )
- {
- // Called once after all resources are loaded
- }
- ;
- MyScript
- .
- prototype
- .
- postInitialize
- =
- function
- (
- )
- {
- // Called after all entities have initialized
- }
- ;
- MyScript
- .
- prototype
- .
- update
- =
- function
- (
- dt
- )
- {
- // Called every frame before rendering
- }
- ;
- MyScript
- .
- prototype
- .
- postUpdate
- =
- function
- (
- dt
- )
- {
- // Called every frame after update
- }
- ;
- MyScript
- .
- prototype
- .
- swap
- =
- function
- (
- old
- )
- {
- // Hot reload support
- }
- ;
- MyScript
- .
- prototype
- .
- destroy
- =
- function
- (
- )
- {
- // Cleanup when entity is destroyed
- }
- ;
- Pattern 6: Input Handling
- Handle keyboard, mouse, and touch input.
- // Keyboard
- if
- (
- app
- .
- keyboard
- .
- isPressed
- (
- pc
- .
- KEY_W
- )
- )
- {
- entity
- .
- translate
- (
- 0
- ,
- 0
- ,
- -
- speed
- *
- dt
- )
- ;
- }
- if
- (
- app
- .
- keyboard
- .
- wasPressed
- (
- pc
- .
- KEY_SPACE
- )
- )
- {
- entity
- .
- rigidbody
- .
- applyImpulse
- (
- 0
- ,
- 10
- ,
- 0
- )
- ;
- }
- // Mouse
- app
- .
- mouse
- .
- on
- (
- pc
- .
- EVENT_MOUSEDOWN
- ,
- (
- event
- )
- =>
- {
- if
- (
- event
- .
- button
- ===
- pc
- .
- MOUSEBUTTON_LEFT
- )
- {
- console
- .
- log
- (
- 'Left click at'
- ,
- event
- .
- x
- ,
- event
- .
- y
- )
- ;
- }
- }
- )
- ;
- app
- .
- mouse
- .
- on
- (
- pc
- .
- EVENT_MOUSEMOVE
- ,
- (
- event
- )
- =>
- {
- const
- dx
- =
- event
- .
- dx
- ;
- const
- dy
- =
- event
- .
- dy
- ;
- camera
- .
- rotate
- (
- -
- dy
- *
- 0.2
- ,
- -
- dx
- *
- 0.2
- ,
- 0
- )
- ;
- }
- )
- ;
- // Touch
- app
- .
- touch
- .
- on
- (
- pc
- .
- EVENT_TOUCHSTART
- ,
- (
- event
- )
- =>
- {
- event
- .
- touches
- .
- forEach
- (
- (
- touch
- )
- =>
- {
- console
- .
- log
- (
- 'Touch at'
- ,
- touch
- .
- x
- ,
- touch
- .
- y
- )
- ;
- }
- )
- ;
- }
- )
- ;
- // Raycasting (mouse picking)
- app
- .
- mouse
- .
- on
- (
- pc
- .
- EVENT_MOUSEDOWN
- ,
- (
- event
- )
- =>
- {
- const
- camera
- =
- app
- .
- root
- .
- findByName
- (
- 'camera'
- )
- ;
- const
- cameraComponent
- =
- camera
- .
- camera
- ;
- const
- from
- =
- cameraComponent
- .
- screenToWorld
- (
- event
- .
- x
- ,
- event
- .
- y
- ,
- cameraComponent
- .
- nearClip
- )
- ;
- const
- to
- =
- cameraComponent
- .
- screenToWorld
- (
- event
- .
- x
- ,
- event
- .
- y
- ,
- cameraComponent
- .
- farClip
- )
- ;
- const
- result
- =
- app
- .
- systems
- .
- rigidbody
- .
- raycastFirst
- (
- from
- ,
- to
- )
- ;
- if
- (
- result
- )
- {
- console
- .
- log
- (
- 'Hit:'
- ,
- result
- .
- entity
- .
- name
- )
- ;
- result
- .
- entity
- .
- model
- .
- material
- .
- emissive
- =
- new
- pc
- .
- Color
- (
- 1
- ,
- 0
- ,
- 0
- )
- ;
- }
- }
- )
- ;
- Pattern 7: Animations
- Play skeletal animations and tweens.
- Skeletal animation
- :
- // Load animated model
- const
- modelAsset
- =
- new
- pc
- .
- Asset
- (
- 'character'
- ,
- 'container'
- ,
- {
- url
- :
- '/models/character.glb'
- }
- )
- ;
- app
- .
- assets
- .
- add
- (
- modelAsset
- )
- ;
- modelAsset
- .
- ready
- (
- (
- asset
- )
- =>
- {
- const
- entity
- =
- asset
- .
- resource
- .
- instantiateRenderEntity
- (
- )
- ;
- app
- .
- root
- .
- addChild
- (
- entity
- )
- ;
- // Get animation component
- entity
- .
- addComponent
- (
- 'animation'
- ,
- {
- assets
- :
- [
- asset
- ]
- ,
- speed
- :
- 1.0
- ,
- loop
- :
- true
- ,
- activate
- :
- true
- }
- )
- ;
- // Play specific animation
- entity
- .
- animation
- .
- play
- (
- 'Walk'
- ,
- 0.2
- )
- ;
- // 0.2s blend time
- // Later, transition to run
- entity
- .
- animation
- .
- play
- (
- 'Run'
- ,
- 0.5
- )
- ;
- }
- )
- ;
- app
- .
- assets
- .
- load
- (
- modelAsset
- )
- ;
- Property tweening
- :
- // Animate position
- entity
- .
- tween
- (
- entity
- .
- getLocalPosition
- (
- )
- )
- .
- to
- (
- {
- x
- :
- 5
- ,
- y
- :
- 2
- ,
- z
- :
- 0
- }
- ,
- 2.0
- ,
- pc
- .
- SineInOut
- )
- .
- start
- (
- )
- ;
- // Animate rotation
- entity
- .
- tween
- (
- entity
- .
- getLocalEulerAngles
- (
- )
- )
- .
- to
- (
- {
- x
- :
- 0
- ,
- y
- :
- 180
- ,
- z
- :
- 0
- }
- ,
- 1.0
- ,
- pc
- .
- Linear
- )
- .
- loop
- (
- true
- )
- .
- yoyo
- (
- true
- )
- .
- start
- (
- )
- ;
- // Animate material color
- const
- color
- =
- material
- .
- emissive
- ;
- app
- .
- tween
- (
- color
- )
- .
- to
- (
- new
- pc
- .
- Color
- (
- 1
- ,
- 0
- ,
- 0
- )
- ,
- 1.0
- ,
- pc
- .
- SineInOut
- )
- .
- yoyo
- (
- true
- )
- .
- loop
- (
- true
- )
- .
- start
- (
- )
- ;
- // Chain tweens
- entity
- .
- tween
- (
- entity
- .
- getLocalPosition
- (
- )
- )
- .
- to
- (
- {
- y
- :
- 2
- }
- ,
- 1.0
- )
- .
- to
- (
- {
- y
- :
- 0
- }
- ,
- 1.0
- )
- .
- delay
- (
- 0.5
- )
- .
- repeat
- (
- 3
- )
- .
- start
- (
- )
- ;
- Integration Patterns
- Integration 1: React Integration
- Wrap PlayCanvas in React components.
- import
- React
- ,
- {
- useEffect
- ,
- useRef
- }
- from
- 'react'
- ;
- import
- *
- as
- pc
- from
- 'playcanvas'
- ;
- function
- PlayCanvasScene
- (
- )
- {
- const
- canvasRef
- =
- useRef
- (
- null
- )
- ;
- const
- appRef
- =
- useRef
- (
- null
- )
- ;
- useEffect
- (
- (
- )
- =>
- {
- // Initialize
- const
- app
- =
- new
- pc
- .
- Application
- (
- canvasRef
- .
- current
- )
- ;
- appRef
- .
- current
- =
- app
- ;
- app
- .
- setCanvasFillMode
- (
- pc
- .
- FILLMODE_FILL_WINDOW
- )
- ;
- app
- .
- setCanvasResolution
- (
- pc
- .
- RESOLUTION_AUTO
- )
- ;
- // Create scene
- const
- camera
- =
- new
- pc
- .
- Entity
- (
- 'camera'
- )
- ;
- camera
- .
- addComponent
- (
- 'camera'
- ,
- {
- clearColor
- :
- new
- pc
- .
- Color
- (
- 0.1
- ,
- 0.2
- ,
- 0.3
- )
- }
- )
- ;
- camera
- .
- setPosition
- (
- 0
- ,
- 0
- ,
- 5
- )
- ;
- app
- .
- root
- .
- addChild
- (
- camera
- )
- ;
- const
- cube
- =
- new
- pc
- .
- Entity
- (
- 'cube'
- )
- ;
- cube
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- }
- )
- ;
- app
- .
- root
- .
- addChild
- (
- cube
- )
- ;
- const
- light
- =
- new
- pc
- .
- Entity
- (
- 'light'
- )
- ;
- light
- .
- addComponent
- (
- 'light'
- )
- ;
- light
- .
- setEulerAngles
- (
- 45
- ,
- 0
- ,
- 0
- )
- ;
- app
- .
- root
- .
- addChild
- (
- light
- )
- ;
- app
- .
- on
- (
- 'update'
- ,
- (
- dt
- )
- =>
- {
- cube
- .
- rotate
- (
- 10
- *
- dt
- ,
- 20
- *
- dt
- ,
- 30
- *
- dt
- )
- ;
- }
- )
- ;
- app
- .
- start
- (
- )
- ;
- // Cleanup
- return
- (
- )
- =>
- {
- app
- .
- destroy
- (
- )
- ;
- }
- ;
- }
- ,
- [
- ]
- )
- ;
- return
- (
- <
- canvas
- ref
- =
- {
- canvasRef
- }
- style
- =
- {
- {
- width
- :
- '100%'
- ,
- height
- :
- '100vh'
- }
- }
- />
- )
- ;
- }
- export
- default
- PlayCanvasScene
- ;
- Integration 2: Editor Export
- Work with PlayCanvas Editor projects.
- // Export from PlayCanvas Editor
- // Download build files, then load in code:
- import
- *
- as
- pc
- from
- 'playcanvas'
- ;
- const
- app
- =
- new
- pc
- .
- Application
- (
- canvas
- )
- ;
- // Load exported project config
- fetch
- (
- '/config.json'
- )
- .
- then
- (
- response
- =>
- response
- .
- json
- (
- )
- )
- .
- then
- (
- config
- =>
- {
- // Load scene
- app
- .
- scenes
- .
- loadSceneHierarchy
- (
- config
- .
- scene_url
- ,
- (
- err
- ,
- parent
- )
- =>
- {
- if
- (
- err
- )
- {
- console
- .
- error
- (
- 'Failed to load scene:'
- ,
- err
- )
- ;
- return
- ;
- }
- // Start application
- app
- .
- start
- (
- )
- ;
- // Find entities by name
- const
- player
- =
- app
- .
- root
- .
- findByName
- (
- 'Player'
- )
- ;
- const
- enemy
- =
- app
- .
- root
- .
- findByName
- (
- 'Enemy'
- )
- ;
- // Access scripts
- player
- .
- script
- .
- myScript
- .
- doSomething
- (
- )
- ;
- }
- )
- ;
- }
- )
- ;
- Performance Optimization
- 1. Object Pooling
- Reuse entities instead of creating/destroying.
- class
- EntityPool
- {
- constructor
- (
- app
- ,
- count
- )
- {
- this
- .
- app
- =
- app
- ;
- this
- .
- pool
- =
- [
- ]
- ;
- this
- .
- active
- =
- [
- ]
- ;
- for
- (
- let
- i
- =
- 0
- ;
- i
- <
- count
- ;
- i
- ++
- )
- {
- const
- entity
- =
- new
- pc
- .
- Entity
- (
- 'pooled'
- )
- ;
- entity
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- }
- )
- ;
- entity
- .
- enabled
- =
- false
- ;
- app
- .
- root
- .
- addChild
- (
- entity
- )
- ;
- this
- .
- pool
- .
- push
- (
- entity
- )
- ;
- }
- }
- spawn
- (
- position
- )
- {
- let
- entity
- =
- this
- .
- pool
- .
- pop
- (
- )
- ;
- if
- (
- !
- entity
- )
- {
- // Pool exhausted, create new
- entity
- =
- new
- pc
- .
- Entity
- (
- 'pooled'
- )
- ;
- entity
- .
- addComponent
- (
- 'model'
- ,
- {
- type
- :
- 'box'
- }
- )
- ;
- this
- .
- app
- .
- root
- .
- addChild
- (
- entity
- )
- ;
- }
- entity
- .
- enabled
- =
- true
- ;
- entity
- .
- setPosition
- (
- position
- )
- ;
- this
- .
- active
- .
- push
- (
- entity
- )
- ;
- return
- entity
- ;
- }
- despawn
- (
- entity
- )
- {
- entity
- .
- enabled
- =
- false
- ;
- const
- index
- =
- this
- .
- active
- .
- indexOf
- (
- entity
- )
- ;
- if
- (
- index
- >
- -
- 1
- )
- {
- this
- .
- active
- .
- splice
- (
- index
- ,
- 1
- )
- ;
- this
- .
- pool
- .
- push
- (
- entity
- )
- ;
- }
- }
- }
- // Usage
- const
- pool
- =
- new
- EntityPool
- (
- app
- ,
- 100
- )
- ;
- const
- bullet
- =
- pool
- .
- spawn
- (
- new
- pc
- .
- Vec3
- (
- 0
- ,
- 0
- ,
- 0
- )
- )
- ;
- // Later
- pool
- .
- despawn
- (
- bullet
- )
- ;
- 2. LOD (Level of Detail)
- Reduce geometry for distant objects.
- // Manual LOD switching
- app
- .
- on
- (
- 'update'
- ,
- (
- )
- =>
- {
- const
- distance
- =
- camera
- .
- getPosition
- (
- )
- .
- distance
- (
- entity
- .
- getPosition
- (
- )
- )
- ;
- if
- (
- distance
- <
- 10
- )
- {
- entity
- .
- model
- .
- asset
- =
- highResModel
- ;
- }
- else
- if
- (
- distance
- <
- 50
- )
- {
- entity
- .
- model
- .
- asset
- =
- mediumResModel
- ;
- }
- else
- {
- entity
- .
- model
- .
- asset
- =
- lowResModel
- ;
- }
- }
- )
- ;
- // Or disable distant entities
- app
- .
- on
- (
- 'update'
- ,
- (
- )
- =>
- {
- entities
- .
- forEach
- (
- entity
- =>
- {
- const
- distance
- =
- camera
- .
- getPosition
- (
- )
- .
- distance
- (
- entity
- .
- getPosition
- (
- )
- )
- ;
- entity
- .
- enabled
- =
- distance
- <
- 100
- ;
- }
- )
- ;
- }
- )
- ;
- 3. Batching
- Combine static meshes to reduce draw calls.
- // Enable static batching for entity
- entity
- .
- model
- .
- batchGroupId
- =
- 1
- ;
- // Batch all entities with same group ID
- app
- .
- batcher
- .
- generate
- (
- [
- entity1
- ,
- entity2
- ,
- entity3
- ]
- )
- ;
- 4. Texture Compression
- Use compressed texture formats.
- // When creating textures, use compressed formats
- const
- texture
- =
- new
- pc
- .
- Texture
- (
- app
- .
- graphicsDevice
- ,
- {
- width
- :
- 512
- ,
- height
- :
- 512
- ,
- format
- :
- pc
- .
- PIXELFORMAT_DXT5
- ,
- // GPU-compressed
- minFilter
- :
- pc
- .
- FILTER_LINEAR_MIPMAP_LINEAR
- ,
- magFilter
- :
- pc
- .
- FILTER_LINEAR
- ,
- mipmaps
- :
- true
- }
- )
- ;
- Common Pitfalls
- Pitfall 1: Not Starting the Application
- Problem
-
- Scene renders but nothing happens.
- // ❌ Wrong - forgot to start
- const
- app
- =
- new
- pc
- .
- Application
- (
- canvas
- )
- ;
- // ... create entities ...
- // Nothing happens!
- // ✅ Correct
- const
- app
- =
- new
- pc
- .
- Application
- (
- canvas
- )
- ;
- // ... create entities ...
- app
- .
- start
- (
- )
- ;
- // Critical!
- Pitfall 2: Modifying Entities During Update
- Problem
-
- Modifying scene graph during iteration.
- // ❌ Wrong - modifying array during iteration
- app
- .
- on
- (
- 'update'
- ,
- (
- )
- =>
- {
- entities
- .
- forEach
- (
- entity
- =>
- {
- if
- (
- entity
- .
- shouldDestroy
- )
- {
- entity
- .
- destroy
- (
- )
- ;
- // Modifies array!
- }
- }
- )
- ;
- }
- )
- ;
- // ✅ Correct - mark for deletion, clean up after
- const
- toDestroy
- =
- [
- ]
- ;
- app
- .
- on
- (
- 'update'
- ,
- (
- )
- =>
- {
- entities
- .
- forEach
- (
- entity
- =>
- {
- if
- (
- entity
- .
- shouldDestroy
- )
- {
- toDestroy
- .
- push
- (
- entity
- )
- ;
- }
- }
- )
- ;
- }
- )
- ;
- app
- .
- on
- (
- 'postUpdate'
- ,
- (
- )
- =>
- {
- toDestroy
- .
- forEach
- (
- entity
- =>
- entity
- .
- destroy
- (
- )
- )
- ;
- toDestroy
- .
- length
- =
- 0
- ;
- }
- )
- ;
- Pitfall 3: Memory Leaks with Assets
- Problem
-
- Not cleaning up loaded assets.
- // ❌ Wrong - assets never cleaned up
- function
- loadModel
- (
- )
- {
- const
- asset
- =
- new
- pc
- .
- Asset
- (
- 'model'
- ,
- 'container'
- ,
- {
- url
- :
- '/model.glb'
- }
- )
- ;
- app
- .
- assets
- .
- add
- (
- asset
- )
- ;
- app
- .
- assets
- .
- load
- (
- asset
- )
- ;
- // Asset stays in memory forever
- }
- // ✅ Correct - clean up when done
- function
- loadModel
- (
- )
- {
- const
- asset
- =
- new
- pc
- .
- Asset
- (
- 'model'
- ,
- 'container'
- ,
- {
- url
- :
- '/model.glb'
- }
- )
- ;
- app
- .
- assets
- .
- add
- (
- asset
- )
- ;
- asset
- .
- ready
- (
- (
- )
- =>
- {
- // Use model
- }
- )
- ;
- app
- .
- assets
- .
- load
- (
- asset
- )
- ;
- // Clean up later
- return
- (
- )
- =>
- {
- app
- .
- assets
- .
- remove
- (
- asset
- )
- ;
- asset
- .
- unload
- (
- )
- ;
- }
- ;
- }
- const
- cleanup
- =
- loadModel
- (
- )
- ;
- // Later: cleanup();
- Pitfall 4: Incorrect Transform Hierarchy
- Problem
-
- Transforms not propagating correctly.
- // ❌ Wrong - setting world transform on child
- const
- parent
- =
- new
- pc
- .
- Entity
- (
- )
- ;
- const
- child
- =
- new
- pc
- .
- Entity
- (
- )
- ;
- parent
- .
- addChild
- (
- child
- )
- ;
- child
- .
- setPosition
- (
- 5
- ,
- 0
- ,
- 0
- )
- ;
- // Local position
- parent
- .
- setPosition
- (
- 10
- ,
- 0
- ,
- 0
- )
- ;
- // Child is at (15, 0, 0) in world space
- // ✅ Correct - understand local vs world
- child
- .
- setLocalPosition
- (
- 5
- ,
- 0
- ,
- 0
- )
- ;
- // Explicit local
- // or
- const
- worldPos
- =
- new
- pc
- .
- Vec3
- (
- 15
- ,
- 0
- ,
- 0
- )
- ;
- child
- .
- setPosition
- (
- worldPos
- )
- ;
- // Explicit world
- Pitfall 5: Physics Not Initialized
- Problem
-
- Physics components don't work.
- // ❌ Wrong - Ammo.js not loaded
- const
- entity
- =
- new
- pc
- .
- Entity
- (
- )
- ;
- entity
- .
- addComponent
- (
- 'rigidbody'
- ,
- {
- type
- :
- pc
- .
- BODYTYPE_DYNAMIC
- }
- )
- ;
- // Error: Ammo is not defined
- // ✅ Correct - ensure Ammo.js is loaded
- const
- script
- =
- document
- .
- createElement
- (
- 'script'
- )
- ;
- script
- .
- src
- =
- 'https://cdn.jsdelivr.net/npm/ammo.js@0.0.10/ammo.js'
- ;
- document
- .
- body
- .
- appendChild
- (
- script
- )
- ;
- script
- .
- onload
- =
- (
- )
- =>
- {
- Ammo
- (
- )
- .
- then
- (
- (
- AmmoLib
- )
- =>
- {
- window
- .
- Ammo
- =
- AmmoLib
- ;
- // Now physics works
- const
- entity
- =
- new
- pc
- .
- Entity
- (
- )
- ;
- entity
- .
- addComponent
- (
- 'rigidbody'
- ,
- {
- type
- :
- pc
- .
- BODYTYPE_DYNAMIC
- }
- )
- ;
- entity
- .
- addComponent
- (
- 'collision'
- ,
- {
- type
- :
- 'box'
- }
- )
- ;
- }
- )
- ;
- }
- ;
- Pitfall 6: Canvas Sizing Issues
- Problem
- Canvas doesn't fill container or respond to resize.
// ❌ Wrong - fixed size canvas
const
canvas
=
document
.
createElement
(
'canvas'
)
;
canvas
.
width
=
800
;
canvas
.
height
=
600
;
// ✅ Correct - responsive canvas
const
canvas
=
document
.
createElement
(
'canvas'
)
;
const
app
=
new
pc
.
Application
(
canvas
)
;
app
.
setCanvasFillMode
(
pc
.
FILLMODE_FILL_WINDOW
)
;
app
.
setCanvasResolution
(
pc
.
RESOLUTION_AUTO
)
;
window
.
addEventListener
(
'resize'
,
(
)
=>
app
.
resizeCanvas
(
)
)
;
Resources
Official API
:
https://api.playcanvas.com/
Developer Docs
:
https://developer.playcanvas.com/
Examples
:
https://playcanvas.github.io/
Editor
:
https://playcanvas.com/
GitHub
:
https://github.com/playcanvas/engine
Forum
:
https://forum.playcanvas.com/
Quick Reference
Application Setup
const
app
=
new
pc
.
Application
(
canvas
)
;
app
.
setCanvasFillMode
(
pc
.
FILLMODE_FILL_WINDOW
)
;
app
.
setCanvasResolution
(
pc
.
RESOLUTION_AUTO
)
;
app
.
start
(
)
;
Entity Creation
const
entity
=
new
pc
.
Entity
(
'name'
)
;
entity
.
addComponent
(
'model'
,
{
type
:
'box'
}
)
;
entity
.
setPosition
(
x
,
y
,
z
)
;
app
.
root
.
addChild
(
entity
)
;
Update Loop
app
.
on
(
'update'
,
(
dt
)
=>
{
// Logic here
}
)
;
Loading Assets
const
asset
=
new
pc
.
Asset
(
'name'
,
'type'
,
{
url
:
'/path'
}
)
;
app
.
assets
.
add
(
asset
)
;
asset
.
ready
(
(
)
=>
{
/ use asset /
}
)
;
app
.
assets
.
load
(
asset
)
;
← 返回排行榜