#Doodle Safari

27 messages · Page 1 of 1 (latest)

verbal cape
#

Doodle Safari is a new remixable game built to show off the new data persistence feature. It comes with a bunch of flexible systems that you can remix and turn into your own games that save player progress!

Check it out here: https://dotbigbang.com/game/bdbff8dbcc8d40af9430ee4336a70001/doodle-safari?mp=discord

Players explore a small world and take photographs of little creatures called Doodles. Each area has a couple species to discover. A merchant is set up near the tent selling hats, scarves and other accessories. Spend the coins and diamonds you find there to look your best!

Just like the Halloween Fun Example, the easiest way to grab this stuff and make your own versions of it is to create your own templates from the existing objects, drop them into your game and change them from there.

The next series of posts will dive into the systems in the game and how you can use them to build your own games.

dot big bang

Hop on in! This is a live multiplayer session!

#

Custom Player Template

In Doodle Safari we have set up a custom player that all players use.

Important Note: In order for player save data to work you will need to set the “Player Template” in your game to a player template that includes the dbb_player_state script. All of the systems in the game communicate with this script to save data.

To build your own Player Template you can do the following:

While editing, play the game

  • Switch to the “Fly Camera” using the button or by pressing B
  • Select your player
  • In the “Entity” panel click “Unlink from Template” and hit OK on the warning dialog that appears
  • Ensure that a dbb_player_state script is on the player Entity
  • Click the “New Template” button and give it a name
  • Click the “Save” button
  • Delete the copy of the player

The “Player Template” field can be found in the “Save Options” panel under “Additional Game Options”. If you don’t see this you may need to click the “Advanced” button in the lower right corner of the editor.

  • Click the “Player Template” field
  • Navigate to your Player Template and select it

Now when you run the game your template will be used!

#

Saving Data

The systems in the following posts all handle saving data for you, but if you would like to save data with your own scripts you can use the methods below:

// Get the entire key/value state object
this.game.sendEventToTag(“player_state”, "getState", null, (state: any) => {
    // Work with the state here.
});

// Or without a callback
this.game.sendEventToTag(“player_state”, "getState", this.entity);

onGetState(state: any) {
     // Work with the state here.
}

// Get the value of a key
this.game.sendEventToTag(“player_state”, "getValue", null, “mySaveId”, (value: any) => {
    // Work with the value here. value will be undefined if it doesn’t exist!
});

// Or without a callback
this.game.sendEventToTag(“player_state”, "getValue", this.entity, “mySaveId”);

onGetValue(key: string, value: any) {
    // Work with the value here. value will be undefined if it doesn’t exist!
}

// Set the value of a key
this.game.sendEventToTag(“player_state”, "setValue", “mySaveId”, value);

// Increment the numeric value of a key
this.game.sendEventToTag(“player_state”, "incrementValue", “mySaveId”, amount);

// Reset a key
this.game.sendEventToTag(“player_state”, "reset", “mySaveId”);

// Reset the entire state
this.game.sendEventToTag(“player_state”, "reset");

You can also listen for save data changes by adding the “player_state_listener” tag to your Entity and adding the following methods to your script:

// Called when player state loads. This may never get called in your script depending on the order things are executed in so don’t rely on it
onPlayerStateLoaded(state: any) {}

// Called any time a key is changed
onPlayerStateChanged(key: string, oldValue: any, newValue: any) {}

// Called on each successful save
onPlayerStateSaveSuccess(state: any) {}

// Called on a failed save
onPlayerStateSaveFailed(state: any) {}
#

Collectables

The simplest form of save data is tracking how many of an object the Player has collected. Drop a “Collectable” script onto an Entity and fill in the properties to easily add them to your game.

Collectables will behave differently based on if they are networked or not. To make a Collectable networked just add the “networked” tag to it.

Networked - The Collectable can be collected by a single Player. When collected it will vanish on all Players screens and reset after the reset time has passed if configured to do so.

Not Networked - Each Player can collect it without affecting other Players it will reset after the reset time has passed based on when that Player collected it.

Next I’ll go over each property on the “Collectable” script and explain what it does.

Data Id - This is used as an identifier for the save data. It should be unique per Collectable type and helps the system understand what it is counting when this is collected.

Amount - The amount of this Collectable type that gets added to the Players data when this is collected. You can use this to make a single coin, or a bundle of 10 experience.

Reset Seconds - If this is greater than 0, after this has been collected it will appear and be collectable again once this amount of seconds has passed.

Collect Fx - This template will be spawned at the same location as the Collectable when it is collected. Make sure you add a script to the template that destroys it after a set amount of time so you don’t end up with a lot of Entities in your game.

#

Floating Text - If you have a “Floating Text Manager” in your game, this text will appear at the location of the Collectable when it is collected. The next several settings let you modify the position and appearance of the text.

You can also use special formatting tags to inject information dynamically. Anything inside curly braces will be swapped for an actual value if it exists. These values need to match property names on the “Collectable” script, or if you are using a Database, the “Collectable Data” script that matches the “Data Id”.

In the example image you can see that {amount} and {displayName} will get swapped for “1” and “Stone Key”.

Floating Text Color - The color of the floating text.

Floating Text Opacity - The transparency of the floating text. 0 = invisible, 1 = fully opaque.

Floating Text Scale - The size of the floating text.

Floating Text Offset - Use this to move the position of the floating text relative to this Collectable. This is useful if you want the text to appear higher up in the air, or off to one side.

While Collectables work on their own without any additional setup, if you want to use Collectables with the other systems, like UI or Collectable Containers, you will need to add them to a Database. This is explained in the Database post below.

#

Database

The Database is a spot to keep all of your game settings for the various systems to read. Each entry in the Database is a script representing that data like “Collectable Data” or “Screen Data”. You can have multiple Databases to spread data around and organize it. You can also remix an existing Data script or make your own to build and store your own game data.

The types of data we included with Doodle Safari are:

Collectable Data - Used to add information about Collectables for use in other systems.

Collectable Drop Data - Used to define lists of Collectables that drop out of things like Collectable Containers.

Cosmetic Data - Used for purchasing cosmetics.

Purchasable Data - Used for purchasing arbitrary things like access to an area or any other thing that isn’t a Collectable or Cosmetic.

Screen Data - Defines settings for screens. This data is used by the “Screen Manager”.

Trade Data - Used to swap one Collectable for another.

Some common fields on data that can help other scripts display it or get access to it are:

Display Name - Several scripts will look for this to write a more readable name into UI.

Description - This can be useful for writing a more in depth description of data into some UI.

Tags - These can be used by scripts to grab a specific subset of data that share tags.

The Database can be queried by other scripts using:

this.game.sendEventToTag(“database”, “getData”, “myDataId”, (data: any) => {
    // Work with data here
});

There are several other methods that can get data in various ways. These can be found in the documentation in the “Database” script.

#

Collectable Container

This is great for chests and things where you want the Player to open it and have several Collectables come out. In Doodle Safari we used these for chests and the hidden Doodles to give out currency, photos and experience.

These do require Collectable Data and Collectable Drop Data in a Database, so please make sure you understand how to set that up. Once you have that data you can add the “Collectable Container” script to an Entity and fill in the values on it.

Just like Collectables, this will behave differently based on if it is networked or not. To make a Collectable Container networked just add the “networked” tag to it.

Networked - The Collectable Container can be opened by a single Player. When opened it will open on all Players screens and reset after the reset time has passed if configured to do so.

Not Networked - Each Player can open it without affecting other Players. It will reset after the reset time has passed based on when that Player opened it.

The properties on the script are:

Unique Container Id - This is used for save data and will save the last collect time and number of collections using this Id. Make sure that it isn’t shared with another container or they will collide and overwrite each others data.

Collectable Drop Data Ids - This is a comma separated list (spaces are ok!) of “Collectable Drop Data” “Data Id” fields. You can list the same one multiple times or list several different Collectable Drop Datas. They will all drop when the container is opened.

Share Loot If Networked - If true and the container Entity is networked, the Collectables that appear when the container is opened will appear for all Players.

Drop Position - The offset from the container Entity that the radius will be calculated at. Collectables will appear randomly in a circle centered on this position.

Drop Radius - The radius of the circle where Collectables can drop.

#

Animate Drops - If true, the Collectables that drop will animate to their destinations in a parabola over a short period of time.

Reset Seconds - If greater than 0, the container will close after this amount of seconds have passed since it was opened.

Max Collections - The total number of times the container can be opened.

