Skip to content

Instantly share code, notes, and snippets.

@dgryski
Forked from magiconair/naivegame-naive.go
Last active September 18, 2016 07:41
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dgryski/61763e2ee58d3446b25bbe00b44f974e to your computer and use it in GitHub Desktop.
Save dgryski/61763e2ee58d3446b25bbe00b44f974e to your computer and use it in GitHub Desktop.
Port of the FastGame Java impl
// This is a port of the FAST Java version of
// https://jackmott.github.io/programming/2016/09/01/performance-in-the-large.html
// Original Go translation of naive version from https://gist.github.com/magiconair/68a524fc847ba2860893a799f637f532
//
// Code is not pretty!!! ;)
//
//
package main
import (
"fmt"
"math"
"strconv"
"time"
)
type Vector struct {
x, y, z float64
}
func Mult(a, b Vector) Vector {
return Vector{a.x * b.x, a.y * b.y, a.z * b.z}
}
func Add(a, b Vector) Vector {
return Vector{a.x + b.x, a.y + b.y, a.z + b.z}
}
func Sub(a, b Vector) Vector {
return Vector{a.x - b.x, a.y - b.y, a.z - b.z}
}
func Dist(a, b Vector) float64 {
v := Sub(a, b)
return math.Sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
}
type Block struct {
location Vector // x,y,z within the chunk
name string
durability int
textureid int
breakable bool
visible bool
typ int
}
type Type int
const (
Zombie Type = iota
Chicken
Exploder
TallCreepyThing
)
type Entity struct {
location Vector
name string
health int
speed Vector
typ Type
}
func NewEntity(location Vector, typ Type) *Entity {
e := &Entity{
location: location,
typ: typ,
}
switch typ {
case Zombie:
e.name = "Zombie"
e.health = 50
e.speed = Vector{0.5, 0.0, 0.5} //slow, can't fly
case Chicken:
e.name = "Chicken"
e.health = 25
e.speed = Vector{0.75, 0.25, 0.75} //can fly a bit
case Exploder:
e.name = "Exploder"
e.health = 75
e.speed = Vector{0.75, 0.0, 0.75}
case TallCreepyThing:
e.name = "Tall Creepy Thing"
e.health = 500
e.speed = Vector{1.0, 1.0, 1.0} //does what he wants
}
return e
}
func (e *Entity) updatePosition() {
// Complex movement AI
rndUnitIshVector := Vector{1, 1, 1}
movementVector := Mult(rndUnitIshVector, e.speed)
e.location = Add(movementVector, e.location)
}
const NUM_BLOCKS = 65536 //just like minecraft!
const NUM_ENTITIES = 1000
type blockID byte
type Chunk struct {
blocks []blockID
entities []*Entity
location Vector // x,y,z within world
}
func NewChunk(location Vector) *Chunk {
c := &Chunk{location: location}
// Preallocate the growable List because we are clever!
c.blocks = make([]blockID, NUM_BLOCKS)
for i := range c.blocks {
c.blocks[i] = blockID(i)
}
c.entities = make([]*Entity, 0, NUM_ENTITIES)
for i := 0; i < NUM_ENTITIES/4; i++ {
// Fancy proc gen initial position equation
f := float64(i)
c.entities = append(c.entities, NewEntity(Vector{f, f, f}, Chicken))
c.entities = append(c.entities, NewEntity(Vector{f + 2, f, f}, Zombie))
c.entities = append(c.entities, NewEntity(Vector{f + 3, f, f}, Exploder))
c.entities = append(c.entities, NewEntity(Vector{f + 4, f, f}, TallCreepyThing))
}
return c
}
func (c *Chunk) processEntities() {
for _, e := range c.entities {
e.updatePosition()
}
}
const CHUNK_COUNT = 100
type Game struct {
chunks []*Chunk
playerLocation Vector
chunkCounter int
blocks []Block
}
func (g *Game) loadWorld() {
g.chunks = make([]*Chunk, CHUNK_COUNT)
for i := range g.chunks {
g.chunks[i] = NewChunk(Vector{float64(g.chunkCounter), 0, 0})
g.chunkCounter++
}
g.blocks = make([]Block, NUM_BLOCKS)
for i := range g.blocks {
g.blocks[i] = Block{
location: Vector{float64(i), float64(i), float64(i)},
name: "Block:" + strconv.Itoa(i),
durability: 100,
textureid: 1,
breakable: true,
visible: true,
typ: 1,
}
}
}
func (g *Game) updateChunks() {
var toRemove []int
for i, c := range g.chunks {
c.processEntities()
chunkDistance := Dist(c.location, g.playerLocation)
if chunkDistance > CHUNK_COUNT {
toRemove = append(toRemove, i)
}
}
for _, v := range toRemove {
g.chunks[v] = NewChunk(Vector{float64(g.chunkCounter), 0, 0})
g.chunkCounter++
}
}
func main() {
game := &Game{}
fmt.Println("Loading World...")
start := time.Now()
game.loadWorld()
loadWorldTime := time.Since(start)
fmt.Println("FINISHED!")
fmt.Printf("Load Time: %s\n", loadWorldTime)
// Game Loop, you can never leave
var frames int
for {
// check for dead entities
start = time.Now()
// mocking polling of the VR controller
playerMovement := Vector{0.1, 0, 0}
game.playerLocation = Add(game.playerLocation, playerMovement)
game.updateChunks()
t := time.Since(start)
fmt.Println(frames, t)
// Lock it at 60FPS
if t < 16*time.Millisecond {
time.Sleep(16*time.Millisecond - t)
}
frames++
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment