Merge branch 'master' of https://git.txmn.tk/tuxmain/bevyjam into nixon-main
This commit is contained in:
commit
4fc53c04aa
6 changed files with 191 additions and 56 deletions
|
@ -17,7 +17,9 @@
|
|||
* (?) multiplayer
|
||||
* 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.
|
||||
* bug: in level2, move the blue character to win, then reset. The characters are lighter than expected. (also level 4)
|
||||
* redshift warning
|
||||
* itchio test
|
||||
|
||||
## Build
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
"characters": [
|
||||
{
|
||||
"pos": [
|
||||
-128.0,
|
||||
-160.0,
|
||||
-192.0
|
||||
],
|
||||
"color": [
|
||||
|
@ -159,7 +159,7 @@
|
|||
},
|
||||
{
|
||||
"pos": [
|
||||
128.0,
|
||||
160.0,
|
||||
-192.0
|
||||
],
|
||||
"color": [
|
||||
|
@ -219,6 +219,14 @@
|
|||
0.0
|
||||
],
|
||||
"font_size": 32.0,
|
||||
"text": "This filter absorbs light."
|
||||
},
|
||||
{
|
||||
"pos": [
|
||||
0.0,
|
||||
-64.0
|
||||
],
|
||||
"font_size": 32.0,
|
||||
"text": "Press R to reset."
|
||||
}
|
||||
]
|
||||
|
@ -297,6 +305,83 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"comment": "Melting platform tutorial",
|
||||
"characters": [
|
||||
{
|
||||
"pos": [
|
||||
-304.0,
|
||||
-208.0
|
||||
],
|
||||
"color": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7,
|
||||
1.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": [
|
||||
304.0,
|
||||
-208.0
|
||||
],
|
||||
"color": [
|
||||
0.3,
|
||||
0.3,
|
||||
0.3,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"platforms": [
|
||||
{
|
||||
"pos": [
|
||||
-304.0,
|
||||
-256.0
|
||||
],
|
||||
"size": [
|
||||
192.0,
|
||||
16.0
|
||||
]
|
||||
},
|
||||
{
|
||||
"pos": [
|
||||
304.0,
|
||||
-256.0
|
||||
],
|
||||
"size": [
|
||||
192.0,
|
||||
16.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"absorbing_filters": [],
|
||||
"rotating_filters": [],
|
||||
"melty_platforms": [
|
||||
{
|
||||
"pos": [
|
||||
0.0,
|
||||
-256.0
|
||||
],
|
||||
"color": [
|
||||
0.5,
|
||||
0.5,
|
||||
0.5,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
],
|
||||
"texts": [
|
||||
{
|
||||
"pos": [
|
||||
0.0,
|
||||
-64.0
|
||||
],
|
||||
"font_size": 32.0,
|
||||
"text": "Too much light\ncause some platforms to melt."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"comment": "First puzzle",
|
||||
"characters": [
|
||||
|
@ -564,16 +649,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"texts": [
|
||||
{
|
||||
"pos": [
|
||||
0.0,
|
||||
-64.0
|
||||
],
|
||||
"font_size": 32.0,
|
||||
"text": "Too much light\ncan cause some platforms to melt."
|
||||
}
|
||||
]
|
||||
"texts": []
|
||||
},
|
||||
{
|
||||
"comment": "Game over",
|
||||
|
|
91
src/game.rs
91
src/game.rs
|
@ -13,6 +13,7 @@ use bevy::{
|
|||
};
|
||||
use bevy_rapier2d::prelude::*;
|
||||
use rapier2d::geometry::CollisionEventFlags;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
pub enum AudioMsg {
|
||||
Color([f32; 3]),
|
||||
|
@ -33,6 +34,7 @@ impl Plugin for GamePlugin {
|
|||
app.add_event::<LevelStartupEvent>()
|
||||
.init_resource::<CharacterMeshes>()
|
||||
.insert_resource(CurrentLevel(None))
|
||||
.init_resource::<CharacterList>()
|
||||
.add_system_set(SystemSet::on_enter(AppState::Game).with_system(setup))
|
||||
.add_system_set(SystemSet::on_enter(AppState::Win).with_system(win_setup))
|
||||
.add_system_set(
|
||||
|
@ -52,7 +54,8 @@ impl Plugin for GamePlugin {
|
|||
.with_system(player_movement_system)
|
||||
.with_system(level_keyboard_system)
|
||||
.with_system(move_camera)
|
||||
.with_system(character_particle_effect_system),
|
||||
.with_system(character_particle_effect_system)
|
||||
.with_system(move_win_text_system),
|
||||
)
|
||||
.add_system_to_stage(CoreStage::PostUpdate, collision_event_system);
|
||||
}
|
||||
|
@ -66,6 +69,9 @@ pub struct LevelStartupEvent;
|
|||
|
||||
pub struct CurrentLevel(pub Option<LevelId>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CharacterList(pub BTreeSet<Entity>);
|
||||
|
||||
pub struct CharacterMeshes {
|
||||
square: Mesh2dHandle,
|
||||
}
|
||||
|
@ -101,6 +107,9 @@ pub struct CollisionCount(usize);
|
|||
#[derive(Component)]
|
||||
pub struct Melty(pub Color);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct WinText;
|
||||
|
||||
// Systems
|
||||
|
||||
fn setup(
|
||||
|
@ -121,6 +130,7 @@ pub fn spawn_characters<I: IntoIterator<Item = (Transform, Color)>>(
|
|||
character_meshes: &Res<CharacterMeshes>,
|
||||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||
audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
|
||||
character_list: &mut ResMut<CharacterList>,
|
||||
|
||||
characters: I,
|
||||
) {
|
||||
|
@ -133,6 +143,7 @@ pub fn spawn_characters<I: IntoIterator<Item = (Transform, Color)>>(
|
|||
character_meshes,
|
||||
materials,
|
||||
audio,
|
||||
character_list,
|
||||
{
|
||||
let mut new_transform: Transform = transform;
|
||||
new_transform.translation.z = curr_z;
|
||||
|
@ -150,6 +161,7 @@ pub fn spawn_character(
|
|||
character_meshes: &Res<CharacterMeshes>,
|
||||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||
audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
|
||||
character_list: &mut ResMut<CharacterList>,
|
||||
mut transform: Transform,
|
||||
color: Color,
|
||||
is_player: bool,
|
||||
|
@ -189,6 +201,8 @@ pub fn spawn_character(
|
|||
.insert(CollisionCount(0));
|
||||
});
|
||||
|
||||
character_list.0.insert(entity_commands.id());
|
||||
|
||||
if is_player {
|
||||
entity_commands.insert(Player);
|
||||
audio
|
||||
|
@ -277,6 +291,7 @@ fn collision_event_system(
|
|||
mut collision_counter_query: Query<&mut CollisionCount>,
|
||||
mut app_state: ResMut<State<AppState>>,
|
||||
audio: Res<crossbeam_channel::Sender<AudioMsg>>,
|
||||
mut character_list: ResMut<CharacterList>,
|
||||
) {
|
||||
for collision_event in collision_events.iter() {
|
||||
match collision_event {
|
||||
|
@ -287,6 +302,8 @@ fn collision_event_system(
|
|||
Ok((c2_color, c2_transform, _c2_material, c2_player)),
|
||||
) = (character_query.get(*e1), character_query.get(*e2))
|
||||
{
|
||||
character_list.0.remove(e1);
|
||||
character_list.0.remove(e2);
|
||||
commands.entity(*e1).despawn_recursive();
|
||||
commands.entity(*e2).despawn_recursive();
|
||||
|
||||
|
@ -294,8 +311,7 @@ fn collision_event_system(
|
|||
.clamp(Vec4::ZERO, Vec4::ONE);
|
||||
|
||||
// If color approximately white
|
||||
if app_state.current() == &AppState::Game
|
||||
&& Vec4::from(new_color).min_element() >= 0.9
|
||||
if app_state.current() == &AppState::Game && new_color.min_element() >= 0.9
|
||||
{
|
||||
app_state.replace(AppState::Win).ok();
|
||||
}
|
||||
|
@ -306,6 +322,7 @@ fn collision_event_system(
|
|||
&character_meshes,
|
||||
&mut materials,
|
||||
&audio,
|
||||
&mut character_list,
|
||||
if c1_player.is_some() {
|
||||
*c1_transform
|
||||
} else if c2_player.is_some() {
|
||||
|
@ -409,41 +426,32 @@ fn change_character_system(
|
|||
keyboard_input: Res<Input<KeyCode>>,
|
||||
characters: Query<(Entity, &CharacterColor, Option<&Player>)>,
|
||||
audio: Res<crossbeam_channel::Sender<AudioMsg>>,
|
||||
character_list: Res<CharacterList>,
|
||||
) {
|
||||
if !keyboard_input.just_pressed(KeyCode::Tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut player_idx: usize = 0;
|
||||
let mut player_count: usize = 0;
|
||||
|
||||
// find player idx
|
||||
for (_entity, _color, player) in characters.iter() {
|
||||
if player.is_some() {
|
||||
player_idx = player_count;
|
||||
if let Some((player_entity, _color, _)) = characters
|
||||
.iter()
|
||||
.find(|(_entity, _color, player)| player.is_some())
|
||||
.or_else(|| characters.iter().next())
|
||||
{
|
||||
commands.entity(player_entity).remove::<Player>();
|
||||
if let Some(new_player_entity) = character_list
|
||||
.0
|
||||
.range(player_entity..)
|
||||
.nth(1)
|
||||
.or_else(|| character_list.0.iter().next())
|
||||
{
|
||||
commands.entity(*new_player_entity).insert(Player);
|
||||
if let Ok((_entity, color, _player)) = characters.get(*new_player_entity) {
|
||||
audio
|
||||
.send(AudioMsg::Color([color.0.r(), color.0.g(), color.0.b()]))
|
||||
.ok();
|
||||
audio.send(AudioMsg::Switch).ok();
|
||||
}
|
||||
}
|
||||
player_count += 1;
|
||||
}
|
||||
|
||||
// calculate next player index
|
||||
let next_player_idx = (player_idx + 1) % player_count;
|
||||
player_count = 0;
|
||||
|
||||
// exchange `Player` component from old `player_idx` to new `next_player_idx`
|
||||
for (entity, color, _player) in characters.iter() {
|
||||
if player_count == player_idx {
|
||||
commands.entity(entity).remove::<Player>();
|
||||
}
|
||||
|
||||
if player_count == next_player_idx {
|
||||
commands.entity(entity).insert(Player);
|
||||
audio
|
||||
.send(AudioMsg::Color([color.0.r(), color.0.g(), color.0.b()]))
|
||||
.ok();
|
||||
audio.send(AudioMsg::Switch).ok();
|
||||
}
|
||||
|
||||
player_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,7 +507,8 @@ fn win_setup(
|
|||
transform: Transform::from_xyz(0., 0., 3.),
|
||||
..default()
|
||||
})
|
||||
.insert(Level);
|
||||
.insert(Level)
|
||||
.insert(WinText);
|
||||
commands
|
||||
.spawn_bundle(Text2dBundle {
|
||||
text: Text::from_section(
|
||||
|
@ -514,7 +523,8 @@ fn win_setup(
|
|||
transform: Transform::from_xyz(0., 0., 4.),
|
||||
..Default::default()
|
||||
})
|
||||
.insert(Level);
|
||||
.insert(Level)
|
||||
.insert(WinText);
|
||||
}
|
||||
|
||||
fn move_camera(
|
||||
|
@ -543,12 +553,24 @@ fn move_camera(
|
|||
}
|
||||
}
|
||||
|
||||
fn move_win_text_system(
|
||||
camera_query: Query<&Transform, With<Camera>>,
|
||||
mut win_text_query: Query<&mut Transform, (With<WinText>, Without<Camera>)>,
|
||||
) {
|
||||
let camera_pos = camera_query.single();
|
||||
for mut pos in win_text_query.iter_mut() {
|
||||
pos.translation.x = camera_pos.translation.x;
|
||||
pos.translation.y = camera_pos.translation.y;
|
||||
}
|
||||
}
|
||||
|
||||
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>>,
|
||||
mut character_list: ResMut<CharacterList>,
|
||||
level_query: Query<Entity, With<Level>>,
|
||||
mut app_state: ResMut<State<AppState>>,
|
||||
) {
|
||||
|
@ -560,6 +582,7 @@ fn level_keyboard_system(
|
|||
}
|
||||
|
||||
if keyboard_input.just_pressed(KeyCode::R) {
|
||||
character_list.0.clear();
|
||||
for entity in level_query.iter() {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,12 @@ pub fn setup_level(
|
|||
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,
|
||||
mut character_list: ResMut<CharacterList>,
|
||||
level_query: Query<Entity, With<Level>>,
|
||||
) {
|
||||
character_list.0.clear();
|
||||
for entity in level_query.iter() {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
|
@ -32,6 +37,7 @@ pub fn post_setup_level(
|
|||
mut level_startup_event: EventReader<LevelStartupEvent>,
|
||||
asset_server: Res<AssetServer>,
|
||||
audio: Res<crossbeam_channel::Sender<AudioMsg>>,
|
||||
mut character_list: ResMut<CharacterList>,
|
||||
stored_levels_assets: Res<Assets<StoredLevels>>,
|
||||
stored_levels_handle: Res<Handle<StoredLevels>>,
|
||||
) {
|
||||
|
@ -50,6 +56,7 @@ pub fn post_setup_level(
|
|||
&mut materials,
|
||||
&asset_server,
|
||||
&audio,
|
||||
&mut character_list,
|
||||
stored_level,
|
||||
);
|
||||
}
|
||||
|
@ -64,6 +71,7 @@ pub fn spawn_stored_level(
|
|||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||
asset_server: &Res<AssetServer>,
|
||||
audio: &Res<crossbeam_channel::Sender<AudioMsg>>,
|
||||
character_list: &mut ResMut<CharacterList>,
|
||||
|
||||
stored_level: &StoredLevel,
|
||||
) {
|
||||
|
@ -84,6 +92,7 @@ pub fn spawn_stored_level(
|
|||
character_meshes,
|
||||
materials,
|
||||
audio,
|
||||
character_list,
|
||||
stored_level.characters.iter().map(|character| {
|
||||
(
|
||||
Transform::from_xyz(character.pos.x, character.pos.y, 0.),
|
||||
|
|
16
src/menu.rs
16
src/menu.rs
|
@ -20,6 +20,22 @@ impl Plugin for MenuPlugin {
|
|||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let font = asset_server.get_handle("UacariLegacy-Thin.ttf");
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
commands
|
||||
.spawn_bundle(Text2dBundle {
|
||||
text: Text::from_section(
|
||||
"Note:\nAudio is NOT available in the WASM build.",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 24.0,
|
||||
color: Color::rgba(1., 0.4, 0.4, 1.),
|
||||
},
|
||||
)
|
||||
.with_alignment(TextAlignment::CENTER),
|
||||
transform: Transform::from_xyz(0., -128.0, 0.),
|
||||
..Default::default()
|
||||
})
|
||||
.insert(Menu);
|
||||
commands
|
||||
.spawn_bundle(Text2dBundle {
|
||||
text: Text::from_section(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use bevy::{prelude::*, sprite::Mesh2dHandle};
|
||||
use rand::Rng;
|
||||
use rand::{rngs::ThreadRng, Rng};
|
||||
use rand_distr::{Distribution, UnitCircle};
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -68,15 +68,20 @@ pub struct ParticleComponent {
|
|||
}
|
||||
|
||||
impl ParticleComponent {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(rng: &mut ThreadRng) -> Self {
|
||||
let mut particle_component: Self = Self::default();
|
||||
particle_component.randomize_velocity(MIN_VELOCITY, MAX_VELOCITY);
|
||||
particle_component.randomize_velocity(rng, MIN_VELOCITY, MAX_VELOCITY);
|
||||
particle_component
|
||||
}
|
||||
|
||||
pub fn randomize_velocity(&mut self, min_velocity: f32, max_velocity: f32) {
|
||||
let random_direction: [f32; 2] = UnitCircle.sample(&mut rand::thread_rng());
|
||||
let random_magnitude: f32 = rand::thread_rng().gen_range(min_velocity..max_velocity);
|
||||
pub fn randomize_velocity(
|
||||
&mut self,
|
||||
rng: &mut ThreadRng,
|
||||
min_velocity: f32,
|
||||
max_velocity: f32,
|
||||
) {
|
||||
let random_direction: [f32; 2] = UnitCircle.sample(rng);
|
||||
let random_magnitude: f32 = rng.gen_range(min_velocity..max_velocity);
|
||||
self.velocity = Vec3::new(random_direction[0], random_direction[1], 0.0) * random_magnitude;
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +92,8 @@ fn particle_effect_startup(
|
|||
particle_mesh: Res<ParticleMesh>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for _p in 0..POOL_COUNT {
|
||||
let color_mesh = ColorMesh2dBundle {
|
||||
mesh: particle_mesh.square.clone(),
|
||||
|
@ -96,7 +103,7 @@ fn particle_effect_startup(
|
|||
|
||||
commands
|
||||
.spawn_bundle(color_mesh)
|
||||
.insert(ParticleComponent::new());
|
||||
.insert(ParticleComponent::new(&mut rng));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,6 +117,8 @@ fn particle_effect_system(
|
|||
mut particle_effect: ResMut<ParticleEffectResource>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let delta_seconds: f32 = f32::max(0.001, time.delta_seconds());
|
||||
let delta_position: Vec3 = particle_effect.translation - particle_effect.prev_translation;
|
||||
// let overall_velocity: Vec3 = delta_position / delta_seconds;
|
||||
|
@ -124,7 +133,7 @@ fn particle_effect_system(
|
|||
.distance_squared(particle_effect.translation);
|
||||
if squared_distance > particle_effect.radius_squared {
|
||||
transform.translation = particle_effect.translation;
|
||||
particle_component.randomize_velocity(MIN_VELOCITY, MAX_VELOCITY);
|
||||
particle_component.randomize_velocity(&mut rng, MIN_VELOCITY, MAX_VELOCITY);
|
||||
}
|
||||
|
||||
if let Some(material) = materials.get_mut(color_material) {
|
||||
|
|
Loading…
Reference in a new issue