Collect Fx - This template will be spawned at the same location as the Collectable Container when it is opened. Make sure you add a script to the template that destroys it after a set amount of time so you don’t end up with a lot of Entities in your game.

Interaction Arrow Offset - Use this to change where the interaction icon appears.

Interaction Arrow Voxel Object - This will let you define a custom interaction icon.

On its own the Collectable Container does not make any visual changes to your object. To get that happening we’ve included a couple scripts that can react to events coming from the container.

Openable Container - Add this to the same Entity that has the Collectable Container script and fill in the “Open Event Name” with “onContainerCollected” and the “Close Event Name” with “onContainerReset”. Fill in the rest of the properties to build your own box with a lid.

Swappable Voxel Object - Add this to the same Entity that has the Collectable Container script and fill in the “First Event Name” with “onContainerReset” and the “Second Event Name” with “onContainerCollected”. Fill in the rest of the properties to have an object change appearance when collected.

#

Collectable Converter

This can be used to automatically convert one Collectable to another when the Player reaches a certain threshold. You can set multiple converters up to create a chain of currency conversions. We use these in Doodle Safari to convert Copper to Silver to Gold.

This also automatically ties into the purchase scripts we offer and will let a Player spend 1 Silver to buy something listed at 40 Copper.

This does expect Collectable Data to exist in a Database, so please make sure you understand how to set that up.

Input Collectable Data Id - The “Data Id” of the Collectable that should be traded for the other Collectable.

Output Collectable Data Id - The “Data Id” of the resulting Collectable.

Input Amount - The number of the Input Collectable needed to convert to the Output Collectable.

Output Amount - The number of the output Collectable given in return for the input Collectable(s).

#

Floating Text Manager

The Floating Text Manager script is required if you want to spawn floating text in the game. In Doodle Safari we only use it with Collectables, but it is quite easy to trigger from other scripts.

The Floating Text Manager is quite simple and only has a single Template reference to the text object that it spawns. Doodle Safari has one already referenced called “Floating Text”. If you want to customize that you can drop your own Template into that field, just make sure that it destroys itself after a set amount of time to avoid cluttering the game.

To trigger it from a script just call the following:

this.game.sendEventToTag(“floating_text”, “showFloatingText”, “My Text”, {
    color: “white”,
    opacity: 1,
    scale: 2
});
#

Screen Manager

The Screen Manager keeps track of active screens and has some helper functions to show and hide screens. It also has an optional modal overlay to allow for clicking / tapping outside of a screen to close it. The best way to get this manager into your own game is to create a template of the one in Doodle Safari.

The Screen Manager expects each screen to have a “Screen Data” in a Database, so please make sure you understand how to set that up.

The Screen Templates themselves can be built using the UI system. Check out the UI post below to see how that works.

We offer a couple of scripts to help open screens.

Open Screen - Add this script to an Entity and fill in the “Screen Data Id” to let a Player open a screen by interacting with the Entity. Doodle Safari uses this to open the Doodle Dex and Costume Store.

Open Screen On Data Amount Changed - Add this script to an Entity to have a screen show when the Player has a specific amount of a data. Doodle Safari is using this to show a “New Doodle Screen” the first time you find each type of Doodle.

You can show and hide screens in your own scripts by using the following:

// Show a screen
this.game.sendEventToTag(“screen_manager”, “showScreen”, “My Screen Data Id”);

// Hide a screen
this.game.sendEventToTag(“screen_manager”, “hideScreen”, myScreenEntity);

// Hide all screens
this.game.sendEventToTag(“screen_manager”, “hideAllScreens”);

If you add the “screen_manager_listener” tag to your Entity you can also get screen events:

// Called when a screen has been added / shown
onScreenAdded(screendataId: string, screenEntity: Entity) {}

// Called when a screen has been removed / hidden
onScreenRemoved(screenDataId: string, screenEntity: Entity) {}
#

UI

We built a small UI system in Doodle Safari to help quickly build complex UI without coding. Below I’ll explain how the system works and how to use it to build your own UI.

The system supports 3 types of elements: UI Panel, UI Button and UI Label. It uses a UI Controller to manage these elements. You generally want an Entity with a UI Controller and any number of elements to represent a part of your UI. For example a Player name display might be made up of a UI Controller, a UI Panel for the background and a UI Label element for the name.

