Привет, мир, здесь я хочу поделиться с вами своей первой простой программой, написанной на Rust и библиотеке GGEZ (Good Game eazy).

О ГГЭЗ

Вкратце о GGEZ: это основа для создания 2D-игры с минимальным трением. Эта библиотека содержит базовый и переносимый 2D-рисунок, звук, загрузку ресурсов и обработку событий.

Программа, которую я сделал, представляет собой простую программу, которая реализует некоторые базовые функции этой библиотеки и помогает лучше понять последовательность выполнения кода. Во-первых, приложение, созданное с помощью этой библиотеки, состоит из трех частей: контекста, обработчика событий и цикла. Контекст - это то место, где все состояние, необходимое для взаимодействия с аппаратным обеспечением компьютера. Обработчик событий - это то место, где все события, такие как нажатие кнопки, щелчок мыши и т. Д., Выполняются в нашей программе. Цикл содержит информацию о том, как наша программа запускается и обновляется.

Вот как выглядит программа

Реализации

Далее поговорим о программе. Программы довольно просты, для этого требуется щелчок мышью и положение курсора, каждый щелчок выводит круг с центром в позиции курсора, после этого круг опускается и, когда он почти достигает нижней части окна, исчезает.

Прежде чем мы начали, вот заголовок.

use ggez;
use ggez::{graphics, Context, GameResult, timer};
use ggez::event::{self, EventHandler, MouseButton};
use ggez::nalgebra as na;

Во-первых, мы должны создать контекст. Контекст, как упоминалось ранее, содержит все состояние. Итак, нам нужно создать свое государство. Состояние - это структура, которая содержит список объектов в нашей программе, для этой программы - круг.

struct MainState {
 circles: Vec<CircleObject>
}

Наши круги - это вектор объекта CircleObject. Этот тип CircleObject имеет атрибуты pos_x и pos_y и реализует функцию draw (). Эта функция будет вызываться в нашем обработчике событий. Затем мы создаем нашу структуру CircleObject.

struct CircleObject {
  pos_x: f32,
  pos_y: f32,
}
impl CircleObject {
 
  fn draw(&self, ctx: &mut Context) -> GameResult<()> {
    
    // make our circle instance
    let circle = graphics::Mesh::new_circle(
      ctx,
      graphics::DrawMode::fill(), // Fill the circle
      na::Point2::new(self.pos_x, self.pos_y), // Center position
      50.0, // radius
      0.2, // mesh tolerance, higher smoother
      graphics::WHITE // Colouring
    )?;
    // draw our circle instance   
    graphics::draw(ctx, &circle, (na::Point2::new(0.0, 0.0),))?;
    Ok(())
  }
}

В методе рисования аргументы являются ссылкой на себя и изменяемой ссылкой на наш контекст. Внутри этого метода мы создаем новый экземпляр круга, используя графику, а затем вызываем метод graphics :: draw для рисования нашего экземпляра круга.

После этого нам нужно реализовать EventHandler для нашего State. Внутри этого EventHandler есть два обязательных метода update () и draw (). EventHandler сначала вызовет update (), здесь наше состояние обновляется. В этой программе мы хотим обновить положение Кругов, чтобы оно опустилось. В этом методе мы также проверяем, попал ли какой-нибудь кружок в нижнюю часть окна, а затем удаляем его из нашего вектора.

impl MainState {
// add this method to MainState implementation
  fn clean_circles(&mut self){
    self.circles.retain(|circle| circle.pos_y < 500.0);
  }
}
impl EventHandler for MainState {
  fn update(&mut self, _ctx: &mut Context) -> GameResult {
    const FPS: u32 = 24; // our desired FPS rate
    // we set our FPS to 24
    while timer::check_update_time(_ctx, FPS) {
      // update our circle position each frame
      // the new position equals to velocity * time passed
      let seconds = 1.0 / (FPS as f32);
      for b in &mut self.circles {
        b.pos_y += 100.0 * seconds;
      }
      // check for dropped circles
      self.clean_circles();
    }
    Ok(())
  }
}

После вызова обновления нам нужно перерисовать все окно. Здесь вызывается метод рисования круга. Внутри этого метода мы собираемся очистить все окна, а затем перерисовать весь экземпляр, который должен быть в окне после обновления.

// put this function inside EventHandler implementation
fn draw(&mut self, _ctx: &mut Context) -> GameResult {
  
  // Clearing the window  
  graphics::clear(_ctx, [0.1, 0.2, 0.3, 1.0].into());
  // Draw all of the Circles
  for circle in &self.circles {
    circle.draw(_ctx)?;
  }
  // this method needs to be called
  // to apply all of the changes
  graphics::present(_ctx)?;
  Ok(())
}

Помните, что мы хотим считывать данные, введенные мышью, в нашу программу. В EventHandler мы могли бы добавить в реализацию метод mouse_button_down_event. Этот метод считывает нажатия кнопок мыши и текущую позицию курсора.

// add this method inside EventHandler implementation
fn mouse_button_down_event(
  &mut self,
  _ctx: &mut Context,
  _button: MouseButton,
  _x: f32,
  _y: f32) {
     match _button {
      MouseButton::Left => {
        // creates new Circle and push to vector
        self.circles.push(CircleObject {
           pos_x: _x,
           pos_y: _y
          });
        }
        _ => {
         println!("Other button is clicked");
       }
     }
}

Наконец, мы могли создать нашу основную функцию

fn main () -> GameResult {
  // create a context builder  
  let cb = ggez::ContextBuilder::new("mouse_event", "miqdude");
  
  // create context and event_loop instance
  let (ctx, event_loop) = &mut cb.build()?;
  // our MainState goes here
  let state = &mut MainState::new(ctx)?;
  event::run(ctx, event_loop, state)
}

Вывод

Вот и все! Надеюсь, эта статья поможет вам создать вашу первую программу с использованием библиотеки GGEZ в Rust. Если при чтении этой статьи возникнут какие-либо трудности, сообщите мне, и я немедленно исправлю это. Спасибо, мир.

Смотрите сайт ГГЕЗ здесь.

Код можно посмотреть на моем гитхабе здесь.