Weber's Rants (tutorials)

Topics related to the API, programming discussions & questions, coding tips, bugs, etc. should go here.
User avatar
Artcfox
Posts: 1382
Joined: Thu Jun 04, 2015 5:35 pm
Contact:

Re: Weber's Rants (tutorials)

Post by Artcfox »

Ooooh, that sounds very awesome!

I think there is still a lot of still untapped potential that we are discovering here.
User avatar
danboid
Posts: 2074
Joined: Sun Jun 14, 2020 12:14 am

Re: Weber's Rants (tutorials)

Post by danboid »

Sounds fantastic DA! Great to hear Columns is getting an update as thats one of my fave UB games.

T2K proved the UB can do Amiga quality (albeit mono) music but it sounds like you think you can outdo that it seems?

You forgot to answer my question about the mult-tap. Does the current kernel support 4/5 joypads and do you have a multitap to test it?
User avatar
D3thAdd3r
Posts: 3289
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Weber's Rants (tutorials)

Post by D3thAdd3r »

Have you ever thought to yourself: "*sigh* if only there was just one more...just one more way to play Sokoban out there.." ?

Or perhaps in deep thought, you considered: "*Eh* Sokoban is ok sometimes, I just wish I could play it inside a VM, within an experimental GUI, on a niche retro 8 bit console, in an open source cycle accurate emulator, running from the comfort of my 64bit PC...can't have everything in this life.."

No, you never thought anything like that ever, that wouldn't even make sense. But neither does this:
sokoland-overview-smaller.jpg
sokoland-overview-smaller.jpg (527.42 KiB) Viewed 2681 times
Haha, this was the most amusing game development I've had in a long time. So much novelty, and the restrictions keep me away from elaborate design decisions, feature creep, etc. I'll write a bit up it and release it soon, but it's basically Sokoban World reimagined as a GUI game(Sokoban Land)! It has 10 levels, basic features, pretty lean compression and code IMO, and uses every last byte of the 3K limit! It is quite playable too, though this demonstrates some feature like being able to capture/lock the joypad to a window(break free with select?) would be huge. Still the button based joypad is passable for this one, at least to toy around.

Anyway I'll fix the remaining bug, test to make sure all the levels actually work, and release it.
User avatar
D3thAdd3r
Posts: 3289
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Weber's Rants (tutorials)

Post by D3thAdd3r »

danboid wrote: Sun Feb 19, 2023 9:29 am Sounds fantastic DA! Great to hear Columns is getting an update as thats one of my fave UB games.

T2K proved the UB can do Amiga quality (albeit mono) music but it sounds like you think you can outdo that it seems?

You forgot to answer my question about the mult-tap. Does the current kernel support 4/5 joypads and do you have a multitap to test it?
1) Thank you!

2) Well not "outdoing" the MOD format, since that can run on stock hardware and it's a sweet spot on space efficiency and no buffers. Really apples and oranges. However even the PCM will be mono as that is how the hardware is built.

From a waveform perspective 15.7Khz can be saturated non-stop from the SD card(the maximum Uzebox sampling rate). Therefore "infinite" PCM waveforms could "emulate"(or just record and playback) any possible sound any other Uzebox method could generate. It is more than that though, as to even approach it would requires pretty radical MOD(or FM?) techniques there isn't flash for, high skill barriers, and still wont hit PCM IMO. PCM is basically brute force destruction, just throw arbitrary waveforms at ~40% CD quality until it's done.

3) I do have some multitaps somewhere, but there is an unfortunate issue I ran into. Basically the SNES multitaps(all variations) require an extra line on the controller ports with Uzebox does not have. Without this line, there would need to be some signal to activate additional hardware, to talk to the taps on this extra line. I did extensively research this(years ago) and from memory there wasn't any SNES tap variation which could dodge this even with light modification. I had considered the NES tap actually which you can do without the extra lines...but then converting to NES and all this...easier just to make an official Uzebox tap using an ATtiny or similar supporting SNES. Otherwise Uzenet is probably realistic for 4 player games.
User avatar
danboid
Posts: 2074
Joined: Sun Jun 14, 2020 12:14 am

Re: Weber's Rants (tutorials)

Post by danboid »

I can't believe how fast you knocked up two games for the worlds most niche OS/windowing system! Its been less than 48 hours since UzeboxUI's release, or at least less than 2 days since you noticed it. Very impressive!

