ajout du programme
This commit is contained in:
2
.gitignore
vendored
Executable file
2
.gitignore
vendored
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
fonctions.ods
|
||||||
5106
Cargo.lock
generated
Executable file
5106
Cargo.lock
generated
Executable file
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
Executable file
14
Cargo.toml
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "snake-game"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy = "0.16.1"
|
||||||
|
rand = "0.9.2"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.dev.package."*"]
|
||||||
|
opt-level = 3
|
||||||
88
src/collision.rs
Normal file
88
src/collision.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
on enumere les differents type de collision (il en existe un nombre fini de type)
|
||||||
|
et on implement une fonction pour creer une variable capable de stocker un type de collision
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum Collision {
|
||||||
|
No,
|
||||||
|
Food,
|
||||||
|
Body,
|
||||||
|
Border,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collision {
|
||||||
|
fn new() -> Self {
|
||||||
|
Collision::No
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Systeme de collision entre les differentes entites
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn collision_checker(
|
||||||
|
head_query: Query<&GridPosition, With<Head>>,
|
||||||
|
body_query: Query<&GridPosition, With<Body>>,
|
||||||
|
food_query: Query<&GridPosition, With<Food>>,
|
||||||
|
) -> Collision {
|
||||||
|
let mut collision_type = Collision::new();
|
||||||
|
if food_collision(head_query, food_query) {
|
||||||
|
collision_type = Collision::Food;
|
||||||
|
}
|
||||||
|
if body_collision(head_query, body_query) {
|
||||||
|
collision_type = Collision::Body;
|
||||||
|
}
|
||||||
|
if border_collision(head_query) {
|
||||||
|
collision_type = Collision::Border;
|
||||||
|
}
|
||||||
|
collision_type
|
||||||
|
}
|
||||||
|
|
||||||
|
fn food_collision(
|
||||||
|
head_query: Query<&GridPosition, With<Head>>,
|
||||||
|
food_query: Query<&GridPosition, With<Food>>,
|
||||||
|
) -> bool {
|
||||||
|
// on balaie parmis les entites qui possedent le composant food (ici un seul) pour tester la collision
|
||||||
|
for food in &food_query {
|
||||||
|
for head in &head_query {
|
||||||
|
if head.x == food.x && head.y == food.y {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body_collision(
|
||||||
|
head_query: Query<&GridPosition, With<Head>>,
|
||||||
|
body_query: Query<&GridPosition, With<Body>>,
|
||||||
|
) -> bool {
|
||||||
|
// on balaie parmis les entites qui possedent le composant body pour tester la collision
|
||||||
|
for body in &body_query {
|
||||||
|
for head in &head_query {
|
||||||
|
if body.x == head.x && body.y == head.y {
|
||||||
|
// on modifie la valeur retournee par la fonction pour correspondre au type d'entite rencontree
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn border_collision(head_query: Query<&GridPosition, With<Head>>) -> bool {
|
||||||
|
// teste la position du composant head pour voir si celui-ci sort de la grille
|
||||||
|
for head in &head_query {
|
||||||
|
if head.x > 190 || head.x < -190 {
|
||||||
|
// on modifie la valeur retournee par la fonction pour correspondre au type de collision rencontree
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if head.y > 190 || head.y < -190 {
|
||||||
|
// on modifie la valeur retournee par la fonction pour correspondre au type de collision rencontree
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/components.rs
Normal file
64
src/components.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Composant de position sur la grille pour les differentes entites
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct GridPosition {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GridPosition {
|
||||||
|
pub fn new(x: i32, y: i32) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Composants qui distinguent les entites entre-elles
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Head;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Body;
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Food;
|
||||||
|
|
||||||
|
#[derive(Component, PartialEq)]
|
||||||
|
pub enum Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Right,
|
||||||
|
Left,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Direction {
|
||||||
|
pub fn new(direction: Direction) -> Self {
|
||||||
|
direction
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_direction(&mut self, direction: Direction) {
|
||||||
|
*self = direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct PreviousPosition {
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreviousPosition {
|
||||||
|
pub fn new(x: i32, y: i32) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/food_gen.rs
Normal file
66
src/food_gen.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use crate::components::{Body, Food, GridPosition, Head};
|
||||||
|
use crate::resources::FoodInGrid;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use rand::Rng;
|
||||||
|
/*
|
||||||
|
|
||||||
|
Systeme d'apparition de food sur la grille
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub fn food_spawn(
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut food: ResMut<FoodInGrid>,
|
||||||
|
head_query: Query<&GridPosition, With<Head>>,
|
||||||
|
body_query: Query<&GridPosition, With<Body>>,
|
||||||
|
) {
|
||||||
|
// si il existe deja un food sur la grille, on ignore cette portion de code
|
||||||
|
if !food.exist() {
|
||||||
|
let mut result = false;
|
||||||
|
// tant que le programme de ne donne pas une position libre pour l'apparition du food, on relance la boucle
|
||||||
|
while result == false {
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
// on recupere une valeur aleatoire pour chaque coordonnee de la position
|
||||||
|
let x = 10 * random_number();
|
||||||
|
let y = 10 * random_number();
|
||||||
|
|
||||||
|
// puis on compare ces coordonnees a la position actuelle de la tete
|
||||||
|
for head in &head_query {
|
||||||
|
if head.x == x && head.y == y {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ainsi que celle des entites de corps
|
||||||
|
for body in &body_query {
|
||||||
|
if body.x == x && body.y == y {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// si la position sur la grille est libre on fait apparaitre un food sur la grille
|
||||||
|
if result == true {
|
||||||
|
commands.spawn((
|
||||||
|
Food,
|
||||||
|
GridPosition::new(x, y),
|
||||||
|
Sprite::from_image(asset_server.load("food.png")),
|
||||||
|
Transform::from_xyz(x as f32, y as f32, 1.0).with_scale(Vec3::splat(0.135)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// on modifie la ressource FoodInGrid pour dire qu'il existe maintenant un food sur la grille
|
||||||
|
food.set();
|
||||||
|
|
||||||
|
// on joue un audio pour specifier au joueur qu'un food est apparu
|
||||||
|
commands.spawn(AudioPlayer::new(asset_server.load("i-like-trains.ogg")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fonction de generation de nombre pour la position d'appartition du food
|
||||||
|
fn random_number() -> i32 {
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
rng.random_range(-20..=20)
|
||||||
|
}
|
||||||
24
src/input_handler.rs
Normal file
24
src/input_handler.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
// une fonction pour effectuer des actions si un appuis sur un bouton particulier est detecte
|
||||||
|
pub fn key_pressed(key: Res<ButtonInput<KeyCode>>) -> char {
|
||||||
|
let mut result: char = '|';
|
||||||
|
// si la barre espace est appuiye, une iteration de play_train_sfx est lancee
|
||||||
|
if key.just_pressed(KeyCode::Space) {
|
||||||
|
result = ' ';
|
||||||
|
}
|
||||||
|
// dans le cas ou c'est la touche entree il n'y a encore rien de prevu
|
||||||
|
if key.just_pressed(KeyCode::KeyW) {
|
||||||
|
result = 'w';
|
||||||
|
};
|
||||||
|
if key.just_pressed(KeyCode::KeyA) {
|
||||||
|
result = 'a';
|
||||||
|
};
|
||||||
|
if key.just_pressed(KeyCode::KeyS) {
|
||||||
|
result = 's';
|
||||||
|
};
|
||||||
|
if key.just_pressed(KeyCode::KeyD) {
|
||||||
|
result = 'd';
|
||||||
|
};
|
||||||
|
result
|
||||||
|
}
|
||||||
54
src/main.rs
Executable file
54
src/main.rs
Executable file
@@ -0,0 +1,54 @@
|
|||||||
|
// on charge les paquets necessaires au fonctionnement du jeu
|
||||||
|
// TODO se renseigner sur les plugins
|
||||||
|
// necessaires pour faire tourner ce jeu
|
||||||
|
|
||||||
|
pub mod components;
|
||||||
|
pub mod food_gen;
|
||||||
|
pub mod input_handler;
|
||||||
|
pub mod movement;
|
||||||
|
pub mod resources;
|
||||||
|
pub mod sound;
|
||||||
|
pub mod visuals;
|
||||||
|
|
||||||
|
use crate::components::{Direction, GridPosition, Head};
|
||||||
|
use crate::food_gen::food_spawn;
|
||||||
|
|
||||||
|
use crate::movement::{change_direction, move_head};
|
||||||
|
use crate::resources::{FoodInGrid, MoveTimer};
|
||||||
|
use crate::visuals::update_visuals;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
// fonction main qui creer l'application graphique et execute les fonctions en fonction de la periode designer pour les executer
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.insert_resource(MoveTimer(Timer::from_seconds(1.0, TimerMode::Repeating)))
|
||||||
|
.add_systems(Startup, (setup, food_spawn).chain())
|
||||||
|
.add_systems(Update, (change_direction, move_head, update_visuals))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// fonction destinee a initialiser l'application avec les differentes fonctions et assets necessaire au lancement
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
// on invoque une camera pour avoir un rendu observable
|
||||||
|
commands.spawn(Camera2d);
|
||||||
|
|
||||||
|
// on charge l'arriere-plan du jeu
|
||||||
|
commands.spawn((
|
||||||
|
Sprite::from_image(asset_server.load("grid.png")),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 0.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
// on charge la tete du serpent a la position voulu
|
||||||
|
commands.spawn((
|
||||||
|
Head,
|
||||||
|
GridPosition::new(10, 10),
|
||||||
|
Direction::new(Direction::Up),
|
||||||
|
Sprite::from_image(asset_server.load("head.png")),
|
||||||
|
Transform::from_xyz(0.0, 0.0, 1.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
// on initialise les ressources
|
||||||
|
commands.insert_resource(FoodInGrid::new());
|
||||||
|
}
|
||||||
56
src/movement.rs
Normal file
56
src/movement.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use crate::components::{Direction, GridPosition, Head, PreviousPosition};
|
||||||
|
use crate::input_handler::key_pressed;
|
||||||
|
use crate::resources::MoveTimer;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub fn change_direction(
|
||||||
|
key: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut query_head: Query<(&mut Direction, &GridPosition, &PreviousPosition), With<Head>>,
|
||||||
|
) {
|
||||||
|
let value = key_pressed(key);
|
||||||
|
// change la direction de la tete en fonction de la touche qui a ete pressee
|
||||||
|
for (mut direction, position, previous_position) in &mut query_head {
|
||||||
|
match value {
|
||||||
|
'w' => {
|
||||||
|
if (position.y + 20) != previous_position.y {
|
||||||
|
direction.change_direction(Direction::Up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'a' => {
|
||||||
|
if (position.x - 20) != previous_position.x {
|
||||||
|
direction.change_direction(Direction::Left);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
's' => {
|
||||||
|
if (position.y - 20) != previous_position.y {
|
||||||
|
direction.change_direction(Direction::Down);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'd' => {
|
||||||
|
if (position.x + 20) != previous_position.x {
|
||||||
|
direction.change_direction(Direction::Right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_head(
|
||||||
|
time: Res<Time>,
|
||||||
|
mut timer: ResMut<MoveTimer>,
|
||||||
|
mut query_head: Query<(&mut GridPosition, &Direction), With<Head>>,
|
||||||
|
) {
|
||||||
|
// si le timer atteint zero, la tete avance de 20 (la taille d'une case) dans la direction actuelle de la tete
|
||||||
|
if timer.0.tick(time.delta()).just_finished() {
|
||||||
|
for (mut position, direction) in &mut query_head {
|
||||||
|
match direction {
|
||||||
|
Direction::Up => position.y += 20,
|
||||||
|
Direction::Down => position.y -= 20,
|
||||||
|
Direction::Right => position.x += 20,
|
||||||
|
Direction::Left => position.x -= 20,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/resources.rs
Normal file
28
src/resources.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
// creation d'un timer pour cadencer le mouvement de la tete
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct MoveTimer(pub Timer);
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct FoodInGrid {
|
||||||
|
pub state: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FoodInGrid {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { state: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self) {
|
||||||
|
self.state = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.state = false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exist(&self) -> bool {
|
||||||
|
self.state
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/sound.rs
Normal file
14
src/sound.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use crate::input_handler::key_pressed;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
// un fonction pour jouer l'audio "i like trains"
|
||||||
|
pub fn play_train_sfx(
|
||||||
|
key: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut commands: Commands,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
) {
|
||||||
|
if key_pressed(key) == ' ' {
|
||||||
|
commands.spawn(AudioPlayer::new(asset_server.load("train-sfx.ogg")));
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/visuals.rs
Normal file
18
src/visuals.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use crate::components::{Direction, GridPosition, Head};
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
// actualisation des sprites en fonction des position et de la direction de ceux-ci
|
||||||
|
pub fn update_visuals(
|
||||||
|
mut head_query: Query<(&mut Transform, &GridPosition, &Direction), With<Head>>,
|
||||||
|
) {
|
||||||
|
for (mut transform, position, direction) in &mut head_query {
|
||||||
|
transform.translation.x = position.x as f32;
|
||||||
|
transform.translation.y = position.y as f32;
|
||||||
|
match direction {
|
||||||
|
Direction::Up => transform.rotation = Quat::from_rotation_z(0_f32.to_radians()),
|
||||||
|
Direction::Down => transform.rotation = Quat::from_rotation_z(180_f32.to_radians()),
|
||||||
|
Direction::Right => transform.rotation = Quat::from_rotation_z(270_f32.to_radians()),
|
||||||
|
Direction::Left => transform.rotation = Quat::from_rotation_z(90_f32.to_radians()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user