Important Note: Since “UI Controller” is an admin only script you cannot add it to Entities yourself. To work around this you should create a Template from an existing UI Entity in Doodle Safari and work from there.

Each UI element shares some general properties that allow you to position it on screen. They also have a smaller set of their own properties. Let's go over the shared properties first:

Element Id - This should be unique across a single Entity containing UI elements. It is used for a few purposes like parenting elements, referencing them in animation sequences or accessing them via script.

Parent Element Id - Set this to the “Element Id” of an element on the same Entity to parent it to that element. This will cause all of its positioning to switch to the space of that parent. For example if you set “Y Anchor” to “Bottom” it will anchor to the bottom of the parent instead of the screen. It will also move with the parent and inherit its scale.

Platform - This can be used to make an element only appear on a specific platform or all platforms. For example setting “Platform” to “Mobile” on a UI Label will cause it to not appear in the game when played on PC. It’s a good way to make your UI work on mobile and non-mobile platforms without building two separate UIs.

#

X Anchor - This can be used to attach an element to the left, center or right of the screen or parent element. Additional offsets can be applied using the “X” property.

Y Anchor - This can be used to attach an element to the top, middle or bottom of the screen or parent element. Additional offsets can be applied using the “Y” property.

X - The number of pixels or normalized units to move this element horizontally.

Y - The number of pixels or normalized units to move this element vertically.

Width - The width of the element. This will default to an actual width, but you can override the default width here if needed.

Height - The height of the element. This will default to an actual height, but you can override the default height here if needed.

Coordinate Space - “Pixels” will make the “X”, “Y”, “Width” and “Height” properties use screen space. “UI” will make them use normalized screen space (Between 0 -1).

Scale - The scale of the element.

Sort Order - A higher sort order will place the element on top of elements with a lower sort order. This only works within Voxel Object elements or Text elements on the same Entity. Text is always rendered on a higher layer so it can’t be placed behind Voxel Objects.

Disabled - If true, the element will not send events when interacted with and depending on the type of element may look different.

Event Tag - If set, any hover and press events will be sent to this tag. You can find script examples for those events below. Hover and press events are always sent to the Entity the element is on.

// Sent when an element is hovered over
onHoverBegin(elementId: string) {}

// Sent when an element is no longer hovered over
onHoverEnd(elementId: string) {}

// Sent when an element is pressed
onPressBegin(elementId: string) {}

// Sent when an element is no longer pressed
onPressEnd(elementId: string) {}
#

UI Panel elements can be used to display Voxel Objects or they can just act as a container for other elements.

Data Id - If set to a value that exists in a Database this will try to find an “Icon” in that data and display it as the Voxel Object.

Opacity - Determines how transparent the Voxel Object is. Unfortunately if a Voxel Object has any transparency it will be rendered after all of the opaque Voxel Objects so Sort Order will not work.

Voxel Object - If set, will display the Voxel Object on screen at the position and scale of the element.

UI Buttons are similar to UI Panels but offer additional Voxel Object fields so they can change appearance as they are hovered over and pressed.

Normal Voxel Object - The Voxel Object to display when the button is not being interacted with. This will also act as the default for any other states that are not set.

Hovered Voxel Object - The Voxel Object to display while the button is being hovered over but not pressed.

Pressed Voxel Object - The Voxel Object to display while the button is being pressed.

Disabled Voxel Object - The Voxel Object to display while the button is disabled.

Click Sound - the sound to play when the button is pressed and released.

Keybind - The shortcut key that can be pressed to press this button. This only works on non-mobile browsers.

#

UI Label displays text on screen and comes with some ways to dynamically inject data into that text. Labels can also react to hover and press events by changing color.

Font - A font must be set for a label to appear.

Text - The text to display on screen. Any text enclosed in curly braces will be replaced with the value of the matching key in Player save data. For example if I had a Collectable in the game with a “Data Id” of “coin” I could set my text to “Coins {coin:0}”. The label would display the number of coins I have collected or default to 0 if I haven’t collected any. This works for any key in the save data.

Autoscale - If true, this will scale long text down to fit the “Max Width”.

Max Width - The maximum width that text can be if “Autoscale” is true.

Normal Color - The color of the text when it is not being interacted with.

Hovered Color - The color of the text while it is being hovered over but not pressed.

Pressed Color - The color of the text while it is being pressed.

