Page 4 of 6

Re: Bubble Bobble (W.I.P.)

Posted: Mon Apr 30, 2018 6:56 pm
by nicksen782
Bubbles could be tiles and save on sprites. I would need to store more bubble frames though. However, I would save on the ram tiles/sprites.
All characters (other than the final boss) are 2x2 with 2 frames each. Often tiles between frames are duplicated (or I have edited them to be duplicated) so there is a little savings right there.

Flash usage is at 56518. Ram is at 3673 and I am hoping to be able to reduce the ram usage.

Yes, bubbles should flicker around. I am concerned about the flicker speed though. With the half on/off trick I can double the sprites. But any more than that and it looks terrible. In Bubble Bobble I literally had two sprite arrays (in addition to the kernel sprite array) and on alternating frames each array would get its chance to be drawn. This isn't so good for ram. I'm working on another game that handles this much more dynamically. Basically, all sprites are re-mapped and re-drawn each frame. All ACTIVE sprites. So, sprite order is not so important anymore and any INACTIVE actor slot can be used for whatever the next sprite is. I could simply keep track of how many I wrote to sprites[] and if all sprites were not written I could keep track and then draw them the next time. Of course handling rollover. I experimented quite a bit with this but I never felt I got it right. I could try again with this new way of doing things.

Using less sprites for the actors. Yes, this I considered. It could look ugly. I could try. May cut my flash usage in half.

For the fruit I was thinking of just switching the actor sprite to a fruit sprite but leaving it as a sprite in an actor slot. It could be complex to mix it around with background tiles since right now my sprite to tile collision detection is literally looking for anything other than tile 0 (the black tile.) However, if I brought the enemies in in small batches (I think the arcade version did this) then I could limit the total number on screen at any given time.

Oh, I did get music done too and a music test menu is available. Pressing select in any screen will bail out of that game mode. Check out the live version (which I just fixed today.) Music is streaming from SPIRAM. In fact, the whole game works from SPIRAM with an initial load from SD (using SD Simple at the moment.)

Re: Bubble Bobble (W.I.P.)

Posted: Mon Apr 30, 2018 7:48 pm
by Artcfox
nicksen782 wrote: Mon Apr 30, 2018 5:20 pm I wanted all actors to share the actor_ struct. However, I know bubbles would really be costly this way.
With Bugz, I have a "base struct" called ENTITY that contains all the shared variables between a PLAYER and the enemies, and the PLAYER struct "inherits" from the ENTITY struct.

Code: Select all

struct ENTITY;
typedef struct ENTITY ENTITY;

struct ENTITY {
  // shared variables
  ...
} __attribute__ ((packed));

struct PLAYER { ENTITY entity;
  BUTTON_INFO buttons;
} __attribute__ ((packed));
In my case the only additional thing the PLAYER struct has is a BUTTON_INFO struct, but you can extend it with anything you want. You can upcast a PLAYER into an ENTITY because the ENTITY struct is first in the PLAYER struct. You could put all the stuff that is shared into your base struct, and add the heavyweight variables only for the stuff that needs them in the inherited struct.

Re: Bubble Bobble (W.I.P.)

Posted: Mon Apr 30, 2018 8:41 pm
by nicksen782
What if I wanted an array of actors, such as 10?

When using a shared function I often just pass the index of the actor array of structs.

Re: Bubble Bobble (W.I.P.)

Posted: Mon Apr 30, 2018 9:34 pm
by Artcfox
nicksen782 wrote: Mon Apr 30, 2018 8:41 pm What if I wanted an array of actors, such as 10?

When using a shared function I often just pass the index of the actor array of structs.
You could use an array of pointers to the base struct, and you can dynamically assign function pointers (stored in the base struct) when you instantiate an actor, so each actor knows how to do its own processing on itself, just by calling:

Code: Select all

ENTITY *entity = actor_ptrs[i];
entity->doCollision(entity);
entity->doRender(entity);
as an example.

Re: Bubble Bobble (W.I.P.)

Posted: Mon Apr 30, 2018 10:33 pm
by CunningFellow
If you look at the T2K source code (The C parts not the ASM parts) you can see function pointers used quite heavily.

There are up to 64 sprites on screen at once. Their object data is all held in an array. The only "special" slot in the array is [zero] which is the claw/player.

How objects move, get drawn, interact with bullets, how many points you get and even what they do when first spawned are all handled by functions pointed to by the function pointers.

objects.c/objects.h is where to start looking.

Re: Bubble Bobble (W.I.P.)

