Jugar a Rust en 24 horas: experiencia de desarrollo personal

Jugar a Rust en 24 horas: experiencia de desarrollo personal

En este artículo hablaré sobre mi experiencia personal al desarrollar un juego pequeño en Rust. Me tomó alrededor de 24 horas crear una versión funcional (trabajaba principalmente por las tardes o los fines de semana). El juego está lejos de estar terminado, pero creo que la experiencia será gratificante. Compartiré lo que aprendí y algunas observaciones que hice mientras creaba el juego desde cero.

Skillbox recomienda: Curso práctico de dos años. "Soy un desarrollador web PRO".

Recordamos: para todos los lectores de "Habr": un descuento de 10 rublos al inscribirse en cualquier curso de Skillbox utilizando el código promocional "Habr".

¿Por qué oxidarse?

Elegí este lenguaje porque he oído muchas cosas buenas sobre él y veo que se está volviendo cada vez más popular en el desarrollo de juegos. Antes de escribir el juego, tenía poca experiencia desarrollando aplicaciones simples en Rust. Esto fue suficiente para darme una sensación de libertad mientras escribía el juego.

¿Por qué el juego y qué tipo de juego?

¡Hacer juegos es divertido! Ojalá hubiera más motivos, pero para los proyectos “caseros” elijo temas que no están demasiado relacionados con mi trabajo habitual. ¿Qué juego es este? Quería hacer algo así como un simulador de tenis que combinara Cities Skylines, Zoo Tycoon, Prison Architect y el tenis mismo. En general, resultó ser un juego sobre una academia de tenis donde la gente viene a jugar.

Entrenamiento tecnico

Quería usar Rust, pero no sabía exactamente cuánto trabajo preliminar se necesitaría para comenzar. No quería escribir sombreadores de píxeles y usar arrastrar y soltar, así que buscaba las soluciones más flexibles.

Encontré recursos útiles que comparto contigo:

Exploré varios motores de juegos Rust y finalmente elegí Piston y ggez. Los encontré mientras trabajaba en un proyecto anterior. Al final, elegí ggez porque parecía más adecuado para implementar un pequeño juego 2D. La estructura modular de Piston es demasiado compleja para un desarrollador novato (o alguien que trabaja con Rust por primera vez).

Estructura del juego

Pasé algún tiempo pensando en la arquitectura del proyecto. El primer paso es hacer "tierra", gente y canchas de tenis. La gente tiene que moverse por los tribunales y esperar. Los jugadores deben tener habilidades que mejoren con el tiempo. Además, debería haber un editor que le permita agregar nuevas personas y tribunales, pero esto ya no es gratuito.

Habiendo pensado todo bien, me puse manos a la obra.

Creación de juegos

Principio: círculos y abstracciones.

Tomé un ejemplo de ggez y apareció un círculo en la pantalla. ¡Maravilloso! Ahora algunas abstracciones. Pensé que sería bueno abstraerse de la idea de un objeto de juego. Cada objeto debe representarse y actualizarse como se indica aquí:

// the game object trait
trait GameObject {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()>;
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;
}
 
// a specific game object - Circle
struct Circle {
    position: Point2,
}
 
 impl Circle {
    fn new(position: Point2) -> Circle {
        Circle { position }
    }
}
impl GameObject for Circle {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        let circle =
            graphics::Mesh::new_circle(ctx, graphics::DrawMode::Fill, self.position, 100.0, 2.0)?;
 
         graphics::draw(ctx, &circle, na::Point2::new(0.0, 0.0), 0.0)?;
        Ok(())
    }
}

Este fragmento de código me dio una buena lista de objetos que podía actualizar y renderizar en un bucle igualmente agradable.

mpl event::EventHandler for MainState {
    fn update(&mut self, context: &mut Context) -> GameResult<()> {
        // Update all objects
        for object in self.objects.iter_mut() {
            object.update(context)?;
        }
 
        Ok(())
    }
 
    fn draw(&mut self, context: &mut Context) -> GameResult<()> {
        graphics::clear(context);
 
        // Draw all objects
        for object in self.objects.iter_mut() {
            object.draw(context)?;
        }
 
        graphics::present(context);
 
        Ok(())
    }
}

main.rs es necesario porque contiene todas las líneas de código. Dediqué un poco de tiempo a separar los archivos y optimizar la estructura del directorio. Así quedó después de eso:
recursos -> aquí es donde están todos los activos (imágenes)
src
- entidades
— juego_object.rs
- círculo.rs
— main.rs -> bucle principal

Personas, suelos e imágenes.

El siguiente paso es crear un objeto de juego Persona y cargar imágenes. Todo debe construirse sobre la base de baldosas de 32*32.

Jugar a Rust en 24 horas: experiencia de desarrollo personal

Canchas de tenis

Después de estudiar cómo son las canchas de tenis, decidí hacerlas con baldosas de 4*2. Inicialmente, era posible hacer una imagen de este tamaño o juntar 8 mosaicos separados. Pero luego me di cuenta de que sólo se necesitaban dos mosaicos únicos, y he aquí por qué.

En total tenemos dos de estos mosaicos: 1 y 2.

