diff --git a/_docs/gen.go b/_docs/gen.go
index 8d36e6ed5..8e1e6c2bb 100644
--- a/_docs/gen.go
+++ b/_docs/gen.go
@@ -176,29 +176,37 @@ func versions() string {
return fmt.Sprintf("v%s (dev: v%s)", stableVersion, devVersion)
}
-var examples = []example{
- {"alphablending", 320, 240},
- {"audio", 320, 240},
- {"font", 320, 240},
- {"highdpi", 320, 240},
- {"hsv", 320, 240},
- {"hue", 320, 240},
- {"gamepad", 320, 240},
- {"infinitescroll", 320, 240},
- {"keyboard", 320, 240},
- {"life", 320, 240},
- {"masking", 320, 240},
- {"mosaic", 320, 240},
- {"noise", 320, 240},
- {"paint", 320, 240},
- {"perspective", 320, 240},
- {"piano", 320, 240},
- {"rotate", 320, 240},
- {"sprites", 320, 240},
- {"typewriter", 320, 240},
- {"2048", 210, 300},
- {"blocks", 256, 240},
-}
+var (
+ graphicsExamples = []example{
+ {"alphablending", 320, 240},
+ {"font", 320, 240},
+ {"highdpi", 320, 240},
+ {"hsv", 320, 240},
+ {"hue", 320, 240},
+ {"infinitescroll", 320, 240},
+ {"life", 320, 240},
+ {"masking", 320, 240},
+ {"mosaic", 320, 240},
+ {"noise", 320, 240},
+ {"paint", 320, 240},
+ {"perspective", 320, 240},
+ {"rotate", 320, 240},
+ {"sprites", 320, 240},
+ }
+ inputExamples = []example{
+ {"gamepad", 320, 240},
+ {"keyboard", 320, 240},
+ {"typewriter", 320, 240},
+ }
+ audioExamples = []example{
+ {"audio", 320, 240},
+ {"piano", 320, 240},
+ }
+ gameExamples = []example{
+ {"2048", 210, 300},
+ {"blocks", 256, 240},
+ }
+)
func clear() error {
if err := filepath.Walk("public", func(path string, info os.FileInfo, err error) error {
@@ -253,11 +261,14 @@ func outputMain() error {
}
data := map[string]interface{}{
- "URL": url,
- "Copyright": copyright,
- "StableVersion": stableVersion,
- "DevVersion": devVersion,
- "Examples": examples,
+ "URL": url,
+ "Copyright": copyright,
+ "StableVersion": stableVersion,
+ "DevVersion": devVersion,
+ "GraphicsExamples": graphicsExamples,
+ "InputExamples": inputExamples,
+ "AudioExamples": audioExamples,
+ "GameExamples": gameExamples,
}
return t.Funcs(funcs).Execute(f, data)
}
@@ -355,6 +366,12 @@ func main() {
if err := outputExampleResources(); err != nil {
log.Fatal(err)
}
+
+ examples := []example{}
+ examples = append(examples, graphicsExamples...)
+ examples = append(examples, inputExamples...)
+ examples = append(examples, audioExamples...)
+ examples = append(examples, gameExamples...)
for _, e := range examples {
if err := outputExampleContent(&e); err != nil {
log.Fatal(err)
diff --git a/_docs/index.tmpl.html b/_docs/index.tmpl.html
index fa38848a5..72ab4a728 100644
--- a/_docs/index.tmpl.html
+++ b/_docs/index.tmpl.html
@@ -60,13 +60,39 @@
Examples
+ Graphics
- {{range .Examples -}}
+ {{range .GraphicsExamples -}}
{{- end}}
+ Input
+
+ {{range .InputExamples -}}
+
+
+
+ {{- end}}
+
+ Audio
+
+ {{range .AudioExamples -}}
+
+
+
+ {{- end}}
+
+ Game
+
+ {{range .GameExamples -}}
+
+
+
+ {{- end}}
+
+
The Gopher photographs by Chris Nokleberg are licensed under the Creative Commons 3.0 Attributions License.
Execute the examples
diff --git a/docs/examples/noise.html b/docs/examples/noise.html
index cbf397e65..cb6a780aa 100644
--- a/docs/examples/noise.html
+++ b/docs/examples/noise.html
@@ -62,20 +62,23 @@ func (r *rand) next() uint32 {
return r.w
}
-var randInstance = &rand{12345678, 4185243, 776511, 45411}
+var theRand = &rand{12345678, 4185243, 776511, 45411}
func update(screen *ebiten.Image) error {
+ // Generate the noise with random RGB values.
const l = screenWidth * screenHeight
for i := 0; i < l; i++ {
- x := randInstance.next()
+ x := theRand.next()
noiseImage.Pix[4*i] = uint8(x >> 24)
noiseImage.Pix[4*i+1] = uint8(x >> 16)
noiseImage.Pix[4*i+2] = uint8(x >> 8)
noiseImage.Pix[4*i+3] = 0xff
}
+
if ebiten.IsRunningSlowly() {
return nil
}
+
screen.ReplacePixels(noiseImage.Pix)
ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %f", ebiten.CurrentFPS()))
return nil
diff --git a/docs/examples/paint.html b/docs/examples/paint.html
index 78a2d4664..8980b1c49 100644
--- a/docs/examples/paint.html
+++ b/docs/examples/paint.html
@@ -53,46 +53,12 @@ var (
canvasImage *ebiten.Image
)
-func paint(screen *ebiten.Image, x, y int) {
- op := &ebiten.DrawImageOptions{}
- op.GeoM.Translate(float64(x), float64(y))
- op.ColorM.Scale(1.0, 0.50, 0.125, 1.0)
- theta := 2.0 * math.Pi * float64(count%ebiten.FPS) / ebiten.FPS
- op.ColorM.RotateHue(theta)
- canvasImage.DrawImage(brushImage, op)
-}
-
-func update(screen *ebiten.Image) error {
- drawn := false
- mx, my := ebiten.CursorPosition()
- if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
- paint(screen, mx, my)
- drawn = true
- }
- for _, t := range ebiten.Touches() {
- x, y := t.Position()
- paint(screen, x, y)
- drawn = true
- }
- if drawn {
- count++
- }
- if ebiten.IsRunningSlowly() {
- return nil
- }
- screen.DrawImage(canvasImage, nil)
-
- msg := fmt.Sprintf("(%d, %d)", mx, my)
- for _, t := range ebiten.Touches() {
- x, y := t.Position()
- msg += fmt.Sprintf("\n(%d, %d) touch %d", x, y, t.ID())
- }
- ebitenutil.DebugPrint(screen, msg)
- return nil
-}
-
-func main() {
- const a0, a1, a2 = 0x40, 0xc0, 0xff
+func init() {
+ const (
+ a0 = 0x40
+ a1 = 0xc0
+ a2 = 0xff
+ )
pixels := []uint8{
a0, a1, a1, a0,
a1, a2, a2, a1,
@@ -107,7 +73,55 @@ func main() {
canvasImage, _ = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
canvasImage.Fill(color.White)
+}
+// paint draws the brush on the given canvas image at the position (x, y).
+func paint(canvas *ebiten.Image, x, y int) {
+ op := &ebiten.DrawImageOptions{}
+ op.GeoM.Translate(float64(x), float64(y))
+ // Scale the color and rotate the hue so that colors vary on each frame.
+ op.ColorM.Scale(1.0, 0.50, 0.125, 1.0)
+ theta := 2.0 * math.Pi * float64(count%ebiten.FPS) / ebiten.FPS
+ op.ColorM.RotateHue(theta)
+ canvas.DrawImage(brushImage, op)
+}
+
+func update(screen *ebiten.Image) error {
+ drawn := false
+
+ // Paint the brush by mouse dragging
+ mx, my := ebiten.CursorPosition()
+ if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
+ paint(canvasImage, mx, my)
+ drawn = true
+ }
+
+ // Paint the brush by touches
+ for _, t := range ebiten.Touches() {
+ x, y := t.Position()
+ paint(canvasImage, x, y)
+ drawn = true
+ }
+ if drawn {
+ count++
+ }
+
+ if ebiten.IsRunningSlowly() {
+ return nil
+ }
+
+ screen.DrawImage(canvasImage, nil)
+
+ msg := fmt.Sprintf("(%d, %d)", mx, my)
+ for _, t := range ebiten.Touches() {
+ x, y := t.Position()
+ msg += fmt.Sprintf("\n(%d, %d) touch %d", x, y, t.ID())
+ }
+ ebitenutil.DebugPrint(screen, msg)
+ return nil
+}
+
+func main() {
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Paint (Ebiten Demo)"); err != nil {
log.Fatal(err)
}
diff --git a/docs/examples/perspective.html b/docs/examples/perspective.html
index 2eb9c6130..5c666c102 100644
--- a/docs/examples/perspective.html
+++ b/docs/examples/perspective.html
@@ -53,17 +53,25 @@ func update(screen *ebiten.Image) error {
if ebiten.IsRunningSlowly() {
return nil
}
+
+ // Split the image into horizontal lines and draw them with different scales.
op := &ebiten.DrawImageOptions{}
w, h := gophersImage.Size()
for i := 0; i < h; i++ {
op.GeoM.Reset()
- width := w + i*3/4
- x := ((h - i) * 3 / 4) / 2
- op.GeoM.Scale(float64(width)/float64(w), 1)
- op.GeoM.Translate(float64(x), float64(i))
- maxWidth := float64(w) + float64(h)*3/4
- op.GeoM.Translate(-maxWidth/2, -float64(h)/2)
+
+ // Move the image's center to the upper-left corner.
+ op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
+
+ // Scale each lines and adjust the position.
+ lineW := w + i*3/4
+ x := -float64(lineW) / float64(w) / 2
+ op.GeoM.Scale(float64(lineW)/float64(w), 1)
+ op.GeoM.Translate(x, float64(i))
+
+ // Move the image's center to the screen's center.
op.GeoM.Translate(screenWidth/2, screenHeight/2)
+
r := image.Rect(0, i, w, i+1)
op.SourceRect = &r
screen.DrawImage(gophersImage, op)
diff --git a/docs/examples/piano.html b/docs/examples/piano.html
index 61d82b386..83674efe6 100644
--- a/docs/examples/piano.html
+++ b/docs/examples/piano.html
@@ -47,10 +47,6 @@ import (
"github.com/hajimehoshi/ebiten/text"
)
-const (
- arcadeFontSize = 8
-)
-
var (
arcadeFont font.Face
)
@@ -72,7 +68,10 @@ func init() {
log.Fatal(err)
}
- const dpi = 72
+ const (
+ arcadeFontSize = 8
+ dpi = 72
+ )
arcadeFont = truetype.NewFace(tt, &truetype.Options{
Size: arcadeFontSize,
DPI: dpi,
@@ -84,6 +83,7 @@ const (
screenWidth = 320
screenHeight = 240
sampleRate = 44100
+ baseFreq = 220
)
var audioContext *audio.Context
@@ -96,27 +96,21 @@ func init() {
}
}
-var pcm = make([]float64, 4*sampleRate)
-
-const baseFreq = 220
-
-func init() {
+// pianoAt returns an i-th sample of piano with the given frequency.
+func pianoAt(i int, freq float64) float64 {
+ // Create piano-like waves with multiple sin waves.
amp := []float64{1.0, 0.8, 0.6, 0.4, 0.2}
x := []float64{4.0, 2.0, 1.0, 0.5, 0.25}
- for i := 0; i < len(pcm); i++ {
- v := 0.0
- for j := 0; j < len(amp); j++ {
- a := amp[j] * math.Exp(-5*float64(i)/(x[j]*sampleRate))
- v += a * math.Sin(2.0*math.Pi*float64(i)*baseFreq*float64(j+1)/sampleRate)
- }
- pcm[i] = v / 5.0
+ v := 0.0
+ for j := 0; j < len(amp); j++ {
+ // Decay
+ a := amp[j] * math.Exp(-5*float64(i)*freq/baseFreq/(x[j]*sampleRate))
+ v += a * math.Sin(2.0*math.Pi*float64(i)*freq*float64(j+1)/sampleRate)
}
+ return v / 5.0
}
-var (
- noteCache = map[int][]byte{}
-)
-
+// toBytes returns the 2ch little endian 16bit byte sequence with the given left/right sequence.
func toBytes(l, r []int16) []byte {
if len(l) != len(r) {
panic("len(l) must equal to len(r)")
@@ -131,62 +125,109 @@ func toBytes(l, r []int16) []byte {
return b
}
-func addNote(freq float64, vol float64) {
- // TODO: Call Close method of *audio.Player.
- // However, this works without Close because Close is automatically called when GC
- // collects a *audio.Player object.
- f := int(freq)
- if n, ok := noteCache[f]; ok {
- p, _ := audio.NewPlayerFromBytes(audioContext, n)
- p.Play()
- return
- }
- length := len(pcm) * baseFreq / f
- l := make([]int16, length)
- r := make([]int16, length)
- j := 0
- jj := 0
- for i := 0; i < len(l); i++ {
- p := pcm[j]
- l[i] = int16(p * vol * math.MaxInt16)
- r[i] = l[i]
- jj += f
- j = jj / baseFreq
- }
- n := toBytes(l, r)
- noteCache[f] = n
- p, _ := audio.NewPlayerFromBytes(audioContext, n)
- p.Play()
- return
-}
-
-var keys = []ebiten.Key{
- ebiten.KeyQ,
- ebiten.KeyA,
- ebiten.KeyW,
- ebiten.KeyS,
- ebiten.KeyD,
- ebiten.KeyR,
- ebiten.KeyF,
- ebiten.KeyT,
- ebiten.KeyG,
- ebiten.KeyH,
- ebiten.KeyU,
- ebiten.KeyJ,
- ebiten.KeyI,
- ebiten.KeyK,
- ebiten.KeyO,
- ebiten.KeyL,
-}
-
-var keyStates = map[ebiten.Key]int{}
+var (
+ pianoNoteSamples = map[int][]byte{}
+ pianoNoteSamplesInited = false
+ pianoNoteSamplesInitCh = make(chan struct{})
+)
func init() {
- for _, key := range keys {
- keyStates[key] = 0
+ // Initialize piano data.
+ // This takes a little long time (especially on browsers),
+ // so run this asynchronously and notice the progress.
+ go func() {
+ // Create a reference data and use this for other frequency.
+ const refFreq = 110
+ length := 4 * sampleRate * baseFreq / refFreq
+ refData := make([]int16, length)
+ for i := 0; i < length; i++ {
+ refData[i] = int16(pianoAt(i, refFreq) * math.MaxInt16)
+ }
+
+ for i := range keys {
+ freq := baseFreq * math.Exp2(float64(i-1)/12.0)
+
+ // Clculate the wave data for the freq.
+ length := 4 * sampleRate * baseFreq / int(freq)
+ l := make([]int16, length)
+ r := make([]int16, length)
+ for i := 0; i < length; i++ {
+ idx := int(float64(i) * freq / refFreq)
+ if len(refData) <= idx {
+ break
+ }
+ l[i] = refData[idx]
+ }
+ copy(r, l)
+ n := toBytes(l, r)
+ pianoNoteSamples[int(freq)] = n
+ }
+ close(pianoNoteSamplesInitCh)
+ }()
+}
+
+// playNote plays piano sound with the given frequency.
+func playNote(freq float64) {
+ f := int(freq)
+ p, _ := audio.NewPlayerFromBytes(audioContext, pianoNoteSamples[f])
+ p.Play()
+}
+
+var (
+ pianoImage *ebiten.Image
+)
+
+func init() {
+ pianoImage, _ = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
+
+ const (
+ keyWidth = 24
+ y = 48
+ )
+
+ whiteKeys := []string{"A", "S", "D", "F", "G", "H", "J", "K", "L"}
+ for i, k := range whiteKeys {
+ x := i*keyWidth + 36
+ height := 112
+ ebitenutil.DrawRect(pianoImage, float64(x), float64(y), float64(keyWidth-1), float64(height), color.White)
+ text.Draw(pianoImage, k, arcadeFont, x+8, y+height-8, color.Black)
+ }
+
+ blackKeys := []string{"Q", "W", "", "R", "T", "", "U", "I", "O"}
+ for i, k := range blackKeys {
+ if k == "" {
+ continue
+ }
+ x := i*keyWidth + 24
+ height := 64
+ ebitenutil.DrawRect(pianoImage, float64(x), float64(y), float64(keyWidth-1), float64(height), color.Black)
+ text.Draw(pianoImage, k, arcadeFont, x+8, y+height-8, color.White)
}
}
+var (
+ keys = []ebiten.Key{
+ ebiten.KeyQ,
+ ebiten.KeyA,
+ ebiten.KeyW,
+ ebiten.KeyS,
+ ebiten.KeyD,
+ ebiten.KeyR,
+ ebiten.KeyF,
+ ebiten.KeyT,
+ ebiten.KeyG,
+ ebiten.KeyH,
+ ebiten.KeyU,
+ ebiten.KeyJ,
+ ebiten.KeyI,
+ ebiten.KeyK,
+ ebiten.KeyO,
+ ebiten.KeyL,
+ }
+ keyStates = map[ebiten.Key]int{}
+)
+
+// updateInput updates the input state.
func updateInput() {
for _, key := range keys {
if !ebiten.IsKeyPressed(key) {
@@ -197,47 +238,33 @@ func updateInput() {
}
}
-var (
- imagePiano *ebiten.Image
-)
-
-func init() {
- imagePiano, _ = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
- whiteKeys := []string{"A", "S", "D", "F", "G", "H", "J", "K", "L"}
- width := 24
- y := 48
- for i, k := range whiteKeys {
- x := i*width + 36
- height := 112
- ebitenutil.DrawRect(imagePiano, float64(x), float64(y), float64(width-1), float64(height), color.White)
- text.Draw(imagePiano, k, arcadeFont, x+8, y+height-8, color.Black)
- }
-
- blackKeys := []string{"Q", "W", "", "R", "T", "", "U", "I", "O"}
- for i, k := range blackKeys {
- if k == "" {
- continue
- }
- x := i*width + 24
- height := 64
- ebitenutil.DrawRect(imagePiano, float64(x), float64(y), float64(width-1), float64(height), color.Black)
- text.Draw(imagePiano, k, arcadeFont, x+8, y+height-8, color.White)
- }
-}
-
func update(screen *ebiten.Image) error {
- updateInput()
- for i, key := range keys {
- if keyStates[key] != 1 {
- continue
+ // The piano data is still being initialized.
+ // Get the progress if available.
+ if !pianoNoteSamplesInited {
+ select {
+ case <-pianoNoteSamplesInitCh:
+ pianoNoteSamplesInited = true
+ default:
}
- addNote(220*math.Exp2(float64(i-1)/12.0), 1.0)
}
+
+ if pianoNoteSamplesInited {
+ updateInput()
+ for i, key := range keys {
+ if keyStates[key] != 1 {
+ continue
+ }
+ playNote(baseFreq * math.Exp2(float64(i-1)/12.0))
+ }
+ }
+
if ebiten.IsRunningSlowly() {
return nil
}
+
screen.Fill(color.RGBA{0x80, 0x80, 0xc0, 0xff})
- screen.DrawImage(imagePiano, nil)
+ screen.DrawImage(pianoImage, nil)
ebitenutil.DebugPrint(screen, fmt.Sprintf("FPS: %0.2f", ebiten.CurrentFPS()))
return nil
diff --git a/docs/examples/rotate.html b/docs/examples/rotate.html
index 0cda9113f..7293a9246 100644
--- a/docs/examples/rotate.html
+++ b/docs/examples/rotate.html
@@ -46,7 +46,7 @@ const (
)
var (
- count int
+ count = 0
gophersImage *ebiten.Image
)
@@ -57,8 +57,17 @@ func update(screen *ebiten.Image) error {
}
w, h := gophersImage.Size()
op := &ebiten.DrawImageOptions{}
+
+ // Move the image's center to the screen's upper-left corner.
+ // This is a prepartion for rotating. When geometry matrices are applied,
+ // the origin point is the upper-left corner.
op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
+
+ // Rotate the image. As a result, the anchor point of this rotate is
+ // the center of the image.
op.GeoM.Rotate(float64(count%360) * 2 * math.Pi / 360)
+
+ // Move the image to the screen's center.
op.GeoM.Translate(screenWidth/2, screenHeight/2)
screen.DrawImage(gophersImage, op)
return nil
diff --git a/docs/examples/sprites.html b/docs/examples/sprites.html
index 19280d029..a528e1fb7 100644
--- a/docs/examples/sprites.html
+++ b/docs/examples/sprites.html
@@ -104,43 +104,7 @@ var (
op = &ebiten.DrawImageOptions{}
)
-func update(screen *ebiten.Image) error {
- if ebiten.IsKeyPressed(ebiten.KeyLeft) {
- sprites.num -= 20
- if sprites.num < MinSprites {
- sprites.num = MinSprites
- }
- }
- if ebiten.IsKeyPressed(ebiten.KeyRight) {
- sprites.num += 20
- if MaxSprites < sprites.num {
- sprites.num = MaxSprites
- }
- }
- sprites.Update()
-
- if ebiten.IsRunningSlowly() {
- return nil
- }
- w, h := ebitenImage.Size()
- for i := 0; i < sprites.num; i++ {
- s := sprites.sprites[i]
- op.GeoM.Reset()
- op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
- op.GeoM.Rotate(2 * math.Pi * float64(s.angle) / maxAngle)
- op.GeoM.Translate(float64(w)/2, float64(h)/2)
- op.GeoM.Translate(float64(s.x), float64(s.y))
- screen.DrawImage(ebitenImage, op)
- }
- msg := fmt.Sprintf(`FPS: %0.2f
-Num of sprites: %d
-Press <- or -> to change the number of sprites`, ebiten.CurrentFPS(), sprites.num)
- ebitenutil.DebugPrint(screen, msg)
- return nil
-}
-
-func main() {
- var err error
+func init() {
img, _, err := ebitenutil.NewImageFromFile("_resources/images/ebiten.png", ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
@@ -165,6 +129,55 @@ func main() {
angle: a,
}
}
+}
+
+func update(screen *ebiten.Image) error {
+ // Decrease the nubmer of the sprites.
+ if ebiten.IsKeyPressed(ebiten.KeyLeft) {
+ sprites.num -= 20
+ if sprites.num < MinSprites {
+ sprites.num = MinSprites
+ }
+ }
+
+ // Increase the nubmer of the sprites.
+ if ebiten.IsKeyPressed(ebiten.KeyRight) {
+ sprites.num += 20
+ if MaxSprites < sprites.num {
+ sprites.num = MaxSprites
+ }
+ }
+
+ sprites.Update()
+
+ if ebiten.IsRunningSlowly() {
+ return nil
+ }
+
+ // Draw each sprite.
+ // DrawImage can be called many many times, but in the implementation,
+ // the actual draw call to GPU is very few since these calls satisfy
+ // some conditions e.g. all the rendering sources and targets are same.
+ // For more detail, see:
+ // https://godoc.org/github.com/hajimehoshi/ebiten#Image.DrawImage
+ w, h := ebitenImage.Size()
+ for i := 0; i < sprites.num; i++ {
+ s := sprites.sprites[i]
+ op.GeoM.Reset()
+ op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
+ op.GeoM.Rotate(2 * math.Pi * float64(s.angle) / maxAngle)
+ op.GeoM.Translate(float64(w)/2, float64(h)/2)
+ op.GeoM.Translate(float64(s.x), float64(s.y))
+ screen.DrawImage(ebitenImage, op)
+ }
+ msg := fmt.Sprintf(`FPS: %0.2f
+Num of sprites: %d
+Press <- or -> to change the number of sprites`, ebiten.CurrentFPS(), sprites.num)
+ ebitenutil.DebugPrint(screen, msg)
+ return nil
+}
+
+func main() {
if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Sprites (Ebiten Demo)"); err != nil {
log.Fatal(err)
}
diff --git a/docs/examples/typewriter.html b/docs/examples/typewriter.html
index c8e1ac91e..c8741af21 100644
--- a/docs/examples/typewriter.html
+++ b/docs/examples/typewriter.html
@@ -46,14 +46,23 @@ var (
)
func update(screen *ebiten.Image) error {
+ // Add a string from InputChars, that returns string input by users.
+ // Note that InputChars result changes every frame, so you need to call this
+ // every frame.
text += string(ebiten.InputChars())
+
+ // Adjust the string to be at most 10 lines.
ss := strings.Split(text, "\n")
if len(ss) > 10 {
text = strings.Join(ss[len(ss)-10:], "\n")
}
+
+ // If the enter key is pressed, add a line break.
if ebiten.IsKeyPressed(ebiten.KeyEnter) && !strings.HasSuffix(text, "\n") {
text += "\n"
}
+
+ // If the backspace key is pressed, remove one character.
bsPressed := ebiten.IsKeyPressed(ebiten.KeyBackspace)
if !bsPrevPressed && bsPressed {
if len(text) >= 1 {
@@ -68,6 +77,7 @@ func update(screen *ebiten.Image) error {
return nil
}
+ // Blink the cursor.
t := text
if counter%60 < 30 {
t += "_"
diff --git a/docs/index.html b/docs/index.html
index f4e6d59e9..ef53dd9ef 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -60,11 +60,10 @@
Examples
+ Graphics
-
-
@@ -73,12 +72,8 @@
-
-
-
-
@@ -91,20 +86,39 @@
-
-
+
+
+ Input
+
+ Audio
+
+
+
+
+
+
+ Game
+
+
The Gopher photographs by Chris Nokleberg are licensed under the Creative Commons 3.0 Attributions License.
Execute the examples