Added basic working code

master
Mark 2023-12-20 19:05:12 -08:00
parent bd138d3db3
commit f5f52e7e75
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
7 changed files with 861 additions and 0 deletions

404
Cargo.lock generated Normal file
View File

@ -0,0 +1,404 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f"
dependencies = [
"cfg-if",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "exr"
version = "1.71.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
dependencies = [
"bit_field",
"flume",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "fdeflate"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868"
dependencies = [
"simd-adler32",
]
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "flume"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [
"spin",
]
[[package]]
name = "game"
version = "0.1.0"
dependencies = [
"image",
"sdl2",
]
[[package]]
name = "gif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "half"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
dependencies = [
"crunchy",
]
[[package]]
name = "image"
version = "0.24.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"exr",
"gif",
"jpeg-decoder",
"num-rational",
"num-traits",
"png",
"qoi",
"tiff",
]
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
dependencies = [
"rayon",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lebe"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
"simd-adler32",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "png"
version = "0.17.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64"
dependencies = [
"bitflags",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sdl2"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
dependencies = [
"bitflags",
"lazy_static",
"libc",
"sdl2-sys",
]
[[package]]
name = "sdl2-sys"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26bcacfdd45d539fb5785049feb0038a63931aa896c7763a2a12e125ec58bd29"
dependencies = [
"cfg-if",
"libc",
"version-compare",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "smallvec"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "tiff"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]]
name = "version-compare"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
[[package]]
name = "weezl"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]

12
Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "game"
version = "0.1.0"
edition = "2021"
[dependencies]
image = "0.24.7"
[dependencies.sdl2]
version = "0.36"
default-features = false
#features = ["ttf"]

74
src/doodad.rs Normal file
View File

@ -0,0 +1,74 @@
use sdl2::rect::{Point, Rect};
use sdl2::render::Canvas;
use sdl2::video::Window;
use crate::physics::PhysVec;
use crate::Camera;
use crate::Drawable;
use crate::SpriteAtlas;
pub struct Doodad {
sprite: String,
pos: PhysVec,
scale: u32,
}
impl Doodad {
pub fn new(pos: PhysVec) -> Self {
Doodad {
sprite: "a0.png".to_owned(),
pos,
scale: 1,
}
}
}
impl Drawable for Doodad {
fn position(&self) -> PhysVec {
self.pos
}
fn draw(
&self,
canvas: &mut Canvas<Window>,
sa: &SpriteAtlas,
c: &Camera,
) -> Result<(), String> {
let win_size = PhysVec::from(canvas.window().size());
let draw_pos = self.screen_position(canvas, &c);
// Don't draw if off screen
if draw_pos.x < 0.0
|| draw_pos.x > win_size.x
|| draw_pos.y < 0.0
|| draw_pos.y > win_size.y
{
return Ok(());
}
let sprite = sa.get(&self.sprite);
let mut dest_rect_0 = Rect::new(0, 0, 112 / self.scale, 112 / self.scale);
dest_rect_0.center_on(Point::new(
112 / (2 * self.scale) as i32,
112 / (2 * self.scale) as i32,
));
// set the current frame for time
dest_rect_0.set_x(draw_pos.x.round() as i32);
dest_rect_0.set_y(draw_pos.y.round() as i32);
// copy the frame to the canvas
canvas.copy_ex(
&sprite.texture,
Some(sprite.rect),
Some(dest_rect_0),
0.0, // angle
Point::new(120 / (2 * self.scale) as i32, 270 / (2 * self.scale) as i32), // center
false,
false,
)?;
return Ok(());
}
}

48
src/inputstatus.rs Normal file
View File

@ -0,0 +1,48 @@
use sdl2::{event::Event, keyboard::Keycode};
// TODO: no boolean modification (no pub)
pub struct InputStatus {
pub key_left: bool,
pub key_right: bool,
pub key_thrust: bool,
}
impl InputStatus {
pub fn new() -> Self {
InputStatus {
key_left: false,
key_right: false,
key_thrust: false,
}
}
fn handle_keyup(&mut self, keycode: Keycode) {
match keycode {
Keycode::Left => self.key_left = false,
Keycode::Right => self.key_right = false,
Keycode::Up => self.key_thrust = false,
_ => {}
}
}
fn handle_keydown(&mut self, keycode: Keycode) {
match keycode {
Keycode::Left => self.key_left = true,
Keycode::Right => self.key_right = true,
Keycode::Up => self.key_thrust = true,
_ => {}
}
}
pub fn update(&mut self, event: Event) {
match event {
Event::KeyDown {
keycode: Some(key), ..
} => self.handle_keydown(key),
Event::KeyUp {
keycode: Some(key), ..
} => self.handle_keyup(key),
_ => {}
}
}
}

175
src/main.rs Normal file
View File

@ -0,0 +1,175 @@
use sdl2::{event::Event, keyboard::Keycode, render::Canvas, video::Window};
use std::{time::Duration, time::Instant};
mod doodad;
mod inputstatus;
mod physics;
mod ship;
mod spriteatlas;
use crate::doodad::Doodad;
use crate::inputstatus::InputStatus;
use crate::physics::PhysVec;
use crate::ship::Ship;
use crate::spriteatlas::SpriteAtlas;
trait Drawable {
/// Return the world position of this object
fn position(&self) -> PhysVec;
/// Get the position of this drawable on the screen
/// (0, 0) is at top-left corner.
///
/// Returned position is this object's center.
fn screen_position(&self, canvas: &mut Canvas<Window>, c: &Camera) -> PhysVec {
let win_size = PhysVec::from(canvas.window().size());
let h = win_size / 2.0;
return self.position() - c.pos + h;
}
// Draw this item on the screen
fn draw(&self, canvas: &mut Canvas<Window>, sa: &SpriteAtlas, c: &Camera)
-> Result<(), String>;
}
enum ShipKind {
Gypsum,
}
impl ShipKind {
fn sprite(&self) -> &'static str {
match self {
Self::Gypsum => "gypsum.png",
}
}
}
struct Camera {
pos: PhysVec,
}
impl Camera {
fn new() -> Self {
Camera {
pos: PhysVec::zero(),
}
}
}
static FTL: f64 = 1.0 / 140.0; // frame time limit
struct System {
decor: Vec<Box<dyn Drawable>>,
}
impl System {
fn new() -> Self {
let mut s = System { decor: Vec::new() };
s.decor.push(Box::new(Doodad::new(PhysVec::zero())));
s.decor
.push(Box::new(Doodad::new(PhysVec { x: 100.0, y: 23.0 })));
return s;
}
fn draw(
&self,
canvas: &mut Canvas<Window>,
sa: &SpriteAtlas,
c: &Camera,
) -> Result<(), String> {
for d in &self.decor {
d.draw(canvas, sa, c)?
}
return Ok(());
}
}
fn main() -> Result<(), String> {
let sdl_context = sdl2::init()?;
let video_subsystem = sdl_context.video()?;
let window = video_subsystem
.window("SDL2", 640, 480)
.position_centered()
.resizable()
.build()
.map_err(|e| e.to_string())?;
let mut canvas = window
.into_canvas()
.accelerated()
.build()
.map_err(|e| e.to_string())?;
let texture_creator = canvas.texture_creator();
let sa = SpriteAtlas::new(&texture_creator)?;
let mut event_pump = sdl_context.event_pump()?;
let system = System::new();
let mut i = InputStatus::new();
let mut c = Camera::new();
let mut s = Ship::new(ShipKind::Gypsum);
//let mut last_frame_time = 0f64;
let mut frame_start;
let mut running = true;
while running {
frame_start = Instant::now();
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => {
running = false;
}
_ => i.update(event),
}
}
c.pos = s.body.pos;
// Draw
canvas.clear();
system.draw(&mut canvas, &sa, &c)?;
s.draw(&mut canvas, &sa, &c)?;
canvas.present();
let frame_time = frame_start.elapsed().as_secs_f64();
// Wait
// (limit frame rate)
if frame_time < FTL {
std::thread::sleep(Duration::from_secs_f64(FTL - frame_time));
}
// Apply input
let frame_time = frame_start.elapsed().as_secs_f64();
//last_frame_time = frame_time;
s.body.tick(frame_time);
if i.key_thrust {
s.body.thrust(50.0 * frame_time);
}
if i.key_right {
s.body.rot(-1.0 * frame_time);
}
if i.key_left {
s.body.rot(1.0 * frame_time);
}
}
Ok(())
}

75
src/ship.rs Normal file
View File

@ -0,0 +1,75 @@
use sdl2::rect::{Point, Rect};
use sdl2::render::Canvas;
use sdl2::video::Window;
use crate::physics::PhysBody;
use crate::physics::PhysVec;
use crate::Camera;
use crate::Drawable;
use crate::ShipKind;
use crate::SpriteAtlas;
pub struct Ship {
pub body: PhysBody,
kind: ShipKind,
scale: u32,
}
impl Ship {
pub fn new(kind: ShipKind) -> Self {
Ship {
body: PhysBody::new(),
kind,
scale: 2,
}
}
}
impl Drawable for Ship {
fn position(&self) -> PhysVec {
return self.body.pos;
}
fn draw(
&self,
canvas: &mut Canvas<Window>,
sa: &SpriteAtlas,
c: &Camera,
) -> Result<(), String> {
let win_size = PhysVec::from(canvas.window().size());
let draw_pos = self.screen_position(canvas, &c);
// Don't draw if off screen
if draw_pos.x < 0.0
|| draw_pos.x > win_size.x
|| draw_pos.y < 0.0
|| draw_pos.y > win_size.y
{
return Ok(());
}
let sprite = sa.get(self.kind.sprite());
let mut dest_rect_0 = Rect::new(0, 0, 120 / self.scale, 270 / self.scale);
dest_rect_0.center_on(Point::new(
120 / (2 * self.scale) as i32,
270 / (2 * self.scale) as i32,
));
// set the current frame for time
dest_rect_0.set_x(draw_pos.x.round() as i32);
dest_rect_0.set_y(draw_pos.y.round() as i32);
// copy the frame to the canvas
canvas.copy_ex(
&sprite.texture,
Some(sprite.rect),
Some(dest_rect_0),
self.body.angle.to_degrees(), // angle
Point::new(120 / (2 * self.scale) as i32, 270 / (2 * self.scale) as i32), // center
false,
false,
)?;
return Ok(());
}
}

73
src/spriteatlas.rs Normal file
View File

@ -0,0 +1,73 @@
use image::io::Reader as ImageReader;
use sdl2::{
rect::Rect,
render::{Texture, TextureCreator},
video::WindowContext,
};
use std::collections::HashMap;
/// A handle for a sprite inside a SpriteAtlas
pub struct Sprite<'a> {
pub texture: &'a Texture<'a>,
pub rect: Rect,
}
/// A cache of textures we use when drawing the screen.
///
/// This is implemented very carefully, since SDL2 textures have tricky lifetimes.
pub struct SpriteAtlas<'a> {
data: HashMap<String, (Texture<'a>, Rect)>,
}
impl<'a> SpriteAtlas<'a> {
pub fn new(texture_creator: &'a TextureCreator<WindowContext>) -> Result<Self, String> {
let mut b = Self {
data: HashMap::new(),
};
b.load_one(texture_creator, "gypsum.png")?;
b.load_one(texture_creator, "a0.png")?;
return Ok(b);
}
pub fn get(&'a self, name: &str) -> Sprite<'a> {
let (texture, rect) = self.data.get(name).unwrap();
return Sprite {
texture,
rect: rect.clone(),
};
}
fn load_one(
&mut self,
texture_creator: &'a TextureCreator<WindowContext>,
s: &str,
) -> Result<(), String> {
let im = ImageReader::open(format!("assets/{s}"))
.unwrap()
.decode()
.unwrap();
let width = im.width();
let height = im.height();
let mut im = im.as_bytes().to_vec();
let surface = sdl2::surface::Surface::from_data(
&mut im,
width,
height,
width * 4,
sdl2::pixels::PixelFormatEnum::RGBA32,
)?;
let texture = texture_creator
.create_texture_from_surface(&surface)
.map_err(|e| e.to_string())?;
self.data
.insert(s.to_owned(), (texture, Rect::new(0, 0, width, height)));
return Ok(());
}
}