Added basic parallax

master
Mark 2023-12-22 22:10:38 -08:00
parent 6ad23b4410
commit c0db69bd76
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
6 changed files with 46 additions and 11 deletions

View File

@ -5,6 +5,7 @@ use crate::{physics::Pfloat, Sprite, Spriteable};
pub struct Doodad {
pub sprite: String,
pub pos: Point2<Pfloat>,
pub parallax: Pfloat,
}
impl Spriteable for Doodad {
@ -14,6 +15,7 @@ impl Spriteable for Doodad {
name: self.sprite.clone(),
angle: Deg { 0: 0.0 },
scale: 1.0,
parallax: self.parallax,
};
}
}

View File

@ -1,5 +1,5 @@
use anyhow::Result;
use cgmath::{Deg, Point2};
use cgmath::{Deg, EuclideanSpace, Point2};
use winit::{
event::{
ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
@ -51,6 +51,22 @@ struct Sprite {
// This sprite's rotation
// (relative to north, measured ccw)
angle: Deg<Pfloat>,
// Parallax factor.
// More positive => farther away
// More negative => closer
// Zero: no parallax.
parallax: Pfloat,
}
impl Sprite {
const PARALLAX_STRENGTH: Pfloat = 0.1;
// Returns post-parallax position in game coordinates.
pub fn post_parallax_position(&self, camera: Camera) -> Point2<f32> {
let v = camera.pos.to_vec() - self.pos.to_vec();
return self.pos + (v * self.parallax * Self::PARALLAX_STRENGTH);
}
}
struct Game {
@ -95,11 +111,11 @@ impl Game {
}
if self.input.key_right {
self.player.body.rot(Deg { 0: 15.0 } * t);
self.player.body.rot(Deg { 0: 35.0 } * t);
}
if self.input.key_left {
self.player.body.rot(Deg { 0: -15.0 } * t);
self.player.body.rot(Deg { 0: -35.0 } * t);
}
if self.input.v_scroll != 0.0 {
@ -119,6 +135,13 @@ impl Game {
sprites.append(&mut self.system.sprites());
sprites.push(self.player.sprite());
// Make sure sprites are drawn in the correct order
// (note the reversed a, b in the comparator)
//
// TODO: use a gpu depth buffer with parallax as z-coordinate?
// Might be overkill.
sprites.sort_by(|a, b| b.parallax.total_cmp(&a.parallax));
return sprites;
}
}

View File

@ -260,13 +260,15 @@ impl GPUState {
let mut instances: Vec<SpriteInstance> = Vec::new();
for s in sprites {
let pos = s.pos - camera.pos.to_vec();
let pos = s.post_parallax_position(camera) - camera.pos.to_vec();
let texture = self.texture_array.get_texture(&s.name[..]);
// Game dimensions of this sprite post-scale
// We really need height / 2 to check if we're on the screen,
// but we omit the division so we get a small "margin"
// and so we can re-use this value.
// Game dimensions of this sprite post-scale.
//
// We only need height / 2 to check if we're on the screen,
// but we omit the division.
// This gives us a small margin, and lets us re-use the value
// without an extra multiply.
let height = texture.height * s.scale;
let width = height * texture.aspect;
@ -277,7 +279,6 @@ impl GPUState {
|| pos.x > clip_sw.x + width
|| pos.y < clip_sw.y - height
{
println!("skip {}", s.name);
continue;
}

View File

@ -11,7 +11,7 @@ pub struct TextureArray {
texture_indices: HashMap<String, u32>,
}
const TEX: &[&str] = &["error", "red", "gypsum", "earth", "a0"];
const TEX: &[&str] = &["error", "small", "gypsum", "earth", "a0"];
pub struct Texture {
pub index: u32, // Index in texture array

View File

@ -35,6 +35,7 @@ impl Spriteable for Ship {
name: self.kind.sprite().to_owned(),
angle: self.body.angle,
scale: 1.0,
parallax: 0.0,
};
}
}

View File

@ -12,6 +12,7 @@ impl System {
s.bodies.push(Doodad {
pos: (0.0, 0.0).into(),
sprite: "a0".to_owned(),
parallax: 3.0,
});
s.bodies.push(Doodad {
@ -22,11 +23,18 @@ impl System {
}
.to_cartesian(),
sprite: "earth".to_owned(),
parallax: 1.0,
});
s.bodies.push(Doodad {
pos: (1000.0, 1000.0).into(),
pos: Polar {
center: (0.0, 0.0).into(),
radius: 200.0,
angle: Deg { 0: 270.0 },
}
.to_cartesian(),
sprite: "small".to_owned(),
parallax: -1.0,
});
return s;