Coordinate cleanup
parent
6631096acf
commit
8fc23dd359
|
@ -38,6 +38,12 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "c_vec"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -382,6 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8356b2697d1ead5a34f40bcc3c5d3620205fe0c7be0a14656223bfeec0258891"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"c_vec",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"sdl2-sys",
|
||||
|
|
|
@ -10,4 +10,4 @@ rand = "0.8.5"
|
|||
[dependencies.sdl2]
|
||||
version = "0.36"
|
||||
default-features = false
|
||||
#features = ["ttf"]
|
||||
features = ["ttf", "gfx"]
|
||||
|
|
|
@ -1,33 +1,29 @@
|
|||
use sdl2::render::Canvas;
|
||||
use sdl2::video::Window;
|
||||
use sdl2::gfx::primitives::DrawRenderer;
|
||||
use sdl2::pixels::Color;
|
||||
|
||||
use crate::physics::PhysVec;
|
||||
use crate::physics::Position;
|
||||
use crate::Camera;
|
||||
use crate::DrawContext;
|
||||
use crate::Drawable;
|
||||
use crate::SpriteAtlas;
|
||||
use crate::WorldPosition;
|
||||
|
||||
pub struct Doodad {
|
||||
pub sprite: String,
|
||||
pub pos: Position,
|
||||
pub pos: WorldPosition,
|
||||
pub scale: u32,
|
||||
pub angle: f64,
|
||||
}
|
||||
|
||||
impl Drawable for Doodad {
|
||||
fn position(&self) -> PhysVec {
|
||||
self.pos.to_cartesian().into()
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
canvas: &mut Canvas<Window>,
|
||||
sa: &SpriteAtlas,
|
||||
c: &Camera,
|
||||
) -> Result<(), String> {
|
||||
let pos = self.screen_position(canvas, c);
|
||||
fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> {
|
||||
let pos = self.pos.screen_position(dc);
|
||||
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(());
|
||||
}
|
||||
}
|
||||
|
|
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 std::{time::Duration, time::Instant};
|
||||
|
||||
|
@ -7,112 +6,59 @@ mod inputstatus;
|
|||
mod physics;
|
||||
mod ship;
|
||||
mod sprite;
|
||||
mod system;
|
||||
|
||||
use crate::{
|
||||
doodad::Doodad, inputstatus::InputStatus, physics::PhysVec, physics::Position, ship::Ship,
|
||||
sprite::SpriteAtlas,
|
||||
doodad::Doodad, inputstatus::InputStatus, physics::Cartesian, physics::WorldPosition,
|
||||
ship::Ship, ship::ShipKind, sprite::SpriteAtlas, system::System,
|
||||
};
|
||||
|
||||
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
|
||||
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",
|
||||
}
|
||||
}
|
||||
fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String>;
|
||||
}
|
||||
|
||||
struct Camera {
|
||||
pos: PhysVec,
|
||||
pos: Cartesian,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
fn new() -> Self {
|
||||
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
|
||||
|
||||
struct System {
|
||||
bodies: Vec<Box<dyn Drawable>>,
|
||||
struct DrawContext<'a> {
|
||||
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 {
|
||||
fn new() -> Self {
|
||||
let mut s = System { bodies: Vec::new() };
|
||||
|
||||
let mut num = rand::thread_rng();
|
||||
for _ in 0..25 {
|
||||
s.bodies.push(Box::new(Doodad {
|
||||
sprite: "small.png".to_owned(),
|
||||
pos: Position::new_cartesian(
|
||||
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(),
|
||||
}));
|
||||
|
||||
impl<'a> DrawContext<'a> {
|
||||
fn new(canvas: &'a mut Canvas<Window>, camera: Camera) -> Self {
|
||||
let mut s = Self {
|
||||
canvas,
|
||||
camera,
|
||||
window_size: Cartesian::new(0.0, 0.0),
|
||||
top_left: Cartesian::new(0.0, 0.0),
|
||||
};
|
||||
s.update();
|
||||
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;
|
||||
}
|
||||
|
||||
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(());
|
||||
fn update(&mut self) {
|
||||
self.window_size = Cartesian::from(self.canvas.window().size());
|
||||
self.top_left = (self.window_size / 2.0) * Cartesian::new(-1.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,16 +86,19 @@ fn main() -> Result<(), String> {
|
|||
let mut system = System::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 frame_start;
|
||||
|
||||
let mut running = true;
|
||||
|
||||
let mut dc = DrawContext::new(&mut canvas, Camera::new());
|
||||
|
||||
while running {
|
||||
frame_start = Instant::now();
|
||||
dc.update();
|
||||
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
|
@ -164,13 +113,13 @@ fn main() -> Result<(), String> {
|
|||
}
|
||||
}
|
||||
|
||||
c.pos = s.body.pos;
|
||||
dc.camera.pos = s.body.pos;
|
||||
|
||||
// Draw
|
||||
canvas.clear();
|
||||
system.draw(&mut canvas, &sa, &c)?;
|
||||
s.draw(&mut canvas, &sa, &c)?;
|
||||
canvas.present();
|
||||
dc.canvas.clear();
|
||||
system.draw(&mut dc, &sa)?;
|
||||
s.draw(&mut dc, &sa)?;
|
||||
dc.canvas.present();
|
||||
|
||||
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};
|
||||
|
||||
pub struct PhysBody {
|
||||
pub pos: PhysVec,
|
||||
pub vel: PhysVec,
|
||||
pub pos: Cartesian,
|
||||
pub vel: Cartesian,
|
||||
pub mass: f64,
|
||||
pub angle: f64, // In radians
|
||||
}
|
||||
|
||||
impl PhysBody {
|
||||
pub fn new(pos: PhysVec) -> Self {
|
||||
pub fn new(pos: Cartesian) -> Self {
|
||||
return PhysBody {
|
||||
pos,
|
||||
vel: PhysVec { x: 0.0, y: 0.0 },
|
||||
mass: 1.0,
|
||||
vel: Cartesian::new(0.0, 0.0),
|
||||
mass: 0.3,
|
||||
angle: 0.0,
|
||||
};
|
||||
}
|
||||
|
@ -24,18 +24,14 @@ impl PhysBody {
|
|||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// Apply force in the direction this object is pointing.
|
||||
pub fn thrust(&mut self, f: f64) {
|
||||
self.force(
|
||||
PhysVec {
|
||||
x: self.angle.sin(),
|
||||
y: -self.angle.cos(),
|
||||
} * f,
|
||||
);
|
||||
let l = Cartesian::new(self.angle.sin(), self.angle.cos()) * f;
|
||||
self.force(l);
|
||||
}
|
||||
|
||||
// Rotate this object by `a` radians.
|
||||
|
|
|
@ -1,21 +1,27 @@
|
|||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PhysVec {
|
||||
pub struct Cartesian {
|
||||
pub x: 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 {
|
||||
PhysVec {
|
||||
Cartesian {
|
||||
x: value.0 as f64,
|
||||
y: value.1 as f64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for PhysVec {
|
||||
impl Add for Cartesian {
|
||||
type Output = 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) {
|
||||
self.x += rhs.x;
|
||||
self.y += rhs.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for PhysVec {
|
||||
impl Sub for Cartesian {
|
||||
type Output = 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) {
|
||||
self.x += rhs.x;
|
||||
self.y += rhs.y;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for PhysVec {
|
||||
type Output = PhysVec;
|
||||
impl Mul for Cartesian {
|
||||
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 {
|
||||
Self {
|
||||
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) {
|
||||
self.x *= rhs;
|
||||
self.y *= rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f64> for PhysVec {
|
||||
type Output = PhysVec;
|
||||
impl Div<f64> for Cartesian {
|
||||
type Output = Cartesian;
|
||||
fn div(self, rhs: f64) -> Self::Output {
|
||||
Self {
|
||||
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) {
|
||||
self.x /= rhs;
|
||||
self.y /= rhs;
|
|
@ -1,9 +1,9 @@
|
|||
mod body;
|
||||
mod physpol;
|
||||
mod physvec;
|
||||
mod position;
|
||||
mod cartesian;
|
||||
mod polar;
|
||||
mod worldposition;
|
||||
|
||||
pub use body::PhysBody;
|
||||
pub use physpol::PhysPol;
|
||||
pub use physvec::PhysVec;
|
||||
pub use position::Position;
|
||||
pub use cartesian::Cartesian;
|
||||
pub use polar::Polar;
|
||||
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 sdl2::video::Window;
|
||||
|
||||
use crate::physics::Cartesian;
|
||||
use crate::physics::PhysBody;
|
||||
use crate::physics::PhysVec;
|
||||
use crate::physics::Position;
|
||||
use crate::Camera;
|
||||
use crate::DrawContext;
|
||||
use crate::Drawable;
|
||||
use crate::ShipKind;
|
||||
use crate::SpriteAtlas;
|
||||
|
||||
pub enum ShipKind {
|
||||
Gypsum,
|
||||
}
|
||||
|
||||
impl ShipKind {
|
||||
fn sprite(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Gypsum => "gypsum.png",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ship {
|
||||
pub body: PhysBody,
|
||||
kind: ShipKind,
|
||||
}
|
||||
|
||||
impl Ship {
|
||||
pub fn new(kind: ShipKind, pos: Position) -> Self {
|
||||
pub fn new(kind: ShipKind, pos: Cartesian) -> Self {
|
||||
Ship {
|
||||
body: PhysBody::new(pos.into()),
|
||||
body: PhysBody::new(pos),
|
||||
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 {
|
||||
fn position(&self) -> PhysVec {
|
||||
return self.body.pos;
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
canvas: &mut Canvas<Window>,
|
||||
sa: &SpriteAtlas,
|
||||
c: &Camera,
|
||||
) -> Result<(), String> {
|
||||
let pos = self.screen_position(canvas, c);
|
||||
fn draw(&self, dc: &mut DrawContext, sa: &SpriteAtlas) -> Result<(), String> {
|
||||
let pos = self.screen_position(dc);
|
||||
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(());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use sdl2::{
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::physics::PhysVec;
|
||||
use crate::physics::Cartesian;
|
||||
|
||||
/// A handle for a sprite inside a SpriteAtlas
|
||||
pub struct Sprite<'a> {
|
||||
|
@ -24,11 +24,11 @@ impl<'a> Sprite<'a> {
|
|||
pub fn draw(
|
||||
&self,
|
||||
canvas: &mut Canvas<Window>,
|
||||
position: PhysVec,
|
||||
position: Cartesian,
|
||||
angle: f64,
|
||||
scale: f64,
|
||||
) -> Result<(), String> {
|
||||
let win_size = PhysVec::from(canvas.window().size());
|
||||
let win_size = Cartesian::from(canvas.window().size());
|
||||
let scale = scale * self.scale;
|
||||
|
||||
// Post-scale dimensions on the screen
|
||||
|
@ -60,6 +60,17 @@ impl<'a> Sprite<'a> {
|
|||
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(());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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