doc: Add new examples

This commit is contained in:
Hajime Hoshi 2016-04-14 01:26:31 +09:00
parent 9bd754c6d4
commit 4e2d661541
12 changed files with 1223 additions and 1 deletions

View File

@ -171,13 +171,18 @@ func versions() string {
var examples = []example{
{Name: "alphablending"},
{Name: "audio"},
{Name: "font"},
{Name: "hsv"},
{Name: "hue"},
{Name: "gamepad"},
{Name: "keyboard"},
{Name: "masking"},
{Name: "mosaic"},
{Name: "noise"},
{Name: "paint"},
{Name: "perspective"},
{Name: "piano"},
{Name: "rotate"},
{Name: "sprites"},
{Name: "blocks"},

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
'use strict';
window.addEventListener('load', function() {
function isProduction() {
var l = window.top.location;
if (l.hash === '#_production') {
return true;
}
if (l.hostname === 'localhost' || l.hostname === '127.0.0.1') {
return false;
}
return true;
}
var s = document.createElement('script');
var src = 'audio.js';
if (isProduction()) {
src = '//hajimehoshi.github.io/ebiten.pagestorage/master/' + src;
}
s.src = src;
document.body.appendChild(s);
});
</script>

View File

@ -0,0 +1,326 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="shortcut icon" href="../favicon.png" type="image/png" >
<link rel="icon" href="../favicon.png" type="image/png" >
<title>Ebiten example - audio</title>
<style>
body {
font-family: sans-serif;
}
iframe {
border-color: #999;
border-style: solid;
border-width: 1px;
overflow: hidden;
}
pre {
background: #eee;
padding: 1em;
}
</style>
<nav><a href="..">Ebiten</a></nav>
<h1>Ebiten example - audio</h1>
<iframe src="audio.content.html" width="640" height="480"></iframe>
<pre><code>package main
import (
&#34;bytes&#34;
&#34;fmt&#34;
&#34;image/color&#34;
&#34;log&#34;
&#34;time&#34;
&#34;github.com/hajimehoshi/ebiten&#34;
&#34;github.com/hajimehoshi/ebiten/ebitenutil&#34;
&#34;github.com/hajimehoshi/ebiten/exp/audio&#34;
&#34;github.com/hajimehoshi/ebiten/exp/audio/vorbis&#34;
&#34;github.com/hajimehoshi/ebiten/exp/audio/wav&#34;
)
const (
screenWidth = 320
screenHeight = 240
)
var (
playerBarImage *ebiten.Image
playerCurrentImage *ebiten.Image
)
func init() {
var err error
playerBarImage, err = ebiten.NewImage(300, 4, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
playerBarImage.Fill(&amp;color.RGBA{0x80, 0x80, 0x80, 0xff})
playerCurrentImage, err = ebiten.NewImage(4, 10, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
playerCurrentImage.Fill(&amp;color.RGBA{0xff, 0xff, 0xff, 0xff})
}
type Player struct {
audioPlayer *audio.Player
total time.Duration
}
var (
audioContext *audio.Context
musicPlayer *Player
seStream *wav.Stream
sePlayer *audio.Player
musicCh = make(chan *Player)
seCh = make(chan *wav.Stream)
mouseButtonState = map[ebiten.MouseButton]int{}
keyState = map[ebiten.Key]int{}
volume128 = 128
)
func playerBarRect() (x, y, w, h int) {
w, h = playerBarImage.Size()
x = (screenWidth - w) / 2
y = screenHeight - h - 16
return
}
type SEStream struct {
*bytes.Reader
}
func (s *SEStream) Close() error {
return nil
}
func (p *Player) updateSE() error {
if seStream == nil {
return nil
}
if !ebiten.IsKeyPressed(ebiten.KeyP) {
keyState[ebiten.KeyP] = 0
return nil
}
keyState[ebiten.KeyP]&#43;&#43;
if keyState[ebiten.KeyP] != 1 {
return nil
}
if sePlayer == nil {
var err error
sePlayer, err = audioContext.NewPlayer(seStream)
if err != nil {
return err
}
}
if sePlayer.IsPlaying() {
return nil
}
if err := sePlayer.Rewind(); err != nil {
return err
}
return sePlayer.Play()
}
func (p *Player) updateVolume() error {
if p.audioPlayer == nil {
return nil
}
if ebiten.IsKeyPressed(ebiten.KeyZ) {
volume128--
}
if ebiten.IsKeyPressed(ebiten.KeyX) {
volume128&#43;&#43;
}
if volume128 &lt; 0 {
volume128 = 0
}
if 128 &lt; volume128 {
volume128 = 128
}
p.audioPlayer.SetVolume(float64(volume128) / 128)
return nil
}
func (p *Player) updatePlayPause() error {
if p.audioPlayer == nil {
return nil
}
if !ebiten.IsKeyPressed(ebiten.KeyS) {
keyState[ebiten.KeyS] = 0
return nil
}
keyState[ebiten.KeyS]&#43;&#43;
if keyState[ebiten.KeyS] != 1 {
return nil
}
if p.audioPlayer.IsPlaying() {
return p.audioPlayer.Pause()
}
return p.audioPlayer.Play()
}
func (p *Player) updateBar() error {
if p.audioPlayer == nil {
return nil
}
if !ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
mouseButtonState[ebiten.MouseButtonLeft] = 0
return nil
}
mouseButtonState[ebiten.MouseButtonLeft]&#43;&#43;
if mouseButtonState[ebiten.MouseButtonLeft] != 1 {
return nil
}
x, y := ebiten.CursorPosition()
bx, by, bw, bh := playerBarRect()
if y &lt; by || by&#43;bh &lt;= y {
return nil
}
if x &lt; bx || bx&#43;bw &lt;= x {
return nil
}
pos := time.Duration(x-bx) * p.total / time.Duration(bw)
return p.audioPlayer.Seek(pos)
}
func (p *Player) close() error {
return p.audioPlayer.Close()
}
func update(screen *ebiten.Image) error {
if err := audioContext.Update(); err != nil {
return err
}
if musicPlayer == nil {
select {
case musicPlayer = &lt;-musicCh:
default:
}
}
if seStream == nil {
select {
case seStream = &lt;-seCh:
default:
}
}
if musicPlayer != nil {
if err := musicPlayer.updateBar(); err != nil {
return err
}
if err := musicPlayer.updatePlayPause(); err != nil {
return err
}
if err := musicPlayer.updateSE(); err != nil {
return err
}
if err := musicPlayer.updateVolume(); err != nil {
return err
}
}
op := &amp;ebiten.DrawImageOptions{}
x, y, w, h := playerBarRect()
op.GeoM.Translate(float64(x), float64(y))
screen.DrawImage(playerBarImage, op)
currentTimeStr := &#34;00:00&#34;
if musicPlayer != nil {
c := musicPlayer.audioPlayer.Current()
// Current Time
m := (c / time.Minute) % 100
s := (c / time.Second) % 60
currentTimeStr = fmt.Sprintf(&#34;%02d:%02d&#34;, m, s)
// Bar
cw, ch := playerCurrentImage.Size()
cx := int(time.Duration(w)*c/musicPlayer.total) &#43; x - cw/2
cy := y - (ch-h)/2
op := &amp;ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(cx), float64(cy))
screen.DrawImage(playerCurrentImage, op)
}
msg := fmt.Sprintf(`FPS: %0.2f
Press S to toggle Play/Pause
Press P to play SE
Press Z or X to change volume of the music
%s`, ebiten.CurrentFPS(), currentTimeStr)
if musicPlayer == nil {
msg &#43;= &#34;\nNow Loading...&#34;
}
ebitenutil.DebugPrint(screen, msg)
return nil
}
func main() {
wavF, err := ebitenutil.OpenFile(&#34;_resources/audio/jab.wav&#34;)
if err != nil {
log.Fatal(err)
}
oggF, err := ebitenutil.OpenFile(&#34;_resources/audio/ragtime.ogg&#34;)
if err != nil {
log.Fatal(err)
}
const sampleRate = 22050
const bytesPerSample = 4 // TODO: This should be defined in audio package
audioContext, err = audio.NewContext(sampleRate)
if err != nil {
log.Fatal(err)
}
go func() {
s, err := wav.Decode(audioContext, wavF)
if err != nil {
log.Fatal(err)
return
}
seCh &lt;- s
close(seCh)
}()
go func() {
s, err := vorbis.Decode(audioContext, oggF)
if err != nil {
log.Fatal(err)
return
}
p, err := audioContext.NewPlayer(s)
if err != nil {
log.Fatal(err)
return
}
musicCh &lt;- &amp;Player{
audioPlayer: p,
total: time.Second * time.Duration(s.Size()) / bytesPerSample / sampleRate,
}
close(musicCh)
// TODO: Is this goroutine-safe?
p.Play()
}()
if err := ebiten.Run(update, screenWidth, screenHeight, 2, &#34;Audio (Ebiten Demo)&#34;); err != nil {
log.Fatal(err)
}
if musicPlayer != nil {
if err := musicPlayer.close(); err != nil {
log.Fatal(err)
}
}
}
</code></pre>
<footer>© 2014 Hajime Hoshi</footer>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
'use strict';
window.addEventListener('load', function() {
function isProduction() {
var l = window.top.location;
if (l.hash === '#_production') {
return true;
}
if (l.hostname === 'localhost' || l.hostname === '127.0.0.1') {
return false;
}
return true;
}
var s = document.createElement('script');
var src = 'font.js';
if (isProduction()) {
src = '//hajimehoshi.github.io/ebiten.pagestorage/master/' + src;
}
s.src = src;
document.body.appendChild(s);
});
</script>

View File

@ -0,0 +1,134 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="shortcut icon" href="../favicon.png" type="image/png" >
<link rel="icon" href="../favicon.png" type="image/png" >
<title>Ebiten example - font</title>
<style>
body {
font-family: sans-serif;
}
iframe {
border-color: #999;
border-style: solid;
border-width: 1px;
overflow: hidden;
}
pre {
background: #eee;
padding: 1em;
}
</style>
<nav><a href="..">Ebiten</a></nav>
<h1>Ebiten example - font</h1>
<iframe src="font.content.html" width="640" height="480"></iframe>
<pre><code>package main
import (
&#34;image&#34;
&#34;io/ioutil&#34;
&#34;log&#34;
&#34;github.com/golang/freetype/truetype&#34;
&#34;github.com/hajimehoshi/ebiten&#34;
&#34;github.com/hajimehoshi/ebiten/ebitenutil&#34;
&#34;golang.org/x/image/font&#34;
&#34;golang.org/x/image/math/fixed&#34;
)
const (
screenWidth = 640
screenHeight = 480
)
var (
textImage *ebiten.Image
)
var text = []string{
&#34;The quick brown fox jumps over the lazy dog.&#34;,
&#34;&#34;,
// A head part of a Japanese novel 山月記 (Sangetsuki)
// See http://www.aozora.gr.jp/cards/000119/files/624_14544.html.
&#34;隴西の李徴は博学才穎、天宝の末年、&#34;,
&#34;若くして名を虎榜に連ね、&#34;,
&#34;ついで江南尉に補せられたが、&#34;,
&#34;性、狷介、自ら恃むところ頗厚く、&#34;,
&#34;賤吏に甘んずるを潔しとしなかった。&#34;,
}
func parseFont() error {
f, err := ebitenutil.OpenFile(&#34;_resources/fonts/mplus-1p-regular.ttf&#34;)
if err != nil {
return err
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return err
}
tt, err := truetype.Parse(b)
if err != nil {
return err
}
w, h := textImage.Size()
dst := image.NewRGBA(image.Rect(0, 0, w, h))
const size = 24
const dpi = 72
d := &amp;font.Drawer{
Dst: dst,
Src: image.White,
Face: truetype.NewFace(tt, &amp;truetype.Options{
Size: size,
DPI: dpi,
Hinting: font.HintingFull,
}),
}
dy := size * dpi / 72
y := dy
for _, s := range text {
d.Dot = fixed.P(0, y)
d.DrawString(s)
y &#43;= dy
}
return textImage.ReplacePixels(dst.Pix)
}
func update(screen *ebiten.Image) error {
if err := screen.DrawImage(textImage, &amp;ebiten.DrawImageOptions{}); err != nil {
return err
}
return nil
}
func main() {
var err error
textImage, err = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
if err := parseFont(); err != nil {
log.Fatal(err)
}
if err := ebiten.Run(update, screenWidth, screenHeight, 1, &#34;Font (Ebiten Demo)&#34;); err != nil {
log.Fatal(err)
}
}
</code></pre>
<footer>© 2014 Hajime Hoshi</footer>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
'use strict';
window.addEventListener('load', function() {
function isProduction() {
var l = window.top.location;
if (l.hash === '#_production') {
return true;
}
if (l.hostname === 'localhost' || l.hostname === '127.0.0.1') {
return false;
}
return true;
}
var s = document.createElement('script');
var src = 'hsv.js';
if (isProduction()) {
src = '//hajimehoshi.github.io/ebiten.pagestorage/master/' + src;
}
s.src = src;
document.body.appendChild(s);
});
</script>

View File

@ -0,0 +1,132 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="shortcut icon" href="../favicon.png" type="image/png" >
<link rel="icon" href="../favicon.png" type="image/png" >
<title>Ebiten example - hsv</title>
<style>
body {
font-family: sans-serif;
}
iframe {
border-color: #999;
border-style: solid;
border-width: 1px;
overflow: hidden;
}
pre {
background: #eee;
padding: 1em;
}
</style>
<nav><a href="..">Ebiten</a></nav>
<h1>Ebiten example - hsv</h1>
<iframe src="hsv.content.html" width="640" height="480"></iframe>
<pre><code>package main
import (
&#34;fmt&#34;
_ &#34;image/jpeg&#34;
&#34;log&#34;
&#34;math&#34;
&#34;github.com/hajimehoshi/ebiten&#34;
&#34;github.com/hajimehoshi/ebiten/ebitenutil&#34;
)
const (
screenWidth = 320
screenHeight = 240
)
var (
hueInt = 0
saturationInt = 128
valueInt = 128
gophersImage *ebiten.Image
)
func clamp(v, min, max int) int {
if min &gt; max {
panic(&#34;min must &lt;= max&#34;)
}
if v &lt; min {
return min
}
if max &lt; v {
return max
}
return v
}
func update(screen *ebiten.Image) error {
if ebiten.IsKeyPressed(ebiten.KeyQ) {
hueInt--
}
if ebiten.IsKeyPressed(ebiten.KeyW) {
hueInt&#43;&#43;
}
if ebiten.IsKeyPressed(ebiten.KeyA) {
saturationInt--
}
if ebiten.IsKeyPressed(ebiten.KeyS) {
saturationInt&#43;&#43;
}
if ebiten.IsKeyPressed(ebiten.KeyZ) {
valueInt--
}
if ebiten.IsKeyPressed(ebiten.KeyX) {
valueInt&#43;&#43;
}
hueInt = clamp(hueInt, -256, 256)
saturationInt = clamp(saturationInt, 0, 256)
valueInt = clamp(valueInt, 0, 256)
w, h := gophersImage.Size()
op := &amp;ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(screenWidth-w)/2, float64(screenHeight-h)/2)
hue := float64(hueInt) * 2 * math.Pi / 128
saturation := float64(saturationInt) / 128
value := float64(valueInt) / 128
op.ColorM.ChangeHSV(hue, saturation, value)
if err := screen.DrawImage(gophersImage, op); err != nil {
return err
}
msg := fmt.Sprintf(`Hue: %0.2f [Q][W]
Saturation: %0.2f [A][S]
Value: %0.2f [Z][X]`, hue, saturation, value)
if err := ebitenutil.DebugPrint(screen, msg); err != nil {
return err
}
return nil
}
func main() {
var err error
gophersImage, _, err = ebitenutil.NewImageFromFile(&#34;_resources/images/gophers.jpg&#34;, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
if err := ebiten.Run(update, screenWidth, screenHeight, 2, &#34;HSV (Ebiten Demo)&#34;); err != nil {
log.Fatal(err)
}
}
</code></pre>
<footer>© 2014 Hajime Hoshi</footer>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
'use strict';
window.addEventListener('load', function() {
function isProduction() {
var l = window.top.location;
if (l.hash === '#_production') {
return true;
}
if (l.hostname === 'localhost' || l.hostname === '127.0.0.1') {
return false;
}
return true;
}
var s = document.createElement('script');
var src = 'masking.js';
if (isProduction()) {
src = '//hajimehoshi.github.io/ebiten.pagestorage/master/' + src;
}
s.src = src;
document.body.appendChild(s);
});
</script>

View File

@ -0,0 +1,169 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="shortcut icon" href="../favicon.png" type="image/png" >
<link rel="icon" href="../favicon.png" type="image/png" >
<title>Ebiten example - masking</title>
<style>
body {
font-family: sans-serif;
}
iframe {
border-color: #999;
border-style: solid;
border-width: 1px;
overflow: hidden;
}
pre {
background: #eee;
padding: 1em;
}
</style>
<nav><a href="..">Ebiten</a></nav>
<h1>Ebiten example - masking</h1>
<iframe src="masking.content.html" width="640" height="480"></iframe>
<pre><code>package main
import (
&#34;image&#34;
&#34;image/color&#34;
_ &#34;image/jpeg&#34;
&#34;log&#34;
&#34;math&#34;
&#34;github.com/hajimehoshi/ebiten&#34;
&#34;github.com/hajimehoshi/ebiten/ebitenutil&#34;
)
const (
screenWidth = 320
screenHeight = 240
)
var (
gophersImage *ebiten.Image
fiveyearsImage *ebiten.Image
maskImage *ebiten.Image
spotLightImage *ebiten.Image
spotLightX = 0
spotLightY = 0
spotLightVX = 1
spotLightVY = 1
)
func update(screen *ebiten.Image) error {
spotLightX &#43;= spotLightVX
spotLightY &#43;= spotLightVY
if spotLightX &lt; 0 {
spotLightX = -spotLightX
spotLightVX = -spotLightVX
}
if spotLightY &lt; 0 {
spotLightY = -spotLightY
spotLightVY = -spotLightVY
}
w, h := spotLightImage.Size()
maxX, maxY := screenWidth-w, screenHeight-h
if maxX &lt; spotLightX {
spotLightX = -spotLightX &#43; 2*maxX
spotLightVX = -spotLightVX
}
if maxY &lt; spotLightY {
spotLightY = -spotLightY &#43; 2*maxY
spotLightVY = -spotLightVY
}
if err := maskImage.Clear(); err != nil {
return err
}
op := &amp;ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(spotLightX), float64(spotLightY))
if err := maskImage.DrawImage(spotLightImage, op); err != nil {
return err
}
op = &amp;ebiten.DrawImageOptions{}
op.CompositeMode = ebiten.CompositeModeSourceOut
if err := maskImage.DrawImage(fiveyearsImage, op); err != nil {
return err
}
if err := screen.Fill(color.RGBA{0x00, 0x00, 0x80, 0xff}); err != nil {
return err
}
if err := screen.DrawImage(gophersImage, &amp;ebiten.DrawImageOptions{}); err != nil {
return err
}
if err := screen.DrawImage(maskImage, &amp;ebiten.DrawImageOptions{}); err != nil {
return err
}
return nil
}
func max(a, b int) int {
if a &lt; b {
return b
}
return a
}
func min(a, b int) int {
if a &lt; b {
return a
}
return b
}
func main() {
var err error
gophersImage, _, err = ebitenutil.NewImageFromFile(&#34;_resources/images/gophers.jpg&#34;, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
fiveyearsImage, _, err = ebitenutil.NewImageFromFile(&#34;_resources/images/fiveyears.jpg&#34;, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
maskImage, err = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
as := image.Point{128, 128}
a := image.NewAlpha(image.Rectangle{image.ZP, as})
for j := 0; j &lt; as.Y; j&#43;&#43; {
for i := 0; i &lt; as.X; i&#43;&#43; {
r := as.X / 2
d := math.Sqrt(float64((i-r)*(i-r) &#43; (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, err = ebiten.NewImageFromImage(a, ebiten.FilterNearest)
if err != nil {
log.Fatal(err)
}
if err := ebiten.Run(update, screenWidth, screenHeight, 2, &#34;Masking (Ebiten Demo)&#34;); err != nil {
log.Fatal(err)
}
}
</code></pre>
<footer>© 2014 Hajime Hoshi</footer>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script>
'use strict';
window.addEventListener('load', function() {
function isProduction() {
var l = window.top.location;
if (l.hash === '#_production') {
return true;
}
if (l.hostname === 'localhost' || l.hostname === '127.0.0.1') {
return false;
}
return true;
}
var s = document.createElement('script');
var src = 'piano.js';
if (isProduction()) {
src = '//hajimehoshi.github.io/ebiten.pagestorage/master/' + src;
}
s.src = src;
document.body.appendChild(s);
});
</script>

View File

@ -0,0 +1,261 @@
<!DOCTYPE html>
<!--
Copyright 2014 Hajime Hoshi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="shortcut icon" href="../favicon.png" type="image/png" >
<link rel="icon" href="../favicon.png" type="image/png" >
<title>Ebiten example - piano</title>
<style>
body {
font-family: sans-serif;
}
iframe {
border-color: #999;
border-style: solid;
border-width: 1px;
overflow: hidden;
}
pre {
background: #eee;
padding: 1em;
}
</style>
<nav><a href="..">Ebiten</a></nav>
<h1>Ebiten example - piano</h1>
<iframe src="piano.content.html" width="640" height="480"></iframe>
<pre><code>package main
import (
&#34;bytes&#34;
&#34;fmt&#34;
&#34;image/color&#34;
&#34;log&#34;
&#34;math&#34;
&#34;github.com/hajimehoshi/ebiten&#34;
&#34;github.com/hajimehoshi/ebiten/ebitenutil&#34;
&#34;github.com/hajimehoshi/ebiten/examples/common&#34;
&#34;github.com/hajimehoshi/ebiten/exp/audio&#34;
)
const (
screenWidth = 320
screenHeight = 240
sampleRate = 44100
)
var audioContext *audio.Context
func init() {
var err error
audioContext, err = audio.NewContext(sampleRate)
if err != nil {
log.Fatal(err)
}
}
var pcm = make([]float64, 4*sampleRate)
const baseFreq = 220
func init() {
s := float64(sampleRate)
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
twoPiF := 2.0 * math.Pi * baseFreq
for j := 0; j &lt; len(amp); j&#43;&#43; {
a := amp[j] * math.Exp(-5*float64(i)/(x[j]*s))
v &#43;= a * math.Sin(float64(i)*twoPiF*float64(j&#43;1)/s)
}
pcm[i] = v / 5.0
}
}
var (
noteCache = map[int][]byte{}
)
func toBytes(l, r []int16) []byte {
if len(l) != len(r) {
panic(&#34;len(l) must equal to len(r)&#34;)
}
b := make([]byte, len(l)*4)
for i, _ := range l {
b[4*i] = byte(l[i])
b[4*i&#43;1] = byte(l[i] &gt;&gt; 8)
b[4*i&#43;2] = byte(r[i])
b[4*i&#43;3] = byte(r[i] &gt;&gt; 8)
}
return b
}
type stream struct {
*bytes.Reader
}
func (s *stream) Close() error {
s.Reader = nil
return nil
}
func addNote(freq float64, vol float64) error {
f := int(freq)
if n, ok := noteCache[f]; ok {
p, err := audioContext.NewPlayer(&amp;stream{bytes.NewReader(n)})
if err != nil {
return err
}
p.Play()
return nil
}
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, err := audioContext.NewPlayer(&amp;stream{bytes.NewReader(n)})
if err != nil {
return err
}
p.Play()
return nil
}
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{}
func init() {
for _, key := range keys {
keyStates[key] = 0
}
}
func updateInput() {
for _, key := range keys {
if !ebiten.IsKeyPressed(key) {
keyStates[key] = 0
continue
}
keyStates[key]&#43;&#43;
}
}
var (
imagePiano *ebiten.Image
)
func init() {
var err error
imageEmpty, err := ebiten.NewImage(16, 16, ebiten.FilterNearest)
if err != nil {
panic(err)
}
imageEmpty.Fill(color.White)
imagePiano, err = ebiten.NewImage(screenWidth, screenHeight, ebiten.FilterNearest)
if err != nil {
panic(err)
}
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
op := &amp;ebiten.DrawImageOptions{}
w, h := imageEmpty.Size()
op.GeoM.Scale(float64(width-1)/float64(w), float64(height)/float64(h))
op.GeoM.Translate(float64(x), float64(y))
op.ColorM.Scale(1, 1, 1, 1)
imagePiano.DrawImage(imageEmpty, op)
common.ArcadeFont.DrawText(imagePiano, k, x&#43;8, y&#43;height-16, 1, 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
op := &amp;ebiten.DrawImageOptions{}
w, h := imageEmpty.Size()
op.GeoM.Scale(float64(width-1)/float64(w), float64(height)/float64(h))
op.GeoM.Translate(float64(x), float64(y))
op.ColorM.Scale(0, 0, 0, 1)
imagePiano.DrawImage(imageEmpty, op)
common.ArcadeFont.DrawText(imagePiano, k, x&#43;8, y&#43;height-16, 1, color.White)
}
}
func update(screen *ebiten.Image) error {
if err := audioContext.Update(); err != nil {
return err
}
updateInput()
for i, key := range keys {
if keyStates[key] != 1 {
continue
}
if err := addNote(220*math.Exp2(float64(i-1)/12.0), 1.0); err != nil {
return err
}
}
screen.Fill(color.RGBA{0x80, 0x80, 0xc0, 0xff})
screen.DrawImage(imagePiano, nil)
ebitenutil.DebugPrint(screen, fmt.Sprintf(&#34;FPS: %0.2f&#34;, ebiten.CurrentFPS()))
return nil
}
func main() {
if err := ebiten.Run(update, screenWidth, screenHeight, 2, &#34;Piano (Ebiten Demo)&#34;); err != nil {
log.Fatal(err)
}
}
</code></pre>
<footer>© 2014 Hajime Hoshi</footer>

View File

@ -63,7 +63,7 @@ pre {
<h2>Example</h2>
<p>
<a href="examples/alphablending.html"><img src="images/examples/alphablending.png" width="320" height="240" alt="Ebiten example: alphablending" class="example"></a><a href="examples/hue.html"><img src="images/examples/hue.png" width="320" height="240" alt="Ebiten example: hue" class="example"></a><a href="examples/gamepad.html"><img src="images/examples/gamepad.png" width="320" height="240" alt="Ebiten example: gamepad" class="example"></a><a href="examples/keyboard.html"><img src="images/examples/keyboard.png" width="320" height="240" alt="Ebiten example: keyboard" class="example"></a><a href="examples/mosaic.html"><img src="images/examples/mosaic.png" width="320" height="240" alt="Ebiten example: mosaic" class="example"></a><a href="examples/noise.html"><img src="images/examples/noise.png" width="320" height="240" alt="Ebiten example: noise" class="example"></a><a href="examples/paint.html"><img src="images/examples/paint.png" width="320" height="240" alt="Ebiten example: paint" class="example"></a><a href="examples/perspective.html"><img src="images/examples/perspective.png" width="320" height="240" alt="Ebiten example: perspective" class="example"></a><a href="examples/rotate.html"><img src="images/examples/rotate.png" width="320" height="240" alt="Ebiten example: rotate" class="example"></a><a href="examples/sprites.html"><img src="images/examples/sprites.png" width="320" height="240" alt="Ebiten example: sprites" class="example"></a><a href="examples/blocks.html"><img src="images/examples/blocks.png" width="256" height="240" alt="Ebiten example: blocks" class="example"></a>
<a href="examples/alphablending.html"><img src="images/examples/alphablending.png" width="320" height="240" alt="Ebiten example: alphablending" class="example"></a><a href="examples/audio.html"><img src="images/examples/audio.png" width="320" height="240" alt="Ebiten example: audio" class="example"></a><a href="examples/font.html"><img src="images/examples/font.png" width="320" height="240" alt="Ebiten example: font" class="example"></a><a href="examples/hsv.html"><img src="images/examples/hsv.png" width="320" height="240" alt="Ebiten example: hsv" class="example"></a><a href="examples/hue.html"><img src="images/examples/hue.png" width="320" height="240" alt="Ebiten example: hue" class="example"></a><a href="examples/gamepad.html"><img src="images/examples/gamepad.png" width="320" height="240" alt="Ebiten example: gamepad" class="example"></a><a href="examples/keyboard.html"><img src="images/examples/keyboard.png" width="320" height="240" alt="Ebiten example: keyboard" class="example"></a><a href="examples/masking.html"><img src="images/examples/masking.png" width="320" height="240" alt="Ebiten example: masking" class="example"></a><a href="examples/mosaic.html"><img src="images/examples/mosaic.png" width="320" height="240" alt="Ebiten example: mosaic" class="example"></a><a href="examples/noise.html"><img src="images/examples/noise.png" width="320" height="240" alt="Ebiten example: noise" class="example"></a><a href="examples/paint.html"><img src="images/examples/paint.png" width="320" height="240" alt="Ebiten example: paint" class="example"></a><a href="examples/perspective.html"><img src="images/examples/perspective.png" width="320" height="240" alt="Ebiten example: perspective" class="example"></a><a href="examples/piano.html"><img src="images/examples/piano.png" width="320" height="240" alt="Ebiten example: piano" class="example"></a><a href="examples/rotate.html"><img src="images/examples/rotate.png" width="320" height="240" alt="Ebiten example: rotate" class="example"></a><a href="examples/sprites.html"><img src="images/examples/sprites.png" width="320" height="240" alt="Ebiten example: sprites" class="example"></a><a href="examples/blocks.html"><img src="images/examples/blocks.png" width="256" height="240" alt="Ebiten example: blocks" class="example"></a>
</p>
<h2>Install on Mac OS X or Windows</h2>