Skip to content

Commit

Permalink
refactor!: update to chuot game engine
Browse files Browse the repository at this point in the history
  • Loading branch information
tversteeg committed Jun 6, 2024
1 parent c207c46 commit cb46716
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 75 deletions.
9 changes: 1 addition & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,10 @@ keywords = ["game", "2d", "destructible"]
categories = ["games"]

[dependencies]
assets_manager = { version = "0.11", default-features = false, features = ["toml"] }
log = "0.4"
pixel-game-lib = { path = "../pixel-game-lib" }
chuot = "0.1"
puffin = "0.19"
serde = "1"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
fastrand = "2"

[target.'cfg(target_arch = "wasm32")'.dependencies]
fastrand = { version = "2", default-features = false, features = ["js"] }

[workspace]
members = ["run-wasm"]
2 changes: 1 addition & 1 deletion assets/level/grass-1.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
offset = "middle_top"
offset = "left_top"
2 changes: 1 addition & 1 deletion src/camera.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use pixel_game_lib::glamour::Vector2;
use chuot::glamour::Vector2;

/// Camera view.
///
Expand Down
65 changes: 49 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ mod unit;
mod utils;

use camera::Camera;
use color::Color;
use pixel_game_lib::{
use chuot::{
glamour::{Size2, Vector2},
Context, GameConfig, KeyCode, PixelGame,
};
use color::Color;
use projectile::Projectile;
use settings::Settings;
use terrain::Terrain;
Expand Down Expand Up @@ -43,15 +43,15 @@ pub enum GameState {
}

impl PixelGame for GameState {
fn tick(&mut self, ctx: Context) {
fn update(&mut self, ctx: Context) {
// Exit when escape is pressed
if ctx.key_pressed(KeyCode::Escape) {
ctx.exit();
return;
}

let dt = ctx.delta_time();
let settings = pixel_game_lib::asset::<Settings>("settings");
let settings = ctx.asset::<Settings>("settings");

match self {
GameState::Load {} => {
Expand Down Expand Up @@ -94,15 +94,15 @@ impl PixelGame for GameState {
}
}

// Update and draw the terrain
terrain.tick(ctx.clone(), *camera);
// Draw the terrain
terrain.draw(ctx.clone(), *camera);

// Update all projectiles
projectiles.retain_mut(|projectile| projectile.tick(terrain, ctx.clone()));
projectiles.retain_mut(|projectile| projectile.update(terrain, ctx.clone()));

// Update all units
units.iter_mut().for_each(|unit| {
if let Some(projectile) = unit.tick(terrain, *camera, ctx.clone()) {
if let Some(projectile) = unit.update(terrain, ctx.clone()) {
projectiles.push(projectile);
}
});
Expand All @@ -126,21 +126,54 @@ impl PixelGame for GameState {
}
}
}
}

fn render(&mut self, ctx: Context) {
// Draw a basic FPS counter
let fps = ctx.delta_time().recip();
ctx.draw_text("font.torus-sans", Vector2::ZERO, format!("{fps:.1}"));
ctx.text("font.torus-sans", &format!("{fps:.1}")).draw();

match self {
GameState::Load {} => {
ctx.text("font.torus-sans", "Loading game")
.translate((20.0, 20.0))
.draw();
}
GameState::Play {
camera,
terrain,
units,
projectiles,
..
} => {
// Draw the terrain
terrain.draw(ctx.clone(), *camera);

// Draw all units
units.iter().for_each(|unit| {
unit.render(*camera, ctx.clone());
});

// Draw all projectiles
projectiles.iter().for_each(|projectile| {
projectile.render(*camera, ctx.clone());
});
}
}
}
}

fn main() {
GameState::Load
.run(GameConfig {
title: "Caste Game".to_owned(),
buffer_size: Size2::new(WIDTH, HEIGHT),
background_color: Color::SkyBlue.into(),
viewport_color: Color::Gray.into(),
..Default::default()
})
.run(
chuot::load_assets!(),
GameConfig {
title: "Castle Game".to_owned(),
buffer_size: Size2::new(WIDTH, HEIGHT),
background_color: Color::SkyBlue.into(),
viewport_color: Color::Gray.into(),
..Default::default()
},
)
.expect("Error running game");
}
19 changes: 13 additions & 6 deletions src/projectile.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use pixel_game_lib::{glamour::Vector2, Context};
use chuot::{glamour::Vector2, Context};

use crate::{camera::Camera, settings::Settings, terrain::Terrain};

Expand All @@ -22,16 +22,23 @@ impl Projectile {
/// Move the projectile.
///
/// Returns whether the projectile should be removed.
pub fn tick(&mut self, terrain: &Terrain, ctx: Context) -> bool {
pub fn update(&mut self, terrain: &Terrain, ctx: Context) -> bool {
puffin::profile_function!();

self.pos += self.vel * ctx.delta_time();
self.vel.y += pixel_game_lib::asset::<Settings>("settings").projectile_gravity;
self.vel.y += ctx.asset::<Settings>("settings").projectile_gravity;

let rotation = self.vel.y.atan2(self.vel.x);
terrain.point_collides(self.pos)
}

ctx.draw_sprite_rotated(ASSET_PATH, self.pos, rotation);
/// Draw the projectile.
pub fn render(&self, camera: Camera, ctx: Context) {
puffin::profile_function!();
let rotation = self.vel.y.atan2(self.vel.x);

terrain.point_collides(self.pos)
ctx.sprite(ASSET_PATH)
.translate(self.pos + camera.into())
.rotate(rotation)
.draw();
}
}
13 changes: 8 additions & 5 deletions src/settings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use assets_manager::{loader::TomlLoader, Asset};
use chuot::assets::{loader::toml::TomlLoader, AssetSource, Id, Loadable};
use serde::Deserialize;

/// Game settings loaded from a file so it's easier to change them with hot-reloading.
Expand All @@ -16,8 +16,11 @@ pub struct Settings {
pub projectile_gravity: f32,
}

impl Asset for Settings {
const EXTENSION: &'static str = "toml";

type Loader = TomlLoader;
impl Loadable for Settings {
fn load_if_exists(id: &Id, assets: &AssetSource) -> Option<Self>
where
Self: Sized,
{
assets.load_if_exists::<TomlLoader, _>(id)
}
}
19 changes: 9 additions & 10 deletions src/terrain.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use pixel_game_lib::{
use chuot::{
glamour::{Size2, Vector2},
Context,
};
Expand All @@ -23,18 +23,15 @@ pub struct Terrain {
impl Terrain {
/// Load the terrain.
pub fn new(level: &str, ctx: Context) -> Self {
// Preload the sprite and get the size
let size = ctx.sprite_size(level);
// First get the raw pixels of the level
let (size, pixels) = ctx.sprite(level).read_pixels();
let width = size.width as usize;
let height = size.height as usize;

// Calculate the horizontal heights

// First get the raw pixels of the level
let raw = ctx.sprite_raw_pixels(level);

// Get the highest height to skip a lot of checks
let first_height = raw
let first_height = pixels
.iter()
.enumerate()
.find_map(|(idx, pixel)| {
Expand All @@ -56,7 +53,7 @@ impl Terrain {
for y in first_height..height {
let idx = y * width + x;
// We only care about transparency
if raw[idx] & 0xFF000000 != 0 {
if pixels[idx] & 0xFF000000 != 0 {
return y as f32;
}
}
Expand All @@ -77,11 +74,13 @@ impl Terrain {
}
}

pub fn tick(&mut self, ctx: Context, camera: Camera) {
pub fn draw(&mut self, ctx: Context, camera: Camera) {
let camera_offset: Vector2 = camera.into();

// Draw the level at the bottom of the screen
ctx.draw_sprite(&self.level, camera_offset + Vector2::new(0.0, self.y));
ctx.sprite(&self.level)
.translate(camera_offset + Vector2::new(0.0, self.y))
.draw();
}

/// Whether a point collides with the terrain
Expand Down
68 changes: 41 additions & 27 deletions src/unit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use assets_manager::{loader::TomlLoader, Asset, AssetReadGuard};
use pixel_game_lib::{
use std::rc::Rc;

use chuot::{
assets::{loader::toml::TomlLoader, AssetSource, Id, Loadable},
glamour::{Size2, Vector2},
Context,
};
Expand All @@ -21,14 +23,14 @@ pub enum UnitType {

impl UnitType {
/// Settings path to load for this type.
pub fn settings(&self) -> AssetReadGuard<Settings> {
pub fn settings(&self, ctx: Context) -> Rc<Settings> {
// Settings asset path
let path = match self {
Self::PlayerSpear => "unit.spear",
Self::EnemySpear => "unit.enemy-spear",
};

pixel_game_lib::asset(path)
ctx.asset(path)
}

/// Asset path based on what type to load.
Expand Down Expand Up @@ -60,12 +62,12 @@ pub struct Unit {
impl Unit {
/// Create a new unit.
pub fn new(pos: Vector2, r#type: UnitType, ctx: Context) -> Self {
let projectile_timer = Timer::new(r#type.settings().projectile_spawn_interval);
let projectile_timer = Timer::new(r#type.settings(ctx.clone()).projectile_spawn_interval);

let hide_hands_delay = 0.0;
let health = r#type.settings().health;
let health = r#type.settings(ctx.clone()).health;

let sprite_size = ctx.sprite_size(r#type.asset_path());
let sprite_size = ctx.sprite(r#type.asset_path()).size();

Self {
r#type,
Expand All @@ -80,8 +82,8 @@ impl Unit {
/// Move the unit.
///
/// When a projectile is returned one is spawned.
pub fn tick(&mut self, terrain: &Terrain, camera: Camera, ctx: Context) -> Option<Projectile> {
puffin::profile_scope!("Unit tick");
pub fn update(&mut self, terrain: &Terrain, ctx: Context) -> Option<Projectile> {
puffin::profile_scope!("Unit update");

if !terrain.point_collides(self.pos) {
// No collision with the terrain, the unit falls down
Expand All @@ -91,7 +93,7 @@ impl Unit {
self.pos.y -= 1.0;
} else {
// Collision with the terrain, the unit walks to the right
let walk_speed = self.settings().walk_speed;
let walk_speed = self.settings(ctx.clone()).walk_speed;
self.pos.x += walk_speed * ctx.delta_time();
}

Expand All @@ -100,19 +102,9 @@ impl Unit {
self.hide_hands_delay -= ctx.delta_time();
}

let draw_pos = self.pos - self.ground_collision_point() + camera.into();

ctx.draw_sprite(self.r#type.asset_path(), draw_pos);

if let Some(hands_asset_path) = &self.settings().hands_asset_path {
if self.hide_hands_delay <= 0.0 {
ctx.draw_sprite(hands_asset_path, draw_pos - Vector2::splat(1.0));
}
}

// Spawn a projectile if timer runs out
if self.projectile_timer.update(ctx.delta_time()) {
let settings = self.settings();
let settings = self.settings(ctx.clone());

let hide_hands_delay = settings.hide_hands_delay;
let velocity = settings.projectile_velocity.value();
Expand Down Expand Up @@ -143,14 +135,33 @@ impl Unit {
*/
}

/// Draw the unit.
pub fn render(&self, camera: Camera, ctx: Context) {
puffin::profile_scope!("Unit render");

let draw_pos = self.pos - self.ground_collision_point() + camera.into();

ctx.sprite(self.r#type.asset_path())
.translate(draw_pos)
.draw();

if let Some(hands_asset_path) = &self.settings(ctx.clone()).hands_asset_path {
if self.hide_hands_delay <= 0.0 {
ctx.sprite(hands_asset_path)
.translate(draw_pos - Vector2::splat(1.0))
.draw();
}
}
}

/// Where the unit collides with the ground.
fn ground_collision_point(&self) -> Vector2 {
Vector2::new(self.sprite_size.width / 2.0, self.sprite_size.height - 2.0)
}

/// The settings for this unit.
fn settings(&self) -> AssetReadGuard<Settings> {
self.r#type.settings()
fn settings(&self, ctx: Context) -> Rc<Settings> {
self.r#type.settings(ctx)
}
}

Expand Down Expand Up @@ -185,10 +196,13 @@ pub struct Settings {
pub healthbar_offset: Vector2,
}

impl Asset for Settings {
const EXTENSION: &'static str = "toml";

type Loader = TomlLoader;
impl Loadable for Settings {
fn load_if_exists(id: &Id, assets: &AssetSource) -> Option<Self>
where
Self: Sized,
{
assets.load_if_exists::<TomlLoader, _>(id)
}
}

/// Player unit or enemy unit.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ impl RandomRange {
pub fn value(&self) -> f32 {
match self {
RandomRange::Static(val) => *val,
RandomRange::Range { min, max } => fastrand::f32() * (max - min) + min,
RandomRange::Range { min, max } => chuot::random(*min, *max),
}
}
}
Expand Down

0 comments on commit cb46716

Please sign in to comment.