From d336fa4d15c263551e1ab4ab617bbd8b236b77ad Mon Sep 17 00:00:00 2001 From: Senad Uka Date: Thu, 28 Dec 2023 14:42:20 +0100 Subject: [PATCH] Bullet support --- collision/types.go | 1 + collision/world.go | 5 +- hero/hero.go | 15 +++++ input/keyboard.go | 5 ++ main.go | 29 +++++++++ resources/embed.go | 3 + resources/handgun.png | Bin 0 -> 967 bytes weapons/handgun.go | 134 ++++++++++++++++++++++++++++++++++++++++++ zombie/zombie.go | 6 +- 9 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 resources/handgun.png create mode 100644 weapons/handgun.go diff --git a/collision/types.go b/collision/types.go index f682d69..fd070a9 100644 --- a/collision/types.go +++ b/collision/types.go @@ -14,6 +14,7 @@ type ObjectType int const ( Hero ObjectType = 1 << iota Zombie + Bullet ) type Collidable interface { diff --git a/collision/world.go b/collision/world.go index 4a56698..fcf9e98 100644 --- a/collision/world.go +++ b/collision/world.go @@ -18,7 +18,10 @@ func (w *World) RemoveEntity(e Collidable) { func (w *World) NotifyAboutCollisions() { for i, entity := range w.Entities { - for _, other := range w.Entities[i+1:] { + for j, other := range w.Entities { + if i == j { + continue + } if entity.CollisionShape().Overlaps(other.CollisionShape()) { entity.HandleCollisionEvent(other) other.HandleCollisionEvent(entity) diff --git a/hero/hero.go b/hero/hero.go index f37e5b8..ad7eddb 100644 --- a/hero/hero.go +++ b/hero/hero.go @@ -6,6 +6,7 @@ import ( "gitlab.com/kbr4/9heroja/collision" "gitlab.com/kbr4/9heroja/configuration" "gitlab.com/kbr4/9heroja/resources" + "gitlab.com/kbr4/9heroja/weapons" "image" "image/color" "log" @@ -248,3 +249,17 @@ func (h *Hero) HandleCollisionEvent(other collision.Collidable) { } } } + +func (h *Hero) Fire(fire bool, offsetX float64, offsetY float64) (bullet *weapons.Handgun) { + if fire { + bullet = weapons.NewHandgun() + bullet.X = h.X + (resources.HeroTileSize / 2) - offsetX + bullet.Y = h.Y + (resources.HeroTileSize / 2) - offsetY + bullet.OffsetX = offsetX + bullet.OffsetY = offsetY + bullet.Fire(h.direction) + return bullet + } else { + return nil + } +} diff --git a/input/keyboard.go b/input/keyboard.go index ffae052..bc0c064 100644 --- a/input/keyboard.go +++ b/input/keyboard.go @@ -2,6 +2,7 @@ package input import ( "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/inpututil" "sort" "strings" ) @@ -51,3 +52,7 @@ func (k *Keyboard) DirectionFromKeys(keys []ebiten.Key) configuration.Direction return configuration.PreviouslyHeld } } + +func (k *Keyboard) ShouldFire(keys []ebiten.Key) bool { + return inpututil.IsKeyJustReleased(ebiten.KeySpace) +} diff --git a/main.go b/main.go index c537853..180ff4c 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,16 @@ package main import ( + "fmt" "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" "github.com/hajimehoshi/ebiten/v2/inpututil" "gitlab.com/kbr4/9heroja/collision" "gitlab.com/kbr4/9heroja/configuration" "gitlab.com/kbr4/9heroja/hero" "gitlab.com/kbr4/9heroja/input" "gitlab.com/kbr4/9heroja/terrain" + "gitlab.com/kbr4/9heroja/weapons" "gitlab.com/kbr4/9heroja/zombie" _ "image/png" "log" @@ -53,18 +56,32 @@ type Game struct { terrain *terrain.Terrain zombies []*zombie.Zombie world *collision.World + bullets []*weapons.Handgun } func (g *Game) Update() error { g.keys = inpututil.AppendPressedKeys(g.keys[:0]) GameInstance.hero.ChangeDirection(GameInstance.control.DirectionFromKeys(g.keys)) GameInstance.terrain.ChangeDirection(GameInstance.control.DirectionFromKeys(g.keys)) + bullet := GameInstance.hero.Fire(GameInstance.control.ShouldFire(g.keys), g.terrain.PositionX, g.terrain.PositionY) + if bullet != nil { + GameInstance.world.AddEntity(bullet) + g.bullets = append(g.bullets, bullet) + } + if len(g.keys) <= 0 { g.hero.Stop() } else { g.terrain.Move() g.hero.Walk() } + + for _, b := range g.bullets { + if b.IsFlying { + b.Move() + } + } + g.world.NotifyAboutCollisions() return nil } @@ -76,7 +93,19 @@ func (g *Game) Draw(screen *ebiten.Image) { z.OffsetY = g.terrain.PositionY z.DrawZombie(screen) } + + for _, b := range g.bullets { + b.OffsetX = g.terrain.PositionX + b.OffsetY = g.terrain.PositionY + if b.IsFlying { + b.DrawHandgunBullet(screen) + } + } g.hero.DrawHero(screen) + msg := fmt.Sprintf(`TPS: %0.2f + FPS: %0.2f`, ebiten.ActualTPS(), ebiten.ActualFPS()) + ebitenutil.DebugPrint(screen, msg) + } func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { diff --git a/resources/embed.go b/resources/embed.go index 53e94d7..90936c9 100644 --- a/resources/embed.go +++ b/resources/embed.go @@ -13,4 +13,7 @@ var ( //go:embed zombie3.png Zombie_png []byte + + //go:embed handgun.png + Handgun_png []byte ) diff --git a/resources/handgun.png b/resources/handgun.png new file mode 100644 index 0000000000000000000000000000000000000000..2964be7252173d243ddd736c6f4119b74f5b18d3 GIT binary patch literal 967 zcmaJ=JCD;q5MH<|;G`=M1&?aA*MLO)$irN$Gp8hWxQKHUC6O)##W~(hV&&{L_J%kS z6&(c?JtYMN6{4Y_pn^YubR~a)f*%mZxj08bux#(l>ifQ#otb^rYChiGxx0f9+HD+} zZCJO{@7fmp-^#%;EO&&}6CFMfQzv4mLV2IzhVKknn>lp$>>E2kXp4B=p6J=fy32jZ zNikC5hrmYY;4ldtcgzItv!NFl8r4jFNL7E79@KEQcJ z&C3eBT23wMB~8yi#H)uuw1^J$wpm}r0?!~LAwpf2r_-r4El4~X%4)e>PBrrR93XP> zED%nT3*x;@!DO);d7~5a(zblvT$8&K zp9Lc0(o3$nk(-W!A3|i4Mc#8p>k;?y^2oZkIhV>ty%m>k&ZWAMD?>7J+StDuoo&JN zq~>~T!C4PJ3t-wK80)WmkMFw`aBnrt oN;f(0pY|#3pHf7WD?hJp-$v*oy7Btc)0+T64XbH>sGh$31B<^Xg8%>k literal 0 HcmV?d00001 diff --git a/weapons/handgun.go b/weapons/handgun.go new file mode 100644 index 0000000..c9c5da7 --- /dev/null +++ b/weapons/handgun.go @@ -0,0 +1,134 @@ +package weapons + +import ( + "bytes" + "fmt" + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "gitlab.com/kbr4/9heroja/collision" + "gitlab.com/kbr4/9heroja/configuration" + "gitlab.com/kbr4/9heroja/resources" + "image" + "log" + "slices" + "time" +) + +const BulletSpeedMs = 300 + +var ( + handgunBulletImage *ebiten.Image +) + +type Handgun struct { + direction configuration.Direction + IsFlying bool + X float64 + Y float64 + OffsetX float64 + OffsetY float64 + IsDisintegrated bool + flyingTicker *time.Ticker +} + +func (h *Handgun) DrawHandgunBullet(screen *ebiten.Image) { + + screenWidth := screen.Bounds().Max.X + screenHeight := screen.Bounds().Max.Y + + if h.X > float64(screenWidth) || h.X < 0 || h.Y > float64(screenHeight) || h.Y < 0 { + h.Stop() + } + + if !h.IsDisintegrated && h.IsFlying { + ebitenutil.DebugPrint(screen, fmt.Sprintf("\n\n\n\n\n\n\ndx: %f y:%f isint: %t, flying: %t", h.X, h.Y, h.IsDisintegrated, h.IsFlying)) + op := &ebiten.DrawImageOptions{} + op.GeoM.Reset() + op.GeoM.Translate(h.X+h.OffsetX, h.Y+h.OffsetY) + op.GeoM.Scale(1, 1) + + screen.DrawImage( + handgunBulletImage, + op, + ) + } +} + +func init() { + img, _, err := image.Decode(bytes.NewReader(resources.Handgun_png)) + if err != nil { + log.Fatal(err) + } + handgunBulletImage = ebiten.NewImageFromImage(img) + +} + +func NewHandgun() *Handgun { + handgun := &Handgun{ + IsFlying: false, + IsDisintegrated: false, + } + return handgun +} + +func (h *Handgun) Fire(direction configuration.Direction) { + h.direction = direction + h.flyingTicker = time.NewTicker(BulletSpeedMs * time.Millisecond) + if !h.IsFlying { + // h.flyingTicker.Reset(BulletSpeedMs * time.Millisecond) + h.IsFlying = true + h.IsDisintegrated = false + } + +} + +func (h *Handgun) Stop() { + h.IsFlying = false + h.IsDisintegrated = true + fmt.Println("Bullet stopped") +} + +func (h *Handgun) CollisionShape() collision.Polygon { + if !h.IsDisintegrated { + return collision.Polygon{ + Points: []collision.Point{ + {X: h.X + h.OffsetX, Y: h.Y + h.OffsetY}, + {X: h.X + h.OffsetX + 4, Y: h.Y + h.OffsetY}, + {X: h.X + h.OffsetX + 4, Y: h.Y + h.OffsetY + 4}, + {X: h.X + h.OffsetX, Y: h.Y + h.OffsetY + 4}, + }, + } + } else { + return collision.Polygon{ + Points: []collision.Point{}, + } + } +} + +func (h *Handgun) CollisionObjectType() collision.ObjectType { + return collision.Bullet +} + +func (h *Handgun) HandleCollisionEvent(other collision.Collidable) { + coll := other.CollisionObjectType() + switch coll { + case collision.Zombie: + fmt.Println("Bullet hit zombie") + h.Stop() + } +} +func (h *Handgun) Move() { + if slices.Contains([]configuration.Direction{configuration.North, configuration.NorthEast, configuration.NorthWest}, h.direction) { + h.Y -= 1 + } + if slices.Contains([]configuration.Direction{configuration.South, configuration.SouthEast, configuration.SouthWest}, h.direction) { + h.Y += 1 + } + if slices.Contains([]configuration.Direction{configuration.East, configuration.NorthEast, configuration.SouthEast}, h.direction) { + h.X += 1 + } + if slices.Contains([]configuration.Direction{configuration.West, configuration.NorthWest, configuration.SouthWest}, h.direction) { + h.X -= 1 + } + +} diff --git a/zombie/zombie.go b/zombie/zombie.go index 3d71515..7ba9987 100644 --- a/zombie/zombie.go +++ b/zombie/zombie.go @@ -2,6 +2,7 @@ package zombie import ( "bytes" + "fmt" "github.com/hajimehoshi/ebiten/v2" "gitlab.com/kbr4/9heroja/collision" "gitlab.com/kbr4/9heroja/configuration" @@ -147,6 +148,9 @@ func (z *Zombie) HandleCollisionEvent(other collision.Collidable) { case collision.Hero: z.Stop() z.IsDead = true - + case collision.Bullet: + fmt.Println("Zombie hit by bullet") + z.Stop() + z.IsDead = true } }