initial commit
This commit is contained in:
commit
be5e225a1e
3506
Cargo.lock
generated
Normal file
3506
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "boids"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.10.1", features = [ "trace" ] }
|
||||||
|
rand = "0.8.5"
|
BIN
assets/bg.png
Normal file
BIN
assets/bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 622 KiB |
BIN
assets/boid.png
Normal file
BIN
assets/boid.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
assets/fonts/DejaVuSans.ttf
Normal file
BIN
assets/fonts/DejaVuSans.ttf
Normal file
Binary file not shown.
185
src/boids.rs
Normal file
185
src/boids.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use crate::map::Map;
|
||||||
|
use crate::Params;
|
||||||
|
|
||||||
|
pub const NBR : usize = 10_000;
|
||||||
|
pub const SIZE : f32 = 100.0;
|
||||||
|
pub const VIEW_DIST : f32 = 1.0;
|
||||||
|
pub const BORDER : f32 = 1.0;
|
||||||
|
|
||||||
|
#[derive(Bundle, Default)]
|
||||||
|
pub struct BoidBundle {
|
||||||
|
mesh: SpriteBundle,
|
||||||
|
pos: Pos,
|
||||||
|
vel: Vel,
|
||||||
|
dir_vec: DirVec,
|
||||||
|
prox_cache: ProxCache
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoidBundle {
|
||||||
|
pub fn new(
|
||||||
|
pos: Vec2, vel: Vec2,
|
||||||
|
mesh: SpriteBundle
|
||||||
|
) -> Self {
|
||||||
|
BoidBundle {
|
||||||
|
mesh,
|
||||||
|
pos: Pos(pos),
|
||||||
|
vel: Vel(vel),
|
||||||
|
..default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BoidPlugin;
|
||||||
|
|
||||||
|
impl Plugin for BoidPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.insert_resource(Map::new(SIZE as usize));
|
||||||
|
app.add_systems((
|
||||||
|
boids_proxcache,
|
||||||
|
boids_sep,
|
||||||
|
boids_ali,
|
||||||
|
boids_coh,
|
||||||
|
boids_dir,
|
||||||
|
boids_move,
|
||||||
|
mesh_update
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Default, Debug, Clone, Copy)]
|
||||||
|
pub struct Pos(pub Vec2);
|
||||||
|
|
||||||
|
#[derive(Component, Default, Debug, Clone, Copy)]
|
||||||
|
pub struct Vel(pub Vec2);
|
||||||
|
|
||||||
|
#[derive(Component, Default, Debug)]
|
||||||
|
pub struct DirVec(Vec<Vec2>);
|
||||||
|
|
||||||
|
#[derive(Component, Default, Debug)]
|
||||||
|
pub struct ProxCache(Vec<(Entity,Vec2,f32)>);
|
||||||
|
|
||||||
|
fn boids_proxcache(
|
||||||
|
map: Res<Map>,
|
||||||
|
mut query0: Query<(Entity, &Pos, &mut ProxCache)>,
|
||||||
|
query1: Query<&Pos>
|
||||||
|
) {
|
||||||
|
query0.par_iter_mut()
|
||||||
|
.for_each_mut(|(e0, Pos(p0), mut prox_cache)| {
|
||||||
|
prox_cache.0.clear();
|
||||||
|
for e1 in map.get(p0, VIEW_DIST) {
|
||||||
|
if e0 == e1 { continue }
|
||||||
|
let Ok(Pos(p1)) = query1.get(e1) else {
|
||||||
|
error!("entity not found! should never occure!");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let diff = *p0 - *p1;
|
||||||
|
let d = diff.length_squared();
|
||||||
|
if d > VIEW_DIST*VIEW_DIST || d == 0.0 { continue }
|
||||||
|
prox_cache.0.push((e1, diff, d));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boids_sep(
|
||||||
|
params: Res<Params>,
|
||||||
|
mut query: Query<(&Pos, &ProxCache, &mut DirVec)>,
|
||||||
|
) {
|
||||||
|
for (Pos(p0), ProxCache(prox_cache), mut dir_vec) in &mut query {
|
||||||
|
let mut sep = Vec2::default();
|
||||||
|
|
||||||
|
for (_,diff,d) in prox_cache {
|
||||||
|
sep += *diff / *d;
|
||||||
|
}
|
||||||
|
dir_vec.0.push(sep * params.sep);
|
||||||
|
dir_vec.0.push(border_vel(p0) * 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boids_coh(
|
||||||
|
params: Res<Params>,
|
||||||
|
mut query: Query<(&ProxCache, &mut DirVec)>,
|
||||||
|
) {
|
||||||
|
for (ProxCache(prox_cache), mut dir_vec) in &mut query {
|
||||||
|
let mut coh = Vec2::default();
|
||||||
|
|
||||||
|
for (_,diff,_) in prox_cache {
|
||||||
|
coh -= *diff;
|
||||||
|
}
|
||||||
|
dir_vec.0.push(coh * params.coh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boids_ali(
|
||||||
|
params: Res<Params>,
|
||||||
|
mut query: Query<(&ProxCache, &mut DirVec)>,
|
||||||
|
query2: Query<&Vel>
|
||||||
|
) {
|
||||||
|
for (ProxCache(prox_cache), mut dir_vec) in &mut query {
|
||||||
|
let mut ali = Vec2::default();
|
||||||
|
|
||||||
|
for (e1,_,_) in prox_cache {
|
||||||
|
let Ok(Vel(v1)) = query2.get(*e1) else {
|
||||||
|
error!("entity not found! should never occure!");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
ali += *v1;
|
||||||
|
}
|
||||||
|
dir_vec.0.push(ali * params.ali);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn border_vel(p0: &Vec2) -> Vec2 {
|
||||||
|
Vec2::new(
|
||||||
|
match p0.x {
|
||||||
|
_ if p0.x < -SIZE+BORDER => 1.0,
|
||||||
|
_ if p0.x > SIZE-BORDER => -1.0,
|
||||||
|
_ => 0.0
|
||||||
|
},
|
||||||
|
match p0.y {
|
||||||
|
_ if p0.y < -SIZE+BORDER => 1.0,
|
||||||
|
_ if p0.y > SIZE-BORDER => -1.0,
|
||||||
|
_ => 0.0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boids_dir(mut query: Query<(&mut DirVec, &mut Vel)>) {
|
||||||
|
for (mut dir_vec, mut vel) in &mut query {
|
||||||
|
for v in &dir_vec.0 {
|
||||||
|
vel.0 += *v;
|
||||||
|
}
|
||||||
|
vel.0 = vel.0.normalize();
|
||||||
|
dir_vec.0.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn boids_move(
|
||||||
|
time: Res<Time>,
|
||||||
|
params: Res<Params>,
|
||||||
|
mut map: ResMut<Map>,
|
||||||
|
mut query: Query<(Entity, &mut Pos, &Vel)>
|
||||||
|
) {
|
||||||
|
let dt = time.delta_seconds();
|
||||||
|
for (e, mut pos, vel) in &mut query {
|
||||||
|
let new_pos = pos.0 + vel.0 * dt * params.speed;
|
||||||
|
let new_pos = Vec2::new(
|
||||||
|
new_pos.x.clamp(-SIZE, SIZE),
|
||||||
|
new_pos.y.clamp(-SIZE, SIZE)
|
||||||
|
);
|
||||||
|
if pos.0 != new_pos {
|
||||||
|
map.update(&pos.0, &new_pos, &e);
|
||||||
|
}
|
||||||
|
pos.0 = new_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mesh_update(mut query: Query<(&mut Transform, &Pos, &Vel)>) {
|
||||||
|
for (mut transform, Pos(pos), Vel(vel)) in &mut query {
|
||||||
|
let pos3 = Vec3::new(pos.x, pos.y, 0.0);
|
||||||
|
let angle = Vec2::X.angle_between(*vel);
|
||||||
|
|
||||||
|
*transform = Transform::from_translation(pos3)
|
||||||
|
.with_rotation(Quat::from_rotation_z(angle));
|
||||||
|
}
|
||||||
|
}
|
59
src/camera.rs
Normal file
59
src/camera.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::input::mouse::{MouseWheel,MouseMotion};
|
||||||
|
|
||||||
|
#[derive(Bundle, Default)]
|
||||||
|
pub struct CamBundle {
|
||||||
|
cam: Camera2dBundle,
|
||||||
|
state: CameraState
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CamBundle {
|
||||||
|
pub fn new(scale: f32) -> Self {
|
||||||
|
CamBundle {
|
||||||
|
cam: Camera2dBundle {
|
||||||
|
transform: Transform::from_scale(Vec3::new(scale, scale, 1.0)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
state: CameraState { scale, ..default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component, Default)]
|
||||||
|
pub struct CameraState {
|
||||||
|
scale: f32,
|
||||||
|
pos: Vec2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CameraPlugin;
|
||||||
|
|
||||||
|
impl Plugin for CameraPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_system(mouse_move_events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_move_events(
|
||||||
|
buttons: Res<Input<MouseButton>>,
|
||||||
|
mut mouse_wheel_events: EventReader<MouseWheel>,
|
||||||
|
mut mouse_motion_events: EventReader<MouseMotion>,
|
||||||
|
mut query: Query<(&mut Transform, &mut CameraState)>
|
||||||
|
) {
|
||||||
|
let (mut transform, mut state) = query.single_mut();
|
||||||
|
|
||||||
|
for event in mouse_wheel_events.iter() {
|
||||||
|
let scale = state.scale;
|
||||||
|
state.scale = (scale + event.y * 0.25 * scale)
|
||||||
|
.clamp(0.001, 1.0);
|
||||||
|
*transform = transform.with_scale(Vec3::new(state.scale,
|
||||||
|
state.scale, 1.0));
|
||||||
|
}
|
||||||
|
if buttons.pressed(MouseButton::Left) {
|
||||||
|
let scale = state.scale;
|
||||||
|
for event in mouse_motion_events.iter() {
|
||||||
|
state.pos -= event.delta * scale;
|
||||||
|
}
|
||||||
|
transform.translation = Vec3::new(state.pos.x, -state.pos.y, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
148
src/main.rs
Normal file
148
src/main.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
mod map;
|
||||||
|
mod boids;
|
||||||
|
mod camera;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::diagnostic::{ Diagnostics, FrameTimeDiagnosticsPlugin };
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use boids::{NBR,BORDER,SIZE,BoidPlugin,BoidBundle};
|
||||||
|
use map::Map;
|
||||||
|
use camera::{CameraPlugin,CamBundle};
|
||||||
|
|
||||||
|
#[derive(Resource,Debug)]
|
||||||
|
pub struct Params {
|
||||||
|
sep: f32,
|
||||||
|
ali: f32,
|
||||||
|
coh: f32,
|
||||||
|
speed: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Params {
|
||||||
|
fn default() -> Self {
|
||||||
|
Params {
|
||||||
|
sep: 0.02,
|
||||||
|
ali: 0.2,
|
||||||
|
coh: 0.02,
|
||||||
|
speed: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct StatsText;
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct FpsTimer(Timer);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
title: "Bevy :: Boids".into(),
|
||||||
|
resolution: (900., 900.).into(),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}))
|
||||||
|
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||||
|
.init_resource::<Params>()
|
||||||
|
.add_plugin(BoidPlugin)
|
||||||
|
.add_plugin(CameraPlugin)
|
||||||
|
.insert_resource(FpsTimer(Timer::new(Duration::from_secs_f32(0.5),
|
||||||
|
TimerMode::Repeating)))
|
||||||
|
.add_system(update_stats)
|
||||||
|
.add_startup_system(setup)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut map: ResMut<Map>,
|
||||||
|
asset_server: Res<AssetServer>
|
||||||
|
) {
|
||||||
|
// Camera
|
||||||
|
commands.spawn(CamBundle::new(0.1));
|
||||||
|
|
||||||
|
//BG
|
||||||
|
commands.spawn(SpriteBundle {
|
||||||
|
texture: asset_server.load("bg.png"),
|
||||||
|
transform: Transform::from_scale(Vec3::new(SIZE as f32 / 512.0,
|
||||||
|
SIZE as f32 / 512.0, 1.0))
|
||||||
|
.with_translation(Vec3::new(0.0, 0.0, -1.0)),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let tex = asset_server.load("boid.png");
|
||||||
|
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
for _ in 0..NBR {
|
||||||
|
let pos = Vec2::new(rng.gen_range(-SIZE+BORDER..SIZE-BORDER),
|
||||||
|
rng.gen_range(-SIZE+BORDER..SIZE-BORDER));
|
||||||
|
let v = Vec2::new(rng.gen_range(-1.0..1.0),
|
||||||
|
rng.gen_range(-1.0..1.0)).normalize();
|
||||||
|
let sprite = SpriteBundle {
|
||||||
|
sprite: Sprite {
|
||||||
|
custom_size: Some(Vec2::new(2.0, 2.0)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
texture: tex.clone(),
|
||||||
|
..default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let entity = commands.spawn(BoidBundle::new(pos, v, sprite)).id();
|
||||||
|
map.insert(&pos, &entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unashamely stolen from the website examples...
|
||||||
|
let text_section = move |color, value: &str| {
|
||||||
|
TextSection::new(
|
||||||
|
value,
|
||||||
|
TextStyle {
|
||||||
|
font: asset_server.load("fonts/DejaVuSans.ttf"),
|
||||||
|
font_size: 20.0,
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
TextBundle::from_sections([
|
||||||
|
text_section(Color::GREEN, "Boid Count: "),
|
||||||
|
text_section(Color::CYAN, &format!("{}", NBR)),
|
||||||
|
text_section(Color::GREEN, "\nFPS: "),
|
||||||
|
text_section(Color::CYAN, ""),
|
||||||
|
])
|
||||||
|
.with_style(Style {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
position: UiRect {
|
||||||
|
top: Val::Px(5.0),
|
||||||
|
left: Val::Px(5.0),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
StatsText,
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_stats(
|
||||||
|
diagnostics: Res<Diagnostics>,
|
||||||
|
mut query: Query<&mut Text, With<StatsText>>,
|
||||||
|
mut fps_timer: ResMut<FpsTimer>,
|
||||||
|
time: Res<Time>
|
||||||
|
) {
|
||||||
|
fps_timer.0.tick(time.delta());
|
||||||
|
if !fps_timer.0.finished() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut text = query.single_mut();
|
||||||
|
if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
|
||||||
|
if let Some(ema) = fps.smoothed() {
|
||||||
|
text.sections[3].value = format!("{ema:.2}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
src/map.rs
Normal file
88
src/map.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
#[derive(Resource,Default,Debug)]
|
||||||
|
pub struct Map { size: usize,
|
||||||
|
map: Vec<Vec<HashSet<Entity>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Map {
|
||||||
|
pub fn new(size: usize) -> Self {
|
||||||
|
Map { size,
|
||||||
|
map: vec! [ vec![ HashSet::default() ; size*2 +1 ] ; size*2 + 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, pos: &Vec2, entity: &Entity) {
|
||||||
|
let (x, y) = self.get_key(pos);
|
||||||
|
self.map[y][x].insert(*entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, old_pos: &Vec2, new_pos: &Vec2, entity: &Entity) {
|
||||||
|
let (old_x, old_y) = self.get_key(old_pos);
|
||||||
|
let (new_x, new_y) = self.get_key(new_pos);
|
||||||
|
self.map[old_y][old_x].remove(entity);
|
||||||
|
self.map[new_y][new_x].insert(*entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, pos: &Vec2, view_dist: f32) -> Vec<Entity> {
|
||||||
|
let vd = Vec2::new(view_dist, view_dist);
|
||||||
|
let (from_x, from_y) = self.get_key(&(*pos - vd));
|
||||||
|
let (to_x, to_y) = self.get_key(&(*pos + vd));
|
||||||
|
let mut v = vec![];
|
||||||
|
|
||||||
|
for y in from_y..=to_y {
|
||||||
|
for x in from_x..=to_x {
|
||||||
|
v.extend(self.map[y][x].iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self, pos: &Vec2) -> (usize, usize) {
|
||||||
|
let size = self.size as i32;
|
||||||
|
((pos.x as i32 + size)
|
||||||
|
.clamp(0, size*2) as usize,
|
||||||
|
(pos.y as i32 + size)
|
||||||
|
.clamp(0, size*2) as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
use std::collections::{HashMap,HashSet};
|
||||||
|
|
||||||
|
#[derive(Resource,Default,Debug)]
|
||||||
|
pub struct Map(HashMap<(i32,i32), HashSet<Entity>>);
|
||||||
|
|
||||||
|
impl Map {
|
||||||
|
pub fn update(&mut self, old_pos: &Vec2, new_pos: &Vec2, entity: &Entity) {
|
||||||
|
let old_p = (old_pos.x as i32, old_pos.y as i32);
|
||||||
|
let new_p = (new_pos.x as i32, new_pos.y as i32);
|
||||||
|
|
||||||
|
self.0.entry(old_p)
|
||||||
|
.and_modify(| set | { set.remove(entity); });
|
||||||
|
self.0.entry(new_p)
|
||||||
|
.and_modify(| set | { set.insert(*entity); })
|
||||||
|
.or_insert([*entity].into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, pos: &Vec2, view_dist: f32) -> Vec<Entity> {
|
||||||
|
let from_x = (pos.x - view_dist) as i32;
|
||||||
|
let from_y = (pos.y - view_dist) as i32;
|
||||||
|
let to_x = (pos.x + view_dist) as i32;
|
||||||
|
let to_y = (pos.y + view_dist) as i32;
|
||||||
|
let mut v = vec![];
|
||||||
|
|
||||||
|
for y in from_y..=to_y {
|
||||||
|
for x in from_x..=to_x {
|
||||||
|
if let Some(set) = self.0.get(&(x,y)) {
|
||||||
|
v.extend(set.iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
52
src/mouse_inputs.rs
Normal file
52
src/mouse_inputs.rs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
//use bevy::input::mouse::{MouseButtonInput,MouseWheel,MouseMotion};
|
||||||
|
use bevy::input::mouse::{MouseWheel,MouseMotion};
|
||||||
|
//use bevy::input::ButtonState;
|
||||||
|
|
||||||
|
use crate::CameraState;
|
||||||
|
|
||||||
|
pub fn mouse_move_events(
|
||||||
|
buttons: Res<Input<MouseButton>>,
|
||||||
|
mut mouse_wheel_events: EventReader<MouseWheel>,
|
||||||
|
mut mouse_motion_events: EventReader<MouseMotion>,
|
||||||
|
mut query: Query<(&mut Transform, &mut CameraState)>
|
||||||
|
) {
|
||||||
|
let (mut transform, mut state) = query.single_mut();
|
||||||
|
|
||||||
|
for event in mouse_wheel_events.iter() {
|
||||||
|
let scale = state.scale;
|
||||||
|
state.scale = (scale + event.y * scale)
|
||||||
|
.clamp(0.001, 0.1);
|
||||||
|
*transform = transform.with_scale(Vec3::new(state.scale,
|
||||||
|
state.scale, 1.0));
|
||||||
|
}
|
||||||
|
if buttons.pressed(MouseButton::Left) {
|
||||||
|
let scale = state.scale;
|
||||||
|
for event in mouse_motion_events.iter() {
|
||||||
|
state.pos -= event.delta * scale;
|
||||||
|
}
|
||||||
|
transform.translation = Vec3::new(state.pos.x, -state.pos.y, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn mouse_button_events(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut mouse_button_events: EventReader<MouseButtonInput>
|
||||||
|
) {
|
||||||
|
for _event in mouse_button_events.iter() {
|
||||||
|
|
||||||
|
if event.button == MouseButton::Left &&
|
||||||
|
event.state == ButtonState::Pressed {
|
||||||
|
} else if event.button == MouseButton::Right &&
|
||||||
|
event.state == ButtonState::Pressed {
|
||||||
|
for (entity,_) in &query {
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
terrain_events
|
||||||
|
.send(GenTerrainEvent { seed: rand::random() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user