Simple 2D game prototyping framework.

  • By null
  • Last update: Dec 17, 2022
  • Comments: 9

prototype

Simply prototype 2D games using an easy, minimal interface that lets you draw simple primitives and images on the screen, easily handle mouse and keyboard events and play sounds.

Games

Installation

Install the Go programming language. After clicking the download link you will be referred to the installation instructions for your specific operating system.

Install Git and make it available in the PATH so the go tool can use it.

For Linux and OS X you need a C compiler installed. On Windows this is not necessary.

On Linux there are two backends available, GLFW and SDL2. For GLFW you need these libraries installed (tested on Linux Mint, other distros might be slightly different):

libx11-dev libxrandr-dev libgl1-mesa-dev libxcursor-dev libxinerama-dev libxi-dev

GLFW is used by default. To use SDL2 you need to add -tags sdl2 to your Go builds, e.g. go run -tags sdl2 main.go. Also you need the SDL2 libraries installed:

libsdl2-dev libsdl2-mixer-dev libsdl2-image-dev

Install the library and samples by running the following on your command line:

go get github.com/gonutz/prototype/...

Documentation

For a description of all library functions, see the godoc page for this project. Note that most of the functionality is in the Window interface and hence the descriptions are listed as code comments in the source for that type.

Example

package main

import (
	"math"

	"github.com/gonutz/prototype/draw"
)

func main() {
	draw.RunWindow("Title", 640, 480, update)
}

func update(window draw.Window) {
	// find the screen center
	w, h := window.Size()
	centerX, centerY := w/2, h/2

	// draw a button in the center of the screen
	mouseX, mouseY := window.MousePosition()
	mouseInCircle := math.Hypot(float64(mouseX-centerX), float64(mouseY-centerY)) < 20
	color := draw.DarkRed
	if mouseInCircle {
		color = draw.Red
	}
	window.FillEllipse(centerX-20, centerY-20, 40, 40, color)
	window.DrawEllipse(centerX-20, centerY-20, 40, 40, draw.White)
	if mouseInCircle {
		window.DrawScaledText("Close!", centerX-40, centerY+25, 1.6, draw.Green)
	}

	// check all mouse clicks that happened during this frame
	for _, click := range window.Clicks() {
		dx, dy := click.X-centerX, click.Y-centerY
		squareDist := dx*dx + dy*dy
		if squareDist <= 20*20 {
			// close the window and end the application
			window.Close()
		}
	}
}

This example displays a window with a round button in the middle to close it. It demonstrates some basic drawing and event handling code.

Download

prototype.zip

