pi

package module
v0.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2022 License: MIT Imports: 18 Imported by: 1

README

pi

Go Reference codecov

The retro game development engine for Go, inspired by Pico-8 and powered by Ebitengine.

FAQ

Is this a new fantasy console?

No, it's not. It's rather a game development library with some additional tools (like a console) which make it simple (and fun!) to write retro games in Go.

What is a retro game?

It's a game that resembles old 8-bit/16-bit games. This usually means:

  • (extremely) Low resolution (like 128x128 pixels)
  • Limited number of colors (like 16)
  • Very small number of assets (like 256 sprites, map having up to 8K tiles)
  • Simple rules (opposite to Paradox grand strategy games)
  • Sound effects and music made using predefined synth instruments and effects
What similarities does Pi have with Pico-8?
  • Most API function names are similar and behave the same way.
  • Screen resolution is small, and the number of colors is limited. Although in Pi you can change the resolution and palette.
  • You have one small sprite sheet.
Why would I use it?

Because it's the easiest way to write a game in Go. IMHO ;)

Is Pi ready to use?

Pi is under development. Only limited functionality is provided. API is not stable. See roadmap for details.

How to get started?

  1. Install dependencies
  1. Create a new game using provided Github template.

See also examples directory and documentation.

Documentation

Overview

Package pi provides API to develop retro games.

Please note that the entire pi package is not concurrency-safe. This means that it is unsafe to run functions and access package variables from go-routines started by your code.

Index

Constants

View Source
const (
	SpriteWidth, SpriteHeight = 8, 8
)

Variables

View Source
var (
	// Update is a user provided function executed each frame (30 times per second).
	//
	// The purpose of this function is to handle user input, perform calculations, update
	// game state etc. Typically, this function does not draw on screen.
	Update func()

	// Draw is a user provided function executed at most each frame (up to 30 times per second).
	// π may skip calling this function if previous frame took too long.
	//
	// The purpose of this function is to draw on screen.
	Draw func()

	Resources fs.ReadFileFS // Resources contains files like sprite-sheet.png

	// Palette has all colors available in the game. Up to 256.
	// Palette is taken from loaded sprite sheet (which must be
	// a PNG file with indexed color mode). If sprite-sheet.png was not
	// found, then default 16 color palette is used.
	//
	// Can be freely read and updated. Changes will be visible immediately.
	Palette [256]image.RGB = defaultPalette

	// SpriteSheetWidth will be used if sprite-sheet.png was not found.
	SpriteSheetWidth = defaultSpriteSheetWidth
	// SpriteSheetHeight will be used if sprite-sheet.png was not found.
	SpriteSheetHeight = defaultSpriteSheetHeight

	// ScreenWidth specifies the width of the screen (in pixels).
	ScreenWidth = defaultScreenWidth
	// ScreenHeight specifies the height of the screen (in pixels).
	ScreenHeight = defaultScreenHeight
)

User parameters. Will be used during Boot (and Run).

View Source
var (
	ErrStateNotFound        = errors.New("state not found")    // ErrStateNotFound is an expected error which is returned when state is not stored.
	ErrInvalidStateName     = errors.New("invalid state name") // ErrInvalidStateName is a programmer error which is returned when state name is invalid.
	ErrNilStateOutput       = errors.New("nil state output")   // ErrNilStateOutput is a programmer error which is returned when output is nil.
	ErrStateUnmarshalFailed = errors.New("state unmarshal failed")
	ErrStateMarshalFailed   = errors.New("state marshal failed")
)
View Source
var (
	// ScreenData contains pixel colors for the screen visible by the player.
	// Each pixel is one byte. It is initialized during pi.Boot.
	//
	// Pixels on the screen are organized from left to right,
	// top to bottom. Slice element number 0 has pixel located
	// in the top-left corner. Slice element number 1 has pixel color
	// on the right and so on.
	//
	// Can be freely read and updated. Useful when you want to use your own
	// functions for pixel manipulation.
	// Pi will panic if you try to change the length of the slice.
	ScreenData []byte
)

Screen-specific data

View Source
var (
	// SpriteSheetData contains pixel colors for the entire sprite sheet.
	// Each pixel is one byte. It is initialized during pi.Boot.
	//
	// Pixels in the sprite-sheet are organized from left to right,
	// top to bottom. Slice element number 0 has pixel located
	// in the top-left corner. Slice element number 1 has a pixel color
	// on the right and so on.
	//
	// Can be freely read and updated.
	// Useful when you want to use your own functions for pixel manipulation.
	// Pi will panic if you try to change the length of the slice.
	SpriteSheetData []byte
)

