Skip to content

Commit

Permalink
Hitboxes (#330)
Browse files Browse the repository at this point in the history
## Description

Added Hitbox component, a crate for it and systems which update
hitboxes.
Issue: #299

## Test Plan

Use example "entity_hitbox"
  • Loading branch information
Jenya705 authored May 9, 2023
1 parent 161d523 commit 8897eea
Show file tree
Hide file tree
Showing 7 changed files with 720 additions and 271 deletions.
146 changes: 146 additions & 0 deletions crates/valence/examples/entity_hitbox.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use std::collections::HashMap;

use bevy_app::App;
use bevy_ecs::prelude::Entity;
use rand::Rng;
use valence::prelude::*;
use valence_entity::entity::NameVisible;
use valence_entity::hoglin::HoglinEntityBundle;
use valence_entity::pig::PigEntityBundle;
use valence_entity::sheep::SheepEntityBundle;
use valence_entity::warden::WardenEntityBundle;
use valence_entity::zombie::ZombieEntityBundle;
use valence_entity::zombie_horse::ZombieHorseEntityBundle;
use valence_entity::{entity, Pose};

pub fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system(init_clients)
.add_systems((spawn_entity, intersections))
.run();
}

fn setup(
mut commands: Commands,
server: Res<Server>,
dimensions: Query<&DimensionType>,
biomes: Query<&Biome>,
) {
let mut instance = Instance::new(ident!("overworld"), &dimensions, &biomes, &server);

for z in -5..5 {
for x in -5..5 {
instance.insert_chunk([x, z], Chunk::default());
}
}

for z in -25..25 {
for x in -25..25 {
instance.set_block([x, 64, z], BlockState::GRASS_BLOCK);
}
}

commands.spawn(instance);
}

fn init_clients(
mut clients: Query<(&mut Location, &mut Position, &mut GameMode, &mut Client), Added<Client>>,
instances: Query<Entity, With<Instance>>,
) {
for (mut loc, mut pos, mut game_mode, mut client) in &mut clients {
loc.0 = instances.single();
pos.set([0.5, 65.0, 0.5]);
*game_mode = GameMode::Creative;
client.send_message("To spawn an entity, press shift. F3 + B to activate hitboxes");
}
}

fn spawn_entity(
mut commands: Commands,
mut sneaking: EventReader<Sneaking>,
client_query: Query<(&Position, &Location)>,
) {
for sneaking in sneaking.iter() {
if sneaking.state == SneakState::Start {
continue;
}

let (position, location) = client_query.get(sneaking.client).unwrap();

let position = *position;
let location = *location;

match rand::thread_rng().gen_range(0..7) {
0 => commands.spawn(SheepEntityBundle {
position,
location,
entity_name_visible: NameVisible(true),
..Default::default()
}),
1 => commands.spawn(PigEntityBundle {
position,
location,
entity_name_visible: NameVisible(true),
..Default::default()
}),
2 => commands.spawn(ZombieEntityBundle {
position,
location,
entity_name_visible: NameVisible(true),
..Default::default()
}),
3 => commands.spawn(ZombieHorseEntityBundle {
position,
location,
entity_name_visible: NameVisible(true),
..Default::default()
}),
4 => commands.spawn(WardenEntityBundle {
position,
location,
entity_name_visible: NameVisible(true),
entity_pose: entity::Pose(Pose::Digging),
..Default::default()
}),
5 => commands.spawn(WardenEntityBundle {
position,
location,
entity_name_visible: NameVisible(true),
..Default::default()
}),
6 => commands.spawn(HoglinEntityBundle {
position,
location,
entity_name_visible: NameVisible(true),

..Default::default()
}),
_ => unreachable!(),
};
}
}

fn intersections(query: Query<(Entity, &Hitbox)>, mut name_query: Query<&mut entity::CustomName>) {
// This code only to show how hitboxes can be used
let mut intersections = HashMap::new();

for [(entity1, hitbox1), (entity2, hitbox2)] in query.iter_combinations() {
let aabb1 = hitbox1.get();
let aabb2 = hitbox2.get();

let _ = *intersections.entry(entity1).or_insert(0);
let _ = *intersections.entry(entity2).or_insert(0);

if aabb1.intersects(aabb2) {
*intersections.get_mut(&entity1).unwrap() += 1;
*intersections.get_mut(&entity2).unwrap() += 1;
}
}

for (entity, value) in intersections {
let Ok(mut name) = name_query.get_mut(entity) else { continue; };
name.0 = Some(format!("{value}").into());
}
}
2 changes: 2 additions & 0 deletions crates/valence/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub mod prelude {
pub use valence_core::ident; // Export the `ident!` macro.
pub use valence_core::uuid::UniqueId;
pub use valence_core::{translation_key, CoreSettings, Server};
pub use valence_entity::hitbox::{Hitbox, HitboxShape};

pub use super::DefaultPlugins;
use super::*;
Expand All @@ -127,6 +128,7 @@ impl PluginGroup for DefaultPlugins {
.add(valence_biome::BiomePlugin)
.add(valence_dimension::DimensionPlugin)
.add(valence_entity::EntityPlugin)
.add(valence_entity::hitbox::HitboxPlugin)
.add(valence_instance::InstancePlugin)
.add(valence_client::ClientPlugin);

Expand Down
4 changes: 3 additions & 1 deletion crates/valence_client/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ use bevy_ecs::system::SystemState;
use bytes::Bytes;
use tracing::{debug, warn};
use valence_core::packet::{Decode, Packet};
use valence_entity::hitbox::HitboxUpdateSet;

use crate::{Client, SpawnClientsSet};

pub(super) fn build(app: &mut App) {
app.configure_set(
RunEventLoopSet
.in_base_set(CoreSet::PreUpdate)
.after(SpawnClientsSet),
.after(SpawnClientsSet)
.after(HitboxUpdateSet),
)
.add_system(run_event_loop.in_set(RunEventLoopSet))
.add_event::<PacketEvent>();
Expand Down
30 changes: 30 additions & 0 deletions crates/valence_core/src/aabb.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ops::Add;

use glam::DVec3;

/// An axis-aligned bounding box. `min` is expected to be <= `max`
Expand Down Expand Up @@ -35,4 +37,32 @@ impl Aabb {
},
}
}

pub fn intersects(&self, second: Aabb) -> bool {
self.max.x >= second.min.x
&& second.max.x >= self.min.x
&& self.max.y >= second.min.y
&& second.max.y >= self.min.y
&& self.max.z >= second.min.z
&& second.max.z >= self.min.z
}
}

impl Add<DVec3> for Aabb {
type Output = Aabb;

fn add(self, rhs: DVec3) -> Self::Output {
Self {
min: self.min + rhs,
max: self.max + rhs,
}
}
}

impl Add<Aabb> for DVec3 {
type Output = Aabb;

fn add(self, rhs: Aabb) -> Self::Output {
rhs + self
}
}
2 changes: 1 addition & 1 deletion crates/valence_entity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ syn.workspace = true
serde_json.workspace = true
heck.workspace = true
serde.workspace = true
valence_build_utils.workspace = true
valence_build_utils.workspace = true
Loading

0 comments on commit 8897eea

Please sign in to comment.