Cada sección de la cancha consta de la loseta 1 o 2. Se pueden distribuir normalmente o girar 180 grados.

Jugar a Rust en 24 horas: experiencia de desarrollo personal

Modo de construcción básica (montaje)

Después de lograr la representación de sitios, personas y mapas, me di cuenta de que también era necesario un modo de ensamblaje básico. Lo implementé así: cuando se presiona el botón, se selecciona el objeto y el clic lo coloca en el lugar deseado. Entonces, el botón 1 te permite seleccionar una cancha y el botón 2 te permite seleccionar un jugador.

Pero aún necesitamos recordar qué significan 1 y 2, así que agregué una estructura alámbrica para dejar claro qué objeto fue seleccionado. Esto es lo que parece.

Jugar a Rust en 24 horas: experiencia de desarrollo personal

Preguntas de arquitectura y refactorización.

Ahora tengo varios objetos del juego: personas, canchas y pisos. Pero para que los wireframes funcionen, es necesario decirle a cada entidad de objeto si los objetos en sí están en modo de demostración o si simplemente se ha dibujado un marco. Esto no es muy conveniente.

Me pareció que era necesario repensar la arquitectura de una manera que revelara algunas limitaciones:

  • Tener una entidad que se renderiza y actualiza a sí misma es un problema porque esa entidad no podrá “saber” qué se supone que debe renderizar: una imagen y una estructura alámbrica;
  • falta de una herramienta para intercambiar propiedades y comportamiento entre entidades individuales (por ejemplo, la propiedad is_build_mode o la representación de comportamiento). Sería posible utilizar la herencia, aunque no existe una forma adecuada de implementarla en Rust. Lo que realmente necesitaba era el diseño;
  • se necesitaba una herramienta de interacción entre entidades para asignar personas a los tribunales;
  • las entidades mismas eran una mezcla de datos y lógica que rápidamente se salió de control.

Investigué un poco más y descubrí la arquitectura. ECS - Sistema de componentes de entidad, que se usa comúnmente en juegos. Estos son los beneficios de ECS:

  • los datos están separados de la lógica;
  • composición en lugar de herencia;
  • Arquitectura centrada en datos.

ECS se caracteriza por tres conceptos básicos:

  • entidades: el tipo de objeto al que se refiere el identificador (podría ser un jugador, una pelota u otra cosa);
  • componentes: las entidades se componen de ellos. Ejemplo: componente de renderizado, ubicaciones y otros. Estos son almacenes de datos;
  • sistemas: utilizan tanto objetos como componentes, además contienen comportamiento y lógica que se basan en estos datos. Un ejemplo es un sistema de renderizado que recorre en iteración todas las entidades con componentes de renderizado y realiza el renderizado.

Después de estudiarlo, quedó claro que ECS resuelve los siguientes problemas:

  • utilizar el diseño en lugar de la herencia para organizar entidades sistémicamente;
  • deshacerse del desorden de códigos a través de los sistemas de control;
  • usando métodos como is_build_mode para mantener la lógica de estructura alámbrica en el mismo lugar: en el sistema de renderizado.

Esto es lo que sucedió después de implementar ECS.

recursos -> aquí es donde están todos los activos (imágenes)
src
- componentes
—posición.rs
— persona.rs
— tenis_court.rs
— piso.rs
- estructura alámbrica.rs
— mouse_tracked.rs
- recursos
—ratón.rs
- sistemas
— renderizado.rs
— constantes.rs
- utils.rs
— world_factory.rs -> funciones de fábrica mundial
— main.rs -> bucle principal

Asignamos personas a los tribunales.

ECS ha hecho la vida más fácil. Ahora tenía una forma sistemática de agregar datos a entidades y agregar lógica basada en esos datos. Y esto, a su vez, permitió organizar la distribución de personas entre los tribunales.

Qué he hecho:

  • se agregaron datos sobre los tribunales asignados a la Persona;
  • se agregaron datos sobre personas distribuidas a TennisCourt;
  • se agregó CourtChoosingSystem, que permite analizar personas y canchas, detectar canchas disponibles y distribuir jugadores entre ellas;
  • Se agregó un PersonMovementSystem, que busca personas asignadas a los tribunales y, si no están allí, las envía a donde deben estar.

Jugar a Rust en 24 horas: experiencia de desarrollo personal

En resumen

Realmente disfruté trabajando en este sencillo juego. Además, me alegro de haber usado Rust para escribirlo porque:

  • Rust te brinda lo que necesitas;
  • tiene excelente documentación, Rust es bastante elegante;
  • la consistencia es fría;
  • no tienes que recurrir a la clonación, copia u otras acciones similares, lo que hacía a menudo en C++;
  • Las opciones son muy fáciles de usar y manejan muy bien los errores;
  • si el proyecto se pudo compilar, entonces el 99% de las veces funciona, y exactamente como debería. Creo que los mensajes de error del compilador son los mejores que he visto.

El desarrollo de juegos en Rust apenas comienza. Pero ya existe una comunidad estable y bastante grande que trabaja para abrir Rust a todos. Por lo tanto, miro el futuro de la lengua con optimismo y espero con ansias los resultados de nuestro trabajo común.

Skillbox recomienda:

Fuente: habr.com

Añadir un comentario