4 people like it.

# Learning Flappy Bird in F# with Nephew

Learning F# with my 11 year old nephew, he is loving it, swapping out assets and creating new ones, adjusting gravity to what feels right to him and putting in very basic collision detection. Going to keep expanding on it over the year.

 ``` 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: ``` ``````#if INTERACTIVE #I @"../packages/MonoGame.Framework.WindowsDX.3.4.0.459/lib/net40/" //#I @"../packages/MonoGame.Framework.WindowsGL.3.4.0.459/lib/net40/" #r "MonoGame.Framework.dll" #endif /// Bird type type Bird = { X:float; Y:float; VY:float; mutable IsAlive:bool; mutable Rectangle:Microsoft.Xna.Framework.Rectangle } /// Respond to flap command let flap (bird:Bird) = { bird with VY = -2.11} /// Applies gravity to bird let gravity (bird:Bird) = { bird with VY = bird.VY + 0.1 } /// Applies physics to bird let physics (bird:Bird) = { bird with Y = bird.Y + bird.VY } /// Updates bird with gravity & physics let update = gravity >> physics /// Generates the level's tube positions let generateLevel n = let rand = System.Random() [for i in 1..n -> 50+(i*150), 32+rand.Next(160)] open System.IO open Microsoft.Xna.Framework open Microsoft.Xna.Framework.Graphics open Microsoft.Xna.Framework.Input let loadImage (device:GraphicsDevice) file = let path = Path.Combine(__SOURCE_DIRECTORY__, file) use stream = File.OpenRead(path) let texture = Texture2D.FromStream(device, stream) let textureData = Array.create (texture.Width * texture.Height) Color.Transparent texture.GetData(textureData) texture type FlappyBird() as this = inherit Game() do this.Window.Title <- "Flappy Callum" let graphics = new GraphicsDeviceManager(this) do graphics.PreferredBackBufferWidth <- 288 do graphics.PreferredBackBufferHeight <- 440 let mutable spriteBatch : SpriteBatch = null let mutable bg : Texture2D = null let mutable ground : Texture2D = null let mutable tube1 : Texture2D = null let mutable tube2 : Texture2D = null let mutable bird_sing : Texture2D = null let mutable gameover : Texture2D = null let mutable lastKeyState = KeyboardState() let mutable lastMouseState = MouseState() let level = generateLevel 10 let mutable flappy = { X = 30.0; Y = 150.0; VY = 0.0; IsAlive=true; Rectangle = Microsoft.Xna.Framework.Rectangle()} let flapMe () = if flappy.IsAlive then flappy <- flap flappy let mutable scroll = 0 override this.LoadContent() = spriteBatch <- new SpriteBatch(this.GraphicsDevice) let load = loadImage this.GraphicsDevice bg <- load "bg.png" ground <- load "beartraps2.png" tube1 <- load "tube1.png" tube2 <- load "tube2.png" gameover <- load "gameover.png" bird_sing <- load "bird_sing.png" flappy.Rectangle <- bird_sing.Bounds override this.Update(gameTime) = let currentKeyState = Keyboard.GetState() let currentMouseState = Mouse.GetState() let isKeyPressedSinceLastFrame key = currentKeyState.IsKeyDown(key) && lastKeyState.IsKeyUp(key) let isMouseClicked () = currentMouseState.LeftButton = ButtonState.Pressed && lastMouseState.LeftButton = ButtonState.Released if flappy.IsAlive then scroll <- scroll - 1 if isKeyPressedSinceLastFrame Keys.Space || isMouseClicked () then flapMe () flappy <- update flappy lastKeyState <- currentKeyState lastMouseState <- currentMouseState else if isKeyPressedSinceLastFrame Keys.Space || isMouseClicked () then scroll <- 0 flappy <- { X = 30.0; Y = 150.0; VY = 0.0; IsAlive=true; Rectangle = Microsoft.Xna.Framework.Rectangle()} override this.Draw(gameTime) = this.GraphicsDevice.Clear Color.White spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied) let draw (texture:Texture2D) (x,y) = spriteBatch.Draw(texture, Rectangle(x,y,texture.Width,texture.Height), Color.White) draw bg (0,0) flappy.Rectangle <- Rectangle(int flappy.X, int flappy.Y, bird_sing.Width, bird_sing.Height) draw bird_sing (int flappy.X,int flappy.Y) for (x,y) in level do let x = x+scroll draw tube1 (x,-320+y) draw tube2 (x,y+100) draw ground (0,360) if flappy.Rectangle.Intersects(Rectangle(0, 360, ground.Width, ground.Height)) then flappy.IsAlive <- false draw gameover (-40, 150) spriteBatch.End() do use game = new FlappyBird() game.Run() ``````
