package main import ( "fmt" "math" "math/rand" rl "github.com/gen2brain/raylib-go/raylib" ) const ( WIDTH = 1100 HEIGHT = 700 buildingSpacing = 2.5 buildingSize = 1.2 maxBuildingHeight = 10 maxColumns = 5 enemyAcc = 0.01 maxSpeed = 0.1 buildingsHealth = 3 ) type enemy struct { pos rl.Vector3 speed float32 } type remainingFrames int type animationFunc func()remainingFrames type building struct { pos rl.Vector3 size rl.Vector3 color rl.Color life int boundingBox rl.BoundingBox animate animationFunc } type healthBar struct { life int damageTaken float32 lastUpdate float64 } type character struct { camera rl.Camera damage int } type projectile struct { pos rl.Vector3 speed rl.Vector3 } type scene struct { buildings []*building healthBar *healthBar projectiles []*projectile character *character emitters []*emitter } type particle struct { // birth float32 lifetime float64 pos rl.Vector3 speed rl.Vector3 } type emitter struct { particles []*particle pos rl.Vector3 particleAmount int } var city *scene func update_emitters() { for i, e := range city.emitters { if remainingParticles := e.update(); remainingParticles == 0 { city.emitters[i] = city.emitters[len(city.emitters)-1] city.emitters = city.emitters[:len(city.emitters)-1] } } } func newEmitter(pos rl.Vector3, particleAmount int) *emitter { emitter := emitter{ pos: pos, particleAmount: particleAmount, } for i := 0; i < particleAmount; i++ { direction := rl.NewVector3( rand.Float32()*2 - 1, 1, rand.Float32()*2 - 1, ) direction = rl.Vector3Normalize(direction) direction = rl.Vector3Scale(direction, 0.3 + rand.Float32()/3) particle := particle{ lifetime: rl.GetTime() + 2, speed: direction, pos: pos, } emitter.particles = append(emitter.particles, &particle) } return &emitter } func (e *emitter) update() int { gravity := rl.NewVector3(0, -0.02, 0) remainingParticles := 0 rl.BeginBlendMode(rl.BlendAdditive) { for _, particle := range e.particles { if rl.GetTime() > particle.lifetime { continue } particle.speed = rl.Vector3Add(particle.speed, gravity) newPos := rl.Vector3Add(particle.pos, particle.speed) if newPos.Y <= 0 { particle.pos.Y = 0 } else { particle.pos = newPos } rl.DrawCubeV(particle.pos, rl.NewVector3(.1, .1, .1), rl.Yellow) remainingParticles += 1 } } rl.EndBlendMode() return remainingParticles } // retona true caso projétil precise ser destruido func (p *projectile) isHit() bool { gravity := rl.NewVector3(0, -0.02, 0) p.speed = rl.Vector3Add(p.speed, gravity) p.pos = rl.Vector3Add(p.pos, p.speed) if p.pos.Y < 0 { city.emitters = append(city.emitters, newEmitter(p.pos, 500)) for _, building := range city.buildings { if dist := rl.Vector3Distance(p.pos, building.pos); dist < 10 { building.causeDamage(3) } } return true } rl.DrawSphere(p.pos, 0.5, rl.Gray) return false } func update_projectiles() { for i, p := range city.projectiles { if hit := p.isHit(); hit { city.projectiles[i] = city.projectiles[len(city.projectiles)-1] city.projectiles = city.projectiles[:len(city.projectiles)-1] } } } func (c *character) kill_aurea() { const min = 10 for _, b := range city.buildings { if distance := rl.Vector3Distance(c.camera.Position, b.pos); distance < min { b.causeDamage(c.damage) } } } func (c *character) throw_bomb(){ if !rl.IsMouseButtonPressed(rl.MouseRightButton) { return } direction := rl.Vector3Subtract(c.camera.Target, c.camera.Position) projectile := projectile{ pos: c.camera.Position, speed: rl.Vector3Scale(direction, 0.15), } city.projectiles = append(city.projectiles, &projectile) } func (c *character) update() { shake := rl.NewVector3( randomShake(0.003) * city.healthBar.damageTaken, randomShake(0.003) * city.healthBar.damageTaken, randomShake(0.003) * city.healthBar.damageTaken, ) c.camera.Target = rl.Vector3Add(c.camera.Target, shake) c.laser_beam() // c.kill_aurea() c.throw_bomb() } func newCharacter() *character { camera := rl.Camera3D{ Position: rl.NewVector3(4.0, 2.0, 4.0), Target: rl.NewVector3(0.0, 1.8, 0.0), Up: rl.NewVector3(0.0, 1.0, 0.0), Fovy: 80.0, Projection: rl.CameraPerspective, } return &character{ camera: camera, damage: 1, } } func draw_plane() { rl.DrawPlane( rl.NewVector3(16, 0.0, 16), rl.NewVector2(128.0, 128.0), rl.NewColor(50, 50, 50, 255), ) } func randomShake(amount float32) float32 { return rand.Float32() * amount - amount/2 } func (hb *healthBar) update() { const MARGIN = 20 const BARWIDTH = (WIDTH - MARGIN*2) currentLife := 0 maxLife := buildingsHealth * len(city.buildings) for _, b := range city.buildings { currentLife += b.life } position := rl.NewVector2(MARGIN, 10) barWidth := BARWIDTH * currentLife/maxLife size := rl.NewVector2(float32(barWidth), 10) hb.damageTaken += float32(hb.life - currentLife) damageTakenBar := size damageTakenBar.X = BARWIDTH * (float32(currentLife) + hb.damageTaken) / float32(maxLife) shake := rl.NewVector2( randomShake(0.4) * hb.damageTaken, randomShake(0.4) * hb.damageTaken, ) position = rl.Vector2Add(position, shake) rl.DrawRectangleV(position, damageTakenBar, rl.Orange) rl.DrawRectangleV(position, size, rl.Red) hb.lastUpdate = rl.GetTime() hb.life = currentLife hb.damageTaken *= 0.95 } func newHealthBar(buildings []*building) *healthBar { return &healthBar{ life: buildingsHealth * len(buildings), } } func (b *building) causeDamage(damage int) { if b.animate != nil || b.life <= 0 { return } b.animate = shakeBuilding(b) b.life -= damage // fmt.Println("damage:", damage, "life:", b.life) } func newBuilding(x, z float32) *building { color := rl.NewColor( uint8(rl.GetRandomValue(0, 180)), uint8(rl.GetRandomValue(0, 180)), uint8(rl.GetRandomValue(0, 180)), 255, ) pos := rl.NewVector3(x * buildingSpacing, 0, z * buildingSpacing) size := rl.NewVector3( buildingSize, float32(rl.GetRandomValue(1, maxBuildingHeight)), buildingSize, ) min := rl.NewVector3( pos.X - buildingSize/2, 0, pos.Z - buildingSize/2, ) max := rl.NewVector3( pos.X + buildingSize/2, size.Y/2, pos.Z + buildingSize/2, ) return &building{ color: color, life: buildingsHealth, pos: pos, size: size, boundingBox: rl.NewBoundingBox(min, max), } } func drawWireframe(building building) { increasedSize := rl.Vector3Scale(building.size, 1.05) invertedColor := building.color invertedColor.R += 127 invertedColor.G += 127 invertedColor.B += 127 rl.DrawCubeWiresV( building.pos, increasedSize, invertedColor, ) } func shakeBuilding(b *building) animationFunc { var frame remainingFrames = 60 * 0.7 return func() remainingFrames { building := *b if frame % 4 == 0 { if b.life == 0 { building.color = rl.NewColor(255, 0, 0, 255) } else { building.color = rl.RayWhite } } drawWireframe(building) shake := rl.NewVector3( rand.Float32() * .2 - .1, rand.Float32() * .1 - .05, rand.Float32() * .2 - .1, ) // shake = rl.Vector3Scale(shake, float32(math.Sin(rl.GetTime()) + 1)/3) rl.DrawCubeV( rl.Vector3Add(building.pos, shake), building.size, building.color, ) frame -= 1 return frame } } func draw_buildings() { for _, building := range city.buildings { hasAnimation := building.animate != nil if building.life <= 0 && !hasAnimation { continue } if hasAnimation { remainingFrames := building.animate() if remainingFrames == 0 { building.animate = nil } continue } drawWireframe(*building) rl.DrawCubeV(building.pos, building.size, building.color) } } func (c *character) laser_beam() { if !rl.IsMouseButtonDown(rl.MouseLeftButton) { return } ray := rl.Ray{ Position: c.camera.Position, Direction: rl.Vector3Subtract(c.camera.Target, c.camera.Position), } closestDistance := math.Inf(1) var closest *building for _, building := range city.buildings { if building.life == 0 { continue } playerDistance := rl.Vector3Distance(c.camera.Position, building.pos) if playerDistance < float32(closestDistance) { collision := rl.GetRayCollisionBox(ray, building.boundingBox) if collision.Hit { closestDistance = float64(playerDistance) closest = building } } } if closest != nil { closest.causeDamage(c.damage) } } func newBuildings() []*building { var buildings []*building for i := 0; i < 32; i++ { for j := 0; j < 32; j++ { buildings = append(buildings, newBuilding(float32(i), float32(j))) } } return buildings } func main() { fmt.Println("hello world") rl.SetConfigFlags(rl.FlagMsaa4xHint) rl.InitWindow(WIDTH, HEIGHT, "raylib [core] example - 3d camera first person") rl.SetTargetFPS(60) rl.HideCursor() rl.DisableCursor() rl.SetMousePosition(WIDTH/2, HEIGHT/2) character := newCharacter() buildings := newBuildings() healthBar := newHealthBar(buildings) city = &scene{ buildings: buildings, healthBar: healthBar, character: character, } // direction := rl.Vector3Subtract(character.camera.Target, character.camera.Position) // aim := direction for !rl.WindowShouldClose() { rl.UpdateCamera(&character.camera, rl.CameraFirstPerson) rl.BeginDrawing() rl.ClearBackground(rl.LightGray) rl.BeginMode3D(character.camera) { character.update() draw_plane() update_projectiles() draw_buildings() update_emitters() // direction = rl.Vector3Subtract( // character.camera.Target, character.camera.Position) // aim = rl.Vector3Lerp(direction, aim, 0.95) // rl.DrawSphere(rl.Vector3Add(character.camera.Position, aim), 0.1, rl.Red) } rl.EndMode3D() rl.DrawLineV( rl.NewVector2(WIDTH/2-5, HEIGHT/2-5), rl.NewVector2(WIDTH/2+5, HEIGHT/2+5), rl.Red, ) rl.DrawLineV( rl.NewVector2(WIDTH/2-5, HEIGHT/2+5), rl.NewVector2(WIDTH/2+5, HEIGHT/2-5), rl.Red, ) rl.DrawFPS(0, HEIGHT-30) healthBar.update() rl.EndDrawing() } rl.CloseWindow() }