organização de código
This commit is contained in:
parent
277c6d070a
commit
a411573a06
5 changed files with 529 additions and 266 deletions
21
ease.go
Normal file
21
ease.go
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
func easeInOutCubic(x float32) float32 {
|
||||||
|
if x < 0.5 {
|
||||||
|
return 4 * x * x * x
|
||||||
|
} else {
|
||||||
|
return 1 - float32(math.Pow(float64(-2*x+2), 3))/2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lerp(v0, v1, t float32) float32 {
|
||||||
|
return (1-t)*v0 + t*v1
|
||||||
|
}
|
||||||
|
|
||||||
|
func easeInOutSine(x float32) float32 {
|
||||||
|
return -float32(math.Cos(math.Pi*float64(x))-1) / 2
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
|
|
@ -1,5 +1,5 @@
|
||||||
module danmaku
|
module danmaku
|
||||||
|
|
||||||
go 1.20
|
go 1.23
|
||||||
|
|
||||||
require github.com/gen2brain/raylib-go/raylib v0.0.0-20230719211022-1083eace2049
|
require github.com/gen2brain/raylib-go/raylib v0.0.0-20230719211022-1083eace2049
|
||||||
|
|
|
||||||
513
main.go
513
main.go
|
|
@ -8,15 +8,16 @@ import (
|
||||||
rl "github.com/gen2brain/raylib-go/raylib"
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
)
|
)
|
||||||
|
|
||||||
// diz para um inimigo como ele deve se mover
|
|
||||||
type movementPattern func(*enemy) rl.Vector2
|
|
||||||
|
|
||||||
// diz para um inimigo em que condições atirar. acionado pelo movementPattern
|
// diz para um inimigo em que condições atirar. acionado pelo movementPattern
|
||||||
type shootingPattern func(*enemy)
|
type shootingPattern func(*enemy)
|
||||||
|
|
||||||
// diz para uma bala como ela deve se mover.
|
// diz para uma bala como ela deve se mover.
|
||||||
type bulletMovementPattern func(*bullet) rl.Vector2
|
type bulletMovementPattern func(*bullet) rl.Vector2
|
||||||
|
|
||||||
|
type duration float32
|
||||||
|
|
||||||
|
const second duration = 1
|
||||||
|
|
||||||
type hazard interface { // inimigos e projéteis
|
type hazard interface { // inimigos e projéteis
|
||||||
Pos() rl.Vector2
|
Pos() rl.Vector2
|
||||||
}
|
}
|
||||||
|
|
@ -28,6 +29,7 @@ type game struct {
|
||||||
frame float32
|
frame float32
|
||||||
enemies []*enemy
|
enemies []*enemy
|
||||||
waves []*wave
|
waves []*wave
|
||||||
|
walls []plane
|
||||||
currentWave int
|
currentWave int
|
||||||
bullets []*bullet
|
bullets []*bullet
|
||||||
score int
|
score int
|
||||||
|
|
@ -35,6 +37,11 @@ type game struct {
|
||||||
backgroundColor rl.Color
|
backgroundColor rl.Color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type plane struct {
|
||||||
|
normal rl.Vector2
|
||||||
|
pos rl.Vector2
|
||||||
|
}
|
||||||
|
|
||||||
type bullet struct {
|
type bullet struct {
|
||||||
pos rl.Vector2
|
pos rl.Vector2
|
||||||
speed rl.Vector2
|
speed rl.Vector2
|
||||||
|
|
@ -63,10 +70,42 @@ type enemy struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type wave struct {
|
type wave struct {
|
||||||
|
duration *timer
|
||||||
enemies []*enemy
|
enemies []*enemy
|
||||||
entrance movementPattern
|
entrance movementPattern
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type timer struct {
|
||||||
|
ttl duration
|
||||||
|
time float32
|
||||||
|
start float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) tick(g *game) {
|
||||||
|
t.time += rl.GetFrameTime() * g.gameSpeed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) isTimeout() bool {
|
||||||
|
return t.time >= float32(t.ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) reset() {
|
||||||
|
t.start = float64(t.time)
|
||||||
|
t.time = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *timer) unit() float32 {
|
||||||
|
return t.time / float32(t.ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTimer(duration duration) *timer {
|
||||||
|
return &timer{
|
||||||
|
time: 0,
|
||||||
|
ttl: duration,
|
||||||
|
start: rl.GetTime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g game) insideArena(v rl.Vector2) bool {
|
func (g game) insideArena(v rl.Vector2) bool {
|
||||||
return v.X >= 0 && v.Y >= 0 &&
|
return v.X >= 0 && v.Y >= 0 &&
|
||||||
v.Y <= float32(g.arenaHeight) && v.X <= float32(g.arenaWidth)
|
v.Y <= float32(g.arenaHeight) && v.X <= float32(g.arenaWidth)
|
||||||
|
|
@ -89,7 +128,7 @@ func (b *bullet) update(g *game, index int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func bulletExplosion(g *game, rate, amount int, bulletSpeed, size float32) shootingPattern {
|
func bulletExplosion(g *game, rate, amount int, bulletSpeed, size float32) shootingPattern {
|
||||||
t := newTimer(second)
|
t := newTimer(second * duration(rate))
|
||||||
return func(e *enemy) {
|
return func(e *enemy) {
|
||||||
|
|
||||||
t.tick(g)
|
t.tick(g)
|
||||||
|
|
@ -140,51 +179,17 @@ func ShootAtPlayer(g *game, p *player, rate int,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type timer struct {
|
|
||||||
time, ttl float32
|
|
||||||
start float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *timer) tick(g *game) {
|
|
||||||
t.time += rl.GetFrameTime() * g.gameSpeed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *timer) isTimeout() bool {
|
|
||||||
return t.time >= t.ttl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *timer) reset() {
|
|
||||||
t.start = float64(t.time)
|
|
||||||
t.time = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTimer(duration float32) *timer {
|
|
||||||
return &timer {
|
|
||||||
time: 0,
|
|
||||||
ttl: duration,
|
|
||||||
start: rl.GetTime(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var second float32 = 1
|
|
||||||
|
|
||||||
func burstShootAtPlayer(g *game, p *player, rate float32, bulletMoveSpeed float32) shootingPattern {
|
func burstShootAtPlayer(g *game, p *player, rate float32, bulletMoveSpeed float32) shootingPattern {
|
||||||
flag := true
|
flag := true
|
||||||
off := newTimer(second)
|
off := newTimer(second)
|
||||||
on := newTimer(second*rate)
|
on := newTimer(duration(float32(second) * rate))
|
||||||
return func(e *enemy) {
|
return func(e *enemy) {
|
||||||
|
|
||||||
fmt.Printf("\r %f %f", rl.GetTime(), rl.GetFrameTime())
|
|
||||||
|
|
||||||
off.tick(g)
|
off.tick(g)
|
||||||
|
|
||||||
if off.isTimeout() {
|
if off.isTimeout() {
|
||||||
flag = !flag
|
flag = !flag
|
||||||
fmt.Println()
|
|
||||||
fmt.Println(off)
|
|
||||||
off.reset()
|
off.reset()
|
||||||
fmt.Println(off)
|
|
||||||
fmt.Println()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !flag {
|
if !flag {
|
||||||
|
|
@ -226,86 +231,59 @@ func shootStraightDown(g *game) shootingPattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shootStill() movementPattern {
|
// func detectWallCollision(planes []plane, initialPos, direction rl.Vector2) float32 {
|
||||||
return func(e *enemy) rl.Vector2 {
|
// p := initialPos
|
||||||
e.shoot(e)
|
// w := direction
|
||||||
return rl.Vector2{X: 0, Y: 0}
|
// var closest = math.Inf(1)
|
||||||
}
|
// for _, plane := range planes {
|
||||||
}
|
// c := plane.pos
|
||||||
|
// n := plane.normal
|
||||||
|
// denominator := rl.Vector2DotProduct(w, n)
|
||||||
|
// if denominator == 0 {
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// t := rl.Vector2DotProduct(rl.Vector2Subtract(p, c), n) / denominator
|
||||||
|
// closest = min(float64(t), closest)
|
||||||
|
// }
|
||||||
|
// return float32(-closest)
|
||||||
|
// }
|
||||||
|
|
||||||
func horizonalPattern(g *game) movementPattern {
|
// func foobarPattern(g *game) movementPattern {
|
||||||
direction := rl.Vector2{X: 4, Y: 0}
|
//
|
||||||
|
// pos := rl.Vector2{}
|
||||||
return func(e *enemy) rl.Vector2 {
|
// state := 0
|
||||||
e.shoot(e)
|
// wait := 0
|
||||||
|
//
|
||||||
result := rl.Vector2Add(direction, e.pos)
|
// return func(e *enemy) rl.Vector2 {
|
||||||
if !g.insideArena(result) {
|
//
|
||||||
direction = rl.Vector2Negate(direction)
|
// switch state {
|
||||||
}
|
// case 0: // init
|
||||||
return direction
|
// pos.X = e.pos.X
|
||||||
}
|
// state = 1
|
||||||
}
|
// return pos
|
||||||
|
// case 1: // descer
|
||||||
func sineHorizonalPattern(g *game) movementPattern {
|
// pos.Y += 1
|
||||||
direction := rl.Vector2{X: 4, Y: 0}
|
// if pos.Y >= 100 { state = 2; wait = 50 }
|
||||||
|
// return pos
|
||||||
return func(e *enemy) rl.Vector2 {
|
// case 2: // atirar por um tempo
|
||||||
if e.shoot != nil {
|
// e.shoot(e)
|
||||||
e.shoot(e)
|
// wait -= 1
|
||||||
}
|
// if wait <= 0 { state = 3; wait = 60 }
|
||||||
|
// return pos
|
||||||
|
// case 3: // wait
|
||||||
result := rl.Vector2Add(direction, e.pos)
|
// wait -= 1
|
||||||
if !g.insideArena(result) {
|
// if wait <= 0 { state = 4 }
|
||||||
direction = rl.Vector2Negate(direction)
|
// return pos
|
||||||
}
|
// case 4: // retornar
|
||||||
|
// pos.Y -= 3
|
||||||
sine := rl.Vector2{
|
// if pos.Y - e.hitBoxRadius < 0 {
|
||||||
X: 0,
|
// e.health = 0
|
||||||
Y: float32(math.Sin(float64(g.frame*0.04))*2),
|
// }
|
||||||
}
|
// return pos
|
||||||
|
// }
|
||||||
return rl.Vector2Add(sine, direction)
|
// panic(state)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func foobarPattern(g *game) movementPattern {
|
|
||||||
|
|
||||||
pos := rl.Vector2{}
|
|
||||||
state := 0
|
|
||||||
wait := 0
|
|
||||||
|
|
||||||
return func(e *enemy) rl.Vector2 {
|
|
||||||
|
|
||||||
switch state {
|
|
||||||
case 0: // init
|
|
||||||
pos.X = e.pos.X
|
|
||||||
state = 1
|
|
||||||
return pos
|
|
||||||
case 1: // descer
|
|
||||||
pos.Y += 1
|
|
||||||
if pos.Y >= 100 { state = 2; wait = 50 }
|
|
||||||
return pos
|
|
||||||
case 2: // atirar por um tempo
|
|
||||||
e.shoot(e)
|
|
||||||
wait -= 1
|
|
||||||
if wait <= 0 { state = 3; wait = 60 }
|
|
||||||
return pos
|
|
||||||
case 3: // wait
|
|
||||||
wait -= 1
|
|
||||||
if wait <= 0 { state = 4 }
|
|
||||||
return pos
|
|
||||||
case 4: // retornar
|
|
||||||
pos.Y -= 3
|
|
||||||
if pos.Y - e.hitBoxRadius < 0 {
|
|
||||||
e.health = 0
|
|
||||||
}
|
|
||||||
return pos
|
|
||||||
}
|
|
||||||
panic(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *enemy) checkHit(g *game) (bool, *bullet, int) {
|
func (e *enemy) checkHit(g *game) (bool, *bullet, int) {
|
||||||
for index, bullet := range g.bullets {
|
for index, bullet := range g.bullets {
|
||||||
|
|
@ -337,7 +315,8 @@ func (e *enemy) update(g *game) {
|
||||||
enemyColor := rl.Blue
|
enemyColor := rl.Blue
|
||||||
|
|
||||||
if e.move != nil {
|
if e.move != nil {
|
||||||
e.pos = rl.Vector2Add(rl.Vector2Scale(e.move(e), g.gameSpeed), e.pos)
|
// e.pos = rl.Vector2Add(rl.Vector2Scale(e.move(e), g.gameSpeed), e.pos)
|
||||||
|
e.move(e)
|
||||||
// e.pos = e.move(e)
|
// e.pos = e.move(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -349,129 +328,9 @@ func (e *enemy) update(g *game) {
|
||||||
g.backgroundColor = rl.NewColor(20, 20, 20, 255)
|
g.backgroundColor = rl.NewColor(20, 20, 20, 255)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
rl.DrawCircleV(e.pos, e.hitBoxRadius, enemyColor)
|
rl.DrawCircleV(e.pos, e.hitBoxRadius, enemyColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *player) move(g *game) {
|
|
||||||
|
|
||||||
var moveSpeed float32
|
|
||||||
if p.focusMode {
|
|
||||||
moveSpeed = p.moveSpeed * p.focusSpeedDecrease
|
|
||||||
} else {
|
|
||||||
moveSpeed = p.moveSpeed
|
|
||||||
}
|
|
||||||
|
|
||||||
p.speed = rl.Vector2{X: 0, Y: 0}
|
|
||||||
|
|
||||||
if rl.IsKeyDown(rl.KeyW) {
|
|
||||||
p.speed.Y -= 1
|
|
||||||
}
|
|
||||||
if rl.IsKeyDown(rl.KeyS) {
|
|
||||||
p.speed.Y += 1
|
|
||||||
}
|
|
||||||
if rl.IsKeyDown(rl.KeyA) {
|
|
||||||
p.speed.X -= 1
|
|
||||||
}
|
|
||||||
if rl.IsKeyDown(rl.KeyD) {
|
|
||||||
p.speed.X += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if !(p.speed.X == 0 && p.speed.Y == 0) {
|
|
||||||
// jogador se move mais rapido na diagonal caso o contrario
|
|
||||||
p.speed = rl.Vector2Normalize(p.speed)
|
|
||||||
p.speed = rl.Vector2Scale(p.speed, moveSpeed)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := rl.Vector2Add(p.pos, p.speed)
|
|
||||||
|
|
||||||
if result.Y-p.hitBoxRadius < 0 ||
|
|
||||||
result.Y+p.hitBoxRadius > float32(g.arenaHeight) {
|
|
||||||
p.speed.Y = 0
|
|
||||||
}
|
|
||||||
if result.X-p.hitBoxRadius < 0 ||
|
|
||||||
result.X+p.hitBoxRadius > float32(g.arenaWidth) {
|
|
||||||
p.speed.X = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
p.pos = rl.Vector2Add(p.pos, p.speed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *player) shoot(g *game) {
|
|
||||||
if p.focusMode {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if int(g.frame)%3 != 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if rl.IsMouseButtonDown(rl.MouseLeftButton) {
|
|
||||||
mouse := rl.GetMousePosition()
|
|
||||||
direction := rl.Vector2Subtract(mouse, p.pos)
|
|
||||||
direction = rl.Vector2Add(direction, p.speed)
|
|
||||||
direction = rl.Vector2Normalize(direction)
|
|
||||||
direction = rl.Vector2Scale(direction, p.bulletMoveSpeed)
|
|
||||||
|
|
||||||
g.bullets = append(g.bullets, &bullet{
|
|
||||||
pos: p.pos,
|
|
||||||
size: p.bulletSize,
|
|
||||||
speed: direction,
|
|
||||||
dmg: 1,
|
|
||||||
enemy: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *player) checkHit(g *game) {
|
|
||||||
for _, bullet := range g.bullets {
|
|
||||||
if !bullet.enemy {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
distance := rl.Vector2Distance(p.pos, bullet.pos) - bullet.size
|
|
||||||
// fmt.Println(distance)
|
|
||||||
|
|
||||||
if distance < p.hitBoxRadius {
|
|
||||||
// fmt.Println("hit!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *player) checkFocus() {
|
|
||||||
// raylib não entende keybindings customizadas através do xmodmap?
|
|
||||||
if rl.IsKeyDown(rl.KeyLeftShift) || rl.IsKeyDown(rl.KeyRightShift) {
|
|
||||||
p.focusMode = true
|
|
||||||
} else {
|
|
||||||
p.focusMode = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *player) update(g *game) {
|
|
||||||
|
|
||||||
p.checkFocus()
|
|
||||||
|
|
||||||
p.move(g)
|
|
||||||
|
|
||||||
p.shoot(g)
|
|
||||||
|
|
||||||
p.checkHit(g)
|
|
||||||
|
|
||||||
// hitbox
|
|
||||||
rl.DrawCircleV(p.pos, p.hitBoxRadius, rl.Red)
|
|
||||||
|
|
||||||
if p.focusMode {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// mira
|
|
||||||
mouse := rl.GetMousePosition()
|
|
||||||
inverted := rl.Vector2Subtract(p.pos, mouse)
|
|
||||||
inverted = rl.Vector2Negate(inverted)
|
|
||||||
inverted = rl.Vector2Normalize(inverted)
|
|
||||||
inverted = rl.Vector2Scale(inverted, 2000)
|
|
||||||
rl.DrawLineV(p.pos, rl.Vector2Add(mouse, inverted), rl.NewColor(255, 0, 0, 100))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *game) fullClear() bool {
|
func (g *game) fullClear() bool {
|
||||||
for _, e := range g.waves[g.currentWave].enemies {
|
for _, e := range g.waves[g.currentWave].enemies {
|
||||||
if e.health != 0 {
|
if e.health != 0 {
|
||||||
|
|
@ -481,12 +340,41 @@ func (g *game) fullClear() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *game) waveTimeout() bool {
|
||||||
|
currentWave := g.waves[g.currentWave]
|
||||||
|
if currentWave.duration == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if currentWave.duration.isTimeout() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
currentWave.duration.tick(g)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *game) wavesOver() bool {
|
||||||
|
return g.currentWave == len(g.waves)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *game) nextWave() bool {
|
||||||
|
g.currentWave++
|
||||||
|
if g.wavesOver() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
g.addEnemy(g.waves[g.currentWave].enemies...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *game) addEnemy(e ...*enemy) {
|
||||||
|
g.enemies = append(g.enemies, e...)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
state := &game{
|
state := &game{
|
||||||
arenaWidth: 450,
|
arenaWidth: 450,
|
||||||
arenaHeight: 700,
|
arenaHeight: 700,
|
||||||
interfaceWidth: 300,
|
interfaceWidth: 400,
|
||||||
gameSpeed: 1,
|
gameSpeed: 1,
|
||||||
backgroundColor: rl.NewColor(0, 0, 0, 100),
|
backgroundColor: rl.NewColor(0, 0, 0, 100),
|
||||||
}
|
}
|
||||||
|
|
@ -502,6 +390,26 @@ func main() {
|
||||||
hitBoxRadius: 5,
|
hitBoxRadius: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var arena = []plane{
|
||||||
|
{
|
||||||
|
normal: rl.Vector2{X: 0, Y: -1},
|
||||||
|
pos: rl.Vector2{X: 1, Y: 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
normal: rl.Vector2{X: -1, Y: 0},
|
||||||
|
pos: rl.Vector2{X: float32(state.arenaWidth), Y: 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
normal: rl.Vector2{X: 0, Y: 1},
|
||||||
|
pos: rl.Vector2{X: 0, Y: float32(state.arenaHeight)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
normal: rl.Vector2{X: 1, Y: 0},
|
||||||
|
pos: rl.Vector2{X: 0, Y: 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
state.walls = arena
|
||||||
|
|
||||||
// rl.SetTraceLog(rl.LogWarning | rl.LogDebug)
|
// rl.SetTraceLog(rl.LogWarning | rl.LogDebug)
|
||||||
|
|
||||||
rl.SetConfigFlags(rl.FlagMsaa4xHint)
|
rl.SetConfigFlags(rl.FlagMsaa4xHint)
|
||||||
|
|
@ -517,26 +425,65 @@ func main() {
|
||||||
// state.arenaHeight,
|
// state.arenaHeight,
|
||||||
// )
|
// )
|
||||||
|
|
||||||
|
// descentAndSine := jjjjjj
|
||||||
|
|
||||||
state.waves = []*wave{
|
state.waves = []*wave{
|
||||||
{
|
{
|
||||||
|
duration: newTimer(second * 10),
|
||||||
enemies: []*enemy{
|
enemies: []*enemy{
|
||||||
{
|
{
|
||||||
pos: rl.Vector2{X: 100, Y: 100},
|
pos: rl.Vector2{X: 100, Y: -20},
|
||||||
health: 100,
|
health: 100,
|
||||||
hitBoxRadius: 20,
|
hitBoxRadius: 20,
|
||||||
move: sineHorizonalPattern(state),
|
move: statePipeline{
|
||||||
|
{
|
||||||
|
duration: 1.1,
|
||||||
|
move: sineDescentMove(state, -20, 200, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
move: sineHorizonalPattern(state, 0),
|
||||||
|
},
|
||||||
|
}.MovementPattern(state),
|
||||||
|
shoot: burstShootAtPlayer(state, &player, 0.1, 4),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: rl.Vector2{X: 200, Y: -20},
|
||||||
|
health: 100,
|
||||||
|
hitBoxRadius: 20,
|
||||||
|
move: statePipeline{
|
||||||
|
{
|
||||||
|
duration: 5,
|
||||||
|
move: shootStill(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: 1.1,
|
||||||
|
move: descentMove(state, -20, 200, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
move: sineHorizonalPattern(state, 1),
|
||||||
|
},
|
||||||
|
}.MovementPattern(state),
|
||||||
shoot: burstShootAtPlayer(state, &player, 0.1, 4),
|
shoot: burstShootAtPlayer(state, &player, 0.1, 4),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
duration: newTimer(second * 20),
|
||||||
enemies: []*enemy{
|
enemies: []*enemy{
|
||||||
{
|
{
|
||||||
pos: rl.Vector2{X: 100, Y: 100},
|
pos: rl.Vector2{X: 100, Y: 100},
|
||||||
health: 100,
|
health: 100,
|
||||||
hitBoxRadius: 20,
|
hitBoxRadius: 20,
|
||||||
move: horizonalPattern(state),
|
move: statePipeline{
|
||||||
shoot: bulletExplosion(state, targetFPS, 20, 2, 11),
|
{
|
||||||
|
duration: 1.1,
|
||||||
|
move: sineDescentMove(state, -20, 200, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
move: sineHorizonalPattern(state, 0),
|
||||||
|
},
|
||||||
|
}.MovementPattern(state),
|
||||||
|
shoot: bulletExplosion(state, 1, 40, 2, 11),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -546,22 +493,45 @@ func main() {
|
||||||
pos: rl.Vector2{X: 200, Y: 200},
|
pos: rl.Vector2{X: 200, Y: 200},
|
||||||
health: 100,
|
health: 100,
|
||||||
hitBoxRadius: 20,
|
hitBoxRadius: 20,
|
||||||
move: horizonalPattern(state),
|
move: statePipeline{
|
||||||
shoot: bulletExplosion(state, targetFPS, 20, 2, 11),
|
{
|
||||||
|
duration: 1.1,
|
||||||
|
move: sineDescentMove(state, -20, 200, 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pos: rl.Vector2{X: 100, Y: 100},
|
move: sineHorizonalPattern(state, 0),
|
||||||
health: 100,
|
},
|
||||||
|
}.MovementPattern(state),
|
||||||
|
shoot: bulletExplosion(state, 1, 20, 2, 11),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: rl.Vector2{X: 100, Y: 100}, health: 100,
|
||||||
hitBoxRadius: 20,
|
hitBoxRadius: 20,
|
||||||
move: horizonalPattern(state),
|
move: statePipeline{
|
||||||
|
{
|
||||||
|
duration: 1.1,
|
||||||
|
move: sineDescentMove(state, -20, 200, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
move: sineHorizonalPattern(state, 0),
|
||||||
|
},
|
||||||
|
}.MovementPattern(state),
|
||||||
shoot: burstShootAtPlayer(state, &player, 0.2, 4),
|
shoot: burstShootAtPlayer(state, &player, 0.2, 4),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pos: rl.Vector2{X: 50, Y: 250},
|
pos: rl.Vector2{X: 50, Y: 250},
|
||||||
health: 100,
|
health: 100,
|
||||||
hitBoxRadius: 20,
|
hitBoxRadius: 20,
|
||||||
move: horizonalPattern(state),
|
move: statePipeline{
|
||||||
shoot: bulletExplosion(state, targetFPS, 20, 2, 11),
|
{
|
||||||
|
duration: 1.1,
|
||||||
|
move: sineDescentMove(state, -20, 200, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
move: sineHorizonalPattern(state, 0),
|
||||||
|
},
|
||||||
|
}.MovementPattern(state),
|
||||||
|
shoot: bulletExplosion(state, 1, 20, 2, 11),
|
||||||
// shoot: shootStraightDown(state),
|
// shoot: shootStraightDown(state),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -569,6 +539,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
currectScore := 0
|
currectScore := 0
|
||||||
|
state.addEnemy(state.waves[0].enemies...)
|
||||||
|
|
||||||
for ; !rl.WindowShouldClose(); state.frame += state.gameSpeed {
|
for ; !rl.WindowShouldClose(); state.frame += state.gameSpeed {
|
||||||
rl.BeginDrawing()
|
rl.BeginDrawing()
|
||||||
|
|
@ -579,16 +550,16 @@ func main() {
|
||||||
|
|
||||||
player.update(state)
|
player.update(state)
|
||||||
|
|
||||||
if state.fullClear() {
|
if state.fullClear() || state.waveTimeout() {
|
||||||
state.currentWave++
|
if !state.nextWave() {
|
||||||
}
|
|
||||||
|
|
||||||
if state.currentWave >= len(state.waves) {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
state.enemies = state.waves[state.currentWave].enemies
|
}
|
||||||
|
|
||||||
|
enemiesTotalLifeRemaining := 0
|
||||||
for i := 0; i < len(state.enemies); i++ {
|
for i := 0; i < len(state.enemies); i++ {
|
||||||
state.enemies[i].update(state)
|
state.enemies[i].update(state)
|
||||||
|
enemiesTotalLifeRemaining += state.enemies[i].health
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(state.bullets); i++ {
|
for i := 0; i < len(state.bullets); i++ {
|
||||||
|
|
@ -606,7 +577,6 @@ func main() {
|
||||||
state.gameSpeed = 1
|
state.gameSpeed = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// rl.EndTextureMode()
|
// rl.EndTextureMode()
|
||||||
|
|
||||||
// { // shaders
|
// { // shaders
|
||||||
|
|
@ -635,17 +605,34 @@ func main() {
|
||||||
|
|
||||||
{ // UI
|
{ // UI
|
||||||
currectScore += (state.score - currectScore) / 11
|
currectScore += (state.score - currectScore) / 11
|
||||||
rl.DrawText(strconv.Itoa(currectScore), state.arenaWidth, 0, 50, rl.White)
|
|
||||||
|
|
||||||
rl.DrawText(
|
rl.DrawText(
|
||||||
strconv.Itoa(len(state.bullets)), state.arenaWidth, 50, 50, rl.White,
|
fmt.Sprint("score: ", strconv.Itoa(currectScore)),
|
||||||
|
state.arenaWidth, 0, 50, rl.White,
|
||||||
)
|
)
|
||||||
|
|
||||||
rl.DrawText(
|
rl.DrawText(
|
||||||
fmt.Sprintf("%f", state.frame), state.arenaWidth, 100, 50, rl.White,
|
fmt.Sprint("bullets: ", strconv.Itoa(len(state.bullets))),
|
||||||
|
state.arenaWidth, 50, 50, rl.White,
|
||||||
|
)
|
||||||
|
|
||||||
|
rl.DrawText(
|
||||||
|
fmt.Sprintf("wave: %d/%d", state.currentWave, len(state.waves)),
|
||||||
|
state.arenaWidth, 100, 50, rl.White,
|
||||||
|
)
|
||||||
|
|
||||||
|
rl.DrawText(
|
||||||
|
fmt.Sprintf("t: %f", rl.GetTime()), state.arenaWidth, 150, 50, rl.White,
|
||||||
)
|
)
|
||||||
rl.DrawText(
|
rl.DrawText(
|
||||||
fmt.Sprintf("%d", int(state.frame)), state.arenaWidth, 150, 50, rl.White,
|
fmt.Sprintf("sec/f: %.5f", rl.GetFrameTime()), state.arenaWidth, 200, 50, rl.White,
|
||||||
|
)
|
||||||
|
rl.DrawText(
|
||||||
|
fmt.Sprintf("total life: %d", enemiesTotalLifeRemaining),
|
||||||
|
state.arenaWidth, 250, 50, rl.White,
|
||||||
|
)
|
||||||
|
rl.DrawText(
|
||||||
|
fmt.Sprintf("speed: %f", state.gameSpeed),
|
||||||
|
state.arenaWidth, 300, 50, rl.White,
|
||||||
)
|
)
|
||||||
rl.DrawText("danmaku babe bullet shoot shoot", 20, 20, 20, rl.DarkGray)
|
rl.DrawText("danmaku babe bullet shoot shoot", 20, 20, 20, rl.DarkGray)
|
||||||
rl.DrawLine(18, 42, state.arenaWidth-18, 42, rl.Black)
|
rl.DrawLine(18, 42, state.arenaWidth-18, 42, rl.Black)
|
||||||
|
|
|
||||||
138
movementPatterns.go
Normal file
138
movementPatterns.go
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type movementPattern func(*enemy) *enemy
|
||||||
|
|
||||||
|
type patternDuration struct {
|
||||||
|
duration duration
|
||||||
|
move movementPattern
|
||||||
|
}
|
||||||
|
|
||||||
|
type statePipeline []patternDuration
|
||||||
|
|
||||||
|
func shootStill() movementPattern {
|
||||||
|
return func(e *enemy) *enemy {
|
||||||
|
if e.shoot != nil {
|
||||||
|
e.shoot(e)
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp statePipeline) MovementPattern(g *game) movementPattern {
|
||||||
|
state := -1
|
||||||
|
t := newTimer(0)
|
||||||
|
return func(e *enemy) *enemy {
|
||||||
|
if !t.isTimeout() {
|
||||||
|
t.tick(g)
|
||||||
|
} else if state < len(sp)-1 {
|
||||||
|
state++
|
||||||
|
t = newTimer(sp[state].duration)
|
||||||
|
}
|
||||||
|
sp[state].move(e)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func horizonalPattern(g *game) movementPattern {
|
||||||
|
direction := 1
|
||||||
|
|
||||||
|
return func(e *enemy) *enemy {
|
||||||
|
if e.shoot != nil {
|
||||||
|
e.shoot(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
delta := rl.Vector2{
|
||||||
|
X: 400,
|
||||||
|
Y: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.pos.X < e.hitBoxRadius || e.pos.X > float32(g.arenaWidth-int32(e.hitBoxRadius)) {
|
||||||
|
direction = -direction
|
||||||
|
}
|
||||||
|
|
||||||
|
e.pos = rl.Vector2Add(
|
||||||
|
e.pos,
|
||||||
|
rl.Vector2Scale(
|
||||||
|
rl.Vector2{X: float32(direction) * delta.X, Y: delta.Y},
|
||||||
|
rl.GetFrameTime()*g.gameSpeed,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func descentMove(g *game, y0, y1 float32, d duration) movementPattern {
|
||||||
|
t := newTimer(d)
|
||||||
|
return func(e *enemy) *enemy {
|
||||||
|
|
||||||
|
if e.shoot != nil {
|
||||||
|
e.shoot(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t.isTimeout() {
|
||||||
|
t.tick(g)
|
||||||
|
} else {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
e.pos.Y = lerp(y0, y1, easeInOutSine(t.unit()))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sineDescentMove(g *game, y0, y1 float32, d duration) movementPattern {
|
||||||
|
t := newTimer(d)
|
||||||
|
return func(e *enemy) *enemy {
|
||||||
|
|
||||||
|
if e.shoot != nil {
|
||||||
|
e.shoot(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !t.isTimeout() {
|
||||||
|
t.tick(g)
|
||||||
|
} else {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
e.pos.Y = lerp(y0, y1, easeInOutSine(t.unit()))
|
||||||
|
e.pos.X += float32(math.Sin(rl.GetTime() * 5 * float64(g.gameSpeed)))
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sineHorizonalPattern(g *game, offset float64) movementPattern {
|
||||||
|
direction := 1
|
||||||
|
|
||||||
|
return func(e *enemy) *enemy {
|
||||||
|
if e.shoot != nil {
|
||||||
|
e.shoot(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
sine := math.Sin(rl.GetTime()*5*float64(g.gameSpeed)+offset) * 150
|
||||||
|
|
||||||
|
delta := rl.Vector2{
|
||||||
|
X: 400,
|
||||||
|
Y: float32(sine),
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.pos.X < e.hitBoxRadius || e.pos.X > float32(g.arenaWidth-int32(e.hitBoxRadius)) {
|
||||||
|
direction = -direction
|
||||||
|
}
|
||||||
|
|
||||||
|
e.pos = rl.Vector2Add(
|
||||||
|
e.pos,
|
||||||
|
rl.Vector2Scale(
|
||||||
|
rl.Vector2{X: float32(direction) * delta.X, Y: delta.Y},
|
||||||
|
rl.GetFrameTime()*g.gameSpeed,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
117
player.go
Normal file
117
player.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
rl "github.com/gen2brain/raylib-go/raylib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *player) move(g *game) {
|
||||||
|
|
||||||
|
var moveSpeed float32
|
||||||
|
if p.focusMode {
|
||||||
|
moveSpeed = p.moveSpeed * p.focusSpeedDecrease
|
||||||
|
} else {
|
||||||
|
moveSpeed = p.moveSpeed
|
||||||
|
}
|
||||||
|
|
||||||
|
p.speed = rl.Vector2{X: 0, Y: 0}
|
||||||
|
|
||||||
|
if rl.IsKeyDown(rl.KeyW) {
|
||||||
|
p.speed.Y -= 1
|
||||||
|
}
|
||||||
|
if rl.IsKeyDown(rl.KeyS) {
|
||||||
|
p.speed.Y += 1
|
||||||
|
}
|
||||||
|
if rl.IsKeyDown(rl.KeyA) {
|
||||||
|
p.speed.X -= 1
|
||||||
|
}
|
||||||
|
if rl.IsKeyDown(rl.KeyD) {
|
||||||
|
p.speed.X += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(p.speed.X == 0 && p.speed.Y == 0) {
|
||||||
|
// jogador se move mais rapido na diagonal caso o contrario
|
||||||
|
p.speed = rl.Vector2Normalize(p.speed)
|
||||||
|
p.speed = rl.Vector2Scale(p.speed, moveSpeed)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := rl.Vector2Add(p.pos, p.speed)
|
||||||
|
|
||||||
|
if result.Y-p.hitBoxRadius < 0 || result.Y+p.hitBoxRadius > float32(g.arenaHeight) {
|
||||||
|
p.speed.Y = 0
|
||||||
|
}
|
||||||
|
if result.X-p.hitBoxRadius < 0 || result.X+p.hitBoxRadius > float32(g.arenaWidth) {
|
||||||
|
p.speed.X = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
p.pos = rl.Vector2Add(p.pos, p.speed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) shoot(g *game) {
|
||||||
|
if p.focusMode {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(g.frame)%3 != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if rl.IsMouseButtonDown(rl.MouseLeftButton) {
|
||||||
|
mouse := rl.GetMousePosition()
|
||||||
|
direction := rl.Vector2Subtract(mouse, p.pos)
|
||||||
|
direction = rl.Vector2Add(direction, p.speed)
|
||||||
|
direction = rl.Vector2Normalize(direction)
|
||||||
|
direction = rl.Vector2Scale(direction, p.bulletMoveSpeed)
|
||||||
|
|
||||||
|
g.bullets = append(g.bullets, &bullet{
|
||||||
|
pos: p.pos,
|
||||||
|
size: p.bulletSize,
|
||||||
|
speed: direction,
|
||||||
|
dmg: 1,
|
||||||
|
enemy: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) checkHit(g *game) {
|
||||||
|
for _, bullet := range g.bullets {
|
||||||
|
if !bullet.enemy {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
distance := rl.Vector2Distance(p.pos, bullet.pos) - bullet.size
|
||||||
|
|
||||||
|
if distance < p.hitBoxRadius {
|
||||||
|
// fmt.Println("hit!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) checkFocus() {
|
||||||
|
// raylib não entende keybindings customizadas através do xmodmap?
|
||||||
|
p.focusMode = rl.IsKeyDown(rl.KeyLeftShift) || rl.IsKeyDown(rl.KeyRightShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *player) update(g *game) {
|
||||||
|
|
||||||
|
p.checkFocus()
|
||||||
|
|
||||||
|
p.move(g)
|
||||||
|
|
||||||
|
p.shoot(g)
|
||||||
|
|
||||||
|
p.checkHit(g)
|
||||||
|
|
||||||
|
// hitbox
|
||||||
|
rl.DrawCircleV(p.pos, p.hitBoxRadius, rl.Red)
|
||||||
|
|
||||||
|
if p.focusMode {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// mira
|
||||||
|
mouse := rl.GetMousePosition()
|
||||||
|
inverted := rl.Vector2Subtract(p.pos, mouse)
|
||||||
|
inverted = rl.Vector2Negate(inverted)
|
||||||
|
inverted = rl.Vector2Normalize(inverted)
|
||||||
|
inverted = rl.Vector2Scale(inverted, 2000)
|
||||||
|
rl.DrawLineV(p.pos, rl.Vector2Add(mouse, inverted), rl.NewColor(255, 0, 0, 100))
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue