Skip to content

Commit c72ce38

Browse files
committed
Adds transit fish behavior to aquarium
Adds a new fish behavior where fish swim straight across the screen and despawn once off-screen. This allows for new types of fish animations within the aquarium, such as schools of fish briefly crossing the display. Maintains fish behavior consistency by resizing the behavior vector to match the fish vector.
1 parent a5a7ca1 commit c72ce38

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

examples/egui_demo.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,7 @@ fn spawn_random_fish(state: &mut AquariumState, asset_count: usize) {
172172
position: (x, y),
173173
velocity: (vx, vy),
174174
});
175+
state
176+
.fish_behaviors
177+
.push(asciiquarium_rust::widgets::asciiquarium::FishBehavior::Normal);
175178
}

src/widgets/asciiquarium.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,24 @@ impl Default for AquariumEnvironment {
115115
}
116116
}
117117

118+
/// How a fish should behave within the aquarium.
119+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120+
pub enum FishBehavior {
121+
/// Normal aquarium fish: bounces on edges and persists.
122+
Normal,
123+
/// Transit fish: swims straight across and despawns once fully off-screen.
124+
Transit,
125+
}
126+
118127
/// The aquarium state that the parent application owns and updates.
119128
#[derive(Debug, Default)]
120129
pub struct AquariumState {
121130
/// Bounds of the aquarium in character cells (width, height).
122131
pub size: (usize, usize),
123132
/// All fish currently in the aquarium.
124133
pub fishes: Vec<FishInstance>,
134+
/// Behavior associated with each fish (parallel to `fishes`). Defaults to Normal.
135+
pub fish_behaviors: Vec<FishBehavior>,
125136
/// Rising bubbles.
126137
pub bubbles: Vec<Bubble>,
127138
/// Background/props animation state.
@@ -379,6 +390,15 @@ pub fn update_aquarium(state: &mut AquariumState, assets: &[FishArt]) {
379390
// Ensure environment exists.
380391
ensure_environment_initialized(state);
381392

393+
// Keep `fish_behaviors` in sync with `fishes` (pad with Normal, or truncate).
394+
if state.fish_behaviors.len() < state.fishes.len() {
395+
state
396+
.fish_behaviors
397+
.resize(state.fishes.len(), FishBehavior::Normal);
398+
} else if state.fish_behaviors.len() > state.fishes.len() {
399+
state.fish_behaviors.truncate(state.fishes.len());
400+
}
401+
382402
// Integrate fish and handle bounce.
383403

384404
// Spawn entities deterministically when none present and past next spawn tick.
@@ -453,10 +473,17 @@ pub fn update_aquarium(state: &mut AquariumState, assets: &[FishArt]) {
453473
position: (xi, y as f32),
454474
velocity: (speed, 0.0),
455475
});
476+
state.fish_behaviors.push(FishBehavior::Transit);
456477
}
457478
state.env.next_school_spawn = state.tick + 1800; // ~60s at 30 fps
458479
}
459-
for fish in &mut state.fishes {
480+
// Update fish with behavior-aware logic (Transit vs Normal).
481+
let mut kept_fishes: Vec<FishInstance> = Vec::with_capacity(state.fishes.len());
482+
let mut kept_behaviors: Vec<FishBehavior> = Vec::with_capacity(state.fish_behaviors.len());
483+
for i in 0..state.fishes.len() {
484+
let mut fish = state.fishes[i].clone();
485+
let behavior = *state.fish_behaviors.get(i).unwrap_or(&FishBehavior::Normal);
486+
460487
fish.position.0 += fish.velocity.0 * dt * fish_speed_mult;
461488
fish.position.1 += fish.velocity.1 * dt * fish_speed_mult;
462489
// Subtle deterministic horizontal jitter (does not mutate velocity)
@@ -470,7 +497,20 @@ pub fn update_aquarium(state: &mut AquariumState, assets: &[FishArt]) {
470497
.map(|a| (a.width as f32, a.height as f32))
471498
.unwrap_or((1.0, 1.0));
472499

473-
// Bounce on X.
500+
if behavior == FishBehavior::Transit {
501+
// Despawn transit fish once fully off-screen.
502+
let off_right = fish.position.0 > aw;
503+
let off_left = fish.position.0 + fw <= 0.0;
504+
if off_right || off_left {
505+
// drop (do not keep)
506+
} else {
507+
kept_fishes.push(fish);
508+
kept_behaviors.push(behavior);
509+
}
510+
continue;
511+
}
512+
513+
// Normal bounce behavior.
474514
if fish.position.0 < 0.0 {
475515
fish.position.0 = 0.0;
476516
fish.velocity.0 = fish.velocity.0.abs();
@@ -493,7 +533,6 @@ pub fn update_aquarium(state: &mut AquariumState, assets: &[FishArt]) {
493533
}
494534
}
495535

496-
// Bounce on Y.
497536
if fish.position.1 < 0.0 {
498537
fish.position.1 = 0.0;
499538
fish.velocity.1 = fish.velocity.1.abs();
@@ -515,7 +554,12 @@ pub fn update_aquarium(state: &mut AquariumState, assets: &[FishArt]) {
515554
fish.velocity.0 = -fish.velocity.0;
516555
}
517556
}
557+
558+
kept_fishes.push(fish);
559+
kept_behaviors.push(behavior);
518560
}
561+
state.fishes = kept_fishes;
562+
state.fish_behaviors = kept_behaviors;
519563

520564
// Occasionally emit bubbles from fish mouths, deterministically based on tick.
521565
// Emit every 24 ticks per fish to avoid randomness in the core crate.

0 commit comments

Comments
 (0)