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
58 changes: 57 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,14 @@ jobs:
- checkout
- attach_workspace:
at: .
- run: npm run build -- --gdevelop-root-path GDevelop
# Off `main`, the build is deployed to staging: it uses the themes of the
# staging assets for the starters re-skinned with each theme.
- run: |
if [ "$CIRCLE_BRANCH" = "main" ]; then
npm run build -- --gdevelop-root-path GDevelop
else
npm run build -- --gdevelop-root-path GDevelop --staging
fi
- run: npm run check-post-build
- persist_to_workspace:
root: .
Expand All @@ -71,6 +78,23 @@ jobs:
at: .
- aws-cli/setup
- run: npm run deploy -- --cf-zoneid $CLOUDFLARE_ZONE_ID --cf-token $CLOUDFLARE_TOKEN
deploy-staging:
docker:
- image: cimg/node:20.13.1
steps:
- run:
# A pull request from a fork has no access to the credentials.
name: Skip if the credentials are not available
command: |
if [ -z "$AWS_ACCESS_KEY_ID" ]; then
echo "No AWS credentials (pull request from a fork?): not deploying to staging."
circleci-agent step halt
fi
- checkout
- attach_workspace:
at: .
- aws-cli/setup
- run: npm run deploy -- --staging --cf-zoneid $CLOUDFLARE_ZONE_ID --cf-token $CLOUDFLARE_TOKEN

# Run the gameplay tests of the example games with a real GDevelop: the
# latest Linux build published on S3 by GDevelop's own CI is downloaded and
Expand Down Expand Up @@ -201,6 +225,38 @@ workflows:
filters:
branches:
only: main
- deploy-staging:
requires:
- build
- tests
filters:
branches:
ignore: main

# Every night (03:00 UTC): rebuild and redeploy `main`. The starters re-skinned
# with each theme are generated by the build from the themes and assets the
# assets repository publishes, so this is how they catch up with it. Only this
# workflow runs on the schedule: the gameplay tests are not run again, as the
# games did not change.
nightly-rebuild:
triggers:
- schedule:
cron: '0 3 * * *'
filters:
branches:
only: main
jobs:
- install
- build:
requires:
- install
- tests:
requires:
- install
- deploy:
requires:
- build
- tests

# On `main` (or when explicitly asked for): check that every gameplay test
# of the repository still passes.
Expand Down
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,107 @@ If you know how to create _Pull Requests_, you can also clone this repository an

To add a game to the homepage the game have to be listed in the `scripts/generate-database.js` file.

### Theme slots

A *theme* replaces the placeholder art of a 3D starter with a coherent set of
assets, so a game created by the AI looks like the setting the user asked for.
Themes and starters never refer to each other: both point at *slots*, roles
such as `character.player` or `env.ground`.

- `theme-slots.json`, at the root, is the list of slots. It is the vocabulary
shared by every starter and every theme.
- `examples/<starter>/theme-slots.json` says which objects of that starter play
which slot:

```json
{
"version": 1,
"objects": {
"scene:Game Scene/Player": "character.player",
"scene:Game Scene/Ground": "env.ground",
"scene:Game Scene/Crate": { "top": "env.ground", "front": "env.wall" },
"object:TankConfiguration::CombinedTank/TankBase": "vehicle.tank.base"
},
"effects": {
"effect:Game Scene//SkyBox": { "frontFaceResourceName": "sky.day.front" }
},
"ignoredObjects": ["scene:Game Scene/Camera"]
}
```

An object is referred to as `scene:<scene>/<object>`, `global/<object>`, or
`object:<Extension>::<CustomObject>/<child>` for the children of a custom
object. A 3D model maps to one model slot. A 3D cube maps either to one texture
slot for all its faces, or to one slot per face. A skybox effect maps each of
its texture parameters.

