F# is akin of melting pot language, including some interesting features from various functional languages like Haskell, OCaml and so on. It is very succint, readable and it is ML-based.
I’ve realized a simple Pong-clone game using the XNA framework in F#, and this is the result:
// A semiserious game // Learn more about F# at http://fsharp.net open System open Microsoft.Xna.Framework open Microsoft.Xna.Framework.Graphics open Microsoft.Xna.Framework.Input open Microsoft.Xna.Framework.Content type Paddle(posX : int, posY : int) = let speed = 10 let x = posX let mutable y = posY static member W = 50 static member H = 80 member this.X with get() = x member this.Y with get() = y and set v = y <- v member this.Bounds with get() = new Rectangle(x - 100, y - Paddle.H / 2, Paddle.W, Paddle.H) member this.Move (keyUp : Keys) (keyDown : Keys) = let ks = Keyboard.GetState() if ks.IsKeyDown(keyUp) then y <- y - speed if ks.IsKeyDown(keyDown) then y <- y + speed type Ball() = let mutable x = 0 let mutable y = 0 let mutable vx = 0 let mutable vy = 0 let mutable number_of_bounce = 10 static member W = 20 static member H = 20 member this.Number_of_bounce with get() = number_of_bounce and set v = number_of_bounce <- v member this.X with get() = x and set v = x <- v member this.Y with get() = y and set v = y <- v member this.Bounds with get() = new Rectangle(x - Ball.W / 2, y - Ball.H / 2, Ball.W, Ball.H) member this.BounceX() = this.Number_of_bounce <- this.Number_of_bounce + 1 vx <- -vx member this.BounceY() = vy <- -vy member this.Start (velX : int) (velY : int) = vx <- velX vy <- velY member this.Update() = x <- x + (int (this.Number_of_bounce/10))*vx y <- y + (int (this.Number_of_bounce/10))*vy member this.Collide (p : Paddle) = this.Bounds.Intersects(p.Bounds) type Game1() as this = inherit Game() let graphics = new GraphicsDeviceManager(this) let contentManager = new ContentManager(this.Services) let mutable spriteBatch = null let mutable texture = null let mutable left_paddle = null let mutable background = null let mutable ball_texture = null let left = new Paddle(Paddle.W * 2, 300) let right = new Paddle(820, 300) let ball = new Ball() let rand = new Random() let mutable leftScore = 0 let mutable rightScore = 0 let clampY x : int = if x < Paddle.H / 2 then Paddle.H / 2 else if x > 600 - (Paddle.H / 2) then 600 - (Paddle.H / 2) else x let reset (b : Ball) = let mutable x = 0 let mutable y = 0 if rand.Next() % 2 = 0 then x <- -3 else x <- 3 if rand.Next() % 2 = 0 then y <- -3 else y <- 3 b.X <- 400 b.Y <- 300 b.Number_of_bounce <- 10 b.Start x y let bounce (b : Ball) = if b.X < Ball.W / 2 then rightScore <- rightScore + 1 reset b else if b.X > 800 - Ball.W / 2 then leftScore <- leftScore + 1 reset b if b.Y < Ball.H / 2 then b.Y <- Ball.H / 2 b.BounceY() else if b.Y > 480 - Ball.H / 2 then b.Y <- 480 - Ball.H / 2 b.BounceY() override Game.LoadContent() = spriteBatch <- new SpriteBatch(this.GraphicsDevice) background <- contentManager.Load<Texture2D>("xnb/green-field") left_paddle <- contentManager.Load<Texture2D>("xnb/cow") ball_texture <- contentManager.Load<Texture2D>("xnb/sheep") texture <- new Texture2D(this.GraphicsDevice, 1, 1); texture.SetData([| Color.White |]) reset ball override Game.Update gameTime = left.Move Keys.W Keys.S right.Move Keys.Up Keys.Down ball.Update() bounce ball left.Y <- clampY left.Y right.Y <- clampY right.Y if ball.Collide(left) || ball.Collide(right) then ball.BounceX() override Game.Draw gameTime = this.GraphicsDevice.Clear(Color.Beige) spriteBatch.Begin() spriteBatch.Draw(background, new Vector2(0.0f, 0.0f), Color.White) spriteBatch.Draw(left_paddle, left.Bounds, Color.White) spriteBatch.Draw(left_paddle, right.Bounds, Color.White) spriteBatch.Draw(ball_texture, ball.Bounds, Color.White) for i in 1 .. leftScore do let r = new Rectangle(50 + i * 10, 5, 5, 5) spriteBatch.Draw(texture, r, Color.Black); for i in 1 .. rightScore do let r = new Rectangle(750 - i * 10 - 5, 5, 5, 5) spriteBatch.Draw(texture, r, Color.Black); spriteBatch.End() base.Draw(gameTime) let g = new Game1() try g.Run() finally g.Dispose()
At the end of the post I will link the entire project as well. As you can see it’s very easy to create a game from scratch, thanks to the support of the XNA Framework. The only tangible problem is related to the creation of the sprites file, due to the lack of support for Contents creation in Visual Studio F#.
A possible workaround is to create a dummy C# project, load all your images there and then compile. You will find all the created .xnb files inside the project directory. Finally, you need to copy the generated files into your bin directory. Here is a screenshot:
And there you can download the entire project:
As a total stranger to functional programming, in the past few months I have been constantly amazed by this type of examples (not to mention scared :p). More and more examples are surfacing, some of them mixed with Rx Framework…
If it is not too much to ask:
– Is this the future? Can we expect in the near future to be programming the functional way?
– When declaring a variable (I bet these are not the terms in F# world, I mean “let speed = 0”) the operator is “=” but when used anywhere else is “<-".
– "let x = posX", but "let mutable y = posY". Is this like "x won't change" but "y will", as in "y" will "mutate" ?
Hi, and thanks for posting that.
1) I hope so. Indeed, the OOP it’s a great way to programming, but is not the only one. For years, the functional programmers used to “hide” from others, due to the lack of support for his preferred programming paradigm. F# is the first language officially supported by a huge company as Microsoft, and it’s fully compatible with the XNA framework and the .NET APIs. Yes, the future is near.
2) The main difference between = and <- is in the let keyword. When we define for the first time a variable or a mutable or even a function, we use the let keyword. We can define both variable:
let a = 3
let square x = x * x
Obviously we can't modify neither a nor square, as you may know, the functional programming is almost free by side effects. In order to introduce them, we need a special keyword, mutable:
let mutable b = 5;;
b <- 10;;
Now b evaluates to 10.
Ok, it’s becoming clearer now. Now I have to play with it…
LOL! Cow-pong. Hilariuos!
Cows are amazing!! 😀
I love them 😛