First commit.
This commit is contained in:
6
coordinates.go
Normal file
6
coordinates.go
Normal file
@@ -0,0 +1,6 @@
|
||||
package main
|
||||
|
||||
type Coordinates struct {
|
||||
X float64
|
||||
Y float64
|
||||
}
|
||||
40
explosion.go
Normal file
40
explosion.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
type Explosion struct {
|
||||
Radius float64
|
||||
Origin Coordinates
|
||||
cycle int
|
||||
Active bool
|
||||
}
|
||||
|
||||
func NewExplosion() *Explosion {
|
||||
return &Explosion{
|
||||
cycle: 1,
|
||||
Active: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Explosion) Draw() {
|
||||
|
||||
}
|
||||
|
||||
func (e *Explosion) Update() {
|
||||
|
||||
if e.Active {
|
||||
e.Radius = float64(e.cycle) / 0.15
|
||||
e.cycle++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (e *Explosion) SetOrigin(origin Coordinates) {
|
||||
e.Origin = origin
|
||||
}
|
||||
|
||||
func (e *Explosion) ToggleActivate() {
|
||||
e.Active = !e.Active
|
||||
}
|
||||
|
||||
func (e *Explosion) Reset() {
|
||||
e.cycle = 1
|
||||
}
|
||||
BIN
fly-eye.png
Normal file
BIN
fly-eye.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
fly-eye2.png
Normal file
BIN
fly-eye2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
BIN
fly-eye3.png
Normal file
BIN
fly-eye3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 682 B |
308
game.go
Normal file
308
game.go
Normal file
@@ -0,0 +1,308 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
const (
|
||||
MOVER_WIDTH = 48
|
||||
MOVER_HEIGHT = 48
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
collisionMask *ebiten.Image
|
||||
projectileMask *ebiten.Image
|
||||
|
||||
Pos Coordinates
|
||||
initialized bool
|
||||
mover *Mover
|
||||
projectiles map[int]*Projectile
|
||||
explosion *Explosion
|
||||
|
||||
counter int
|
||||
targets []*Mover
|
||||
|
||||
gamepadIDsBuf []ebiten.GamepadID
|
||||
gamepadIDs map[ebiten.GamepadID]struct{}
|
||||
//axes map[ebiten.GamepadID][]string
|
||||
//pressedButtons map[ebiten.GamepadID][]string
|
||||
}
|
||||
|
||||
func (g *Game) Initialize() {
|
||||
|
||||
origin := Coordinates{X: 640 / 2, Y: 480 / 2}
|
||||
|
||||
g.mover = NewMover()
|
||||
g.mover.SetOrigin(origin)
|
||||
g.mover.ToggleRotate()
|
||||
|
||||
g.collisionMask = ebiten.NewImage(screenWidth, screenHeight)
|
||||
g.projectileMask = ebiten.NewImage(screenWidth, screenHeight)
|
||||
|
||||
/* single target
|
||||
g.target = NewMover()
|
||||
g.target.SetOrigin(Coordinates{X: rand.Float64() * 640, Y: rand.Float64() * 480})
|
||||
*/
|
||||
//g.projectiles = append(g.projectiles, NewProjectile(Coordinates{X: 640 / 2, Y: 480 / 2}, 0., 5.))
|
||||
|
||||
g.explosion = NewExplosion()
|
||||
g.explosion.SetOrigin(origin)
|
||||
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
|
||||
if g.gamepadIDs == nil {
|
||||
g.gamepadIDs = map[ebiten.GamepadID]struct{}{}
|
||||
}
|
||||
|
||||
g.gamepadIDsBuf = inpututil.AppendJustConnectedGamepadIDs(g.gamepadIDsBuf[:0])
|
||||
for _, id := range g.gamepadIDsBuf {
|
||||
log.Printf("gamepad connected: id: %d, SDL ID: %s", id, ebiten.GamepadSDLID(id))
|
||||
g.gamepadIDs[id] = struct{}{}
|
||||
}
|
||||
for id := range g.gamepadIDs {
|
||||
if inpututil.IsGamepadJustDisconnected(id) {
|
||||
log.Printf("gamepad disconnected: id: %d", id)
|
||||
delete(g.gamepadIDs, id)
|
||||
}
|
||||
}
|
||||
|
||||
//handle gamepad input
|
||||
inpx := ebiten.GamepadAxisValue(0, 0)
|
||||
inpy := ebiten.GamepadAxisValue(0, 1)
|
||||
if inpx >= 0.15 || inpx <= -0.15 {
|
||||
g.mover.Pos.X += ebiten.GamepadAxisValue(0, 0) * 5
|
||||
}
|
||||
|
||||
if inpy >= 0.15 || inpy <= -0.15 {
|
||||
g.mover.Pos.Y += ebiten.GamepadAxisValue(0, 1) * 5
|
||||
}
|
||||
|
||||
if !g.initialized {
|
||||
g.Initialize()
|
||||
|
||||
g.projectiles = make(map[int]*Projectile)
|
||||
g.initialized = true
|
||||
} else {
|
||||
|
||||
if len(g.gamepadIDs) > 0 {
|
||||
xaxis := ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickHorizontal)
|
||||
yaxis := ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickVertical)
|
||||
|
||||
maxButton := ebiten.GamepadButton(ebiten.GamepadButtonCount(0))
|
||||
for b := ebiten.GamepadButton(0); b < maxButton; b++ {
|
||||
if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton7) {
|
||||
if !g.explosion.Active {
|
||||
g.explosion.SetOrigin(g.mover.Pos)
|
||||
g.explosion.Reset()
|
||||
g.explosion.ToggleActivate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if yaxis <= 0.09 && yaxis >= -0.09 {
|
||||
yaxis = 0
|
||||
}
|
||||
if xaxis <= 0.09 && xaxis >= -0.09 {
|
||||
xaxis = 0
|
||||
}
|
||||
|
||||
inputangle := math.Atan2(yaxis, xaxis)
|
||||
g.mover.SetAngle(inputangle)
|
||||
}
|
||||
/*
|
||||
for id := range g.gamepadIDs {
|
||||
xaxis := ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickHorizontal)
|
||||
yaxis := ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickVertical)
|
||||
|
||||
inputangle := math.Atan2(yaxis, xaxis)
|
||||
g.mover.SetAngle(inputangle)
|
||||
}
|
||||
*/
|
||||
|
||||
g.mover.Update()
|
||||
g.explosion.Update()
|
||||
|
||||
for _, target := range g.targets {
|
||||
|
||||
if !target.Hit {
|
||||
dx := g.mover.Pos.X - target.Pos.X
|
||||
dy := g.mover.Pos.Y - target.Pos.Y
|
||||
|
||||
//dist := math.Sqrt(dx*dx + dy + dy)
|
||||
angle := math.Atan2(dy, dx)
|
||||
|
||||
maxspeed := 3.
|
||||
target.Pos.X += maxspeed * math.Cos(angle)
|
||||
target.Pos.Y += maxspeed * math.Sin(angle)
|
||||
}
|
||||
|
||||
target.Update()
|
||||
}
|
||||
|
||||
for k, p := range g.projectiles {
|
||||
|
||||
//for i := 0; i < len(g.projectiles); i++ {
|
||||
// g.projectiles[i].Update()
|
||||
p.Update()
|
||||
|
||||
//if g.projectiles[i].Pos.X < 5 || g.projectiles[i].Pos.X > 635 || g.projectiles[i].Pos.Y < 5 || g.projectiles[i].Pos.Y > 475 {
|
||||
if p.Pos.X < 5 || p.Pos.X > 635 || p.Pos.Y < 5 || p.Pos.Y > 475 {
|
||||
p.Velocity = 0
|
||||
delete(g.projectiles, k)
|
||||
}
|
||||
|
||||
//compute collisions
|
||||
for _, target := range g.targets {
|
||||
//first, boundary check
|
||||
if p.Pos.X >= target.Pos.X-MOVER_WIDTH/2 && p.Pos.X <= target.Pos.X+MOVER_WIDTH/2 && p.Pos.Y >= target.Pos.Y-MOVER_HEIGHT/2 && p.Pos.Y <= target.Pos.Y+MOVER_HEIGHT/2 && target.Action == MoverActionDamaged {
|
||||
fmt.Println("potential collision")
|
||||
|
||||
g.collisionMask.Clear()
|
||||
g.collisionMask.DrawImage(g.projectileMask, nil)
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Reset()
|
||||
op.Blend = ebiten.BlendSourceIn
|
||||
op.GeoM.Translate(target.Pos.X-MOVER_WIDTH/2, target.Pos.Y-MOVER_HEIGHT/2)
|
||||
g.collisionMask.DrawImage(target.Sprite, op)
|
||||
|
||||
//var pixels []byte = make([]byte, MOVER_WIDTH*MOVER_HEIGHT*4)
|
||||
var pixels []byte = make([]byte, screenWidth*screenHeight*4)
|
||||
g.collisionMask.ReadPixels(pixels)
|
||||
for i := 0; i < len(pixels); i = i + 4 {
|
||||
if pixels[i+3] != 0 {
|
||||
fmt.Println("pixel collision")
|
||||
delete(g.projectiles, k)
|
||||
//target.ToggleColor()
|
||||
target.SetHit()
|
||||
//target.SetOrigin(Coordinates{X: rand.Float64() * 640, Y: rand.Float64() * 480})
|
||||
target.Hit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//append new projectiles
|
||||
if g.counter%14 == 0 {
|
||||
//g.projectiles = append(g.projectiles, NewProjectile(Coordinates{X: g.mover.Pos.X, Y: g.mover.Pos.Y}, g.mover.Angle, 5.))
|
||||
//g.projectiles = append(g.projectiles, NewProjectile(Coordinates{X: g.mover.Pos.X, Y: g.mover.Pos.Y}, g.mover.Angle+math.Pi, 5.))
|
||||
|
||||
g.projectiles[g.counter] = NewProjectile(Coordinates{X: g.mover.Pos.X, Y: g.mover.Pos.Y}, g.mover.Angle, 5.)
|
||||
g.projectiles[g.counter+1] = NewProjectile(Coordinates{X: g.mover.Pos.X, Y: g.mover.Pos.Y}, g.mover.Angle+math.Pi, 5.)
|
||||
}
|
||||
|
||||
//add new target with increasing frequency
|
||||
|
||||
f := 40000 / (g.counter + 1)
|
||||
|
||||
if g.counter%f == 0 {
|
||||
g.targets = append(g.targets, NewMover())
|
||||
g.targets[len(g.targets)-1].SetOrigin(Coordinates{X: rand.Float64() * 640, Y: rand.Float64() * 480})
|
||||
}
|
||||
|
||||
//handle explosion updates
|
||||
if g.explosion.Active {
|
||||
if g.explosion.Radius > math.Sqrt(640*640+480*480) {
|
||||
g.explosion.ToggleActivate()
|
||||
g.explosion.Reset()
|
||||
}
|
||||
|
||||
//check collisions
|
||||
for _, target := range g.targets {
|
||||
dx := target.Pos.X - g.mover.Pos.X
|
||||
dy := target.Pos.Y - g.mover.Pos.Y
|
||||
r := math.Sqrt(dx*dx + dy*dy)
|
||||
|
||||
if r >= g.explosion.Radius-5 && r <= g.explosion.Radius+5 && target.Action <= MoverActionDefault {
|
||||
//target.ToggleColor()
|
||||
target.SetHit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g.counter++
|
||||
}
|
||||
|
||||
g.CleanupTargets()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
|
||||
g.mover.Draw()
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
|
||||
/*
|
||||
dx := 40 * math.Cos(float64(g.counter)/16)
|
||||
dy := 40 * math.Sin(float64(g.counter)/16)
|
||||
a := float64(g.counter) / (math.Pi * 2)
|
||||
*/
|
||||
op.GeoM.Translate(-MOVER_WIDTH/2, -MOVER_HEIGHT/2)
|
||||
op.GeoM.Rotate(g.mover.Angle)
|
||||
op.GeoM.Translate(g.mover.Pos.X, g.mover.Pos.Y)
|
||||
screen.DrawImage(g.mover.Sprite, op)
|
||||
|
||||
for _, target := range g.targets {
|
||||
target.Draw()
|
||||
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(-MOVER_WIDTH/2, -MOVER_HEIGHT/2)
|
||||
op.GeoM.Rotate(target.Angle)
|
||||
op.GeoM.Translate(target.Pos.X, target.Pos.Y)
|
||||
screen.DrawImage(target.Sprite, op)
|
||||
}
|
||||
|
||||
g.projectileMask.Clear()
|
||||
|
||||
//ebitenutil.DrawCircle()
|
||||
for _, p := range g.projectiles {
|
||||
//vector.DrawFilledCircle(screen, float32(p.Pos.X), float32(p.Pos.Y), 3, color.White, true)
|
||||
vector.DrawFilledCircle(g.projectileMask, float32(p.Pos.X), float32(p.Pos.Y), 3, color.White, true)
|
||||
}
|
||||
|
||||
screen.DrawImage(g.projectileMask, nil)
|
||||
|
||||
vector.StrokeCircle(screen, float32(g.explosion.Origin.X), float32(g.explosion.Origin.Y), float32(g.explosion.Radius), 3, color.White, true)
|
||||
|
||||
/*for _, gamepad ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickHorizontal),
|
||||
ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisRightStickVertical))*/
|
||||
|
||||
}
|
||||
|
||||
func (g *Game) Layout(width, height int) (int, int) {
|
||||
return screenWidth, screenHeight
|
||||
}
|
||||
|
||||
func (g *Game) CleanupTargets() {
|
||||
// remove dead targets by 1) iterating over all targets
|
||||
i := 0
|
||||
for _, target := range g.targets {
|
||||
//moving valid targets to the front of the slice
|
||||
if target.Action < MoverActionDead {
|
||||
g.targets[i] = target
|
||||
i++
|
||||
}
|
||||
}
|
||||
//then culling the last elements of the slice
|
||||
if len(g.targets)-i > 0 {
|
||||
fmt.Printf("Removing %d elements\n", len(g.targets)-i)
|
||||
}
|
||||
for j := i; j < len(g.targets); j++ {
|
||||
g.targets[j] = nil
|
||||
}
|
||||
g.targets = g.targets[:i]
|
||||
}
|
||||
29
main.go
Normal file
29
main.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
screenWidth = 640
|
||||
screenHeight = 480
|
||||
)
|
||||
|
||||
func main() {
|
||||
ver := "Mover Test v0.05"
|
||||
|
||||
fmt.Println(ver)
|
||||
|
||||
moverGame := &Game{}
|
||||
|
||||
ebiten.SetWindowSize(screenWidth*1.5, screenHeight*1.5)
|
||||
ebiten.SetWindowTitle(ver)
|
||||
|
||||
if err := ebiten.RunGame(moverGame); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
168
mover.go
Normal file
168
mover.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"log"
|
||||
|
||||
_ "embed"
|
||||
"image/color"
|
||||
_ "image/png"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
flyeyeImage *ebiten.Image
|
||||
flyeyeImage2 *ebiten.Image
|
||||
flyeyeImage3 *ebiten.Image
|
||||
|
||||
//go:embed fly-eye.png
|
||||
flyeye_img []byte
|
||||
//go:embed fly-eye2.png
|
||||
flyeye_img2 []byte
|
||||
//go:embed fly-eye3.png
|
||||
flyeye_img3 []byte
|
||||
)
|
||||
|
||||
const (
|
||||
MoverActionDefault = iota
|
||||
MoverActionDamaged
|
||||
MoverActionDying
|
||||
MoverActionExploding
|
||||
MoverActionDead
|
||||
MoverActionMax
|
||||
)
|
||||
|
||||
type MoverAction uint
|
||||
|
||||
func init() {
|
||||
img, _, err := image.Decode(bytes.NewReader(flyeye_img))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
flyeyeImage = ebiten.NewImageFromImage(img)
|
||||
|
||||
img, _, err = image.Decode(bytes.NewReader(flyeye_img2))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
flyeyeImage2 = ebiten.NewImageFromImage(img)
|
||||
|
||||
img, _, err = image.Decode(bytes.NewReader(flyeye_img3))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
flyeyeImage3 = ebiten.NewImageFromImage(img)
|
||||
}
|
||||
|
||||
type Mover struct {
|
||||
Sprite *ebiten.Image
|
||||
Maks *ebiten.Image
|
||||
MaksDest *ebiten.Image
|
||||
Angle float64
|
||||
Pos Coordinates
|
||||
Origin Coordinates
|
||||
Action MoverAction
|
||||
cycles int
|
||||
rotating bool
|
||||
Toggled bool
|
||||
Hit bool
|
||||
dyingcount int
|
||||
}
|
||||
|
||||
func NewMover() *Mover {
|
||||
m := &Mover{
|
||||
Sprite: ebiten.NewImage(48, 48),
|
||||
Maks: ebiten.NewImage(48, 48),
|
||||
MaksDest: ebiten.NewImage(48, 48),
|
||||
Action: MoverActionDefault,
|
||||
cycles: 4,
|
||||
Angle: 0,
|
||||
rotating: false,
|
||||
Toggled: false,
|
||||
dyingcount: 0,
|
||||
}
|
||||
|
||||
m.Maks.Fill(color.White)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Mover) ToggleRotate() {
|
||||
m.rotating = !m.rotating
|
||||
}
|
||||
|
||||
func (m *Mover) SetAngle(a float64) {
|
||||
m.Angle = a
|
||||
}
|
||||
|
||||
func (m *Mover) SetOrigin(coords Coordinates) {
|
||||
m.Origin = coords
|
||||
m.Pos = coords
|
||||
}
|
||||
|
||||
func (m *Mover) Draw() {
|
||||
m.Sprite.Clear()
|
||||
m.MaksDest.Clear()
|
||||
|
||||
idx := (m.cycles / 8) % 4
|
||||
|
||||
y0 := 0
|
||||
y1 := 48
|
||||
x0 := 48 * idx
|
||||
x1 := x0 + 48
|
||||
|
||||
switch m.Action {
|
||||
case MoverActionDefault:
|
||||
m.Sprite.DrawImage(flyeyeImage.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
case MoverActionDamaged:
|
||||
m.Sprite.DrawImage(flyeyeImage2.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
case MoverActionDying:
|
||||
m.dyingcount++
|
||||
if (m.cycles/5)%2 == 0 {
|
||||
|
||||
m.MaksDest.DrawImage(flyeyeImage2.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Reset()
|
||||
op.Blend = ebiten.BlendSourceAtop
|
||||
m.MaksDest.DrawImage(m.Maks, op)
|
||||
m.Sprite.DrawImage(m.MaksDest, nil)
|
||||
|
||||
} else {
|
||||
m.Sprite.DrawImage(flyeyeImage2.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
}
|
||||
if m.dyingcount > 60 {
|
||||
m.cycles = 0
|
||||
m.SetHit()
|
||||
}
|
||||
case MoverActionExploding:
|
||||
m.Sprite.DrawImage(flyeyeImage3.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
if idx == 3 {
|
||||
m.SetHit()
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mover) Update() {
|
||||
/*
|
||||
dx := 0. //40 * math.Cos(float64(m.cycles)/16)
|
||||
dy := 0. //40 * math.Sin(float64(m.cycles)/16)
|
||||
|
||||
m.Pos = Coordinates{X: m.Origin.X + dx, Y: m.Origin.Y + dy}
|
||||
*/
|
||||
/*
|
||||
if m.rotating {
|
||||
m.Angle = float64(m.cycles) / (math.Pi * 2)
|
||||
}
|
||||
*/
|
||||
m.cycles++
|
||||
}
|
||||
|
||||
func (m *Mover) SetHit() {
|
||||
m.Action++ // = (m.Action + 1) % MoverActionMax
|
||||
}
|
||||
|
||||
func (m *Mover) ToggleColor() {
|
||||
m.Toggled = !m.Toggled
|
||||
}
|
||||
31
projectile.go
Normal file
31
projectile.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import "math"
|
||||
|
||||
type Projectile struct {
|
||||
Pos Coordinates
|
||||
Velocity float64
|
||||
a float64
|
||||
}
|
||||
|
||||
func NewProjectile(origin Coordinates, angle, velocity float64) *Projectile {
|
||||
return &Projectile{
|
||||
Velocity: velocity,
|
||||
a: angle,
|
||||
Pos: origin,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Projectile) Update() {
|
||||
|
||||
dx := p.Velocity * math.Cos(p.a)
|
||||
dy := p.Velocity * math.Sin(p.a)
|
||||
|
||||
p.Pos.X += dx
|
||||
p.Pos.Y += dy
|
||||
|
||||
}
|
||||
|
||||
func (p *Projectile) Draw() {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user