Smooth camera and unit motion: display positions, camera target smoothing, pan speed setting
This commit is contained in:
1 parent
583c3fcdaf
commit
eb11cca8cb
10 files changed
+107
-23
No files matched your search
+48
-13
@@ -26,11 +26,46 @@ pub struct Cam {
|
||||
pub yaw: f32,
|
||||
pub pitch: f32,
|
||||
pub zoom: f32, // visible world height in tiles (orthographic)
|
||||
#[serde(skip, default = "zero_f32")]
|
||||
pub rx: f32, // smoothed look-at target used by the screen (world x)
|
||||
#[serde(skip, default = "zero_f32")]
|
||||
pub rz: f32, // smoothed look-at target used by the screen (world z = map y)
|
||||
#[serde(skip, default = "cam_uninit")]
|
||||
pub render_init: bool,
|
||||
}
|
||||
|
||||
fn zero_f32() -> f32 {
|
||||
0.0
|
||||
}
|
||||
|
||||
fn cam_uninit() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
impl Cam {
|
||||
pub fn new(tx: f32, tz: f32) -> Cam {
|
||||
Cam { tx, tz, yaw: 0.785, pitch: 0.6155, zoom: 18.0 }
|
||||
Cam { tx, tz, yaw: 0.785, pitch: 0.6155, zoom: 18.0, rx: tx, rz: tz, render_init: true }
|
||||
}
|
||||
|
||||
pub fn snap(&mut self) {
|
||||
self.rx = self.tx;
|
||||
self.rz = self.tz;
|
||||
self.render_init = true;
|
||||
}
|
||||
|
||||
pub fn update_render(&mut self, dt: f32) {
|
||||
if !self.render_init {
|
||||
self.snap();
|
||||
return;
|
||||
}
|
||||
let dist = (self.tx - self.rx).abs() + (self.tz - self.rz).abs();
|
||||
if dist > self.zoom.max(1.0) {
|
||||
self.snap();
|
||||
return;
|
||||
}
|
||||
let k = (1.0 - (-28.0 * dt.max(0.0)).exp()).clamp(0.0, 1.0);
|
||||
self.rx += (self.tx - self.rx) * k;
|
||||
self.rz += (self.tz - self.rz) * k;
|
||||
}
|
||||
|
||||
pub fn eye_dir(&self) -> Vec3 {
|
||||
@@ -38,7 +73,7 @@ impl Cam {
|
||||
}
|
||||
|
||||
fn target(&self) -> Vec3 {
|
||||
vec3(self.tx, 0.0, self.tz)
|
||||
vec3(self.rx, 0.0, self.rz)
|
||||
}
|
||||
|
||||
pub fn camera(&self) -> Camera3D {
|
||||
@@ -990,7 +1025,7 @@ impl R3d {
|
||||
if !e.alive || e.held || (player_only && e.hero) {
|
||||
continue;
|
||||
}
|
||||
let s = Cam::project(&m, vec3(e.x, 0.3, e.y));
|
||||
let s = Cam::project(&m, vec3(e.disp_x, 0.3, e.disp_y));
|
||||
let d = s.distance(vec2(sx, sy));
|
||||
if d < bd {
|
||||
bd = d;
|
||||
@@ -1231,7 +1266,7 @@ impl R3d {
|
||||
if mirror {
|
||||
std::mem::swap(&mut u0, &mut u1);
|
||||
}
|
||||
let a = amb(e.x, e.y) * if sleeping || e.captive == 1 { 0.6 } else { 1.0 };
|
||||
let a = amb(e.disp_x, e.disp_y) * if sleeping || e.captive == 1 { 0.6 } else { 1.0 };
|
||||
// spell looks: stone grey, plague green, see-through when invisible
|
||||
let alpha = if e.invis > 0.0 { 0.35 } else { 1.0 };
|
||||
let c = if white {
|
||||
@@ -1247,7 +1282,7 @@ impl R3d {
|
||||
};
|
||||
// pulled towards the camera: unchanged on screen (orthographic), but walls behind
|
||||
// the unit no longer cut into the sprite
|
||||
let base = vec3(e.x, bob, e.y) + toward * 0.45 + r * (lunge * fwd);
|
||||
let base = vec3(e.disp_x, bob, e.disp_y) + toward * 0.45 + r * (lunge * fwd);
|
||||
let (sr, cr) = rot.sin_cos();
|
||||
let corner = |dx: f32, dy: f32| {
|
||||
let (dx, dy) = (dx * sx, dy * sy);
|
||||
@@ -1288,7 +1323,7 @@ impl R3d {
|
||||
}
|
||||
let big = matches!(e.kind, EK::Ogre | EK::Paladin | EK::Jailer | EK::Chainbreaker | EK::Wyrm | EK::Gargoyle | EK::Giant);
|
||||
let lift = if flies(e.kind) && !(matches!(e.job, Job::Sleep(_)) && !e.moving) { 0.45 } else { 0.0 };
|
||||
let head = proj(vec3(e.x, lift + if big { 0.9 } else { 0.72 }, e.y));
|
||||
let head = proj(vec3(e.disp_x, lift + if big { 0.9 } else { 0.72 }, e.disp_y));
|
||||
// bar width in block characters, depending on the zoom
|
||||
let cols = ((px_per_tile * 0.6 / cw) as usize).clamp(3, 8);
|
||||
if (e.hp < e.max_hp || e.hero) && e.captive == 0 {
|
||||
@@ -1320,7 +1355,7 @@ impl R3d {
|
||||
gfx.glyph(b'z', head.x + cw * 2.0, head.y - GH * S() - rise, S(), Color::new(0.8, 0.85, 1.0, 1.0 - k));
|
||||
}
|
||||
if e.carry > 0 {
|
||||
let p = proj(vec3(e.x, 0.35, e.y));
|
||||
let p = proj(vec3(e.disp_x, 0.35, e.disp_y));
|
||||
gfx.glyph(0x0F, p.x + cw * 2.0, p.y - GH * small, small, Color::from_rgba(250, 210, 60, 255));
|
||||
}
|
||||
}
|
||||
@@ -1385,7 +1420,7 @@ fn draw_gear_glow(g: &Game, cam: &Cam, t: f32) {
|
||||
for e in g.ents.iter().filter(|e| gear_shown(e) && e.level >= 9) {
|
||||
let w = sprite_size(e.kind);
|
||||
let lift = if flies(e.kind) { 0.45 } else { 0.0 };
|
||||
let base = vec3(e.x, lift, e.y) + toward * 0.5;
|
||||
let base = vec3(e.disp_x, lift, e.disp_y) + toward * 0.5;
|
||||
let c = if e.hero { Color::new(0.8, 0.9, 1.0, 0.8) } else { Color::new(1.0, 0.8, 0.3, 0.8) };
|
||||
let y = w * 1.12 + (t * 2.0 + e.anim).sin() * 0.02;
|
||||
for k in 0..8 {
|
||||
@@ -1394,7 +1429,7 @@ fn draw_gear_glow(g: &Game, cam: &Cam, t: f32) {
|
||||
line3(base + vec3(a0.cos() * 0.12, y, a0.sin() * 0.12), base + vec3(a1.cos() * 0.12, y, a1.sin() * 0.12), 0.025, c);
|
||||
}
|
||||
if e.level >= 10 {
|
||||
disc(vec3(e.x, floor_y(g, e.x, e.y) + 0.015, e.y), 0.42 + 0.03 * (t * 3.0).sin(), Color::new(c.r, c.g, c.b, 0.18), 16);
|
||||
disc(vec3(e.disp_x, floor_y(g, e.disp_x, e.disp_y) + 0.015, e.disp_y), 0.42 + 0.03 * (t * 3.0).sin(), Color::new(c.r, c.g, c.b, 0.18), 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2090,8 +2125,8 @@ fn draw_shadows(g: &Game) {
|
||||
continue;
|
||||
}
|
||||
let r = if matches!(e.kind, EK::Ogre | EK::Paladin | EK::Jailer | EK::Chainbreaker | EK::Giant) { 0.32 } else { 0.24 };
|
||||
let fy = floor_y(g, e.x, e.y);
|
||||
disc(vec3(e.x, fy + 0.012, e.y), r, Color::new(0.0, 0.0, 0.0, 0.4), 12);
|
||||
let fy = floor_y(g, e.disp_x, e.disp_y);
|
||||
disc(vec3(e.disp_x, fy + 0.012, e.disp_y), r, Color::new(0.0, 0.0, 0.0, 0.4), 12);
|
||||
let rc = if e.captive != 0 {
|
||||
Color::new(0.6, 0.6, 0.6, 0.6)
|
||||
} else if e.dwarf() {
|
||||
@@ -2109,7 +2144,7 @@ fn draw_shadows(g: &Game) {
|
||||
} else {
|
||||
Color::new(0.95, 0.3, 0.35, 0.7)
|
||||
};
|
||||
ring(vec3(e.x, fy + 0.02, e.y), r + 0.03, rc);
|
||||
ring(vec3(e.disp_x, fy + 0.02, e.disp_y), r + 0.03, rc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2190,7 +2225,7 @@ fn draw_traps_solid(g: &Game, t: f32) {
|
||||
}
|
||||
CARRIED => {
|
||||
if let Some(e) = g.ents.iter().find(|e| e.id + 1 == tr.carrier) {
|
||||
trap_crate(vec3(e.x, 0.42, e.y));
|
||||
trap_crate(vec3(e.disp_x, 0.42, e.disp_y));
|
||||
}
|
||||
}
|
||||
ARMED => {
|
||||
|
||||
Reference in new issue
Block a user