Rost lernen: Wie ich ein Schlangenspiel gemacht habe

Bild

Vor kurzem habe ich angefangen, die Programmiersprache Rust zu lernen, und da ich, als ich eine neue Sprache lerne, „Snake“ darauf mache , habe ich beschlossen, genau das zu machen.

Für 3D-Grafiken wurde die Three.rs- Bibliothek verwendet, die ein Port der Three.js-Bibliothek ist

Code
Herunterladen und spielen

Spiel Screenshot
Bild

Spielcode
/*  .    Cargo.toml [dependencies] rand="*" three="*" serde="*" bincode="*" serde_derive="*"  */ extern crate rand; extern crate three; extern crate bincode; extern crate serde; #[macro_use] extern crate serde_derive; //        . use rand::Rng; use three::*; use std::error::Error; //Entities ------------------------------------------------------------------ /*  .       .    : Debug -          Clone -        . .      clone() Eq  PartialEq    Point    == */ #[derive(Debug, Clone, Eq, PartialEq, Default)] //    .      struct Point { x: u8, y: u8, } //   impl Point { //      ==  ,         pub fn intersects(&self, point: &Point) -> bool { self.x == point.x && self.y == point.y } } #[derive(Debug, Clone, Eq, PartialEq, Default)] //               struct Frame { min_x: u8, min_y: u8, max_x: u8, max_y: u8, } impl Frame { pub fn intersects(&self, point: &Point) -> bool { point.x == self.min_x || point.y == self.min_y || point.x == self.max_x || point.y == self.max_y } } #[derive(Debug, Clone, Eq, PartialEq)] //   4  //            enum Direction { Left, Right, Top, Bottom, } //  (      ) //   . //        . impl Default for Direction { fn default() -> Direction { return Direction::Right; } } #[derive(Debug, Clone, Eq, PartialEq, Default)] //   struct Snake { direction: Direction, points: std::collections::VecDeque<Point>, start_x: u8, start_y: u8, } impl Snake { //         pub fn new(x: u8, y: u8) -> Snake { let mut points = std::collections::VecDeque::new(); for i in 0..3 { points.push_front(Point { x: x + i, y: i + y }); } Snake { direction: Direction::default(), points, start_x: x, start_y: y } } //       pub fn grow(mut self) -> Snake { if let Some(tail) = self.points.pop_back() { self.points.push_back(Point { x: tail.x, y: tail.y }); self.points.push_back(tail); } self } //      pub fn reset(self) -> Snake { Snake::new(self.start_x, self.start_y) } //       pub fn turn(mut self, direction: Direction) -> Snake { self.direction = direction; self } //                     pub fn try_eat(mut self, point: &Point) -> (Snake, bool) { let head = self.head(); if head.intersects(point) { return (self.grow(), true); } (self, false) } //            pub fn try_intersect_frame(mut self, frame: &Frame) -> Snake { let head = self.head(); if frame.intersects(&head) { return self.reset(); } self } //            . pub fn try_intersect_tail(mut self) -> Snake { let head = self.head(); let p = self.points.clone(); let points = p.into_iter().filter(|p| head.intersects(p)); if points.count() > 1 { return self.reset(); } self } //   pub fn head(&self) -> Point { self.points.front().unwrap().clone() } //               pub fn move_snake(mut self) -> Snake { if let Some(mut tail) = self.points.pop_back() { let head = self.head(); match self.direction { Direction::Right => { tail.x = head.x + 1; tail.y = head.y; } Direction::Left => { tail.x = head.x - 1; tail.y = head.y; } Direction::Top => { tail.x = head.x; tail.y = head.y - 1; } Direction::Bottom => { tail.x = head.x; tail.y = head.y + 1; } } self.points.push_front(tail); } self } } //Data Access Layer ---------------------------------------------------------------- #[derive(Debug, Clone, Eq, PartialEq, Default)] //       struct FoodGenerator { frame: Frame } impl FoodGenerator { //         pub fn generate(&self) -> Point { let x = rand::thread_rng().gen_range(self.frame.min_x + 1, self.frame.max_x); let y = rand::thread_rng().gen_range(self.frame.min_y + 1, self.frame.max_y); Point { x, y } } } #[derive(Serialize, Deserialize)] //      struct ScoreRepository { score: usize } impl ScoreRepository { //        // Result             fn save(value: usize) -> Result<(), Box<Error>> { use std::fs::File; use std::io::Write; let score = ScoreRepository { score: value }; //        bincode //  ?     . .          let bytes: Vec<u8> = bincode::serialize(&score)?; //          . let mut file = File::create(".\\score.data")?; match file.write_all(&bytes) { Ok(t) => Ok(t), //Error             //      Box         //                  //     Err(e) => Err(Box::new(e)) } } //     fn load() -> Result<usize, Box<Error>> { use std::fs::File; let mut file = File::open("./score.data")?; let data: ScoreRepository = bincode::deserialize_from(file)?; Ok(data.score) } } //Business Logic Layer------------------------------------------------------------ #[derive(Debug, Clone, Default)] //     struct Game { snake: Snake, frame: Frame, food: Point, food_generator: FoodGenerator, score: usize, max_score: usize, total_time: f32, } impl Game { //          fn new(height: u8, width: u8) -> Game { let frame = Frame { min_x: 0, min_y: 0, max_x: width, max_y: height }; let generator = FoodGenerator { frame: frame.clone() }; let food = generator.generate(); let snake = Snake::new(width / 2, height / 2); Game { snake, frame, food, food_generator: generator, score: 0, max_score: match ScoreRepository::load() { Ok(v) => v, Err(_) => 0 }, total_time: 0f32, } } // ,            //         //          //     fn update(mut self, time_delta_in_seconds: f32) -> Game { let (game, is_moving) = self.is_time_to_move(time_delta_in_seconds); self = game; if is_moving { self.snake = self.snake.clone() .move_snake() .try_intersect_tail() .try_intersect_frame(&self.frame); self.try_eat() } else { self } } //,        . fn is_time_to_move(mut self, time_delta_in_seconds: f32) -> (Game, bool) { let time_to_move: f32 = 0.030; self.total_time += time_delta_in_seconds; if self.total_time > time_to_move { self.total_time -= time_to_move; (self, true) } else { (self, false) } } //,         //    ,    //      fn try_eat(mut self) -> Game { let initial_snake_len = 3; if self.snake.points.len() == initial_snake_len { self.score = 0 } let (snake, eaten) = self.snake.clone().try_eat(&self.food); self.snake = snake; if eaten { self.food = self.food_generator.generate(); self.score += 1; if self.max_score < self.score { self.max_score = self.score; ScoreRepository::save(self.max_score); } }; self } //      fn handle_input(mut self, input: Direction) -> Game { let snake = self.snake.turn(input); self.snake = snake; self } } //Application Layer-------------------------------------------------------------- // --- Model ---- #[derive(Debug, Clone, Eq, PartialEq)] enum PointDtoType { Head, Tail, Food, Frame, } impl Default for PointDtoType { fn default() -> PointDtoType { PointDtoType::Frame } } #[derive(Debug, Clone, Eq, PartialEq, Default)] //       . struct PointDto { x: u8, y: u8, state_type: PointDtoType, } //------------------------------Controller ----------------------------- #[derive(Debug, Clone, Default)] //           struct GameController { game: Game, } impl GameController { fn new() -> GameController { GameController { game: Game::new(30, 30) } } //          fn get_state(&self) -> Vec<PointDto> { let mut vec: Vec<PointDto> = Vec::new(); vec.push(PointDto { x: self.game.food.x, y: self.game.food.y, state_type: PointDtoType::Food }); let head = self.game.snake.head(); vec.push(PointDto { x: head.x, y: head.y, state_type: PointDtoType::Head }); //      for p in self.game.snake.points.iter().filter(|p| **p != head) { vec.push(PointDto { x: px, y: py, state_type: PointDtoType::Tail }); } //   for x in self.game.frame.min_x..=self.game.frame.max_x { vec.push(PointDto { x: x, y: self.game.frame.max_y, state_type: PointDtoType::Frame }); vec.push(PointDto { x: x, y: self.game.frame.min_y, state_type: PointDtoType::Frame }); } //   for y in self.game.frame.min_y..=self.game.frame.max_y { vec.push(PointDto { x: self.game.frame.max_x, y: y, state_type: PointDtoType::Frame }); vec.push(PointDto { x: self.game.frame.min_x, y: y, state_type: PointDtoType::Frame }); } vec } //   fn update(mut self, time_delta: f32, direction: Option<Direction>) -> GameController { let game = self.game.clone(); self.game = match direction { None => game, Some(d) => game.handle_input(d) } .update(time_delta); self } pub fn get_max_score(&self) -> usize { self.game.max_score.clone() } pub fn get_score(&self) -> usize { self.game.score.clone() } } //------------------------View --------------- //           struct GameView { controller: GameController, window: three::Window, camera: three::camera::Camera, ambient: three::light::Ambient, directional: three::light::Directional, font: Font, current_score: Text, max_score: Text, } impl GameView { fn new() -> GameView { let controller = GameController::new(); //        let mut window = three::Window::new("3D Snake Game By Victorem"); //         let camera = window.factory.perspective_camera(60.0, 10.0..40.0); //   [x, y, z] camera.set_position([15.0, 15.0, 30.0]); //    let ambient_light = window.factory.ambient_light(0xFFFFFF, 0.5); window.scene.add(&ambient_light); //   let mut dir_light = window.factory.directional_light(0xffffff, 0.5); dir_light.look_at([350.0, 350.0, 550.0], [0.0, 0.0, 0.0], None); window.scene.add(&dir_light); //        let font = window.factory.load_font(".\\DejaVuSans.ttf"); //           let current_score = window.factory.ui_text(&font, "0"); let mut max_score = window.factory.ui_text(&font, "0"); max_score.set_pos([0.0, 40.0]); window.scene.add(&current_score); window.scene.add(&max_score); GameView { controller, window, camera, ambient: ambient_light, directional: dir_light, font, current_score, max_score } } //             fn get_input(&self) -> Option<Direction> { match self.window.input.keys_hit().last() { None => None, Some(k) => match *k { three::Key::Left => Some(Direction::Left), three::Key::Right => Some(Direction::Right), three::Key::Down => Some(Direction::Top), three::Key::Up => Some(Direction::Bottom), _ => None, } } } //           fn get_meshes(mut self) -> (Vec<Mesh>, GameView) { //  let sphere = &three::Geometry::uv_sphere(0.5, 24, 24); //           let green = &three::material::Phong { color: three::color::GREEN, glossiness: 30.0, }; let blue = &three::material::Phong { color: three::color::BLUE, glossiness: 30.0, }; let red = &three::material::Phong { color: three::color::RED, glossiness: 30.0, }; let yellow = &three::material::Phong { color: three::color::RED | three::color::GREEN, glossiness: 30.0, }; //       let meshes = self.controller.clone().get_state().iter().map(|s| { let state = s.clone(); match state.state_type { PointDtoType::Frame => { let m = self.window.factory.mesh(sphere.clone(), blue.clone()); m.set_position([state.x as f32, state.y as f32, 0.0]); m } PointDtoType::Tail => { let m = self.window.factory.mesh(sphere.clone(), yellow.clone()); m.set_position([state.x as f32, state.y as f32, 0.0]); m } PointDtoType::Head => { let m = self.window.factory.mesh(sphere.clone(), red.clone()); m.set_position([state.x as f32, state.y as f32, 0.0]); m } PointDtoType::Food => { let m = self.window.factory.mesh(sphere.clone(), green.clone()); m.set_position([state.x as f32, state.y as f32, 0.0]); m } } }).collect(); (meshes, self) } //   fn update(mut self) -> GameView { //       let elapsed_time = self.window.input.delta_time(); let input = self.get_input(); let controller = self.controller.update(elapsed_time, input); self.controller = controller; self } //    fn draw(mut self) -> GameView { let (meshes, view) = self.get_meshes(); self = view; //   . for m in &meshes { self.window.scene.add(m); } //    self.window.render(&self.camera); //  for m in meshes { self.window.scene.remove(m); } //    self.max_score.set_text(format!("MAX SCORE: {}", self.controller.get_max_score())); self.current_score.set_text(format!("CURRENT SCORE: {}", self.controller.get_score())); self } //         pub fn run(mut self) { //           while self.window.update() && !self.window.input.hit(three::KEY_ESCAPE) { self = self.update().draw(); } } } fn main() { let mut view = GameView::new(); view.run(); } 


Neben Three.rs habe ich auch Piston , eine Reihe von Bibliotheken zum Erstellen von Spielen, und Ametist , eine Spiel-Engine, in Betracht gezogen. Ich habe mich für Three.rs entschieden, weil es mir am einfachsten und am besten für das Prototyping geeignet erschien.
Leider war es im Rahmen dieses Spiels nicht möglich, die Abläufe zu spüren und mit dem Netzwerk zu arbeiten. Ich werde es beim nächsten Projekt versuchen. Bisher mag ich die Sprache und es ist mir ein Vergnügen, damit zu arbeiten. Ich wäre dankbar für gute Ratschläge und konstruktive Kritik.

Source: https://habr.com/ru/post/de428830/


All Articles