Do you work as a pro games dev DA? If so, whats platforms do you code for professionally and whats your fave platform to write for, paid or unpaid?

That's a shame about the SNES multitaps. It would be good to see a Uzebox multitap.
User avatar
D3thAdd3r
Posts: 3289
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Weber's Rants (tutorials)

Post by D3thAdd3r »

Never made a dime programming games, but it offers a no pressure concentrated relaxation doing it. Uzebox is my favorite because it's pretty much the only system I have actually completed games on, it's an interesting performance/limitations point along with the community. If it wasn't Uzebox, it would be Sega 32X+CD or Saturn.

The code is pretty simple, though it took some getting used to the compiler(it's C like, but it definitely is not C and there are several caveats I had to brute force deduce). Here is an example of an almost complete game. Nothing too amazing, but I think it at least explores the boundaries of what the full 3K looks like:

Code: Select all

array8u jump[3];	// empty space so a jump instruction can be inserted by the build script
array8u size[1];	// empty space so the number of sectors can be inserted by the build script

$include "../libUI.inc"
$include "levels.inc"

global handle;
global targetsLeft;
array8u grid[225];
global px;
global py;
global levelCurrent = 0;
global numLevels = 10;
array8u hbuf[12];

function buildJoypad(){
        createButton(10,12,1,1,handle,1,"^",1,1);
        createButton(10,14,1,1,handle,2,"V",1,1);
        createButton(9,13,1,1,handle,3,"<",1,1);
        createButton(11,13,1,1,handle,4,">",1,1);

        createButton(0,12,7,1,handle,9,"[Retry]",7,1);
	createButton(0,13,6,1,handle,10,"[Skip]",6,1);
	createButton(0,14,6,1,handle,11,"[Quit]",6,1);
}

function loadLevel(){
	local g,i,j,l,t,m;
	l = (levelCurrent*72);
	targetsLeft = 0;
	clearWindow(handle,0);//show that we're working...
	for(i=0; i<12; i++){
		for(j=0; j<12; j++){
			if(j&1)
				t = m&0x0f; //use lo nybble
			else{
				m = levelData[l++];
				t = m>>4; //use hi nybble
			}
			if(t == 0)//floor
				setWindowTile(j,i,handle,36);
			else if(t == 1)//wall
				setWindowTile(j,i,handle,15);
			else if(t == 2)//block
				setWindowTile(j,i,handle,5);
			else if(t == 3){//target
				setWindowTile(j,i,handle,6);
				targetsLeft++;
			}else if(t == 4)//block on target
				setWindowTile(j,i,handle,40);
			else if(t == 5){//player
				setWindowTile(j,i,handle,83);
				px = j;
				py = i;
			}else if(t == 6){//player on target
				setWindowTile(j,i,handle,83);
				px = j;
				py = i;
			}else//BG
				setWindowTile(j,i,handle,22);
			grid[g++] = t;
		}
	}
	buildJoypad();
}

function logic(){
	local b,d,f,g,t;
	f = 0;
	b = 0;
	g = (py*12)+px;

	//check the button keypad
	if(checkButton(handle,9)){//player pressed restart?
//		playPatch(21,200);
		loadLevel();
		return;
	}
	if(checkButton(handle,10)){//player pressed skip?
//		playPatch(21,200);
		if(levelCurrent == numLevels-1)
			levelCurrent = 0;//wrap
		else
			levelCurrent++;
		loadLevel();
		return;
	}
	if(checkButton(handle,11)){//player pressed quit?
		return 2;
	}

	if(checkButton(handle,1)){// || checkButton(handle,5)){ //up
		t = grid[g-12];
		d = 1;
		if(t == 0 || t == 3){goto CAN_MOVE;}//floor or target?
		if(t == 2 || t == 4){//a block(or one on a target)?
			b = 1;
			f = grid[g-24];//open past that(floor or target)?
			if(f == 0 || f == 3){goto CAN_MOVE;}
		}
		return;
	}else if(checkButton(handle,2)){// || checkButton(handle,6)){ //down
		t = grid[g+12];
		d = 2;
		if(t == 0 || t == 3){goto CAN_MOVE;}//floor or target?
		if(t == 2 || t == 4){//a block(or one on a target)?
			b = 1;
			f = grid[g+24];//open past that(floor or target)?
			if(f == 0 || f == 3){goto CAN_MOVE;}
		}
		return;	
	}else if(checkButton(handle,3)){// || checkButton(handle,7)){ //left
		t = grid[g-1];
		d = 3;
		if(t == 0 || t == 3){goto CAN_MOVE;}//floor or target?
		if(t == 2 || t == 4){//a block(or one on a target)?
			b = 1;
			f = grid[g-2];//open past that(floor or target)?
			if(f == 0 || f == 3){goto CAN_MOVE;}
		}
		return;
	}else if(checkButton(handle,4)){// || checkButton(handle,8)){ //right
		t = grid[g+1];
		d = 4;
		if(t == 0 || t == 3){goto CAN_MOVE;}//floor or target?
		if(t == 2 || t == 4){//a block(or one on a target)?
			b = 1;
			f = grid[g+2];//open past that?
			if(f == 0 || f == 3){goto CAN_MOVE;}
		}
		return;
	}

	return;
CAN_MOVE:
	if(f == 0)//not pushing block, or no target past the block(normal)
		playPatch(5,120);
	else//block overlapping target
		playPatch(10,200);

	if(grid[g] < 6){//not player on target
		grid[g] = 0;//floor
		setWindowTile(px,py,handle,36);
	}else{//player on target
		grid[g] = 3;//target
		targetsLeft++;
		setWindowTile(px,py,handle,6);
	}

	if(d == 1){//up
		if(b == 1){//player needs to push a block?
			if(f == 0){//next space is floor?
				grid[g-24] = 2;//mark block
				setWindowTile(px,py-2,handle,5);			
			}else{//next space is a target?
				grid[g-24] = 4;//mark block on target
				setWindowTile(px,py-2,handle,40);
				targetsLeft--;
			}
		}
		py--;
	}else if(d == 2){//down
		if(b){//player needs to push a block?
			if(f == 0){//next space is floor?
				grid[g+24] = 2;//mark block
				setWindowTile(px,py+2,handle,5);			
			}else{//next space is a target?
				grid[g+24] = 4;//mark block on target
				setWindowTile(px,py+2,handle,40);
				targetsLeft--;
			}
		}
		py++;
	}else if(d == 3){//left
		if(b){//player needs to push a block?
			if(f == 0){//next space is floor?
				grid[g-2] = 2;//mark block
				setWindowTile(px-2,py,handle,5);			
			}else{//next space is a target?
				grid[g-2] = 4;//mark block on target
				setWindowTile(px-2,py,handle,40);
				targetsLeft--;
			}
		}
		px--;
	}else{ //if(d == 4){//right
		if(b){//player needs to push a block?
			if(f == 0){//next space is floor?
				grid[g+2] = 2;//mark block
				setWindowTile(px+2,py,handle,5);			
			}else{//next space is a target?
				grid[g+2] = 4;//mark block on target
				setWindowTile(px+2,py,handle,40);
				targetsLeft--;
			}
		}
		px++;
	}
	setWindowTile(px,py,handle,83);
	if(!targetsLeft){//victory?
		playPatch(20,200);
		return 1;
	}
	//TODO add buttons 5-8 around the player for another movement option
	return;
}

function titleScreen(){
	clearWindow(handle,0);
	printWindow(2,3,handle,"SoKoBaN",7,1);
	printWindow(4,4,handle,"Land",4,1);
	printWindow(2,7,handle,"Lvl:",4,1);
	createButton(7,7,1,1,handle,1,"<",1,1);
	printWindowInt(9,7,handle,levelCurrent+1,1,0);
	createButton(10,7,1,1,handle,2,">",1,1);
	createButton(3,9,8,1,handle,3,"[Start]",7,1);

	while(1){
		if(checkButton(handle,3))
			break;
		if(levelCurrent && checkButton(handle,1)){
			levelCurrent--;
			setWindowTile(8,7,handle,0);//blank tens
			printWindowInt(9,7,handle,levelCurrent+1,1,0);
		}else if(levelCurrent < numLevels-1 && checkButton(handle,2)){
			levelCurrent++;
			printWindowInt(9,7,handle,levelCurrent+1,1,0);
		}
	}
	playPatch(24,200);
}

function winGame(){
	local i,j;
	clearWindow(handle,3);
	for(i=0; i<4; i++){
		playPatch(handle,22+i);
		if(i == 0)
			printWindow(2,3,handle,"Victory!",8,1);
		else if(i == 1)
			printWindow(1,5,handle,"Well Done!",10,1);
		else if(i == 2){
			printWindow(1,7,handle,"Thanks For",10,1);
			printWindow(2,8,handle,"Playing!",8,1);
		}
		for(j=0; j<100; j++)
			j++;
	}
	createButton(6,10,5,1,handle,1,"[WIN]",5,1);
	while(!checkButton(handle,1)){}
}

function main(){
	local i,t;
	createWindow(4,3,12,15,"SokoLand",8);
	handle = $uf3();
	playPatch(21,200);
	levelCurrent = 0;
GTOP:
	titleScreen();
	loadLevel();
GLOOP:
	t = logic();
	if(t == 1){//player won the level?
		levelCurrent++;	
		if(levelCurrent == numLevels){//win game?
			winGame();
			goto GTOP;
		}
		loadLevel();
	}else if(t == 2)//player quit?
		goto GTOP;
	goto GLOOP;
}
Normally I wouldn't do some of that the way it is done. Main thing I see(in the current form), is to minimize worst case scenarios and make sure you can always rapidly respond to player input in places they would expect it. This basically means equalize code paths(even if there are nicer looking ways to write it, like goto vs while(1), not sure exactly what code the compiler does...), and stick to tight loops waiting on input. Even a small amount of extra operations per iteration you can literally "feel". Compare that so Sokoban World, and it's like...4% the development time just due to smaller scale
User avatar
danboid
Posts: 2074
Joined: Sun Jun 14, 2020 12:14 am

Re: Weber's Rants (tutorials)

Post by danboid »

The 32X!? That is one of the last platforms I would've expected you to say, with the Saturn (or Jaguar or 2600) being the least unlikely but thats exactly where you went! Interesting! What have you written for the 32X or the Saturn?

Is your Sokoban'd UzeboxUI going to run on real hardware, have you attempted to fix that?
User avatar
ry755
Posts: 226
Joined: Mon May 22, 2017 6:01 am

Re: Weber's Rants (tutorials)

Post by ry755 »

Oh my god this is amazing :lol: Never thought I'd see an actual game made for UzeboxUI, I figured it was far too clunky and slow for that. But I guess this is the perfect type of game for it!!
User avatar
D3thAdd3r
Posts: 3289
Joined: Wed Apr 29, 2009 10:00 am
Location: Minneapolis, United States

Re: Weber's Rants (tutorials)

Post by D3thAdd3r »

danboid wrote: Mon Feb 20, 2023 11:35 pmThe 32X!?
Well someone has already accomplished what I only ever dreamed of with Doom Resurrection. I could probably will let the imaginary 32X mission go some day haha. Never got far anywhere beyond initial testing on the 68K side for the 32X, SH2s just in a loop, nowhere really on Saturn. The problem is I was already roughly aware the amount of work it would take to complete even a 16bit game(I dabbled for years with some way too lofty PC projects...never finished any, thought I could part time AAA PC games haha). Uzebox is a much more friendly in scope for a solo for fun developer.

I haven't tried UzeboxUI on real hardware yet, but I'm sure it could be fixed up if it isn't reliable currently.
ry755 wrote: Tue Feb 21, 2023 1:01 am But I guess this is the perfect type of game for it!!
Haha yes, very few reads or writes per frame and pretty tolerant to input delay. There is promise there, especially with more host side functions like bulk memory writes, reads, etc. I think several additional apps could be created.
User avatar
danboid
Posts: 2074
Joined: Sun Jun 14, 2020 12:14 am

Re: Weber's Rants (tutorials)

Post by danboid »

What is your fave "it runs Doom" Lee? The C64 (w/accelerator) port is probably my fave. James Howards port of Wolfenstein to the 8088 / CGA is also right up there, even tho its not Doom. I include Woldenstein ports as a natural extension of it runs Doom. Doom on the megadrive also looks great:

https://www.youtube.com/watch?v=LhBsmINZev8

As you probably noticed, it turned out my issue with running UzeboxUI was that I don't have any SPI RAM yet although it should be on its way.

Have you had chance to have a look for the Mega Bomber source yet?

EDIT

I was just having a quick look at https://itrunsdoom.tumblr.com/ and turns out you can run Doom within DOS Doom. Don't you all feel better? Can anyone ever win the Doom port comp? I suppose not because someone would invent a new platform just to do another more impressive Doom port.
Post Reply