Posted: Tue May 01, 2018 2:58 am
by Artcfox
CunningFellow wrote: Mon Apr 30, 2018 10:33 pm If you look at the T2K source code (The C parts not the ASM parts) you can see function pointers used quite heavily.

There are up to 64 sprites on screen at once. Their object data is all held in an array. The only "special" slot in the array is [zero] which is the claw/player.

How objects move, get drawn, interact with bullets, how many points you get and even what they do when first spawned are all handled by functions pointed to by the function pointers.

objects.c/objects.h is where to start looking.
Is this the source? Have you considered putting it up on your GitHub?

Re: Bubble Bobble (W.I.P.)

Posted: Fri Mar 22, 2019 5:35 pm
by nicksen782
Artcfox wrote: Mon Apr 30, 2018 9:34 pm
You could use an array of pointers to the base struct, and you can dynamically assign function pointers (stored in the base struct) when you instantiate an actor, so each actor knows how to do its own processing on itself, just by calling:

Code: Select all

ENTITY *entity = actor_ptrs[i];

entity->doCollision(entity);
entity->doRender(entity);

as an example.
I understand this concept. But, let me try to explain my understanding so as to be sure.

You create an instance of ENTITY called "entity" with the "*" and set that equal to actor_ptrs. What is the data type of actor_ptrs ? Is that an array of ENTITY? Why redeclare the type? In a for loop you can just do actor_ptrs.propertyname and work with it that way instead of using "->" on it.

When using a function pointer, how do you do it? Could you provide an example of the function pointer, how it is called, and how it is declared/defined?

Re: Bubble Bobble (W.I.P.)

Posted: Fri Mar 22, 2019 9:17 pm
by CunningFellow
I am guessing ArtcFox has examples of function pointers in his games, but I can't point you to them.

I can point you to the function pointers in my games.

Have a look in the source code for T2K in the file objects.c

There is an array of function pointer for all the objects on screen for their different actions.

I hope it is easy to follow - but if not I can answer any questions pretty quickly.

Re: Bubble Bobble (W.I.P.)

Posted: Mon Apr 01, 2019 9:44 pm
by Artcfox
In my example, the PLAYER type inherits from the ENTITY type, but stores extra stuff relating to the player's button state. If I want to access all the stuff that an entity normally has, I can cast it to an ENTITY, and then treat it as if it were an ENTITY, passing it to functions that expect an ENTITY* or setting parameters on it as if it were an ENTITY, rather than saying

Code: Select all

players[0].super.monsterhop = true;
for instance.

Code: Select all

#define PLAYERS 2
#define MONSTERS 6
PLAYER player[PLAYERS];
ENTITY monster[MONSTERS];

      // Get inputs/update the state of the monsters, and perform collision detection with each player
      for (uint8_t i = 0; i < MONSTERS; ++i) {
        uint8_t monsterPrevY = sprites[PLAYERS + i + 4].y; // cache the previous Y value to use for kill detection below
        monster[i].input(&monster[i]);
        monster[i].update(&monster[i]);
        monster[i].render(&monster[i]);

        // Collision detection (calculation assumes each sprite is WORLD_METER wide, and uses a shrunken hitbox for the monster)
        for (uint8_t p = 0; p < PLAYERS; ++p) {
          ENTITY* const e = (ENTITY*)(&player[p]);
          if (monster[i].interacts && !monster[i].dead && e->interacts && !e->dead &&
              overlap(sprites[p].x, sprites[p].y, TILE_WIDTH, TILE_HEIGHT,
                      sprites[PLAYERS + i + 4].x + 1,
                      sprites[PLAYERS + i + 4].y + 3,
                      TILE_WIDTH - 2, TILE_HEIGHT - 4)) {
            // If a player and a monster overlap, and the bottom pixel of the player's previous Y is above the top
            // of the monster's previous Y then the player kills the monster, otherwise the monster kills the player.
            if (((playerPrevY[p] + TILE_HEIGHT - 1) <= (monsterPrevY + 3)) && !monster[i].invincible) {
              killMonster(&monster[i]);
              BCD_addConstant(&levelScore[SCORE_DIGITS * p], SCORE_DIGITS, KILL_MONSTER_POINTS);
              if (e->update == player_update)
                e->monsterhop = true; // player should now do the monster hop, but only if gravity applies
            } else {
              killPlayer(e);
            }
          }
        }
}
Look at https://github.com/artcfox/bugz/blob/master/bugz.c and entity.c and entity.h to see how I "subclassed" it in C.

Re: Bubble Bobble (W.I.P.)

Posted: Tue Apr 09, 2019 2:31 pm
by nicksen782
This is very helpful! Thank you.