Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
ShenMian committed Jun 8, 2024
1 parent 884c442 commit f59193d
Show file tree
Hide file tree
Showing 13 changed files with 549 additions and 113 deletions.
7 changes: 4 additions & 3 deletions mobile/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use bevy::{prelude::*, window::WindowMode};
use soukoban_rs::SokobanPlugin;
use bevy::prelude::*;
use bevy::window::WindowMode;
use bevy_game::GamePlugin; // ToDo: Replace bevy_game with your new crate name.

#[bevy_main]
fn main() {
Expand All @@ -13,7 +14,7 @@ fn main() {
}),
..default()
}),
// SokobanPlugin,
GamePlugin,
))
.run()
}
35 changes: 35 additions & 0 deletions src/actions/game_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use bevy::prelude::{ButtonInput, KeyCode, Res};

pub enum GameControl {
Up,
Down,
Left,
Right,
}

impl GameControl {
pub fn pressed(&self, keyboard_input: &Res<ButtonInput<KeyCode>>) -> bool {
match self {
GameControl::Up => {
keyboard_input.pressed(KeyCode::KeyW) || keyboard_input.pressed(KeyCode::ArrowUp)
}
GameControl::Down => {
keyboard_input.pressed(KeyCode::KeyS) || keyboard_input.pressed(KeyCode::ArrowDown)
}
GameControl::Left => {
keyboard_input.pressed(KeyCode::KeyA) || keyboard_input.pressed(KeyCode::ArrowLeft)
}
GameControl::Right => {
keyboard_input.pressed(KeyCode::KeyD) || keyboard_input.pressed(KeyCode::ArrowRight)
}
}
}
}

pub fn get_movement(control: GameControl, input: &Res<ButtonInput<KeyCode>>) -> f32 {
if control.pressed(input) {
1.0
} else {
0.0
}
}
60 changes: 60 additions & 0 deletions src/actions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use bevy::math::Vec3Swizzles;
use bevy::prelude::*;

use crate::actions::game_control::{get_movement, GameControl};
use crate::player::Player;
use crate::GameState;

mod game_control;

pub const FOLLOW_EPSILON: f32 = 5.;

pub struct ActionsPlugin;

// This plugin listens for keyboard input and converts the input into Actions
// Actions can then be used as a resource in other systems to act on the player input.
impl Plugin for ActionsPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Actions>().add_systems(
Update,
set_movement_actions.run_if(in_state(GameState::Playing)),
);
}
}

#[derive(Default, Resource)]
pub struct Actions {
pub player_movement: Option<Vec2>,
}

pub fn set_movement_actions(
mut actions: ResMut<Actions>,
keyboard_input: Res<ButtonInput<KeyCode>>,
touch_input: Res<Touches>,
player: Query<&Transform, With<Player>>,
camera: Query<(&Camera, &GlobalTransform), With<Camera2d>>,
) {
let mut player_movement = Vec2::new(
get_movement(GameControl::Right, &keyboard_input)
- get_movement(GameControl::Left, &keyboard_input),
get_movement(GameControl::Up, &keyboard_input)
- get_movement(GameControl::Down, &keyboard_input),
);

if let Some(touch_position) = touch_input.first_pressed_position() {
let (camera, camera_transform) = camera.single();
if let Some(touch_position) = camera.viewport_to_world_2d(camera_transform, touch_position)
{
let diff = touch_position - player.single().translation.xy();
if diff.length() > FOLLOW_EPSILON {
player_movement = diff.normalize();
}
}
}

if player_movement != Vec2::ZERO {
actions.player_movement = Some(player_movement.normalize());
} else {
actions.player_movement = None;
}
}
56 changes: 56 additions & 0 deletions src/audio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::actions::{set_movement_actions, Actions};
use crate::loading::AudioAssets;
use crate::GameState;
use bevy::prelude::*;
use bevy_kira_audio::prelude::*;

pub struct InternalAudioPlugin;

// This plugin is responsible to control the game audio
impl Plugin for InternalAudioPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(AudioPlugin)
.add_systems(OnEnter(GameState::Playing), start_audio)
.add_systems(
Update,
control_flying_sound
.after(set_movement_actions)
.run_if(in_state(GameState::Playing)),
);
}
}

#[derive(Resource)]
struct FlyingAudio(Handle<AudioInstance>);

fn start_audio(mut commands: Commands, audio_assets: Res<AudioAssets>, audio: Res<Audio>) {
audio.pause();
let handle = audio
.play(audio_assets.flying.clone())
.looped()
.with_volume(0.3)
.handle();
commands.insert_resource(FlyingAudio(handle));
}

fn control_flying_sound(
actions: Res<Actions>,
audio: Res<FlyingAudio>,
mut audio_instances: ResMut<Assets<AudioInstance>>,
) {
if let Some(instance) = audio_instances.get_mut(&audio.0) {
match instance.state() {
PlaybackState::Paused { .. } => {
if actions.player_movement.is_some() {
instance.resume(AudioTween::default());
}
}
PlaybackState::Playing { .. } => {
if actions.player_movement.is_none() {
instance.pause(AudioTween::default());
}
}
_ => {}
}
}
}
52 changes: 43 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,51 @@
#![allow(clippy::type_complexity)]

