diff --git a/apps/game/src/gameplay/galaxy/mod.rs b/apps/game/src/gameplay/galaxy/mod.rs index cdc7d3d..d573c1d 100644 --- a/apps/game/src/gameplay/galaxy/mod.rs +++ b/apps/game/src/gameplay/galaxy/mod.rs @@ -221,6 +221,7 @@ fn generate_system_positions(params: &GalaxyParams, rng: &mut StdRng) -> Vec, rng: &mut StdRng, @@ -321,6 +325,7 @@ fn generate_disk( base_spacing: f32, next_index: &mut usize, disk_index: usize, + core_radius: f32, ) { let arm_count = disk.arms.max(1); // Effective outer radius: explicit value if set, else galaxy size. @@ -329,7 +334,16 @@ fn generate_disk( } else { galaxy_size }; - let inner = disk.inner_radius; + // Anchor disk arms to the core: when inner_radius is 0 (auto), start + // systems inside the core zone so arms visually emerge from the core + // boundary rather than appearing detached. The 0.4 factor places the + // density peak well inside the core sphere, producing overlap that + // creates a seamless core-to-arm transition. + let inner = if disk.inner_radius > 0.0 { + disk.inner_radius + } else { + core_radius * 0.4 + }; let span = (outer - inner).max(1.0); // Pre-compute the rotation quaternion for this disk's tilt + Y rotation. @@ -354,7 +368,10 @@ fn generate_disk( let mut position = Option::::None; let mut final_radius = 0.0f32; for attempt in 0..SPACING_ATTEMPTS { - let r = inner + rng.gen::().powf(0.62) * span; + // powf(0.45) concentrates ~60% of systems in the inner 30% of the + // radial span, ensuring arms are dense near the core boundary and + // fade naturally toward the edge. + let r = inner + rng.gen::().powf(0.45) * span; let angle = std::f32::consts::TAU * arm as f32 / arm_count as f32 + ((r - inner) / span) * disk.twist + (rng.gen::() - 0.5) * arm_scatter(arm_count); diff --git a/apps/game/src/gameplay/galaxy/ui.rs b/apps/game/src/gameplay/galaxy/ui.rs index 8b85afc..b5cb452 100644 --- a/apps/game/src/gameplay/galaxy/ui.rs +++ b/apps/game/src/gameplay/galaxy/ui.rs @@ -1334,9 +1334,15 @@ fn randomize_disk(rng: &mut impl rand::Rng) -> DiskParams { disk.rotation_offset = rng.gen_range(DISK_ROTATION_MIN..=DISK_ROTATION_MAX); disk.rotation_offset = (disk.rotation_offset / DISK_ROTATION_STEP).round() * DISK_ROTATION_STEP; - disk.inner_radius = rng.gen_range(DISK_INNER_RADIUS_MIN..=DISK_INNER_RADIUS_MAX); - disk.inner_radius = - (disk.inner_radius / DISK_INNER_RADIUS_STEP).round() * DISK_INNER_RADIUS_STEP; + // Bias inner_radius toward 0 (auto) ~70% of the time so randomized disks + // are always anchored to the core by default. + if rng.gen_bool(0.7) { + disk.inner_radius = 0.0; + } else { + disk.inner_radius = rng.gen_range(DISK_INNER_RADIUS_MIN..=DISK_INNER_RADIUS_MAX); + disk.inner_radius = + (disk.inner_radius / DISK_INNER_RADIUS_STEP).round() * DISK_INNER_RADIUS_STEP; + } disk.outer_radius = rng.gen_range(DISK_OUTER_RADIUS_MIN..=DISK_OUTER_RADIUS_MAX); disk.outer_radius = (disk.outer_radius / DISK_OUTER_RADIUS_STEP).round() * DISK_OUTER_RADIUS_STEP;