Pagine

venerdì 2 dicembre 2011

Farkanoid -> F# + XNA 4.0


Probabilmente otterrò la candidatura come "Worst Arkanoid Ever" e "Worst F# Code Ever" con discrete possibilità di vittoria. In ogni caso ho mosso da pochi giorni i primi passi con F# e mi premeva capire come poterlo integrare con XNA 4 e come ereditare e fare overriding di metodi di classi .NET.

Con la poca sintassi che ho imparato questo è il risultato.
Il progetto è scaricabile da qui.




open System
open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Graphics
open Microsoft.Xna.Framework.Input

module Farkanoid =

 type GameState =
  {
     mutable running: bool
  }

 type PlayerBrick =
  {
     mutable texture : Texture2D
     mutable position : Vector2
     lives : int
  }

 type Ball =
  {
     mutable texture : Texture2D
     mutable position : Vector2
     mutable direction: Vector2
     velocity : int
  }

 type CollisionSide = None | Left | Right | Top | Bottom

 let initTexture(graphics : GraphicsDevice, width : int, height : int) =
  let mutable colorList = []
  let texture = new Texture2D(graphics, width, height)
  
  for i = 1 to height do
   for j = 1 to width do
    if (j = 1) || (i = 1) || (j = width) || (i = height) 
     || (j = 2) || (i = 2) || (j = width - 1) || (i = height - 1)
    then
     colorList <- Color.DarkGray :: colorList
    else
     colorList <- Color.White :: colorList
  
  let colorArray = List.toArray(colorList)
  texture.SetData(colorArray)
  texture

 let initBrickList(texture : Texture2D) =
  let mutable brickList : Brick list = []
  for i = 0 to 5 do
   for j = 0 to i do
    let (c : float),(k : float) = (float)i,(float)j
    let offsetX : float = (float)texture.Width * (k - (0.5 * c) - 0.5) 
    let offsetY : float = ((float)texture.Height * 0.5) - (c * (float)texture.Height)
    let brickPosition = new Vector2(320.0 + offsetX |> float32 , 120.0 + offsetY |> float32) 
    let brick =
     {
      position = brickPosition
      live = 2
     }

    brickList <- brick :: brickList
  brickList
    
 type FarkanoidGame() as this =
    inherit Game()

    let mutable spriteBatch = null
    let graphicsDeviceManager = new GraphicsDeviceManager(this)
    
    let mutable brickTexture = null
    let mutable brickList : Brick list = []

    let mutable state =
     {
      running = false
     }

    let mutable playerBrick =
     {
      texture = null
      position = new Vector2(260.0 |> float32, 450 |> float32)
      lives = 3
     }

    let mutable ball = 
     {
      texture = null
      position = new Vector2(playerBrick.position.X + (float32)50, playerBrick.position.Y - (float32)10)
      direction = Vector2.Zero
      velocity = 5
     }

    let newDirection() = 
     let rand = new Random()
     let angle = 0.6 + rand.NextDouble() * 0.3
     new Vector2(Math.Cos(angle) |> float32, Math.Sin(angle) |> float32) * (float32)ball.velocity

    let rec checkBrickCollision () : CollisionSide =

     let collision = ref CollisionSide.None

     if ball.position.Y < (float32)300 then
         let rec updateBrickList(brickList) = 
          match brickList with
            | [] -> []
            | brick::list -> 
                if (ball.position.X + (float32)10 >= brick.position.X) &&
                 (ball.position.X <= brick.position.X + (float32)100) then
                 if (ball.position.Y + (float32)10 >= brick.position.Y) && 
                  (ball.position.Y <= brick.position.Y + (float32)20) then

                  if (ball.position.X <= brick.position.X) then collision := CollisionSide.Left
                  elif (ball.position.X + (float32)10 >= brick.position.X + (float32)100) then collision := CollisionSide.Right
                  elif (ball.position.Y + (float32)10 <= brick.position.Y + (float32)20)  then collision := CollisionSide.Top
                   else collision := CollisionSide.Bottom
                  
                  if brick.live > 1 then
                   brick.live <- 1
                   brick::updateBrickList(list)
                  else list
                 else
                  brick::updateBrickList(list)
                else
                 brick::updateBrickList(list)
                

         brickList <- updateBrickList(brickList)
     collision.Value
    
           
    member private this.updateInput : bool =
     let keyboardState = Keyboard.GetState()
     let mutable pressedKey = false
     if keyboardState.IsKeyDown(Keys.Left) then
      playerBrick.position.X <- (playerBrick.position.X - (float32)5)
      pressedKey <- true 
     elif keyboardState.IsKeyDown(Keys.Right) then
      playerBrick.position.X <- (playerBrick.position.X + (float32)5)
      pressedKey <- true
     playerBrick.position.X <- MathHelper.Clamp(playerBrick.position.X, (float32)10, (float32)510)
     pressedKey

  
    member private this.updateBall : bool =
     let mutable gameOver = false
     let mutable xChanger = (float32)1
     let mutable yChanger = (float32)1
     let mutable change = false

     let collisionSide = checkBrickCollision()

     (*bottom Collision (top of the brick) *)
     if collisionSide = CollisionSide.Top then
      if ball.direction.X < (float32)0.0 then xChanger <- float32(-1)
      yChanger <- float32(-1)
      change <- true

     if ball.position.Y >= (playerBrick.position.Y - (float32)10) then
      if (ball.position.X >= playerBrick.position.X) && 
       (ball.position.X <= (playerBrick.position.X + (float32)120)) then
       if ball.direction.X < (float32)0.0 then xChanger <- float32(-1)
       yChanger <- float32(-1)
       change <- true
      else
       state.running <- false
       brickList <- initBrickList(brickTexture)
       playerBrick.position <- new Vector2(260.0 |> float32, 450 |> float32)
       ball.position <- new Vector2(playerBrick.position.X + (float32)50, playerBrick.position.Y - (float32)10)
       ball.direction <- Vector2.Zero
      
     (*right Collision (left of the brick)*)
     if ball.position.X >= (float32)620 || collisionSide = CollisionSide.Left then
      if ball.direction.Y < (float32)0.0 then yChanger <- float32(-1)
      xChanger <- float32(-1)
      change <- true

     (*left Collision (right of the brick)*)
     if ball.position.X <= (float32)10 || collisionSide = CollisionSide.Right then
      if ball.direction.Y < (float32)0.0 then yChanger <- float32(-1)
      change <- true
 
     
     (*top Collision (bottom of the brick)*)
     if ball.position.Y <= (float32)10 || collisionSide = CollisionSide.Bottom then
      if ball.direction.X < (float32)0.0 then xChanger <- float32(-1)
      change <- true
      
     if change then  
      let newDirection = newDirection()   
      ball.direction.X <-newDirection.X * xChanger
      ball.direction.Y <- newDirection.Y * yChanger
     
     ball.position <- ball.position + ball.direction
      
     gameOver

 
    override Super.Initialize() =
     graphicsDeviceManager.PreferredBackBufferHeight <- 480
     graphicsDeviceManager.PreferredBackBufferWidth <- 640
     graphicsDeviceManager.ApplyChanges()
     base.Initialize()
  

    override Super.LoadContent() =
     playerBrick.texture <- initTexture(this.GraphicsDevice, 120, 20)
     ball.texture <- initTexture(this.GraphicsDevice, 11,11)
     brickTexture <- initTexture(this.GraphicsDevice, 100, 20)
     brickList <- initBrickList(brickTexture)
     spriteBatch <- new SpriteBatch(this.GraphicsDevice)

    override Super.Update(time : GameTime) =
     
     match state.running with
      | false -> 
         if this.updateInput then
          state.running <- true
      | true ->
         ignore(this.updateInput)
         ignore(this.updateBall)

     base.Update(time)

    override Super.Draw(time : GameTime) =
     this.GraphicsDevice.Clear(Color.Black)
     
     spriteBatch.Begin()

     spriteBatch.Draw(playerBrick.texture, playerBrick.position, Color.CornflowerBlue)
     spriteBatch.Draw(ball.texture, ball.position, Color.Red)
     
     for brick in brickList do
       if brick.live > 1 then
        spriteBatch.Draw(brickTexture, brick.position, Color.White)
       else
        spriteBatch.Draw(brickTexture, brick.position, Color.Gray)
     
     spriteBatch.End()
     base.Draw(time)
     
 let farkanoidGame = new FarkanoidGame()
 farkanoidGame.Run()
 farkanoidGame.Dispose()

Nessun commento:

Posta un commento