NESFab Tutorial Part 4

Making a game. Adding enemies.

In this NESFab project we add enemies to our platformer. Here you can download the example.

The enemies will move horizontally from one initial position to the final one. All information we need about them is stored in a array of structs called enemies.

 
    // Snake enemies
    struct Enemy
        UU map_x
        UU map_y
        UU initial_x
        UU final_x
        S x_speed
        U status // 0-alive 1-killed and falling 2-killed and off the screen
    //
    ct U level1_num_enemies=8
    ct UU enemy_char_width=16
    ct UU enemy_char_height=16
    ...
    //enemies
    Enemy[level1_num_enemies] enemies

Enemy movement. How it works.

We add our enemy displaying code to the update_sprites function. It handles sprites positions to be able to display them flipped.

 
    ...
    // draw enemies
    for U ei=0; ei<level1_num_enemies; ei+=1
        if ( (enemies[ei].status < 2) && (enemies[ei].map_x > my_x_scroll) && ((enemies[ei].map_x+enemy_char_width) < (my_x_scroll+256)) )
            UU tex=enemies[ei].map_x-my_x_scroll
            U ex=tex.a
            U ey=enemies[ei].map_y.a
            U attrs=2 // https://www.nesdev.org/wiki/PPU_OAM
            U which_sprite=(enemies[ei].map_x.a>>4)&1
            //
            if (enemies[ei].x_speed < 0) // going left
                attrs|=%01000000 // flip h
                which_sprite+=2
            //
            if (enemies[ei].status == 1) //falling enemy
                attrs|=%11000000 // flip h&v
                which_sprite=4 // flip h&v
            //
            set_oam_x(i, ex)     // x-position
            set_oam_y(i, ey) // y-position
            set_oam_p(i, e_chr_sprt_table[(which_sprite<<2)])    // tile
            set_oam_a(i, attrs)      // options
            i += 4
            set_oam_x(i, ex+8)     // x-position
            set_oam_y(i, ey) // y-position
            set_oam_p(i, e_chr_sprt_table[(which_sprite<<2)+1])    // tile
            set_oam_a(i, attrs)      // options
            i += 4
            set_oam_x(i, ex)     // x-position
            set_oam_y(i, ey+8) // y-position
            set_oam_p(i, e_chr_sprt_table[(which_sprite<<2)+2])    // tile
            set_oam_a(i, attrs)      // options
            i += 4
            set_oam_x(i, ex+8)     // x-position
            set_oam_y(i, ey+8) // y-position
            set_oam_p(i, e_chr_sprt_table[(which_sprite<2)+3])    // tile
            set_oam_a(i, attrs)      // options
            i += 4
    ...

And create a new function executed in the main loop called move_enemies(). This function also checks if an enemy killed the player or the player killed crushed an enemy.

  
    ...
    // move the enemies through the level
    fn move_enemies()
        // move enemies
        for U i=0; i= enemies[i].final_x)
                    enemies[i].x_speed=-1
    
        // check if player crushed an enemy, killing it
        for U i=0; i<level1_num_enemies; i+=1
            if (did_crush_enemy(i))
                    enemies[i].status=1
    
        // check if an enemy killed the player
        for U i=0; i<level1_num_enemies; i+=1
            if (enemies[i].status == 0)
                if ( intersect(player_map_x, player_y, player_map_x+player_char_width, player_y+player_char_height, 
                    enemies[i].map_x, enemies[i].map_y, enemies[i].map_x+enemy_char_width, enemies[i].map_y+enemy_char_height) )
                    goto mode play_level1()  // Initialize game by entering the play level mode
                    : preserves
    
        // check if a dead enemy has to fall until it dissapears
        for U i=0; i<level1_num_enemies; i+=1
            if (enemies[i].status == 1)
                enemies[i].map_y+=1
                if (enemies[i].map_y >= 240)
                    enemies[i].status=2 // dead and no more movements    
    ...
player and enemies player and enemies