Compare commits

...

1 commit

Author SHA1 Message Date
gallant 660fe25234 Add timer and windows support (#6)
Reviewed-on: karx/catbox#6
Co-authored-by: gallant <vincentknightwork@gmail.com>
Co-committed-by: gallant <vincentknightwork@gmail.com>
2023-04-09 22:14:57 -05:00
6 changed files with 267 additions and 90 deletions

6
.gitignore vendored
View file

@ -1,2 +1,8 @@
/target
Cargo.lock
.DS_Store
gnu-mingw
SDL2.dll
SDL2_image.dll
SDL2_ttf.dll
/.idea

View file

@ -17,7 +17,16 @@ features = ["image", "ttf"]
[dependencies]
rodio = { version = "0.15.0", optional = true}
[build-dependencies]
ureq = {version = "2.6.2", features = ["native-tls"]}
native-tls = "0.2.7"
zip-extract = "0.1.2"
tempfile = "3.5.0"
[features]
default = ["audio"]
static = ["sdl2/static-link", "sdl2/bundled"]
audio = ["dep:rodio"]
[[example]]
name = "example_1"

132
build.rs Normal file
View file

@ -0,0 +1,132 @@
#![warn(clippy::pedantic)]
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{copy, BufWriter};
use std::path::{Path, PathBuf};
use std::sync::Arc;
use ureq::AgentBuilder;
use zip_extract::extract;
use tempfile::tempdir;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let target = env::var("TARGET")?;
if target.contains("pc-windows-gnu") {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?);
let temp = tempdir()?;
let temp_dir = temp.path();
//GETTING LIBRARY DIRECTORIES GIVEN THE BUILD TARGET
let mut lib_dir = manifest_dir.clone();
let mut dll_dir = manifest_dir.clone();
let mut zip_extract = manifest_dir.clone();
zip_extract.push("gnu-mingw");
lib_dir.push("gnu-mingw");
dll_dir.push("gnu-mingw");
let mut part = String::new();
if target.contains("x86_64") {
part += "x86_64-w64-mingw32";
lib_dir.push("x86_64-w64-mingw32");
dll_dir.push("x86_64-w64-mingw32");
} else {
part += "x86_64-w64-mingw32";
lib_dir.push("i686-w64-mingw32");
dll_dir.push("i686-w64-mingw32");
}
lib_dir.push("lib");
dll_dir.push("bin");
println!("DEBUG: Managed Dirs!");
if !zip_extract.exists() {
std::fs::create_dir_all(&zip_extract)?;
}
println!("DEBUG: Created Dirs!");
if !lib_dir.exists() {
//NOW THAT WE HAVE THE OUTPUT DIRECTORIES, WE NEED TO EXTRACT THE ZIP FILES INTO THE
//CORRECT DIRECTORIES
//returns zip files
let url_sdl = download_files(temp_dir,"https://github.com/libsdl-org/SDL/releases/download/release-2.26.4/SDL2-devel-2.26.4-mingw.zip")?;
url_sdl.sync_all()?;
let url_ttf = download_files(temp_dir,"https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.20.2/SDL2_ttf-devel-2.20.2-mingw.zip")?;
url_ttf.sync_all()?;
let url_image = download_files(temp_dir,"https://github.com/libsdl-org/SDL_image/releases/download/release-2.6.3/SDL2_image-devel-2.6.3-mingw.zip")?;
url_image.sync_all()?;
println!("DEBUG: Downloaded Files!");
let zip_vec = vec![&url_sdl, &url_ttf, &url_image];
for file in zip_vec {
extract(file, &zip_extract, true)?;
println!("DEBUG: Extracted 'a' File");
}
temp.close()?;
}
//SEARCHES AND LINKS LIBRARIES WITH CARGO
println!("cargo:rustc-link-search=all={}", lib_dir.display());
for entry in std::fs::read_dir(dll_dir).expect("Can't read DLL dir") {
let entry_path = entry.expect("Invalid fs entry").path();
let file_name_result = entry_path.file_name();
let mut new_file_path = manifest_dir.clone();
if let Some(file_name) = file_name_result {
let file_name = file_name.to_str().unwrap();
if Path::new(file_name)
.extension()
.map_or(false, |ext| ext.eq_ignore_ascii_case("dll"))
{
new_file_path.push(file_name);
std::fs::copy(&entry_path, new_file_path.as_path())
.expect("Can't copy from DLL dir");
}
}
}
}
Ok(())
}
/// Downloads files and returns the file if it was able to be downloaded
/// # Panics
/// panics if no response or response lacks "content-disposition" header
/// # Errors
/// errors if
/// A: unable to get `TlsConnector`
/// B: A File is unable to be create
/// C: or if the reader is unable to be copied into the writer
pub fn download_files(path: &Path,url: &str) -> Result<File, Box<dyn std::error::Error>> {
let agent = AgentBuilder::new()
.tls_connector(Arc::new(native_tls::TlsConnector::new()?))
.build();
let resp = agent.get(url).call()?;
let content_disposition = resp.header("content-disposition").unwrap();
let file_name = content_disposition
.split("; ")
.find(|s| s.starts_with("filename="))
.unwrap()
.split('=')
.nth(1)
.unwrap()
.trim_matches('"');
// Create a new File object to store the downloaded zip file
let mut path = path.to_path_buf();
path.push(file_name);
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)?;
// Use a BufWriter to efficiently write the contents of the response to the file
let mut writer = BufWriter::new(file);
copy(&mut resp.into_reader(), &mut writer)?;
Ok(writer.into_inner()?)
}

93
examples/example_1.rs Normal file
View file

@ -0,0 +1,93 @@
#![warn(clippy::pedantic)]
use cat_box::{draw_text, get_keyboard_state, get_mouse_state, Game, Sprite, SpriteCollection};
use sdl2::keyboard::Scancode;
fn main() {
let game = Game::new("catbox demo", 1000, 800);
let mut i = 0u8;
let mut s = Sprite::new("duck.png", 500, 400).unwrap();
let mut s2 = Sprite::new("duck.png", 400, 500).unwrap();
let bytes = include_bytes!("../duck.png");
let mut coll = SpriteCollection::new();
for n in 0..10 {
for o in 0..8 {
let x = Sprite::from_bytes(bytes, n * 100, o * 100).unwrap();
coll.push(x);
}
}
#[cfg(feature = "audio")]
cat_box::play("output.mp3", 120);
game.run(|ctx| {
if game.step() >= 1 {
i = (i + 1) % 255;
ctx.set_background_colour(i, 64, 255);
draw_text(
ctx,
format!("i is {i}"),
"MesloLGS NF Regular.ttf",
72,
(300, 300),
cat_box::TextMode::Shaded {
foreground: (255, 255, 255),
background: (0, 0, 0),
},
)
.unwrap();
let (start_x, start_y) = s.position().into();
let m = get_mouse_state(ctx);
let x_diff = m.x - start_x;
let y_diff = m.y - start_y;
let angle = f64::from(y_diff).atan2(f64::from(x_diff));
s.set_angle(angle.to_degrees());
for spr in coll.iter() {
let (start_x, start_y) = spr.position().into();
let m = get_mouse_state(ctx);
let x_diff = m.x - start_x;
let y_diff = m.y - start_y;
let angle = f64::from(y_diff).atan2(f64::from(x_diff));
spr.set_angle(angle.to_degrees());
}
let keys = get_keyboard_state(ctx).keys;
for key in keys {
let offset = match key {
Scancode::Escape => {
game.terminate();
(0, 0)
}
Scancode::W | Scancode::Up => (0, 5),
Scancode::S | Scancode::Down => (0, -5),
Scancode::A | Scancode::Left => (-5, 0),
Scancode::D | Scancode::Right => (5, 0),
_ => (0, 0),
};
s.translate(offset);
for spr in coll.iter() {
spr.translate(offset);
}
}
if !cat_box::physics::check_for_collision_with_collection(&s2, &coll).is_empty() {
println!("Sprites collided! {i}");
}
game.t_reset();
}
s2.draw(ctx).unwrap();
s.draw(ctx).unwrap();
coll.draw(ctx).unwrap();
})
.unwrap();
}

View file

@ -113,6 +113,7 @@ use std::{
ops::{Deref, DerefMut},
path::Path,
slice::IterMut,
time::Instant,
};
use vec2::Vec2Int;
@ -706,6 +707,7 @@ pub struct Game {
pub width: u32,
/// The height of the opened window
pub height: u32,
pub time: Cell<Instant>,
stopped: Cell<bool>,
}
@ -725,10 +727,35 @@ impl Game {
title: title.to_string(),
width,
height,
time: Instant::now().into(),
stopped: Cell::new(false),
}
}
///Gets time elapsed since last timer reset in milliseconds
///
///Run this within the game loop
///```
///# use cat_box::Game;
///# let game = Game::new("wacky game", 1000, 1000);
///# game.run(|ctx| {
/// if game.step() >= 1000
/// {
/// println!("A second has passed approx!");
/// game.t_reset();
/// }
///}).unwrap();
///```
pub fn step(&self) -> u128 {
self.time.get().elapsed().as_millis()
}
///Resets in-game timer
pub fn t_reset(&self) {
self.time.set(Instant::now());
}
/// Runs the game. Note: this method blocks, as it uses an infinite loop.
///
/// ```no_run

View file

@ -1,90 +0,0 @@
#![warn(clippy::pedantic)]
use cat_box::{draw_text, get_keyboard_state, get_mouse_state, Game, Sprite, SpriteCollection};
use sdl2::keyboard::Scancode;
fn main() {
let game = Game::new("catbox demo", 1000, 800);
let mut i = 0u8;
let mut s = Sprite::new("duck.png", 500, 400).unwrap();
let mut s2 = Sprite::new("duck.png", 400, 500).unwrap();
let bytes = include_bytes!("../duck.png");
let mut coll = SpriteCollection::new();
for n in 0..10 {
for o in 0..8 {
let x = Sprite::from_bytes(bytes, n * 100, o * 100).unwrap();
coll.push(x);
}
}
#[cfg(feature = "audio")]
cat_box::play("output.mp3", 120);
game.run(|ctx| {
i = (i + 1) % 255;
ctx.set_background_colour(i as u8, 64, 255);
draw_text(
ctx,
format!("i is {}", i),
"MesloLGS NF Regular.ttf",
72,
(300, 300),
cat_box::TextMode::Shaded {
foreground: (255, 255, 255),
background: (0, 0, 0),
},
)
.unwrap();
let (start_x, start_y) = s.position().into();
let m = get_mouse_state(ctx);
let x_diff = m.x - start_x;
let y_diff = m.y - start_y;
let angle = f64::from(y_diff).atan2(f64::from(x_diff));
s.set_angle(angle.to_degrees());
for spr in coll.iter() {
let (start_x, start_y) = spr.position().into();
let m = get_mouse_state(ctx);
let x_diff = m.x - start_x;
let y_diff = m.y - start_y;
let angle = f64::from(y_diff).atan2(f64::from(x_diff));
spr.set_angle(angle.to_degrees());
}
let keys = get_keyboard_state(ctx).keys;
for key in keys {
let offset = match key {
Scancode::Escape => {
game.terminate();
(0, 0)
}
Scancode::W | Scancode::Up => (0, 5),
Scancode::S | Scancode::Down => (0, -5),
Scancode::A | Scancode::Left => (-5, 0),
Scancode::D | Scancode::Right => (5, 0),
_ => (0, 0),
};
s.translate(offset);
for spr in coll.iter() {
spr.translate(offset);
}
}
if !cat_box::physics::check_for_collision_with_collection(&s2, &coll).is_empty() {
println!("Sprites collided! {}", i);
}
s2.draw(ctx).unwrap();
s.draw(ctx).unwrap();
coll.draw(ctx).unwrap();
})
.unwrap();
}