Porting Bitsy engine to Pokitto

Doesn’t run :frowning:

Sorry! Doesn’t run at all? Are you using the version in Edit2 or was it you who downloaded the first one?

Note to self: Stop posting bins at 2AM.

Here’s what’s happening so far (but I was too drowsy to explain when I posted):

There’s 3 problems to solve:

  • parsing game data
  • rendering & movement
  • dialog & scripts

To solve the first problem in the most straight-forward way, I wrote a utility in javascript that uses the Bitsy parser to translate game files to C++. Having to convert games to run them is less convenient, but this is the fastest/simplest way to get them running at all and we can reimplement the parser in C++ in the future if we want to. At least, since it’s a command line app, we could batch-convert a few hundred games in one go.

For now, a translated project looks like this: [EDIT: A lot changed, see next post.]


/* 
FLAGS: 
ROOM_FORMAT=1
*/


const uint16_t anims_pok[8] = {
0,1,1,1,2,1,3,1
};

const unsigned char images_pok[4][8] = {
	/*TIL_a 0*/{255,129,129,153,153,129,129,255},
	/*SPR_A 0*/{32,16,250,63,63,248,16,32},
	/*SPR_a 0*/{0,60,248,124,96,224,16,12},
	/*ITM_0 0*/{0,16,56,72,72,56,0,0}
};


const uint32_t rooms[1][256] = {
	{0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}
};
    
uint16_t palettes[1][3] = {
	{0x298, 0x7cff, 0xffff}
};


const char *title = "Write your game's title here";
const char *dialog[] = {
 "I'm a cat",
 "You found a nice warm cup of tea"
};


struct Sprite {
    uint16_t animId, dlgId;
    uint8_t roomId;
    int8_t x, y;
} sprites[2] = {
	{ 2, 0, 1, 4, 4 },
	{ 3, 1, 1, 8, 12 }
	},
  &player = sprites[0];

struct Variable {
  std::string s;     // 0
  union{
    int32_t  i;      // 1
    float    f;      // 2
  };
  uint32_t type;
  Variable( int32_t _i ) : i(_i), type(1){}
  Variable( float _f ) : f(_f), type(2){}
  Variable( const std::string &_s ) : s(_s), type(0){}
} vars[] = {
/*a*/ {int32_t(42)}
};

I haven’t given much thought to Variable, it’s more of a TO-DO. Rooms are uint32_t but I’ll switch to uint16_t.
Still have to convert items.
Should I be using std::string on a Pokitto?

Now that we have most of the data loaded, we can render it:

#define DISABLEAVRMIN
#include "Pokitto.h"

Pokitto::Core PC;

#include <string>
#include "bitsy.h" // game data

void (*updateState)();
bool stateInitialized = false;
uint32_t room[256];

inline void write_command_16(uint16_t data);
inline void write_data_16(uint16_t data);

void draw(int32_t x, int32_t y, int32_t imgId, uint32_t bg, uint32_t fg ){

  Pokitto::setWindow( y<<3, (x<<3)+46, ((y+1)<<3)-1, ((x+1)<<3)-1+46 );
  write_command_16(0x22);

  auto frameId = anims_pok[(imgId-1)<<1];
  auto frameCount = anims_pok[((imgId-1)<<1)+1];
  
  if( frameCount>1 )
    frameId += Pokitto::Core::frameCount%frameCount;

  auto img = images_pok[ frameId ];

  for( y=0; y<8; ++y ){
    uint32_t m = *img++;
    for( x=0; x<8; ++x, m>>=1 ){
      write_data_16( m&1 ? fg : bg );
    }
  }
  
  Pokitto::setWindow(0, 0, 175, 219);
  
}

void stateMap(){
  auto pal = palettes[player.roomId-1];

  if( !stateInitialized ){
    stateInitialized = true;
    memcpy( room, rooms[player.roomId-1], sizeof room );

    for( int i=0; i<sizeof(sprites)/sizeof(Sprite); ++i ){
      auto &sprite = sprites[i];
      if(
		 sprite.roomId != player.roomId ||
		 sprite.x<0 ||
		 sprite.y<0 ||
		 sprite.x>=16 ||
		 sprite.y>=16
	 )
		continue;
      room[sprite.y*16+sprite.x] |= (i+1)<<16;	
    }

    Pokitto::lcdFillSurface( pal[0] );
    
    for( int y=0; y<16; y++ ){
      for( int x=0; x<16; x++ ){
		uint32_t mixed = room[y*16+x];
		uint32_t id = mixed & 0xFF;
		uint32_t spriteId = (mixed>>16) & 0xFF;
		uint32_t bg = pal[0], fg = pal[1];
		if( spriteId ){
		  id = sprites[spriteId-1].animId;
		  int isPlayer = &sprites[spriteId-1] == &player;
		  fg = pal[1+isPlayer];
		}else if( !id ) continue;
	
		draw( x, y, id, bg, fg );
      }
    }
  
  }

}

void stateDialog(){
  // TO-DO: Say something
}

int main () {
  PC.begin();

  updateState = stateMap;
    
  while( PC.isRunning() ){
    if( PC.update() ){
      updateState();
    }
  }
    
}

