Basic basic game

This commit is contained in:
Paul Brinkmeier 2022-02-20 01:48:00 +01:00
parent b020d0564c
commit ce070ce4be
10 changed files with 3471 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

3159
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

16
Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "arnold-survivors"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.dev]
opt-level = 1
[profile.dev.package."*"]
opt-level = 3
[dependencies]
bevy = "0.6.1"
rand = "0.8.5"

BIN
assets/arno.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
assets/arno.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
assets/knife.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
assets/knife.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
assets/monster.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
assets/monster.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

295
src/main.rs Normal file
View File

@ -0,0 +1,295 @@
use std::f32::consts::PI;
use bevy::core::{FixedTimestep, Time, Timer};
use bevy::input::system::exit_on_esc_system;
use bevy::input::Input;
use bevy::math::Vec3;
use bevy::prelude::{
App, AssetServer, Commands, Component, Entity, KeyCode, OrthographicCameraBundle, Query, Res,
SystemSet, Transform, Without,
};
use bevy::sprite::{Sprite, SpriteBundle};
use bevy::window::Windows;
use bevy::DefaultPlugins;
use rand::Rng;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.add_system_set(
SystemSet::new()
.with_run_criteria(FixedTimestep::step(1.0 / 60.0))
.with_system(projectile_collide_system)
.with_system(arnold_movement_system)
.with_system(arnold_attack_system)
.with_system(linear_movement_system)
.with_system(expiration_date_system)
.with_system(follow_arnold_system)
.with_system(enemy_spawner_system),
)
.add_system(exit_on_esc_system)
.run();
}
fn setup(mut commands: Commands, assets: Res<AssetServer>) {
let arnold_sh = assets.load("arno.png");
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands
.spawn_bundle(SpriteBundle {
texture: arnold_sh,
..Default::default()
})
.insert(EnemySpawner::new(Timer::from_seconds(3.0, true)))
.insert(Arnold::new())
.insert(Transform {
translation: Vec3::new(0.0, 0.0, 0.0),
..Default::default()
});
}
#[derive(Component)]
struct Projectile {
}
impl Projectile {
fn new() -> Self {
Self {}
}
}
#[derive(Component)]
struct Hitpoints {
}
impl Hitpoints {
fn new() -> Self {
Self {}
}
}
fn projectile_collide_system(
mut commands: Commands,
projectile_query: Query<(&Projectile, &Transform)>,
target_query: Query<(Entity, &Hitpoints, &Transform)>
) {
for (entity, _hp, target_transform) in target_query.iter() {
let mut has_been_hit = false;
for (_projectile, projectile_transform) in projectile_query.iter() {
if target_transform.translation.distance(projectile_transform.translation) < 32.0 {
has_been_hit = true;
}
}
if has_been_hit {
commands.entity(entity).despawn();
}
}
}
#[derive(PartialEq)]
enum Facing {
Left,
Right,
}
#[derive(Component)]
struct Arnold {
facing: Facing,
attack_timer: Timer,
}
impl Arnold {
pub fn new() -> Self {
Self {
facing: Facing::Left,
attack_timer: Timer::from_seconds(1.0, true),
}
}
}
#[derive(Component)]
struct Knife {}
impl Knife {
fn new() -> Self {
Self {}
}
}
#[derive(Component)]
struct LinearMovement {
direction: Vec3,
}
impl LinearMovement {
fn new(direction: Vec3) -> Self {
Self { direction }
}
}
fn linear_movement_system(mut query: Query<(&LinearMovement, &mut Transform)>) {
for (lin_movement, mut transform) in query.iter_mut() {
transform.translation += lin_movement.direction;
}
}
#[derive(Component)]
struct ExpirationDate {
countdown: Timer,
}
impl ExpirationDate {
fn new(countdown: Timer) -> Self {
Self { countdown }
}
}
fn expiration_date_system(
mut commands: Commands,
time: Res<Time>,
mut query: Query<(Entity, &mut ExpirationDate)>,
) {
for (entity, mut exp_date) in query.iter_mut() {
if exp_date.countdown.tick(time.delta()).just_finished() {
commands.entity(entity).despawn();
}
}
}
fn arnold_movement_system(
keyboard_input: Res<Input<KeyCode>>,
mut query: Query<(&mut Arnold, &mut Sprite, &mut Transform)>,
) {
let (mut arnold, mut sprite, mut transform) = query.single_mut();
let mut velocity = Vec3::new(0.0, 0.0, 0.0);
if keyboard_input.pressed(KeyCode::A) {
velocity.x -= 1.0;
}
if keyboard_input.pressed(KeyCode::D) {
velocity.x += 1.0;
}
if keyboard_input.pressed(KeyCode::S) {
velocity.y -= 1.0;
}
if keyboard_input.pressed(KeyCode::W) {
velocity.y += 1.0;
}
velocity *= 1.5;
if keyboard_input.just_pressed(KeyCode::A) {
sprite.flip_x = false;
arnold.facing = Facing::Left;
}
if keyboard_input.just_pressed(KeyCode::D) {
sprite.flip_x = true;
arnold.facing = Facing::Right;
}
transform.translation += velocity;
}
fn arnold_attack_system(
assets: Res<AssetServer>,
mut commands: Commands,
time: Res<Time>,
mut query: Query<(&mut Arnold, &Transform)>,
) {
let (mut arnold, arnold_transform) = query.single_mut();
if arnold.attack_timer.tick(time.delta()).just_finished() {
let knife_velocity = match arnold.facing {
Facing::Left => Vec3::new(-1.0, 0.0, 0.0),
Facing::Right => Vec3::new(1.0, 0.0, 0.0),
} * 4.0;
commands
.spawn_bundle(SpriteBundle {
texture: assets.load("knife.png"),
sprite: Sprite {
flip_x: arnold.facing == Facing::Left,
..Default::default()
},
..Default::default()
})
.insert(Knife::new())
.insert(Transform {
translation: arnold_transform.translation
+ Vec3::new(0.0, 0.0, 10.0)
+ knife_velocity,
..Default::default()
})
.insert(LinearMovement::new(knife_velocity))
.insert(ExpirationDate::new(Timer::from_seconds(5.0, false)))
.insert(Projectile::new());
}
}
#[derive(Component)]
struct EnemySpawner {
timer: Timer,
}
impl EnemySpawner {
fn new(timer: Timer) -> Self {
Self { timer }
}
}
fn enemy_spawner_system(
mut commands: Commands,
assets: Res<AssetServer>,
windows: Res<Windows>,
time: Res<Time>,
mut query: Query<&mut EnemySpawner>,
) {
let mut enemy_spawner = query.single_mut();
if !enemy_spawner.timer.tick(time.delta()).just_finished() {
return;
}
let window = windows.get_primary().unwrap();
let spawn_distance = f32::max(window.width(), window.height()) / 2.0;
let mut rng = rand::thread_rng();
let phi = rng.gen_range(0.0..2.0 * PI);
commands
.spawn_bundle(SpriteBundle {
texture: assets.load("monster.png"),
transform: Transform {
translation: Vec3::new(phi.cos() * spawn_distance, phi.sin() * spawn_distance, 0.0),
..Default::default()
},
..Default::default()
})
.insert(FollowArnold::new(1.0))
.insert(Hitpoints::new());
}
#[derive(Component)]
struct FollowArnold {
speed: f32,
}
impl FollowArnold {
fn new(speed: f32) -> Self {
Self { speed }
}
}
fn follow_arnold_system(
mut follow_query: Query<(&FollowArnold, &mut Transform), Without<Arnold>>,
arnold_query: Query<(&Arnold, &Transform)>,
) {
let (_, arnold_transform) = arnold_query.single();
for (follow, mut follow_transform) in follow_query.iter_mut() {
let velocity = (arnold_transform.translation - follow_transform.translation).normalize()
* follow.speed;
follow_transform.translation += velocity;
}
}