-
Notifications
You must be signed in to change notification settings - Fork 23
/
main.go
162 lines (145 loc) · 4.14 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package main
import (
"bytes"
"encoding/base64"
"fmt"
"html/template"
"image"
"image/color"
"image/png"
"log"
"net/http"
"os"
"path/filepath"
"time"
"github.com/zserge/webview"
"github.com/faiface/beep/speaker"
"github.com/faiface/beep/wav"
"golang.org/x/image/font"
"golang.org/x/image/font/inconsolata"
"golang.org/x/image/math/fixed"
"net"
)
var frame string // game frames
var dir string // current directory
var events chan string // keyboard events
var gameOver = false // end of game
var windowWidth, windowHeight = 400, 300 // width and height of the window
var frameRate int // how many frames to show per second (fps)
var gameDelay int // delay time added to each game loop
func init() {
// events is a channel of string events that come from the front end
events = make(chan string, 1000)
// getting the current directory to access resources
var err error
dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatal(err)
}
frameRate = 50 // 50 fps
gameDelay = 20 // 20 ms delay
sprites = getImage(dir + "/public/images/sprites.png") // spritesheet
background = getImage(dir + "/public/images/bg.png") // background image
}
// main function
func main() {
// channel to get the web prefix
prefixChannel := make(chan string)
// run the web server in a separate goroutine
go app(prefixChannel)
prefix := <- prefixChannel
// create a web view
err := webview.Open("Space Invaders", prefix + "/public/html/index.html",
windowWidth, windowHeight, false)
if err != nil {
log.Fatal(err)
}
}
// web app
func app(prefixChannel chan string) {
mux := http.NewServeMux()
mux.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir(dir+"/public"))))
mux.HandleFunc("/start", start)
mux.HandleFunc("/frame", getFrame)
mux.HandleFunc("/key", captureKeys)
// get an ephemeral port, so we're guaranteed not to conflict with anything else
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
panic(err)
}
portAddress := listener.Addr().String()
prefixChannel <- "http://" + portAddress
listener.Close()
server := &http.Server{
Addr: portAddress,
Handler: mux,
}
server.ListenAndServe()
}
// start the game
func start(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles(dir + "/public/html/invaders.html")
// start generating frames in a new goroutine
go generateFrames()
t.Execute(w, 1000/frameRate)
}
// capture keyboard events
func captureKeys(w http.ResponseWriter, r *http.Request) {
ev := r.FormValue("event")
// what to react to when the game is over
if gameOver {
if ev == "83" { // s
gameOver = false
go generateFrames()
}
if ev == "81" { // q
os.Exit(0)
}
} else {
events <- ev
}
w.Header().Set("Cache-Control", "no-cache")
}
// get the game frames
func getFrame(w http.ResponseWriter, r *http.Request) {
str := "data:image/png;base64," + frame
w.Header().Set("Cache-Control", "no-cache")
w.Write([]byte(str))
}
// print a line of text to the image
func printLine(img *image.RGBA, x, y int, label string, col color.RGBA) {
point := fixed.Point26_6{X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64)}
d := &font.Drawer{
Dst: img,
Src: image.NewUniform(col),
Face: inconsolata.Bold8x16,
Dot: point,
}
d.DrawString(label)
}
// create a frame from the image
func createFrame(img image.Image) {
var buf bytes.Buffer
png.Encode(&buf, img)
frame = base64.StdEncoding.EncodeToString(buf.Bytes())
}
// play a sound
func playSound(name string) {
f, _ := os.Open(dir + "/public/sounds/" + name + ".wav")
s, format, _ := wav.Decode(f)
speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/20))
speaker.Play(s)
}
// get an image from the file
func getImage(filePath string) image.Image {
imgFile, err := os.Open(filePath)
defer imgFile.Close()
if err != nil {
fmt.Println("Cannot read file:", err)
}
img, _, err := image.Decode(imgFile)
if err != nil {
fmt.Println("Cannot decode file:", err)
}
return img
}