Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 38 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

This library includes a number of helpful pre-built tools that offer simple solutions to common scenarios that you're likely to run into.

- [Init Library](#init-library)
- [Debug helpers](#debug-helpers)
- [Label](#label)
- [Cube](#cube)
Expand Down Expand Up @@ -46,6 +47,7 @@ This library includes a number of helpful pre-built tools that offer simple solu

To use any of the helpers provided by the utils library you must install it in your Decentrland project.


### Via the Decentraland Editor

Make sure you've [installed the Decentraland editor](https://docs.decentraland.org/creator/development-guide/sdk7/installation-guide/#the-decentraland-editor).
Expand Down Expand Up @@ -86,6 +88,25 @@ import * as utils from '@dcl-sdk/utils'

4. In your TypeScript file, write `utils.` and let the suggestions of your IDE show the available helpers.


## Init library

Since version 2.0.0 it is needed to initialize the library from scene `index.ts` file.

Place the call to `initLibrary()` outside the scene `main()` function.

index.ts:
```ts
import { engine } from '@dcl/sdk/ecs'
import * as utils from '@dcl-sdk/utils'

utils.initLibrary(engine)

export function main() {
//scene code goes here
}
```

## Debug helpers

### Label
Expand Down Expand Up @@ -646,14 +667,14 @@ pointerEventsSystem.onPointerDown(
Use `utils.triggers.addTrigger` to add a trigger area to an entity. It has the following arguments:

- `entity`: Trigger's owner entity. Trigger area's coordinates depend on `entity`'s Transform component.
- `layerMask`: Specificies layers to which this trigger belongs to. The library provides eight layers: `utils.LAYER_1`, ... `utils.LAYER_8`. If an entity is supposed to belong to multiple layers, for example layer 1 and layer 3, set `layerMask` to a combination of layer constants separated by `|` (bitwise OR): `utils.LAYER_1 | utils.LAYER_3`. If an entity is supposed to belong to all 8 layers, set `layerMask` to `utils.ALL_LAYERS`. Default value of `layerMask` is `utils.NO_LAYERS`, i.e. an entity does not belong to any layer and won't be able to trigger other entities (it still can be triggered by others, see `triggeredByMask` below).
- `triggeredByMask`: Specifies layers which can trigger an entity. For example, if an entity is supposed to be triggered by entities that belong to either or both layer 2 and layer 4, set `triggeredByMask` to `utils.LAYER_2 | utils.LAYER_4`. Default value of `triggeredByMask` is `utils.NO_LAYERS`, i.e. an entity won't be triggered by other entities at all. When set to `utils.ALL_LAYERS` an entity will be triggered by all entities that belong to at least one layer.
- `layerMask`: Specificies layers to which this trigger belongs to. The library provides eight layers: `utils.triggers.LAYER_1`, ... `utils.triggers.LAYER_8`. If an entity is supposed to belong to multiple layers, for example layer 1 and layer 3, set `layerMask` to a combination of layer constants separated by `|` (bitwise OR): `utils.triggers.LAYER_1 | utils.triggers.LAYER_3`. If an entity is supposed to belong to all 8 layers, set `layerMask` to `utils.triggers.ALL_LAYERS`. Default value of `layerMask` is `utils.triggers.NO_LAYERS`, i.e. an entity does not belong to any layer and won't be able to trigger other entities (it still can be triggered by others, see `triggeredByMask` below).
- `triggeredByMask`: Specifies layers which can trigger an entity. For example, if an entity is supposed to be triggered by entities that belong to either or both layer 2 and layer 4, set `triggeredByMask` to `utils.triggers.LAYER_2 | utils.triggers.LAYER_4`. Default value of `triggeredByMask` is `utils.triggers.NO_LAYERS`, i.e. an entity won't be triggered by other entities at all. When set to `utils.triggers.ALL_LAYERS` an entity will be triggered by all entities that belong to at least one layer.
- `areas`: An array of shapes (either boxes or spheres) which describes trigger area. A box is indicated by the object `{type: 'box', position?: Vector3, scale?: Vector3}`, and a sphere by the object `{type: 'sphere', position?: Vector3, radius?: number}`. `position`, `scale` and `radius` fields are optional and default to `{x: 0, y: 0, z: 0}`, `{x: 1, y: 1, z: 1}` and `1` respectively. Please note that box's or sphere's coordinates are relative to `entity`'s Transform. Additionally, box areas always stay axis-aligned, disregarding `entity`'s rotation.
- `onEnterCallback`: This function will be called when a trigger's area intersects with an area of another, layer-compatible trigger. It will receive an entity which owns intersecting trigger as a single argument.
- `onExitCallback`: This function will be called when a trigger's area no longer intersects with an area of another trigger. It will receive an entity which owns formerly intersecting trigger as a single argument.
- `debugColor`: Defines a color of trigger area's shapes when debug visualization is active: call `utils.triggers.enableDebugDraw(true)` to enable it.

The following example creates a trigger that changes its position randomly when triggered by the player. Please note that the library automatically creates a trigger area for the player entity: it's a box closely matching avatar's shape with `layerMask` set to `utils.LAYER_1` and `triggeredByMask` set to `utils.NO_LAYERS`.
The following example creates a trigger that changes its position randomly when triggered by the player. Please note that the library automatically creates a trigger area for the player entity: it's a box closely matching avatar's shape with `layerMask` set to `utils.triggers.LAYER_1` and `triggeredByMask` set to `utils.triggers.NO_LAYERS`.

```ts
export * from '@dcl/sdk'
Expand All @@ -672,8 +693,8 @@ const box = utils.addTestCube(

utils.triggers.addTrigger(
box,
utils.NO_LAYERS,
utils.LAYER_1,
utils.triggers.NO_LAYERS,
utils.triggers.LAYER_1,
[{ type: 'box' }],
function (otherEntity) {
console.log(`triggered by ${otherEntity}!`)
Expand Down Expand Up @@ -708,8 +729,8 @@ Transform.create(triggerEntity)

utils.triggers.oneTimeTrigger(
triggerEntity,
utils.NO_LAYERS,
utils.LAYER_1,
utils.triggers.NO_LAYERS,
utils.triggers.LAYER_1,
[
{
type: 'box',
Expand Down Expand Up @@ -744,9 +765,9 @@ import * as utils from '@dcl-sdk/utils'
import { Color4 } from '@dcl/sdk/math'

// Define layers
const FOOD_LAYER = utils.LAYER_1
const MOUSE_LAYER = utils.LAYER_2
const CAT_LAYER = utils.LAYER_3
const FOOD_LAYER = utils.triggers.LAYER_1
const MOUSE_LAYER = utils.triggers.LAYER_2
const CAT_LAYER = utils.triggers.LAYER_3

// Remove default trigger from a player so that they don't interfere
utils.triggers.removeTrigger(engine.PlayerEntity)
Expand Down Expand Up @@ -876,6 +897,7 @@ console.log(result)
### World position

If an entity is parented to another entity, or to the player, then its Transform position will be relative to its parent. To find what its global position is, taking into account any parents, use `utils.getWorldPosition`. It returns a `Vector3` object, with the resulting position of adding the given entity and all its chain of parents.
This calculation takes into account position, rotation and scale from parent entities.

The following example sets a cube as a child of another cube, and logs its world position.

Expand All @@ -884,7 +906,11 @@ export * from '@dcl/sdk'
import * as utils from '@dcl-sdk/utils'
import { Transform } from '@dcl/sdk/ecs'

const cube = utils.addTestCube({ position: { x: 1, y: 1, z: 1 } })
const cube = utils.addTestCube({
position: { x: 1, y: 1, z: 1 },
scale: { x: 1, y: 2, z: 1 },
rotation: Quaternion.fromEulerDegrees(45, 0, 0)
})
const childCube = utils.addTestCube({ position: { x: 0, y: 1, z: 0 } })
Transform.getMutable(childCube).parent = cube

Expand Down Expand Up @@ -1076,7 +1102,7 @@ class ScaleAction implements utils.actions.IAction {
transform.scale,
this.scale,
1.5,
utils.InterpolationType.EASEINQUAD,
utils.InterpolationType.LINEAR,
() => {
this.hasFinished = true
}
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "@dcl-sdk/utils",
"version": "1.1.1",
"version": "2.0.0",
"description": "A collection of helpers to make it easier to build a Decentraland scene using the SDK 7.",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json"
"build": "tsc -p tsconfig.json",
"dev": "tsc -p tsconfig.json --watch"
},
"repository": {
"type": "git",
Expand All @@ -25,7 +26,7 @@
},
"homepage": "https://github.com/decentraland/sdk7-utils#readme",
"devDependencies": {
"@dcl/sdk": "https://sdk-team-cdn.decentraland.org/@dcl/js-sdk-toolchain/branch/shape-up/control-audio-scene/dcl-sdk-7.4.8-8267289014.commit-3356b5d.tgz",
"@dcl/sdk": "^7.6.3",
"typescript": "^5.0.2"
},
"files": [
Expand Down
47 changes: 26 additions & 21 deletions src/audio.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { AudioSource, Entity, EntityState, IEngine, PBAudioSource, engine } from "@dcl/sdk/ecs";
import { AudioSource, Entity, EntityState, PBAudioSource } from "@dcl/sdk/ecs";
import { priority } from "./priority";
import { getSDK } from "./sdk";

export type Sounds = ReturnType<typeof createSounds>
export type Sounds = ReturnType<typeof initSounds>
const soundMap = new Map<Entity, { currentTime: number; end: number; }>()
let soundsSystemStarted = false

function assertSound(start: number, end: number) {
if (start < 0) {
Expand All @@ -13,15 +16,19 @@ function assertSound(start: number, end: number) {
}
}

function createSounds(targetEngine: IEngine) {
const soundMap = new Map<Entity, { currentTime: number; end: number; }>()
function initSounds() {

const { engine } = getSDK()

if (soundsSystemStarted) return
soundsSystemStarted = true

function makeSystem(dt: number) {
const deadSounds = [];

for (const [entity, soundData] of soundMap.entries()) {
if (
targetEngine.getEntityState(entity) == EntityState.Removed ||
engine.getEntityState(entity) == EntityState.Removed ||
!AudioSource.has(entity)
) {
soundMap.delete(entity);
Expand All @@ -41,22 +48,20 @@ function createSounds(targetEngine: IEngine) {
}
}

function playSoundSegment(entity: Entity, value: PBAudioSource, start: number, end: number) {
assertSound(start, end)

soundMap.set(entity, { currentTime: start, end })
AudioSource.createOrReplace(entity, {
...value,
playing: true,
currentTime: start
})
}
engine.addSystem(makeSystem, priority.TweenSystemPriority)
}

targetEngine.addSystem(makeSystem, priority.TweenSystemPriority)
export function playSoundSegment(entity: Entity, value: PBAudioSource, start: number, end: number) {

return {
playSoundSegment,
}
}
const { components: { AudioSource } } = getSDK()

initSounds()
assertSound(start, end)

export const sounds = createSounds(engine)
soundMap.set(entity, { currentTime: start, end })
AudioSource.createOrReplace(entity, {
...value,
playing: true,
currentTime: start
})
}
11 changes: 9 additions & 2 deletions src/debug.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
Entity, Transform, TextShape, Billboard, TransformType, MeshRenderer,
MeshCollider, Material, pointerEventsSystem, engine, EventSystemCallback, InputAction
Entity, TransformType, pointerEventsSystem, EventSystemCallback, InputAction
} from '@dcl/sdk/ecs'
import { Vector3, Color4 } from '@dcl/sdk/math'
import { getSDK } from './sdk'

/**
* Creates a text label spatially linked to an entity
Expand All @@ -24,6 +24,9 @@ export function addLabel(
size?: number,
textOffset?: Vector3
): Entity {

const { engine, components: { Transform, Billboard, TextShape } } = getSDK()

let label = engine.addEntity()

Transform.create(label, {
Expand Down Expand Up @@ -63,6 +66,10 @@ export function addTestCube(
sphere?: boolean,
noCollider?: boolean
): Entity {

const { engine, pointerEventsSystem, components: { Transform, MeshRenderer, Material, MeshCollider } } = getSDK()


let cube = engine.addEntity()

Transform.create(cube, transform)
Expand Down
13 changes: 12 additions & 1 deletion src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Entity, engine, Transform, AudioSource, EasingFunction } from '@dcl/sdk/ecs'
import { Entity, EasingFunction } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'
import { InterpolationType } from './math';
import { getSDK } from './sdk';

/**
* Returns an array of entities that all share the provided entity as parent.
Expand All @@ -12,6 +13,9 @@ import { InterpolationType } from './math';
export function getEntitiesWithParent(
parent: Entity
): Entity[] {

const { engine, components: { Transform } } = getSDK()

const entitiesWithParent: Entity[] = [];

for (const [entity, transform] of engine.getEntitiesWith(Transform)) {
Expand All @@ -35,6 +39,9 @@ export function getEntitiesWithParent(
export function getEntityParent(
child: Entity
): Entity {

const { engine, components: { Transform } } = getSDK()

const transform = Transform.getOrNull(child);
if (transform) {
return transform.parent as Entity;
Expand All @@ -51,6 +58,8 @@ export function getEntityParent(
* @public
*/
export function getPlayerPosition(): Vector3 {
const { engine, components: { Transform } } = getSDK()

return Transform.getOrNull(engine.PlayerEntity)?.position || Vector3.create()
}

Expand All @@ -70,6 +79,8 @@ export function playSound(
loop: boolean = false,
position?: Vector3
) {
const { engine, components: { AudioSource, Transform } } = getSDK()

const entity = engine.addEntity()
AudioSource.create(entity, {
audioClipUrl: file,
Expand Down
62 changes: 20 additions & 42 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import { IEngine } from '@dcl/sdk/ecs'
import { setSDK } from './sdk'
import { defineTriggerComponent } from './trigger'

export let engine: IEngine

export function initLibrary(
engine: IEngine,
) {
setSDK({ engine })
defineTriggerComponent()
}

export {
InterpolationType,
remap,
Expand All @@ -11,54 +24,19 @@ export {
addTestCube
} from './debug'

export {
ToggleState,
ToggleCallback,
Toggles,
toggles
} from './toggle'
export * as toggles from './toggle'

export {
Tweens,
tweens
} from './tween'
export * as tweens from './tween'

export {
Sounds,
sounds
} from './audio'
export * as audio from './audio'

export {
PerpetualMotions,
perpetualMotions
} from './perpetualMotion'
export * as perpetualMotions from './perpetualMotion'

export {
Paths,
paths
} from './path'
export * as paths from './path'

export {
Triggers,
triggers,
LAYER_1,
LAYER_2,
LAYER_3,
LAYER_4,
LAYER_5,
LAYER_6,
LAYER_7,
LAYER_8,
ALL_LAYERS,
NO_LAYERS,
PLAYER_LAYER_ID
} from './trigger'
export * as triggers from './trigger'

export {
Timers,
TimerId,
timers
} from './timer'
export * as timers from './timer'

export {
actions
Expand Down
Loading
Loading