**When you add a 3D starter, add its `theme-slots.json`.** The build refuses to
publish a 3D starter without one, or one where a 3D model or cube is neither
mapped nor listed in `ignoredObjects`, and it names the object:

```
Starter "starting-3d-sailing": the Scene3D::Model3DObject "scene:Game Scene/Boat"
is neither mapped to a slot nor listed in ignoredObjects in theme-slots.json.
```

- **Reuse an existing slot whenever the object plays an existing role.** A new
starter's hero is `character.player`. Every theme then covers it already.
- **Add a slot to the root `theme-slots.json` only for a genuinely new role.**
Every theme is missing it until someone fills it in the assets repository:
those objects keep their placeholder art in the meantime, which is not an
error.
- **List in `ignoredObjects`** what is not meant to be seen as art: the camera
anchor cube, an invisible collision helper, the faceless player of a first
person game.

Renaming or removing an object makes the build fail the same way on the entry
that no longer matches: fix or delete the line it names. HUD sprites, fonts and
sounds are not part of this.

#### Themed starters

The build uses these mappings to write, next to each 3D starter, a copy of it
re-skinned with each theme published by the assets repository:

```
examples/starting-3d-tank/starting-3d-tank.json # the starter
examples/starting-3d-tank/starting-3d-tank.theme-pirate.json # its pirate copy
```

When the AI picks a theme, GDevelop opens the copy instead of the starter, so
the game shows the themed assets from the first frame. The copies live in the
starter's folder because a starter refers to its images, sounds and fonts by a
relative path. They are not examples of their own and are never listed:
`themedStarters.json`, in the database, says which themes exist and which
starters each one covers. It is also what the AI prompts read, so a theme is
only offered once its copies are there.

A theme only re-skins what it has an asset for. Anything else keeps the
starter's placeholder art.

The copies are rebuilt on every deploy of this repository, and every night by
the `nightly-rebuild` workflow of the CircleCI configuration, which is how they
catch up with a theme or an asset published by the assets repository. A new
theme is therefore available the day after it is merged there (or right away,
by pushing to `main` here or rerunning the last `main` pipeline in CircleCI).

#### Trying a theme before merging

Any branch but `main` is built with `--staging` and deployed next to the live
examples, under `staging/examples` and `staging/examples-database`, like the
assets repository does. A staging build uses the themes of the **staging**
assets, so a theme pushed on a branch of the assets repository can be tried end
to end without merging anything:

1. push the theme on a branch of the assets repository (deployed to staging);
2. push (or rerun the pipeline of) a branch here, so the starters re-skinned
with it are built and deployed to staging;
3. push a branch of the AI prompts, whose dev prompts list the staging themes;
4. in a development build of GDevelop, turn on "Show staging assets" in the
asset store, then create a game with the AI.

Staging holds the result of the last branch that was pushed, whoever pushed it.

### Gameplay tests

