ARYAN KINO

This commit is contained in:
2025-11-29 20:35:51 +01:00
commit 5de00847b0
5 changed files with 4662 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

4415
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

12
Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "birdgame"
version = "0.1.0"
edition = "2024"
[dependencies]
bevy = { version = "0.14", features = ["dynamic_linking"] } # 0.14 is current as of Nov 2025
bevy_flycam = "0.14" # optional: for free camera while testing
rand = "0.8"
[target.x86_64-pc-windows-msvc]
linker = "rust-lld"

BIN
assets/models/bird.glb Normal file

Binary file not shown.

234
src/main.rs Normal file
View File

@@ -0,0 +1,234 @@
use bevy::prelude::*;
use rand::Rng;
const GRAVITY: f32 = -20.0;
const FLAP_STRENGTH: f32 = 12.0;
const MOVE_SPEED: f32 = 10.0;
const ARENA_HALF: f32 = 22.0;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Bird PvP".into(),
resolution: (1280., 720.).into(),
..default()
}),
..default()
}))
.insert_resource(Scoreboard::default())
.insert_resource(ClearColor(Color::srgb(0.2, 0.6, 1.0)))
.add_systems(Startup, setup)
.add_systems(
Update,
(
player_input,
bird_physics,
bird_vs_bird_collision,
arena_collision,
ui_update,
),
)
.run();
}
#[derive(Resource, Default)]
struct Scoreboard {
p1: u32,
p2: u32,
}
#[derive(Component)]
struct Bird {
velocity: Vec3,
player_id: usize,
}
#[derive(Component)]
struct ScoreText;
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// LIGHT
commands.spawn(PointLightBundle {
point_light: PointLight {
intensity: 4000.0,
range: 200.,
shadows_enabled: true,
..default()
},
transform: Transform::from_xyz(20.0, 30.0, 20.0),
..default()
});
// GROUND
commands.spawn(PbrBundle {
mesh: meshes.add(Plane3d::default().mesh().size(60.0, 60.0)),
material: materials.add(Color::srgb(0.1, 0.7, 0.1)),
transform: Transform::from_xyz(0.0, -1.0, 0.0),
..default()
});
// PLAYER 1 — red bird
commands.spawn((
SceneBundle {
scene: asset_server.load("models/bird.glb#Scene0"),
transform: Transform::from_xyz(-5.0, 2.0, 0.0).with_scale(Vec3::splat(0.15)),
..default()
},
Bird {
velocity: Vec3::ZERO,
player_id: 1,
},
));
// PLAYER 2 — blue bird
commands.spawn((
SceneBundle {
scene: asset_server.load("models/bird.glb#Scene0"),
transform: Transform::from_xyz(5.0, 2.0, 0.0).with_scale(Vec3::splat(0.15)),
..default()
},
Bird {
velocity: Vec3::ZERO,
player_id: 2,
},
));
// CAMERA
commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(0.0, 30.0, 30.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
// UI
commands.spawn((
TextBundle::from_section(
"P1: 0 P2: 0",
TextStyle {
font_size: 40.0,
color: Color::WHITE,
..default()
},
),
ScoreText,
));
}
// ----------------------------------------------
// PLAYER INPUT
// ----------------------------------------------
fn player_input(input: Res<ButtonInput<KeyCode>>, mut birds: Query<(&mut Bird, &mut Transform)>) {
for (mut bird, mut tf) in &mut birds {
let mut move_x = 0.0;
let mut flap = false;
match bird.player_id {
1 => {
if input.pressed(KeyCode::KeyA) {
move_x -= 1.0;
}
if input.pressed(KeyCode::KeyD) {
move_x += 1.0;
}
if input.just_pressed(KeyCode::KeyW) {
flap = true;
}
}
2 => {
if input.pressed(KeyCode::ArrowLeft) {
move_x -= 1.0;
}
if input.pressed(KeyCode::ArrowRight) {
move_x += 1.0;
}
if input.just_pressed(KeyCode::ArrowUp) {
flap = true;
}
}
_ => {}
}
if flap {
bird.velocity.y = FLAP_STRENGTH;
}
tf.translation.x += move_x * MOVE_SPEED * 0.016;
}
}
// ----------------------------------------------
// PHYSICS
// ----------------------------------------------
fn bird_physics(time: Res<Time>, mut q: Query<(&mut Bird, &mut Transform)>) {
for (mut bird, mut tf) in &mut q {
bird.velocity.y += GRAVITY * time.delta_seconds();
tf.translation += bird.velocity * time.delta_seconds();
}
}
// ----------------------------------------------
// BIRD VS BIRD COLLISION
// ----------------------------------------------
fn bird_vs_bird_collision(mut birds: Query<(&mut Bird, &Transform)>) {
let mut iter = birds.iter_mut();
let Some((mut a, tf_a)) = iter.next() else {
return;
};
let Some((mut b, tf_b)) = iter.next() else {
return;
};
let diff = tf_b.translation - tf_a.translation;
let dist = diff.length();
if dist < 1.5 {
let push = diff.normalize() * 10.0;
a.velocity -= push;
b.velocity += push;
}
}
// ----------------------------------------------
// WALL COLLISION + SCORING
// ----------------------------------------------
fn arena_collision(
mut birds: Query<(&mut Transform, &mut Bird)>,
mut scoreboard: ResMut<Scoreboard>,
) {
for (mut tf, mut bird) in &mut birds {
if tf.translation.x.abs() > ARENA_HALF
|| tf.translation.z.abs() > ARENA_HALF
|| tf.translation.y < -2.0
|| tf.translation.y > 15.0
{
match bird.player_id {
1 => scoreboard.p2 += 1,
2 => scoreboard.p1 += 1,
_ => {}
}
// Respawn
tf.translation = Vec3::new(rand::thread_rng().gen_range(-5.0..5.0), 2.0, 0.0);
bird.velocity = Vec3::ZERO;
}
}
}
// ----------------------------------------------
// UI UPDATE
// ----------------------------------------------
fn ui_update(scoreboard: Res<Scoreboard>, mut text: Query<&mut Text, With<ScoreText>>) {
let mut ui = text.single_mut();
ui.sections[0].value = format!("P1: {} P2: {}", scoreboard.p1, scoreboard.p2);
}