mod actions;
mod audio;
mod loading;
mod menu;
mod player;

use crate::actions::ActionsPlugin;
use crate::audio::InternalAudioPlugin;
use crate::loading::LoadingPlugin;
use crate::menu::MenuPlugin;
use crate::player::PlayerPlugin;

use bevy::app::App;
#[cfg(debug_assertions)]
use bevy::diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin};
use bevy::prelude::*;

mod performance_matrix;
mod systems;
// This example game uses States to separate logic
// See https://bevy-cheatbook.github.io/programming/states.html
// Or https://github.com/bevyengine/bevy/blob/main/examples/ecs/state.rs
#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)]
enum GameState {
// During the loading State the LoadingPlugin will load our assets
#[default]
Loading,
// During this State the actual game logic is executed
Playing,
// Here the menu is drawn and waiting for player interaction
Menu,
}

pub struct SokobanPlugin;
pub struct GamePlugin;

impl Plugin for SokobanPlugin {
impl Plugin for GamePlugin {
fn build(&self, app: &mut App) {
// app.add_plugins(DefaultPlugins);

// #[cfg(debug_assertions)]
// app.add_plugins(performance_matrix::PerformanceMatrixPlugin);
app.init_state::<GameState>().add_plugins((
LoadingPlugin,
MenuPlugin,
ActionsPlugin,
InternalAudioPlugin,
PlayerPlugin,
));

// app.add_systems(Startup, (systems::window::setup, systems::camera::setup));
#[cfg(debug_assertions)]
{
app.add_plugins((FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin::default()));
}
}
}
37 changes: 37 additions & 0 deletions src/loading.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::GameState;
use bevy::prelude::*;
use bevy_asset_loader::prelude::*;
use bevy_kira_audio::AudioSource;

pub struct LoadingPlugin;

/// This plugin loads all assets using [`AssetLoader`] from a third party bevy plugin
/// Alternatively you can write the logic to load assets yourself
/// If interested, take a look at <https://bevy-cheatbook.github.io/features/assets.html>
impl Plugin for LoadingPlugin {
fn build(&self, app: &mut App) {
app.add_loading_state(
LoadingState::new(GameState::Loading)
.continue_to_state(GameState::Menu)
.load_collection::<AudioAssets>()
.load_collection::<TextureAssets>(),
);
}
}

// the following asset collections will be loaded during the State `GameState::Loading`
// when done loading, they will be inserted as resources (see <https://github.com/NiklasEi/bevy_asset_loader>)

#[derive(AssetCollection, Resource)]
pub struct AudioAssets {
#[asset(path = "audio/flying.ogg")]
pub flying: Handle<AudioSource>,
}

#[derive(AssetCollection, Resource)]
pub struct TextureAssets {
#[asset(path = "textures/bevy.png")]
pub bevy: Handle<Image>,
#[asset(path = "textures/github.png")]
pub github: Handle<Image>,
}
56 changes: 45 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,53 @@
// disable console on windows for release builds
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use bevy::asset::AssetMetaCheck;
use bevy::prelude::*;
use soukoban_rs::SokobanPlugin;
use bevy::window::PrimaryWindow;
use bevy::winit::WinitWindows;
use bevy::DefaultPlugins;
use bevy_game::GamePlugin; // ToDo: Replace bevy_game with your new crate name.
use std::io::Cursor;
use winit::window::Icon;

fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Sokoban".to_string(),
canvas: Some("#bevy".to_owned()),
prevent_default_event_handling: false,
..default()
}),
.insert_resource(Msaa::Off)
.insert_resource(AssetMetaCheck::Never)
.insert_resource(ClearColor(Color::rgb(0.4, 0.4, 0.4)))
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Bevy game".to_string(), // ToDo
// Bind to canvas included in `index.html`
canvas: Some("#bevy".to_owned()),
// Tells wasm not to override default event handling, like F5 and Ctrl+R
prevent_default_event_handling: false,
..default()
}),
// SokobanPlugin,
))
..default()
}))
.add_plugins(GamePlugin)
.add_systems(Startup, set_window_icon)
.run();
}

// Sets the icon on windows and X11
fn set_window_icon(
windows: NonSend<WinitWindows>,
primary_window: Query<Entity, With<PrimaryWindow>>,
) {
let primary_entity = primary_window.single();
let Some(primary) = windows.get_window(primary_entity) else {
return;
};
let icon_buf = Cursor::new(include_bytes!(
"../build/macos/AppIcon.iconset/icon_256x256.png"
));
if let Ok(image) = image::load(icon_buf, image::ImageFormat::Png) {
let image = image.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
let icon = Icon::from_rgba(rgba, width, height).unwrap();
primary.set_window_icon(Some(icon));
};
}
Loading

0 comments on commit f59193d

Please sign in to comment.