main.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. use std::default::Default;
  2. use bevy::core::FixedTimestep;
  3. use bevy::prelude::*;
  4. use bevy::render::pass::ClearColor;
  5. use rand::prelude::random;
  6. pub mod components;
  7. use crate::components as c;
  8. pub mod snake;
  9. fn main() {
  10. App::build()
  11. .insert_resource(WindowDescriptor {
  12. title: "Snake!".to_string(),
  13. width: 500.0,
  14. height: 500.0,
  15. ..Default::default()
  16. })
  17. .insert_resource(ClearColor(Color::rgb(0.04, 0.04, 0.04)))
  18. .insert_resource(c::SnakeSegments::default())
  19. .insert_resource(c::LastTailPosition::default())
  20. .add_startup_system(setup.system())
  21. .add_startup_stage("game_setup", SystemStage::single(snake::spawn.system()))
  22. .add_plugins(DefaultPlugins)
  23. .add_system(
  24. snake::input
  25. .system()
  26. .label(c::SnakeMovement::Input)
  27. .before(c::SnakeMovement::Movement),
  28. )
  29. .add_system(game_over.system().after(c::SnakeMovement::Movement))
  30. .add_system_set(
  31. SystemSet::new()
  32. .with_run_criteria(FixedTimestep::step(0.3))
  33. .with_system(snake::movement.system().label(c::SnakeMovement::Movement))
  34. .with_system(
  35. snake::eating
  36. .system()
  37. .label(c::SnakeMovement::Eating)
  38. .after(c::SnakeMovement::Movement),
  39. )
  40. .with_system(
  41. snake::growth
  42. .system()
  43. .label(c::SnakeMovement::Growth)
  44. .after(c::SnakeMovement::Eating),
  45. ),
  46. )
  47. .add_system_set_to_stage(
  48. CoreStage::PostUpdate,
  49. SystemSet::new()
  50. .with_system(position_translation.system())
  51. .with_system(size_scaling.system()),
  52. )
  53. .add_system_set(
  54. SystemSet::new()
  55. .with_run_criteria(FixedTimestep::step(0.9))
  56. .with_system(food_spawner.system()),
  57. )
  58. .add_event::<c::GrowthEvent>()
  59. .add_event::<c::GameOverEvent>()
  60. .run();
  61. }
  62. fn setup(mut commands: Commands, mut materials: ResMut<Assets<ColorMaterial>>) {
  63. commands.spawn_bundle(OrthographicCameraBundle::new_2d());
  64. commands.insert_resource(c::Materials {
  65. head_material: materials.add(Color::rgb(0.7, 0.7, 0.7).into()),
  66. segment_material: materials.add(Color::rgb(0.3, 0.3, 0.3).into()),
  67. food_material: materials.add(Color::rgb(1.0, 0.0, 1.0).into()),
  68. });
  69. }
  70. fn food_spawner(mut commands: Commands, materials: Res<c::Materials>) {
  71. commands
  72. .spawn_bundle(SpriteBundle {
  73. material: materials.food_material.clone(),
  74. ..Default::default()
  75. })
  76. .insert(c::Food)
  77. .insert(c::Position {
  78. x: (random::<f32>() * c::ARENA_WIDTH as f32) as i32,
  79. y: (random::<f32>() * c::ARENA_HEIGHT as f32) as i32,
  80. })
  81. .insert(c::GridSize::square(0.8));
  82. }
  83. fn size_scaling(windows: Res<Windows>, mut q: Query<(&c::GridSize, &mut Sprite)>) {
  84. let window = windows.get_primary().unwrap();
  85. for (size, mut sprite) in q.iter_mut() {
  86. sprite.size = Vec2::new(
  87. size.width / c::ARENA_WIDTH as f32 * window.width() as f32,
  88. size.height / c::ARENA_HEIGHT as f32 * window.height() as f32,
  89. );
  90. }
  91. }
  92. fn position_translation(windows: Res<Windows>, mut q: Query<(&c::Position, &mut Transform)>) {
  93. fn convert(pos: f32, bound_window: f32, bound_game: f32) -> f32 {
  94. let tile_size = bound_window / bound_game;
  95. pos / bound_game * bound_window - (bound_window / 2.0) + (tile_size / 2.0)
  96. }
  97. let window = windows.get_primary().unwrap();
  98. for (pos, mut tf) in q.iter_mut() {
  99. tf.translation = Vec3::new(
  100. convert(pos.x as f32, window.width() as f32, c::ARENA_WIDTH as f32),
  101. convert(pos.y as f32, window.height() as f32, c::ARENA_HEIGHT as f32),
  102. 0.0,
  103. )
  104. }
  105. }
  106. fn game_over(
  107. mut commands: Commands,
  108. mut reader: EventReader<c::GameOverEvent>,
  109. materials: Res<c::Materials>,
  110. segments_res: ResMut<c::SnakeSegments>,
  111. food: Query<Entity, With<c::Food>>,
  112. segments: Query<Entity, With<c::SnakeSegment>>,
  113. ) {
  114. if reader.iter().next().is_some() {
  115. for ent in food.iter().chain(segments.iter()) {
  116. commands.entity(ent).despawn();
  117. }
  118. snake::spawn(commands, materials, segments_res);
  119. }
  120. }