Aprendiendo Rust - Kiwi Towers

JuAn4k4

Imagino que ya lo habrás mirado: https://github.com/bevyengine/awesome-bevy

Tiene otros juegos de ejemplo y algún resource que te puede ir bien como el de los mapas.

La verdad es que hay bastante poco, se nota que está aún en su fase inicial.

1 respuesta
Lecherito

#31 sip, estoy mirando esas cosas también pero amos, como has dicho esto está muy en pañales y apenas hay ningún ejemplo así que lo que estoy haciendo es investigación y aprendizaje a base de ver el código fuente del engine y algún que otro ejemplo.

De todas maneras creo que no se me está dando nada mal, creía que iba a tener mucha más dificultad ya que el lenguaje y el motor son nuevos para mi y me estoy sintiendo bastante cómodo escribiendo EN Rust. Todavía noto que me faltan algunos aspectos pero vamos, cosa de mirar el libro y buscar un poquito en duckduckgo

1 respuesta
desu

#32 xq tanto .unwrap()

q hace el bundle?

1 respuesta
Lecherito

#33 Porque eran pruebas, todo ese codigo ya no existe en el repositorio.

El bundle es un simple conjunto de componentes

1 respuesta
desu

#34 Mas te vale que no existe.

Como funciona en el motor el Bundle, he mirado la doc oficial por encima pero no lo he acabado de pillar.

Lo que hace es sacar de tus entidades/struct los subcomponentes para gestionarlos en el sistema?

Me ha parecido que hacia algo del estilo.

Esta interesante el framework.

JuAn4k4

Es el bundle algo así como "un trozo" de lo que acaba siendo una entidad, ¿no?

Es decir, puedes hacer un bundle de melee attack, donde de defines el daño, los % de hit, crit, miss y demás. Y ya sería cuestión de añadir ese bundle a la entidad final o a otro bundle (no se si se puede, aunque he leido que hay jerarquías por lo que entiendo que si.) Y crear sistemas en base a esos bundles.

Un día de estos empiezo el OS de componentes bevy, aunque no sepa ni de Rust ni de gamedev.

@desu Lo veo además de tu estilo, haciendo los systems funcionales para poder definir la lógica del juego desde fuera, por ej: critRating -> %crit, o attack + target -> damage (hof)... etc

2 respuestas
Lecherito

#36 yo apenas sabía de rust ni de game dev xD estoy aprendiendo sobre la marcha. Sé que como lo hago no es como se debería hacer pero todavía he de encontrar la manera buena. Ya seré más pesado en stackoverflow o algo

1 1 respuesta
desu

#36 Si es el estilo que uso yo tambien para todo, yo tengo curiosidad de los ECS, como utilizan los macros de rust (que tampoco son la gran cosa) para hacer cosas que en otros lados tienes en runtime.

#37 Al final tu que ya sabes picar codigo lo importante es meterle lineas de codigo y hacer que las cosas funcionen.

Tu venias de cpp? O rust es lo unico que haces sin GC? Yo tambien encuentro que se avanza muy rapido pero es darte cabezazos contra el compilador hasta acabar de pillar las cosas xd A nivel basico no me parece nada complicado, encuentro cpp mas lioso. Es que el estilo imperativo lo tenemos demasiado metido dentro, no es muy agresivo pasarte a rust comparado con otros. Cuando empiezo a meter lineas si que me pierdo un poco.

2 respuestas
JuAn4k4

#38 Yo personalmente he odiado siempre que el ide no te diga si compila o no compila, creo que es un trauma adquirido en la uni de programar en vi a pelo y luego acomodarme en un ide y lenguajes simplones como C#/Java/Kt.

Hoy en día solo soy capaz de programar en js así. Al final con lo demas mezclo sintaxis

Lecherito

#38 Cuando empece, lo hice con C pero no llegue a hacer nada raro con ello asi que la complejidad era infima. Lo mas complejo que hice en su dia fue un piano para la Nintendo DS que podia guardar canciones (16 teclas creo que eran). Asi que se podria decir que Rust es el primero que hago sin GC.

Ayer estuve un poco con el plan que tengo, no es nada del otro mundo pero cosas que tarde o temprano me gustaria implementar asi como algo de documentacion ya que no voy a ser el unico trabajando en esto!

