127 lines
3.1 KiB
Rust
127 lines
3.1 KiB
Rust
extern crate piston_window;
|
|
use piston_window::*;
|
|
|
|
use std::f64::consts::*;
|
|
|
|
#[derive(PartialEq, Debug)]
|
|
struct Position {
|
|
x: f64,
|
|
y: f64,
|
|
}
|
|
|
|
struct Player {
|
|
pos: Position,
|
|
angle: f64, // radian or degree
|
|
}
|
|
|
|
pub enum Tile {
|
|
Empty,
|
|
Wall,
|
|
}
|
|
|
|
pub struct Level {
|
|
pub width: u16,
|
|
pub height: u16,
|
|
pub tiles: Vec<Tile>,
|
|
}
|
|
|
|
pub struct Engine {
|
|
w: f64,
|
|
h: f64,
|
|
horiz_fov: f64,
|
|
player: Player,
|
|
level: Level,
|
|
}
|
|
|
|
impl Engine {
|
|
pub fn new(size: Size) -> Engine {
|
|
Engine {
|
|
w: size.width as f64,
|
|
h: size.height as f64,
|
|
horiz_fov: 90.,
|
|
player: Player {
|
|
pos: Position { x: 2., y: 2. },
|
|
angle: 0.,
|
|
},
|
|
level: Level {
|
|
width: 0,
|
|
height: 0,
|
|
tiles: vec![],
|
|
},
|
|
}
|
|
}
|
|
|
|
fn closest_point(pos: &Position, angle: f64) -> (Tile, Position) {
|
|
// First let's find the closest intersections with the grid
|
|
let dx = angle.cos();
|
|
let x_dist = if dx > 0.0 { 1.0 - pos.x.fract() } else { pos.x.fract() };
|
|
let x_relative_dist = x_dist / dx.abs();
|
|
let dy = angle.sin();
|
|
let y_dist = if dy > 0.0 { 1.0 - pos.y.fract() } else { pos.y.fract() };
|
|
let y_relative_dist = y_dist / dy.abs();
|
|
if x_relative_dist > y_relative_dist {
|
|
// first grid hit is horizontal line
|
|
} else {
|
|
// first grid hit is vertical line
|
|
}
|
|
|
|
// see Game Engine Black Book
|
|
let xstep = angle.tan();
|
|
let ystep = ((PI / 2.0) - angle).tan();
|
|
|
|
(Tile::Empty, Position {x: 2., y: 2.})
|
|
}
|
|
|
|
pub fn render(&mut self, context: Context, graphics: &mut G2d) {
|
|
|
|
clear([1.0; 4], graphics);
|
|
|
|
// Ceiling
|
|
let ceiling_color = [0.3, 0.3, 0.3, 1.0];
|
|
rectangle(ceiling_color,
|
|
[0.0, 0.0, self.w, self.h / 2.0],
|
|
context.transform,
|
|
graphics);
|
|
|
|
// Floor
|
|
let floor_color = [0.5, 0.5, 0.5, 1.0];
|
|
rectangle(floor_color,
|
|
[0.0, self.h / 2.0, self.w, self.h / 2.0],
|
|
context.transform,
|
|
graphics);
|
|
|
|
// Walls
|
|
let left = self.player.angle + (self.horiz_fov / 2.0);
|
|
let right = self.player.angle - (self.horiz_fov / 2.0);
|
|
// for every angle (range / w)
|
|
let step = (left - right) / self.w;
|
|
let mut ray_angle = left;
|
|
let width = self.w as i32;
|
|
for n in 0..width {
|
|
// cast a ray
|
|
let (tile, pos) = Engine::closest_point(&self.player.pos, ray_angle);
|
|
// see what wall it hits
|
|
// compute wall height
|
|
// draw wall portion
|
|
ray_angle += step;
|
|
}
|
|
}
|
|
|
|
pub fn load_level(&mut self, level: Level) {
|
|
self.level = level;
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn closest_point() {
|
|
let origin = super::Position { x: 2.2, y: 2.3 };
|
|
let angle = 0.;
|
|
let closest = Engine::closest_point(origin, angle);
|
|
assert_eq!(closest, super::Position { x: 3.0, y: 2.3 });
|
|
}
|
|
}
|