361 lines
8.2 KiB
Go
361 lines
8.2 KiB
Go
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
|
|
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
|
|
}
|
|
|
|
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(buildings []*building) {
|
|
const MARGIN = 20
|
|
const BARWIDTH = (WIDTH - MARGIN*2)
|
|
|
|
currentLife := 0
|
|
maxLife := buildingsHealth * len(buildings)
|
|
for _, b := range 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: 3 * len(buildings),
|
|
}
|
|
}
|
|
|
|
func (b *building) causeDamage(damage int) {
|
|
if b.animate != nil {
|
|
return
|
|
}
|
|
b.animate = shakeBuilding(*b)
|
|
b.life -= damage
|
|
fmt.Println("damage:", 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, 7)),
|
|
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 {
|
|
building.color = rl.RayWhite
|
|
}
|
|
|
|
drawWireframe(b)
|
|
|
|
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(buildings []*building) {
|
|
for _, building := range buildings {
|
|
|
|
hasAnimation := building.animate != nil
|
|
|
|
if hasAnimation {
|
|
|
|
remainingFrames := building.animate()
|
|
|
|
if remainingFrames == 0 {
|
|
building.animate = nil
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
if building.life == 0 {
|
|
continue
|
|
}
|
|
|
|
drawWireframe(*building)
|
|
rl.DrawCubeV(building.pos, building.size, building.color)
|
|
}
|
|
}
|
|
|
|
func damage_building(camera rl.Camera, buildings []*building) {
|
|
if !rl.IsMouseButtonDown(rl.MouseLeftButton) {
|
|
return
|
|
}
|
|
ray := rl.Ray{
|
|
Position: camera.Position,
|
|
Direction: rl.Vector3Subtract(camera.Target, camera.Position),
|
|
}
|
|
|
|
closestDistance := math.Inf(1)
|
|
var closest *building
|
|
|
|
for _, building := range buildings {
|
|
|
|
if building.life == 0 {
|
|
continue
|
|
}
|
|
|
|
playerDistance := rl.Vector3Distance(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(3)
|
|
}
|
|
}
|
|
|
|
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)
|
|
|
|
var enemies []*enemy
|
|
|
|
for i := 0; i < 10; i++ {
|
|
pos := rl.NewVector3(
|
|
float32(rl.GetRandomValue(-10, 10)),
|
|
2,
|
|
float32(rl.GetRandomValue(-10, 10)),
|
|
)
|
|
enemies = append(enemies, &enemy{ pos: pos })
|
|
}
|
|
|
|
camera := rl.Camera3D{}
|
|
camera.Position = rl.NewVector3(4.0, 2.0, 4.0)
|
|
camera.Target = rl.NewVector3(0.0, 1.8, 0.0)
|
|
camera.Up = rl.NewVector3(0.0, 1.0, 0.0)
|
|
camera.Fovy = 80.0
|
|
camera.Projection = rl.CameraPerspective
|
|
|
|
var buildings []*building
|
|
|
|
for i := 0; i < 32; i++ {
|
|
for j := 0; j < 32; j++ {
|
|
buildings = append(buildings, newBuilding(float32(i), float32(j)))
|
|
}
|
|
}
|
|
|
|
healthBar := newHealthBar(buildings)
|
|
|
|
for !rl.WindowShouldClose() {
|
|
|
|
// shake := rl.NewVector3(
|
|
// rand.Float32() * .4 - .2,
|
|
// rand.Float32() * .4 - .2,
|
|
// rand.Float32() * .4 - .2,
|
|
// )
|
|
// shake = rl.Vector3Scale(shake, float32(math.Sin(rl.GetTime()) + 1)/3)
|
|
// camera.Target = rl.Vector3Add(camera.Target, shake)
|
|
|
|
rl.UpdateCamera(&camera, rl.CameraFirstPerson)
|
|
rl.BeginDrawing()
|
|
rl.ClearBackground(rl.LightGray)
|
|
|
|
rl.BeginMode3D(camera)
|
|
{
|
|
|
|
damage_building(camera, buildings)
|
|
|
|
draw_plane()
|
|
|
|
draw_buildings(buildings)
|
|
|
|
|
|
}
|
|
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(buildings)
|
|
|
|
rl.EndDrawing()
|
|
}
|
|
|
|
rl.CloseWindow()
|
|
}
|
|
|
|
|
|
|
|
// for _, e := range enemies {
|
|
//
|
|
// if dist := rl.Vector3Distance(camera.Position, e.pos); dist > 8 {
|
|
// rl.DrawSphere(e.pos, 1, rl.Blue)
|
|
// e.speed = 0
|
|
// continue
|
|
// }
|
|
// rl.DrawSphere(e.pos, 1, rl.Red)
|
|
//
|
|
// direction := rl.Vector3Normalize(rl.Vector3Subtract(camera.Position, e.pos))
|
|
// e.speed += enemyAcc
|
|
// e.speed = float32(math.Mod(float64(e.speed), maxSpeed))
|
|
// direction.Y = 0
|
|
// direction = rl.Vector3Scale(direction, e.speed)
|
|
// e.pos = rl.Vector3Add(e.pos, direction)
|
|
// }
|
|
// fmt.Println(
|
|
// rl.Vector3Subtract(camera.Target, camera.Position),
|
|
// )
|
|
|
|
// ray = rl.GetMouseRay(rl.GetMousePosition(), camera)
|
|
|
|
// rl.GetRayCollisionBox(ray
|
|
// )
|
|
|
|
|
|
|