📄 src/main.rs
mod bunnies;
mod dog;
mod obstacles;

use bevy::{
    DefaultPlugins,
    app::{App, Startup},
    asset::{AssetServer, Assets},
    camera::{Camera3d, OrthographicProjection, Projection, ScalingMode},
    ecs::{
        resource::Resource,
        system::{Commands, Res, ResMut},
    },
    gltf::GltfAssetLabel,
    light::{AmbientLight, DirectionalLight},
    math::{Quat, Vec3, primitives::Cuboid},
    mesh::{Mesh, Mesh3d, MeshBuilder, Meshable},
    prelude::{Deref, DerefMut},
    scene::SceneRoot,
    transform::components::Transform,
};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;

use crate::bunnies::BunnySystems;
use crate::dog::DogSystems;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, (setup, setup_fences))
        .init_resource::<Rng>()
        .add_dog_systems()
        .add_bunny_systems()
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn((
        Camera3d::default(),
        Projection::from(OrthographicProjection {
            scaling_mode: ScalingMode::FixedVertical {
                viewport_height: 30.0,
            },
            ..OrthographicProjection::default_3d()
        }),
        AmbientLight {
            brightness: 1000.0,
            ..Default::default()
        },
        Transform::from_xyz(0.0, 10.0, -5.0).looking_at(Vec3::ZERO, Vec3::Z),
    ));

    commands.spawn((
        DirectionalLight {
            illuminance: 15000.0,
            ..Default::default()
        },
        Transform::from_xyz(0.0, 0.0, 0.0).looking_to(Vec3::NEG_Y, Vec3::Z),
    ));
}

fn setup_fences(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut meshes: ResMut<Assets<Mesh>>,
) {
    let model =
        asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/fantasy-town-kit/fence.glb"));
    let mesh = meshes.add(
        Cuboid::from_length(1.0)
            .mesh()
            .build()
            .translated_by(Vec3::new(0.5, 0.0, 0.0)),
    );

    const SIDE: usize = 30;

    for offset in 0..SIDE {
        commands.spawn((
            SceneRoot(model.clone()),
            Transform::from_xyz(
                offset as f32 - (SIDE as f32 / 2.0 - 0.5),
                0.0,
                SIDE as f32 / 2.0 + 0.5,
            )
            .with_rotation(Quat::from_rotation_y(std::f32::consts::FRAC_PI_2)),
            Mesh3d(mesh.clone()),
        ));

        commands.spawn((
            SceneRoot(model.clone()),
            Transform::from_xyz(
                offset as f32 - (SIDE as f32 / 2.0 - 0.5),
                0.0,
                -(SIDE as f32 / 2.0 - 0.5),
            )
            .with_rotation(Quat::from_rotation_y(std::f32::consts::FRAC_PI_2)),
            Mesh3d(mesh.clone()),
        ));

        commands.spawn((
            SceneRoot(model.clone()),
            Transform::from_xyz(
                SIDE as f32 / 2.0 - 0.5,
                0.0,
                offset as f32 - (SIDE as f32 / 2.0 - 0.5),
            ),
            Mesh3d(mesh.clone()),
        ));

        commands.spawn((
            SceneRoot(model.clone()),
            Transform::from_xyz(
                -(SIDE as f32 / 2.0 + 0.5),
                0.0,
                offset as f32 - (SIDE as f32 / 2.0 - 0.5),
            ),
            Mesh3d(mesh.clone()),
        ));
    }
}

#[derive(Resource, Deref, DerefMut)]
struct Rng(ChaCha8Rng);

impl Default for Rng {
    fn default() -> Self {
        Self(ChaCha8Rng::seed_from_u64(0))
    }
}