Disabled Color - The color of the text while it is disabled.

#

There are a variety of things you can do with your own scripts and UI. Here are some ways that UI can be manipulated through script on the same Entity:

// Disables an element
this.entity.sendEvent(“disableUIElement”, “myElementId”);

// Enables an element
this.entity.sendEvent(“enableUIElement”, “myElementId”);

// Checks to see if the game is on a mobile browser
this.entity.sendEvent(“isOnMobile”, (isOnMobile: boolean) => {
    // Work with the result here
});

// Checks to see if the Player is touching the screen on a mobile browser
this.entity.sendEvent(“isTouchDown”, (isTouchDown: boolean) => {
    // Work with the result here
});

// Gets the last position a Player touched the screen on a mobile browser
this.entity.sendEvent(“getLastTouchPosition”, (position: Vector2) => {
    // Work with the result here
});

// Checks to see if the mouse or touch position is over any UI element on an Entity
this.entity.sendEvent(“isInputOverlapping”, (isOverlapping: boolean) => {
    // Work with the result here
});

// Checks to see if the Player pressing the mouse button
this.entity.sendEvent(“isMousePressed”, (isMousePressed: boolean) => {
    // Work with the result here
});

// Gets the UI Element Transform for a specific element
this.entity.sendEvent(“getUIElementTransform”, “myElementId”, (transform: any) => {
    // Work with the result here (See the “UI Controller” script for more details)
});

// Gets all UI Element Transforms on the Entity
this.entity.sendEvent(“getAllUIElementTransforms”, (transforms: Array<any>) => {
    // Work with the result here (See the “UI Controller” script for more details)
});

// Sets the parent of an element on this Entity to an element on a different Entity
this.entity.sendEvent(“setParentUITransform”, otherEntity, “targetElementId”, otherUITransform);
#

Change Mood Trigger

This can be used to change the lighting, fog, vignette and other mood settings over time when a Player enters or exits a trigger.

Add the “Change Mood Trigger” to an Entity and make sure it has a Trigger Collider set up at the size you need.

Change Seconds - The number of seconds the change should take. Settings will animate over this period so you can get some neat moving shadows.

Mode - “Change on Enter - Reset on Exit” is good if your trigger encompasses an entire area. A Player will change the mood while they are within the trigger and it will change back when they leave. “Change on Enter - Reset on Enter” should use a thin door like trigger. This will change the mood each time the Player passes through the trigger.

The rest of the properties are the same as the settings in the Mood Panel so you have complete control over the look of your game in each area.

Reset Player State UI

This can be used to reset the state for a Player. If enabled it places a button on screen that can be pressed to wipe data and teleport the Player back to the start.

Use this with caution!

This is meant to be a development tool and should generally not be enabled in a published game. The “Reset Player State UI” script has a “Disabled” checkbox on it that can be used to disable it without removing it from the game.

fading gull
#

this is so amazing!!! nice work! wow I have been looking for something like this!

p.s. you should put this wonderfully valuable and informative post on an actual web page on the dbb website somewhere! tons more users could benefit from these ephemeral chat posts if they were googlable web pages.

verbal cape
#

That's a very good idea, I'll see what I can do!

plucky igloo
#

If anyone needs help just with making a custom player character, I did a slightly more detailed breakdown with pictures for that. It needs to be formatted better, but for now: https://docs.google.com/document/d/11NwO1Cm-V_vSzftrS-TIZOeIxLjh6BjySPbsI1XXn74/

#

(That post is focused on the visuals of the player character, rather than anything else.)

plucky igloo
#

@inland nacelle I made a diagram of the data for just the containers and collectables. Is that helpful at all?

#

Several bits on there are optional, like you don't actually have to use the thing that random spawns the doodle templates unless you would like that functionality. If that helps, and you tell us the systems you are finding convoluted, then we will make breakdowns of those ones as well 🙂

Breaking up data like this is good for the flexibility and longevity of a game, but it does take getting use to 😆

inland nacelle
#

THIS IS AMAZING!!!!

#

For myself, the detail level and flow is perfect.
A little box that specifies what each thing is (template, object, etc) might help out folks who are totally brand new. (Even though "template" is pretty darn clear)

plucky igloo
#

ya, fair. I was trying to do what I could with colour, though I am not sure brand new people should start with doodle safari, roflmao.

plucky igloo