Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pawn ai #8

Merged
merged 6 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ bevy = { version = "0.12.1", features = [
"wayland",
] }
bevy_egui = "0.24"
big-brain = { git = "https://github.com/PlagueAutomata/big-brain", rev = "aed238e" }
113 changes: 62 additions & 51 deletions src/character.rs
Original file line number Diff line number Diff line change
@@ -1,64 +1,59 @@
use crate::mechanics::*;
use crate::{game_state::GameState, selectable::Selectable};
use bevy::prelude::*;
use big_brain::prelude::*;

mod inventory;
mod movement;

pub use self::{
inventory::Inventory,
movement::{CachedFinder, FindAndMove},
};

pub const DEFAULT_COLOR: Color = Color::BLACK;
pub const SLEEP_COLOR: Color = Color::BLUE;
pub const FARM_COLOR: Color = Color::YELLOW;

pub struct CharacterPlugin;

impl Plugin for CharacterPlugin {
fn build(&self, app: &mut App) {
app.add_event::<SpawnCharacter>()
app.add_plugins((BigBrainPlugin::new(PreUpdate),))
.add_event::<SpawnCharacter>()
.add_systems(PreUpdate, spawner_system)
.add_systems(
Update,
(read_target_position, move_character)
.chain()
.run_if(in_state(GameState::Playing)),
sync_character_color.run_if(in_state(GameState::Playing)),
);
}
}

#[derive(Component)]
pub struct CharacterController {
pub target: Option<Vec3>,
pub velocity: Vec3,
pub speed: f32,
pub color: Color,
pub is_sleeping: bool,
}

#[derive(Component)]
pub struct MovementTarget(pub Entity);

pub fn read_target_position(
transforms: TransformHelper,
mut query: Query<(&mut CharacterController, &MovementTarget)>,
) {
for (mut ctrl, &MovementTarget(target)) in query.iter_mut() {
ctrl.target = transforms
.compute_global_transform(target)
.map(|transform| transform.translation())
.ok();
}
}

