docs: Add example groups

This commit is contained in:
Hajime Hoshi 2018-01-31 01:18:33 +09:00
parent d139b8ad6e
commit 444e5bab84
10 changed files with 370 additions and 229 deletions

View File

@ -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)

View File

@ -60,13 +60,39 @@
</dl>
<h2 id="examples">Examples</h2>
<h3>Graphics</h3>
<div class="row">
{{range .Examples -}}
{{range .GraphicsExamples -}}
<div class="col-3">
<a href="examples/{{.Name}}.html"><img src="images/examples/{{.Name}}.png" width="{{.ThumbWidth}}" height="{{.ThumbHeight}}" alt="Ebiten example: {{.Name}}" class="img-thumbnail"></a>
</div>
{{- end}}
</div>
<h3>Input</h3>
<div class="row">
{{range .InputExamples -}}
<div class="col-3">
<a href="examples/{{.Name}}.html"><img src="images/examples/{{.Name}}.png" width="{{.ThumbWidth}}" height="{{.ThumbHeight}}" alt="Ebiten example: {{.Name}}" class="img-thumbnail"></a>
</div>
{{- end}}
</div>
<h3>Audio</h3>
<div class="row">
{{range .AudioExamples -}}
<div class="col-3">
<a href="examples/{{.Name}}.html"><img src="images/examples/{{.Name}}.png" width="{{.ThumbWidth}}" height="{{.ThumbHeight}}" alt="Ebiten example: {{.Name}}" class="img-thumbnail"></a>
</div>
{{- end}}
</div>
<h3>Game</h3>
<div class="row">
{{range .GameExamples -}}
<div class="col-3">
<a href="examples/{{.Name}}.html"><img src="images/examples/{{.Name}}.png" width="{{.ThumbWidth}}" height="{{.ThumbHeight}}" alt="Ebiten example: {{.Name}}" class="img-thumbnail"></a>
</div>
{{- end}}
</div>
<p><a href="https://blog.golang.org/go-programming-language-turns-two">The Gopher photographs by Chris Nokleberg</a> are licensed under <a href="https://creativecommons.org/licenses/by/3.0/">the Creative Commons 3.0 Attributions License</a>.</p>
<h3>Execute the examples</h3>

View File

