ebitest.go (4780B)
1 package main 2 3 import ( 4 _ "bytes" 5 "errors" 6 "fmt" 7 "github.com/hajimehoshi/ebiten/v2" 8 "github.com/hajimehoshi/ebiten/v2/ebitenutil" 9 "image/color" 10 _ "image/jpeg" 11 _ "image/png" 12 "io" 13 "log" 14 "math" 15 "math/rand" 16 "net/http" 17 "os" 18 "strconv" 19 "time" 20 ) 21 22 const S_FRAMERATE = 60 23 const S_SPEED = 200.0 24 const S_TTL = 20 25 26 var has_req bool = false 27 28 var NumSprites int = 1 29 var ScreenSize struct { 30 width, height int 31 } 32 33 // images initialized in init() 34 var Images = make(map[string]*ebiten.Image) 35 36 type Sprite struct { 37 img *ebiten.Image 38 opt ebiten.DrawImageOptions 39 x, y float64 40 angle float64 41 rotation float64 42 expires int64 43 } 44 45 type Game struct { 46 inited bool 47 sprites []*Sprite 48 animation *TGifObject 49 } 50 51 var game *Game 52 53 func (g *Game) Update() error { 54 if !g.inited { 55 fmt.Println("(Game::update()) calling Game::init()") 56 g.init() 57 } 58 59 if ebiten.IsKeyPressed(ebiten.KeyQ) || ebiten.IsKeyPressed(ebiten.KeyEscape) { 60 // @todo find out if it is possible to gracefully stop the game via ebiten structure 61 return errors.New("exit") 62 } 63 64 if ebiten.IsKeyPressed(ebiten.KeySpace) || has_req { 65 g.sprites = append(g.sprites, makeRandomSprite()) 66 has_req = false 67 } 68 69 sprites_new := []*Sprite{} 70 for _, sprite := range g.sprites { 71 if sprite.expires > time.Now().Unix() { 72 sprites_new = append(sprites_new, sprite) 73 } 74 } 75 76 g.sprites = sprites_new 77 for _, sprite := range g.sprites { 78 err := sprite.Update() 79 if err != nil { 80 return err 81 } 82 } 83 84 _ = g.animation.Update() 85 86 return nil 87 } 88 89 func (sprite *Sprite) Update() error { 90 wInt, hInt := sprite.img.Size() 91 w, h := float64(wInt), float64(hInt) 92 fps := ebiten.CurrentFPS() 93 if fps == 0 { 94 fps = S_FRAMERATE 95 } 96 97 delta := S_SPEED / fps 98 dx := delta * math.Cos(sprite.angle) 99 dy := delta * math.Sin(sprite.angle) 100 101 x := sprite.x + dx 102 y := sprite.y + dy 103 104 scrW, scrH := float64(ScreenSize.width), float64(ScreenSize.height) 105 106 bounce := false 107 // bouncing off walls 108 if x+w >= scrW || x <= 0 { 109 sprite.angle = math.Pi*3 - sprite.angle 110 bounce = true 111 } else if y+h >= scrH || y <= 0 { 112 sprite.angle = math.Pi*2 - sprite.angle 113 bounce = true 114 } 115 116 if bounce { 117 dx = delta * math.Cos(sprite.angle) 118 dy = delta * math.Sin(sprite.angle) 119 x = sprite.x + dx 120 y = sprite.y + dy 121 122 if x+w >= scrW || x <= 0 { 123 sprite.x -= dx 124 } 125 if y+h >= scrH || y <= 0 { 126 sprite.y -= dy 127 } 128 129 } else { 130 sprite.x = x 131 sprite.y = y 132 } 133 134 // rotating image 135 sprite.rotation += math.Pi / 60.0 136 137 return nil 138 } 139 140 func (g *Game) Draw(screen *ebiten.Image) { 141 //defer g.ShowDebug(screen) 142 143 // background 144 screen.Fill(color.RGBA{0, 0xFF, 0, 0xFF}) 145 146 for _, sprite := range g.sprites { 147 w, h := sprite.img.Size() 148 sprite.opt.GeoM.Reset() 149 sprite.opt.GeoM.Translate(-float64(w)/2, -float64(h)/2) 150 sprite.opt.GeoM.Rotate(sprite.rotation) 151 sprite.opt.GeoM.Translate(float64(w/2), float64(h/2)) 152 sprite.opt.GeoM.Translate(sprite.x, sprite.y) 153 //sprite.opt.ColorM.Translate(0, 0, 0, -0.0003) 154 screen.DrawImage(sprite.img, &sprite.opt) 155 } 156 157 g.animation.Draw(screen) 158 } 159 160 func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { 161 return ScreenSize.width, ScreenSize.height 162 } 163 164 func (g *Game) init() { 165 defer func() { 166 g.inited = true 167 fmt.Println("Game inited ok") 168 }() 169 fmt.Println("Game::init()") 170 rand.Seed(time.Now().UnixNano()) 171 172 for i := 0; i < NumSprites; i++ { 173 g.sprites = append(g.sprites, makeRandomSprite()) 174 } 175 176 var err error 177 g.animation, err = MakeGifObject("assets/terry1.gif") 178 if err != nil { 179 log.Fatal(err) 180 } 181 } 182 183 func makeRandomSprite() *Sprite { 184 x := float64(ScreenSize.width) / 2.0 185 y := float64(ScreenSize.height) / 2.0 186 angle := rand.Float64() * math.Pi * 2.0 187 fmt.Printf("initial x, y, angle: %0.2f %0.2f %0.2f\n", x, y, angle) 188 return makeSprite("chikaiKnife.png", x, y, angle) 189 } 190 191 func main() { 192 if len(os.Args) > 1 { 193 NumSprites, _ = strconv.Atoi(os.Args[1]) 194 } 195 196 game = &Game{} 197 ebiten.SetWindowSize(ScreenSize.width, ScreenSize.height) 198 ebiten.SetWindowTitle("Twitchapon Receiver Module") 199 ebiten.SetMaxTPS(S_FRAMERATE) 200 ebiten.SetRunnableOnUnfocused(true) 201 202 http.HandleFunc("/", handleHttpRequest) 203 go func() { 204 http.ListenAndServe("localhost:2345", nil) 205 }() 206 207 if err := ebiten.RunGame(game); err != nil { 208 log.Fatal(err) 209 } 210 } 211 212 func makeSprite(name string, x float64, y float64, angle float64) *Sprite { 213 return &Sprite{Images[name], ebiten.DrawImageOptions{}, x, y, angle, 0.0 /* rotation */, time.Now().Unix() + S_TTL /* expires */} 214 } 215 216 func (g *Game) ShowDebug(screen *ebiten.Image) { 217 msg := fmt.Sprintf("TPS: %0.2f\nFPS: %0.2f", ebiten.CurrentTPS(), ebiten.CurrentFPS()) 218 ebitenutil.DebugPrint(screen, msg) 219 } 220 221 func handleHttpRequest(w http.ResponseWriter, req *http.Request) { 222 io.WriteString(w, "OK\n") 223 fmt.Println("http request ok") 224 has_req = true 225 }