With that I get the level and sprites on screen. I think animations work, I’d just have to move the rendering out of the if. I’m using PROJ_TILEDMODE 1 as I don’t need a framebuffer so far.

That leaves movement, dialog and scripting.
For movement, it’s simply a matter of changing the player’s X/Y, checking collision, and setting stateInitialized to false so the room gets updated.
Dialog and scripting is going to be some work.

I centered the map horizontally and plan to put text in the space below it. That’s about all the thought I’ve given to text so far.

2 Likes

Can someone please tell me if this bin works on hardware? dream.bin (48.9 KB)

It features:

  • No breakpoints, this time! :stuck_out_tongue:
  • Movement
  • Collision detection with walls, items, sprites.
  • Animation
  • All sorts of strange animals!
  • 18 Rooms

Animation might be too fast. No dialog/scripting implemented yet (the text you’ll see is drawn as tiles).

Source for the converter can be found here.

1 Like

White screen of death. Should I place anything else in the sd card?

1 Like

I will also check it once I get home, but it will be another 4-5h.

1 Like

No, it doesn’t need anything else on the SD.

I think there’s something wrong with the way I’m compiling, so I’ll just upload the source to the engine when I get home.
Thanks for trying it, though!

If anyone wants to see it before then, just drag-and-drop the BIN file into my emulator.

2 Likes

I got white screen also. Pokitto goes to flash mode, and CRP DISABLD window occurs on PC. Could it be that the checksum is incorrect and Pokitto cannot flash the binary?

That is my suspicion, as it works in the emulator which doesn’t care for the checksum.

The game looks great in the emulator :slight_smile:

1 Like

I hope it looks good on hardware too! :stuck_out_tongue:
Now with 100% more checksum: dream.bin (48.9 KB)
Did I get it right, this time?

I uploaded all the code here. Some day I’ll organize it better.

1 Like

It’s working!

Few things:
I think having the 128 pixels stretch full height would be good. The font bitsy uses is 6x6 or so. Even at that resolution wouldn’t be unreadable and I believe tiled mode can call fonts on top of tiles. We can do what bitsy does, just draw a black box depending where the player sprite is (box is on top if player is on the bottom for example) and print text to it.

The animation and walking is bit too fast. I believe animations run every 400ms and player input fires every 200ms.

Nevertheless - it is super exciting to see it working already! Thank you so much @FManga!

EDIT: I want to post the progress on twitter, @FManga do you have a twitter handle so I can credit you?

2 Likes

It looks good, seems to run a bit fast but then I have no idea what speed it’s supposed to run at.

I completely disagree about stretching the screen. I personally can’t stand stretching images unless the pixel size can be kept 100% uniform. Having pixels of different sizes on the screen can be very distracting, especially with moving sprites as they will stretch and warp when moved. A much better option would be to centre the screen and possibly have some sort of border to indicate the edges.

3 Likes

I also do not like non-uniformly zoomed screens, i.e. where some original pixels are 1 pixel, some are 2 or 4 pixels. Even if Pokitto would have CPU power to make a filtered scale-up the result would be messy and low-contrast.

I do not think we need borders as in the web page they are without borders too.

2 Likes

(Kind of was disappointed by this when playing DS game on the 3DS for that exact reason! Beautiful pixels being tortured aaaah)

1 Like

Hold start when you launch the game and it gets rid of the stretching and does padding instead.
It’s still not as good as playing on an actual DS, but it’s more bearable.


I fourth the “centre don’t stretch” campaign.

1 Like

Yeah, that’s what I did. Still better to play on the actual DS (I’ve kept mine!)

1 Like

Awesome! Thanks for posting a photo!

Seeing all that empty space around the game does feel like a waste, but, unfortunately, stretching the game so it’s full height is going to look terrible.

I’m thinking: press C to toggle between centered 1:1 and panning 1:2.

3 Likes

I am not sure if I chose my words wrong, or there is an underlying issue I am not aware of.

What I meant is to make the screen render 128px tall (and 128+x wide to match the screen). I assumed it was doable because of all the other resolution modes we have. I didn’t mean to make the pixels not square. Does that make sense?
I guess better word was to set the screen to 128 tall, not stretch.

Is there a reason we would not be able to make the screen render of 160x128 (this is based on 110x88 ratio) perhaps?

The screen has a hardware resolution of 220x176, the other modes are just the resolution of the screen buffer and must be a multiple of the hardware’s. 110x88 gets scaled by 2.
For 128 to fit in 176, we’d have to scale by 1.375, which can’t be done evenly.

1 Like

You just chose your wording badly.
If it’s being rendered at the intended resolution then it isn’t being stretched.

‘Stretching’ usually implies that the sprites will end up looking deformed because they’re made larger in one dimension than another.
Like one of the two on the right here:

If the original is 128x128 then the best we can do is to render at the same resolution in the centre of the screen and pad the surrounding area with either a decorative border or simply a block of one colour.


Maybe we could use the extra space on the horizontal sides (46 pixels either side) to provide additional menu options?

1 Like