-
-
Save BjoernSchilberg/82b83609b0e7c3d33a8e01a6657c08e7 to your computer and use it in GitHub Desktop.
Ebiten example with perlin noise generation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module samw.dev/ebiten | |
go 1.15 | |
require ( | |
github.com/aquilax/go-perlin v1.1.0 | |
github.com/hajimehoshi/ebiten/v2 v2.2.4 | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | |
github.com/aquilax/go-perlin v1.1.0 h1:Gg+3jQ24wT4Y5GI7TCRLmYarzUG0k+n/JATFqOimb7s= | |
github.com/aquilax/go-perlin v1.1.0/go.mod h1:z9Rl7EM4BZY0Ikp2fEN1I5mKSOJ26HQpk0O2TBdN2HE= | |
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be h1:vEIVIuBApEBQTEJt19GfhoU+zFSV+sNTa9E9FdnRYfk= | |
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210727001814-0db043d8d5be/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | |
github.com/hajimehoshi/bitmapfont/v2 v2.1.3/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs= | |
github.com/hajimehoshi/ebiten v1.12.12 h1:JvmF1bXRa+t+/CcLWxrJCRsdjs2GyBYBSiFAfIqDFlI= | |
github.com/hajimehoshi/ebiten/v2 v2.2.4 h1:/+qrmbv+W6scgVWwQJ7IyiI2z4y8QM2n0JDHStNC+Ns= | |
github.com/hajimehoshi/ebiten/v2 v2.2.4/go.mod h1:olKl/qqhMBBAm2oI7Zy292nCtE+nitlmYKNF3UpbFn0= | |
github.com/hajimehoshi/file2byteslice v0.0.0-20210813153925-5340248a8f41/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE= | |
github.com/hajimehoshi/go-mp3 v0.3.2/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= | |
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= | |
github.com/hajimehoshi/oto/v2 v2.1.0-alpha.2/go.mod h1:rUKQmwMkqmRxe+IAof9+tuYA2ofm8cAWXFmSfzDN8vQ= | |
github.com/jakecoffman/cp v1.1.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg= | |
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4= | |
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4= | |
github.com/jfreymuth/oggvorbis v1.0.3/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII= | |
github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ= | |
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= | |
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | |
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | |
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | |
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | |
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | |
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= | |
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= | |
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | |
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | |
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | |
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= | |
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= | |
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | |
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= | |
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5 h1:peBP2oZO/xVnGMaWMCyFEI0WENsGj71wx5K12mRELHQ= | |
golang.org/x/mobile v0.0.0-20210902104108-5d9a33257ab5/go.mod h1:c4YKU3ZylDmvbw+H/PSvm42vhdWbuxCzbonauEAP9B8= | |
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= | |
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | |
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | |
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | |
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | |
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | |
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= | |
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | |
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | |
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw= | |
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | |
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | |
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | |
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | |
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | |
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= | |
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This generates Perlin noise with ebiten and lets you move around using the arrow keys. | |
// To try out yourself: Install Go (latest version). Create a directory and copy / paste the files in there. | |
// Run `go get` to install all the packages. Then run `go run .` to run the game. | |
package main | |
// Importing some packages we need to run the game. Main packages are a color library, perlin noise stuff, and | |
// some utilities embitten provides. | |
import ( | |
"image/color" | |
"log" | |
"github.com/aquilax/go-perlin" | |
ebiten "github.com/hajimehoshi/ebiten/v2" | |
"github.com/hajimehoshi/ebiten/v2/ebitenutil" | |
) | |
// We'll create a grid of tiles and fill them up with Perlin noise. The screen size will be square to make things simpler. | |
const ( | |
// Size of the screen. | |
SCREEN_SIZE = 500 | |
// Size of the tiles (in pixels) | |
TILE_SIZE = 25 | |
// Number of tiles in the grid | |
NUM_TILES = SCREEN_SIZE / TILE_SIZE | |
) | |
/// Struct (basically an object or class) to hold the current position. | |
/// `justChanged` is a boolean that is set to true when the position changes, | |
/// and cleared using the `ClearChanged` method. | |
type Position struct { | |
x int | |
y int | |
justChanged bool | |
} | |
// Quick explanation of go syntax. | |
// | |
// Functions start with `func`, followed by a name and their arguments. | |
// The function body is wrapped in curly braces, like in C or Java. | |
// | |
// Example: `func HelloWorld() { | |
// Code here | |
// }` | |
// | |
// Go is a strongly typed language (like Java or C), so you have to write types for arguments | |
// and return types. Generally, types go *after* the variable name, like in Java or C. | |
// Types for arguments go in the parentheses, return types appear after the parentheses. | |
// | |
// Example: | |
// | |
// `func HelloWorld(name string) string { | |
// return "Hello, " + name | |
// }` | |
// | |
// Functions are called like in C or Java: `HelloWorld("John")`. Variables are created using `:=` | |
// and assigned with `=`. Use `:=` when in doubt, the go compiler will scream at you if you're creating | |
// a variable that already exists. | |
// | |
// Go allows you to create *structs*, which are basically classes in Java. Structs are defined using a type | |
// definition like this: | |
// | |
// type Position struct { | |
// x int | |
// y int | |
// } | |
// | |
// Types for each of the fields appear after the name of the field. Structs are created like this: | |
// | |
// Position { x: 0, y: 0 } | |
// | |
// If a field is omitted in a struct, it will be set to the zero value for that type. For example, the zero | |
// value for `int` is 0, for boolean is `false`, etc. So the above code can be simplified to this: | |
// | |
// Position{} | |
// | |
// To put methods on structs, you write a `func` with the struct in paranthesis before the function name. | |
// We could implement a `Move` method on the `Position` struct like this: | |
// | |
// func (p *Position) Move(x int, y int) { | |
// p.x += x | |
// p.y += y | |
// } | |
// | |
// The parenthesis are like a second set of arguments, but you can only have one of them. They will let you call | |
// the method on the struct like this: | |
// | |
// pos := Position{} | |
// pos.Move(1, 2) | |
// | |
// Arguments in Go are copied before they're passed in. These are *deep copies* by default. This might take a while on larger structs. | |
// To get around this, we can use *pointers*, just like in C. So above, we're using a pointer to the struct, instead of a copy. If we removed the | |
// asterisk (`*`) from the type, we'd get a copy instead. If you want to change fields on a struct, use a pointer, not a copy. | |
// | |
// Congrats, you now know Go! It's not too hard. | |
/// Clears the `justChanged` flag. | |
func (x *Position) ClearChanged() { | |
x.justChanged = false | |
} | |
/// Increments the position by `x` and `y`. Also sets the `justChanged` value to `true`. | |
func (a *Position) Increment(x int, y int) { | |
a.x += x | |
a.y += y | |
a.justChanged = true | |
} | |
/// The current state of the game. We have a list of tiles and a position. | |
type Game struct{ | |
tiles [][]float64 | |
currentPos Position | |
} | |
/// Ebiten requires that we create a few methods on our `Game` struct. | |
/// It has an interface (also called `Game`) with some functions. This is one of them. | |
/// | |
/// This function gets called on every 'tick' (default: 60 times per second). We use it | |
/// to update our game state. | |
func (g *Game) Update() error { | |
// Check to see if the user has pressed the arrow keys. | |
g.HandleKeyPress() | |
// If the position has changed, we need to regenerate the tiles. | |
if g.currentPos.justChanged { | |
// Create a new perlin noise generator. Idk what these do, I fiddled until they worked. | |
noise := perlin.NewPerlin(2, 2, 3, 100) | |
// This is a for loop, `range` is a utility for looping over arrays. `i` here is an int, | |
// this is equivalent to i := 0; i < len(g.tiles); i++. | |
for i := range g.tiles { | |
for j := range g.tiles[i] { | |
// Current position is the current x, y in the tile plus our position offset. | |
x := i + g.currentPos.x | |
y := j + g.currentPos.y | |
// Perlin noise generator will not work unless you divide the position by 10 for some reason. | |
g.tiles[i][j] = noise.Noise2D(float64(x) / 10., float64(y) / 10.) | |
} | |
} | |
// We've updated the position, clear the `justChanged` flag. | |
g.currentPos.ClearChanged() | |
} | |
return nil | |
} | |
/// Checks to see if any of the arrow keys are pressed currently. If they are, moves the position as necessary. | |
func (g *Game) HandleKeyPress() { | |
if ebiten.IsKeyPressed(ebiten.KeyUp) { | |
g.currentPos.Increment(0, -1) | |
} else if ebiten.IsKeyPressed(ebiten.KeyDown) { | |
g.currentPos.Increment(0, 1) | |
} else if ebiten.IsKeyPressed(ebiten.KeyLeft) { | |
g.currentPos.Increment(-1, 0) | |
} else if ebiten.IsKeyPressed(ebiten.KeyRight) { | |
g.currentPos.Increment(1, 0) | |
} | |
} | |
/// This is another one of the required functions for ebiten. `Draw` is called on every frame, | |
/// after `Update`. It's where we draw things on the screen. | |
func (g *Game) Draw(screen *ebiten.Image) { | |
// Range returns two values. The first is the index, the second is the value. | |
// This is equivalent to for i := 0; i < len(g.tiles); i++ { tileRow := g.tiles[i] } | |
for i, tileRow := range g.tiles { | |
for j, tile := range tileRow { | |
// Create a color. RGB values are white, the last value (the alpha) is based on the perlin noise value. | |
color := color.RGBA{0xff, 0xff, 0xff, uint8((tile + 1.) * 100.)} | |
// Actually draw the rectangle. The `screen` is the rectangle we're drawing inside, the third + fourth arguments | |
// are the x, y position of the rectangle, and the fifth + sixth arguments are the width and height of the rectangle. | |
ebitenutil.DrawRect(screen, float64(i * TILE_SIZE), float64(j * TILE_SIZE), TILE_SIZE, TILE_SIZE, color) | |
} | |
} | |
} | |
/// Last function required by embitten. This just returns the screen height and width. | |
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { | |
return SCREEN_SIZE, SCREEN_SIZE | |
} | |
/// Function that creates a new position. | |
func NewPosition() Position { | |
return Position { | |
justChanged: true, | |
} | |
} | |
/// Function that creates a new `Game` struct. | |
func NewGame() Game { | |
// Create an empty 2D array of tiles. `make` is like C's `malloc`, except Go is garbage collected. | |
// Once we stop using this value, it'll collect it for us automatically. | |
tiles := make([][]float64, NUM_TILES) | |
for i := range tiles { | |
tiles[i] = make([]float64, NUM_TILES) | |
} | |
return Game { | |
tiles: tiles, | |
currentPos: NewPosition(), | |
} | |
} | |
/// `main` is called when we start the program, it's like C's `main`. | |
func main() { | |
// Set the window size and title. | |
ebiten.SetWindowSize(SCREEN_SIZE, SCREEN_SIZE) | |
ebiten.SetWindowTitle("Hello, World!") | |
// Create a new instance of our game struct. | |
game := NewGame() | |
// Pass the struct to ebiten to run. ebiten will take care of calling our functions as needed. | |
if err := ebiten.RunGame(&game); err != nil { | |
log.Fatal(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment