Skip to content

Instantly share code, notes, and snippets.

@vbfox
Last active September 13, 2016 16:31
Show Gist options
  • Save vbfox/eb5079ecc6b46fc9713439197a6bf8fd to your computer and use it in GitHub Desktop.
Save vbfox/eb5079ecc6b46fc9713439197a6bf8fd to your computer and use it in GitHub Desktop.
Naive F# Game
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
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
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