Compare commits

..

13 commits

Author SHA1 Message Date
1d22c11cde
Levels stored outside program 2022-08-25 19:55:34 +02:00
21d98a4a1d
cli: skip to level 2022-08-25 15:52:28 +02:00
445f3850ca
Tutorial levels 2022-08-25 15:39:16 +02:00
f117b2750a
Prevent mid-air jump 2022-08-25 14:47:20 +02:00
e7f2188091
Spawn platforms function 2022-08-25 12:32:06 +02:00
10abb0a676
Reset level 2022-08-25 11:31:12 +02:00
56fea25a87
Filter spawn functions 2022-08-25 09:55:39 +02:00
5ac4b4711c
Rotating filter 2022-08-25 09:30:47 +02:00
05ecbbbbec
Fullscreen & exit controls 2022-08-25 08:20:13 +02:00
2a4625269f
Color filter 2022-08-24 23:11:59 +02:00
b42a032cfd
fix: character fusion without player 2022-08-24 18:51:23 +02:00
ec7d04c8c8 Audio: switch, fusion 2022-08-24 17:43:46 +02:00
297811e882 Platform material & filter bundle 2022-08-24 17:39:31 +02:00
16 changed files with 769 additions and 345 deletions

149
Cargo.lock generated
View file

@ -208,9 +208,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]] [[package]]
name = "bevy" name = "bevy"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55f08528a4e59d607460513a823b40f602d013c1a00f57b824f1075d5d48c3cd" checksum = "fea147ef1ebb92d41294cfad804c40de151b174c711ce6e0a4a40eba23eae1a4"
dependencies = [ dependencies = [
"bevy_internal", "bevy_internal",
] ]
@ -241,9 +241,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_animation" name = "bevy_animation"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e243169af495ad616ff701247c0d3e40078a26ed8de231cf9e54bde6b3c4bb45" checksum = "a4365465fca7bd78295eb81d0a04afc049399852793d562eb017849bb5d6c55e"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_asset", "bevy_asset",
@ -259,9 +259,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_app" name = "bevy_app"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d26d6ffdf493609d2fedc1018a2ef0cb4d8e48f6d3bcea56fa2df81867e464" checksum = "9e4ae0a6ed2adf3b153511b4645241660a93f747c05ecd1e5a909dafc803cad4"
dependencies = [ dependencies = [
"bevy_derive", "bevy_derive",
"bevy_ecs", "bevy_ecs",
@ -274,9 +274,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_asset" name = "bevy_asset"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d8fb95306d5f18fa70df40632cd984993aeb71e91ce059ae99699098a4f9ce9" checksum = "2ec773c861a7e9d9978771f59f385500ec6da3a1ab5487705cddb054393d3d19"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bevy_app", "bevy_app",
@ -302,9 +302,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_audio" name = "bevy_audio"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee08ac575397ce17477dd291862bafa15226334bdfb82c02bbc3d10bad7bdb8" checksum = "1e5cf4713a24f318841f73a9e030854cfd5bad46bc81fa1acc9590cdab053c6f"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bevy_app", "bevy_app",
@ -317,10 +317,22 @@ dependencies = [
] ]
[[package]] [[package]]
name = "bevy_core" name = "bevy_common_assets"
version = "0.8.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6712146d54fff9e1865362e9f39a7b63c7b037ddb72a3d7bb05b959213fb61e" checksum = "3f7be9ee39085d8319d5cd853447b0b5c4f8b4bfd647aec91e2bd996e9db67ef"
dependencies = [
"anyhow",
"bevy",
"serde",
"serde_json",
]
[[package]]
name = "bevy_core"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c53172003d5cde7780870b5403c66c8ede3581faf3e510e916d8b4baa5b538d2"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -333,9 +345,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_core_pipeline" name = "bevy_core_pipeline"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080bb00399b6d7697e505f871d67c6de8b52eb06b47b0cda2be80c2396805983" checksum = "5e60efd10d593f6d122f2687f74c09ad55835a8f999c35bed6380ddd8e6ff7f2"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_asset", "bevy_asset",
@ -351,9 +363,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_derive" name = "bevy_derive"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4b8f0786d1fc7e0d35297917be463db3d0886f7bd8d4221ca3d565502579ffb" checksum = "0e6345431bbe6d7b6c165cd860ecd0b35da929779571259c5df970ac256d45f9"
dependencies = [ dependencies = [
"bevy_macro_utils", "bevy_macro_utils",
"quote", "quote",
@ -362,9 +374,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_diagnostic" name = "bevy_diagnostic"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adab74ee5375fbf5d2b1d3da41de8d1491a8a706d17441b5e31214b65349d692" checksum = "58ac9f4c2815f412be4b6e21e4b299cdafa710f651d064f6d40b2a8377a0d17c"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -375,9 +387,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_ecs" name = "bevy_ecs"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5643dc27b7d6778e3a66c8e0f6ad1fd33309aa2fa61d935f360ccc85b7be6a2" checksum = "c174066a24ed8a14d15ea58b0aea1c1f5c763f4bb36ebdc2b1dc78026007d0f5"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"bevy_ecs_macros", "bevy_ecs_macros",
@ -394,9 +406,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_ecs_macros" name = "bevy_ecs_macros"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5f2f12677f8725d40930d0a19652f007fe0ef5ac38e23817cfc4930c61f5680" checksum = "cc50c39e49e8febccc74e8e731680adb0cb4aef1f53275740cbaa95c6da71f4f"
dependencies = [ dependencies = [
"bevy_macro_utils", "bevy_macro_utils",
"proc-macro2", "proc-macro2",
@ -419,9 +431,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_encase_derive" name = "bevy_encase_derive"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a767adc36ce1fc917a736843b026d4de7069d90ed5e669c852481ef69fd5aa" checksum = "68bc194009c5e9b97da64a08142dd183c264885d99c985cf849868103018adf1"
dependencies = [ dependencies = [
"bevy_macro_utils", "bevy_macro_utils",
"encase_derive_impl", "encase_derive_impl",
@ -429,9 +441,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_gilrs" name = "bevy_gilrs"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963940426127533164af2a556971a03c493143c0afb95afadb4a070b6ab8c3df" checksum = "cb15a3427d9707be92b457e5d66900b02d853b475c21dd8662bdda387ba9f24e"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -442,9 +454,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_gltf" name = "bevy_gltf"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "150cc6782c4472600c2ade5d78c6ce481c992690f0499e63765aba752d7e0f04" checksum = "79db7d7e71b47a69953fbe8407ded5c6308eaeecf9a05efd5dfb42992f400a16"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@ -471,9 +483,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_hierarchy" name = "bevy_hierarchy"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2e4c20d7c843cd26ef3c5d7b4c20e3e32c275943e2437ecaca1cfd6cfe3b30" checksum = "5eb1ec76099ea5a716de08ea42ff41f036ebe2502df1d569168b58f16458a85e"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -484,9 +496,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_input" name = "bevy_input"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11c70573fb4d4c056ba32cfa553daa7e6e1245cb876ccfbe322640928b7ee1c" checksum = "1821c4b760ba6ddb4fe61806e9cc33f40b09a884557aca4553a29b8c7d73c6b4"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -496,9 +508,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_internal" name = "bevy_internal"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d603b597772130782eab6e604706cbb764fb037f9cf0a1904b6f342845b6f44" checksum = "ee63ad1e3f95a26ff2c227fadb1534a7bfe3a098e0e45c347f2f2575a573d9bc"
dependencies = [ dependencies = [
"bevy_animation", "bevy_animation",
"bevy_app", "bevy_app",
@ -534,9 +546,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_log" name = "bevy_log"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cafb12fc84734236e36f407ab62c72d5d4279fa4777e40a95d7cc973cbabcd1" checksum = "092daf498887814a064331dfcd1cf487a5ddab01fd38629b84a35b8b664462a1"
dependencies = [ dependencies = [
"android_log-sys", "android_log-sys",
"bevy_app", "bevy_app",
@ -549,9 +561,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_macro_utils" name = "bevy_macro_utils"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d081af83b701e16cad209255ba6b383744dfa49efa99eb6505989f293305ab3" checksum = "43fb5137e5198302d7c6c33d1e454cf48a586e7c6fd12f4860f12863951e16b9"
dependencies = [ dependencies = [
"quote", "quote",
"syn", "syn",
@ -560,18 +572,18 @@ dependencies = [
[[package]] [[package]]
name = "bevy_math" name = "bevy_math"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db5394e86c5708d3aa506c6e98ec4ed2a4083a7a018c6693d9ac0e77ebfabfc2" checksum = "531f2b90c7e861a96f418b3d560131b3354c5e67a67eba3953a45a56ea0114d2"
dependencies = [ dependencies = [
"glam", "glam",
] ]
[[package]] [[package]]
name = "bevy_mikktspace" name = "bevy_mikktspace"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b299a61175a6f7e7398f83cd5b50920fd8bad4df674e614ad94f25f8426509" checksum = "941e7d3d4e1dbb735f040e4cdc1558be1d3c38d43f1d9fdbb039c39a7849a00b"
dependencies = [ dependencies = [
"glam", "glam",
] ]
@ -599,9 +611,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_ptr" name = "bevy_ptr"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d92d5679e89602a18682a37846573dcd1979410179e14204280460ba9fb8713a" checksum = "9960c19e582b43cebe1894b6679520a4f50802d1cc5b6fa432f8d685ed232f09"
[[package]] [[package]]
name = "bevy_rapier2d" name = "bevy_rapier2d"
@ -618,9 +630,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_reflect" name = "bevy_reflect"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08798e67f2d4e6898ef117d8c99cf3b50a8eebc8da4159e6dad2657a0fbe9a4e" checksum = "3fc689dd7a7df3b3768884a4754711d406aa302ea48da483c03b52715fa95045"
dependencies = [ dependencies = [
"bevy_ptr", "bevy_ptr",
"bevy_reflect_derive", "bevy_reflect_derive",
@ -637,9 +649,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_reflect_derive" name = "bevy_reflect_derive"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19209a7f0238053802b7de04e6724bd90d4ed7d90e87101dbd1b64cca64ff694" checksum = "8c36fa5100832c787c10558d31632ddc454c221e8dfacbbef836938f59614754"
dependencies = [ dependencies = [
"bevy_macro_utils", "bevy_macro_utils",
"proc-macro2", "proc-macro2",
@ -650,9 +662,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_render" name = "bevy_render"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb49530388ef17cff3fb8bd5e47372fb3cfeb4befc73e3036f6462ac20f049ef" checksum = "600bcef85c7efac6e38ed725707f0e5b7c59b510430034ba2f743f472493f845"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bevy_app", "bevy_app",
@ -693,9 +705,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_render_macros" name = "bevy_render_macros"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d0b7a51fa819c20c64f43856c5aaea40f853050bbb09b9ba3672e5ff2688a5" checksum = "1be90adc9e5d5808833e363670818da5fe68ccafd7ca983a457f90957d2a430b"
dependencies = [ dependencies = [
"bevy_macro_utils", "bevy_macro_utils",
"proc-macro2", "proc-macro2",
@ -705,9 +717,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_scene" name = "bevy_scene"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0064d73ebb0de39901478b493604a1a6d448fd337b66803004c60f41f1fa6c37" checksum = "a045d575d2c8f776d8ea965363c81660243fefbfc3712ead938b00dfd6797216"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bevy_app", "bevy_app",
@ -753,9 +765,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_tasks" name = "bevy_tasks"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff874c91a36eaac3ef957c6f3b590fb71332d9d136671cc858847d56fe9f80a3" checksum = "719b753acb3d5b9dbfd77038560fe1893c17d4ee0a4242c2ee70da9d59430537"
dependencies = [ dependencies = [
"async-channel", "async-channel",
"async-executor", "async-executor",
@ -768,9 +780,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_text" name = "bevy_text"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef05a788c2c04aaa5db95b22a8f0fff0d3a0b08a7bcd1a71f050a628b38eec6e" checksum = "c265b7515faf55a3b92fd6ce0ab65dd246f247e11d737d6f5cdaf49c2be42c63"
dependencies = [ dependencies = [
"ab_glyph", "ab_glyph",
"anyhow", "anyhow",
@ -791,9 +803,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_time" name = "bevy_time"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74ec681d641371df81d7bfbcb0eea725ed873f38a094f34b5f7b436f0889e77c" checksum = "22830665b8476292b861216383fd79922aef2b540f9fd09d49144e3e5e94550e"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -804,9 +816,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_transform" name = "bevy_transform"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e1528e35f30bede46a50ee4134f150efc01f5c1002c340b3b2e6a0bfcb8aa5" checksum = "a4bb8760f03e9667e7499a5ceec1f7630fc3e45702781ac0df56cb969e8ae668"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -845,9 +857,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_utils" name = "bevy_utils"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bda6dada53e546845887ae7357eec57b8d547ef71627b716b33839b4a98b687" checksum = "f6e9aa1866c1cf7ee000f281ce9e90d02d701f5c7380a107252017e58e2f5246"
dependencies = [ dependencies = [
"ahash", "ahash",
"getrandom", "getrandom",
@ -874,9 +886,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_winit" name = "bevy_winit"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57537a56ac4f4e1ffcad95227bcab37cd17b51770dacff82374a2d88be376322" checksum = "98b15fee4b75472e3441b0c7221467303e4ce59b342a94a328e447e7cdb5a43c"
dependencies = [ dependencies = [
"approx", "approx",
"bevy_app", "bevy_app",
@ -898,12 +910,15 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy-inspector-egui", "bevy-inspector-egui",
"bevy_common_assets",
"bevy_rapier2d", "bevy_rapier2d",
"cpal 0.14.0", "cpal 0.14.0",
"crossbeam-channel", "crossbeam-channel",
"hexodsp", "hexodsp",
"rand", "rand",
"rand_distr", "rand_distr",
"rapier2d",
"serde",
"ticktock", "ticktock",
] ]

View file

@ -6,12 +6,15 @@ license = "AGPL-3.0-only"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
bevy = "0.8.0" bevy = "0.8.1"
bevy_common_assets = { version = "0.3.0", features = ["json"] }
bevy-inspector-egui = "0.12.1" bevy-inspector-egui = "0.12.1"
bevy_rapier2d = "0.16.2" bevy_rapier2d = "0.16.2"
crossbeam-channel = "0.5.6" crossbeam-channel = "0.5.6"
rand = "0.8.5" rand = "0.8.5"
rand_distr = "0.4.3" rand_distr = "0.4.3"
rapier2d = "0.14.0"
serde = { version = "1.0.144", features = ["derive"] }
[target."cfg(not(target_arch = \"wasm32\"))".dependencies] [target."cfg(not(target_arch = \"wasm32\"))".dependencies]
cpal = "0.14.0" cpal = "0.14.0"

View file

@ -5,16 +5,19 @@
* **Move**: arrows * **Move**: arrows
* **Switch character**: Tab * **Switch character**: Tab
* **Level up**: Enter (when character is white) * **Level up**: Enter (when character is white)
* **Reset**: R
## TODO ## TODO
* name * name
* color filters * more filters
* despawn black characters
* despawn character when too far
* level design * level design
* (?) can jump only from a surface (no mid-air jump)
* (?) multiplayer * (?) multiplayer
* level reset
* more audio * more audio
* "jumpable" component to avoid jumping on sensors
* bug: in level2, move the blue character to win, then reset. The characters are lighter than expected.
## Build ## Build

BIN
assets/bevy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

119
assets/game.levels.json Normal file
View file

@ -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."
}
]
}
]
}

File diff suppressed because one or more lines are too long

View file

@ -2,6 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<title>Bevyjam</title>
</head> </head>
<body> <body>
<script type="module"> <script type="module">

View file

@ -33,9 +33,18 @@ pub fn setup(event_channel: Receiver<AudioMsg>) {
let color_mix_b_gain = color_mix.inp_param("gain3").unwrap(); let color_mix_b_gain = color_mix.inp_param("gain3").unwrap();
let jump_ad = NodeId::Ad(0); let jump_ad = NodeId::Ad(0);
let jump_ad_trig = jump_ad.inp_param("trig").unwrap(); let jump_ad_trig = jump_ad.inp_param("trig").unwrap();
let switch_ad = NodeId::Ad(1);
let switch_ad_trig = switch_ad.inp_param("trig").unwrap();
let fusion_ad = NodeId::Ad(2);
let fusion_ad_trig = fusion_ad.inp_param("trig").unwrap();
let fusion_ad2 = NodeId::Ad(3);
let fusion_ad2_trig = fusion_ad2.inp_param("trig").unwrap();
for (_tick, _now) in Clock::framerate(10.0).iter() { for (_tick, _now) in Clock::framerate(20.0).iter() {
matrix.set_param(jump_ad_trig, (0.0).into()); matrix.set_param(jump_ad_trig, (0.0).into());
matrix.set_param(switch_ad_trig, (0.0).into());
matrix.set_param(fusion_ad_trig, (0.0).into());
matrix.set_param(fusion_ad2_trig, (0.0).into());
if let Ok(msg) = event_channel.try_recv() { if let Ok(msg) = event_channel.try_recv() {
match msg { match msg {
@ -44,7 +53,12 @@ pub fn setup(event_channel: Receiver<AudioMsg>) {
matrix.set_param(color_mix_g_gain, g.into()); matrix.set_param(color_mix_g_gain, g.into());
matrix.set_param(color_mix_b_gain, b.into()); matrix.set_param(color_mix_b_gain, b.into());
} }
AudioMsg::Fusion => {
matrix.set_param(fusion_ad_trig, (1.0).into());
matrix.set_param(fusion_ad2_trig, (1.0).into());
}
AudioMsg::Jump => matrix.set_param(jump_ad_trig, (1.0).into()), AudioMsg::Jump => matrix.set_param(jump_ad_trig, (1.0).into()),
AudioMsg::Switch => matrix.set_param(switch_ad_trig, (1.0).into()),
} }
} }
} }

99
src/filters.rs Normal file
View file

@ -0,0 +1,99 @@
use crate::game::Level;
use bevy::prelude::{shape::Quad, *};
use bevy_rapier2d::prelude::*;
#[derive(Component)]
pub enum PassThroughFilter {
/// Absorbs this color (ignoring alpha)
Absorbing(Color),
/// Rotates hue by this angle (in degrees)
Rotating(f32),
}
impl PassThroughFilter {
pub fn apply(&self, color: Color) -> Color {
match self {
PassThroughFilter::Absorbing(filter_color) => Vec4::from(color)
.mul_add(
-Vec4::from(*filter_color) * Vec4::from([1., 1., 1., 0.]),
Vec4::from(color),
)
.into(),
PassThroughFilter::Rotating(filter_angle) => {
let mut hsla = color.as_hsla_f32();
hsla[0] = (hsla[0] + filter_angle) % 360.;
Color::hsla(hsla[0], hsla[1], hsla[2], hsla[3])
}
}
}
}
#[derive(Bundle)]
pub struct AbsorbingFilter {
#[bundle]
pub mesh: ColorMesh2dBundle,
pub collider: Collider,
pub sensor: Sensor,
pub filter: PassThroughFilter,
}
pub fn spawn_absorbing_filter(
commands: &mut Commands,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<ColorMaterial>>,
transform: Transform,
size: Vec2,
color: Color,
) {
let mut material_color = color;
material_color.set_a(0.5);
commands
.spawn_bundle(AbsorbingFilter {
mesh: ColorMesh2dBundle {
mesh: meshes.add(Mesh::from(Quad { size, flip: false })).into(),
material: materials.add(material_color.into()),
transform,
..Default::default()
},
collider: Collider::cuboid(size.x / 2., size.y / 2.),
sensor: Sensor,
filter: PassThroughFilter::Absorbing(color),
})
.insert(Level);
}
#[derive(Bundle)]
pub struct RotatingFilter {
#[bundle]
pub sprite: SpriteBundle,
pub collider: Collider,
pub sensor: Sensor,
pub filter: PassThroughFilter,
pub velocity: Velocity,
pub rigid_body: RigidBody,
}
pub fn spawn_rotating_filter(
commands: &mut Commands,
asset_server: &Res<AssetServer>,
transform: Transform,
angle: f32,
) {
commands
.spawn_bundle(RotatingFilter {
sprite: SpriteBundle {
texture: asset_server.get_handle("bevy.png"),
transform,
..Default::default()
},
collider: Collider::ball(32.),
sensor: Sensor,
filter: PassThroughFilter::Rotating(angle),
velocity: Velocity::angular(1.),
rigid_body: RigidBody::KinematicVelocityBased,
})
.insert(Level);
}

View file

@ -1,20 +1,28 @@
#![allow(clippy::precedence)] #![allow(clippy::precedence)]
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments)]
pub use crate::filters::*;
use crate::AppState; use crate::AppState;
use bevy::{ use bevy::{
ecs::system::EntityCommands,
input::{keyboard::KeyCode, Input}, input::{keyboard::KeyCode, Input},
prelude::{shape::Quad, *}, prelude::{shape::Quad, *},
sprite::Mesh2dHandle, ecs::system::EntityCommands, sprite::Mesh2dHandle,
}; };
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
use rapier2d::geometry::CollisionEventFlags;
pub enum AudioMsg { pub enum AudioMsg {
Color([f32; 3]), Color([f32; 3]),
Fusion,
Jump, Jump,
Switch,
} }
pub struct FirstLevel(pub LevelId);
#[derive(Clone, Copy, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct LevelId(pub u32); pub struct LevelId(pub u32);
@ -35,12 +43,14 @@ impl Plugin for GamePlugin {
.with_system(crate::levels::post_setup_level) .with_system(crate::levels::post_setup_level)
.with_system(change_character_system) .with_system(change_character_system)
.with_system(player_movement_system) .with_system(player_movement_system)
.with_system(level_keyboard_system)
.with_system(move_camera) .with_system(move_camera)
.with_system(character_particle_effect_system), .with_system(character_particle_effect_system),
) )
.add_system_set( .add_system_set(
SystemSet::on_update(AppState::Win) SystemSet::on_update(AppState::Win)
.with_system(player_movement_system) .with_system(player_movement_system)
.with_system(level_keyboard_system)
.with_system(move_camera) .with_system(move_camera)
.with_system(character_particle_effect_system), .with_system(character_particle_effect_system),
) )
@ -50,7 +60,7 @@ impl Plugin for GamePlugin {
// Events // Events
pub struct LevelStartupEvent(pub Entity); pub struct LevelStartupEvent;
// Resources // Resources
@ -85,52 +95,41 @@ pub struct CharacterColor(pub Color);
#[derive(Component)] #[derive(Component)]
pub struct Player; pub struct Player;
#[derive(Component)]
pub struct CollisionCount(usize);
// Systems // Systems
fn setup( fn setup(
mut commands: Commands, first_level: Res<FirstLevel>,
mut current_level: ResMut<CurrentLevel>, mut current_level: ResMut<CurrentLevel>,
mut level_startup_event: EventWriter<LevelStartupEvent>, mut level_startup_event: EventWriter<LevelStartupEvent>,
mut camera_query: Query<&mut Transform, With<Camera>>, mut camera_query: Query<&mut Transform, With<Camera>>,
) { ) {
let level_id = LevelId(current_level.0.map_or(0, |level_id| level_id.0 + 1)); if current_level.0.is_none() {
crate::levels::setup_level( current_level.0 = Some(first_level.0);
&mut commands,
&mut current_level,
&mut level_startup_event,
&mut camera_query,
level_id,
);
} }
pub fn spawn_characters( crate::levels::setup_level(&mut level_startup_event, &mut camera_query);
}
pub fn spawn_characters<I: IntoIterator<Item = (Transform, Color)>>(
commands: &mut Commands, commands: &mut Commands,
character_meshes: &Res<CharacterMeshes>, character_meshes: &Res<CharacterMeshes>,
materials: &mut ResMut<Assets<ColorMaterial>>, materials: &mut ResMut<Assets<ColorMaterial>>,
audio: &Res<crossbeam_channel::Sender<AudioMsg>>, audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
transforms: &mut Vec<Transform>, characters: I,
colors: &Vec<Color>,
) { ) {
for (i, (transform, color)) in characters.into_iter().enumerate() {
spawn_character( spawn_character(
commands, commands,
character_meshes, character_meshes,
materials, materials,
audio, audio,
transforms[0], transform,
colors[0], color,
true, i == 0,
);
for c in 1 .. transforms.len() {
spawn_character(
commands,
character_meshes,
materials,
audio,
transforms[c],
colors[c],
false,
); );
} }
} }
@ -160,7 +159,6 @@ pub fn spawn_character(
.insert(CharacterColor(color)) .insert(CharacterColor(color))
.insert(RigidBody::Dynamic) .insert(RigidBody::Dynamic)
.insert(Collider::cuboid(32., 32.)) .insert(Collider::cuboid(32., 32.))
.insert(ExternalForce::default())
.insert(Velocity::default()) .insert(Velocity::default())
.insert(GravityScale(10.0)) .insert(GravityScale(10.0))
.insert(LockedAxes::ROTATION_LOCKED) .insert(LockedAxes::ROTATION_LOCKED)
@ -169,8 +167,16 @@ pub fn spawn_character(
linear_damping: 0.5, linear_damping: 0.5,
angular_damping: 0.5, angular_damping: 0.5,
}) })
.insert(ExternalImpulse::default()) .insert(ActiveEvents::COLLISION_EVENTS)
.insert(ActiveEvents::COLLISION_EVENTS); .with_children(|c| {
c.spawn_bundle(TransformBundle::from_transform(Transform::from_xyz(
0., -33., 0.,
)))
.insert(Sensor)
.insert(Collider::cuboid(30., 0.5))
.insert(ActiveEvents::COLLISION_EVENTS)
.insert(CollisionCount(0));
});
if is_player { if is_player {
entity_commands.insert(Player); entity_commands.insert(Player);
@ -180,24 +186,62 @@ pub fn spawn_character(
} }
} }
pub fn spawn_platforms<I: IntoIterator<Item = (Transform, Vec2)>>(
commands: &mut Commands,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<ColorMaterial>>,
platforms: I,
) {
for (transform, size) in platforms.into_iter() {
spawn_platform(commands, meshes, materials, transform, size);
}
}
pub fn spawn_platform(
commands: &mut Commands,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<ColorMaterial>>,
transform: Transform,
size: Vec2,
) {
commands
.spawn_bundle(ColorMesh2dBundle {
mesh: meshes.add(Mesh::from(Quad { size, flip: false })).into(),
material: materials.add(ColorMaterial::from(Color::GRAY)),
transform,
..default()
})
.insert(Collider::cuboid(size.x / 2., size.y / 2.))
.insert(Level);
}
fn collision_event_system( fn collision_event_system(
mut commands: Commands, mut commands: Commands,
character_meshes: Res<CharacterMeshes>, character_meshes: Res<CharacterMeshes>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
mut collision_events: EventReader<CollisionEvent>, mut collision_events: EventReader<CollisionEvent>,
character_query: Query<(&CharacterColor, &Transform, Option<&Player>)>, mut character_query: Query<(
&mut CharacterColor,
&Transform,
&mut Handle<ColorMaterial>,
Option<&Player>,
)>,
pass_through_filter_query: Query<&PassThroughFilter>,
mut collision_counter_query: Query<&mut CollisionCount>,
mut app_state: ResMut<State<AppState>>, mut app_state: ResMut<State<AppState>>,
audio: Res<crossbeam_channel::Sender<AudioMsg>>, audio: Res<crossbeam_channel::Sender<AudioMsg>>,
) { ) {
for collision_event in collision_events.iter() { for collision_event in collision_events.iter() {
if let CollisionEvent::Started(e1, e2, flags) = collision_event { match collision_event {
CollisionEvent::Started(e1, e2, flags) => {
if flags.is_empty() { if flags.is_empty() {
if let ( if let (
Ok((c1_color, c1_transform, c1_player)), Ok((c1_color, c1_transform, _c1_material, c1_player)),
Ok((c2_color, c2_transform, c2_player))) = Ok((c2_color, c2_transform, _c2_material, c2_player)),
(character_query.get(*e1), character_query.get(*e2)) ) = (character_query.get(*e1), character_query.get(*e2))
{ {
// TODO completely remove particles
commands.entity(*e1).despawn_recursive(); commands.entity(*e1).despawn_recursive();
commands.entity(*e2).despawn_recursive(); commands.entity(*e2).despawn_recursive();
@ -212,26 +256,87 @@ fn collision_event_system(
} }
// position character based on current player location // position character based on current player location
if let Some(_player) = c1_player {
spawn_character( spawn_character(
&mut commands, &mut commands,
&character_meshes, &character_meshes,
&mut materials, &mut materials,
&audio, &audio,
*c1_transform, if c1_player.is_some() {
*c1_transform
} else if c2_player.is_some() {
*c2_transform
} else {
Transform::identity().with_translation(
(c1_transform.translation + c2_transform.translation) / 2.,
)
},
new_color.into(), new_color.into(),
true, c1_player.is_some() || c2_player.is_some(),
);
} else if let Some(_player) = c2_player {
spawn_character(
&mut commands,
&character_meshes,
&mut materials,
&audio,
*c2_transform,
new_color.into(),
true,
); );
audio.send(AudioMsg::Fusion).ok();
}
} else if *flags == CollisionEventFlags::SENSOR {
if let (Ok((mut c_color, _c_transform, mut c_material, c_player)), Ok(filter)) = (
character_query.get_mut(*e1),
pass_through_filter_query.get(*e2),
) {
c_color.0 = filter.apply(c_color.0);
*c_material = materials.add(ColorMaterial::from(c_color.0));
if c_player.is_some() {
audio
.send(AudioMsg::Color([
c_color.0.r(),
c_color.0.g(),
c_color.0.b(),
]))
.ok();
}
} else if let (
Ok((mut c_color, _c_transform, mut c_material, c_player)),
Ok(filter),
) = (
character_query.get_mut(*e2),
pass_through_filter_query.get(*e1),
) {
c_color.0 = filter.apply(c_color.0);
*c_material = materials.add(ColorMaterial::from(c_color.0));
if c_player.is_some() {
audio
.send(AudioMsg::Color([
c_color.0.r(),
c_color.0.g(),
c_color.0.b(),
]))
.ok();
}
} else if let (Ok(mut collision_count), Err(_)) = (
collision_counter_query.get_mut(*e1),
character_query.get_mut(*e2),
) {
collision_count.0 += 1;
} else if let (Ok(mut collision_count), Err(_)) = (
collision_counter_query.get_mut(*e2),
character_query.get_mut(*e1),
) {
collision_count.0 += 1;
}
}
}
CollisionEvent::Stopped(e1, e2, flags) => {
if *flags == CollisionEventFlags::SENSOR {
if let (Ok(mut collision_count), Err(_)) = (
collision_counter_query.get_mut(*e1),
character_query.get_mut(*e2),
) {
collision_count.0 = collision_count.0.saturating_sub(1);
} else if let (Ok(mut collision_count), Err(_)) = (
collision_counter_query.get_mut(*e2),
character_query.get_mut(*e1),
) {
collision_count.0 = collision_count.0.saturating_sub(1);
} }
} }
} }
@ -246,14 +351,16 @@ fn change_character_system(
characters: Query<(Entity, &CharacterColor, Option<&Player>)>, characters: Query<(Entity, &CharacterColor, Option<&Player>)>,
audio: Res<crossbeam_channel::Sender<AudioMsg>>, audio: Res<crossbeam_channel::Sender<AudioMsg>>,
) { ) {
if !keyboard_input.just_pressed(KeyCode::Tab) { return; } if !keyboard_input.just_pressed(KeyCode::Tab) {
return;
}
let mut player_idx: usize = 0; let mut player_idx: usize = 0;
let mut player_count: usize = 0; let mut player_count: usize = 0;
// find player idx // find player idx
for (_entity, _color, player) in characters.iter() { for (_entity, _color, player) in characters.iter() {
if let Some(_player) = player { if player.is_some() {
player_idx = player_count; player_idx = player_count;
} }
player_count += 1; player_count += 1;
@ -274,7 +381,7 @@ fn change_character_system(
audio audio
.send(AudioMsg::Color([color.0.r(), color.0.g(), color.0.b()])) .send(AudioMsg::Color([color.0.r(), color.0.g(), color.0.b()]))
.ok(); .ok();
audio.send(AudioMsg::Jump).ok(); audio.send(AudioMsg::Switch).ok();
} }
player_count += 1; player_count += 1;
@ -283,53 +390,69 @@ fn change_character_system(
fn player_movement_system( fn player_movement_system(
keyboard_input: Res<Input<KeyCode>>, keyboard_input: Res<Input<KeyCode>>,
mut characters: Query<&mut Velocity, With<Player>>, mut characters: Query<(&mut Velocity, &Children), With<Player>>,
mut app_state: ResMut<State<AppState>>, collision_counter_query: Query<&CollisionCount>,
audio: Res<crossbeam_channel::Sender<AudioMsg>>, audio: Res<crossbeam_channel::Sender<AudioMsg>>,
) { ) {
let right_pressed: bool = let right_pressed: bool =
keyboard_input.pressed(KeyCode::Right) || keyboard_input.pressed(KeyCode::D); keyboard_input.pressed(KeyCode::Right) || keyboard_input.pressed(KeyCode::D);
let left_pressed: bool = let left_pressed: bool =
keyboard_input.pressed(KeyCode::Left) || keyboard_input.pressed(KeyCode::A); keyboard_input.pressed(KeyCode::Left) || keyboard_input.pressed(KeyCode::A);
for mut velocity in characters.iter_mut() { for (mut velocity, children) in characters.iter_mut() {
velocity.linvel.x = 200. * (right_pressed as i8 - left_pressed as i8) as f32; velocity.linvel.x = 200. * (right_pressed as i8 - left_pressed as i8) as f32;
if keyboard_input.just_pressed(KeyCode::Space) { if keyboard_input.just_pressed(KeyCode::Space)
&& collision_counter_query.get(children[0]).unwrap().0 != 0
{
audio.send(AudioMsg::Jump).ok(); audio.send(AudioMsg::Jump).ok();
velocity.linvel.y = 500.; velocity.linvel.y = 700.;
} }
} }
if app_state.current() == &AppState::Win && keyboard_input.just_pressed(KeyCode::Return) {
app_state.replace(AppState::Game).unwrap();
}
} }
fn character_particle_effect_system( fn character_particle_effect_system(
characters: Query<(&Transform, &CharacterColor), With<Player>>, player_character: Query<(&Transform, &CharacterColor), With<Player>>,
mut particle_effect: ResMut<crate::particle_effect::ParticleEffectResource>, mut particle_effect: ResMut<crate::particle_effect::ParticleEffectResource>,
) { ) {
for (transform, color) in characters.iter() { if let Ok((transform, color)) = player_character.get_single() {
particle_effect.translation = transform.translation; particle_effect.translation = transform.translation;
particle_effect.color = color.0; particle_effect.color = color.0;
} }
} }
fn win_setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn win_setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
asset_server: Res<AssetServer>,
) {
let font = asset_server.get_handle("UacariLegacy-Thin.ttf"); let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
commands
.spawn_bundle(ColorMesh2dBundle {
mesh: meshes
.add(Mesh::from(Quad {
size: Vec2 { x: 512., y: 64. },
flip: false,
}))
.into(),
material: materials.add(ColorMaterial::from(Color::rgba(0., 0., 0., 0.9))),
transform: Transform::from_xyz(0., 0., 3.),
..default()
})
.insert(Level);
commands commands
.spawn_bundle(Text2dBundle { .spawn_bundle(Text2dBundle {
text: Text::from_section( text: Text::from_section(
"Press ENTER to level up", "Press ENTER to level up",
TextStyle { TextStyle {
font, font,
font_size: 32.0, font_size: 36.0,
color: Color::WHITE, color: Color::WHITE,
}, },
) )
.with_alignment(TextAlignment::CENTER), .with_alignment(TextAlignment::CENTER),
transform: Transform::from_xyz(0., 0., 4.),
..Default::default() ..Default::default()
}) })
.insert(Level); .insert(Level);
@ -344,13 +467,13 @@ fn move_camera(
const FOLLOW_SPEED: f32 = std::f32::consts::PI; const FOLLOW_SPEED: f32 = std::f32::consts::PI;
for character_transform in characters.iter() { for character_transform in characters.iter() {
let (camera, mut camera_transform) = let (camera, mut camera_transform) = camera_query.single_mut();
camera_query.single_mut();
let size: Vec2 = camera.logical_viewport_size().unwrap(); let size: Vec2 = camera.logical_viewport_size().unwrap();
let half_height: f32 = size.y * 0.5; let half_height: f32 = size.y * 0.5;
camera_transform.translation = camera_transform.translation.lerp( camera_transform.translation = camera_transform.translation.lerp(
character_transform.translation, time.delta_seconds() * FOLLOW_SPEED character_transform.translation,
time.delta_seconds() * FOLLOW_SPEED,
); );
// prevent camera from going too low // prevent camera from going too low
@ -359,3 +482,29 @@ fn move_camera(
camera_transform.translation.z = 999.0; camera_transform.translation.z = 999.0;
} }
} }
fn level_keyboard_system(
mut commands: Commands,
mut current_level: ResMut<CurrentLevel>,
mut level_startup_event: EventWriter<LevelStartupEvent>,
mut camera_query: Query<&mut Transform, With<Camera>>,
keyboard_input: Res<Input<KeyCode>>,
level_query: Query<Entity, With<Level>>,
mut app_state: ResMut<State<AppState>>,
) {
if app_state.current() == &AppState::Win && keyboard_input.just_pressed(KeyCode::Return) {
current_level.0 = Some(LevelId(
current_level.0.map_or(0, |level_id| level_id.0 + 1),
));
app_state.replace(AppState::Game).unwrap();
}
if keyboard_input.just_pressed(KeyCode::R) {
for entity in level_query.iter() {
commands.entity(entity).despawn_recursive();
}
if app_state.replace(AppState::Game).is_err() {
crate::levels::setup_level(&mut level_startup_event, &mut camera_query);
}
}
}

View file

@ -1,29 +1,17 @@
#![allow(clippy::too_many_arguments)] #![allow(clippy::too_many_arguments)]
mod game_over;
mod level0;
mod level1;
use crate::game::*; use crate::game::*;
use bevy::prelude::*; use bevy::{prelude::*, reflect::TypeUuid};
use serde::{Deserialize, Serialize};
pub fn setup_level( pub fn setup_level(
commands: &mut Commands,
current_level: &mut ResMut<CurrentLevel>,
level_startup_event: &mut EventWriter<LevelStartupEvent>, level_startup_event: &mut EventWriter<LevelStartupEvent>,
camera_query: &mut Query<&mut Transform, With<Camera>>, camera_query: &mut Query<&mut Transform, With<Camera>>,
level_id: LevelId,
) { ) {
let level_entity = commands
.spawn()
.insert(Level)
.id();
current_level.0 = Some(level_id);
camera_query.single_mut().translation = Default::default(); camera_query.single_mut().translation = Default::default();
level_startup_event.send(LevelStartupEvent(level_entity)); level_startup_event.send(LevelStartupEvent);
} }
pub fn despawn_level(mut commands: Commands, level_query: Query<Entity, With<Level>>) { pub fn despawn_level(mut commands: Commands, level_query: Query<Entity, With<Level>>) {
@ -36,29 +24,158 @@ pub fn despawn_level(mut commands: Commands, level_query: Query<Entity, With<Lev
pub fn post_setup_level( pub fn post_setup_level(
mut commands: Commands, mut commands: Commands,
character_meshes: Res<CharacterMeshes>, character_meshes: Res<CharacterMeshes>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
current_level: Res<CurrentLevel>, current_level: Res<CurrentLevel>,
mut level_startup_event: EventReader<LevelStartupEvent>, mut level_startup_event: EventReader<LevelStartupEvent>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
audio: Res<crossbeam_channel::Sender<AudioMsg>>, audio: Res<crossbeam_channel::Sender<AudioMsg>>,
stored_levels_assets: Res<Assets<StoredLevels>>,
stored_levels_handle: Res<Handle<StoredLevels>>,
) { ) {
for LevelStartupEvent(level_entity) in level_startup_event.iter() { for _ in level_startup_event.iter() {
if let Some(level_id) = current_level.0 { if let Some(level_id) = current_level.0 {
match level_id.0 { if let Some(stored_level) = stored_levels_assets
0 => level0::setup( .get(&stored_levels_handle)
.unwrap()
.levels
.get(level_id.0 as usize)
{
spawn_stored_level(
&mut commands, &mut commands,
&character_meshes, &character_meshes,
&mut meshes,
&mut materials, &mut materials,
&asset_server,
&audio, &audio,
), stored_level,
1 => level1::setup( );
&mut commands,
&character_meshes,
&mut materials,
&audio,
),
_ => game_over::setup(&mut commands, &asset_server),
} }
} }
} }
} }
#[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);
}
}

View file

@ -1,36 +0,0 @@
use crate::game::*;
use bevy::prelude::*;
pub fn setup(commands: &mut Commands, asset_server: &Res<AssetServer>) {
let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
commands
.spawn_bundle(Text2dBundle {
text: Text::from_section(
"GAME OVER",
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);
}

View file

@ -1,46 +0,0 @@
use crate::game::*;
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
pub fn setup(
commands: &mut Commands,
character_meshes: &Res<CharacterMeshes>,
materials: &mut ResMut<Assets<ColorMaterial>>,
// selected_character_id: &mut Mut<SelectedCharacterId>,
// character_id_list: &mut Mut<CharacterIdList>,
audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
) {
commands
.spawn_bundle(TransformBundle::from(Transform::from_xyz(0.0, -256.0, 0.0)))
.insert(Collider::cuboid(400., 10.))
.insert(Level);
spawn_character(
commands,
character_meshes,
materials,
audio,
Transform::from_xyz(-128., -64., 0.),
Color::RED,
true,
);
spawn_character(
commands,
character_meshes,
materials,
audio,
Transform::from_xyz(0., -64., 0.),
Color::GREEN,
false,
);
spawn_character(
commands,
character_meshes,
materials,
audio,
Transform::from_xyz(128., -64., 0.),
Color::BLUE,
false,
);
}

View file

@ -1,50 +0,0 @@
use crate::game::*;
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
pub fn setup(
commands: &mut Commands,
character_meshes: &Res<CharacterMeshes>,
materials: &mut ResMut<Assets<ColorMaterial>>,
audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
) {
commands
.spawn_bundle(TransformBundle::from(Transform::from_xyz(0.0, -256.0, 0.0)))
.insert(Collider::cuboid(400., 10.))
.insert(Level);
commands
.spawn_bundle(TransformBundle::from(Transform::from_xyz(
256.0, -128.0, 0.0,
)))
.insert(Collider::cuboid(200., 10.))
.insert(Level);
spawn_character(
commands,
character_meshes,
materials,
audio,
Transform::from_xyz(128., 64., 0.),
Color::BLUE,
true,
);
spawn_character(
commands,
character_meshes,
materials,
audio,
Transform::from_xyz(-128., -128., 0.),
Color::RED,
false,
);
spawn_character(
commands,
character_meshes,
materials,
audio,
Transform::from_xyz(0., -128., 0.),
Color::GREEN,
false,
);
}

View file

@ -1,11 +1,16 @@
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
mod audio; mod audio;
mod filters;
mod game; mod game;
mod levels; mod levels;
mod menu; mod menu;
mod particle_effect; mod particle_effect;
use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*}; use bevy::{
prelude::*,
window::{WindowId, WindowMode},
};
use bevy_common_assets::json::JsonAssetPlugin;
use bevy_rapier2d::prelude::*; use bevy_rapier2d::prelude::*;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@ -22,35 +27,66 @@ fn main() {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
std::thread::spawn(move || audio::setup(audio_event_receiver)); std::thread::spawn(move || audio::setup(audio_event_receiver));
#[cfg(not(target_arch = "wasm32"))]
let first_level = game::LevelId(
std::env::args()
.nth(1)
.map_or(0, |s| s.parse().unwrap_or(0)),
);
#[cfg(target_arch = "wasm32")]
let first_level = game::LevelId(0);
App::new() App::new()
.insert_resource(Msaa { samples: 4 })
.insert_resource(audio_event_sender) .insert_resource(audio_event_sender)
.add_state(AppState::Menu) .add_state(AppState::Menu)
.insert_resource(game::FirstLevel(first_level))
.insert_resource(ClearColor(Color::BLACK))
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(JsonAssetPlugin::<levels::StoredLevels>::new(&[
"levels.json",
]))
.add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(64.0)) .add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(64.0))
.add_plugin(RapierDebugRenderPlugin::default()) //.add_plugin(RapierDebugRenderPlugin::default())
.add_plugin(menu::MenuPlugin) .add_plugin(menu::MenuPlugin)
.add_plugin(game::GamePlugin) .add_plugin(game::GamePlugin)
.add_plugin(particle_effect::ParticleEffectPlugin) .add_plugin(particle_effect::ParticleEffectPlugin)
.add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new()) //.add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new())
.add_system(keyboard_util_system)
.add_startup_system(setup) .add_startup_system(setup)
.run(); .run();
} }
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, mut windows: ResMut<Windows>, asset_server: Res<AssetServer>) {
#[cfg(not(target_arch = "wasm32"))] windows
let font: Handle<Font> = asset_server.load("UacariLegacy-Thin.ttf"); .get_mut(WindowId::primary())
#[cfg(target_arch = "wasm32")] .unwrap()
let font: Handle<Font> = asset_server.load("UacariLegacy-Thin.ttf"); .set_title(String::from("Bevyjam"));
commands.insert_resource(font);
commands.spawn_bundle(Camera2dBundle { commands.insert_resource(asset_server.load::<levels::StoredLevels, _>("game.levels.json"));
camera_2d: Camera2d { commands.insert_resource(asset_server.load::<Font, _>("UacariLegacy-Thin.ttf"));
clear_color: ClearColorConfig::Custom(Color::BLACK), commands.insert_resource(asset_server.load::<Image, _>("bevy.png"));
},
..Default::default() commands.spawn_bundle(Camera2dBundle::default());
});
commands.insert_resource(AmbientLight { commands.insert_resource(AmbientLight {
color: Color::WHITE, color: Color::WHITE,
brightness: 0.6, brightness: 0.6,
}); });
} }
fn keyboard_util_system(keyboard_input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
#[cfg(not(target_arch = "wasm32"))]
{
if keyboard_input.just_released(KeyCode::Escape) {
std::process::exit(0);
}
if keyboard_input.just_pressed(KeyCode::F11) {
if let Some(window) = windows.get_primary_mut() {
window.set_mode(match window.mode() {
WindowMode::Windowed => WindowMode::Fullscreen,
_ => WindowMode::Windowed,
});
}
}
}
}

View file

@ -26,7 +26,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
"BEVYJAM", "BEVYJAM",
TextStyle { TextStyle {
font: font.clone(), font: font.clone(),
font_size: 48.0, font_size: 96.0,
color: Color::WHITE, color: Color::WHITE,
}, },
) )