SEPARATED
Introduction
SEPARATED is a 2D platformer game where you can talk to NPCs. Most of the game is not yet implemented.
Table of Contents
- SEPARATED
Player Inputs ∆
Input | KeyCode | Gamepad Button/Axis |
---|---|---|
Run | Shift | Xbox: X PS5: Square |
Interact | E | Xbox: B PS5: ◯ |
Attack1 | Q | Xbox/PS5: L1 |
Jump | Space | Xbox: A PS5: ╳ |
Move | WASD + Arrow Keys | Any Axis + D-Pad |
Debugging Keyboard Shortcuts
Action | KeyCode |
---|---|
Toggle Physics Wireframes | F9 |
StateInspector (GameState) | F10 |
WorldInspector | F11 |
TODO
- Use WyRand instead of
thread_rng()
fn print_random_value(mut rng: ResMut<GlobalEntropy<WyRand>>) {
println!("Random value: {}", rng.next_u32());
}
use bevy_rand::WyRand;
use bevy_rand::prelude::{GlobalEntropy, ForkableRng};
#[derive(Component)]
struct Source;
fn setup_source(mut commands: Commands, mut global: ResMut<GlobalEntropy<WyRand>>) {
commands
.spawn((
Source,
global.fork_rng(),
));
}
if ( jumping || falling ) {
if velocity.y.abs() < jumpHangTimeThreshold {
// Increase acceleration for this duration also.
// Reduce gravity.
}
}
// If the player is moving downwards..
if velocity.y < 0 {
// Increase gravity while falling.
gravityScale *= fallGravityMultiplier;
// Cap maximum fall speed, so when falling over large distances,
// we don't accelerate to insanely high speeds.
}
Localization
- Tool to rename all neighbours to neighbors and other britishism! There is some in
bevy_ecs_ldtk
for example. - ⚠️
Started work by integratingbevy_device_lang
. Requires a proper system that saves this value and allows the player to change it in the game menu, and also requires starting work on localization and saving and loading settings. - Use
sys-lang
crate instead!
- Tool to rename all neighbours to neighbors and other britishism! There is some in
bevy_asepritesheet
+bevy_ecs_ldtk
integration.Patrol
- Flip sprite when turning around!
Movement Improvements
- Movement animations.
- Movement particle effects.
- Coyote (Grace) Time after falling off a ledge.
- Maybe needs a raycast in front of the player? Timer needs to start before falling off a ledge.
- Jump Improvements
- Jumping animations.
- Jumping particle effects.
- Wall Jumping
Prevent player movement for a short duration during the wall jump.Reduce run force? Maybe a lerp between the wall jump speed and running speed?
- Air Time
- Jump Height
- Increase the player's jump height the longer the jump button is being held down.
- Clamp maximum falling speed.
- Coyote Time while jumping and pressing the jump button.
- There is already some check for being in the air we just need the input part I think.
- Bonus Air Time
- Peak Control
- Fast Fall
- Increase Player's falling speed after the peak of their jump by adjusting gravity.
Game Feel Improvements
This is kinda broad but always iterate over every small mechanic towards more fun.
AI Stuff ⚠️ Started work
- Pass player input(s) to ai-brain so it can use it for prediction.
- Basic Timer with Action Scheduling
- Thirst ✅
- Fatigue ⚠️
Pathfinding ⚠️ Started work
Use something to copy
dxil.dll
anddxcompiler.dll
to Windows builds.YarnSpinner
- Begin YarnSpinner integration ✅
- YarnSpinner+LDTK integration ⚠️ Started work
UI
- sickle_ui
- labels ✅
- keycap/gamepad button switching ⚠️
- sickle_ui
filesystem_watcher
and asset_processor
???
Rust Things 🦀
Run in Wolf Mode (Debug)
cargo run --profile awoo 2>&1 | Out-String -Stream | Where-Object { $_ -notmatch "ID3D12Device::CreateCommittedResource:" -and $_ -notmatch "Live Object at" -and $_ -notmatch "LineGizmo" -and $_ -notmatch "End of Frame" -and $_ -notmatch "prepare_windows" -and $_ -notmatch "cleanup" -and $_ -notmatch "SwapChain" -and $_ -notmatch "create_view" }
Pedantic linting
cargo clippy -- -W clippy::pedantic
Linting on all packages, treating warnings as errors
cargo clippy --workspace --all-targets --all-features -- -D warnings
This command runs the clippy
linter on all packages in the workspace, for all targets and features. The -D warnings
option treats any warnings as errors.
Format code
cargo fmt --all
This command formats the code in every package using the default formatting rules provided by rustfmt
.
Test without default features
cargo test --no-default-features
This command runs tests in the package, but disables the default features.
Test with only the bevy_ui
features
cargo test --no-default-features --features="bevy_ui"
This command runs tests with only the bevy_ui
feature enabled.
Test with all features enabled
cargo test --all-features
This command runs tests with all features enabled.
Test with all features enabled on nightly
cargo +nightly build --all-features
This command builds the package with all features enabled using the nightly version of the Rust compiler. This is typically used for generating documentation on docs.rs.
Generate documentation with all features enabled
cargo +nightly doc --all-features --no-deps
This command generates documentation for the package with all features enabled, without including dependencies, using the nightly version of the Rust compiler.
seldom_state
+ input_manager
Example
// In this game, you can move with the left and right arrow keys, and jump with space.
// `input-manager` handles the input.
use bevy::prelude::*;
use leafwing_input_manager::{ axislike::VirtualAxis, prelude::* };
use seldom_state::prelude::*;
fn main() {
App::new()
.add_plugins((DefaultPlugins, InputManagerPlugin::<Action>::default(), StateMachinePlugin))
.add_systems(Startup, init)
.add_systems(Update, (walk, fall))
.run();
}
const JUMP_VELOCITY: f32 = 500.0;
fn init(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2dBundle::default());
commands.spawn((
SpriteBundle {
transform: Transform::from_xyz(500.0, 0.0, 0.0),
texture: asset_server.load("player.png"),
..default()
},
// From `input-manager`
InputManagerBundle {
input_map: InputMap::default()
.insert(Action::Move, VirtualAxis::horizontal_arrow_keys())
.insert(Action::Move, SingleAxis::symmetric(GamepadAxisType::LeftStickX, 0.0))
.insert(Action::Jump, KeyCode::Space)
.insert(Action::Jump, GamepadButtonType::South)
.build(),
..default()
},
// This state machine achieves a very rigid movement system. Consider a state machine for
// whatever parts of your player controller that involve discrete states. Like the movement
// in Castlevania and Celeste, and the attacks in a fighting game.
StateMachine::default()
// Whenever the player presses jump, jump
.trans::<Grounded, _>(just_pressed(Action::Jump), Falling {
velocity: JUMP_VELOCITY,
})
// When the player hits the ground, idle
.trans::<Falling, _>(grounded, Grounded::Idle)
// When the player is grounded, set their movement direction
.trans_builder(value_unbounded(Action::Move), |_: &Grounded, value| {
Some(match value {
value if value > 0.5 => Grounded::Right,
value if value < -0.5 => Grounded::Left,
_ => Grounded::Idle,
})
}),
Grounded::Idle,
));
}
#[derive(Actionlike, Clone, Eq, Hash, PartialEq, Reflect)]
enum Action {
Move,
Jump,
}
fn grounded(In(entity): In<Entity>, fallings: Query<(&Transform, &Falling)>) -> bool {
let (transform, falling) = fallings.get(entity).unwrap();
transform.translation.y <= 0.0 && falling.velocity <= 0.0
}
#[derive(Clone, Copy, Component, Reflect)]
#[component(storage = "SparseSet")]
enum Grounded {
Left = -1,
Idle = 0,
Right = 1,
}
#[derive(Clone, Component, Reflect)]
#[component(storage = "SparseSet")]
struct Falling {
velocity: f32,
}
const PLAYER_SPEED: f32 = 200.0;
fn walk(mut groundeds: Query<(&mut Transform, &Grounded)>, time: Res<Time>) {
for (mut transform, grounded) in &mut groundeds {
transform.translation.x += (*grounded as i32 as f32) * time.delta_seconds() * PLAYER_SPEED;
}
}
const GRAVITY: f32 = -1000.0;
fn fall(mut fallings: Query<(&mut Transform, &mut Falling)>, time: Res<Time>) {
for (mut transform, mut falling) in &mut fallings {
let dt = time.delta_seconds();
falling.velocity += dt * GRAVITY;
transform.translation.y += dt * falling.velocity;
}
}