Last active
September 13, 2016 16:31
-
-
Save vbfox/eb5079ecc6b46fc9713439197a6bf8fd to your computer and use it in GitHub Desktop.
Naive F# Game
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System | |
open System.Diagnostics | |
open System.Threading | |
[<Struct>] | |
type Vector = | |
val X: float32 | |
val Y: float32 | |
val Z: float32 | |
new(x,y,z) = { X=x; Y=y; Z=z } | |
let inline (^*) (a: Vector) (b:Vector) = new Vector(a.X * b.X, a.Y * b.Y, a.Z * b.Z) | |
let inline (^+) (a: Vector) (b:Vector) = new Vector(a.X + b.X, a.Y + b.Y, a.Z + b.Z) | |
let inline (^-) (a: Vector) (b:Vector) = new Vector(a.X - b.X, a.Y - b.Y, a.Z - b.Z) | |
let inline mkVector x y z = new Vector(x, y, z) | |
let inline getDistance (a: Vector) (b:Vector) = | |
let s = a ^- b | |
float32(Math.Sqrt(float(s.X * s.X + s.Y * s.Y + s.Z * s.Z))) | |
[<Struct>] | |
type Block = | |
val mutable Location: Vector //x,y,z within the chunk | |
val mutable Name: string | |
val mutable Durability: int | |
val mutable Textureid: int | |
val mutable Breakable: bool | |
val mutable Visible: bool | |
val mutable Type: int | |
type EntityType = |Zombie |Chicken |Exploder |TallCreepyThing | |
[<Struct>] | |
type Entity = | |
val mutable Location: Vector //x,y,z within the chunk | |
val mutable Name: string | |
val mutable Health: int | |
val mutable Speed: Vector | |
new(location, name, health, speed) = {Location = location; Name = name; Health = health; Speed = speed } | |
let mkEntity location type' = | |
match type' with | |
| Zombie -> new Entity(location, "Zombie", 50, mkVector 0.5f 0.0f 0.5f) | |
| Chicken -> new Entity(location, "Chicken", 25, mkVector 0.75f 0.25f 0.75f) | |
| Exploder -> new Entity(location, "Exploder", 75, mkVector 0.75f 0.0f 0.75f) | |
| TallCreepyThing -> new Entity(location, "Tall Creepy Thing", 500, mkVector 1.0f 1.0f 1.0f) | |
let updateEntityPosition (entity: Entity byref) = | |
// Complex movement AI | |
let rndUnitIshVector = mkVector 1.0f 1.0f 1.0f | |
let movementVector = rndUnitIshVector ^* entity.Speed | |
entity.Location <- movementVector ^+ entity.Location | |
[<Literal>] | |
let NUM_BLOCKS = 65536; //just like minecraft! | |
[<Literal>] | |
let NUM_ENTITIES = 1000 | |
type Chunk = { | |
mutable Blocks: Block list | |
mutable Entities: Entity array | |
mutable Location: Vector //x,y,z within world | |
} | |
let mkChunk location = | |
let blocks = [ | |
for i = 0 to NUM_BLOCKS-1 do | |
let ifloat = float32 i | |
let mutable block = new Block() | |
block.Location <- mkVector ifloat ifloat ifloat | |
block.Name <- "Block:" + (i.ToString ()) | |
block.Durability <- 100 | |
block.Textureid <- 1 | |
block.Breakable <- true | |
block.Visible <- true | |
block.Type <- 1 | |
yield block | |
] | |
let entities = [| | |
for i = 0 to NUM_ENTITIES/4-1 do | |
let ifloat = float32 i | |
yield mkEntity (mkVector ifloat ifloat ifloat) Chicken | |
yield mkEntity (mkVector (ifloat+2.0f) ifloat ifloat) Zombie | |
yield mkEntity (mkVector (ifloat+3.0f) ifloat ifloat) Exploder | |
yield mkEntity (mkVector (ifloat+4.0f) ifloat ifloat) TallCreepyThing | |
|] | |
{ Location = location; Blocks = blocks; Entities = entities } | |
let inline processEntities chunk = | |
let arr = chunk.Entities | |
for i = 0 to arr.Length-1 do | |
updateEntityPosition &(arr.[i]) | |
[<Literal>] | |
let CHUNK_COUNT = 100; | |
type Game = { | |
Chunks: Chunk array | |
mutable PlayerLocation: Vector | |
mutable ChunkCounter: int | |
} | |
let mkGame () = { Chunks = Array.empty; PlayerLocation = mkVector 0.0f 0.0f 0.0f; ChunkCounter = 0 } | |
let loadWorld game = | |
let chunks = Array.create<Chunk> CHUNK_COUNT { Location = mkVector 0.0f 0.0f 0.0f; Blocks = List.empty; Entities = Array.empty } | |
for i in [0..CHUNK_COUNT-1] do | |
let chunkCounter = game.ChunkCounter + i | |
chunks.[i] <- mkChunk (mkVector (float32(chunkCounter)) 0.0f 0.0f) | |
{ game with Chunks = chunks; ChunkCounter = game.ChunkCounter + CHUNK_COUNT } | |
let updateChunks game = | |
let mutable chunkCounter = game.ChunkCounter | |
let chunks = game.Chunks | |
for i = 0 to chunks.Length - 1 do | |
let chunk = chunks.[i] | |
processEntities chunk | |
let chunkDistance = getDistance chunk.Location game.PlayerLocation | |
if chunkDistance > float32(CHUNK_COUNT) then | |
chunks.[i] <- mkChunk (mkVector (float32(chunkCounter)) 0.0f 0.0f) | |
chunkCounter <- chunkCounter + 1 | |
game.ChunkCounter <- chunkCounter | |
[<EntryPoint>] | |
let main _ = | |
// Praise be to the RNG | |
let s = new Stopwatch(); | |
let mutable game = mkGame () | |
Console.Write("Loading World..."); | |
s.Start(); | |
game <- loadWorld game | |
s.Stop(); | |
Console.WriteLine("FINISHED!"); | |
Console.WriteLine("Load Time:{0}", s.ElapsedMilliseconds); | |
//Game Loop, you can never leave | |
while true do | |
//check for dead entities | |
s.Restart(); | |
// mocking polling of the VR controller | |
let playerMovement = mkVector 0.1f 0.0f 0.0f | |
game.PlayerLocation <- game.PlayerLocation ^+ playerMovement | |
updateChunks game | |
s.Stop(); | |
let time = ((float)s.ElapsedTicks / (float)Stopwatch.Frequency) * 1000.0; | |
Console.WriteLine(time); | |
//Lock it at 60FPS | |
if time < 16.0 then | |
Thread.Sleep((int)(16.0 - time)); | |
0 // return an integer exit code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System | |
open System.Diagnostics | |
open System.Threading | |
type Vector = { | |
X: float32 | |
Y: float32 | |
Z: float32 | |
} | |
let mkVector (x: float32) (y: float32) (z: float32) = { X = x; Y = y; Z = z} | |
let (^*) (a: Vector) (b:Vector) = { X = a.X * b.X; Y = a.Y * b.Y; Z = a.Z * b.Z } | |
let (^+) (a: Vector) (b:Vector) = { X = a.X + b.X; Y = a.Y + b.Y; Z = a.Z + b.Z } | |
let (^-) (a: Vector) (b:Vector) = { X = a.X - b.X; Y = a.Y - b.Y; Z = a.Z - b.Z } | |
let getDistance (a: Vector) (b:Vector) = | |
let s = a ^- b | |
float32(Math.Sqrt(float(s.X * s.X + s.Y * s.Y + s.Z * s.Z))) | |
type Block = { | |
Location: Vector //x,y,z within the chunk | |
Name: string | |
Durability: int | |
Textureid: int | |
Breakable: bool | |
Visible: bool | |
Type: int | |
} | |
type EntityType = |Zombie |Chicken |Exploder |TallCreepyThing | |
type Entity = { | |
Location: Vector //x,y,z within the chunk | |
Name: string | |
Health: int | |
Speed: Vector; | |
} | |
let mkEntity location type' = | |
match type' with | |
| Zombie -> { Location = location; Name = "Zombie"; Health = 50; Speed = mkVector 0.5f 0.0f 0.5f } | |
| Chicken -> { Location = location; Name = "Chicken"; Health = 25; Speed = mkVector 0.75f 0.25f 0.75f } | |
| Exploder -> { Location = location; Name = "Exploder"; Health = 75; Speed = mkVector 0.75f 0.0f 0.75f } | |
| TallCreepyThing -> { Location = location; Name = "Tall Creepy Thing"; Health = 500; Speed = mkVector 1.0f 1.0f 1.0f } | |
let updateEntityPosition entity = | |
// Complex movement AI | |
let rndUnitIshVector = mkVector 1.0f 1.0f 1.0f | |
let movementVector = rndUnitIshVector ^* entity.Speed | |
{ entity with Location = movementVector ^+ entity.Location } | |
[<Literal>] | |
let NUM_BLOCKS = 65536; //just like minecraft! | |
[<Literal>] | |
let NUM_ENTITIES = 1000 | |
type Chunk = { | |
Blocks: Block list | |
Entities: Entity list | |
Location: Vector //x,y,z within world | |
} | |
let mkChunk location = | |
let blocks = [ | |
for i in [0..NUM_BLOCKS-1] do | |
let ifloat = float32 i | |
yield { | |
Location = mkVector ifloat ifloat ifloat | |
Name = sprintf "Block:%i" i | |
Durability = 100 | |
Textureid = 1 | |
Breakable = true | |
Visible = true | |
Type = 1 | |
} | |
] | |
let entities = [ | |
for i in [0..NUM_ENTITIES/4-1] do | |
let ifloat = float32 i | |
yield mkEntity (mkVector ifloat ifloat ifloat) Chicken | |
yield mkEntity (mkVector (ifloat+2.0f) ifloat ifloat) Zombie | |
yield mkEntity (mkVector (ifloat+3.0f) ifloat ifloat) Exploder | |
yield mkEntity (mkVector (ifloat+4.0f) ifloat ifloat) TallCreepyThing | |
] | |
{ Location = location; Blocks = blocks; Entities = entities } | |
let processEntities chunk = | |
{ chunk with | |
Entities = chunk.Entities |> List.map updateEntityPosition | |
} | |
[<Literal>] | |
let CHUNK_COUNT = 100; | |
type Game = { | |
Chunks: Chunk list | |
PlayerLocation: Vector | |
ChunkCounter: int | |
} | |
let mkGame () = { Chunks = List.empty; PlayerLocation = mkVector 0.0f 0.0f 0.0f; ChunkCounter = 0 } | |
let loadWorld game = | |
let chunks = [ | |
for i in [0..CHUNK_COUNT-1] do | |
let chunkCounter = game.ChunkCounter + i | |
yield mkChunk (mkVector (float32(chunkCounter)) 0.0f 0.0f) | |
] | |
{ game with Chunks = chunks; ChunkCounter = game.ChunkCounter + CHUNK_COUNT } | |
let updateChunks game = | |
let mutable chunkCounter = game.ChunkCounter | |
let chunks = [ | |
for chunk in game.Chunks do | |
let chunk = processEntities chunk | |
let chunkDistance = getDistance chunk.Location game.PlayerLocation | |
if chunkDistance > float32(CHUNK_COUNT) then | |
yield mkChunk (mkVector (float32(chunkCounter)) 0.0f 0.0f) | |
chunkCounter <- chunkCounter + 1 | |
else | |
yield chunk | |
] | |
{ game with Chunks = chunks; ChunkCounter = chunkCounter } | |
[<EntryPoint>] | |
let main _ = | |
// Praise be to the RNG | |
let s = new Stopwatch(); | |
let mutable game = mkGame () | |
Console.Write("Loading World..."); | |
s.Start(); | |
game <- loadWorld game | |
s.Stop(); | |
Console.WriteLine("FINISHED!"); | |
Console.WriteLine("Load Time:{0}", s.ElapsedMilliseconds); | |
//Game Loop, you can never leave | |
while true do | |
//check for dead entities | |
s.Restart(); | |
// mocking polling of the VR controller | |
let playerMovement = mkVector 0.1f 0.0f 0.0f | |
game <- { game with PlayerLocation = game.PlayerLocation ^+ playerMovement } | |
game <- updateChunks game | |
s.Stop(); | |
let time = ((float)s.ElapsedTicks / (float)Stopwatch.Frequency) * 1000.0; | |
Console.WriteLine(time); | |
//Lock it at 60FPS | |
if time < 16.0 then | |
Thread.Sleep((int)(16.0 - time)); | |
0 // return an integer exit code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System | |
open System.Diagnostics | |
open System.Threading | |
[<Struct>] | |
type Vector = | |
val X: float32 | |
val Y: float32 | |
val Z: float32 | |
new(x,y,z) = { X=x; Y=y; Z=z } | |
let inline (^*) (a: Vector) (b:Vector) = new Vector(a.X * b.X, a.Y * b.Y, a.Z * b.Z) | |
let inline (^+) (a: Vector) (b:Vector) = new Vector(a.X + b.X, a.Y + b.Y, a.Z + b.Z) | |
let inline (^-) (a: Vector) (b:Vector) = new Vector(a.X - b.X, a.Y - b.Y, a.Z - b.Z) | |
let inline mkVector x y z = new Vector(x, y, z) | |
let inline getDistance (a: Vector) (b:Vector) = | |
let s = a ^- b | |
float32(Math.Sqrt(float(s.X * s.X + s.Y * s.Y + s.Z * s.Z))) | |
type Block = { | |
Location: Vector //x,y,z within the chunk | |
Name: string | |
Durability: int | |
Textureid: int | |
Breakable: bool | |
Visible: bool | |
Type: int | |
} | |
type EntityType = |Zombie |Chicken |Exploder |TallCreepyThing | |
type Entity = { | |
Location: Vector //x,y,z within the chunk | |
Name: string | |
Health: int | |
Speed: Vector; | |
} | |
let mkEntity location type' = | |
match type' with | |
| Zombie -> { Location = location; Name = "Zombie"; Health = 50; Speed = mkVector 0.5f 0.0f 0.5f } | |
| Chicken -> { Location = location; Name = "Chicken"; Health = 25; Speed = mkVector 0.75f 0.25f 0.75f } | |
| Exploder -> { Location = location; Name = "Exploder"; Health = 75; Speed = mkVector 0.75f 0.0f 0.75f } | |
| TallCreepyThing -> { Location = location; Name = "Tall Creepy Thing"; Health = 500; Speed = mkVector 1.0f 1.0f 1.0f } | |
let updateEntityPosition entity = | |
// Complex movement AI | |
let rndUnitIshVector = mkVector 1.0f 1.0f 1.0f | |
let movementVector = rndUnitIshVector ^* entity.Speed | |
{ entity with Location = movementVector ^+ entity.Location } | |
[<Literal>] | |
let NUM_BLOCKS = 65536; //just like minecraft! | |
[<Literal>] | |
let NUM_ENTITIES = 1000 | |
type Chunk = { | |
Blocks: Block list | |
Entities: Entity list | |
Location: Vector //x,y,z within world | |
} | |
let mkChunk location = | |
let blocks = [ | |
for i in [0..NUM_BLOCKS-1] do | |
let ifloat = float32 i | |
yield { | |
Location = mkVector ifloat ifloat ifloat | |
Name = "Block:" + (i.ToString ()) | |
Durability = 100 | |
Textureid = 1 | |
Breakable = true | |
Visible = true | |
Type = 1 | |
} | |
] | |
let entities = [ | |
for i in [0..NUM_ENTITIES/4-1] do | |
let ifloat = float32 i | |
yield mkEntity (mkVector ifloat ifloat ifloat) Chicken | |
yield mkEntity (mkVector (ifloat+2.0f) ifloat ifloat) Zombie | |
yield mkEntity (mkVector (ifloat+3.0f) ifloat ifloat) Exploder | |
yield mkEntity (mkVector (ifloat+4.0f) ifloat ifloat) TallCreepyThing | |
] | |
{ Location = location; Blocks = blocks; Entities = entities } | |
let inline processEntities chunk = | |
{ chunk with | |
Entities = chunk.Entities |> List.map updateEntityPosition | |
} | |
[<Literal>] | |
let CHUNK_COUNT = 100; | |
type Game = { | |
Chunks: Chunk list | |
PlayerLocation: Vector | |
ChunkCounter: int | |
} | |
let mkGame () = { Chunks = List.empty; PlayerLocation = mkVector 0.0f 0.0f 0.0f; ChunkCounter = 0 } | |
let loadWorld game = | |
let chunks = [ | |
for i in [0..CHUNK_COUNT-1] do | |
let chunkCounter = game.ChunkCounter + i | |
yield mkChunk (mkVector (float32(chunkCounter)) 0.0f 0.0f) | |
] | |
{ game with Chunks = chunks; ChunkCounter = game.ChunkCounter + CHUNK_COUNT } | |
let updateChunks game = | |
let mutable chunkCounter = game.ChunkCounter | |
let chunks = [ | |
for chunk in game.Chunks do | |
let chunk = processEntities chunk | |
let chunkDistance = getDistance chunk.Location game.PlayerLocation | |
if chunkDistance > float32(CHUNK_COUNT) then | |
yield mkChunk (mkVector (float32(chunkCounter)) 0.0f 0.0f) | |
chunkCounter <- chunkCounter + 1 | |
else | |
yield chunk | |
] | |
{ game with Chunks = chunks; ChunkCounter = chunkCounter } | |
[<EntryPoint>] | |
let main _ = | |
// Praise be to the RNG | |
let s = new Stopwatch(); | |
let mutable game = mkGame () | |
Console.Write("Loading World..."); | |
s.Start(); | |
game <- loadWorld game | |
s.Stop(); | |
Console.WriteLine("FINISHED!"); | |
Console.WriteLine("Load Time:{0}", s.ElapsedMilliseconds); | |
//Game Loop, you can never leave | |
while true do | |
//check for dead entities | |
s.Restart(); | |
// mocking polling of the VR controller | |
let playerMovement = mkVector 0.1f 0.0f 0.0f | |
game <- { game with PlayerLocation = game.PlayerLocation ^+ playerMovement } | |
game <- updateChunks game | |
s.Stop(); | |
let time = ((float)s.ElapsedTicks / (float)Stopwatch.Frequency) * 1000.0; | |
Console.WriteLine(time); | |
//Lock it at 60FPS | |
if time < 16.0 then | |
Thread.Sleep((int)(16.0 - time)); | |
0 // return an integer exit code |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment