Coordinate cleanup
parent
6631096acf
commit
8fc23dd359
|
@ -38,6 +38,12 @@ version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "c_vec"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -382,6 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
|
checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"c_vec",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
"sdl2-sys",
|
"sdl2-sys",
|
||||||
|
|
|
@ -10,4 +10,4 @@ rand = "0.8.5"
|
||||||
[dependencies.sdl2]
|
[dependencies.sdl2]
|
||||||
version = "0.36"
|
version = "0.36"
|
||||||
default-features = false
|
default-features = false
|
||||||
#features = ["ttf"]
|
features = ["ttf", "gfx"]
|
||||||
|
|
|
@ -1,33 +1,29 @@
|
||||||
use sdl2::render::Canvas;
|
use sdl2::gfx::primitives::DrawRenderer;
|
||||||
use sdl2::video::Window;
|
use sdl2::pixels::Color;
|
||||||
|
|
||||||
use crate::physics::PhysVec;
|
use crate::DrawContext;
|
||||||
use crate::physics::Position;
|
|
||||||
use crate::Camera;
|
|
||||||
use crate::Drawable;
|
use crate::Drawable;
|
||||||
use crate::SpriteAtlas;
|
use crate::SpriteAtlas;
|
||||||
|
use crate::WorldPosition;
|
||||||
|
|
||||||
pub struct Doodad {
|
pub struct Doodad {
|
||||||
pub sprite: String,
|
pub sprite: String,
|
||||||
pub pos: Position,
|
pub pos: WorldPosition,
|
||||||
pub scale: u32,
|
pub scale: u32,
|
||||||
pub angle: f64,
|
pub angle: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawable for Doodad {
|
impl Drawable for Doodad {
|
||||||
fn position(&self) -> PhysVec {
|
fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> {
|
||||||
self.pos.to_cartesian().into()
|
let pos = self.pos.screen_position(dc);
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
canvas: &mut Canvas<Window>,
|
|
||||||
sa: &SpriteAtlas,
|
|
||||||
c: &Camera,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let pos = self.screen_position(canvas, c);
|
|
||||||
let sprite = sa.get(&self.sprite);
|
let sprite = sa.get(&self.sprite);
|
||||||
sprite.draw(canvas, pos, self.angle, 1.0)?;
|
sprite.draw(dc.canvas, pos, self.angle, 1.0)?;
|
||||||
|
|
||||||
|
let p = self.pos.screen_position_real(dc);
|
||||||
|
dc.canvas
|
||||||
|
.aa_circle(p.x as i16, p.y as i16, 5, Color::RGB(255, 0, 0))?;
|
||||||
|
dc.canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
127
src/main.rs
127
src/main.rs
|
@ -1,4 +1,3 @@
|
||||||
use rand::Rng;
|
|
||||||
use sdl2::{event::Event, keyboard::Keycode, render::Canvas, video::Window};
|
use sdl2::{event::Event, keyboard::Keycode, render::Canvas, video::Window};
|
||||||
use std::{time::Duration, time::Instant};
|
use std::{time::Duration, time::Instant};
|
||||||
|
|
||||||
|
@ -7,112 +6,59 @@ mod inputstatus;
|
||||||
mod physics;
|
mod physics;
|
||||||
mod ship;
|
mod ship;
|
||||||
mod sprite;
|
mod sprite;
|
||||||
|
mod system;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
doodad::Doodad, inputstatus::InputStatus, physics::PhysVec, physics::Position, ship::Ship,
|
doodad::Doodad, inputstatus::InputStatus, physics::Cartesian, physics::WorldPosition,
|
||||||
sprite::SpriteAtlas,
|
ship::Ship, ship::ShipKind, sprite::SpriteAtlas, system::System,
|
||||||
};
|
};
|
||||||
|
|
||||||
trait Drawable {
|
trait Drawable {
|
||||||
/// Return the world position of the center 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
|
// Draw this item on the screen
|
||||||
fn draw(&self, canvas: &mut Canvas<Window>, sa: &SpriteAtlas, c: &Camera)
|
fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String>;
|
||||||
-> Result<(), String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ShipKind {
|
|
||||||
Gypsum,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ShipKind {
|
|
||||||
fn sprite(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::Gypsum => "gypsum.png",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Camera {
|
struct Camera {
|
||||||
pos: PhysVec,
|
pos: Cartesian,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Camera {
|
impl Camera {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Camera {
|
Camera {
|
||||||
pos: PhysVec { x: 0.0, y: 0.0 },
|
pos: Cartesian::new(0.0, 0.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static FTL: f64 = 1.0 / 200.0; // frame time limit
|
static FTL: f64 = 1.0 / 200.0; // frame time limit
|
||||||
|
|
||||||
struct System {
|
struct DrawContext<'a> {
|
||||||
bodies: Vec<Box<dyn Drawable>>,
|
canvas: &'a mut Canvas<Window>,
|
||||||
|
camera: Camera,
|
||||||
|
|
||||||
|
// Dimensions of the window
|
||||||
|
window_size: Cartesian,
|
||||||
|
|
||||||
|
// Position of the top-left corner of the window,
|
||||||
|
// relative to camera position.
|
||||||
|
top_left: Cartesian,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System {
|
impl<'a> DrawContext<'a> {
|
||||||
fn new() -> Self {
|
fn new(canvas: &'a mut Canvas<Window>, camera: Camera) -> Self {
|
||||||
let mut s = System { bodies: Vec::new() };
|
let mut s = Self {
|
||||||
|
canvas,
|
||||||
let mut num = rand::thread_rng();
|
camera,
|
||||||
for _ in 0..25 {
|
window_size: Cartesian::new(0.0, 0.0),
|
||||||
s.bodies.push(Box::new(Doodad {
|
top_left: Cartesian::new(0.0, 0.0),
|
||||||
sprite: "small.png".to_owned(),
|
};
|
||||||
pos: Position::new_cartesian(
|
s.update();
|
||||||
num.gen_range(-500.0..500.0),
|
|
||||||
num.gen_range(-500.0..500.0),
|
|
||||||
),
|
|
||||||
scale: 1,
|
|
||||||
angle: num.gen_range(-180f64..180f64).to_radians(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
s.bodies.push(Box::new(Doodad {
|
|
||||||
pos: Position::new_cartesian(0.0, 0.0),
|
|
||||||
sprite: "a0.png".to_owned(),
|
|
||||||
scale: 1,
|
|
||||||
angle: 0.0,
|
|
||||||
}));
|
|
||||||
|
|
||||||
s.bodies.push(Box::new(Doodad {
|
|
||||||
pos: Position::new_polar(PhysVec { x: 0.0, y: 0.0 }, 300.0, 31.0),
|
|
||||||
sprite: "earth.png".to_owned(),
|
|
||||||
scale: 1,
|
|
||||||
angle: (180f64).to_radians(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the state of this body after t seconds.
|
fn update(&mut self) {
|
||||||
pub fn tick(&mut self, _t: f64) {
|
self.window_size = Cartesian::from(self.canvas.window().size());
|
||||||
//let body = &mut self.bodies[1];
|
self.top_left = (self.window_size / 2.0) * Cartesian::new(-1.0, 1.0);
|
||||||
//body.pos.angle += 0.1 * t;
|
|
||||||
//body.angle -= 0.1 * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
canvas: &mut Canvas<Window>,
|
|
||||||
sa: &SpriteAtlas,
|
|
||||||
c: &Camera,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
for body in &self.bodies {
|
|
||||||
body.draw(canvas, sa, c)?;
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,16 +86,19 @@ fn main() -> Result<(), String> {
|
||||||
let mut system = System::new();
|
let mut system = System::new();
|
||||||
|
|
||||||
let mut i = InputStatus::new();
|
let mut i = InputStatus::new();
|
||||||
let mut c = Camera::new();
|
|
||||||
|
|
||||||
let mut s = Ship::new(ShipKind::Gypsum, Position::new_cartesian(0.0, 0.0));
|
let mut s = Ship::new(ShipKind::Gypsum, Cartesian::new(0.0, 0.0));
|
||||||
|
|
||||||
//let mut last_frame_time = 0f64;
|
//let mut last_frame_time = 0f64;
|
||||||
let mut frame_start;
|
let mut frame_start;
|
||||||
|
|
||||||
let mut running = true;
|
let mut running = true;
|
||||||
|
|
||||||
|
let mut dc = DrawContext::new(&mut canvas, Camera::new());
|
||||||
|
|
||||||
while running {
|
while running {
|
||||||
frame_start = Instant::now();
|
frame_start = Instant::now();
|
||||||
|
dc.update();
|
||||||
|
|
||||||
for event in event_pump.poll_iter() {
|
for event in event_pump.poll_iter() {
|
||||||
match event {
|
match event {
|
||||||
|
@ -164,13 +113,13 @@ fn main() -> Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.pos = s.body.pos;
|
dc.camera.pos = s.body.pos;
|
||||||
|
|
||||||
// Draw
|
// Draw
|
||||||
canvas.clear();
|
dc.canvas.clear();
|
||||||
system.draw(&mut canvas, &sa, &c)?;
|
system.draw(&mut dc, &sa)?;
|
||||||
s.draw(&mut canvas, &sa, &c)?;
|
s.draw(&mut dc, &sa)?;
|
||||||
canvas.present();
|
dc.canvas.present();
|
||||||
|
|
||||||
let frame_time = frame_start.elapsed().as_secs_f64();
|
let frame_time = frame_start.elapsed().as_secs_f64();
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
use crate::physics::PhysVec;
|
use crate::physics::Cartesian;
|
||||||
use std::f64::consts::{PI, TAU};
|
use std::f64::consts::{PI, TAU};
|
||||||
|
|
||||||
pub struct PhysBody {
|
pub struct PhysBody {
|
||||||
pub pos: PhysVec,
|
pub pos: Cartesian,
|
||||||
pub vel: PhysVec,
|
pub vel: Cartesian,
|
||||||
pub mass: f64,
|
pub mass: f64,
|
||||||
pub angle: f64, // In radians
|
pub angle: f64, // In radians
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhysBody {
|
impl PhysBody {
|
||||||
pub fn new(pos: PhysVec) -> Self {
|
pub fn new(pos: Cartesian) -> Self {
|
||||||
return PhysBody {
|
return PhysBody {
|
||||||
pos,
|
pos,
|
||||||
vel: PhysVec { x: 0.0, y: 0.0 },
|
vel: Cartesian::new(0.0, 0.0),
|
||||||
mass: 1.0,
|
mass: 0.3,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -24,18 +24,14 @@ impl PhysBody {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply an instantaneous force to this object
|
/// Apply an instantaneous force to this object
|
||||||
pub fn force(&mut self, v: PhysVec) {
|
pub fn force(&mut self, v: Cartesian) {
|
||||||
self.vel += v / self.mass;
|
self.vel += v / self.mass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply force in the direction this object is pointing.
|
/// Apply force in the direction this object is pointing.
|
||||||
pub fn thrust(&mut self, f: f64) {
|
pub fn thrust(&mut self, f: f64) {
|
||||||
self.force(
|
let l = Cartesian::new(self.angle.sin(), self.angle.cos()) * f;
|
||||||
PhysVec {
|
self.force(l);
|
||||||
x: self.angle.sin(),
|
|
||||||
y: -self.angle.cos(),
|
|
||||||
} * f,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate this object by `a` radians.
|
// Rotate this object by `a` radians.
|
||||||
|
|
|
@ -1,21 +1,27 @@
|
||||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct PhysVec {
|
pub struct Cartesian {
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
pub y: f64,
|
pub y: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(u32, u32)> for PhysVec {
|
impl Cartesian {
|
||||||
|
pub fn new(x: f64, y: f64) -> Self {
|
||||||
|
Cartesian { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<(u32, u32)> for Cartesian {
|
||||||
fn from(value: (u32, u32)) -> Self {
|
fn from(value: (u32, u32)) -> Self {
|
||||||
PhysVec {
|
Cartesian {
|
||||||
x: value.0 as f64,
|
x: value.0 as f64,
|
||||||
y: value.1 as f64,
|
y: value.1 as f64,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add for PhysVec {
|
impl Add for Cartesian {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn add(self, other: Self) -> Self {
|
fn add(self, other: Self) -> Self {
|
||||||
|
@ -26,14 +32,14 @@ impl Add for PhysVec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign for PhysVec {
|
impl AddAssign for Cartesian {
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
self.x += rhs.x;
|
self.x += rhs.x;
|
||||||
self.y += rhs.y;
|
self.y += rhs.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub for PhysVec {
|
impl Sub for Cartesian {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn sub(self, other: Self) -> Self {
|
fn sub(self, other: Self) -> Self {
|
||||||
|
@ -44,15 +50,33 @@ impl Sub for PhysVec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubAssign for PhysVec {
|
impl SubAssign for Cartesian {
|
||||||
fn sub_assign(&mut self, rhs: Self) {
|
fn sub_assign(&mut self, rhs: Self) {
|
||||||
self.x += rhs.x;
|
self.x += rhs.x;
|
||||||
self.y += rhs.y;
|
self.y += rhs.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<f64> for PhysVec {
|
impl Mul for Cartesian {
|
||||||
type Output = PhysVec;
|
type Output = Self;
|
||||||
|
|
||||||
|
fn mul(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x * other.x,
|
||||||
|
y: self.y * other.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MulAssign for Cartesian {
|
||||||
|
fn mul_assign(&mut self, rhs: Self) {
|
||||||
|
self.x *= rhs.x;
|
||||||
|
self.y *= rhs.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f64> for Cartesian {
|
||||||
|
type Output = Cartesian;
|
||||||
fn mul(self, rhs: f64) -> Self::Output {
|
fn mul(self, rhs: f64) -> Self::Output {
|
||||||
Self {
|
Self {
|
||||||
x: self.x * rhs,
|
x: self.x * rhs,
|
||||||
|
@ -61,15 +85,15 @@ impl Mul<f64> for PhysVec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MulAssign<f64> for PhysVec {
|
impl MulAssign<f64> for Cartesian {
|
||||||
fn mul_assign(&mut self, rhs: f64) {
|
fn mul_assign(&mut self, rhs: f64) {
|
||||||
self.x *= rhs;
|
self.x *= rhs;
|
||||||
self.y *= rhs;
|
self.y *= rhs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Div<f64> for PhysVec {
|
impl Div<f64> for Cartesian {
|
||||||
type Output = PhysVec;
|
type Output = Cartesian;
|
||||||
fn div(self, rhs: f64) -> Self::Output {
|
fn div(self, rhs: f64) -> Self::Output {
|
||||||
Self {
|
Self {
|
||||||
x: self.x / rhs,
|
x: self.x / rhs,
|
||||||
|
@ -78,7 +102,7 @@ impl Div<f64> for PhysVec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DivAssign<f64> for PhysVec {
|
impl DivAssign<f64> for Cartesian {
|
||||||
fn div_assign(&mut self, rhs: f64) {
|
fn div_assign(&mut self, rhs: f64) {
|
||||||
self.x /= rhs;
|
self.x /= rhs;
|
||||||
self.y /= rhs;
|
self.y /= rhs;
|
|
@ -1,9 +1,9 @@
|
||||||
mod body;
|
mod body;
|
||||||
mod physpol;
|
mod cartesian;
|
||||||
mod physvec;
|
mod polar;
|
||||||
mod position;
|
mod worldposition;
|
||||||
|
|
||||||
pub use body::PhysBody;
|
pub use body::PhysBody;
|
||||||
pub use physpol::PhysPol;
|
pub use cartesian::Cartesian;
|
||||||
pub use physvec::PhysVec;
|
pub use polar::Polar;
|
||||||
pub use position::Position;
|
pub use worldposition::WorldPosition;
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
use super::PhysVec;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct PhysPol {
|
|
||||||
pub center: PhysVec,
|
|
||||||
pub radius: f64,
|
|
||||||
pub angle: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhysPol {
|
|
||||||
pub fn to_cartesian(&self) -> PhysVec {
|
|
||||||
return PhysVec {
|
|
||||||
x: self.radius * self.angle.sin(),
|
|
||||||
y: self.radius * self.angle.cos(),
|
|
||||||
} + self.center;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
use super::Cartesian;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Polar {
|
||||||
|
pub center: Cartesian,
|
||||||
|
pub radius: f64,
|
||||||
|
pub angle: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Cartesian> for Polar {
|
||||||
|
fn into(self) -> Cartesian {
|
||||||
|
return Cartesian::new(
|
||||||
|
self.radius * self.angle.sin(),
|
||||||
|
self.radius * self.angle.cos(),
|
||||||
|
) + self.center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
use super::PhysPol;
|
|
||||||
use super::PhysVec;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum Position {
|
|
||||||
Cartesian(PhysVec),
|
|
||||||
Polar(PhysPol),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A convenient wrapper around PhysVec and PhysPol
|
|
||||||
/// that allows us to position game objects (like doodads)
|
|
||||||
/// with whatever scheme we want.
|
|
||||||
///
|
|
||||||
/// This should ONLY be used for game objects, physics
|
|
||||||
/// calculations should always use PhysVec.
|
|
||||||
impl Position {
|
|
||||||
pub fn new_cartesian(x: f64, y: f64) -> Self {
|
|
||||||
return Self::Cartesian(PhysVec { x, y });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_polar(center: PhysVec, radius: f64, angle: f64) -> Self {
|
|
||||||
return Self::Polar(PhysPol {
|
|
||||||
center,
|
|
||||||
radius,
|
|
||||||
angle,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn to_polar(&self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Cartesian(pv) => Self::Polar(pv.to_polar()),
|
|
||||||
Self::Polar(_) => self.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn to_cartesian(&self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Cartesian(_) => self.clone(),
|
|
||||||
Self::Polar(pp) => Self::Cartesian(pp.to_cartesian()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<PhysVec> for Position {
|
|
||||||
fn into(self) -> PhysVec {
|
|
||||||
let c = self.to_cartesian();
|
|
||||||
match c {
|
|
||||||
Self::Cartesian(pv) => pv,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
use super::Cartesian;
|
||||||
|
use crate::DrawContext;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
||||||
|
pub struct WorldPosition {
|
||||||
|
pub pos: Cartesian, // True world position
|
||||||
|
pub par: f64, // Parallax factor
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldPosition {
|
||||||
|
pub fn new(pos: Cartesian, par: f64) -> Self {
|
||||||
|
WorldPosition { pos: pos, par }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_screen_position(dc: &DrawContext, pos: Cartesian, par: f64) -> Self {
|
||||||
|
WorldPosition {
|
||||||
|
par,
|
||||||
|
pos: ((pos * Cartesian::new(1.0, -1.0)) + dc.top_left) * par + dc.camera.pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the position of this drawable on the screen
|
||||||
|
/// (0, 0) is at top-left corner.
|
||||||
|
///
|
||||||
|
/// Returned position is this object's center.
|
||||||
|
pub fn screen_position(&self, dc: &DrawContext) -> Cartesian {
|
||||||
|
let par = self.par;
|
||||||
|
let pos: Cartesian = self.pos.into();
|
||||||
|
return (((pos - dc.camera.pos) / par) - dc.top_left) * Cartesian::new(1.0, -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the position of this drawable on the screen
|
||||||
|
/// (0, 0) is at top-left corner.
|
||||||
|
///
|
||||||
|
/// Returned position is this object's center.
|
||||||
|
pub fn screen_position_real(&self, dc: &DrawContext) -> Cartesian {
|
||||||
|
let pos: Cartesian = self.pos.into();
|
||||||
|
return ((pos - dc.camera.pos) - dc.top_left) * Cartesian::new(1.0, -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this object on screen?
|
||||||
|
fn on_screen(&self, dc: &DrawContext) -> bool {
|
||||||
|
let pos = self.screen_position(dc);
|
||||||
|
let (width, height) = (10, 10); //TODO: actual
|
||||||
|
|
||||||
|
// Don't draw if we're not on the screen.
|
||||||
|
// An offset is included to ensure we're completely
|
||||||
|
// off the screen. We add the whole width intentionally.
|
||||||
|
return !(pos.x < -1.0 * (width as f64)
|
||||||
|
|| pos.x > dc.window_size.x + width as f64
|
||||||
|
|| pos.y < -1.0 * (height as f64)
|
||||||
|
|| pos.y > dc.window_size.y + height as f64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Cartesian> for WorldPosition {
|
||||||
|
fn into(self) -> Cartesian {
|
||||||
|
self.pos.into()
|
||||||
|
}
|
||||||
|
}
|
48
src/ship.rs
48
src/ship.rs
|
@ -1,42 +1,48 @@
|
||||||
use sdl2::render::Canvas;
|
use crate::physics::Cartesian;
|
||||||
use sdl2::video::Window;
|
|
||||||
|
|
||||||
use crate::physics::PhysBody;
|
use crate::physics::PhysBody;
|
||||||
use crate::physics::PhysVec;
|
use crate::DrawContext;
|
||||||
use crate::physics::Position;
|
|
||||||
use crate::Camera;
|
|
||||||
use crate::Drawable;
|
use crate::Drawable;
|
||||||
use crate::ShipKind;
|
|
||||||
use crate::SpriteAtlas;
|
use crate::SpriteAtlas;
|
||||||
|
|
||||||
|
pub enum ShipKind {
|
||||||
|
Gypsum,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShipKind {
|
||||||
|
fn sprite(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Gypsum => "gypsum.png",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Ship {
|
pub struct Ship {
|
||||||
pub body: PhysBody,
|
pub body: PhysBody,
|
||||||
kind: ShipKind,
|
kind: ShipKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ship {
|
impl Ship {
|
||||||
pub fn new(kind: ShipKind, pos: Position) -> Self {
|
pub fn new(kind: ShipKind, pos: Cartesian) -> Self {
|
||||||
Ship {
|
Ship {
|
||||||
body: PhysBody::new(pos.into()),
|
body: PhysBody::new(pos),
|
||||||
kind,
|
kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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, dc: &DrawContext) -> Cartesian {
|
||||||
|
return ((self.body.pos - dc.camera.pos) - dc.top_left) * Cartesian::new(1.0, -1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawable for Ship {
|
impl Drawable for Ship {
|
||||||
fn position(&self) -> PhysVec {
|
fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> {
|
||||||
return self.body.pos;
|
let pos = self.screen_position(dc);
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
canvas: &mut Canvas<Window>,
|
|
||||||
sa: &SpriteAtlas,
|
|
||||||
c: &Camera,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let pos = self.screen_position(canvas, c);
|
|
||||||
let sprite = sa.get(self.kind.sprite());
|
let sprite = sa.get(self.kind.sprite());
|
||||||
sprite.draw(canvas, pos, self.body.angle.to_degrees(), 1.0)?;
|
sprite.draw(dc.canvas, pos, self.body.angle.to_degrees(), 1.0)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use sdl2::{
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::physics::PhysVec;
|
use crate::physics::Cartesian;
|
||||||
|
|
||||||
/// A handle for a sprite inside a SpriteAtlas
|
/// A handle for a sprite inside a SpriteAtlas
|
||||||
pub struct Sprite<'a> {
|
pub struct Sprite<'a> {
|
||||||
|
@ -24,11 +24,11 @@ impl<'a> Sprite<'a> {
|
||||||
pub fn draw(
|
pub fn draw(
|
||||||
&self,
|
&self,
|
||||||
canvas: &mut Canvas<Window>,
|
canvas: &mut Canvas<Window>,
|
||||||
position: PhysVec,
|
position: Cartesian,
|
||||||
angle: f64,
|
angle: f64,
|
||||||
scale: f64,
|
scale: f64,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let win_size = PhysVec::from(canvas.window().size());
|
let win_size = Cartesian::from(canvas.window().size());
|
||||||
let scale = scale * self.scale;
|
let scale = scale * self.scale;
|
||||||
|
|
||||||
// Post-scale dimensions on the screen
|
// Post-scale dimensions on the screen
|
||||||
|
@ -60,6 +60,17 @@ impl<'a> Sprite<'a> {
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
/*
|
||||||
|
canvas.set_draw_color(Color::RGB(255, 0, 0));
|
||||||
|
canvas.aa_circle(
|
||||||
|
position.x as i16,
|
||||||
|
position.y as i16,
|
||||||
|
5,
|
||||||
|
Color::RGB(255, 0, 0),
|
||||||
|
)?;
|
||||||
|
canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||||
|
*/
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
use crate::{
|
||||||
|
physics::Cartesian, physics::Polar, physics::WorldPosition, sprite::SpriteAtlas, Doodad,
|
||||||
|
DrawContext, Drawable,
|
||||||
|
};
|
||||||
|
use rand::Rng;
|
||||||
|
use sdl2::{gfx::primitives::DrawRenderer, pixels::Color};
|
||||||
|
|
||||||
|
struct StarField {
|
||||||
|
stars: Vec<WorldPosition>,
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StarField {
|
||||||
|
fn new(width: f64, height: f64, d: f64) -> Self {
|
||||||
|
let mut s = StarField {
|
||||||
|
stars: Vec::new(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut num = rand::thread_rng();
|
||||||
|
let area = (width / 100.0) * (height / 100.0);
|
||||||
|
let n = (area * d) as i32;
|
||||||
|
for _ in 0..n {
|
||||||
|
s.stars.push(WorldPosition::new(
|
||||||
|
Cartesian::new(
|
||||||
|
num.gen_range(-width / 2.0..width / 2.0),
|
||||||
|
num.gen_range(-height / 2.0..height / 2.0),
|
||||||
|
),
|
||||||
|
num.gen_range(3f64..4f64),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_with_offset(
|
||||||
|
&self,
|
||||||
|
dc: &DrawContext,
|
||||||
|
pos_in_field: Cartesian,
|
||||||
|
offset: Cartesian,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
for wp in &self.stars {
|
||||||
|
// Coordinate of star on screen,
|
||||||
|
// with (0, 0) at top left
|
||||||
|
let p: Cartesian = wp.pos.into();
|
||||||
|
let q =
|
||||||
|
((p - pos_in_field + offset) / wp.par - dc.top_left) * Cartesian::new(1.0, -1.0);
|
||||||
|
|
||||||
|
dc.canvas.filled_circle(
|
||||||
|
q.x as i16,
|
||||||
|
q.y as i16,
|
||||||
|
(5.0 - (1.0 * wp.par)) as i16,
|
||||||
|
Color::RGBA(255, 255, 255, 100),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self, dc: &mut DrawContext, _sa: &SpriteAtlas) -> Result<(), String> {
|
||||||
|
let w = self.width;
|
||||||
|
let h = self.height;
|
||||||
|
let ww = w / 2.0;
|
||||||
|
let hh = h / 2.0;
|
||||||
|
|
||||||
|
let pos_in_field = Cartesian::new(
|
||||||
|
dc.camera.pos.x.signum() * (((dc.camera.pos.x.abs() + ww) % w) - ww),
|
||||||
|
dc.camera.pos.y.signum() * (((dc.camera.pos.y.abs() + hh) % h) - hh),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Field center in world coordinates
|
||||||
|
let field_center = dc.camera.pos - pos_in_field;
|
||||||
|
|
||||||
|
// screen coordinates of top left position of starfield
|
||||||
|
let field_tl =
|
||||||
|
WorldPosition::new(field_center + Cartesian::new(-ww, hh), 3.0).screen_position(dc);
|
||||||
|
|
||||||
|
let field_br =
|
||||||
|
WorldPosition::new(field_center + Cartesian::new(ww, -hh), 3.0).screen_position(dc);
|
||||||
|
|
||||||
|
dc.canvas.aa_circle(
|
||||||
|
field_tl.x as i16,
|
||||||
|
field_tl.y as i16,
|
||||||
|
5,
|
||||||
|
Color::RGB(255, 0, 0),
|
||||||
|
)?;
|
||||||
|
dc.canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||||
|
|
||||||
|
let north = field_tl.y > 0.0;
|
||||||
|
let south = field_br.y < dc.window_size.y;
|
||||||
|
let east = field_br.x < dc.window_size.x;
|
||||||
|
let west = field_tl.x > 0.0;
|
||||||
|
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(0.0, 0.0))?;
|
||||||
|
if north {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(0.0, h))?;
|
||||||
|
}
|
||||||
|
if south {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(0.0, -h))?;
|
||||||
|
}
|
||||||
|
if east {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(w, 0.0))?;
|
||||||
|
}
|
||||||
|
if west {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(-w, 0.0))?;
|
||||||
|
}
|
||||||
|
if north && east {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(w, h))?;
|
||||||
|
}
|
||||||
|
if north && west {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(-w, h))?;
|
||||||
|
}
|
||||||
|
if south && east {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(w, -h))?;
|
||||||
|
}
|
||||||
|
if south && west {
|
||||||
|
self.draw_with_offset(dc, pos_in_field, Cartesian::new(-w, -h))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct System {
|
||||||
|
bodies: Vec<Box<dyn Drawable>>,
|
||||||
|
starfield: StarField,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl System {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut s = System {
|
||||||
|
bodies: Vec::new(),
|
||||||
|
starfield: StarField::new(6000.0, 6000.0, 1.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
s.bodies.push(Box::new(Doodad {
|
||||||
|
pos: WorldPosition::new(Cartesian::new(0.0, 0.0), 2.0),
|
||||||
|
sprite: "a0.png".to_owned(),
|
||||||
|
scale: 1,
|
||||||
|
angle: 0.0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
s.bodies.push(Box::new(Doodad {
|
||||||
|
pos: WorldPosition::new(
|
||||||
|
Polar {
|
||||||
|
center: Cartesian::new(0.0, 0.0),
|
||||||
|
radius: 300.0,
|
||||||
|
angle: 31.0,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
1.5,
|
||||||
|
),
|
||||||
|
sprite: "earth.png".to_owned(),
|
||||||
|
scale: 1,
|
||||||
|
angle: (180f64).to_radians(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
s.bodies.push(Box::new(Doodad {
|
||||||
|
pos: WorldPosition::new(Cartesian::new(1000.0, 1000.0), 2.0),
|
||||||
|
sprite: "small.png".to_owned(),
|
||||||
|
scale: 1,
|
||||||
|
angle: 0.0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the state of this body after t seconds.
|
||||||
|
pub fn tick(&mut self, _t: f64) {
|
||||||
|
//let body = &mut self.bodies[1];
|
||||||
|
//body.pos.angle += 0.1 * t;
|
||||||
|
//body.angle -= 0.1 * t;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(&mut self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> {
|
||||||
|
self.starfield.draw(dc, sa)?;
|
||||||
|
for body in &self.bodies {
|
||||||
|
body.draw(dc, sa)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue