From 1d22c11cde12798dbbda9ec80e9999831786bffd Mon Sep 17 00:00:00 2001
From: tuxmain <tuxmain@zettascript.org>
Date: Thu, 25 Aug 2022 19:55:34 +0200
Subject: [PATCH] Levels stored outside program
---
Cargo.lock | 14 ++++
Cargo.toml | 2 +
assets/game.levels.json | 119 ++++++++++++++++++++++++++
src/levels.rs | 180 ++++++++++++++++++++++++++++++----------
src/levels/game_over.rs | 61 --------------
src/levels/level0.rs | 50 -----------
src/levels/level1.rs | 58 -------------
src/levels/level2.rs | 72 ----------------
src/levels/level3.rs | 59 -------------
src/main.rs | 12 +--
10 files changed, 280 insertions(+), 347 deletions(-)
create mode 100644 assets/game.levels.json
delete mode 100644 src/levels/game_over.rs
delete mode 100644 src/levels/level0.rs
delete mode 100644 src/levels/level1.rs
delete mode 100644 src/levels/level2.rs
delete mode 100644 src/levels/level3.rs
diff --git a/Cargo.lock b/Cargo.lock
index 3ddac41..f26df36 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -316,6 +316,18 @@ dependencies = [
"rodio",
]
+[[package]]
+name = "bevy_common_assets"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f7be9ee39085d8319d5cd853447b0b5c4f8b4bfd647aec91e2bd996e9db67ef"
+dependencies = [
+ "anyhow",
+ "bevy",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "bevy_core"
version = "0.8.1"
@@ -898,6 +910,7 @@ version = "0.1.0"
dependencies = [
"bevy",
"bevy-inspector-egui",
+ "bevy_common_assets",
"bevy_rapier2d",
"cpal 0.14.0",
"crossbeam-channel",
@@ -905,6 +918,7 @@ dependencies = [
"rand",
"rand_distr",
"rapier2d",
+ "serde",
"ticktock",
]
diff --git a/Cargo.toml b/Cargo.toml
index f50dc6f..df12e56 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,12 +7,14 @@ edition = "2021"
[dependencies]
bevy = "0.8.1"
+bevy_common_assets = { version = "0.3.0", features = ["json"] }
bevy-inspector-egui = "0.12.1"
bevy_rapier2d = "0.16.2"
crossbeam-channel = "0.5.6"
rand = "0.8.5"
rand_distr = "0.4.3"
rapier2d = "0.14.0"
+serde = { version = "1.0.144", features = ["derive"] }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies]
cpal = "0.14.0"
diff --git a/assets/game.levels.json b/assets/game.levels.json
new file mode 100644
index 0000000..549c17b
--- /dev/null
+++ b/assets/game.levels.json
@@ -0,0 +1,119 @@
+{
+ "levels": [
+ {
+ "comment": "Movement tutorial",
+ "platforms": [
+ {"pos": [0, -256], "size": [800, 16]}
+ ],
+ "characters": [
+ {"pos": [0, -192], "color": [1,0,0,1]},
+ {"pos": [-128, -192], "color": [0,1,0,1]},
+ {"pos": [128, -192], "color": [0,0,1,1]}
+ ],
+ "absorbing_filters": [],
+ "rotating_filters": [],
+ "texts": [
+ {
+ "pos": [0, 0],
+ "font_size": 32,
+ "text": "Combine the colors to synthetize a white light.\nUse arrows to move."
+ }
+ ]
+ },
+ {
+ "comment": "Switch tutorial",
+ "platforms": [
+ {"pos": [0, -256], "size": [800, 16]},
+ {"pos": [128, 256], "size": [96, 16]}
+ ],
+ "characters": [
+ {"pos": [0, -192], "color": [0,1,0,1]},
+ {"pos": [-128, -192], "color": [1,0,0,1]},
+ {"pos": [128, 320], "color": [0,0,1,1]}
+ ],
+ "absorbing_filters": [],
+ "rotating_filters": [],
+ "texts": [
+ {
+ "pos": [0, 0],
+ "font_size": 32,
+ "text": "Press Tab to switch."
+ }
+ ]
+ },
+ {
+ "comment": "Absorbing filter tutorial",
+ "platforms": [
+ {"pos": [0, -256], "size": [800, 16]},
+ {"pos": [0, -128], "size": [800, 16]}
+ ],
+ "characters": [
+ {"pos": [-128, -192], "color": [1,0.64,0,1]},
+ {"pos": [128, -192], "color": [0,0.37,1,1]}
+ ],
+ "absorbing_filters": [
+ {
+ "pos": [0, -192],
+ "size": [16, 112],
+ "color": [1,0,0,1]
+ }
+ ],
+ "rotating_filters": [],
+ "texts": [
+ {
+ "pos": [0, 0],
+ "font_size": 32,
+ "text": "Press R to reset."
+ }
+ ]
+ },
+ {
+ "comment": "Rotating filter tutorial",
+ "platforms": [
+ {"pos": [0, -256], "size": [800, 16]}
+ ],
+ "characters": [
+ {"pos": [0, -192], "color": [1,0,0,1]},
+ {"pos": [-128, -192], "color": [1,0,0,1]},
+ {"pos": [128, -192], "color": [1,0,0,1]}
+ ],
+ "absorbing_filters": [],
+ "rotating_filters": [
+ {
+ "pos": [0, -64],
+ "angle": 45
+ }
+ ],
+ "texts": [
+ {
+ "pos": [0, 0],
+ "font_size": 32,
+ "text": "Let's rotate the hue!"
+ }
+ ]
+ },
+ {
+ "comment": "Game over",
+ "platforms": [
+ {"pos": [0, -256], "size": [800, 16]}
+ ],
+ "characters": [
+ {"pos": [0, -64], "color": [1,0,0,1]}
+ ],
+ "absorbing_filters": [],
+ "rotating_filters": [],
+ "texts": [
+ {
+ "pos": [0, 128],
+ "font_size": 48,
+ "text": "Thank you for playing!"
+ },
+ {
+ "pos": [0, 0],
+ "font_size": 32,
+ "text": "There is no more light to combine."
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/levels.rs b/src/levels.rs
index ad217e1..c131e63 100644
--- a/src/levels.rs
+++ b/src/levels.rs
@@ -1,14 +1,9 @@
#![allow(clippy::too_many_arguments)]
-mod game_over;
-mod level0;
-mod level1;
-mod level2;
-mod level3;
-
use crate::game::*;
-use bevy::prelude::*;
+use bevy::{prelude::*, reflect::TypeUuid};
+use serde::{Deserialize, Serialize};
pub fn setup_level(
level_startup_event: &mut EventWriter<LevelStartupEvent>,
@@ -35,51 +30,152 @@ pub fn post_setup_level(
mut level_startup_event: EventReader<LevelStartupEvent>,
asset_server: Res<AssetServer>,
audio: Res<crossbeam_channel::Sender<AudioMsg>>,
+ stored_levels_assets: Res<Assets<StoredLevels>>,
+ stored_levels_handle: Res<Handle<StoredLevels>>,
) {
for _ in level_startup_event.iter() {
if let Some(level_id) = current_level.0 {
- match level_id.0 {
- 0 => level0::setup(
+ if let Some(stored_level) = stored_levels_assets
+ .get(&stored_levels_handle)
+ .unwrap()
+ .levels
+ .get(level_id.0 as usize)
+ {
+ spawn_stored_level(
&mut commands,
- &mut meshes,
&character_meshes,
- &mut materials,
- &audio,
- &asset_server,
- ),
- 1 => level1::setup(
- &mut commands,
&mut meshes,
- &character_meshes,
&mut materials,
- &audio,
&asset_server,
- ),
- 2 => level2::setup(
- &mut commands,
- &mut meshes,
- &character_meshes,
- &mut materials,
&audio,
- &asset_server,
- ),
- 3 => level3::setup(
- &mut commands,
- &mut meshes,
- &character_meshes,
- &mut materials,
- &audio,
- &asset_server,
- ),
- _ => game_over::setup(
- &mut commands,
- &mut meshes,
- &character_meshes,
- &mut materials,
- &audio,
- &asset_server,
- ),
+ stored_level,
+ );
}
}
}
}
+
+#[derive(Deserialize, Serialize, TypeUuid)]
+#[uuid = "1fbba930-644b-0d62-2514-4b302b945327"]
+pub struct StoredLevels {
+ levels: Vec<StoredLevel>,
+}
+
+#[derive(Deserialize, Serialize, TypeUuid)]
+#[uuid = "a1464a30-1f57-a654-d56c-ded41032af0b"]
+pub struct StoredLevel {
+ pub comment: String,
+ pub characters: Vec<StoredCharacter>,
+ pub platforms: Vec<StoredPlatform>,
+ pub absorbing_filters: Vec<StoredAbsorbingFilter>,
+ pub rotating_filters: Vec<StoredRotatingFilter>,
+ pub texts: Vec<StoredText>,
+}
+
+#[derive(Deserialize, Serialize, TypeUuid)]
+#[uuid = "1c798f8c-ef15-c528-693e-76becdef6b10"]
+pub struct StoredCharacter {
+ pub pos: Vec2,
+ pub color: Vec4,
+}
+
+#[derive(Deserialize, Serialize, TypeUuid)]
+#[uuid = "31696095-59de-93be-b5e9-333c2afbc900"]
+pub struct StoredPlatform {
+ pub pos: Vec2,
+ pub size: Vec2,
+}
+
+#[derive(Deserialize, Serialize, TypeUuid)]
+#[uuid = "bcad7fff-0605-c4e3-3cd4-42d5bbaad926"]
+pub struct StoredAbsorbingFilter {
+ pub pos: Vec2,
+ pub size: Vec2,
+ pub color: Vec4,
+}
+
+#[derive(Deserialize, Serialize, TypeUuid)]
+#[uuid = "fa2843f2-6e34-601b-6c46-4827b0370b3f"]
+pub struct StoredRotatingFilter {
+ pub pos: Vec2,
+ pub angle: f32,
+}
+
+#[derive(Deserialize, Serialize, TypeUuid)]
+#[uuid = "72f6321a-f01f-6eea-9b17-3159837a2fd3"]
+pub struct StoredText {
+ pub pos: Vec2,
+ pub font_size: f32,
+ pub text: String,
+}
+
+pub fn spawn_stored_level(
+ commands: &mut Commands,
+ character_meshes: &Res<CharacterMeshes>,
+ meshes: &mut ResMut<Assets<Mesh>>,
+ materials: &mut ResMut<Assets<ColorMaterial>>,
+ asset_server: &Res<AssetServer>,
+ audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
+
+ stored_level: &StoredLevel,
+) {
+ let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
+ spawn_platforms(
+ commands,
+ meshes,
+ materials,
+ stored_level.platforms.iter().map(|platform| {
+ (
+ Transform::from_xyz(platform.pos.x, platform.pos.y, 0.),
+ platform.size,
+ )
+ }),
+ );
+ spawn_characters(
+ commands,
+ character_meshes,
+ materials,
+ audio,
+ stored_level.characters.iter().map(|character| {
+ (
+ Transform::from_xyz(character.pos.x, character.pos.y, 0.),
+ character.color.into(),
+ )
+ }),
+ );
+ for absorbing_filter in stored_level.absorbing_filters.iter() {
+ spawn_absorbing_filter(
+ commands,
+ meshes,
+ materials,
+ Transform::from_xyz(absorbing_filter.pos.x, absorbing_filter.pos.y, 2.),
+ absorbing_filter.size,
+ absorbing_filter.color.into(),
+ );
+ }
+ for rotating_filter in stored_level.rotating_filters.iter() {
+ spawn_rotating_filter(
+ commands,
+ asset_server,
+ Transform::from_xyz(rotating_filter.pos.x, rotating_filter.pos.y, 2.),
+ rotating_filter.angle,
+ );
+ }
+ for text in stored_level.texts.iter() {
+ commands
+ .spawn_bundle(Text2dBundle {
+ text: Text::from_section(
+ &text.text,
+ TextStyle {
+ font: font.clone(),
+ font_size: text.font_size,
+ color: Color::WHITE,
+ },
+ )
+ .with_alignment(TextAlignment::CENTER),
+ transform: Transform::from_xyz(text.pos.x, text.pos.y, 0.),
+ ..Default::default()
+ })
+ .insert(Level);
+ }
+}
diff --git a/src/levels/game_over.rs b/src/levels/game_over.rs
deleted file mode 100644
index d513383..0000000
--- a/src/levels/game_over.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use crate::game::*;
-
-use bevy::prelude::*;
-
-pub fn setup(
- commands: &mut Commands,
- meshes: &mut ResMut<Assets<Mesh>>,
- character_meshes: &Res<CharacterMeshes>,
- materials: &mut ResMut<Assets<ColorMaterial>>,
- audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
- asset_server: &Res<AssetServer>,
-) {
- let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
- commands
- .spawn_bundle(Text2dBundle {
- text: Text::from_section(
- "Thank you for playing!",
- TextStyle {
- font: font.clone(),
- font_size: 48.0,
- color: Color::WHITE,
- },
- )
- .with_alignment(TextAlignment::CENTER),
- transform: Transform::from_xyz(0., 128.0, 0.),
- ..Default::default()
- })
- .insert(Level);
- commands
- .spawn_bundle(Text2dBundle {
- text: Text::from_section(
- "There is no more light to combine.",
- TextStyle {
- font,
- font_size: 32.0,
- color: Color::WHITE,
- },
- )
- .with_alignment(TextAlignment::CENTER),
- ..Default::default()
- })
- .insert(Level);
-
- spawn_platform(
- commands,
- meshes,
- materials,
- Transform::from_xyz(0.0, -256.0, 0.0),
- Vec2 { x: 800.0, y: 16.0 },
- );
-
- spawn_character(
- commands,
- character_meshes,
- materials,
- audio,
- Transform::from_xyz(0., -64., 0.),
- Color::RED,
- true,
- );
-}
diff --git a/src/levels/level0.rs b/src/levels/level0.rs
deleted file mode 100644
index d6789d8..0000000
--- a/src/levels/level0.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Movement tutorial
-
-use crate::game::*;
-
-use bevy::prelude::*;
-
-pub fn setup(
- commands: &mut Commands,
- meshes: &mut ResMut<Assets<Mesh>>,
- character_meshes: &Res<CharacterMeshes>,
- materials: &mut ResMut<Assets<ColorMaterial>>,
- audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
- asset_server: &Res<AssetServer>,
-) {
- let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
- commands
- .spawn_bundle(Text2dBundle {
- text: Text::from_section(
- "Combine the colors to synthetize a white light.\nUse arrows to move.",
- TextStyle {
- font,
- font_size: 32.0,
- color: Color::WHITE,
- },
- )
- .with_alignment(TextAlignment::CENTER),
- ..Default::default()
- })
- .insert(Level);
-
- spawn_platform(
- commands,
- meshes,
- materials,
- Transform::from_xyz(0.0, -256.0, 0.0),
- Vec2 { x: 800.0, y: 16.0 },
- );
-
- spawn_characters(
- commands,
- character_meshes,
- materials,
- audio,
- [
- (Transform::from_xyz(0., -192., 0.), Color::RED),
- (Transform::from_xyz(-128., -192., 0.), Color::GREEN),
- (Transform::from_xyz(128., -192., 0.), Color::BLUE),
- ],
- );
-}
diff --git a/src/levels/level1.rs b/src/levels/level1.rs
deleted file mode 100644
index 1d92187..0000000
--- a/src/levels/level1.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Switch tutorial
-
-use crate::game::*;
-
-use bevy::prelude::*;
-
-pub fn setup(
- commands: &mut Commands,
- meshes: &mut ResMut<Assets<Mesh>>,
- character_meshes: &Res<CharacterMeshes>,
- materials: &mut ResMut<Assets<ColorMaterial>>,
- audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
- asset_server: &Res<AssetServer>,
-) {
- let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
- commands
- .spawn_bundle(Text2dBundle {
- text: Text::from_section(
- "Press Tab to switch.",
- TextStyle {
- font,
- font_size: 32.0,
- color: Color::WHITE,
- },
- )
- .with_alignment(TextAlignment::CENTER),
- ..Default::default()
- })
- .insert(Level);
-
- spawn_platforms(
- commands,
- meshes,
- materials,
- [
- (
- Transform::from_xyz(0.0, -256.0, 0.0),
- Vec2 { x: 800.0, y: 16.0 },
- ),
- (
- Transform::from_xyz(128.0, 256.0, 0.0),
- Vec2 { x: 96.0, y: 16.0 },
- ),
- ],
- );
-
- spawn_characters(
- commands,
- character_meshes,
- materials,
- audio,
- [
- (Transform::from_xyz(0., -192., 0.), Color::GREEN),
- (Transform::from_xyz(-128., -192., 0.), Color::RED),
- (Transform::from_xyz(128., 320., 0.), Color::BLUE),
- ],
- );
-}
diff --git a/src/levels/level2.rs b/src/levels/level2.rs
deleted file mode 100644
index 91606d5..0000000
--- a/src/levels/level2.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Absorbing filter tutorial
-
-use crate::game::*;
-
-use bevy::prelude::*;
-
-pub fn setup(
- commands: &mut Commands,
- meshes: &mut ResMut<Assets<Mesh>>,
- character_meshes: &Res<CharacterMeshes>,
- materials: &mut ResMut<Assets<ColorMaterial>>,
- audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
- asset_server: &Res<AssetServer>,
-) {
- let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
- commands
- .spawn_bundle(Text2dBundle {
- text: Text::from_section(
- "Press R to reset.",
- TextStyle {
- font,
- font_size: 32.0,
- color: Color::WHITE,
- },
- )
- .with_alignment(TextAlignment::CENTER),
- ..Default::default()
- })
- .insert(Level);
-
- spawn_platforms(
- commands,
- meshes,
- materials,
- [
- (
- Transform::from_xyz(0.0, -256.0, 0.0),
- Vec2 { x: 800.0, y: 16.0 },
- ),
- (
- Transform::from_xyz(0.0, -128.0, 0.0),
- Vec2 { x: 800.0, y: 16.0 },
- ),
- ],
- );
-
- spawn_characters(
- commands,
- character_meshes,
- materials,
- audio,
- [
- (
- Transform::from_xyz(-128., -192., 0.),
- Color::rgba(1., 0.64, 0., 1.),
- ),
- (
- Transform::from_xyz(128., -192., 0.),
- Color::rgba(0., 0.37, 1., 1.),
- ),
- ],
- );
-
- spawn_absorbing_filter(
- commands,
- meshes,
- materials,
- Transform::from_xyz(0., -192., 2.),
- Vec2 { x: 16.0, y: 112.0 },
- Color::RED,
- );
-}
diff --git a/src/levels/level3.rs b/src/levels/level3.rs
deleted file mode 100644
index 5fa2607..0000000
--- a/src/levels/level3.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-// Rotating filter tutorial
-
-use crate::game::*;
-
-use bevy::prelude::*;
-
-pub fn setup(
- commands: &mut Commands,
- meshes: &mut ResMut<Assets<Mesh>>,
- character_meshes: &Res<CharacterMeshes>,
- materials: &mut ResMut<Assets<ColorMaterial>>,
- audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
- asset_server: &Res<AssetServer>,
-) {
- let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
- commands
- .spawn_bundle(Text2dBundle {
- text: Text::from_section(
- "Let's rotate the hue!",
- TextStyle {
- font,
- font_size: 32.0,
- color: Color::WHITE,
- },
- )
- .with_alignment(TextAlignment::CENTER),
- ..Default::default()
- })
- .insert(Level);
-
- spawn_platforms(
- commands,
- meshes,
- materials,
- [(
- Transform::from_xyz(0.0, -256.0, 0.0),
- Vec2 { x: 800.0, y: 16.0 },
- )],
- );
-
- spawn_characters(
- commands,
- character_meshes,
- materials,
- audio,
- [
- (Transform::from_xyz(0., -192., 0.), Color::RED),
- (Transform::from_xyz(-128., -192., 0.), Color::RED),
- (Transform::from_xyz(128., -192., 0.), Color::RED),
- ],
- );
-
- spawn_rotating_filter(
- commands,
- asset_server,
- Transform::from_xyz(0., -64., 2.),
- 45.,
- );
-}
diff --git a/src/main.rs b/src/main.rs
index 6cb80d7..410492a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ use bevy::{
prelude::*,
window::{WindowId, WindowMode},
};
+use bevy_common_assets::json::JsonAssetPlugin;
use bevy_rapier2d::prelude::*;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -42,6 +43,9 @@ fn main() {
.insert_resource(game::FirstLevel(first_level))
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins)
+ .add_plugin(JsonAssetPlugin::<levels::StoredLevels>::new(&[
+ "levels.json",
+ ]))
.add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(64.0))
//.add_plugin(RapierDebugRenderPlugin::default())
.add_plugin(menu::MenuPlugin)
@@ -59,11 +63,9 @@ fn setup(mut commands: Commands, mut windows: ResMut<Windows>, asset_server: Res
.unwrap()
.set_title(String::from("Bevyjam"));
- let font: Handle<Font> = asset_server.load("UacariLegacy-Thin.ttf");
- commands.insert_resource(font);
-
- let bevy_icon: Handle<Image> = asset_server.load("bevy.png");
- commands.insert_resource(bevy_icon);
+ commands.insert_resource(asset_server.load::<levels::StoredLevels, _>("game.levels.json"));
+ commands.insert_resource(asset_server.load::<Font, _>("UacariLegacy-Thin.ttf"));
+ commands.insert_resource(asset_server.load::<Image, _>("bevy.png"));
commands.spawn_bundle(Camera2dBundle::default());
commands.insert_resource(AmbientLight {