mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-12 12:08:58 +01:00
docs: Update
This commit is contained in:
parent
80e4ee0998
commit
4f67af726b
@ -82,7 +82,7 @@ func update(screen *ebiten.Image) error {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
ebitenImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "ebiten.png"), ebiten.FilterNearest)
|
ebitenImage, _, err = ebitenutil.NewImageFromFile("_resources/images/ebiten.png", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -120,11 +120,11 @@ func playerBarRect() (x, y, w, h int) {
|
|||||||
|
|
||||||
func NewPlayer(audioContext *audio.Context) (*Player, error) {
|
func NewPlayer(audioContext *audio.Context) (*Player, error) {
|
||||||
const bytesPerSample = 4 // TODO: This should be defined in audio package
|
const bytesPerSample = 4 // TODO: This should be defined in audio package
|
||||||
wavF, err := ebitenutil.OpenFile(ebitenutil.JoinStringsIntoFilePath("_resources", "audio", "jab.wav"))
|
wavF, err := ebitenutil.OpenFile("_resources/audio/jab.wav")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mp3F, err := ebitenutil.OpenFile(ebitenutil.JoinStringsIntoFilePath("_resources", "audio", "classic.mp3"))
|
mp3F, err := ebitenutil.OpenFile("_resources/audio/classic.mp3")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -45,13 +45,16 @@ import (
|
|||||||
var (
|
var (
|
||||||
count int
|
count int
|
||||||
highDPIImage *ebiten.Image
|
highDPIImage *ebiten.Image
|
||||||
|
highDPIImageCh = make(chan *ebiten.Image)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// licensed under Public Domain
|
// Licensed under Public Domain
|
||||||
// https://commons.wikimedia.org/wiki/File:As08-16-2593.jpg
|
// https://commons.wikimedia.org/wiki/File:As08-16-2593.jpg
|
||||||
const url = "https://upload.wikimedia.org/wikipedia/commons/1/1f/As08-16-2593.jpg"
|
const url = "https://upload.wikimedia.org/wikipedia/commons/1/1f/As08-16-2593.jpg"
|
||||||
|
|
||||||
|
// Load the image asynchronously.
|
||||||
|
go func() {
|
||||||
res, err := http.Get(url)
|
res, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -63,17 +66,35 @@ func init() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
highDPIImage, err = ebiten.NewImageFromImage(img, ebiten.FilterLinear)
|
eimg, err := ebiten.NewImageFromImage(img, ebiten.FilterLinear)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
highDPIImageCh <- eimg
|
||||||
|
close(highDPIImageCh)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
|
if highDPIImage == nil {
|
||||||
|
// Use select and 'default' clause for non-blocking receiving.
|
||||||
|
select {
|
||||||
|
case img := <-highDPIImageCh:
|
||||||
|
highDPIImage = img
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ebiten.IsRunningSlowly() {
|
if ebiten.IsRunningSlowly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if highDPIImage == nil {
|
||||||
|
ebitenutil.DebugPrint(screen, "Loading the image...")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
scale := ebiten.DeviceScaleFactor()
|
scale := ebiten.DeviceScaleFactor()
|
||||||
sw, sh := screen.Size()
|
sw, sh := screen.Size()
|
||||||
|
|
||||||
@ -85,6 +106,7 @@ func update(screen *ebiten.Image) error {
|
|||||||
|
|
||||||
// The image is just too big. Adjust the scale.
|
// The image is just too big. Adjust the scale.
|
||||||
op.GeoM.Scale(0.25, 0.25)
|
op.GeoM.Scale(0.25, 0.25)
|
||||||
|
|
||||||
// Scale the image by the device ratio so that the rendering result can be same
|
// Scale the image by the device ratio so that the rendering result can be same
|
||||||
// on various (diffrent-DPI) environments.
|
// on various (diffrent-DPI) environments.
|
||||||
op.GeoM.Scale(scale, scale)
|
op.GeoM.Scale(scale, scale)
|
||||||
|
@ -47,15 +47,17 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hueInt = 0
|
hue128 = 0
|
||||||
saturationInt = 128
|
saturation128 = 128
|
||||||
valueInt = 128
|
value128 = 128
|
||||||
|
|
||||||
inverted = false
|
inverted = false
|
||||||
|
|
||||||
prevPressedI = false
|
prevPressedI = false
|
||||||
gophersImage *ebiten.Image
|
gophersImage *ebiten.Image
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// clamp clamps v to the range [min, max].
|
||||||
func clamp(v, min, max int) int {
|
func clamp(v, min, max int) int {
|
||||||
if min > max {
|
if min > max {
|
||||||
panic("min must <= max")
|
panic("min must <= max")
|
||||||
@ -70,25 +72,30 @@ func clamp(v, min, max int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
|
// Adjust HSV values along with the user's input.
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyQ) {
|
if ebiten.IsKeyPressed(ebiten.KeyQ) {
|
||||||
hueInt--
|
hue128--
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyW) {
|
if ebiten.IsKeyPressed(ebiten.KeyW) {
|
||||||
hueInt++
|
hue128++
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
if ebiten.IsKeyPressed(ebiten.KeyA) {
|
||||||
saturationInt--
|
saturation128--
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyS) {
|
if ebiten.IsKeyPressed(ebiten.KeyS) {
|
||||||
saturationInt++
|
saturation128++
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyZ) {
|
if ebiten.IsKeyPressed(ebiten.KeyZ) {
|
||||||
valueInt--
|
value128--
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyX) {
|
if ebiten.IsKeyPressed(ebiten.KeyX) {
|
||||||
valueInt++
|
value128++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hue128 = clamp(hue128, -256, 256)
|
||||||
|
saturation128 = clamp(saturation128, 0, 256)
|
||||||
|
value128 = clamp(value128, 0, 256)
|
||||||
|
|
||||||
pressedI := ebiten.IsKeyPressed(ebiten.KeyI)
|
pressedI := ebiten.IsKeyPressed(ebiten.KeyI)
|
||||||
if pressedI && !prevPressedI {
|
if pressedI && !prevPressedI {
|
||||||
inverted = !inverted
|
inverted = !inverted
|
||||||
@ -98,18 +105,19 @@ func update(screen *ebiten.Image) error {
|
|||||||
if ebiten.IsRunningSlowly() {
|
if ebiten.IsRunningSlowly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
hueInt = clamp(hueInt, -256, 256)
|
|
||||||
saturationInt = clamp(saturationInt, 0, 256)
|
|
||||||
valueInt = clamp(valueInt, 0, 256)
|
|
||||||
|
|
||||||
|
// Center the image on the screen.
|
||||||
w, h := gophersImage.Size()
|
w, h := gophersImage.Size()
|
||||||
op := &ebiten.DrawImageOptions{}
|
op := &ebiten.DrawImageOptions{}
|
||||||
op.GeoM.Translate(float64(screenWidth-w)/2, float64(screenHeight-h)/2)
|
op.GeoM.Translate(float64(screenWidth-w)/2, float64(screenHeight-h)/2)
|
||||||
|
|
||||||
hue := float64(hueInt) * 2 * math.Pi / 128
|
// Change HSV.
|
||||||
saturation := float64(saturationInt) / 128
|
hue := float64(hue128) * 2 * math.Pi / 128
|
||||||
value := float64(valueInt) / 128
|
saturation := float64(saturation128) / 128
|
||||||
|
value := float64(value128) / 128
|
||||||
op.ColorM.ChangeHSV(hue, saturation, value)
|
op.ColorM.ChangeHSV(hue, saturation, value)
|
||||||
|
|
||||||
|
// Invert the color.
|
||||||
if inverted {
|
if inverted {
|
||||||
op.ColorM.Scale(-1, -1, -1, 1)
|
op.ColorM.Scale(-1, -1, -1, 1)
|
||||||
op.ColorM.Translate(1, 1, 1, 0)
|
op.ColorM.Translate(1, 1, 1, 0)
|
||||||
@ -117,6 +125,7 @@ func update(screen *ebiten.Image) error {
|
|||||||
|
|
||||||
screen.DrawImage(gophersImage, op)
|
screen.DrawImage(gophersImage, op)
|
||||||
|
|
||||||
|
// Draw the text of the current status.
|
||||||
msgInverted := "false"
|
msgInverted := "false"
|
||||||
if inverted {
|
if inverted {
|
||||||
msgInverted = "true"
|
msgInverted = "true"
|
||||||
@ -131,7 +140,7 @@ Inverted: %s [I]`, hue, saturation, value, msgInverted)
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
gophersImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "gophers.jpg"), ebiten.FilterNearest)
|
gophersImage, _, err = ebitenutil.NewImageFromFile("_resources/images/gophers.jpg", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -52,20 +52,26 @@ var (
|
|||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
count++
|
count++
|
||||||
|
|
||||||
if ebiten.IsRunningSlowly() {
|
if ebiten.IsRunningSlowly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Center the image on the screen.
|
||||||
w, h := gophersImage.Size()
|
w, h := gophersImage.Size()
|
||||||
op := &ebiten.DrawImageOptions{}
|
op := &ebiten.DrawImageOptions{}
|
||||||
op.GeoM.Translate(float64(screenWidth-w)/2, float64(screenHeight-h)/2)
|
op.GeoM.Translate(float64(screenWidth-w)/2, float64(screenHeight-h)/2)
|
||||||
|
|
||||||
|
// Rotate the hue.
|
||||||
op.ColorM.RotateHue(float64(count%360) * 2 * math.Pi / 360)
|
op.ColorM.RotateHue(float64(count%360) * 2 * math.Pi / 360)
|
||||||
|
|
||||||
screen.DrawImage(gophersImage, op)
|
screen.DrawImage(gophersImage, op)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
gophersImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "gophers.jpg"), ebiten.FilterNearest)
|
gophersImage, _, err = ebitenutil.NewImageFromFile("_resources/images/gophers.jpg", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
"github.com/hajimehoshi/ebiten/ebitenutil"
|
"github.com/hajimehoshi/ebiten/ebitenutil"
|
||||||
@ -47,10 +46,19 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
theViewport = &viewport{}
|
|
||||||
bgImage *ebiten.Image
|
bgImage *ebiten.Image
|
||||||
repeatedBgImage *ebiten.Image
|
)
|
||||||
groundImage *ebiten.Image
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
bgImage, _, err = ebitenutil.NewImageFromFile("_resources/images/tile.png", ebiten.FilterNearest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
theViewport = &viewport{}
|
||||||
)
|
)
|
||||||
|
|
||||||
type viewport struct {
|
type viewport struct {
|
||||||
@ -58,80 +66,48 @@ type viewport struct {
|
|||||||
y16 int
|
y16 int
|
||||||
}
|
}
|
||||||
|
|
||||||
func round(x float64) float64 {
|
|
||||||
return math.Floor(x + 0.5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *viewport) Move() {
|
func (p *viewport) Move() {
|
||||||
w, h := bgImage.Size()
|
w, h := bgImage.Size()
|
||||||
mx := w * 16
|
maxX16 := w * 16
|
||||||
my := h * 16
|
maxY16 := h * 16
|
||||||
|
|
||||||
p.x16 += w / 32
|
p.x16 += w / 32
|
||||||
p.y16 += h / 32
|
p.y16 += h / 32
|
||||||
|
p.x16 %= maxX16
|
||||||
for mx <= p.x16 {
|
p.y16 %= maxY16
|
||||||
p.x16 -= mx
|
|
||||||
}
|
|
||||||
for my <= p.y16 {
|
|
||||||
p.y16 -= my
|
|
||||||
}
|
|
||||||
for p.x16 < 0 {
|
|
||||||
p.x16 += mx
|
|
||||||
}
|
|
||||||
for p.y16 < 0 {
|
|
||||||
p.y16 += my
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *viewport) Position() (int, int) {
|
func (p *viewport) Position() (int, int) {
|
||||||
return p.x16, p.y16
|
return p.x16, p.y16
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateGroundImage(ground *ebiten.Image) {
|
|
||||||
ground.Clear()
|
|
||||||
x16, y16 := theViewport.Position()
|
|
||||||
op := &ebiten.DrawImageOptions{}
|
|
||||||
op.GeoM.Translate(float64(-x16)/16, float64(-y16)/16)
|
|
||||||
ground.DrawImage(repeatedBgImage, op)
|
|
||||||
}
|
|
||||||
|
|
||||||
func drawGroundImage(screen *ebiten.Image, ground *ebiten.Image) {
|
|
||||||
op := &ebiten.DrawImageOptions{}
|
|
||||||
screen.DrawImage(ground, op)
|
|
||||||
}
|
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
theViewport.Move()
|
theViewport.Move()
|
||||||
|
|
||||||
if ebiten.IsRunningSlowly() {
|
if ebiten.IsRunningSlowly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
updateGroundImage(groundImage)
|
|
||||||
drawGroundImage(screen, groundImage)
|
|
||||||
|
|
||||||
msg := fmt.Sprintf("FPS: %0.2f", ebiten.CurrentFPS())
|
x16, y16 := theViewport.Position()
|
||||||
ebitenutil.DebugPrint(screen, msg)
|
offsetX, offsetY := float64(-x16)/16, float64(-y16)/16
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
// Draw bgImage on the screen repeatedly.
|
||||||
var err error
|
const repeat = 3
|
||||||
bgImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "tile.png"), ebiten.FilterNearest)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
w, h := bgImage.Size()
|
w, h := bgImage.Size()
|
||||||
const repeat = 5
|
|
||||||
repeatedBgImage, _ = ebiten.NewImage(w*repeat, h*repeat, ebiten.FilterNearest)
|
|
||||||
for j := 0; j < repeat; j++ {
|
for j := 0; j < repeat; j++ {
|
||||||
for i := 0; i < repeat; i++ {
|
for i := 0; i < repeat; i++ {
|
||||||
op := &ebiten.DrawImageOptions{}
|
op := &ebiten.DrawImageOptions{}
|
||||||
op.GeoM.Translate(float64(w*i), float64(h*j))
|
op.GeoM.Translate(float64(w*i), float64(h*j))
|
||||||
repeatedBgImage.DrawImage(bgImage, op)
|
op.GeoM.Translate(offsetX, offsetY)
|
||||||
|
screen.DrawImage(bgImage, op)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
groundImage, _ = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
|
|
||||||
|
|
||||||
|
ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f", ebiten.CurrentFPS()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Infinite Scroll (Ebiten Demo)"); err != nil {
|
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Infinite Scroll (Ebiten Demo)"); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ var keyboardImage *ebiten.Image
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
keyboardImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "keyboard", "keyboard.png"), ebiten.FilterNearest)
|
keyboardImage, _, err = ebitenutil.NewImageFromFile("_resources/images/keyboard/keyboard.png", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -86,15 +86,7 @@ var keyNames = map[ebiten.Key]string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
if ebiten.IsRunningSlowly() {
|
// Collect pressed keys' names.
|
||||||
return nil
|
|
||||||
}
|
|
||||||
const offsetX, offsetY = 24, 40
|
|
||||||
op := &ebiten.DrawImageOptions{}
|
|
||||||
op.GeoM.Translate(offsetX, offsetY)
|
|
||||||
op.ColorM.Scale(0.5, 0.5, 0.5, 1)
|
|
||||||
screen.DrawImage(keyboardImage, op)
|
|
||||||
|
|
||||||
pressed := []string{}
|
pressed := []string{}
|
||||||
for i := 0; i <= 9; i++ {
|
for i := 0; i <= 9; i++ {
|
||||||
if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.Key0) {
|
if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.Key0) {
|
||||||
@ -102,12 +94,12 @@ func update(screen *ebiten.Image) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for c := 'A'; c <= 'Z'; c++ {
|
for c := 'A'; c <= 'Z'; c++ {
|
||||||
if ebiten.IsKeyPressed(ebiten.Key(c) - 'A' + ebiten.KeyA) {
|
if ebiten.IsKeyPressed(ebiten.KeyA + ebiten.Key(c-'A')) {
|
||||||
pressed = append(pressed, string(c))
|
pressed = append(pressed, string(c))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 1; i <= 12; i++ {
|
for i := 1; i <= 12; i++ {
|
||||||
if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.KeyF1 - 1) {
|
if ebiten.IsKeyPressed(ebiten.KeyF1 + ebiten.Key(i-1)) {
|
||||||
pressed = append(pressed, "F"+strconv.Itoa(i))
|
pressed = append(pressed, "F"+strconv.Itoa(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,6 +109,22 @@ func update(screen *ebiten.Image) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ebiten.IsRunningSlowly() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
offsetX = 24
|
||||||
|
offsetY = 40
|
||||||
|
)
|
||||||
|
|
||||||
|
// Draw the base (grayed) keyboard image.
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Translate(offsetX, offsetY)
|
||||||
|
op.ColorM.Scale(0.5, 0.5, 0.5, 1)
|
||||||
|
screen.DrawImage(keyboardImage, op)
|
||||||
|
|
||||||
|
// Draw the highlighted keys.
|
||||||
op = &ebiten.DrawImageOptions{}
|
op = &ebiten.DrawImageOptions{}
|
||||||
for _, p := range pressed {
|
for _, p := range pressed {
|
||||||
op.GeoM.Reset()
|
op.GeoM.Reset()
|
||||||
|
@ -39,40 +39,50 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
)
|
)
|
||||||
|
|
||||||
// World represents the game state
|
// World represents the game state.
|
||||||
type World struct {
|
type World struct {
|
||||||
area [][]bool
|
area [][]bool
|
||||||
rnd *rand.Rand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorld creates a new world
|
func newArea(width, height int) [][]bool {
|
||||||
func NewWorld(width, height int) *World {
|
a := make([][]bool, height)
|
||||||
world := World{
|
for i := 0; i < height; i++ {
|
||||||
area: makeArea(width, height),
|
a[i] = make([]bool, width)
|
||||||
rnd: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
||||||
}
|
}
|
||||||
return &world
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomSeed inits world with a random state
|
// NewWorld creates a new world.
|
||||||
func (w *World) RandomSeed(limit int) {
|
func NewWorld(width, height int, maxInitLiveCells int) *World {
|
||||||
|
w := &World{
|
||||||
|
area: newArea(width, height),
|
||||||
|
}
|
||||||
|
w.init(maxInitLiveCells)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// init inits world with a random state.
|
||||||
|
func (w *World) init(maxLiveCells int) {
|
||||||
height := len(w.area)
|
height := len(w.area)
|
||||||
width := len(w.area[0])
|
width := len(w.area[0])
|
||||||
for i := 0; i < limit; i++ {
|
for i := 0; i < maxLiveCells; i++ {
|
||||||
x := w.rnd.Intn(width)
|
x := rand.Intn(width)
|
||||||
y := w.rnd.Intn(height)
|
y := rand.Intn(height)
|
||||||
w.area[y][x] = true
|
w.area[y][x] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Progress game state by one tick
|
// Update game state by one tick.
|
||||||
func (w *World) Progress() {
|
func (w *World) Update() {
|
||||||
height := len(w.area)
|
height := len(w.area)
|
||||||
width := len(w.area[0])
|
width := len(w.area[0])
|
||||||
next := makeArea(width, height)
|
next := newArea(width, height)
|
||||||
for y := 0; y < height; y++ {
|
for y := 0; y < height; y++ {
|
||||||
for x := 0; x < width; x++ {
|
for x := 0; x < width; x++ {
|
||||||
|
|
||||||
pop := neighbourCount(w.area, x, y)
|
pop := neighbourCount(w.area, x, y)
|
||||||
switch {
|
switch {
|
||||||
case pop < 2:
|
case pop < 2:
|
||||||
@ -100,66 +110,63 @@ func (w *World) Progress() {
|
|||||||
w.area = next
|
w.area = next
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrawImage paints current game state
|
// Draw paints current game state.
|
||||||
func (w *World) DrawImage(pix []uint8) {
|
func (w *World) Draw(pix []byte) {
|
||||||
height := len(w.area)
|
height := len(w.area)
|
||||||
width := len(w.area[0])
|
width := len(w.area[0])
|
||||||
for y := 0; y < height; y++ {
|
for y := 0; y < height; y++ {
|
||||||
for x := 0; x < width; x++ {
|
for x := 0; x < width; x++ {
|
||||||
pos := 4*y*width + 4*x
|
idx := 4*y*width + 4*x
|
||||||
if w.area[y][x] {
|
if w.area[y][x] {
|
||||||
pix[pos] = 0xff
|
pix[idx] = 0xff
|
||||||
pix[pos+1] = 0xff
|
pix[idx+1] = 0xff
|
||||||
pix[pos+2] = 0xff
|
pix[idx+2] = 0xff
|
||||||
pix[pos+3] = 0xff
|
pix[idx+3] = 0xff
|
||||||
} else {
|
} else {
|
||||||
pix[pos] = 0
|
pix[idx] = 0
|
||||||
pix[pos+1] = 0
|
pix[idx+1] = 0
|
||||||
pix[pos+2] = 0
|
pix[idx+2] = 0
|
||||||
pix[pos+3] = 0
|
pix[idx+3] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// neighbourCount calculates the Moore neighborhood of x, y
|
func max(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// neighbourCount calculates the Moore neighborhood of (x, y).
|
||||||
func neighbourCount(a [][]bool, x, y int) int {
|
func neighbourCount(a [][]bool, x, y int) int {
|
||||||
height := len(a)
|
w := len(a[0])
|
||||||
width := len(a[0])
|
h := len(a)
|
||||||
lowX := 0
|
minI := max(x-1, 0)
|
||||||
if x > 0 {
|
minJ := max(y-1, 0)
|
||||||
lowX = x - 1
|
maxI := min(x+1, w-1)
|
||||||
}
|
maxJ := min(y+1, h-1)
|
||||||
lowY := 0
|
|
||||||
if y > 0 {
|
|
||||||
lowY = y - 1
|
|
||||||
}
|
|
||||||
highX := width - 1
|
|
||||||
if x < width-1 {
|
|
||||||
highX = x + 1
|
|
||||||
}
|
|
||||||
highY := height - 1
|
|
||||||
if y < height-1 {
|
|
||||||
highY = y + 1
|
|
||||||
}
|
|
||||||
near := 0
|
|
||||||
for pY := lowY; pY <= highY; pY++ {
|
|
||||||
for pX := lowX; pX <= highX; pX++ {
|
|
||||||
if !(pX == x && pY == y) && a[pY][pX] {
|
|
||||||
near++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return near
|
c := 0
|
||||||
|
for j := minJ; j <= maxJ; j++ {
|
||||||
|
for i := minI; i <= maxI; i++ {
|
||||||
|
if i == x && j == y {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
if a[j][i] {
|
||||||
func makeArea(width, height int) [][]bool {
|
c++
|
||||||
area := make([][]bool, height)
|
|
||||||
for i := 0; i < height; i++ {
|
|
||||||
area[i] = make([]bool, width)
|
|
||||||
}
|
}
|
||||||
return area
|
}
|
||||||
|
}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -168,23 +175,24 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
world = NewWorld(screenWidth, screenHeight)
|
world = NewWorld(screenWidth, screenHeight, int((screenWidth*screenHeight)/10))
|
||||||
pixels = make([]uint8, screenWidth*screenHeight*4)
|
pixels = make([]byte, screenWidth*screenHeight*4)
|
||||||
)
|
)
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
world.Progress()
|
world.Update()
|
||||||
|
|
||||||
if ebiten.IsRunningSlowly() {
|
if ebiten.IsRunningSlowly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
world.DrawImage(pixels)
|
|
||||||
|
world.Draw(pixels)
|
||||||
screen.ReplacePixels(pixels)
|
screen.ReplacePixels(pixels)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
world.RandomSeed(int((screenWidth * screenHeight) / 10))
|
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Game of Life (Ebiten Demo)"); err != nil {
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 2.0, "Game of Life (Ebiten Demo)"); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gophersImage *ebiten.Image
|
bgImage *ebiten.Image
|
||||||
fiveyearsImage *ebiten.Image
|
fgImage *ebiten.Image
|
||||||
maskImage *ebiten.Image
|
maskedFgImage *ebiten.Image
|
||||||
spotLightImage *ebiten.Image
|
spotLightImage *ebiten.Image
|
||||||
spotLightX = 0
|
spotLightX = 0
|
||||||
spotLightY = 0
|
spotLightY = 0
|
||||||
@ -58,6 +58,36 @@ var (
|
|||||||
spotLightVY = 1
|
spotLightVY = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
bgImage, _, err = ebitenutil.NewImageFromFile("_resources/images/gophers.jpg", ebiten.FilterNearest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fgImage, _, err = ebitenutil.NewImageFromFile("_resources/images/fiveyears.jpg", ebiten.FilterNearest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
maskedFgImage, _ = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
|
||||||
|
|
||||||
|
// Initialize the spot light image.
|
||||||
|
const r = 64
|
||||||
|
alphas := image.Point{r * 2, r * 2}
|
||||||
|
a := image.NewAlpha(image.Rectangle{image.ZP, alphas})
|
||||||
|
for j := 0; j < alphas.Y; j++ {
|
||||||
|
for i := 0; i < alphas.X; i++ {
|
||||||
|
// d is the distance between (i, j) and the (circle) center.
|
||||||
|
d := math.Sqrt(float64((i-r)*(i-r) + (j-r)*(j-r)))
|
||||||
|
// Alphas around the center are 0 and values outside of the circle are 0xff.
|
||||||
|
b := uint8(max(0, min(0xff, int(3*d*0xff/r)-2*0xff)))
|
||||||
|
a.SetAlpha(i, j, color.Alpha{b})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spotLightImage, _ = ebiten.NewImageFromImage(a, ebiten.FilterNearest)
|
||||||
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
spotLightX += spotLightVX
|
spotLightX += spotLightVX
|
||||||
spotLightY += spotLightVY
|
spotLightY += spotLightVY
|
||||||
@ -79,22 +109,34 @@ func update(screen *ebiten.Image) error {
|
|||||||
spotLightY = -spotLightY + 2*maxY
|
spotLightY = -spotLightY + 2*maxY
|
||||||
spotLightVY = -spotLightVY
|
spotLightVY = -spotLightVY
|
||||||
}
|
}
|
||||||
|
|
||||||
if ebiten.IsRunningSlowly() {
|
if ebiten.IsRunningSlowly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
maskImage.Clear()
|
|
||||||
|
|
||||||
|
// Reset the maskedFgImage.
|
||||||
|
maskedFgImage.Fill(color.White)
|
||||||
op := &ebiten.DrawImageOptions{}
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.CompositeMode = ebiten.CompositeModeCopy
|
||||||
op.GeoM.Translate(float64(spotLightX), float64(spotLightY))
|
op.GeoM.Translate(float64(spotLightX), float64(spotLightY))
|
||||||
maskImage.DrawImage(spotLightImage, op)
|
maskedFgImage.DrawImage(spotLightImage, op)
|
||||||
|
|
||||||
|
// Use 'source-in' composite mode so that the source image (fgImage) is used but the alpha
|
||||||
|
// is determined by the destination image (maskedFgImage).
|
||||||
|
//
|
||||||
|
// The result image is the source image with the destination alpha. In maskedFgImage, alpha
|
||||||
|
// values in the hole is 0 and alpha values in other places are 0xff. As a result, the
|
||||||
|
// maskedFgImage draws the source image with a hole that shape is spotLightImage. Note that
|
||||||
|
// RGB values in the destination image are ignored.
|
||||||
|
//
|
||||||
|
// See also https://www.w3.org/TR/compositing-1/#porterduffcompositingoperators_srcin.
|
||||||
op = &ebiten.DrawImageOptions{}
|
op = &ebiten.DrawImageOptions{}
|
||||||
op.CompositeMode = ebiten.CompositeModeSourceOut
|
op.CompositeMode = ebiten.CompositeModeSourceIn
|
||||||
maskImage.DrawImage(fiveyearsImage, op)
|
maskedFgImage.DrawImage(fgImage, op)
|
||||||
|
|
||||||
screen.Fill(color.RGBA{0x00, 0x00, 0x80, 0xff})
|
screen.Fill(color.RGBA{0x00, 0x00, 0x80, 0xff})
|
||||||
screen.DrawImage(gophersImage, &ebiten.DrawImageOptions{})
|
screen.DrawImage(bgImage, &ebiten.DrawImageOptions{})
|
||||||
screen.DrawImage(maskImage, &ebiten.DrawImageOptions{})
|
screen.DrawImage(maskedFgImage, &ebiten.DrawImageOptions{})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -114,28 +156,6 @@ func min(a, b int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
|
||||||
gophersImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "gophers.jpg"), ebiten.FilterNearest)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
fiveyearsImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "fiveyears.jpg"), ebiten.FilterNearest)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
maskImage, _ = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
|
|
||||||
|
|
||||||
as := image.Point{128, 128}
|
|
||||||
a := image.NewAlpha(image.Rectangle{image.ZP, as})
|
|
||||||
for j := 0; j < as.Y; j++ {
|
|
||||||
for i := 0; i < as.X; i++ {
|
|
||||||
r := as.X / 2
|
|
||||||
d := math.Sqrt(float64((i-r)*(i-r) + (j-r)*(j-r)))
|
|
||||||
b := uint8(max(0, min(0xff, 3*0xff-int(d*3*0xff)/r)))
|
|
||||||
a.SetAlpha(i, j, color.Alpha{b})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spotLightImage, _ = ebiten.NewImageFromImage(a, ebiten.FilterNearest)
|
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Masking (Ebiten Demo)"); err != nil {
|
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Masking (Ebiten Demo)"); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -51,13 +51,26 @@ var (
|
|||||||
gophersRenderTarget *ebiten.Image
|
gophersRenderTarget *ebiten.Image
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
gophersImage, _, err = ebitenutil.NewImageFromFile("_resources/images/gophers.jpg", ebiten.FilterNearest)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
func update(screen *ebiten.Image) error {
|
||||||
if ebiten.IsRunningSlowly() {
|
if ebiten.IsRunningSlowly() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shrink the image once.
|
||||||
op := &ebiten.DrawImageOptions{}
|
op := &ebiten.DrawImageOptions{}
|
||||||
op.GeoM.Scale(1.0/mosaicRatio, 1.0/mosaicRatio)
|
op.GeoM.Scale(1.0/mosaicRatio, 1.0/mosaicRatio)
|
||||||
gophersRenderTarget.DrawImage(gophersImage, op)
|
gophersRenderTarget.DrawImage(gophersImage, op)
|
||||||
|
|
||||||
|
// Enlarge the shrunk image.
|
||||||
|
// The filter is the nearest filter, so the result will be mosaic.
|
||||||
op = &ebiten.DrawImageOptions{}
|
op = &ebiten.DrawImageOptions{}
|
||||||
op.GeoM.Scale(mosaicRatio, mosaicRatio)
|
op.GeoM.Scale(mosaicRatio, mosaicRatio)
|
||||||
screen.DrawImage(gophersRenderTarget, op)
|
screen.DrawImage(gophersRenderTarget, op)
|
||||||
@ -65,11 +78,6 @@ func update(screen *ebiten.Image) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
|
||||||
gophersImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "gophers.jpg"), ebiten.FilterNearest)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
w, h := gophersImage.Size()
|
w, h := gophersImage.Size()
|
||||||
gophersRenderTarget, _ = ebiten.NewImage(w/mosaicRatio, h/mosaicRatio, ebiten.FilterNearest)
|
gophersRenderTarget, _ = ebiten.NewImage(w/mosaicRatio, h/mosaicRatio, ebiten.FilterNearest)
|
||||||
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Mosaic (Ebiten Demo)"); err != nil {
|
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Mosaic (Ebiten Demo)"); err != nil {
|
||||||
|
@ -73,7 +73,7 @@ func update(screen *ebiten.Image) error {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
gophersImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "gophers.jpg"), ebiten.FilterNearest)
|
gophersImage, _, err = ebitenutil.NewImageFromFile("_resources/images/gophers.jpg", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
f, err := ebitenutil.OpenFile(ebitenutil.JoinStringsIntoFilePath("_resources", "fonts", "arcade_n.ttf"))
|
f, err := ebitenutil.OpenFile("_resources/fonts/arcade_n.ttf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ func update(screen *ebiten.Image) error {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
gophersImage, _, err = ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "gophers.jpg"), ebiten.FilterNearest)
|
gophersImage, _, err = ebitenutil.NewImageFromFile("_resources/images/gophers.jpg", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ Press <- or -> to change the number of sprites`, ebiten.CurrentFPS(), spri
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
img, _, err := ebitenutil.NewImageFromFile(ebitenutil.JoinStringsIntoFilePath("_resources", "images", "ebiten.png"), ebiten.FilterNearest)
|
img, _, err := ebitenutil.NewImageFromFile("_resources/images/ebiten.png", ebiten.FilterNearest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user