ok i can post the whole thing. But its my first Rust project i am certain its wrong in more than one way. There def some warnings due to unused code etc. But i made sure the way i post it here it compiles and runs on my machine (linux):
main.rs:
use std::time::Instant;
mod entity;
mod world;
mod screen;
//define window dimensions
const WIDTH:usize = 1600;
const HEIGHT:usize = 900;
const FPS:u64 = 21000;
//define pixel entity
const MAX_ENTITY:usize = 20000;
fn main() {
//generate the World
let mut world = world::World::new(WIDTH, HEIGHT, MAX_ENTITY);
let mut screen = screen::Screen::new(WIDTH, HEIGHT, FPS);
let mut frame_counter:f32 =0.0;
let mut timer = Instant::now();
//main loop
while screen.is_open(){
//update prey
//update hunters
world.update_entities();
screen.write_into_buffer(&world.prey.borrow());
screen.write_into_buffer(&world.hunters.borrow());
//draw pixels to screen
screen.show();
// prepare for next frame with black background
screen.clear();
//calc fps
frame_counter+=1.0;
if frame_counter % 60.0 == 0.0{
println!("fps: {}, prey: {}, hunters: {}"
, 1000.0*frame_counter/timer.elapsed().as_millis()as f32
, world.prey.borrow().len()
, world.hunters.borrow().len()
);
let timer = Instant::now();
}
}
}
world.rs:
use crate::entity::Entity;
use rayon::prelude::*;
use std::cell::RefCell;
//use crossbeam_utils::thread;
use rand::Rng;
/*
functions that are not tied to the struct
but are useful in the context of level
*/
const RED:u32 = 0xff_00_00;
const GREEN:u32 = 0x00_ff_00;
const BLACK:u32 = 0x00_00_00;
//return the distance between two points
pub fn distance(x0:f32, x1:f32, y0:f32, y1:f32)->f32{
return ((x0-x1).powf(2.0) + (y0-y1).powf(2.0)).sqrt();
}
//return the square distance between two points
pub fn distance_sq(x0:f32, x1:f32, y0:f32, y1:f32)->f32{
return (x0-x1).powf(2.0) + (y0-y1).powf(2.0);
}
/* leaving on one side of the screen warps to the other */
pub fn warp(xx:f32, wall:usize)->f32{
let mut x = xx;
while x> wall as f32{ x-= wall as f32;}
while x < 0.0{ x+= wall as f32;}
if x as usize == wall { x-=1.0;}
//println!("{}, {}, {}", xx, x, wall);
return x;
}
/*
struct
*/
pub struct World{
//window drawable area buffer
pub w:usize,
pub h:usize,
//entity container
pub hunters:RefCell<Vec<Entity>>,
pub prey:RefCell<Vec<Entity>>,
pub chunk:usize
}
impl World{
pub fn new(width:usize, height:usize, max_entity:usize)->World{
let h = max_entity/100;
return World{
chunk:32 as usize,
prey:RefCell::new(vec![Entity::prey(); max_entity-h]),
hunters:RefCell::new(vec![Entity::hunter(); h]),
w:width as usize,
h:height as usize,
};
}
pub fn update_entities(&mut self){
//prey runs away
self.prey.borrow_mut().iter_mut().for_each(|e|{
let other = e.closest(&self.hunters.borrow());
let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
//if a hunter is too close we got eaten
if dis < 1.0{
//teleport away eg respawn somewhere else
let rand_x = rand::thread_rng().gen_range(0.0..self.w as f32);
let rand_y = rand::thread_rng().gen_range(0.0..self.h as f32);
e.set_pos(rand_x , rand_y);
}else if dis < e.view {
e.move_away(other.0, other.1);
}
//random movement
let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
e.move_by(rand_x, rand_y);
//make sure the entity doesn't walk offscreen
e.set_pos(warp(e.pos_x, self.w), warp(e.pos_y, self.h));
});
//hunters follow
for e in self.hunters.borrow_mut().iter_mut(){
let other = e.closest(&self.prey.borrow());
let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
if dis < e.view {
e.move_to(other.0, other.1);
}
//random movement
let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
e.move_by(rand_x, rand_y);
//make sure the entity doesn't walk offscreen
e.set_pos(warp(e.pos_x, self.w), warp(e.pos_y, self.h));
}
}
}
entity.rs:
//colors
const RED:u32 = 0xff_00_00;
const GREEN:u32 = 0x00_ff_00;
const BLACK:u32 = 0x00_00_00;
use crate::world;
//one entity
#[derive(Clone,Debug)]
pub struct Entity {
pub pos_x:f32,
pub pos_y:f32,
pub color:u32,
pub speed:f32,
pub hp:i32,
pub view:f32,
}
//constructor
impl Entity {
pub fn prey() -> Entity {
let e = Entity {pos_x:100.0, pos_y:100.0, color:GREEN, speed:2.0, hp:100, view:20.0};
return e;
}
pub fn hunter()->Entity{
Entity {pos_x:200.0, pos_y:100.0, color:RED, speed:1.0, hp:100, view:22.0}
}
pub fn move_by(&mut self, u:f32,v:f32){
let mut x = u;
let mut y = v;
//calc the actual distance with pythagoras
let distance = (x.powf(2.0) + y.powf(2.0)).sqrt();
//scale down if further away than the speed
if distance > self.speed{
let scale = self.speed/distance;
x = x*scale;
y = y*scale;
}
self.pos_x += x;
self.pos_y += y;
}
pub fn move_to(&mut self,x:f32,y:f32){
self.move_by(x-self.pos_x, y-self.pos_y);
}
pub fn move_away(&mut self,x:f32,y:f32){
self.move_by(self.pos_x-x, self.pos_y-y);
}
pub fn set_pos(&mut self, x:f32, y:f32){
self.pos_x = x;
self.pos_y = y;
}
/*
//which of those entities is the closest to your own position
pub fn closest(&self, others:&Vec<Entity>) -> (f32, f32){
return thread::scope(|s| {
let pt = s.spawn(|_|{
return self.closest_par(others,0, others.len()/2);
});
let bt = s.spawn(|_|{
return self.closest_par(others, others.len()/2, others.len());
});
let p = pt.join().unwrap();
let b = bt.join().unwrap();
if p.2 < b.2{
return (p.0,p.1);
}
return (b.0,b.1);
}).unwrap();
//return (0.0 , 0.0);
}
*/
pub fn closest(&self, others:&Vec<Entity>) -> (f32, f32){
let mut closest:f32 = 10000000.0;//f32.MAX;
let mut x:f32 = 0.0;
let mut y:f32 = 0.0;
for other in others.iter(){
let dis = world::distance_sq(other.pos_x, self.pos_x, other.pos_y, self.pos_y);
if closest > dis{
closest = dis;
x = other.pos_x;
y = other.pos_y;
}
}
return (x,y);
}
}
screen.rs:
use minifb::{Key, Scale, Window, WindowOptions};
use std::time::Instant;
use crate::entity::Entity;
/*
deals with everything that hass to do with rendering
*/
pub struct Screen{
h:usize,
w:usize,
buffer:Vec<u32>,
win:minifb::Window,
fps:u64
}
impl Screen{
pub fn new(width:usize, height:usize, fps:u64)->Screen{
let mut s = Screen{
//make a window
win:Window::new(
"Pixels!",
width,
height,
WindowOptions {
scale: Scale::X1, ..WindowOptions::default()
},
).unwrap(),
fps:fps,
h:height,
w:width,
buffer:vec![0; width * height]
};
//limit fps
s.win.limit_update_rate(Some(std::time::Duration::from_micros(1000000/fps)));
return s;
}
pub fn show(&mut self){
self.win.update_with_buffer(&self.buffer, self.w, self.h).unwrap();
}
pub fn clear(&mut self){
self.buffer = vec![0; self.w * self.h];
}
pub fn is_open(&self)->bool{
self.win.is_open() && !self.win.is_key_down(Key::Escape)
}
pub fn cartesian_to_array(&self, x:f32, y:f32) -> usize{
let u = x;
let v = y;
let r = (u.floor() as usize + ((v.floor() as usize)*self.w));
//println!("{} {} {}", u, v, r);
return r;
}
pub fn write_into_buffer(&mut self, entities:&Vec<Entity>){
//put pixel pos into buffer
for e in entities.iter(){
let index = self.cartesian_to_array(e.pos_x, e.pos_y);
if index > self.h*self.w{
println!("{}, {}, {}", index, e.pos_x, e.pos_y);
}
self.buffer[index] = e.color;
}
}
}
dependencies in Cargo.toml:
[dependencies]
minifb = "0.19.1"
rand = "0.8.0"
chrono = "0.4.19"
rayon = "1.5.0"
crossbeam-utils = "0.8"