basic field drawer and basic timer implementation
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
use game_core::field::field::{Field, Tile};
|
||||||
|
|
||||||
|
const BOUNDARY_TILE: &'static str = "#";
|
||||||
|
const WALL_TILE: &'static str = "█";
|
||||||
|
const EMPTY_TILE : &'static str = " ";
|
||||||
|
const FOOD_TILE : &'static str = "o";
|
||||||
|
|
||||||
|
pub fn draw_field(field: &Field) {
|
||||||
|
println!("{}", BOUNDARY_TILE.repeat(field.width + 2));
|
||||||
|
for y in 0..field.height {
|
||||||
|
print!("{}", BOUNDARY_TILE);
|
||||||
|
for x in 0..field.width {
|
||||||
|
let tile = field.tiles[y * field.width + x];
|
||||||
|
let char = match tile {
|
||||||
|
Tile::Empty => EMPTY_TILE,
|
||||||
|
Tile::Wall => WALL_TILE,
|
||||||
|
Tile::Food => FOOD_TILE,
|
||||||
|
};
|
||||||
|
print!("{}", char);
|
||||||
|
}
|
||||||
|
println!("{}", BOUNDARY_TILE);
|
||||||
|
}
|
||||||
|
println!("{}", BOUNDARY_TILE.repeat(field.width + 2));
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod field;
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
use game_core;
|
|
||||||
|
mod drawers;
|
||||||
|
mod renderers;
|
||||||
|
|
||||||
|
use crate::renderers::renderer::main_loop;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
game_core::init();
|
main_loop();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod renderer;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use std::io::{self, Write};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use game_core::field::loader::loader::FieldLoader;
|
||||||
|
use game_core::field::loader::plain_text_loader::PlainTextFieldLoader;
|
||||||
|
use game_core::timer::Timer;
|
||||||
|
use crate::drawers::field::draw_field;
|
||||||
|
use std::{thread, time};
|
||||||
|
|
||||||
|
fn clear_screen() {
|
||||||
|
// Clear the entire screen and move cursor to top-left
|
||||||
|
print!("\x1B[2J\x1B[1;1H");
|
||||||
|
// Flush to ensure the escape codes are sent immediately
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_loop() {
|
||||||
|
|
||||||
|
let path = env::current_dir().unwrap();
|
||||||
|
let field_loader = PlainTextFieldLoader{};
|
||||||
|
let full_path = format!("{}{}", path.display(), "/game_data/fields/maze1");
|
||||||
|
let field = field_loader.load(full_path);
|
||||||
|
|
||||||
|
let mut timer = Timer::new(120);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
timer.start();
|
||||||
|
clear_screen();
|
||||||
|
draw_field(&field);
|
||||||
|
|
||||||
|
timer.limit_fps();
|
||||||
|
timer.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::field::field::Field;
|
use crate::field::field::Field;
|
||||||
|
|
||||||
pub trait FieldLoader {
|
pub trait FieldLoader {
|
||||||
fn load(field_id: String) -> Field;
|
fn load(&self, field_id: String) -> Field;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ use std::fs;
|
|||||||
pub struct PlainTextFieldLoader;
|
pub struct PlainTextFieldLoader;
|
||||||
|
|
||||||
impl FieldLoader for PlainTextFieldLoader {
|
impl FieldLoader for PlainTextFieldLoader {
|
||||||
fn load(field_id: String) -> Field {
|
fn load(&self, field_id: String) -> Field {
|
||||||
let content = fs::read_to_string(&field_id)
|
let content = fs::read_to_string(&field_id)
|
||||||
.expect("Could not read field file");
|
.expect("Could not read field file");
|
||||||
|
|
||||||
let lines: Vec<&str> = content.lines().collect();
|
let lines: Vec<&str> = content.lines().filter(|l| !l.is_empty()).collect();
|
||||||
let height = lines.len();
|
let height = lines.len();
|
||||||
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
let width = lines.iter().map(|l| l.len()).max().unwrap_or(0);
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ mod tests {
|
|||||||
|
|
||||||
fs::write(test_file, level_data).expect("Could not write test file");
|
fs::write(test_file, level_data).expect("Could not write test file");
|
||||||
|
|
||||||
let field = PlainTextFieldLoader::load(test_file.to_string());
|
let field = PlainTextFieldLoader{}.load(test_file.to_string());
|
||||||
|
|
||||||
let _ = fs::remove_file(test_file);
|
let _ = fs::remove_file(test_file);
|
||||||
|
|
||||||
|
|||||||
+1
-19
@@ -5,22 +5,4 @@ pub mod common;
|
|||||||
pub mod traits;
|
pub mod traits;
|
||||||
pub mod field;
|
pub mod field;
|
||||||
pub mod game_handler;
|
pub mod game_handler;
|
||||||
|
pub mod timer;
|
||||||
pub fn add(left: u64, right: u64) -> u64 {
|
|
||||||
left + right
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() {
|
|
||||||
println!("init game engine");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use std::time;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
pub struct Timer {
|
||||||
|
timer: Instant,
|
||||||
|
target_fps: usize,
|
||||||
|
_target_fps_as_micros: u128,
|
||||||
|
last_delta_time: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
pub fn new(target_fps: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
timer: Instant::now(),
|
||||||
|
target_fps: target_fps,
|
||||||
|
_target_fps_as_micros: Timer::get_target_fps_as_micros(target_fps),
|
||||||
|
last_delta_time: Duration::ZERO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_target_fps_as_micros(target_fps: usize) -> u128 {
|
||||||
|
(1000.0 / (target_fps as f64)) as u128 * 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_target_fps(&mut self, target_fps: usize) {
|
||||||
|
self.target_fps = target_fps;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) {
|
||||||
|
self.timer = Instant::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&mut self) {
|
||||||
|
self.last_delta_time = self.timer.elapsed()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_elapsed(&mut self) -> Duration {
|
||||||
|
self.timer.elapsed()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_delta_time(&self) -> Duration {
|
||||||
|
// deltaTime should be the elapsed time between the last two frames.
|
||||||
|
self.last_delta_time
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit_fps(&self) {
|
||||||
|
// You should be using something like V-Sync instead of this, as there's no warrantee that thread::sleep will always wait the exact desired time.
|
||||||
|
let elapsed = self.timer.elapsed().as_micros();
|
||||||
|
if elapsed > self._target_fps_as_micros {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let wait = (self._target_fps_as_micros - elapsed).try_into().unwrap();
|
||||||
|
thread::sleep(time::Duration::from_micros(wait));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user