psxsdk

Scrolling backgrounds

A scrolling game's world is too big for video memory as a picture, so it is built of tiles: a few dozen small squares in one texture, and a map of which square goes where. The library draws such a map for a program, as its fixed background, in three parts:

  • the cells, GsCELL, each a square of a texture page with its palette,
  • the map, GsMAP, a grid of indexes into the cells,
  • and the background, GsBG, the window on the screen it is drawn in, how far it is scrolled, and its map.

The tiles example cuts sixteen cells from the SCEE tutorial's own tile image, and its map is the tutorial's, forty cells by thirty-two, two screens each way:

func main() {
	screen.Open()
	screen.Font(8, 8, 304, 40, 200)
	text.Init(textBuf[:])

	if f := disc.LoadAll(300, files...); f != nil {
		for range screen.Frames() {
			yaroze.Print("could not read ")
			yaroze.Print(f.Name)
		}
	}
	im := yaroze.TIM(bgTIM.Data()).Load()

	// Sixteen cells, eight across and two down, from the image's corner.
	tpage := yaroze.GetTPage(1, 0, int32(im.Px), int32(im.Py))
	clut := yaroze.GetClut(int32(im.Cx), int32(im.Cy))
	for i := range cells {
		cells[i] = yaroze.GsCELL{U: uint8(i % 8 * tileSize), V: uint8(i / 8 * tileSize), Cba: clut, Tpage: tpage}
	}
	tiles = yaroze.GsMAP{
		Cellw: tileSize, Cellh: tileSize,
		Ncellw: mapW, Ncellh: mapH,
		Base:  &cells[0],
		Index: &layout[0],
	}
	// The window is the whole screen, the cells 8-bit and drawn as they
	// are.
	b := screen.Bounds()
	bg = yaroze.GsBG{
		Attribute: 1 << 24,
		X:         b.X, Y: b.Y, W: b.W, H: b.H,
		R: 128, G: 128, B: 128,
		Map:    &tiles,
		Scalex: yaroze.ONE, Scaley: yaroze.ONE,
	}
	yaroze.GsInitFixBg16(&bg, &work[0])

	for ot := range screen.Frames() {
		pad.Update()
		dx, dy := pad.Dir()
		bg.Scrollx = max(0, min(bg.Scrollx+int16(dx*speed), mapW*tileSize-bg.W))
		bg.Scrolly = max(0, min(bg.Scrolly+int16(dy*speed), mapH*tileSize-bg.H))
		yaroze.GsSortFixBg16(&bg, &work[0], ot, 1)

		text.Reset()
		text.WriteString("~c000SCROLL ").WriteInt(bg.Scrollx).WriteString(",").WriteInt(bg.Scrolly)
		text.WriteString(" OF ").WriteInt(mapW * tileSize).WriteString(" BY ").WriteInt(mapH * tileSize).WriteString("\n")
		text.Print()
	}
}

GsInitFixBg16 prepares a work area once, sized for the window, and GsSortFixBg16 then makes the packets for the cells that show each frame into that area rather than the frame's own packet space. It draws only what the window shows, 21 by 16 cells here of the map's 1,280, so the map can be as big as memory allows. Scrolling is changing Scrollx and Scrolly, kept within the map so the window never shows past its edge.

Tiles in video memory#

A cell is 16 by 16 pixels within one texture page, 256 by 256, so a page holds 256 of them. Cells of 8 bits each use a palette of 256 colours, and of 4 bits each one of 16, half the memory again. The tutorial's tiles are 8-bit, at 320, 0 in video memory beside the display buffers, with the palette at 320, 509 below them. Making data plans where images go.

A 0xffff in the map leaves a cell empty, so a background over another shows through it. Two backgrounds scrolled at different speeds, a near one over a far one, are the parallax of many 2D games.