Add goroutines to map renderer
This commit is contained in:
parent
696b827a7d
commit
842c41fdca
123
main.go
123
main.go
@ -31,13 +31,14 @@ const (
|
||||
ReloadCooldown = 16
|
||||
|
||||
Debug = false
|
||||
MapWindow = true
|
||||
MapWindow = false
|
||||
RenderGameObjects = true
|
||||
ProfilerOn = true
|
||||
ProfilerInterval = 100
|
||||
|
||||
JoyStickDeadZone = 8000
|
||||
|
||||
DoAllKeymapsPlayers = true
|
||||
DoAllKeymapsPlayers = false
|
||||
DoJoyStickPlayers = true
|
||||
DoKeymapPlayer = true
|
||||
)
|
||||
@ -71,45 +72,85 @@ func main() {
|
||||
mapWindow, mapSurface = setupMapWindowAndSurface()
|
||||
}
|
||||
running := true
|
||||
var totalRenderTime, totalScalingTime, totalFrameTime, frameCount uint64
|
||||
|
||||
// Delta time management
|
||||
var prevTime = sdl.GetTicks64()
|
||||
const maxDeltaTime uint64 = 1000 / 60 // max 60 FPS, ~16ms per frame
|
||||
|
||||
for running {
|
||||
frameStart := sdl.GetTicks64()
|
||||
currentTime := sdl.GetTicks64()
|
||||
deltaTime := currentTime - prevTime
|
||||
prevTime = currentTime
|
||||
|
||||
// Catch up in case of a large delta time
|
||||
for deltaTime > maxDeltaTime {
|
||||
deltaTime -= maxDeltaTime
|
||||
|
||||
// Run multiple logic ticks if deltaTime is too large
|
||||
runGameLogic(players, gameMap, bases, bullets, bulletParticles)
|
||||
}
|
||||
|
||||
for playerIndex, player := range *players {
|
||||
// Delay to achieve roughly 60 FPS
|
||||
if player.local {
|
||||
running = doPlayerFrame(uint8(playerIndex), player, players, gameMap, bases, bullets, bulletParticles)
|
||||
running := doPlayerFrame(uint8(playerIndex), player, players, gameMap, bases, bullets, bulletParticles)
|
||||
if !running {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run logic for the remaining delta time
|
||||
runGameLogic(players, gameMap, bases, bullets, bulletParticles)
|
||||
|
||||
// Render logic
|
||||
if MapWindow {
|
||||
gameMap.render(mapRendererRect, mapSurface)
|
||||
for _, bullet := range *bullets {
|
||||
(*bullet).render(mapRendererRect, mapSurface)
|
||||
}
|
||||
for _, base := range *bases {
|
||||
(*base).render(mapRendererRect, mapSurface)
|
||||
}
|
||||
for _, playerLoop := range *players {
|
||||
(*playerLoop).render(mapRendererRect, mapSurface)
|
||||
}
|
||||
for _, bulletParticle := range *bulletParticles {
|
||||
bulletParticle.render(mapRendererRect, mapSurface)
|
||||
}
|
||||
adjustWindow(mapWindow, mapSurface)
|
||||
}
|
||||
//now tick world
|
||||
for _, bullet := range *bullets {
|
||||
(*bullet).tick(gameMap, bulletParticles, bullets, players)
|
||||
}
|
||||
for _, base := range *bases {
|
||||
(*base).tick(players)
|
||||
}
|
||||
for _, bulletParticle := range *bulletParticles {
|
||||
bulletParticle.tick(bulletParticles)
|
||||
// Profile rendering (aggregate all renderings)
|
||||
totalRenderTime += profileSection(func() {
|
||||
gameMap.render(mapRendererRect, mapSurface)
|
||||
for _, bullet := range *bullets {
|
||||
(*bullet).render(mapRendererRect, mapSurface)
|
||||
}
|
||||
for _, base := range *bases {
|
||||
(*base).render(mapRendererRect, mapSurface)
|
||||
}
|
||||
for _, playerLoop := range *players {
|
||||
(*playerLoop).render(mapRendererRect, mapSurface)
|
||||
}
|
||||
for _, bulletParticle := range *bulletParticles {
|
||||
bulletParticle.render(mapRendererRect, mapSurface)
|
||||
}
|
||||
})
|
||||
totalScalingTime += profileSection(func() {
|
||||
adjustWindow(mapWindow, mapSurface)
|
||||
})
|
||||
}
|
||||
|
||||
enforceFrameRate(frameStart, 60)
|
||||
frameEnd := sdl.GetTicks64()
|
||||
totalFrameTime += frameEnd - currentTime
|
||||
frameCount++
|
||||
|
||||
// Log profiling information every 1000 frames
|
||||
if frameCount%ProfilerInterval == 0 && ProfilerOn {
|
||||
logMapProfilingInfo(totalRenderTime, totalScalingTime, totalFrameTime, frameCount)
|
||||
resetMapProfilingCounters(&totalRenderTime, &totalScalingTime, &totalFrameTime, &frameCount)
|
||||
}
|
||||
|
||||
enforceFrameRate(currentTime, 60)
|
||||
}
|
||||
}
|
||||
|
||||
// Separate function to handle game logic
|
||||
func runGameLogic(players *[]*Player, gameMap *GameMap, bases *[]*Base, bullets *[]*Bullet, bulletParticles *[]*BulletParticle) {
|
||||
// Tick world
|
||||
for _, bullet := range *bullets {
|
||||
(*bullet).tick(gameMap, bulletParticles, bullets, players)
|
||||
}
|
||||
for _, base := range *bases {
|
||||
(*base).tick(players)
|
||||
}
|
||||
for _, bulletParticle := range *bulletParticles {
|
||||
bulletParticle.tick(bulletParticles)
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,22 +171,20 @@ func initPlayer(playerIndex uint8, player *Player) {
|
||||
func doPlayerFrame(playerIndex uint8, player *Player, players *[]*Player, gameMap *GameMap, bases *[]*Base, bullets *[]*Bullet, bulletParticles *[]*BulletParticle) bool {
|
||||
running := true
|
||||
var (
|
||||
totalHandleEventsTime, totalRenderTime, totalFrameTime uint64
|
||||
totalScalingTime, frameCount uint64
|
||||
isShooting, shouldContinue bool
|
||||
isShooting, shouldContinue bool
|
||||
)
|
||||
frameStart := sdl.GetTicks64()
|
||||
|
||||
// Profile handleEvents
|
||||
totalHandleEventsTime += profileSection(func() {
|
||||
player.totalHandleEventsTime += profileSection(func() {
|
||||
running = handleEvents(player.window, player.logicalSurface)
|
||||
keyboard := sdl.GetKeyboardState()
|
||||
shouldContinue, isShooting = handleInput(keyboard, bullets, player, gameMap, player.playSurface.Format, players)
|
||||
shouldContinue, isShooting = handleInput(keyboard, bullets, player, gameMap, players)
|
||||
running = running && shouldContinue
|
||||
})
|
||||
|
||||
// Profile rendering (aggregate all renderings)
|
||||
totalRenderTime += profileSection(func() {
|
||||
player.totalRenderTime += profileSection(func() {
|
||||
player.track(player.camera)
|
||||
gameMap.render(player.camera, player.playSurface)
|
||||
|
||||
@ -169,18 +208,18 @@ func doPlayerFrame(playerIndex uint8, player *Player, players *[]*Player, gameMa
|
||||
})
|
||||
|
||||
// Profile window adjustments
|
||||
totalScalingTime += profileSection(func() {
|
||||
player.totalScalingTime += profileSection(func() {
|
||||
adjustWindow(player.window, player.logicalSurface)
|
||||
})
|
||||
|
||||
frameEnd := sdl.GetTicks64()
|
||||
totalFrameTime += frameEnd - frameStart
|
||||
frameCount++
|
||||
player.totalFrameTime += frameEnd - frameStart
|
||||
player.frameCount++
|
||||
|
||||
// Log profiling information every 1000 frames
|
||||
if frameCount%1000 == 0 && ProfilerOn && playerIndex == 0 {
|
||||
logProfilingInfo(totalHandleEventsTime, totalRenderTime, totalScalingTime, totalFrameTime, frameCount)
|
||||
resetProfilingCounters(&totalHandleEventsTime, &totalRenderTime, &totalScalingTime, &totalFrameTime, &frameCount)
|
||||
if player.frameCount%ProfilerInterval == 0 && ProfilerOn && playerIndex == 0 {
|
||||
logProfilingInfo(player.totalHandleEventsTime, player.totalRenderTime, player.totalScalingTime, player.totalFrameTime, player.frameCount)
|
||||
resetProfilingCounters(&player.totalHandleEventsTime, &player.totalRenderTime, &player.totalScalingTime, &player.totalFrameTime, &player.frameCount)
|
||||
}
|
||||
return running
|
||||
}
|
||||
|
76
map.go
76
map.go
@ -4,6 +4,7 @@ import (
|
||||
"github.com/aquilax/go-perlin"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"image/color"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -48,26 +49,49 @@ func (gameMap *GameMap) getTileColor(x int32, y int32) color.Color {
|
||||
}
|
||||
|
||||
func (gameMap *GameMap) createGameMap(width int32, height int32) {
|
||||
// Initialize the 2D slice for game tiles
|
||||
gameMap.tiles = make([][]uint8, width)
|
||||
|
||||
// Create Perlin noise generators
|
||||
perlinNoiseRock := perlin.NewPerlin(2, 4, 8, time.Now().Unix())
|
||||
perlinNoiseSoil := perlin.NewPerlin(2, 8, 4, time.Now().Unix())
|
||||
|
||||
// WaitGroup to synchronize goroutines
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Loop over the rows
|
||||
for i := range gameMap.tiles {
|
||||
gameMap.tiles[i] = make([]uint8, height)
|
||||
for j := range gameMap.tiles[i] {
|
||||
perlinRock := perlinNoiseRock.Noise2D(float64(i)/float64(width)*6, float64(j)/float64(height)*6)
|
||||
if perlinRock > 0.2 {
|
||||
gameMap.tiles[i][j] = 1
|
||||
} else {
|
||||
perlinSoil := perlinNoiseSoil.Noise2D(float64(i)/float64(width)*10, float64(j)/float64(height)*10)
|
||||
if perlinSoil >= 0 {
|
||||
gameMap.tiles[i][j] = 2
|
||||
// Increment WaitGroup counter for each goroutine
|
||||
wg.Add(1)
|
||||
|
||||
// Launch a goroutine for each row
|
||||
go func(i int) {
|
||||
defer wg.Done() // Signal that this goroutine is done
|
||||
|
||||
// Initialize the row
|
||||
gameMap.tiles[i] = make([]uint8, height)
|
||||
|
||||
// Process each tile in the row
|
||||
for j := range gameMap.tiles[i] {
|
||||
perlinRock := perlinNoiseRock.Noise2D(float64(i)/float64(width)*6, float64(j)/float64(height)*6)
|
||||
if perlinRock > 0.2 {
|
||||
gameMap.tiles[i][j] = 1
|
||||
} else {
|
||||
gameMap.tiles[i][j] = 3
|
||||
perlinSoil := perlinNoiseSoil.Noise2D(float64(i)/float64(width)*10, float64(j)/float64(height)*10)
|
||||
if perlinSoil >= 0 {
|
||||
gameMap.tiles[i][j] = 2
|
||||
} else {
|
||||
gameMap.tiles[i][j] = 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Wait for all goroutines to finish
|
||||
wg.Wait()
|
||||
|
||||
// Set the dimensions of the game map
|
||||
gameMap.width = width
|
||||
gameMap.height = height
|
||||
}
|
||||
@ -77,17 +101,31 @@ func (gameMap *GameMap) render(camera *sdl.Rect, surface *sdl.Surface) {
|
||||
fromY := camera.Y
|
||||
toX := camera.X + camera.W
|
||||
toY := camera.Y + camera.H
|
||||
|
||||
// Create a WaitGroup to wait for all goroutines to finish
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Process columns instead of individual pixels
|
||||
for x := fromX; x < toX; x++ {
|
||||
for y := fromY; y < toY; y++ {
|
||||
cameraY := y - camera.Y
|
||||
cameraX := x - camera.X
|
||||
pixelColor := gameMap.getTileColor(x, y)
|
||||
surface.Set(int(cameraX), int(cameraY), pixelColor)
|
||||
// Increment the WaitGroup counter
|
||||
wg.Add(1)
|
||||
|
||||
}
|
||||
// Launch a goroutine for each column
|
||||
go func(x int32) {
|
||||
defer wg.Done() // Decrement the counter when the goroutine completes
|
||||
|
||||
for y := fromY; y < toY; y++ {
|
||||
cameraY := y - camera.Y
|
||||
cameraX := x - camera.X
|
||||
pixelColor := gameMap.getTileColor(x, y)
|
||||
surface.Set(int(cameraX), int(cameraY), pixelColor)
|
||||
}
|
||||
}(x)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all goroutines to finish
|
||||
wg.Wait()
|
||||
}
|
||||
func (gameMap *GameMap) checkCollision(posX, posY int32) uint8 {
|
||||
//0 no collision
|
||||
//1 destructible collision
|
||||
|
@ -35,6 +35,12 @@ type Player struct {
|
||||
|
||||
playSurfaceTargetRect *sdl.Rect
|
||||
HUDSurfaceTargetRect *sdl.Rect
|
||||
|
||||
totalHandleEventsTime uint64
|
||||
totalRenderTime uint64
|
||||
totalFrameTime uint64
|
||||
totalScalingTime uint64
|
||||
frameCount uint64
|
||||
}
|
||||
|
||||
func (player *Player) track(camera *sdl.Rect) {
|
||||
|
@ -285,7 +285,7 @@ func initPlayerColors() {
|
||||
}
|
||||
}
|
||||
|
||||
func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *GameMap, format *sdl.PixelFormat, players *[]*Player) (bool, bool) {
|
||||
func handleInput(keyboard []uint8, bullets *[]*Bullet, player *Player, gameMap *GameMap, players *[]*Player) (bool, bool) {
|
||||
if !player.local || player.shields <= 0 || player.shields > MaxShields {
|
||||
return true, false
|
||||
}
|
||||
|
15
profiler.go
15
profiler.go
@ -21,6 +21,14 @@ func logProfilingInfo(handleEventsTime, renderTime, scaleTime, frameTime, frameC
|
||||
fmt.Print("\n")
|
||||
}
|
||||
|
||||
func logMapProfilingInfo(renderTime, scaleTime, frameTime, frameCount uint64) {
|
||||
floatFrame := float64(frameCount)
|
||||
fmt.Printf("Average map render time: %f ms\n", float64(renderTime)/floatFrame)
|
||||
fmt.Printf("Average map scaling time: %f ms\n", float64(scaleTime)/floatFrame)
|
||||
fmt.Printf("Average full render time: %f ms\n", float64(frameTime)/floatFrame)
|
||||
fmt.Print("\n")
|
||||
}
|
||||
|
||||
func resetProfilingCounters(handleEventsTime, renderTime, scaleTime, frameTime *uint64, frameCount *uint64) {
|
||||
*handleEventsTime = 0
|
||||
*renderTime = 0
|
||||
@ -29,6 +37,13 @@ func resetProfilingCounters(handleEventsTime, renderTime, scaleTime, frameTime *
|
||||
*frameCount = 0
|
||||
}
|
||||
|
||||
func resetMapProfilingCounters(renderTime, scaleTime, frameTime *uint64, frameCount *uint64) {
|
||||
*renderTime = 0
|
||||
*scaleTime = 0
|
||||
*frameTime = 0
|
||||
*frameCount = 0
|
||||
}
|
||||
|
||||
func enforceFrameRate(frameStart uint64, targetFPS int) {
|
||||
frameDuration := 1000 / targetFPS
|
||||
elapsed := sdl.GetTicks64() - frameStart
|
||||
|
Loading…
Reference in New Issue
Block a user