Several newb questions

What is a Uzebox? How can I get one? Check here!
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Several newb questions

Post by Artcfox »

I think that if we are going to include games in the official repo, those games need to work with the latest Uzebox kernel, we don't want multiple copies of different kernel versions in the official repo. It looks like the code for Pong is intended for a really old version of the Uzebox kernel, and removing the deps/ directory will allow the "make" to work, but not if you then run "make clean" followed by "make".

I tried pointing it to the latest kernel, but that fails too, because according to the Makefile, Pong uses Video Mode 3, but it's calling API functions like DrawMap which are meant for Video Mode 1. I changed all of those calls, and made all of the arrays the correct type, but when I played the game the sounds were glitchy and the gameplay was different from the original. I put everything back, and then left it pointing at the original kernel it includes, and added const to the PROGMEM array it complains about, but some of the sounds and gameplay are different from the released Pong.hex and Pong.uze files, so now I'm not even sure that that source code matches up with the released game.

I think that the Pong game does show how to use sprites though, but you can also check the API documentation for examples to get an idea of how to use the different functions available. For isntance: http://uzebox.org/wiki/Function_MapSprite

Here is a link to the top level API docs: http://uzebox.org/wiki/API_Functions

Oh, and The Uzebox Mode 3 with Scrolling Guide I linked you to isn't a video, it's a PDF that has diagrams and example source code, and it links to follow-along mini tutorials on github that you can clone and start modifying.
User avatar
danboid
Posts: 1937
Joined: Sun Jun 14, 2020 12:14 am

Re: Several newb questions

Post by danboid »

You're totally right that all the included examples should build on the current kernel. I had noticed that Pong wasn't building after I did a `make clean` but that's where I stopped yesterday, presuming it would just be an issue with the makefile. I was going to look into cleaning Pong up and integrating it into the UB repo build today although I would've only encountered all those issues you described.

How is the gameplay different in your port of pong to the newer kernel? We can fix the sounds easy enough and as long as:

* The ball bounces off the paddles and walls
* The correct players score increments when the ball goes into the goal

If those functions still work then surely we have a valid pong remake? It need not be 100% faithful to either the arcade nor the original UB remake. The quality of the game actually doesn't matter, it just has to build and run with the current kernel. The UB repo would greatly benefit from having an example like this as a middle ground example between the Hello world and Arkanoid, Loderunner and Super Mario examples etc.

This brings me onto a question I'd not yet asked which has been highlighted excellently by this pong issue. It seems Uzebox is the "Linux of game consoles" but unfortunately along with the open nature UB has also replicated Linux's crap backwards compatibility, at least on the source code / API level, as evidenced by my lack of success at getting (old) UB games to build.

So whose fault is this, mainly? GCC's or the UB devs or both? Is there any hope that things will improve going forward so that code I write against the current kernel may still build against the UB kernel in 2025? Has the kernel / API stabilised? Are more big changes in the pipeline that would cause more breakage?

I shall have a look at this PDF shortly Matt, thanks!
User avatar
D3thAdd3r
Posts: 3221
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Several newb questions

Post by D3thAdd3r »

I think the kernel has been stable for quite some time, and probably some individual games have done things in a nonstandard way on occasion. I for one am guilty of having source code relying on hacked kernels because I needed extra/different functionality. Over the course of the years, the kernel pretty well added all the things needed even for advanced usage. I would call it bit rot, but not too bad considering the loose nature of it all. It would be nice to fix all games.

Pong would be a good game to have in the repo for sure. I believe the first video game I ever really completed was a Pong clone, with huge cheater AI :lol: It is fairly easy to wrap your head around the entirety of the game, versus more advanced ones to start with.
User avatar
danboid
Posts: 1937
Joined: Sun Jun 14, 2020 12:14 am

Re: Several newb questions

Post by danboid »

Artcfox:

Could you upload your updated pong code please?

I've had a busy week but I'll get some time to play with UB stuff this weekend.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Several newb questions

Post by Artcfox »

Attached is a quick changing of some things in the Pong code to try to get them to work with the correct APIs. Extract it in your

Code: Select all

uzebox/MY_GAMES/
directory, so the path to its Makefile is

Code: Select all

uzebox/MY_GAMES/Pong-mode3/default/Makefile
Under the "See Also" section of this page:
http://uzebox.org/wiki/Video_Modes#Mode_3
is where I learned how to use sprites on the Uzebox.

Good luck!
Attachments
Pong-mode3.zip
(273.84 KiB) Downloaded 476 times
User avatar
danboid
Posts: 1937
Joined: Sun Jun 14, 2020 12:14 am

Re: Several newb questions

Post by danboid »

Thanks for sharing your updated pong port Artcfox!

I can confirm this code still builds fine after a `make clean` with the current kernel for me, and it's less than 200 lines so this seems like the perfect example to me but as you say a couple of the sounds effects need tweaking. You should let me fix that, then maybe we can push it into the repo? I'm hoping to read your PDF later tonight.
User avatar
danboid
Posts: 1937
Joined: Sun Jun 14, 2020 12:14 am

Re: Several newb questions

Post by danboid »

Hi ArtcFox!

I have fixed the two dodgy sound FX in pong by using the following for patches.inc:

Code: Select all

