Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

step 6: scoreboard #24

Open
wants to merge 19 commits into
base: 5-progress
Choose a base branch
from
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
200 changes: 154 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,68 @@
# SDK Utils Player Library
# SDK Mini Games Library

This library provides utilities for managing player queues and active players in a scene.

## Exported Functions
## [Step 1: Install & Init Library](https://github.com/decentraland/sdk-mini-games/pull/19/files)

### initLibrary(_engine: IEngine, syncEntity: typeof syncEntity, playersApi: typeof players)`
#### Install @dcl-sdk/mini-games Library in your scene
```ts
// Run the following command in the scene terminal
npm install @dcl-sdk/mini-games@next
```
#### Copy all the assets needed for the UI, Scoreboard and other models inside your scene.
```ts
// Run the following command in the scene terminal
node ./node_modules/@dcl-sdk/mini-games/scripts/postinstall.js
// This will create the `mini-games-assets` folder inside your scene with all the models that the library uses.
```

Initializes the library with the necessary dependencies. Returns listeners that can be overridden with callbacks.
### Import Mini Games library in your scene
```ts
// game.ts file
import { initLibrary } from '@dcl-sdk/mini-games/src'
import { syncEntity } from '@dcl/sdk/network'
import players from '@dcl/sdk/players'

### setNextPlayer()
// make sure to put this line outside the main function.
initLibrary(engine, syncEntity, players, {
environment: 'dev',
gameId: 'game-id-here'
})

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

Sets the next player in the queue as active and removes the current active player.
## [Step 2: Players queue](https://github.com/decentraland/sdk-mini-games/pull/20/files)

### addPlayer()
The library provides all the logic behind to handle the multiplayers queue.


### Queue Players Functions
```ts
import { queue } from '@dcl-sdk/mini-games/src'
```

#### queue.addPlayer(): void
Adds the current player to the queue if they're not already in it.

### isActive(): boolean
#### queue.setNextPlayer(): void
Removes current player from the queue (if its the active one), and set the next player in the queue as active.

#### queue.isActive(): boolean
Checks if the current user is the active player.

### getQueue()
#### queue.getQueue(): Player[]
Returns an array of players in the queue, sorted by the ime they join the queue.

Returns an array of players in the queue, sorted by join time.
#### queue.initQueueDisplay(displayTransform: TransformType): void
Create a display on the scene that informs the player the current status of the queue.
After installing the library needs to place display glb model in `mini-game-assets/models/queueDisplay/workstation_display.glb`

## Exported Types
### Listener for active player updates
#### listeners.onActivePlayerChange

### PlayerType
### Player Type

```typescript
type PlayerType = {
Expand All @@ -37,47 +73,119 @@ type PlayerType = {
}
```

## Exported Listeners
### onActivePlayerChange: (player: PlayerType) => void
## [Step 3: Scene Rotation + Game Area Checker + Timeout](https://github.com/decentraland/sdk-mini-games/pull/21/files)

### Scene Rotation
Set the orientation of the scene. You can set any number but ideally you want to set 0, 90, 180 or 270.
This would add a RootEntity and then you can parent all the entities to this root entity so they all point to the same orientation.


### Game Area Check
You can define your play game area. So if the active player decides to leave the area, it switches automatically to the next player.
Also if a player finds a way to enter the game area and is not the active player, it will be kicked out.

### Timeout check
Set a max amout of time that a player can play the game without being kicked.
If the player is alone in the game, it will continue playing till a new user arrives.


This can be done by passing extra params to the initLibrary
```ts
import { initLibrary, sceneParentEntity } from '@dcl-sdk/mini-games/src'
const _1_SEC = 1000
const _1_MIN = _1_SEC * 60

miniGames.initLibrary(engine as any, syncEntity, playersApi, {
gameId: "4ee1d308-5e1e-4b2b-9e91-9091878a7e3d",
environment: "dev",
// time in ms
gameTimeoutMs: _1_MIN,
inactiveTimeoutMs: 20 * _1_SEC_,
// game area rectangle
gameArea: {
// top left point
topLeft: Vector3.create(5.15, 0, 2.23),
// bottom right point
bottomRight: Vector3.create(13.77, 0, 13.77),
// point outside the game area to exit players
exitSpawnPoint: Vector3.create(0,0,7)
}
})
function main() {
const gameArea = engine.addEntity()
Transform.create(gameArea, { parent: sceneParentEntity, position, ...etc })
}
```

You can override this listener to perform custom actions when the active player changes.
## [Step 4: UI](https://github.com/decentraland/sdk-mini-games/pull/22/files)

## Example
```typescript
import { engine } from '@dcl/sdk/ecs'
import { syncEntity } from '@dcl/sdk/network'
import playersApi from '@dcl/sdk/players'
import { initPlayersQueue, listeners, addPlayer, isActive, getQueue, setNextPlayer } from './sdk-utils-player'
We already implement the generic UI to use inside the mini-games. Like the Play Game sign, Music On/Off, Levels, etc.

// Initialize the library
initPlayersQueue(engine, syncEntity, playersApi)
#### Menu Button UI

// Override the onActivePlayerChange listener
listeners.onActivePlayerChange = (player) => {
console.log(`New active player: ${player.address}`)
}
const startCube = createCube(2, 1, 2)
const finishCube = createCube(2, 4, 2)
```ts
// MenuButton(position: TransformType, assetShape, assetIcon, hoverText, callback, enabledByDefault (optional), releaseTime (optional) )
import { ui } from '@dcl-sdk/mini-games/src'

// Listen to changes on the queue
listeners.onActivePlayerChange = (player) => {
console.log('active player changed', player)
}
// All the available icons
const icons = ui.uiAssets.icons
// All the available Shapes & Colors
const shapes = ui.uiAssets.shapes

// Add player to the queue
pointerEventsSystem.onPointerDown({ entity: startCube, opts: { hoverText: 'Add player to queue' } }, () => {
addPlayer()
})

// Finish game and set the next player
pointerEventsSystem.onPointerDown({ entity: finishCube, opts: { hoverText: 'Finish game'} }, () => {
setNextPlayer()
new ui.MenuButton(
{
parent: sceneParentEntity,
position: Vector3.create(-3.74, 1.03, 0),
rotation: Quaternion.fromEulerDegrees(-45, 90, 0),
scale: Vector3.create(1.2, 1.2, 1.2)
},
ui.uiAssets.shapes.RECT_GREEN,
ui.uiAssets.icons.playText,
"PLAY GAME",
() => {
queue.addPlayer()
}
)
```

## [Step 5: Progress API](https://github.com/decentraland/sdk-mini-games/pull/23/files)
We manage all the Progress API for you. We use the gameId set on the initLibrary, so be sure to update this value !

### Get last user progress of the game
```ts
type ProgressKey = 'level' | 'score' | 'moves' | 'time'
const userProgress = await progress.getProgress('level', progress.SortDirection.DESC, 1)
```

### Upsert the user progress based on the game.
```ts
import { progress } from '@dcl-sdk/mini-games/src'

await progress.upsertProgress({
level: gameData.currentLevel,
time: gameData.levelFinishedAt - gameData.levelStartedAt,
moves: gameData.moves
})
```

// Check if current player is active
console.log(`Is current player active? ${isActive()}`);
## [Step 6: ScoreBoard](https://github.com/decentraland/sdk-mini-games/pull/24/files)
Initialize the scoreboard and that's it !
```ts
// Add scoreboard
const width = 2.5
const height = 2.8
const scale = 1.2
new ui.ScoreBoard(
{
// parent: sideSignB,
position: Vector3.create(1.3, 4, 0.15),
rotation: Quaternion.fromEulerDegrees(0, 180, 0)
},
width,
height,
scale,
ui.TIME_LEVEL
)

// Get the current Queue
const queue = getQueue();
console.log('Current queue:', queue);
```
17 changes: 16 additions & 1 deletion example-scene/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ export function main() {
rotation: Quaternion.fromEulerDegrees(0, -90, 0)
})

// Add scoreboard
const width = 2.5
const height = 2.8
const scale = 1.2
new ui.ScoreBoard(
{
position: Vector3.create(4, 4, 0.15),
rotation: Quaternion.fromEulerDegrees(0, 180, 0)
},
width,
height,
scale,
ui.TIME_LEVEL
)

// Add the Play Button to the fence instead of the old start game cube.
new ui.MenuButton(
{
Expand Down Expand Up @@ -72,7 +87,7 @@ export function main() {
queue.listeners.onActivePlayerChange = (player) => {
// If the user is the active player, move it to the game area
if (queue.isActive()) {
startGame()
void startGame()
void movePlayerTo({ newRelativePosition: Vector3.create(6.5, 2, 8), cameraTarget: Vector3.create(13, 2, 8) })
}
console.log('active player changed', player)
Expand Down
Loading
Loading