Sprite-sheet data

Functions

func Atan2

func Atan2(dx, dy float64) float64

Atan2 converts DX, DY into an angle from 0..1

Similar to Cos and Sin, angle is taken to run anticlockwise in screenspace. For example:

atan(0,-1)  // returns 0.25

func Boot

func Boot() error

Boot initializes the engine based on user parameters such as ScreenWidth and ScreenHeight. It loads the resources like sprite-sheet.png.

If sprite-sheet.png was not found in pi.Resources, then empty sprite-sheet is used with the size of pi.SpriteSheetWidth * pi.SpriteSheetHeight.

Boot also resets all draw state information like camera position and clipping region.

Boot can be run multiple times. This is useful for writing unit tests.

func Btn added in v0.6.0

func Btn(button Button) bool

Btn returns true if a button is being pressed at this moment by player 0.

func BtnBits added in v0.6.0

func BtnBits() int

BtnBits returns the state of all buttons for players 0 and 1 as bitset.

The first byte contains the button states for player 0 (bits 0 through 5, bits 6 and 7 are unused). The second byte contains the button states for player 1 (bits 8 through 13).

Bit 0 is Left, 1 is Right, bit 5 is the X button.

A bit of 1 means the button is pressed.

func BtnPlayer added in v0.6.0

func BtnPlayer(button Button, player int) bool

BtnPlayer returns true if a button is being pressed at this moment by specific player. The player can be 0..7.

func Btnp added in v0.6.0

func Btnp(button Button) bool

Btnp returns true when the button has just been pressed. It also returns true after the next 15 frames, and then every 4 frames. This simulates keyboard-like repeating.

func BtnpBits added in v0.6.0

func BtnpBits() int

BtnpBits returns the state of all buttons for players 0 and 1 as bitset.

The first byte contains the button states for player 0 (bits 0 through 5, bits 6 and 7 are unused). The second byte contains the button states for player 1 (bits 8 through 13).

Bit 0 is Left, 1 is Right, bit 5 is the X button.

A bit of 1 means the button has just been pressed.

func BtnpPlayer added in v0.6.0

func BtnpPlayer(button Button, player int) bool

BtnpPlayer returns true when the button has just been pressed. It also returns true after the next 15 frames, and then every 4 frames. This simulates keyboard-like repeating. The player can be 0..7.

func Camera

func Camera(x, y int) (prevX, prevY int)

Camera sets the camera offset used for all subsequent draw operations.

func CameraReset

func CameraReset() (prevX, prevY int)

CameraReset resets the camera offset to origin (0,0).

func Circ added in v0.10.0

func Circ(centerX, centerY, radius int, color byte)

Circ draws a circle

Circ takes into account camera position, clipping region and draw palette.

func CircFill added in v0.10.0

func CircFill(centerX, centerY, radius int, color byte)

CircFill draws a filled circle

CircFill takes into account camera position, clipping region and draw palette.

func Clip

func Clip(x, y, w, h int) (prevX, prevY, prevW, prevH int)

Clip sets the clipping region in the form of rectangle. All screen drawing operations will not affect any pixels outside the region.

Clip returns previous clipping region.

func ClipReset

func ClipReset() (prevX, prevY, prevW, prevH int)

ClipReset resets the clipping region, which means that entire screen will be clipped.

func Cls

func Cls()

Cls cleans the entire screen with color 0. It does not take into account any draw state parameters such as clipping region or camera.

Cls also resets the cursor used for printing text and resets the clipping region.

func ClsCol

func ClsCol(col byte)

ClsCol cleans the entire screen with specified color. It does not take into account any draw state parameters such as clipping region or camera.

ClsCol also resets the cursor used for printing text and resets the clipping region.

func Cos

func Cos(angle float64) float64

Cos returns the cosine of angle which is in the range of 0.0-1.0 measured clockwise.

If you want to use conventional radian-based function use math.Cos.

func Cursor added in v0.7.1

func Cursor(x, y int) (prevX, prevY int)

Cursor set cursor position (in pixels) used by Print.

Cursor returns previously set cursor position.

func CursorReset added in v0.7.1

func CursorReset() (prevX, prevY int)

CursorReset resets cursor position used by Print to 0,0.

CursorReset returns previously set cursor position.

func Line added in v0.9.0

func Line(x0, y0, x1, y1 int, color byte)

Line draws a line between points x0,y0 and x1,y1 (inclusive).

Line takes into account camera position, clipping region and draw palette.

func MouseBtn added in v0.11.0

func MouseBtn(b MouseButton) bool

MouseBtn returns true if the mouse button is being pressed at this moment.

func MouseBtnp added in v0.11.0

func MouseBtnp(b MouseButton) bool

MouseBtnp returns true when the mouse button has just been pressed. It also returns true after the next 15 frames, and then every 4 frames. This simulates keyboard-like repeating.

func MousePos added in v0.11.0

func MousePos() (x, y int)

MousePos returns the position of mouse in screen coordinates.

func MustBoot added in v0.13.0

func MustBoot()

MustBoot does the same as Boot, but panics instead of returning an error.

Useful for writing unit tests and quick and dirty prototypes. Do not use on production ;)

func MustRun added in v0.13.0

func MustRun()

MustRun does the same as Run, but panics instead of returning an error.

Useful for writing unit tests and quick and dirty prototypes. Do not use on production ;)

func Pal added in v0.5.0

func Pal(color byte, replacementColor byte)

Pal replaces color with another one for all subsequent drawings (it is changing the so-called draw palette).

Affected functions are Pset, Spr, SprSize, SprSizeFlip, Rect and RectFill.

func PalDisplay added in v0.5.0

func PalDisplay(color byte, replacementColor byte)

PalDisplay replaces color with another one for the whole screen at the end of a frame (it is changing the so-called display palette).

func PalReset added in v0.5.0

func PalReset()

PalReset resets all swapped colors for all palettes.

func Palt added in v0.4.0

func Palt(color byte, transparent bool)

Palt sets color transparency. If true then the color will not be drawn for next drawing operations.

Color transparency is used by Spr, SprSize and SprSizeFlip.

func PaltReset added in v0.4.0

func PaltReset()

PaltReset sets all transparent colors to false and makes color 0 transparent.

func Pget

func Pget(x, y int) byte

Pget gets a pixel color on the screen.

func Print added in v0.7.1

func Print(text string, color byte) (x int)

Print prints text on the screen. It takes into consideration cursor position, clipping region and camera position.

After printing all characters Print goes to the next line. When there is no space left on screen the clipping region is scrolled to make room.

Only unicode characters with code < 256 are supported. Unsupported chars are printed as question mark. The entire table of available chars can be found here: https://github.com/elgopher/pi/blob/master/internal/system-font.png

Print returns the right-most x position that occurred while printing.

func Pset

func Pset(x, y int, color byte)

Pset sets a pixel color on the screen.

func Rect added in v0.8.0

func Rect(x0, y0, x1, y1 int, color byte)

Rect draws a rectangle between points x0,y0 and x1,y1 (inclusive).

Rect takes into account camera position, clipping region and draw palette.

func RectFill added in v0.8.0

func RectFill(x0, y0, x1, y1 int, color byte)

RectFill draws a filled rectangle between points x0,y0 and x1,y1 (inclusive).

RectFill takes into account camera position, clipping region and draw palette.

func Reset

func Reset()

Reset resets all user parameters to default values. Useful in unit tests.

func Run

func Run() error

Run boots the game, opens the window and run the game loop. It must be called from the main thread.

Run does not boot the game once the game has been booted. Thanks to this, the user can call Boot directly and draw to the screen before the game loop starts.

It returns error when something terrible happened during initialization.

func Sget

func Sget(x, y int) byte

Sget gets the pixel color on the sprite sheet.

func Sin

func Sin(angle float64) float64

Sin returns the sine of angle which is in the range of 0.0-1.0 measured clockwise.

Sin returns an inverted result to suit screen space (where Y means "DOWN", as opposed to mathematical diagrams where Y typically means "UP"):

sin(0.25) // returns -1

If you want to use conventional radian-based function without the y inversion, use math.Sin.

func Snap added in v0.7.1

func Snap() (string, error)

Snap takes a screenshot and saves it to temp dir.

Snap returns a filename. If something went wrong error is returned.

func Spr

func Spr(n, x, y int)

Spr draws a sprite with specified number on the screen. Sprites are counted from left to right, top to bottom. Sprite 0 is on top-left corner, sprite 1 is to the right and so on.

func SprSize

func SprSize(n, x, y int, w, h float64)

SprSize draws a range of sprites on the screen.

n is a sprite number in the top-left corner.

Non-integer w or h may be used to draw partial sprites.

func SprSizeFlip

func SprSizeFlip(n, x, y int, w, h float64, flipX, flipY bool)

SprSizeFlip draws a range of sprites on the screen.

If flipX is true then sprite is flipped horizontally. If flipY is true then sprite is flipped vertically.

func Sset

func Sset(x, y int, color byte)

Sset sets the pixel color on the sprite sheet. It does not update the global Color.