A game can contain _gameplay tests_: scripts that play the game like a player
Expand Down
38 changes: 38 additions & 0 deletions examples/starting-3D-platformer/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Ground": {
"front": "env.ground",
"left": "env.wall",
"right": "env.wall",
"top": "env.wall",
"bottom": "env.wall"
},
"scene:Game Scene/Player": "character.player",
"scene:Game Scene/Obstacle": {
"front": "env.wall",
"left": "env.wall",
"right": "env.wall",
"top": "env.wall",
"bottom": "env.wall"
},
"scene:Game Scene/Coin": "pickup.coin",
"scene:Game Scene/PushableBox": {
"front": "env.crate",
"left": "env.crate",
"right": "env.crate",
"top": "env.crate",
"bottom": "env.crate"
}
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
}
}
27 changes: 27 additions & 0 deletions examples/starting-3d-car-racing/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Road_3D": "env.road",
"scene:Game Scene/Grass_3D": "env.ground",
"scene:Game Scene/PlayerCar": "vehicle.car",
"scene:Game Scene/TrafficCone": "prop.obstacle",
"scene:Game Scene/FinishLine_3D": {
"front": "env.finish",
"left": "env.finish",
"right": "env.finish",
"top": "env.finish",
"bottom": "env.finish"
},
"scene:Game Scene/Walls_3D": "env.wall"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
}
}
17 changes: 17 additions & 0 deletions examples/starting-3d-draggable-tiles/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Ground": {
"front": "env.ground.tiled"
},
"scene:Game Scene/Unit": "character.player",
"scene:Game Scene/Tower": "building.tower",
"scene:Game Scene/Tree": "nature.tree",
"scene:Game Scene/PlacementGrid": {
"front": "env.tiles"
}
},
"ignoredObjects": [
"scene:Game Scene/Camera"
]
}
20 changes: 20 additions & 0 deletions examples/starting-3d-driving/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Road_3D": "env.road",
"scene:Game Scene/Grass_3D": "env.ground",
"scene:Game Scene/PlayerCar": "vehicle.car",
"scene:Game Scene/TrafficCone": "prop.obstacle",
"scene:Game Scene/Walls_3D": "env.wall"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
}
}
22 changes: 22 additions & 0 deletions examples/starting-3d-endless-runner/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Platform_Ground": {
"front": "env.wall",
"left": "env.ground",
"right": "env.ground",
"top": "env.ground"
},
"scene:Game Scene/Hazard": "hazard.spike",
"scene:Game Scene/Platform_Floating": {
"front": "env.ground",
"left": "env.ground",
"right": "env.ground",
"top": "env.ground"
},
"scene:Game Scene/Player": "character.player"
},
"ignoredObjects": [
"scene:Game Scene/Camera"
]
}
19 changes: 19 additions & 0 deletions examples/starting-3d-flight-sim/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Ground": {
"front": "env.ground.tiled"
},
"scene:Game Scene/PlayerAircraft": "vehicle.aircraft.player"
},
"effects": {
"effect:Game Scene//SkyBox": {
"backFaceResourceName": "sky.day.back",
"bottomFaceResourceName": "sky.day.bottom",
"frontFaceResourceName": "sky.day.front",
"leftFaceResourceName": "sky.day.left",
"rightFaceResourceName": "sky.day.right",
"topFaceResourceName": "sky.day.top"
}
}
}
18 changes: 18 additions & 0 deletions examples/starting-3d-point-and-click-adventure/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Wall": "env.wall",
"scene:Game Scene/Water": {
"front": "env.water"
},
"scene:Game Scene/Ground": {
"front": "env.ground.tiled"
},
"scene:Game Scene/Player": "character.player",
"scene:Game Scene/NPC": "character.npc",
"scene:Game Scene/Grass": "nature.grass"
},
"ignoredObjects": [
"scene:Game Scene/Camera"
]
}
17 changes: 17 additions & 0 deletions examples/starting-3d-rts-unit-selection/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"version": 1,
"objects": {
"scene:Game Scene/Ground": {
"front": "env.ground.tiled"
},
"scene:Game Scene/RTSUnit": "character.player",
"scene:Game Scene/Building_Impassable": "env.wall",
"scene:Game Scene/Water_Passable": {
"front": "env.water"
},
"scene:Game Scene/Grass": "nature.grass"
},
"ignoredObjects": [
"scene:Game Scene/Camera"
]
}
15 changes: 15 additions & 0 deletions examples/starting-3d-shootemup/theme-slots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": 1,
"objects": {
"scene:Game Scene/RepeatingBackground": {
"front": "env.ground.tiled"
},
"scene:Game Scene/Player": "vehicle.aircraft.player",
"scene:Game Scene/Enemy": "vehicle.aircraft.enemy",
"scene:Game Scene/PlayerBullet": "projectile.player",
"scene:Game Scene/EnemyBullet": "projectile.enemy"
},
"ignoredObjects": [
"scene:Game Scene/Camera"
]
}
Loading
Loading