pub fn move_character(
time: Res<Time>,
mut query: Query<(&mut CharacterController, &mut Transform), Without<Parent>>,
pub fn sync_character_color(
query: Query<(&CharacterController, &Children)>,
material_query: Query<&Handle<StandardMaterial>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
for (mut ctrl, mut transform) in query.iter_mut() {
ctrl.velocity = match ctrl.target {
Some(target) => (target - transform.translation).normalize_or_zero() * ctrl.speed,
None => Vec3::ZERO,
};
transform.translation += ctrl.velocity * time.delta_seconds();

if ctrl.velocity.length() > 0.0 {
// TODO: remove pitch/roll
*transform = transform.looking_to(ctrl.velocity, Vec3::Y);
for (ctrl, children) in &query {
let child = children.first();
let material_id = child.and_then(|&entity| material_query.get(entity).ok());
let material = material_id.and_then(|id| materials.get_mut(id));
if let Some(material) = material {
material.base_color = ctrl.color;
}
}
}

#[derive(Event, Default)]
pub struct SpawnCharacter {
pub transform: Transform,
pub target: Option<Entity>,
pub model: CharacterModel,
}

Expand All @@ -84,29 +79,41 @@ pub fn spawner_system(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
for SpawnCharacter {
transform,
target,
model,
} in events.drain()
{
for SpawnCharacter { transform, model } in events.drain() {
let mut entity = commands.spawn((
SpatialBundle {
transform,
..default()
},
Selectable {},
CharacterController {
target: None,
velocity: Vec3::ZERO,
speed: 3.0,
speed: 5.0,
color: DEFAULT_COLOR,
is_sleeping: false,
},
Selectable {},
Fatigue {
current: 0.0,
change: 8.0,
},
Inventory {
money: 0,
items: 0.0,
},
Thinker::first_to_score(0.6)
.when(
FatigueScorer::default(),
Sequence::step((FindAndMove::<House>::new(0.1), Sleep::new(10.0, 30.0))),
)
.when(
WorkNeedScorer,
Sequence::step((FindAndMove::<Field>::new(0.1), Farm::new(10.0, 30.0))),
)
.when(
SellNeedScorer,
Sequence::step((FindAndMove::<Market>::new(0.1), Sell)),
),
));

if let Some(target) = target {
entity.insert(MovementTarget(target));
}

entity.with_children(|builder| {
let capsule = shape::Capsule {
depth: model.height,
Expand All @@ -117,7 +124,11 @@ pub fn spawner_system(
builder.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(capsule)),
material: materials.add(Color::YELLOW.into()),
transform: Transform::from_translation(Vec3::new(0.0, model.height / 2.0, 0.0)),
transform: Transform::from_translation(Vec3::new(
0.0,
model.height / 2.0 + model.radius,
0.0,
)),
..default()
});

Expand All @@ -130,7 +141,7 @@ pub fn spawner_system(
material: materials.add(Color::RED.into()),
transform: Transform::from_translation(Vec3::new(
0.0,
model.height - model.face_height / 2.0,
model.height - model.face_height / 2.0 + model.radius,
-model.radius,
)),
..default()
Expand Down
11 changes: 11 additions & 0 deletions src/character/inventory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use bevy::prelude::*;

#[derive(Component, Reflect)]
pub struct Inventory {
pub money: u32,
pub items: f32,
lain-dono marked this conversation as resolved.
Show resolved Hide resolved
}

impl Inventory {
pub const MAX_ITEMS: f32 = 20.0;
}
99 changes: 99 additions & 0 deletions src/character/movement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use super::CharacterController;
use bevy::prelude::*;
use big_brain::prelude::*;

// TODO: radius maybe readed from world

#[derive(Component, Clone, ActionSpawn)]
pub struct FindAndMove<T: Component + Clone> {
radius: f32,
finder: CachedFinder,
marker: std::marker::PhantomData<T>,
}

impl<T: Component + Clone> FindAndMove<T> {
pub fn new(radius: f32) -> Self {
Self {
radius,
finder: CachedFinder { target: None },
marker: std::marker::PhantomData,
}
}

pub fn system(
time: Res<Time>,
query: Query<(Entity, &Transform), With<T>>,
mut actors: Query<(&mut Transform, &CharacterController), Without<T>>,
mut actions: Query<(ActionQuery, &mut Self)>,
) {
for (mut action, mut move_to) in actions.iter_mut() {
if action.is_executing() {
let (mut transform, ctrl) = actors.get_mut(action.actor()).unwrap();

let Some(goal) = move_to.finder.find(&query, transform.translation) else {
action.failure();
continue;
};

let delta = goal.translation - transform.translation;
let distance = delta.length();

trace!("Distance to {:?}: {}", std::any::type_name::<T>(), distance);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не имеет обозначения кто вызывает этот трейс, и из-за этого в процессе дебага будет довольно бесполезен.


if distance > move_to.radius {
let step = (ctrl.speed * time.delta_seconds()).min(distance);
transform.translation += delta.normalize_or_zero() * step;
transform.look_to(delta, Vec3::Y);
} else {
debug!("Reached {:?}", std::any::type_name::<T>());
action.success()
}
}

if action.is_cancelled() {
debug!("Movement to {:?} is cancelled", std::any::type_name::<T>());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Добавь идентификацию процесса поиска.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мб я просто удалю весь трейс, раз он выглядит ненужным?


// cleanup just for sure
let _ = move_to.finder.take_target();
action.failure();
}
}
}
}

#[derive(Clone, Default)]
pub struct CachedFinder {
target: Option<Entity>,
}

impl CachedFinder {
pub fn target(&self) -> Option<Entity> {

Check failure on line 70 in src/character/movement.rs

View workflow job for this annotation

GitHub Actions / clippy

method `target` is never used

Check warning on line 70 in src/character/movement.rs

View workflow job for this annotation

GitHub Actions / build

method `target` is never used
self.target
}

#[must_use]
pub fn take_target(&mut self) -> Option<Entity> {
self.target.take()
}

pub fn find<T: Component>(
&mut self,
query: &Query<(Entity, &'_ Transform), With<T>>,
translation: Vec3,
) -> Option<Transform> {
let (entity, transform) = if let Some(entity) = self.target {
query.get(entity).ok()
} else {
debug!("Try find {:?}", std::any::type_name::<T>());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужна идентификация и начальная позиция

query.iter().min_by(|(_, a), (_, b)| {
let a = (a.translation - translation).length_squared();
let b = (b.translation - translation).length_squared();
f32::total_cmp(&a, &b)
})
}?;

self.target = Some(entity);

Some(*transform)
}
}
22 changes: 7 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use crate::character::SpawnCharacter;
use crate::raycast::PlaneRaycast;
use crate::selectable::{CurrentlySelected, Selectable};
use bevy::input::mouse::*;
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
window::PresentMode,
};

use crate::character::SpawnCharacter;
use crate::raycast::PlaneRaycast;
use crate::selectable::{CurrentlySelected, Selectable};
use bevy::input::mouse::*;

mod character;
mod game_state;
mod main_menu;
mod mechanics;
mod raycast;
mod selectable;
mod splash_screen;
Expand Down Expand Up @@ -50,6 +50,7 @@ fn main() {
bevy_egui::EguiPlugin,
crate::splash_screen::SplashScreenPlugin,
crate::main_menu::MainMenuPlugin,
crate::mechanics::MechanicsPlugin,
crate::character::CharacterPlugin,
crate::raycast::RaycastPlugin,
));
Expand All @@ -74,17 +75,8 @@ fn setup(
..default()
});

// cube
let cube = commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(Color::rgb_u8(124, 144, 255).into()),
transform: Transform::from_xyz(0.0, 0.5, 0.0),
..default()
});

spawner.send(SpawnCharacter {
transform: Transform::from_translation(Vec3::new(20.0, 0.0, 30.0)),
target: Some(cube.id()),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
..default()
});

Expand Down
Loading
Loading