type Bird =
{X: float;
Y: float;
VY: float;
mutable IsAlive: bool;
mutable Rectangle: obj;}

Full name: Script.Bird

Bird type
Bird.X: float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = System.Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
Bird.Y: float
Bird.VY: float
Bird.IsAlive: bool
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
Bird.Rectangle: obj
namespace Microsoft
val flap : bird:Bird -> Bird

Full name: Script.flap

Respond to flap command
val bird : Bird
val gravity : bird:Bird -> Bird

Full name: Script.gravity

Applies gravity to bird
val physics : bird:Bird -> Bird

Full name: Script.physics

Applies physics to bird
val update : (Bird -> Bird)

Full name: Script.update

Updates bird with gravity & physics
val generateLevel : n:int -> (int * int) list

Full name: Script.generateLevel

Generates the level's tube positions
val n : int
val rand : System.Random
namespace System
Multiple items
type Random =
new : unit -> Random + 1 overload
member Next : unit -> int + 2 overloads
member NextBytes : buffer:byte[] -> unit
member NextDouble : unit -> float

Full name: System.Random

--------------------
System.Random() : unit
System.Random(Seed: int) : unit
val i : int
System.Random.Next() : int
System.Random.Next(maxValue: int) : int
System.Random.Next(minValue: int, maxValue: int) : int
namespace System.IO
val loadImage : device:'a -> file:string -> 'b

val device : 'a
val file : string
val path : string
type Path =
static val DirectorySeparatorChar : char
static val AltDirectorySeparatorChar : char
static val VolumeSeparatorChar : char
static val InvalidPathChars : char[]
static val PathSeparator : char
static member ChangeExtension : path:string * extension:string -> string
static member Combine : [<ParamArray>] paths:string[] -> string + 3 overloads
static member GetDirectoryName : path:string -> string
static member GetExtension : path:string -> string
static member GetFileName : path:string -> string
...

Full name: System.IO.Path
Path.Combine([<System.ParamArray>] paths: string []) : string
Path.Combine(path1: string, path2: string) : string
Path.Combine(path1: string, path2: string, path3: string) : string
Path.Combine(path1: string, path2: string, path3: string, path4: string) : string
val stream : FileStream
type File =
static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
static member AppendAllText : path:string * contents:string -> unit + 1 overload
static member AppendText : path:string -> StreamWriter
static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
static member Create : path:string -> FileStream + 3 overloads
static member CreateText : path:string -> StreamWriter
static member Decrypt : path:string -> unit
static member Delete : path:string -> unit
static member Encrypt : path:string -> unit
static member Exists : path:string -> bool
...

Full name: System.IO.File
val texture : 'b
val textureData : obj
module Array

from Microsoft.FSharp.Collections
val create : count:int -> value:'T -> 'T []

Full name: Microsoft.FSharp.Collections.Array.create
Multiple items
type FlappyBird =
inherit obj
new : unit -> FlappyBird
override Draw : gameTime:'a -> 'b
override LoadContent : unit -> 'a
override Update : gameTime:'a -> 'b

Full name: Script.FlappyBird

--------------------
new : unit -> FlappyBird
val this : FlappyBird
override FlappyBird.LoadContent : unit -> 'a

override FlappyBird.Update : gameTime:'a -> 'b

Full name: Script.FlappyBird.Update
override FlappyBird.Draw : gameTime:'a -> 'b

Full name: Script.FlappyBird.Draw
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
val game : FlappyBird