@ -62,20 +62,23 @@ func (r *rand) next() uint32 {
return r.w
}
var randInstance = &amp;rand{12345678, 4185243, 776511, 45411}
var theRand = &amp;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 &lt; l; i&#43;&#43; {
x := randInstance.next()
x := theRand.next()
noiseImage.Pix[4*i] = uint8(x &gt;&gt; 24)
noiseImage.Pix[4*i&#43;1] = uint8(x &gt;&gt; 16)
noiseImage.Pix[4*i&#43;2] = uint8(x &gt;&gt; 8)
noiseImage.Pix[4*i&#43;3] = 0xff
}
if ebiten.IsRunningSlowly() {
return nil
}
screen.ReplacePixels(noiseImage.Pix)
ebitenutil.DebugPrint(screen, fmt.Sprintf(&#34;FPS: %f&#34;, ebiten.CurrentFPS()))
return nil

View File

@ -53,46 +53,12 @@ var (
canvasImage *ebiten.Image
)
func paint(screen *ebiten.Image, x, y int) {
op := &amp;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&#43;&#43;
}
if ebiten.IsRunningSlowly() {
return nil
}
screen.DrawImage(canvasImage, nil)
msg := fmt.Sprintf(&#34;(%d, %d)&#34;, mx, my)
for _, t := range ebiten.Touches() {
x, y := t.Position()
msg &#43;= fmt.Sprintf(&#34;\n(%d, %d) touch %d&#34;, 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 := &amp;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&#43;&#43;
}
if ebiten.IsRunningSlowly() {
return nil
}
screen.DrawImage(canvasImage, nil)
msg := fmt.Sprintf(&#34;(%d, %d)&#34;, mx, my)
for _, t := range ebiten.Touches() {
x, y := t.Position()
msg &#43;= fmt.Sprintf(&#34;\n(%d, %d) touch %d&#34;, x, y, t.ID())
}
ebitenutil.DebugPrint(screen, msg)
return nil
}
func main() {
if err := ebiten.Run(update, screenWidth, screenHeight, 2, &#34;Paint (Ebiten Demo)&#34;); err != nil {
log.Fatal(err)
}

View File

@ -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 := &amp;ebiten.DrawImageOptions{}
w, h := gophersImage.Size()
for i := 0; i &lt; h; i&#43;&#43; {
op.GeoM.Reset()
width := w &#43; 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) &#43; float64(h)*3/4
op.GeoM.Translate(-maxWidth/2, -float64(h)/2)
// Move the image&#39;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 &#43; 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&#39;s center to the screen&#39;s center.
op.GeoM.Translate(screenWidth/2, screenHeight/2)
r := image.Rect(0, i, w, i&#43;1)
op.SourceRect = &amp;r
screen.DrawImage(gophersImage, op)

View File

@ -47,10 +47,6 @@ import (
&#34;github.com/hajimehoshi/ebiten/text&#34;
)
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, &amp;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 &lt; len(pcm); i&#43;&#43; {
v := 0.0
for j := 0; j &lt; len(amp); j&#43;&#43; {
a := amp[j] * math.Exp(-5*float64(i)/(x[j]*sampleRate))
v &#43;= a * math.Sin(2.0*math.Pi*float64(i)*baseFreq*float64(j&#43;1)/sampleRate)
}
pcm[i] = v / 5.0
v := 0.0
for j := 0; j &lt; len(amp); j&#43;&#43; {
// Decay
a := amp[j] * math.Exp(-5*float64(i)*freq/baseFreq/(x[j]*sampleRate))
v &#43;= a * math.Sin(2.0*math.Pi*float64(i)*freq*float64(j&#43;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(&#34;len(l) must equal to len(r)&#34;)
@ -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 &lt; len(l); i&#43;&#43; {
p := pcm[j]
l[i] = int16(p * vol * math.MaxInt16)
r[i] = l[i]
jj &#43;= 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 &lt; length; i&#43;&#43; {
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 &lt; length; i&#43;&#43; {
idx := int(float64(i) * freq / refFreq)
if len(refData) &lt;= 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{&#34;A&#34;, &#34;S&#34;, &#34;D&#34;, &#34;F&#34;, &#34;G&#34;, &#34;H&#34;, &#34;J&#34;, &#34;K&#34;, &#34;L&#34;}
for i, k := range whiteKeys {
x := i*keyWidth &#43; 36
height := 112
ebitenutil.DrawRect(pianoImage, float64(x), float64(y), float64(keyWidth-1), float64(height), color.White)
text.Draw(pianoImage, k, arcadeFont, x&#43;8, y&#43;height-8, color.Black)
}
blackKeys := []string{&#34;Q&#34;, &#34;W&#34;, &#34;&#34;, &#34;R&#34;, &#34;T&#34;, &#34;&#34;, &#34;U&#34;, &#34;I&#34;, &#34;O&#34;}
for i, k := range blackKeys {
if k == &#34;&#34; {
continue
}
x := i*keyWidth &#43; 24
height := 64
ebitenutil.DrawRect(pianoImage, float64(x), float64(y), float64(keyWidth-1), float64(height), color.Black)
text.Draw(pianoImage, k, arcadeFont, x&#43;8, y&#43;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{&#34;A&#34;, &#34;S&#34;, &#34;D&#34;, &#34;F&#34;, &#34;G&#34;, &#34;H&#34;, &#34;J&#34;, &#34;K&#34;, &#34;L&#34;}
width := 24
y := 48
for i, k := range whiteKeys {
x := i*width &#43; 36
height := 112
ebitenutil.DrawRect(imagePiano, float64(x), float64(y), float64(width-1), float64(height), color.White)
text.Draw(imagePiano, k, arcadeFont, x&#43;8, y&#43;height-8, color.Black)
}
blackKeys := []string{&#34;Q&#34;, &#34;W&#34;, &#34;&#34;, &#34;R&#34;, &#34;T&#34;, &#34;&#34;, &#34;U&#34;, &#34;I&#34;, &#34;O&#34;}
for i, k := range blackKeys {
if k == &#34;&#34; {
continue
}
x := i*width &#43; 24
height := 64
ebitenutil.DrawRect(imagePiano, float64(x), float64(y), float64(width-1), float64(height), color.Black)
text.Draw(imagePiano, k, arcadeFont, x&#43;8, y&#43;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 &lt;-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(&#34;FPS: %0.2f&#34;, ebiten.CurrentFPS()))
return nil

View File

@ -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 := &amp;ebiten.DrawImageOptions{}
// Move the image&#39;s center to the screen&#39;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&#39;s center.
op.GeoM.Translate(screenWidth/2, screenHeight/2)
screen.DrawImage(gophersImage, op)
return nil

View File

@ -104,43 +104,7 @@ var (
op = &amp;ebiten.DrawImageOptions{}
)
func update(screen *ebiten.Image) error {
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
sprites.num -= 20
if sprites.num &lt; MinSprites {
sprites.num = MinSprites
}
}
if ebiten.IsKeyPressed(ebiten.KeyRight) {
sprites.num &#43;= 20
if MaxSprites &lt; sprites.num {
sprites.num = MaxSprites
}
}
sprites.Update()
if ebiten.IsRunningSlowly() {
return nil
}
w, h := ebitenImage.Size()
for i := 0; i &lt; sprites.num; i&#43;&#43; {
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 &lt;- or -&gt; 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(&#34;_resources/images/ebiten.png&#34;, 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 &lt; MinSprites {
sprites.num = MinSprites
}
}
// Increase the nubmer of the sprites.
if ebiten.IsKeyPressed(ebiten.KeyRight) {
sprites.num &#43;= 20
if MaxSprites &lt; 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 &lt; sprites.num; i&#43;&#43; {
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 &lt;- or -&gt; 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, &#34;Sprites (Ebiten Demo)&#34;); err != nil {
log.Fatal(err)
}

View File

@ -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 &#43;= string(ebiten.InputChars())
// Adjust the string to be at most 10 lines.
ss := strings.Split(text, &#34;\n&#34;)
if len(ss) &gt; 10 {
text = strings.Join(ss[len(ss)-10:], &#34;\n&#34;)
}
// If the enter key is pressed, add a line break.
if ebiten.IsKeyPressed(ebiten.KeyEnter) &amp;&amp; !strings.HasSuffix(text, &#34;\n&#34;) {
text &#43;= &#34;\n&#34;
}
// If the backspace key is pressed, remove one character.
bsPressed := ebiten.IsKeyPressed(ebiten.KeyBackspace)
if !bsPrevPressed &amp;&amp; bsPressed {
if len(text) &gt;= 1 {
@ -68,6 +77,7 @@ func update(screen *ebiten.Image) error {
return nil
}
// Blink the cursor.
t := text
if counter%60 &lt; 30 {
t &#43;= &#34;_&#34;

View File

@ -60,11 +60,10 @@
</dl>
<h2 id="examples">Examples</h2>
<h3>Graphics</h3>
<div class="row">
<div class="col-3">
<a href="examples/alphablending.html"><img src="images/examples/alphablending.png" width="320" height="240" alt="Ebiten example: alphablending" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/audio.html"><img src="images/examples/audio.png" width="320" height="240" alt="Ebiten example: audio" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/font.html"><img src="images/examples/font.png" width="320" height="240" alt="Ebiten example: font" class="img-thumbnail"></a>
</div><div class="col-3">
@ -73,12 +72,8 @@
<a href="examples/hsv.html"><img src="images/examples/hsv.png" width="320" height="240" alt="Ebiten example: hsv" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/hue.html"><img src="images/examples/hue.png" width="320" height="240" alt="Ebiten example: hue" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/gamepad.html"><img src="images/examples/gamepad.png" width="320" height="240" alt="Ebiten example: gamepad" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/infinitescroll.html"><img src="images/examples/infinitescroll.png" width="320" height="240" alt="Ebiten example: infinitescroll" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/keyboard.html"><img src="images/examples/keyboard.png" width="320" height="240" alt="Ebiten example: keyboard" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/life.html"><img src="images/examples/life.png" width="320" height="240" alt="Ebiten example: life" class="img-thumbnail"></a>
</div><div class="col-3">
@ -91,20 +86,39 @@
<a href="examples/paint.html"><img src="images/examples/paint.png" width="320" height="240" alt="Ebiten example: paint" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/perspective.html"><img src="images/examples/perspective.png" width="320" height="240" alt="Ebiten example: perspective" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/piano.html"><img src="images/examples/piano.png" width="320" height="240" alt="Ebiten example: piano" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/rotate.html"><img src="images/examples/rotate.png" width="320" height="240" alt="Ebiten example: rotate" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/sprites.html"><img src="images/examples/sprites.png" width="320" height="240" alt="Ebiten example: sprites" class="img-thumbnail"></a>
</div>
</div>
<h3>Input</h3>
<div class="row">
<div class="col-3">
<a href="examples/gamepad.html"><img src="images/examples/gamepad.png" width="320" height="240" alt="Ebiten example: gamepad" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/keyboard.html"><img src="images/examples/keyboard.png" width="320" height="240" alt="Ebiten example: keyboard" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/typewriter.html"><img src="images/examples/typewriter.png" width="320" height="240" alt="Ebiten example: typewriter" class="img-thumbnail"></a>
</div>
</div>
<h3>Audio</h3>
<div class="row">
<div class="col-3">
<a href="examples/audio.html"><img src="images/examples/audio.png" width="320" height="240" alt="Ebiten example: audio" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/piano.html"><img src="images/examples/piano.png" width="320" height="240" alt="Ebiten example: piano" class="img-thumbnail"></a>
</div>
</div>
<h3>Game</h3>
<div class="row">
<div class="col-3">
<a href="examples/2048.html"><img src="images/examples/2048.png" width="210" height="300" alt="Ebiten example: 2048" class="img-thumbnail"></a>
</div><div class="col-3">
<a href="examples/blocks.html"><img src="images/examples/blocks.png" width="256" height="240" alt="Ebiten example: blocks" class="img-thumbnail"></a>
</div>
</div>
<p><a href="https://blog.golang.org/go-programming-language-turns-two">The Gopher photographs by Chris Nokleberg</a> are licensed under <a href="https://creativecommons.org/licenses/by/3.0/">the Creative Commons 3.0 Attributions License</a>.</p>
<h3>Execute the examples</h3>