snake.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. use bevy::prelude::*;
  2. use rand::prelude::random;
  3. use crate::components as c;
  4. fn spawn_segment(
  5. mut commands: Commands,
  6. material: &Handle<ColorMaterial>,
  7. position: c::Position,
  8. ) -> Entity {
  9. commands
  10. .spawn_bundle(SpriteBundle {
  11. material: material.clone(),
  12. ..Default::default()
  13. })
  14. .insert(c::SnakeSegment)
  15. .insert(position)
  16. .insert(c::GridSize::square(0.65))
  17. .id()
  18. }
  19. pub fn spawn(
  20. mut commands: Commands,
  21. materials: Res<c::Materials>,
  22. mut segments: ResMut<c::SnakeSegments>,
  23. ) {
  24. segments.segments = vec![
  25. commands
  26. .spawn_bundle(SpriteBundle {
  27. material: materials.head_material.clone(),
  28. sprite: Sprite::new(Vec2::new(10.0, 10.0)),
  29. ..Default::default()
  30. })
  31. .insert(c::SnakeHead {
  32. direction: c::Direction::Up,
  33. intention: c::Direction::Up,
  34. })
  35. .insert(c::SnakeSegment)
  36. .insert(c::Position { x: 3, y: 3 })
  37. .insert(c::GridSize::square(0.8))
  38. .id(),
  39. spawn_segment(
  40. commands,
  41. &materials.segment_material,
  42. c::Position { x: 3, y: 2 },
  43. ),
  44. ]
  45. }
  46. pub fn input(keyboard_input: Res<Input<KeyCode>>, mut heads: Query<&mut c::SnakeHead>) {
  47. if let Some(mut head) = heads.iter_mut().next() {
  48. let dir = if keyboard_input.pressed(KeyCode::Left) {
  49. c::Direction::Left
  50. } else if keyboard_input.pressed(KeyCode::Down) {
  51. c::Direction::Down
  52. } else if keyboard_input.pressed(KeyCode::Up) {
  53. c::Direction::Up
  54. } else if keyboard_input.pressed(KeyCode::Right) {
  55. c::Direction::Right
  56. } else {
  57. head.intention
  58. };
  59. if dir != head.direction.opposite() {
  60. head.intention = dir;
  61. }
  62. }
  63. }
  64. pub fn movement(
  65. segments: ResMut<c::SnakeSegments>,
  66. mut last_tail_position: ResMut<c::LastTailPosition>,
  67. mut game_over_writer: EventWriter<c::GameOverEvent>,
  68. mut heads: Query<(Entity, &mut c::SnakeHead)>,
  69. mut positions: Query<&mut c::Position>,
  70. ) {
  71. if let Some((head_entity, mut head)) = heads.iter_mut().next() {
  72. let segment_positions = segments
  73. .segments
  74. .iter()
  75. .map(|e| *positions.get_mut(*e).unwrap())
  76. .collect::<Vec<c::Position>>();
  77. let mut head_pos = positions.get_mut(head_entity).unwrap();
  78. head.direction = head.intention;
  79. match &head.direction {
  80. c::Direction::Left => head_pos.x -= 1,
  81. c::Direction::Right => head_pos.x += 1,
  82. c::Direction::Up => head_pos.y += 1,
  83. c::Direction::Down => head_pos.y -= 1,
  84. }
  85. if head_pos.x < 0
  86. || head_pos.y < 0
  87. || head_pos.x as u32 >= c::ARENA_WIDTH
  88. || head_pos.y as u32 >= c::ARENA_HEIGHT
  89. || segment_positions.contains(&head_pos)
  90. {
  91. game_over_writer.send(c::GameOverEvent);
  92. }
  93. segment_positions
  94. .iter()
  95. .zip(segments.segments.iter().skip(1))
  96. .for_each(|(pos, segment)| {
  97. *positions.get_mut(*segment).unwrap() = *pos;
  98. });
  99. last_tail_position.pos = Some(*segment_positions.last().unwrap());
  100. }
  101. }
  102. pub fn eating(
  103. mut commands: Commands,
  104. mut growth_writer: EventWriter<c::GrowthEvent>,
  105. food_positions: Query<(Entity, &c::Position), With<c::Food>>,
  106. head_positions: Query<&c::Position, With<c::SnakeHead>>,
  107. ) {
  108. for head_pos in head_positions.iter() {
  109. for (ent, food_pos) in food_positions.iter() {
  110. if food_pos == head_pos {
  111. commands.entity(ent).despawn();
  112. growth_writer.send(c::GrowthEvent);
  113. }
  114. }
  115. }
  116. }
  117. pub fn growth(
  118. commands: Commands,
  119. last_tail_position: Res<c::LastTailPosition>,
  120. mut segments: ResMut<c::SnakeSegments>,
  121. mut growth_reader: EventReader<c::GrowthEvent>,
  122. materials: Res<c::Materials>,
  123. ) {
  124. if growth_reader.iter().next().is_some() {
  125. segments.segments.push(spawn_segment(
  126. commands,
  127. &materials.segment_material,
  128. last_tail_position.pos.unwrap(),
  129. ));
  130. }
  131. }
  132. pub fn die(
  133. mut commands: Commands,
  134. mut reader: EventReader<c::GameOverEvent>,
  135. materials: Res<c::Materials>,
  136. segments_res: ResMut<c::SnakeSegments>,
  137. food: Query<Entity, With<c::Food>>,
  138. segments: Query<Entity, With<c::SnakeSegment>>,
  139. ) {
  140. if reader.iter().next().is_some() {
  141. for ent in food.iter().chain(segments.iter()) {
  142. commands.entity(ent).despawn();
  143. }
  144. spawn(commands, materials, segments_res);
  145. }
  146. }
  147. pub fn food(
  148. mut commands: Commands,
  149. materials: Res<c::Materials>,
  150. segments: Query<&c::Position, With<c::SnakeSegment>>,
  151. ) {
  152. let snake_positions = segments.iter().collect::<Vec<&c::Position>>();
  153. // try three times, giving up if we still haven't found a free spot
  154. let mut x;
  155. let mut y;
  156. let mut attempts = 3;
  157. loop {
  158. x = (random::<f32>() * c::ARENA_WIDTH as f32) as i32;
  159. y = (random::<f32>() * c::ARENA_HEIGHT as f32) as i32;
  160. if !snake_positions.contains(&&c::Position { x, y }) {
  161. break;
  162. }
  163. attempts -= 1;
  164. if attempts == 0 {
  165. return;
  166. }
  167. }
  168. commands
  169. .spawn_bundle(SpriteBundle {
  170. material: materials.food_material.clone(),
  171. ..Default::default()
  172. })
  173. .insert(c::Food)
  174. .insert(c::Position { x, y })
  175. .insert(c::GridSize::square(0.8));
  176. }