Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement green bugs update #35

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 196 additions & 12 deletions bugs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (
"math"

"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/vector"

_ "embed"
"image/color"
_ "image/png"
)

Expand Down Expand Up @@ -71,7 +73,7 @@ func newBug(game *Game, bugColor bugColor, x, y int, onDestroy func(b *bug)) *bu
}()
bugImage := bugsImage.SubImage(rect).(*ebiten.Image)

return &bug{
bug := &bug{
game: game,

x: x,
Expand All @@ -93,6 +95,31 @@ func newBug(game *Game, bugColor bugColor, x, y int, onDestroy func(b *bug)) *bu

onDestroy: onDestroy,
}

switch bugColor {
case bugsRed:
bug.speed = 5
bug.attackPower = 1
bug.attackRange = 1
bug.health = 2
bug.name = "Red bug"
case bugsBlue:
bug.speed = 4
bug.attackPower = 1
bug.attackRange = 1
bug.health = 3
bug.name = "Blue bug"
case bugsGreen:
bug.speed = 3
bug.attackPower = 1
bug.attackRange = 50
bug.health = 4
bug.name = "Green bug"
default:
log.Fatal("invalid bug color")
}

return bug
}

func redBug() image.Rectangle {
Expand Down Expand Up @@ -122,6 +149,69 @@ func (b *bug) Update() {

func (b *bug) attack(a Damager) {
a.Damage(b.attackPower)

// エフェクトを表示する
switch b.selfColor {
case bugsRed:
// TODO: implement
case bugsBlue:
// TODO: implement
case bugsGreen:
tx, ty := a.(Building).Position()
e := newGreenBugAttackEffect(b.game, b.x, b.y, tx, ty)
b.game.updateHandler.Add(e)
b.game.drawHandler.Add(e)
}
}

type greenBugAttackEffect struct {
game *Game

currentX, currentY int
startX, startY int
targetX, targetY int

erapsedFrame int

zindex int
}

func newGreenBugAttackEffect(game *Game, startX, startY, targetX, targetY int) *greenBugAttackEffect {
return &greenBugAttackEffect{
game: game,

currentX: startX,
currentY: startY,
startX: startX,
startY: startY,
targetX: targetX,
targetY: targetY,

zindex: 220,
}
}

func (e *greenBugAttackEffect) Update() {
// 攻撃エフェクトを描画する
// 直線上に移動するエフェクトを描画する
// 30 frame かけて target に向かって移動する
e.currentX = e.startX + (e.targetX-e.startX)*e.erapsedFrame/30
e.currentY = e.startY + (e.targetY-e.startY)*e.erapsedFrame/30
if e.erapsedFrame <= 30 {
e.erapsedFrame++
return
}
e.game.updateHandler.Remove(e)
e.game.drawHandler.Remove(e)
}

func (e *greenBugAttackEffect) Draw(screen *ebiten.Image) {
// 直線上に移動するエフェクトを描画する
vector.StrokeLine(screen, float32(e.startX), float32(e.startY), float32(e.currentX), float32(e.currentY), 10, color.RGBA{R: 128, G: 128, B: 128, A: 128}, true)
}

func (e *greenBugAttackEffect) ZIndex() int {
return e.zindex
}

type Damager interface {
Expand Down Expand Up @@ -195,14 +285,21 @@ func redBugUpdate(b *bug) {

// house に向かう
var moveTargetX, moveTargetY int
var found bool

for _, building := range b.game.buildings {
if building.Name() == "House" {
moveTargetX, moveTargetY = building.Position()
found = true
break
}
}

if !found {
// すべての建物が破壊されている場合はその場にとどまる
return
}

// ターゲットへの直線距離を計算
dx := moveTargetX - b.x
dy := moveTargetY - b.y
Expand Down Expand Up @@ -261,6 +358,10 @@ func blueBugUpdate(b *bug) {

// 対象の建物と bugs の攻撃範囲を踏まえた当たり判定を行う
// bugs は size + attackRange の範囲を当たり判定として用いる
if nearestBuilding == nil {
// すべての建物が破壊されている場合はその場にとどまる
return
}
target := nearestBuilding.(Building)
x, y := target.Position()
width, height := target.Size()
Expand Down Expand Up @@ -328,20 +429,103 @@ func blueBugUpdate(b *bug) {
}

func greenBugUpdate(b *bug) {
// todo: implement
// 緑虫の特徴
// 家に向かって一直線に進む。
// 攻撃は一定時間ごとに行う。攻撃範囲が広い。飛び道具のようなものを放つ。
// 攻撃範囲に任意の障害物が入ったとき、その障害物に向かって攻撃を行う。
// 体力は青虫よりも多い。
// 出現頻度は低い。
// 動きは遅い。

// target に向かう途中に障害物が攻撃射程に入ったとき、その障害物を target とする
// いずれかの建物が攻撃レンジに入っているか確認
var attackTarget Damager
for _, building := range b.game.buildings {
x, y := building.Position()
width, height := building.Size()

// 対象の建物と bugs の攻撃範囲を踏まえた当たり判定を行う
// bugs は size + attackRange の範囲を当たり判定として用いる
if intersects(
// bug
rect{
b.x - b.width/2 - int(b.attackRange), b.y - b.height/2 - int(b.attackRange),
b.width + int(b.attackRange)*2, b.height + int(b.attackRange)*2,
},
// building
rect{x - width/2, y - height/2,
width, height},
) {
// 攻撃射程圏内であるので、その建物を attack 対象にする
attackTarget = building.(Damager)

break
}
}

// attack target がいるならば攻撃する。そうでないならば house に向かう
if attackTarget != nil {
// クールダウン中でなければ攻撃
if b.attackCooldown <= 0 {
b.attack(attackTarget)
b.attackCooldown = 60
} else {
// クールダウンを消化する
b.attackCooldown -= 1
}

return
}

// house に向かう
var moveTargetX, moveTargetY int
var found bool
for _, building := range b.game.buildings {
if building.Name() == "House" {
moveTargetX, moveTargetY = building.Position()
found = true
break
}
}

if !found {
// すべての建物が破壊されている場合はその場にとどまる
return
}

// ターゲットへの直線距離を計算
dx := moveTargetX - b.x
dy := moveTargetY - b.y

// 移動方向のラジアンを計算
angle := math.Atan2(float64(dy), float64(dx))

// 回避動作
// 虫同士がぴったり重ならないようにするための計算
// やや自信のないロジックではある
avoidX, avoidY := 0.0, 0.0
for _, e := range b.game.enemies {
ee := e.(*bug)
if ee != b {
distX := float64(ee.x - b.x)
distY := float64(ee.y - b.y)
distance := math.Sqrt(distX*distX + distY*distY)
if distance > 0 && distance < float64(b.width) {
avoidX -= distX / distance
avoidY -= distY / distance
}
}
}

// 移動
moveX := math.Cos(angle)*b.speed + avoidX
moveY := math.Sin(angle)*b.speed + avoidY
b.x += int(moveX)
b.y += int(moveY)
}

func (b *bug) Name() string {
switch b.selfColor {
case bugsRed:
return "Red bug"
case bugsBlue:
return "Blue bug"
case bugsGreen:
return "Green bug"
}
log.Fatal("invalid bug color")
return ""
return b.name
}

func (b *bug) Position() (int, int) {
Expand Down
1 change: 1 addition & 0 deletions house.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func newHouse(game *Game) *house {
// TODO: 爆発したり消えたりする処理を書く
// ここでフラグを設定しといて、Update() や Draw で続きの処理を行うのもあり
// いったんシンプルに消す
h.game.RemoveBuilding(h)
h.game.drawHandler.Remove(h)
},
}
Expand Down
9 changes: 6 additions & 3 deletions wavectl.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,14 @@ func generateSpawnInfos(num int) []spawnInfo {
y = rand.Intn(screenHeight)
}

// 赤虫:青虫 = 4:6 で生成する
if rand.Intn(10) < 4 {
// 赤虫:青虫:緑 = 3:5:2 で生成する
r := rand.Intn(10)
if r < 3 {
infos = append(infos, spawnInfo{bugsRed, x, y})
} else {
} else if r < 8 {
infos = append(infos, spawnInfo{bugsBlue, x, y})
} else {
infos = append(infos, spawnInfo{bugsGreen, x, y})
}
}

Expand Down