const char patch00[] PROGMEM ={	
	0,PC_ENV_VOL,0xff, 
	0,PC_PITCH, 50,
	3,PC_ENV_VOL,0x00, 
	0,PATCH_END
};

const char patch01[] PROGMEM ={
   	0,PC_ENV_VOL,0xff, 
	0,PC_NOTE_DOWN,20,
	2,PC_ENV_VOL,0x00,
	0,PATCH_END 
};

const char patch02[] PROGMEM ={	
	0,PC_ENV_VOL,0xff, 
	0,PC_PITCH, 50,
	13,PC_ENV_VOL,0x00, 
	0,PATCH_END
};

const struct PatchStruct PongPatch[] PROGMEM = {
   {0,NULL,patch00,0,0},
   {0,NULL,patch01,0,0},
   {0,NULL,patch02,0,0},
};
The pitch values were invalid (-50 when it has to be between 0 and 126) nor did it like PC_NOTE_UP.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Several newb questions

Post by Artcfox »

Nice find!
User avatar
danboid
Posts: 1937
Joined: Sun Jun 14, 2020 12:14 am

Re: Several newb questions

Post by danboid »

Here's my first UB coding experiment which moves a sprite around the screen with the joystick.

This is the sort of example demo I was hoping for in the repo, although done proper. I'd like to know what an experienced C/UB programmer would do differently as no doubt this code can be improved in several ways

Code: Select all

#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include <stdlib.h>
#include <string.h>
#include <avr/pgmspace.h>
#include <uzebox.h>

#include "data/tileset.inc"

int btnPrev = 0;     // Previous button
int btnHeld = 0;     // buttons that are held right now
int btnPressed = 0;  // buttons that were pressed this frame
int btnReleased = 0; // buttons that were released this frame 

struct Playerstruct{
	unsigned char x;
	unsigned char y;
};

struct Playerstruct Player;

int main()
{
	ClearVram();
	SetTileTable(tileset);
	Player.x = 100;
	Player.y = 100;
	MoveSprite(3,Player.x,Player.y,1,1);
	
	while(1){
        WaitVsync(1);
    
        btnHeld = ReadJoypad(0);
        btnPressed = btnHeld & (btnHeld ^ btnPrev);
        btnReleased = btnPrev & (btnHeld ^ btnPrev);
    
        if(btnHeld & BTN_RIGHT){
            MoveSprite(3,Player.x+=3,Player.y,1,1);
        }
        if(btnHeld & BTN_LEFT){
            MoveSprite(3,Player.x-=3,Player.y,1,1);
        }
        if(btnHeld & BTN_UP){
            MoveSprite(3,Player.x,Player.y-=3,1,1);
        }
        if(btnHeld & BTN_DOWN){
            MoveSprite(3,Player.x,Player.y+=3,1,1);
        }
        btnPrev = btnHeld;
    }
}
 
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Several newb questions

Post by Artcfox »

Nice!

I don't see the call to:

Code: Select all

SetSpritesTileBank(0, mysprites);
nor do I see the call to:

Code: Select all

MapSprite2
which you would use when calling

Code: Select all

MoveSprite
Have a read through this page: http://uzebox.org/wiki/Video_Mode_3 specifically the "Sprites structure" section and the following "Using sprites" section.

It looks like you just want to move a single sprite, so you can just set that sprite's x and y coordinate directly in the sprite structure:

Code: Select all

sprites[3].x = Player.x;
sprites[3].y = Player.y;
The MapSprite2 and MoveSprite functions are helper functions for when you have a megasprite (an object made up of multiple smaller sprites arranged in a grid pattern). It doesn't look like you have that, since the size is 1x1, so you can get away with just setting the sprites structure manually.

I typically use a structure to manage all of the button stuff:

Code: Select all

typedef struct {
  uint16_t held;
  uint16_t prev;
  uint16_t pressed;
  uint16_t released;
} __attribute__ ((packed)) BUTTON_INFO;
and then in the beginning of my program I do:

Code: Select all

  BUTTON_INFO buttons;
  memset(&buttons, 0, sizeof(BUTTON_INFO));
And when I want to read them and act:

Code: Select all

      buttons.prev = buttons.held;
      buttons.held = ReadJoypad(0);
      buttons.pressed = buttons.held & (buttons.held ^ buttons.prev);
      buttons.released = buttons.prev & (buttons.held ^ buttons.prev);

      if (buttons.pressed & BTN_DOWN) {
...
      }
      if (buttons.pressed & BTN_UP) {
...
      }
Another thing that I always always do is use types that have the size in them: uint8_t, int8_t, uint16_t, int16_t, etc... That way you always know exactly what size you're getting, and you can prototype or unit test the same code on your dev machine, and get the same results.

You'll find things like this:

Code: Select all

      uint8_t hmod = ((tx - BOARD_START_X) % BOARD_H_SPACING);
      if (hmod < TOKEN_WIDTH)
are not only more readable, but will compile down to more efficient machine code than if you had written it as:

Code: Select all

      if (((tx - BOARD_START_X) % BOARD_H_SPACING) < TOKEN_WIDTH)
because in the first, the compiler knows you are only interested in the first 8 bits, and so it only needs to compare those. In the second, the result of that math is stored internally as a 16 bit integer, and so the comparison is performed on all 16 bits.
Post Reply