func StateDelete added in v0.12.0

func StateDelete(name string) error

StateDelete permanently deletes data with given name.

func StateLoad added in v0.12.0

func StateLoad[T any](name string, out *T) error

StateLoad reads the persistent game data with specified name. Data will be stored in out param. The type of out should be compatible with type used during StateSave. For example, trying to load stored string into an int will return the ErrStateUnmarshalFailed.

ErrStateNotFound error is returned when state does not exist. Please check the error with following code:

if errors.Is(err, pi.ErrStateNotFound) { ... }

ErrNilStateOutput is returned when out is nil.

func StateSave added in v0.12.0

func StateSave(name string, data any) error

StateSave permanently stores the data with given name. Data could be loaded using StateLoad after restarting the game. Data can be of any type: string, int, time.Time, slice, map or a struct. Only struct public fields will be stored.

Please note that on some platforms there are limits for how much data could be stored. Web browsers for example have 5MB total data limit. So, if you want your game to be portable across different platforms, please limit the size of data below that number (in fact a game which stores more than hundreds of KBs per savegame does not look retro anymore). Similarly to data size limit, there is a limit for the number of states. It should be no more than tens of states. Generally using structs instead of primitive types like string or int could overcome this limit. For example, a single struct could have all the user preferences, or the entire hall of fame.

Saving a state is an atomic operation. Either the entire operation is successful and state is stored, or error is reported and no data is stored (previous data is not updated). You can leverage this feature to store similar data in a consistent manner - either all changes to data are applied or no changes at all. For example, you could design a struct having all user preferences, or a struct dedicated for an entire savegame. Such design will highly decrease the chance of consistency problems in case the game/OS crashes or during power loss.

Name cannot be empty, have characters "/", "\", or by longer than 32 characters

func States added in v0.12.0

func States() ([]string, error)

States returns names of all states.

func Stop added in v0.2.0

func Stop()

Stop will stop the game loop after Update is finished, but before Draw. For now the entire app will be closed, but later it will show a dev console instead, where developer will be able to resume the game.

func Time

func Time() float64

Time returns the amount of time since game was run, as a (fractional) number of seconds.

Calling Time() multiple times in the same frame will always return the same result.

Types

type Button added in v0.6.0

type Button int

Button is a virtual button on any game controller. The game controller can be a gamepad or a keyboard.

Button is used by Btn, Btnp, BtnPlayer, BtnpPlayer, BtnBits and BtnpBits.

const (
	Left  Button = 0
	Right Button = 1
	Up    Button = 2
	Down  Button = 3
	O     Button = 4 // O is a first fire button
	X     Button = 5 // X is a second fire button
)

Keyboard mappings:

player 0: [DPAD] - cursors  [O] - Z C N   [X] - X V M
player 1: [DPAD] - SFED     [O] - LSHIFT  [X] - TAB W Q A

First connected gamepad controller is player 0, second player 1 and so on. On XBox controller O is A and Y, X is B and X.

type Font added in v0.7.1

type Font struct {
	// Data contains all 256 characters sorted by their ascii-like number.
	// Each character is 8 subsequent bytes, starting from the top.
	// Left-most pixel in a line is bit 0. Right-most pixel in a line is bit 7.
	Data [8 * 256]byte
	// Width in pixels for all characters below 128
	Width int
	// WidthSpecial is a with of all special characters (code>=128)
	WidthSpecial int
	Height       int
}

Font contains all information about loaded font.

type MouseButton added in v0.11.0

type MouseButton int
const (
	MouseLeft   MouseButton = 0
	MouseMiddle MouseButton = 1
	MouseRight  MouseButton = 2
)

Directories

Path Synopsis
examples
boot command
Example showing how to change screen resolution and run π functions before game loop.
Example showing how to change screen resolution and run π functions before game loop.
controller command
Example showing how to test pressed buttons of game controllers.
Example showing how to test pressed buttons of game controllers.
hello command
Example animating HELLO WORLD text on screen.
Example animating HELLO WORLD text on screen.
memory command
Example showing how to directly modify screen memory.
Example showing how to directly modify screen memory.
pal command
Example showing practical use of palette swapping.
Example showing practical use of palette swapping.
print command
Example showing how to print text to screen.
Example showing how to print text to screen.
shapes command
Example showing how to draw shapes and use a mouse.
Example showing how to draw shapes and use a mouse.
state command
Example showing how to save and load the state.
Example showing how to save and load the state.
trigonometry command
Example plotting sin and cos on screen
Example plotting sin and cos on screen
Package image provides API for decoding images.
Package image provides API for decoding images.
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL