Compare commits
No commits in common. "d50bed0777b70e52256cfe9448cb45439707dab6" and "e5daf2a7ed4ed483b1d957cd7fec3a81c40fc19d" have entirely different histories.
d50bed0777
...
e5daf2a7ed
7 changed files with 218 additions and 1000 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,3 +0,0 @@
|
|||
danmaku
|
||||
danmaku.exe
|
||||
shaders/*
|
||||
21
ease.go
21
ease.go
|
|
@ -1,21 +0,0 @@
|
|||
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
|
||||
|
||||
go 1.23
|
||||
go 1.20
|
||||
|
||||
require github.com/gen2brain/raylib-go/raylib v0.0.0-20230719211022-1083eace2049
|
||||
|
|
|
|||
692
main.go
692
main.go
|
|
@ -3,51 +3,24 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
// diz para um inimigo em que condições atirar. acionado pelo movementPattern
|
||||
type shootingPattern func(body)
|
||||
type movementPattern int
|
||||
type shootingPattern int
|
||||
|
||||
// diz para uma bala como ela deve se mover.
|
||||
type bulletMovementPattern func(*bullet) rl.Vector2
|
||||
|
||||
type duration float32
|
||||
|
||||
const second duration = 1
|
||||
|
||||
type body interface { // implementado por player e enemy
|
||||
Pos() rl.Vector2
|
||||
SetPos(rl.Vector2)
|
||||
Direction() rl.Vector2
|
||||
}
|
||||
|
||||
// type hazard interface { // inimigos e projéteis
|
||||
// body
|
||||
// }
|
||||
const (
|
||||
horizontal movementPattern = iota
|
||||
circular movementPattern = iota
|
||||
)
|
||||
|
||||
type game struct {
|
||||
arenaWidth int32
|
||||
arenaHeight int32
|
||||
interfaceWidth int32
|
||||
frame float32
|
||||
screenWidth int32
|
||||
screenHeight int32
|
||||
time int
|
||||
enemies []*enemy
|
||||
waves []*wave
|
||||
walls []plane
|
||||
currentWave int
|
||||
bullets []*bullet
|
||||
score int
|
||||
gameSpeed float32 // 1 = velocidade normal
|
||||
backgroundColor rl.Color
|
||||
}
|
||||
|
||||
type plane struct {
|
||||
normal rl.Vector2
|
||||
pos rl.Vector2
|
||||
}
|
||||
|
||||
type bullet struct {
|
||||
|
|
@ -55,72 +28,27 @@ type bullet struct {
|
|||
speed rl.Vector2
|
||||
size float32
|
||||
dmg int
|
||||
owner body
|
||||
onHit func(body)
|
||||
onDestroy func()
|
||||
enemy bool
|
||||
}
|
||||
|
||||
type player struct {
|
||||
pos rl.Vector2
|
||||
moveSpeed float32
|
||||
bulletMoveSpeed float32
|
||||
hitBoxRadius float32
|
||||
}
|
||||
|
||||
type enemy struct {
|
||||
pos rl.Vector2
|
||||
direction rl.Vector2
|
||||
health int
|
||||
move movementPattern
|
||||
shoot shootingPattern
|
||||
movePattern func(*enemy)rl.Vector2
|
||||
shootPattern func()
|
||||
hitBoxRadius float32
|
||||
}
|
||||
|
||||
func (e *enemy) Pos() rl.Vector2 {
|
||||
return e.pos
|
||||
}
|
||||
|
||||
func (e *enemy) Direction() rl.Vector2 {
|
||||
return e.direction
|
||||
}
|
||||
|
||||
func (e *enemy) SetPos(x rl.Vector2) {
|
||||
e.pos = x
|
||||
}
|
||||
|
||||
type wave struct {
|
||||
duration *timer
|
||||
enemies []*enemy
|
||||
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 {
|
||||
return v.X >= 0 && v.Y >= 0 &&
|
||||
v.Y <= float32(g.arenaHeight) && v.X <= float32(g.arenaWidth)
|
||||
v.Y <= float32(g.screenHeight) && v.X <= float32(g.screenWidth)
|
||||
}
|
||||
|
||||
func (g *game) removeBullet(index int) {
|
||||
|
|
@ -129,12 +57,9 @@ func (g *game) removeBullet(index int) {
|
|||
}
|
||||
|
||||
func (b *bullet) update(g *game, index int) {
|
||||
b.pos = rl.Vector2Add(b.pos, rl.Vector2Scale(b.speed, rl.GetFrameTime()*g.gameSpeed))
|
||||
b.pos = rl.Vector2Add(b.pos, b.speed)
|
||||
|
||||
if !g.insideArena(b.pos) {
|
||||
if b.onDestroy != nil {
|
||||
b.onDestroy()
|
||||
}
|
||||
g.removeBullet(index)
|
||||
return
|
||||
}
|
||||
|
|
@ -142,172 +67,71 @@ func (b *bullet) update(g *game, index int) {
|
|||
rl.DrawCircleV(b.pos, b.size, rl.Yellow)
|
||||
}
|
||||
|
||||
func bulletExplosion(g *game, rate float32, amount int, bulletSpeed, size float32) shootingPattern {
|
||||
t := newTimer(second * duration(rate+rand.Float32()))
|
||||
return func(e body) {
|
||||
|
||||
t.tick(g)
|
||||
if !t.isTimeout() {
|
||||
return
|
||||
}
|
||||
t.reset()
|
||||
|
||||
var bullets []*bullet
|
||||
|
||||
for i := 0; i < amount; i++ {
|
||||
angle := 2.0 * math.Pi * float64(i) / float64(amount)
|
||||
cos := math.Cos(angle)
|
||||
sin := math.Sin(angle)
|
||||
direction := rl.Vector2{X: float32(cos), Y: float32(sin)}
|
||||
direction = rl.Vector2Scale(direction, bulletSpeed)
|
||||
|
||||
bullets = append(bullets, &bullet{
|
||||
speed: direction,
|
||||
size: size,
|
||||
dmg: 1,
|
||||
owner: e,
|
||||
pos: e.Pos(),
|
||||
})
|
||||
}
|
||||
|
||||
g.bullets = append(g.bullets, bullets...)
|
||||
}
|
||||
}
|
||||
|
||||
// func ShootAtPlayer(g *game, p *player, rate int,
|
||||
// bulletMoveSpeed float32) shootingPattern {
|
||||
// return func(e body) {
|
||||
// if int(g.frame)%rate != 0 {
|
||||
// return
|
||||
// }
|
||||
// direction := rl.Vector2Subtract(p.pos, e.Pos())
|
||||
// direction = rl.Vector2Normalize(direction)
|
||||
// direction = rl.Vector2Scale(direction, bulletMoveSpeed)
|
||||
//
|
||||
// g.bullets = append(g.bullets, &bullet{
|
||||
// speed: direction,
|
||||
// size: 12,
|
||||
// dmg: 1,
|
||||
// enemy: true,
|
||||
// pos: e.Pos(),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
func burstShootAtPlayer(g *game, p *player, rate float32, bulletMoveSpeed float32) shootingPattern {
|
||||
flag := true
|
||||
off := newTimer(second)
|
||||
on := newTimer(duration(float32(second) * rate))
|
||||
return func(e body) {
|
||||
|
||||
off.tick(g)
|
||||
|
||||
if off.isTimeout() {
|
||||
flag = !flag
|
||||
off.reset()
|
||||
}
|
||||
|
||||
if !flag {
|
||||
func (e *enemy) shoot(g *game) {
|
||||
if g.time % 15 != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
on.tick(g)
|
||||
if !on.isTimeout() {
|
||||
return
|
||||
}
|
||||
on.reset()
|
||||
|
||||
direction := rl.Vector2Subtract(p.pos, e.Pos())
|
||||
direction = rl.Vector2Normalize(direction)
|
||||
direction = rl.Vector2Scale(direction, bulletMoveSpeed)
|
||||
|
||||
g.bullets = append(g.bullets, &bullet{
|
||||
speed: direction,
|
||||
size: 12,
|
||||
speed: rl.Vector2{X: 0, Y: 10},
|
||||
size: 4,
|
||||
dmg: 1,
|
||||
owner: e,
|
||||
pos: e.Pos(),
|
||||
enemy: true,
|
||||
pos: e.pos,
|
||||
})
|
||||
}
|
||||
|
||||
func horizonalPattern(g *game) (func(*enemy)rl.Vector2) {
|
||||
direction := rl.Vector2{X: 4, Y: 0}
|
||||
|
||||
return func(e *enemy) rl.Vector2 {
|
||||
result := rl.Vector2Add(direction, e.pos)
|
||||
|
||||
if !g.insideArena(result) {
|
||||
direction = rl.Vector2Negate(direction)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
func shootStraightDown(g *game) shootingPattern {
|
||||
return func(e body) {
|
||||
if int(g.frame)%10 != 0 {
|
||||
return
|
||||
}
|
||||
g.bullets = append(g.bullets, &bullet{
|
||||
speed: rl.Vector2{X: 0, Y: 5},
|
||||
size: 12,
|
||||
dmg: 1,
|
||||
owner: e,
|
||||
pos: e.Pos(),
|
||||
})
|
||||
func circlePattern(g *game, center rl.Vector2) (func(*enemy)rl.Vector2) {
|
||||
|
||||
t := float64(0.5)
|
||||
|
||||
return func(e *enemy) rl.Vector2 {
|
||||
// x' = (x - x₀) * cos(θ) - (y - y₀) * sin(θ) + x₀
|
||||
// y' = (x - x₀) * sin(θ) + (y - y₀) * cos(θ) + y₀
|
||||
|
||||
// x' = x * cos(θ) - y * sin(θ)
|
||||
// y' = x * sin(θ) + y * cos(θ)
|
||||
|
||||
// result := center
|
||||
//
|
||||
// result.X = result.X + (e.pos.X - result.X) * float32(math.Cos(t)) -
|
||||
// (e.pos.Y - result.Y) * float32(math.Sin(t))
|
||||
//
|
||||
// result.Y = result.Y + (e.pos.Y - result.Y) * float32(math.Sin(t)) +
|
||||
// (e.pos.Y - result.Y) * float32(math.Cos(t))
|
||||
|
||||
result := center
|
||||
result.X = result.X * float32(math.Cos(t)) - result.Y * float32(math.Sin(t))
|
||||
result.Y = result.X * float32(math.Sin(t)) - result.Y * float32(math.Cos(t))
|
||||
|
||||
t += .08
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// func detectWallCollision(planes []plane, initialPos, direction rl.Vector2) float32 {
|
||||
// p := initialPos
|
||||
// w := direction
|
||||
// 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 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) {
|
||||
for index, bullet := range g.bullets {
|
||||
|
||||
_, playerBullet := bullet.owner.(*player)
|
||||
playerBullet := !bullet.enemy
|
||||
if !playerBullet {
|
||||
continue
|
||||
}
|
||||
distance := rl.Vector2Distance(e.pos, bullet.pos) - bullet.size
|
||||
// fmt.Println(distance)
|
||||
|
||||
if distance < e.hitBoxRadius {
|
||||
return true, bullet, index
|
||||
|
|
@ -321,348 +145,150 @@ func (g *game) killEnemy(index int) {
|
|||
g.enemies = g.enemies[:len(g.enemies)-1]
|
||||
}
|
||||
|
||||
func (e *enemy) deleteBullets(g *game) {
|
||||
for i := 0; i < len(g.bullets); i++ {
|
||||
enemy, isEnemyBullet := g.bullets[i].owner.(*enemy)
|
||||
if isEnemyBullet && enemy == e {
|
||||
g.removeBullet(i)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *enemy) update(g *game) {
|
||||
func (e *enemy) update(g *game, index int) {
|
||||
|
||||
if e.health <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
enemyColor := rl.Blue
|
||||
|
||||
if e.move != nil {
|
||||
// e.pos = rl.Vector2Add(rl.Vector2Scale(e.move(e), g.gameSpeed), e.pos)
|
||||
e.move(e)
|
||||
// e.pos = e.move(e)
|
||||
if e.movePattern != nil {
|
||||
e.pos = e.movePattern(e)
|
||||
}
|
||||
|
||||
if hit, bullet, idx := e.checkHit(g); hit {
|
||||
|
||||
g.score += 273
|
||||
e.health -= bullet.dmg
|
||||
|
||||
if bullet.onHit != nil {
|
||||
bullet.onHit(e)
|
||||
}
|
||||
|
||||
g.removeBullet(idx)
|
||||
enemyColor = rl.White
|
||||
g.backgroundColor = rl.NewColor(20, 20, 20, 255)
|
||||
}
|
||||
|
||||
if e.health <= 0 {
|
||||
e.deleteBullets(g)
|
||||
g.killEnemy(index)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rl.DrawCircleV(e.pos, e.hitBoxRadius, enemyColor)
|
||||
e.shoot(g)
|
||||
|
||||
rl.DrawCircleV(e.pos, 5, rl.Blue)
|
||||
}
|
||||
|
||||
func (g *game) fullClear() bool {
|
||||
for _, e := range g.enemies {
|
||||
if e.health > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
func (p *player) move() {
|
||||
if rl.IsKeyDown(rl.KeyW) { p.pos.Y -= p.moveSpeed }
|
||||
if rl.IsKeyDown(rl.KeyS) { p.pos.Y += p.moveSpeed }
|
||||
if rl.IsKeyDown(rl.KeyA) { p.pos.X -= p.moveSpeed }
|
||||
if rl.IsKeyDown(rl.KeyD) { p.pos.X += p.moveSpeed }
|
||||
}
|
||||
|
||||
func (g *game) waveTimeout() bool {
|
||||
currentWave := g.waves[g.currentWave]
|
||||
if currentWave.duration == nil {
|
||||
return false
|
||||
func (p *player) shoot(g *game){
|
||||
if g.time % 10 != 0 {
|
||||
return
|
||||
}
|
||||
if currentWave.duration.isTimeout() {
|
||||
return true
|
||||
|
||||
if rl.IsMouseButtonDown(rl.MouseLeftButton) {
|
||||
// não leva em consideração a velocidade do jogador
|
||||
mouse := rl.GetMousePosition()
|
||||
direction := rl.Vector2Subtract(mouse, p.pos)
|
||||
direction = rl.Vector2Normalize(direction)
|
||||
g.bullets = append(g.bullets, &bullet{
|
||||
pos: p.pos,
|
||||
size: 2,
|
||||
speed: rl.Vector2Scale(direction, p.bulletMoveSpeed),
|
||||
dmg: 1,
|
||||
enemy: false,
|
||||
})
|
||||
}
|
||||
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
|
||||
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!")
|
||||
}
|
||||
}
|
||||
g.addEnemy(g.waves[g.currentWave].enemies...)
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *game) addEnemy(e ...*enemy) {
|
||||
g.enemies = append(g.enemies, e...)
|
||||
func (p *player) update(g *game) {
|
||||
|
||||
p.move()
|
||||
|
||||
p.shoot(g)
|
||||
|
||||
p.checkHit(g)
|
||||
|
||||
// hitbox
|
||||
rl.DrawCircleV(p.pos, p.hitBoxRadius, rl.Red)
|
||||
|
||||
// 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 main() {
|
||||
state := &game{
|
||||
arenaWidth: 450,
|
||||
arenaHeight: 900,
|
||||
interfaceWidth: 400,
|
||||
gameSpeed: 1,
|
||||
backgroundColor: rl.NewColor(0, 0, 0, 100),
|
||||
}
|
||||
player := player{
|
||||
pos: rl.Vector2{
|
||||
X: float32(state.arenaWidth) / 2,
|
||||
Y: float32(state.arenaHeight) * 0.8,
|
||||
},
|
||||
direction: rl.Vector2{X: 0, Y: -1},
|
||||
moveSpeed: 400,
|
||||
focusSpeedDecrease: 0.5,
|
||||
bulletMoveSpeed: 6,
|
||||
bulletSize: 9,
|
||||
hitBoxRadius: 5,
|
||||
shoot: snipe(state),
|
||||
}
|
||||
|
||||
// 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
|
||||
state := &game{screenWidth: 450, screenHeight: 700}
|
||||
|
||||
player := player{
|
||||
pos: rl.Vector2{X: 100, Y: 100},
|
||||
moveSpeed: 4,
|
||||
bulletMoveSpeed: 8,
|
||||
hitBoxRadius: 5,
|
||||
}
|
||||
|
||||
rl.SetTraceLog(rl.LogWarning | rl.LogDebug)
|
||||
rl.InitWindow(state.screenWidth, state.screenHeight, "danmaku")
|
||||
rl.SetTargetFPS(60)
|
||||
|
||||
rl.SetConfigFlags(rl.FlagMsaa4xHint)
|
||||
rl.InitWindow(state.arenaWidth+state.interfaceWidth, state.arenaHeight, "danmaku")
|
||||
targetFPS := 120
|
||||
rl.SetTargetFPS(int32(targetFPS))
|
||||
|
||||
// shader_path := path.Join("shaders", "trails.glsl")
|
||||
// shader := rl.LoadShader("", shader_path)
|
||||
// timeShaderLocation := rl.GetShaderLocation(shader, "time")
|
||||
// target := rl.LoadRenderTexture(
|
||||
// state.arenaWidth+state.interfaceWidth,
|
||||
// state.arenaHeight,
|
||||
// )
|
||||
|
||||
shader_path := path.Join("shaders", "pixelized.glsl")
|
||||
shader := rl.LoadShader("", shader_path)
|
||||
// timeShaderLocation := rl.GetShaderLocation(shader, "time")
|
||||
// target := rl.LoadRenderTexture(
|
||||
// state.arenaWidth+state.interfaceWidth,
|
||||
// state.arenaHeight,
|
||||
// )
|
||||
|
||||
state.waves = []*wave{
|
||||
{
|
||||
duration: newTimer(second * 10),
|
||||
enemies: []*enemy{
|
||||
{
|
||||
pos: rl.Vector2{X: 100, Y: -20},
|
||||
health: 100,
|
||||
hitBoxRadius: 20,
|
||||
move: statePipeline{
|
||||
{
|
||||
duration: 1.1,
|
||||
move: sineDescentMove(state, -20, 200, 1),
|
||||
},
|
||||
{
|
||||
move: sineHorizonalPattern(state, 0),
|
||||
},
|
||||
}.MovementPattern(state),
|
||||
shoot: burstShootAtPlayer(state, &player, 0.1, 400),
|
||||
},
|
||||
{
|
||||
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, 400),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
duration: newTimer(second * 20),
|
||||
enemies: []*enemy{
|
||||
{
|
||||
pos: rl.Vector2{X: 100, Y: 100},
|
||||
health: 100,
|
||||
hitBoxRadius: 20,
|
||||
move: statePipeline{
|
||||
{
|
||||
duration: 1.1,
|
||||
move: sineDescentMove(state, -20, 200, 1),
|
||||
},
|
||||
{
|
||||
move: sineHorizonalPattern(state, 0),
|
||||
},
|
||||
}.MovementPattern(state),
|
||||
shoot: bulletExplosion(state, 1, 40, 300, 11),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
enemies: []*enemy{
|
||||
state.enemies = []*enemy{
|
||||
{
|
||||
pos: rl.Vector2{X: 200, Y: 200},
|
||||
health: 100,
|
||||
hitBoxRadius: 20,
|
||||
move: statePipeline{
|
||||
{
|
||||
duration: 1.1,
|
||||
move: sineDescentMove(state, -20, 200, 1),
|
||||
health: 1,
|
||||
hitBoxRadius: 5,
|
||||
movePattern: horizonalPattern(state),
|
||||
},
|
||||
{
|
||||
move: sineHorizonalPattern(state, 0),
|
||||
},
|
||||
}.MovementPattern(state),
|
||||
shoot: bulletExplosion(state, 1, 20, 300, 11),
|
||||
pos: rl.Vector2{X: 169, Y: 222},
|
||||
health: 1,
|
||||
hitBoxRadius:5,
|
||||
movePattern: horizonalPattern(state),
|
||||
},
|
||||
{
|
||||
pos: rl.Vector2{X: 100, Y: 100}, health: 100,
|
||||
hitBoxRadius: 20,
|
||||
move: statePipeline{
|
||||
{
|
||||
duration: 1.1,
|
||||
move: sineDescentMove(state, -20, 200, 1),
|
||||
pos: rl.Vector2{X: 400, Y: 400},
|
||||
health: 1,
|
||||
hitBoxRadius:5,
|
||||
// movePattern: circlePattern(state, rl.Vector2{X: 200, Y: 200}),
|
||||
},
|
||||
{
|
||||
move: sineHorizonalPattern(state, 0),
|
||||
},
|
||||
}.MovementPattern(state),
|
||||
shoot: burstShootAtPlayer(state, &player, 0.2, 400),
|
||||
},
|
||||
{
|
||||
pos: rl.Vector2{X: 50, Y: 250},
|
||||
health: 100,
|
||||
hitBoxRadius: 20,
|
||||
move: statePipeline{
|
||||
{
|
||||
duration: 1.1,
|
||||
move: sineDescentMove(state, -20, 200, 1),
|
||||
},
|
||||
{
|
||||
move: sineHorizonalPattern(state, 0),
|
||||
},
|
||||
}.MovementPattern(state),
|
||||
shoot: bulletExplosion(state, 1, 20, 300, 11),
|
||||
// shoot: shootStraightDown(state),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
pos: rl.Vector2{X: 200, Y: 200},
|
||||
health: 1,
|
||||
hitBoxRadius:5,
|
||||
// movePattern: circlePattern(state, rl.Vector2{X: 200, Y: 200}),
|
||||
}}
|
||||
|
||||
currectScore := 0
|
||||
state.addEnemy(state.waves[0].enemies...)
|
||||
|
||||
spawner := starsBackground(state)
|
||||
|
||||
for ; !rl.WindowShouldClose(); state.frame += state.gameSpeed {
|
||||
for ; !rl.WindowShouldClose(); state.time += 1 {
|
||||
rl.BeginDrawing()
|
||||
rl.BeginShaderMode(shader)
|
||||
rl.ClearBackground(rl.Black)
|
||||
|
||||
// rl.BeginTextureMode(target)
|
||||
rl.ClearBackground(state.backgroundColor)
|
||||
state.backgroundColor = rl.Black
|
||||
rl.DrawText("danmaku", 20, 20, 20, rl.DarkGray)
|
||||
rl.DrawLine(18, 42, state.screenWidth-18, 42, rl.Black)
|
||||
rl.DrawFPS(0, 0)
|
||||
|
||||
player.update(state)
|
||||
|
||||
if state.fullClear() || state.waveTimeout() {
|
||||
if !state.nextWave() {
|
||||
break
|
||||
}
|
||||
for i := range state.enemies {
|
||||
state.enemies[i].update(state, i)
|
||||
}
|
||||
|
||||
enemiesTotalLifeRemaining := 0
|
||||
for i := 0; i < len(state.enemies); i++ {
|
||||
state.enemies[i].update(state)
|
||||
enemiesTotalLifeRemaining += max(state.enemies[i].health, 0)
|
||||
}
|
||||
for i := 0; i < len(state.bullets); i++ {
|
||||
state.bullets[i].update(state, i)
|
||||
}
|
||||
|
||||
rl.DrawRectangle(
|
||||
state.arenaWidth, 0, state.interfaceWidth, state.arenaHeight,
|
||||
rl.NewColor(0, 33, 59, 255),
|
||||
)
|
||||
|
||||
spawner.update(state)
|
||||
|
||||
if player.focusMode {
|
||||
state.gameSpeed = 0.3
|
||||
} else {
|
||||
state.gameSpeed = 1
|
||||
}
|
||||
|
||||
{ // UI
|
||||
currectScore += (state.score - currectScore) / 11
|
||||
rl.DrawText(
|
||||
fmt.Sprint("score: ", strconv.Itoa(currectScore)),
|
||||
state.arenaWidth, 0, 50, rl.White,
|
||||
)
|
||||
|
||||
rl.DrawText(
|
||||
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(
|
||||
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.DrawLine(18, 42, state.arenaWidth-18, 42, rl.Black)
|
||||
rl.DrawFPS(0, 0)
|
||||
}
|
||||
|
||||
rl.EndShaderMode()
|
||||
rl.EndDrawing()
|
||||
|
||||
}
|
||||
|
||||
rl.CloseWindow()
|
||||
|
|
|
|||
|
|
@ -1,138 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
243
player.go
243
player.go
|
|
@ -1,243 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
type player struct {
|
||||
pos rl.Vector2
|
||||
speed rl.Vector2
|
||||
direction rl.Vector2
|
||||
moveSpeed float32
|
||||
bulletMoveSpeed float32
|
||||
hitBoxRadius float32
|
||||
shoot shootingPattern
|
||||
bulletSize float32
|
||||
focusMode bool
|
||||
focusSpeedDecrease float32
|
||||
}
|
||||
|
||||
func (p *player) Pos() rl.Vector2 {
|
||||
return p.pos
|
||||
}
|
||||
|
||||
func (p *player) SetPos(x rl.Vector2) {
|
||||
p.pos = x
|
||||
}
|
||||
|
||||
func (p *player) Direction() rl.Vector2 {
|
||||
return p.direction
|
||||
}
|
||||
|
||||
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, rl.Vector2Scale(p.speed, rl.GetFrameTime()))
|
||||
|
||||
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, rl.Vector2Scale(p.speed, rl.GetFrameTime()))
|
||||
}
|
||||
|
||||
// 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 {
|
||||
_, playerBullet := bullet.owner.(*player)
|
||||
if playerBullet {
|
||||
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)
|
||||
|
||||
if !p.focusMode && p.shoot != nil {
|
||||
p.shoot(p)
|
||||
}
|
||||
|
||||
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.DrawLineEx(p.pos, rl.Vector2Add(mouse, inverted), 3, rl.NewColor(255, 0, 0, 100))
|
||||
|
||||
}
|
||||
|
||||
func tripleFire(g *game) shootingPattern {
|
||||
t := newTimer(second * 0.05)
|
||||
return func(b body) {
|
||||
|
||||
if !t.isTimeout() {
|
||||
t.tick(g)
|
||||
return
|
||||
}
|
||||
t.reset()
|
||||
|
||||
if rl.IsMouseButtonDown(rl.MouseLeftButton) {
|
||||
|
||||
g.bullets = append(g.bullets, &bullet{
|
||||
pos: b.Pos(),
|
||||
size: 9,
|
||||
speed: rl.Vector2Scale(
|
||||
rl.Mat2MultiplyVector2(rl.Mat2Radians(0.3), b.Direction()),
|
||||
600,
|
||||
),
|
||||
dmg: 1,
|
||||
owner: b,
|
||||
})
|
||||
g.bullets = append(g.bullets, &bullet{
|
||||
pos: b.Pos(),
|
||||
size: 9,
|
||||
speed: rl.Vector2Scale(
|
||||
rl.Mat2MultiplyVector2(rl.Mat2Radians(0), b.Direction()),
|
||||
600,
|
||||
),
|
||||
dmg: 1,
|
||||
owner: b,
|
||||
})
|
||||
g.bullets = append(g.bullets, &bullet{
|
||||
pos: b.Pos(),
|
||||
size: 9,
|
||||
speed: rl.Vector2Scale(
|
||||
rl.Mat2MultiplyVector2(rl.Mat2Radians(-0.3), b.Direction()),
|
||||
600,
|
||||
),
|
||||
dmg: 1,
|
||||
owner: b,
|
||||
})
|
||||
}
|
||||
|
||||
// rl.matrix
|
||||
}
|
||||
}
|
||||
|
||||
func snipe(g *game) shootingPattern {
|
||||
t := newTimer(1)
|
||||
hits := 0
|
||||
return func(b body) {
|
||||
|
||||
p, ok := b.(*player)
|
||||
if !ok {
|
||||
panic(b)
|
||||
}
|
||||
|
||||
if !t.isTimeout() {
|
||||
t.tick(g)
|
||||
return
|
||||
}
|
||||
|
||||
if !rl.IsMouseButtonPressed(rl.MouseLeftButton) {
|
||||
return
|
||||
}
|
||||
|
||||
t.reset()
|
||||
|
||||
mouse := rl.GetMousePosition()
|
||||
direction := rl.Vector2Subtract(mouse, p.pos)
|
||||
direction = rl.Vector2Add(direction, rl.Vector2Scale(p.speed, rl.GetFrameTime()*g.gameSpeed))
|
||||
direction = rl.Vector2Normalize(direction)
|
||||
direction = rl.Vector2Scale(direction, p.bulletMoveSpeed*700)
|
||||
|
||||
g.bullets = append(g.bullets, &bullet{
|
||||
pos: b.Pos(),
|
||||
size: p.bulletSize,
|
||||
speed: direction,
|
||||
dmg: 10 * hits,
|
||||
owner: b,
|
||||
onHit: func(b body) {
|
||||
hits += 2
|
||||
fmt.Println("hit!", hits)
|
||||
},
|
||||
onDestroy: func() {
|
||||
hits--
|
||||
hits = max(hits, 0)
|
||||
fmt.Println("hit!", hits)
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc GOOS=windows GOARCH=amd64 CGO_LDFLAGS="-static-libgcc -static -lpthread" go build
|
||||
Loading…
Add table
Add a link
Reference in a new issue