Hoy he estado investigando un poco mas sobre el engine y todavia le faltarian muchas cosas necesarias para el editor de mapas (como un input de texto), pero he estado hablando con un colega y quiza se anime a hacer el la parte de la interfaz/editor mientras que yo estare con todo el backend + la logica del juego. De aqui a unos meses el motor estara bastante mas avanzado (y pulido), y yo sabre mucho mas sobre Rust asi que no lo veo tan mal plan ya que sabia que no iba a ser rapido para nada ademas de que sabia donde me metia al hacerlo tan "temprano".

Lecherito

Hoy he estado con los tests. La verdad es que no son para nada difíciles ya que los sistemas son bastante pequeños y cada uno hace lo suyo. El código es algo extenso y con muchas cosas en común que me gustaría sacar a una macro en el futuro próximo ya que el setup es siempre el mismo.

Por lo demás los tests del ataque ya los tengo y los rulo con cargo test. Una de las siguientes cosas que debo hacer es montar algún tipo de CI para que al menos corra los tests y diga si está todo bien o no.

Pero eso ya lo dejo para otro día, la verdad es que me está sorprendiendo el motor y me gustaría que pasaran un par de años ipsofacto para tenerlo mucho más completo lol

1 1 respuesta
JuAn4k4

#41 Con rust lo más rápido para hacer CI es docker y github actions. En dos patadas lo montas.

desu

No hay alguna opcion de cargo para que si no tira los tests no compile? Se que hay cosas de este estilo sobretodo en el nightly

1 respuesta
JuAn4k4

#43 ¿ No necesita compilar para ejecutar los tests ?

Lecherito

Ayer monte lo de los github actions con algo muy parecido a lo que hace el propio engine (ya que me faltaban las mismas librerias). Y todo furula bien, le tuve que quitar algunas cosas que luego ire arreglando poco a poco pero por ahora todo funciona bien.

Obviamente no se puede rular los test sin compilar pero creo que se refiere a que si no tira los test, no pase la build xD (y eso ya es asi, ya que el exit code != 0 y eso la falla)

Lecherito

Pues he reescrito un poco como tenia hecho lo de los components y las entidades, he aprendido bastante mas sobre ello y ademas he hecho que los mobs mueran! (aunque no dan oro para nada). He entendido un poco mejor como funcionan los bundles y la verdad es que no esta nada mal.

He invertido el como se hacen las cosas y he creado un trait para que lo implementen los bundles para spawnear la entidad con no solo el bundle si no con lo que haga falta (como por ejemplo el timer en el caso de las torres):

pub trait Spawnable {
    fn spawn(self, commands: &mut Commands) -> Entity;
}

impl Spawnable for Tower {
    fn spawn(self, commands: &mut Commands) -> Entity {
        let timer = Timer::new(Duration::from_secs_f32(self.attack.attack_speed.0), true);
        return commands
            .spawn(())
            .with_bundle(self)
            .with(timer)
            .current_entity()
            .unwrap();
    }


#[derive(Debug)]
pub struct TowerComp;

#[derive(Debug, Bundle)]
pub struct Tower {
    pub comp: TowerComp,
    pub hp: HealthPoints,
    pub attack: Attack,
    pub critical: Option<Critical>,
    pub slow: Option<Slow>,
    pub stun: Option<Stun>,
}

Con esto se ha de cambiar obviamente la signature del sistema pero bueno, algo bastante sencillo. Lo unico que no me gusta es que has de poner el .0 en muchos de los casos al ser structs con un solo parametro sin nombre.

fn attack_mobs(
    mut commands: Commands,
    mut towers: Query<(&TowerComp, &Attack, &Timer)>,
    mut mobs: Query<(Entity, &MobComp, &mut HealthPoints)>,
) {

Pero con esto quedan cosas super guays como el sistema para borrar los minion del "mapa":

fn despawn_dead_entities(mut commands: Commands, dead_mobs: Query<(Entity, &Dead)>) {
    for (entity, _) in dead_mobs.iter() {
        commands.despawn(entity);
    }
}

Son literalmente 3 lineas!

Asi que sigo cuidando mas o menos el codigo, a;adiendo mas tests y mejorando el plugin basico que simula un mapa. Lo siguiente (ya tengo las issues creadas en el repo) seria a;adir waves y que el spawn de los mobs se pueda configurar.

Lecherito

Pues sigo en ello, ahora mismo ya tengo un sistema (en verdad son varios), que spawnean una oleada de mobs. Por ahora lo tengo solamente el primer mob ya que todavia no tengo ni idea como lo hare al final pero bueno, esta testeado y lo siguiente sera ver como lo puedo meter en el plugin para un mapa muy simple.

He rehecho (otra vez) el como se spawnean las cosas aunque es bastante parecido a la ultima vez. Ahora he decidido que esto sea (mas o menos), lo que continee los datos y se convierte en componentes al llamar a spawn, de esta manera podemos generar multiple spawns desde un mismo mob.

#[derive(Debug, Bundle)]
pub struct Tower {
    pub hp: f32,
    pub attack: Attack,
    pub critical: Option<Critical>,
    pub slow: Option<Slow>,
    pub stun: Option<Stun>,
}

impl Spawnable for Tower {
    fn spawn(self, commands: &mut Commands) -> Entity {
        let timer = Timer::new(Duration::from_secs_f32(self.attack.attack_speed.0), true);
        return commands
            .spawn(())
            .with(TowerComp)
            .with(HealthPoints(self.hp))
            .with(self.attack.clone())
            .with(self.critical.clone())
            .with(self.slow.clone())
            .with(self.stun.clone())
            .with(timer)
            .current_entity()
            .unwrap();
    }
}

Sobre el spawn de las oleadas, la cosa funciona de la siguiente manera, lo que hago es spawnear una serie de timers (1 por oleada) que tiene como propiedad cuantas veces se va a ejecutar altes de hacerse el harakiri:

// Esto mira los wavetimer y spawnea los mobs cada vez que termina
pub(crate) fn spawn_wave(mut commands: Commands, mut query: Query<(&Timer, &WaveTimer, &Mob)>) {
    for (timer, _, mob) in query.iter() {
        if timer.finished {
            println!("Spawning mob.");
            mob.spawn(&mut commands);
        }
    }
}

// Este se encarga de que el wavetimer no este para siempre!
pub(crate) fn check_multiple_timers(
    mut commands: Commands,
    mut query: Query<(Entity, &Timer, &mut WaveTimer)>,
) {
    for (entity, timer, mut mult_times) in query.iter_mut() {
        if timer.just_finished {
            mult_times.times -= 1;
        }

        if mult_times.times <= 0 {
            commands.despawn(entity);
        }
    }
}

Y el como se spawnean estos timers tambien esta hecho en su propio sistema asi que no tienen ni idea uno del otro!

pub struct WaveTimer {
    duration: Duration,
    times: i32,
}

impl WaveTimer {
    fn from(wave: &Wave) -> Self {
        Self {
            duration: Duration::from_secs_f32(wave.timer),
            times: wave.mobs.len().try_into().unwrap(),
        }
    }

    fn new(duration: Duration, times: i32) -> Self {
        Self { duration, times }
    }
}

impl Spawnable for WaveTimer {
    fn spawn(self, commands: &mut Commands) -> Entity {
        let timer = Timer::new(self.duration, true);
        return commands
            .spawn(())
            .with(self)
            .with(timer)
            .current_entity()
            .unwrap();
    }
}

Asi que ya tengo esto mas o menos furulando y los mobs estan siendo spawneados. Asi que como he dicho antes, ahora lo que tengo que hacer es cambiar el mob.spawn system del simple plugin al system que hace spawn de las waves y esperar :D

9 días después
Lecherito

Y con otros poquitos cambios ya tengo funcionando lo de hacer spawn de una oleada (un mob cada X tiempo) y una torre que les dispara. En este ejemplo la torre tiene 2 targets por lo que los dos mobs que se spawnean (que parecen no hacerlo cada X tiempo), son matados al instante

2020-11-26T23:00:55.785300+01:00 INFO kiwi_towers - Initializing
2020-11-26T23:00:55.786988+01:00 INFO kiwi_towers::plugins::simple - Initializing SimpleMapPlugin
Spawning mob.
Spawning mob.
2020-11-26T23:00:56.789374+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has 0 hp.
2020-11-26T23:00:56.789440+01:00 INFO kiwi_towers::attack - Killed mob 3v0
2020-11-26T23:00:56.789462+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has 0 hp.
2020-11-26T23:00:56.789482+01:00 INFO kiwi_towers::attack - Killed mob 4v0

Si se cambia la torre a 1 target solo:

2020-11-29T12:01:34.770119+01:00 INFO kiwi_towers - Initializing
2020-11-29T12:01:34.771829+01:00 INFO kiwi_towers::plugins::simple - Initializing SimpleMapPlugin
Spawning mob.
Spawning mob.
2020-11-29T12:01:35.774109+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has 0 hp.
2020-11-29T12:01:35.774181+01:00 INFO kiwi_towers::attack - Killed mob 3v0
2020-11-29T12:01:36.774170+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has 0 hp.
2020-11-29T12:01:36.774239+01:00 INFO kiwi_towers::attack - Killed mob 4v0

Vemos que si que ataca 1 segundo despues (velocidad de ataque). Asi que la cosa va cogiendo forma, y ahora las bases si estan siendo sentadas bien (o al menos eso es lo que veo) ya que escribir mas codigo no cuesta mas, simplemente es a;adir otro sistema que haga algo extra.

Aunque una de las cosas que mas lia sobre ECS son las stages. Las stages son basicamente un conjunto de sistemas que actuan a la vez y esto tiene unas caracteristicas especiales dado que los Commands se ejecutan al final del stage asi que si en una misma tienes algo que spawnea, en ese mismo frame no lo va a ver el siguiente sistema, lo vera en el siguiente frame (y esto puede crear algunos problemas aunque no sean facilmente visibles).

La solucion a esto es usar lo que llaman thread_local_system. Ese sistema se va a ejecutar en un hilito el solo y el World se va a modificar en ese mismo momento, asi que es una buena forma para decir algo como: este sistema es muy critico y se necesita actualizar YA. Que es lo que he hecho con los spawns al ver este comportamiento en los test.

Ejemplo del SimpleMapPlugin ahora:

    fn build(&self, app: &mut AppBuilder) {
        info!("Initializing SimpleMapPlugin");
        let game = Game {
            lives: 40,
            starting_coins: HashMap::default(),
            towers: vec![],
            waves: vec![Wave::from(vec![Mob::new(100_f32), Mob::new(1000_f32)], 0.5)]
        };

        app.add_stage_before("update", "preupdate");
        app.add_stage_after("update", "postupdate");

        app.add_resource(GameState::from(&game));
        app.add_resource(game);

        app.add_startup_system(add_tower.system());
        app.add_startup_system( spawn_mobs.thread_local_system());
        app.add_system_to_stage("preupdate", spawn_wave.system());
        app.add_plugin(AttackPlugin);
        app.add_system_to_stage("update", check_multiple_timers.system());
        app.add_system_to_stage("postupdate", finish_game.system());
    }

Esto lo hice hace unos dias pero tengo demasiadas cosas que hacer y ahora a esto no le estoy dedicando el tiepo que me gustaria

Lecherito

Ah, pues si que funciona bien lo del spawn de los mobs, pero es todo tan rapido que creia que no lul

2020-11-29T13:09:40.778105+01:00 INFO kiwi_towers - Initializing
2020-11-29T13:09:40.780010+01:00 INFO kiwi_towers::plugins::simple - Initializing SimpleMapPlugin
2020-11-29T13:09:41.282286+01:00 INFO kiwi_towers::plugins::tower_defense - Spawning mob. Mob { hp: 100.0, ms: Speed(0), attack: None }
2020-11-29T13:09:41.782576+01:00 INFO kiwi_towers::plugins::tower_defense - Spawning mob. Mob { hp: 100.0, ms: Speed(0), attack: None }
2020-11-29T13:09:41.782795+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has 0 hp.
2020-11-29T13:09:41.782830+01:00 INFO kiwi_towers::attack - Killed mob 3v0
2020-11-29T13:09:41.782851+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has 0 hp.
2020-11-29T13:09:41.782870+01:00 INFO kiwi_towers::attack - Killed mob 4v0

Ahi si podemos ver que hay 0.5 segundos entre el spawn de uno y otro.

Otro ejemplo con 2 torres, una con 22,1 (ataque y velocidad) y otra 100,2.5

2020-11-29T13:13:25.705309+01:00 INFO kiwi_towers - Initializing
2020-11-29T13:13:25.707140+01:00 INFO kiwi_towers::plugins::simple - Initializing SimpleMapPlugin
2020-11-29T13:13:26.209176+01:00 INFO kiwi_towers::plugins::tower_defense - Spawning mob. Mob { hp: 100.0, ms: Speed(0), attack: None }
2020-11-29T13:13:26.709448+01:00 INFO kiwi_towers::plugins::tower_defense - Spawning mob. Mob { hp: 100.0, ms: Speed(0), attack: None }
2020-11-29T13:13:26.709700+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has 78 hp.
2020-11-29T13:13:26.709741+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has 78 hp.
2020-11-29T13:13:26.709806+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has 56 hp.
2020-11-29T13:13:26.709830+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has 56 hp.
2020-11-29T13:13:27.709219+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has 34 hp.
2020-11-29T13:13:27.709277+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has 34 hp.
2020-11-29T13:13:27.709337+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has 12 hp.
2020-11-29T13:13:27.709368+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has 12 hp.
2020-11-29T13:13:28.209664+01:00 INFO kiwi_towers::attack - Now the mob 3v0 has -88 hp.
2020-11-29T13:13:28.209717+01:00 INFO kiwi_towers::attack - Killed mob 3v0
2020-11-29T13:13:28.709517+01:00 INFO kiwi_towers::attack - Now the mob 4v0 has -10 hp.
2020-11-29T13:13:28.709576+01:00 INFO kiwi_towers::attack - Killed mob 4v0

Y todo parece funcionar perfecto :D

Lecherito

Acabo de actualizar Bevy a la ultima version (que viene de git) y me estan gustando los cambios. Por ejemplo ya no hace falta el .system o .thread_local_system en cada uno de los sistemas, ahora es todo automatico. Tambien han quitado los ticks automaticos de todos los Timer asi que me he vuelto un poco loco pero nada que no se arregle leyendo el changelog.

Por lo demas todo esta chuli y funcionando, he de seguir con el "end game" y poco mas, ahora voy a echar un hots y a dormir.

1
Lecherito

Ahora he a;adido que el jugador pierda si se queda sin vidas aunque todavia no esta codificado que los mobs lleguen al final. Asi el jugador no va a perder nunca pero bueno, ya tengo mis tests funcionando tranquilamente. Se queda super limpito (aunque en el futuro ha de cambiar ya que hay diferentes maneras de perder)

fn finish_no_lives_game(state: Res<GameState>, mut app_exit: ResMut<Events<AppExit>>) {
    if state.lives <= 0 {
        app_exit.send(AppExit)
    }
}

Por otra parte he refactorizado un poquito y metido todo bien en plugins, con sus funciones privadas ya que asi no puedo ni tan siquier caer en la tentacion de usarlas fuera de donde deberia. Cada plugin hace una cosa y por ahora tengo unos cuantos:

  1. Plugin para a;adir las scenes necesarias (por ahora ninguna, pero ya esta preparado)
  2. Plugin que maneja el ataque
  3. Plugin para cuando ha de terminar la partida (por ejemplo tiene dos checkeos, el numero de vidas y cuando termina la partida satisfactoriamente)
  4. Grupo con todos los plugins ya que asi solo necesito aplicar un plugin en el main

Ejemplo

#[derive(Default)]
pub struct EndGamePlugin;

impl Plugin for EndGamePlugin {
    fn build(&self, app: &mut AppBuilder) {
        app.add_system_to_stage(stage::POST_UPDATE, finish_successful_game);
        app.add_system_to_stage(stage::POST_UPDATE, finish_no_lives_game);
    }
}

Y por ahora todo va 12/12, he de hacer algunos tests mas (sobre todo probando mejor los plugins antes que las funciones), pero por ahora estoy bastante contento conforme esta quedando.

1
Lecherito

Como anedota, por ahora las lineas de codigo quedan asi:

--------------------------------------------------------------------------------
 Language             Files        Lines        Blank      Comment         Code
--------------------------------------------------------------------------------
 Rust                    13          836          121            3          712
 Toml                     1           14            3            1           10
 YAML                     1            8            0            0            8
 Markdown                 1            0            0            0            0
--------------------------------------------------------------------------------
 Total                   16          858          124            4          730
--------------------------------------------------------------------------------
Lecherito

Pues siguiente episodio, he estado investigando un poquito mas sobre Bevy y las cosas que van haciendo en master, la verdad es que el desarrollo es super activo, y su discord aun mas.

Hay una forma de que un componente en la entidad no este en la Query como tal (los datos sobre los que luego vas a iterar) si no que sean una especie de condiciones.

fn attack_mobs(
    commands: &mut Commands,
    mut towers: Query<(&Attack, &Timer, Option<&Critical>), (With<TowerComp>)>,
    mut mobs: Query<(Entity, &mut HealthPoints), (Without<Dead>, With<MobComp>)>,
)

Ahora tambien existe un derive que te completa los parametros de la query con los items de la struct, asi no se te quedan queries kilometricas, aunque por ahora no me esta pasando, cuando tenga que a;adir el Slow, Stun, Invis.... supongo que habra unas cuantas todavia no se como quedara eso pero bueno, estoy en ello.

Ademas, como el snippet anterior indica, ya he implementado el ataque critico, aunque no me gusta que todo este en el mismo sistema, seguramente lo acabe dividiendo de alguna manera.

Lecherito

Entre hoy y ayer he estado aprendiendo sobre macros. La verdad es que parecen la leche en vinagre aunque me gustaria aprender un poquito mas sobre procedural macros (e incluso macros 2.0). Por ahora he creado una macro para poder testear mas facilmente el codigo sin tener que entrar a saber como funcionan los Scheduler/World/Resources y esas mierdas.

#[cfg(test)]
macro_rules! test_setup {
    ($world:ident, $($var_type:ident: $var_name:ident),*) => {
        let mut resources = Resources::default();
        let mut $world = World::default();
        let mut commands = Commands::default();
        commands.set_entity_reserver($world.get_entity_reserver());
        $(
            test_setup_spawn!($var_type, commands, $var_name);
        )*
        commands.apply(&mut $world, &mut resources);

        let mut resources = Resources::default();

        let mut schedule = Schedule::default();
        schedule.add_stage("update");
        $(
            test_setup_system!($var_type schedule, "update", $var_name);
        )*
        schedule.initialize(&mut $world, &mut resources);
        schedule.run(&mut $world, &mut resources);
    };
}

Luego tengo otras 2 macros para el system y el spawn. Y se usa de una manera super sencilla, por ahora solo he traducido uno de los tests pero cuando termine de cenar van los demas.

    #[test]
    fn do_not_attack_to_dead_mobs() {
        let tower = Tower::new(
            Damage(50_f32),
            100_f32,
            Bullet::default(),
            AttackSpeed(0.5_f32),
            Targets(2),
        );
        let mob1 = Mob::new(100_f32);
        let mob2 = Mob::new(0_f32);

        test_setup!(
            world,
            spawn: mob1,
            spawn: mob2,
            spawn: tower,
            system: advance_timers,
            system: attack_mobs
        );

        let mob1_health_points = world.get::<HealthPoints>(mob1).unwrap();
        let mob2_health_points = world.get::<HealthPoints>(mob2).unwrap();

        assert_eq!(mob1_health_points.0, 50_f32);
        assert_eq!(mob2_health_points.0, 0_f32);
    }

Me gusta el spawn: mob1, system: xxxxxx y se pueden repetir las veces que hagan falta. No ha sido nada super interesante pero al menos veo cuando rompo las cosas (que ya me ha pasado mas de una vez). No se todavia que va a ser lo siguiente, tengo una lista de issues en el repo asi que ya elegire algo, pero me mola como esta quedando

1
Lecherito

He continuado con la macro de los tests y aunque he escrito mas codigo del que me he ahorrado por ahora, me ha bajado un cojon las lineas de codigo de los tests que no tendran los siguientes.

Ahora se me ha quedado asi:

        test_setup!(
            world;
            run_times: 1;
            spawn: tower, mob1;
            res: game, game_state;
            system: advance_timers, attack_mobs;
            system: despawn_dead_entities "post_update"
        );

Le he a;adido para que en una linea puedas tener varios items. Tambien para que en un sistema le puedas decir la stage en la que correr, cuantos frames han de pasar (run_times) y tambien para resources. Asi que por ahora voy a cerrar este capitulo aunque lo vaya mejorando poco a poco ya voy a dejar de centrarme en esto.

Ejemplo de test:

    #[test]
    fn multi_attack_tower() {
        let tower = Tower::new(
            Damage(50_f32),
            100_f32,
            Bullet::default(),
            AttackSpeed(0.5_f32),
            Targets(2),
        );
        let mob1 = Mob::new(100_f32);
        let mob2 = Mob::new(100_f32);

        test_setup!(
            world;
            run_times: 1;
            spawn: tower, mob1, mob2;
            system: advance_timers, attack_mobs
        );

        let mob1_health_points = world.get::<HealthPoints>(mob1).unwrap();
        let mob2_health_points = world.get::<HealthPoints>(mob2).unwrap();

        assert_eq!(mob1_health_points.0, 50_f32);
        assert_eq!(mob2_health_points.0, 50_f32);
    }

Para ma;ana, tengo 3 issues abiertas sobre las oleadas de mobs que voy a revisar.

Y estoy bastante pendiente de https://github.com/bevyengine/bevy/pull/1021 que hace un poco un rework de como funcionan las stages y tiene una pinta de puta madre (y me tocara actualizar la macro :D)

2
Lecherito

Pues he migrado del repositorio donde lo tenia a jetbrains spaces, tenia ganas de probarlo y quiza me ayuda otra persona, asi que lo he puesto para que se puedan crear cuentas con mi dominio y le he creado un correo a un amigo para probar.

No tiene mala pinta y el tier gratis ofrecen bastantes cosas aunque no se como llegue a ser luego lo que he visto, me gusta.

Esto va de probar cosas, asi que se hace a lo grande xdd

Lecherito

Pues he migrado todo y bastante mas sencillo de lo que parecia. Me gusta mucho que su sistema de automatizacion de builds es un script de Kotlin por lo que puedes incluso programar ahi. No es una basura de yml como en todos los demas sitios basura.

job("Build Towers") {
    container("bevybuild:latest") {
    	shellScript {
    	    content = "cargo build"
        }
    }
}

Tan simple como eso. Ahora tengo que ver mas formas de personalizarlo pero la verdad es que me gusta bastante el ecosistema que han montado, no solo eso si no que tambien tienen los gestores de paquetes (contenedores incluidos) y todo bastante integrado. Les doy mis dieses por ahora.

Lo siguiente es mover los Plugins que tengo hacia la carpeta examples para poder tener mas de uno y poder ir testeando diferentes cosas. Despues de eso me pondre a averiguar como funciona lo del slow/stun y me gustaria tambien jugar con los scripts y los eventos dentro del juego para llamar a diferentes funciones cuando X cosa pasa.

Lecherito

Despues de hacer la masa de pizza para ma;ana me he puesto otro rato. Me esta gustando como lo tienen organizado los de jetbrains.

Tengo ya el ejemplo prepareado igual que lo tenia en el main antes (lo tenia configurado como binario), lo he cambiado a libreria simplemente moviendo el main al ejemplo y cambiandolo por un libs.rs. Ahora puedo hacer lo mismo que antes con cargo run --example simple.

He a;adido tambien que se ejecute la build al hacer una review y estoy viendo a ver como puedo bloquear el merge si esta rota.

1
Zoko

No dejes de postear! Me encantan los TD y esto es bastante chulo.

1 respuesta
Lecherito

#59 No tengo tanto tiempo como me gustaria para dedicarle, pero en ello estoy, poco a poco. Ahora que he hecho lo de los ejemplos me gustaria ver como puedo hacer lo del scripting. Para luego asi poder ir haciendo diferentes cosas dependiendo de lo que me apetezca hacer ese dia.

2

Usuarios habituales

  • desu
  • Ranthas
  • Lecherito
  • JuAn4k4
  • AikonCWD
  • Saiko9
  • Wei-Yu