Comments(9)

  • 1

    Is there a way to have the screen size smaller than the window size?

    I want to use this for pixel art games, but I don't want to have a tiny window. So how do I make the screen size smaller than the window size? For example, having the window 960x600 but drawing on it like it's 320x200.

  • 2

    Build errors on Windows w/go 1.15 and 1.16

    > go build .
    go: finding module for package github.com/gonutz/prototype/draw
    go: found github.com/gonutz/prototype/draw in github.com/gonutz/prototype v0.0.0-20210219081453-295a54f2a887
    go: finding module for package github.com/gonutz/d3d9
    go: finding module for package github.com/gonutz/mixer
    go: finding module for package github.com/gonutz/w32
    go: finding module for package github.com/gonutz/mixer/wav
    go: found github.com/gonutz/d3d9 in github.com/gonutz/d3d9 v1.0.1
    go: found github.com/gonutz/mixer in github.com/gonutz/mixer v0.0.0-20171129091500-a40121441cc5
    go: found github.com/gonutz/mixer/wav in github.com/gonutz/mixer v0.0.0-20171129091500-a40121441cc5
    go: found github.com/gonutz/w32 in github.com/gonutz/w32 v1.0.0
    go: finding module for package github.com/gonutz/ds
    go: found github.com/gonutz/ds in github.com/gonutz/ds v0.0.0-20170706082326-82969548412b
    # github.com/gonutz/prototype/draw
    ..\..\..\..\..\pkg\mod\github.com\gonutz\protot[email protected]\draw\window_windows.go:93:8: undefined: w32.UnregisterClassAtom
    ..\..\..\..\..\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:442:7: undefined: w32.WM_MOUSEHWHEEL
    ..\..\..\..\..\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:515:10: cannot use style & ^w32.WS_OVERLAPPEDWINDOW (type int32) as type uint32 in argument to w32.SetWindowLong
    

    What version of go is wanted/desired? It might be worth adding a go.mod file (go mod init) so you can specify the go and dependency versions inline?

    Steps to reproduce:

    I tested with the built-in Windows Sandbox feature (if not enabled, hit start, type enough of 'turn windows features on' to get a completion, and enable 'Windows Sandbox' in the list of optional features). Could also be done with a virtual machine.

    • Install latest git: https://github.com/git-for-windows/git/releases/download/v2.30.1.windows.1/Git-2.30.1-64-bit.exe

    • Install go 1.16: https://golang.org/dl/go1.16.windows-amd64.msi

    • Open a Powershell session (I'm a linux/bash guy, powershell is based on the same spec as bash so it's a little more natural to me) -- hit start -- type 'powershell'

    • ensure git and go are installed (note: powershell supports completion on flags and arguments)

      gcm git -ea stop # short for: get-command git -erroraction stop gcm go -ea stop

    • create a local go project:

      cd (mkdir -f (join-path $env:GOPATH local/scratch))

    • initialize local go module

      go mod init local/scratch

    • create a simple test file

      notepad main.go with the following code package main

      import "github.com/gonutz/prototype/draw"

      func main() { draw.RunWindow("Test", 640, 480, update) }

      func update(window draw.Window) { window.DrawLine(100, 100, 440, 380, draw.Blue) }

    • install dependencies

      go mod tidy

    • attempt to run PS C:\Users\WDAGUtilityAccount\go\local\scratch> go run . # github.com/gonutz/prototype/draw ....\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:93:8: undefined: w32.UnregisterClassAtom ....\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:442:7: undefined: w32.WM_MOUSEHWHEEL ....\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:515:10: cannot use style & ^w32.WS_OVERLAPPEDWINDOW (type int32) as type uint32 in argument to w32.SetWindowLong

  • 3

    Web Assembly?

    Will there be any support for Web Assembly? I want to use this for game jams, but usually people don't want to download games, so I usually put web builds of my games. :smile:

  • 4

    I know this isn't technically an issue but,

    It is an issue that this is so underrated! I've been trying everything everywhere from Vulkan-go to glfw-go and NOTHING worked cross platform! Thank you guys, and I'll probably contribute to this in the future! :smile:

  • 5

    some tips

    hello I am again sorry(I am only student who do not understand many things), but I am working on my bachalor and I would like to share tip how to improve code(if I do found and it is already exist, sorry):

    • I was create interface "shape":
    type Shape interface {
    	GetXY() (int, int)
    	SetXY(x, y int)
    
    	GetSize() (int, int)
    	SetSize(width, height int)
    
    	GetIdName() (int, string)
    
    	WasPressed() bool
    	WasActive() bool
    	In(x int, y int) bool
    
    	GetFunc() func(window draw.Window)
            SetFunc(function func(window draw.Window))
    	Paint(window draw.Window, scaledText float32)
    	CarryEvent(window draw.Window)
    }
    
    • I was create several structures("basic", TextField, Label, CheckBox, Button), "basic" is implementing Shape and other have embed it, so all are Shape and can also overwrite method....... .
    type Basic struct {
    	x, y, endX, endY, width, height float64
    	id                              int64
    	name                            string
    	pressed, active                 bool
    	function                        func(draw.Window)
    }
    ......
    
    • you can prepare slice structures:= make([]Shape,...) before "animation loop"(or during first turn - I have "init turn" because I use func "GetScaledText") and your "main code" will look like:
    func DrawControlAll(window draw.Window) {
    	for i := 0; i < len(shapes); i++ {
    		shape := shapes[i]
    		shape.CarryEvent(window)
    		shape.Paint(window, 3)
    	}
    }
    
    • if you would like to control themself, you can also at init make lamda with references to themself like this:
            label := my_package.NewLabel(name)
    	label.SetFunc(func(window draw.Window) {
    		label.DoSomeThingSpecial()//e.g. Hide by name
    	})
    
    • this only tip ............
  • 6

    some errors on linux arm

    hello, i am sorry(I am only begener) but I was trying this library on linux arm(andronix, you can see on Linux Distro on Android) and I found this:

    • build with sdl2:
    ./sdl2
    shared memfd open() failed: Function not implemented
    shared memfd open() failed: Function not implemented
    
    • build without tags:
    ./default
    fatal error: unexpected signal during runtime execution
    [signal SIGSEGV: segmentation violation code=0x2 addr=0x7f7d92a000 pc=0x7f54028800]
    
    runtime stack:
    runtime.throw(0x5ccad6, 0x2a)
    /usr/local/go/src/runtime/panic.go:1117 +0x54
    runtime.sigpanic()
    /usr/local/go/src/runtime/signal_unix.go:718 +0x29c
    
    goroutine 1 [syscall, locked to thread]:
    runtime.cgocall(0x580a40, 0x40000bfdb8, 0x4000070000)
    /usr/local/go/src/runtime/cgocall.go:154 +0x50 fp=0x40000bfd80 sp=0x40000bfd40 pc=0x445ba0
    github.com/gonutz/glfw/v3.3/glfw._Cfunc_glfwSwapBuffers(0x76ab60)
    _cgo_gotypes.go:2403 +0x40 fp=0x40000bfdb0 sp=0x40000bfd80 pc=0x54ec10
    github.com/gonutz/glfw/v3.3/glfw.(*Window).SwapBuffers.func1(0x40000d0000)
    /root/go/pkg/mod/github.com/gonutz/[email protected]/v3.3/glfw/context.go:41 +0x50 fp=0x40000bfdf0 sp=0x40000bfdb0 pc=0x551580
    github.com/gonutz/glfw/v3.3/glfw.(*Window).SwapBuffers(0x40000d0000)
    /root/go/pkg/mod/github.com/gonutz/[email protected]/v3.3/glfw/context.go:41 +0x28 fp=0x40000bfe30 sp=0x40000bfdf0 pc=0x54ef28
    github.com/gonutz/prototype/draw.RunWindow(0x5b7992, 0x5, 0x280, 0x1e0, 0x5ce898, 0x0, 0x0)
    /root/go/pkg/mod/github.com/gonutz/[email protected]/draw/window_glfw.go:125 +0x6a8 fp=0x40000bff20 sp=0x40000bfe30 pc=0x554c58
    main.main()
    /root/go/src/my/my.go:10 +0x50 fp=0x40000bff70 sp=0x40000bff20 pc=0x558fb0
    runtime.main()
    /usr/local/go/src/runtime/proc.go:225 +0x274 fp=0x40000bffd0 sp=0x40000bff70 pc=0x475bf4
    runtime.goexit()
    /usr/local/go/src/runtime/asm_arm64.s:1130 +0x4 fp=0x40000bffd0 sp=0x40000bffd0 pc=0x4a4bc4
    
  • 7

    Fix/w32 modules ref

    Prototype references github.com/gonutz/w32 which, with modules, fetches the v1.0.0 version that does not build. This change updates prototype to build with go 1.15 and 1.16 in a fresh environment.

    Testing:

    • reimaged a vm, win 10 2020 h2
    • installed git, go,
    • clone github.com/kfsone/prototype into $env:GOPATH/src/github.com/gonutz/prototype and checked out the fix/w32-modules-ref branch
    • execute the following steps:
        $nutzdir = Join-Path $env:GOPATH src/github.com/gonutz
        $srcurl = "https://raw.githubusercontent.com/gonutz/prototype/master/samples/worm/worm.go"
    
        cd (mkdir (join-path $nutzdir proto.before))
        invoke-webrequest $srcurl -outfile worm.go  # iwr for short
        go run worm.go
    
        # go 1.16 fails because no modules stuff
        go mod init ; go mod tidy
        go run worm.go
    

    fails:

    # github.com/gonutz/prototype/draw
    ..\..\..\..\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:93:8: undefined: w32.UnregisterClassAtom
    ..\..\..\..\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:442:7: undefined: w32.WM_MOUSEHWHEEL
    ..\..\..\..\pkg\mod\github.com\gonutz\[email protected]\draw\window_windows.go:515:10: cannot use style & ^w32.WS_OVERLAPPEDWINDOW (type int32) as type uint32 in argument to w32.SetWindowLong
    

    then I used go mod edit to switch to using the local on-disk copy of prototype:

        cd (mkdir (Join-Path $testdir proto.after))
        iwr $srcurl -outfile worm.go
    
        go mod init
        # --vv--
        go mod edit -replace github.com/gonutz/prototype=../../gonutz/prototype  ## <<
        # --^^--
        go mod tidy
        go run worm.go
    

    which works

    image

  • 8

    Cannot call RunWindow mutliple times

    Tested on Windows: calling RunWindow twice should open a window and once that is closed should open another one. Currently after closing the first window, no second window appears.

  • 9

    Is it possible to draw triangles or polygons?

    Looking at the source code, there doesn't seem to be any way to draw triangles or polygons.

    Is there any way to access lower-level functions to achieve this, or is this being planned for a future update?

    Thanks!