|
|
XNA game studio
Last post 05-18-2007, 13:20 by Andyman. 17 replies.
-
-
08-26-2006, 8:47 |
-
palevich
-
-
-
Joined on 05-15-2006
-
-
Posts 15
-
-
|
I asked an XNA developer about this at the recent Game Fest conference. The short answer is that you should be able to:
- Write XNA Game Framework components in any first class .NET language. (Including F#)
- Use XNA Game Framework components from any first class .NET language. (Including F#)
- Use XNA Game Framework APIs from any first class .NET language. (Including F#)
The bad news is:
- The free version of the IDE that supports XNA Game Studio only supports C#, so no intellisense for F#. You might be able to use custom build rules to support compilation of F# files as part of your game's build, but this is not 100% sure. We'll know more in early September.
- The IDE has a very nice forms-designer-like UI for hooking up game components. Like the forms designer this IDE has to generate code. Unlike the forms designer the generated code is currently limited to being C# code. So the "main loop" of your game will be written in partially generated C#.
Overall, you should be able to use the XNA Frameworks from F# just as easily as you can currently use the "Managed DirectX" APIs. But the IDE is currently C#-centric, so it will be more clumsy to use from any non-C# language. There are plenty of envious Visual Basic fans asking for more general language support, so perhaps this will change in the future.
|
|
-
08-27-2006, 4:03 |
-
SniperEye
-
-
-
Joined on 06-04-2006
-
-
Posts 10
-
-
|
palevich:
- The IDE has a very nice forms-designer-like UI for hooking up game components. Like the forms designer this IDE has to generate code. Unlike the forms designer the generated code is currently limited to being C# code. So the "main loop" of your game will be written in partially generated C#.
Overall, you should be able to use the XNA Frameworks from F# just as easily as you can currently use the "Managed DirectX" APIs. But the IDE is currently C#-centric, so it will be more clumsy to use from any non-C# language. There are plenty of envious Visual Basic fans asking for more general language support, so perhaps this will change in the future.
Do you know whether or not the IDE extension uses CodeDom? If so, Thomas recent F# CodeDom extension might come in very handy (http://cs.hubfs.net/blogs/tomasp/archive/2006/08/22/549.aspx).
Ralf Herbrich
Applied Games Group, Microsoft Research Ltd., Cambridge, UK
|
|
-
08-27-2006, 8:37 |
-
palevich
-
-
-
Joined on 05-15-2006
-
-
Posts 15
-
-
|
SniperEye:
Do you know whether or not the IDE extension uses CodeDom?
No idea how it is implemented. We'll know more when the beta is released.
My guess is that it doesn't matter how it is implemented, because I expect the main problem will be that the free Game Studio IDE is based upon the Express version of Visual Studio, which will not load add-on DLLs. So you can't, for example, add F# intellisense to the Express IDE. I would expect similar problems trying to add any other F# customization, including whatever is needed for generating F# code for the game studio.
|
|
-
-
08-30-2006, 15:10 |
-
palevich
-
-
-
Joined on 05-15-2006
-
-
Posts 15
-
-
|
The XNA Game Studio Express Beta is out. In order to install it you need to install these two items. (The Game Studio Express Beta must be installed last.)
- Install the C# Express IDE: http://msdn.microsoft.com/vstudio/express/visualcsharp/download/
- Install the Game Studio Beta: http://msdn.microsoft.com/directx/xna/
(You can also optionally install the August 2006 DirectX SDK http://msdn.microsoft.com/directx/sdk/ , if you want to compose sound effects using XACT.)
As expected, the IDE is C#-centric. But you can use the underlying XNA Framework from any CLR language, including F#.
Here is some code (using F# 1.11.12.3) that draws a blank blue screen using the XNA framework. (This is the first step of the getting started tutorial from the XNA documentation.) #I @"C:\Program Files\Microsoft XNA\XNA Game Studio Express\v1.0\References\Windows\x86"; #r "Microsoft.Xna.Framework.dll"; #r "Microsoft.Xna.Framework.Game.dll"; #light open System;open System.Collections.Generic;open Microsoft.Xna.Framework;open Microsoft.Xna.Framework.Audio;open Microsoft.Xna.Framework.Components;open Microsoft.Xna.Framework.Graphics;open Microsoft.Xna.Framework.Input;open Microsoft.Xna.Framework.Storage;type Game1 = class inherit Game val graphics : GraphicsComponent new() as g = { graphics = new GraphicsComponent() } then g.GameComponents.Add(g.graphics) override g.Update() = g.UpdateComponents() override g.Draw() = if g.graphics.EnsureDevice() then g.graphics.GraphicsDevice.Clear(Color.CornflowerBlue); g.graphics.GraphicsDevice.BeginScene(); g.DrawComponents(); g.graphics.GraphicsDevice.EndScene(); g.graphics.GraphicsDevice.Present() end let main() = let game = new Game1() game.Run() [<STAThread>] do main()
|
|
-
09-13-2006, 13:17 |
-
Andyman
-
-
-
Joined on 08-26-2006
-
Scotland
-
Posts 53
-
-
|
Hi Palevich,
Thanks for the code. Here's an updated version which renders a coloured cube which can be rotated and translated using the xbox 360 controller.
Cheers,
Andy
#I @"C:\Program Files\Microsoft XNA\XNA Game Studio Express\v1.0\References\Windows\x86"; #r "Microsoft.Xna.Framework.dll"; #r "Microsoft.Xna.Framework.Game.dll";
open System; open System.Collections.Generic; open Microsoft.Xna.Framework; open Microsoft.Xna.Framework.Audio; open Microsoft.Xna.Framework.Components; open Microsoft.Xna.Framework.Graphics; open Microsoft.Xna.Framework.Input; open Microsoft.Xna.Framework.Storage;
(**************************************************************************)
(* Framework code. Rather than embed logic in this class I provide callback functions for initialisation, update and drawing. Perhaps when I understand f# classes a bit better some boilerplate functionality can move into here *) type Game1 = class inherit Game val graphics : GraphicsComponent val update : Game1 -> unit val draw : Game1 -> unit val init : Game1 -> bool val bgColour : Color
new(init, update, draw, bgColour) as g = { graphics = new GraphicsComponent(); init = init; update = update; draw = draw; bgColour = bgColour } then g.GameComponents.Add(g.graphics); if not (init g) then failwith "Could not initialise" override g.Update() = g.update g; g.UpdateComponents() override g.Draw() = if g.graphics.EnsureDevice() then g.graphics.GraphicsDevice.Clear(g.bgColour); g.graphics.GraphicsDevice.BeginScene(); g.draw g; g.DrawComponents(); g.graphics.GraphicsDevice.EndScene(); g.graphics.GraphicsDevice.Present() end
(**************************************************************************)
type gamestate_t = { mutable screenSize : Vector2; mutable prevPadState : GamePadState; cube : VertexPositionColor array; mutable effect : Effect; mutable worldMatrix : Matrix; mutable viewMatrix : Matrix; mutable projMatrix : Matrix; }
let shader = System.Text.Encoding.ASCII.GetBytes " uniform extern float4x4 WorldViewProj : WORLDVIEWPROJECTION; struct VS_OUTPUT { float4 position : POSITION; float4 color : COLOR0; }; VS_OUTPUT Transform( float4 Pos : POSITION, float4 Color : COLOR0 ) { VS_OUTPUT Out = (VS_OUTPUT)0; Out.position = mul(Pos, WorldViewProj); Out.color = Color; return Out; } technique transform { pass P0 { vertexShader = compile vs_2_0 Transform(); } } "
let shaderCompilerFlags = Enum.combine [ CompilerOptions.Debug; CompilerOptions.SkipOptimization ]
let topLeftFront = new Vector3(-1.0f, 1.0f, 1.0f) let bottomLeftFront = new Vector3( -1.0f, -1.0f, 1.0f ) let topRightFront = new Vector3( 1.0f, 1.0f, 1.0f ) let bottomRightFront = new Vector3( 1.0f, -1.0f, 1.0f ) let topLeftBack = new Vector3( -1.0f, 1.0f, -1.0f ) let topRightBack = new Vector3( 1.0f, 1.0f, -1.0f ) let bottomLeftBack = new Vector3( -1.0f, -1.0f, -1.0f ) let bottomRightBack = new Vector3( 1.0f, -1.0f, -1.0f )
let textureTopLeft = new Vector2( 0.0f, 0.0f ) let textureTopRight = new Vector2( 1.0f, 0.0f ) let textureBottomLeft = new Vector2( 0.0f, 1.0f ) let textureBottomRight = new Vector2( 1.0f, 1.0f )
let fPI = 3.1415927f
(* Game state structure *) let gamestate = { screenSize = new Vector2(0.0f,0.0f); prevPadState = GamePad.GetState(PlayerIndex.One); cube = [| (* Front face *) new VertexPositionColor( topLeftFront, Color.Red ); new VertexPositionColor( bottomLeftFront, Color.Red ); new VertexPositionColor( topRightFront, Color.Red ); new VertexPositionColor( bottomLeftFront, Color.Red ); new VertexPositionColor( bottomRightFront, Color.Red ); new VertexPositionColor( topRightFront, Color.Red ); (* Back face *) new VertexPositionColor( topLeftBack, Color.Orange ); new VertexPositionColor( topRightBack, Color.Orange ); new VertexPositionColor( bottomLeftBack, Color.Orange ); new VertexPositionColor( bottomLeftBack, Color.Orange ); new VertexPositionColor( topRightBack, Color.Orange ); new VertexPositionColor( bottomRightBack, Color.Orange ); (* Top face *) new VertexPositionColor( topLeftFront, Color.Yellow ); new VertexPositionColor( topRightBack, Color.Yellow ); new VertexPositionColor( topLeftBack, Color.Yellow ); new VertexPositionColor( topLeftFront, Color.Yellow ); new VertexPositionColor( topRightFront, Color.Yellow ); new VertexPositionColor( topRightBack, Color.Yellow ); (* Bottom face *) new VertexPositionColor( bottomLeftFront, Color.Purple ); new VertexPositionColor( bottomLeftBack, Color.Purple ); new VertexPositionColor( bottomRightBack, Color.Purple ); new VertexPositionColor( bottomLeftFront, Color.Purple ); new VertexPositionColor( bottomRightBack, Color.Purple ); new VertexPositionColor( bottomRightFront, Color.Purple ); (* Left face *) new VertexPositionColor( topLeftFront, Color.Blue ); new VertexPositionColor( bottomLeftBack, Color.Blue ); new VertexPositionColor( bottomLeftFront, Color.Blue ); new VertexPositionColor( topLeftBack, Color.Blue ); new VertexPositionColor( bottomLeftBack, Color.Blue ); new VertexPositionColor( topLeftFront, Color.Blue ); (* Right face *) new VertexPositionColor( topRightFront, Color.Green ); new VertexPositionColor( bottomRightFront, Color.Green ); new VertexPositionColor( bottomRightBack, Color.Green ); new VertexPositionColor( topRightBack, Color.Green ); new VertexPositionColor( topRightFront, Color.Green ); new VertexPositionColor( bottomRightBack, Color.Green ); |]; effect = null; worldMatrix = Matrix.Identity; viewMatrix = Matrix.Identity; projMatrix = Matrix.Identity; }
let init (g : Game1) = let compiledEffect = Effect.CompileEffectFromFile(new IO.MemoryStream(shader), null, null, shaderCompilerFlags, TargetPlatform.Windows) in gamestate.effect <- new Effect(g.graphics.GraphicsDevice, compiledEffect.GetShaderCode(), CompilerOptions.None, null); gamestate.screenSize <- new Vector2( float32_of_int g.graphics.GraphicsDevice.Viewport.Width, float32_of_int g.graphics.GraphicsDevice.Viewport.Height); gamestate.viewMatrix <- Matrix.CreateLookAt(new Vector3(0.0f, 0.5f, 4.5f), Vector3.Zero, Vector3.Up); gamestate.projMatrix <- Matrix.CreatePerspectiveFieldOfView(fPI / 2.0f, gamestate.screenSize.X / gamestate.screenSize.Y, 0.1f, 1000.0f); true
let update_input () = let currentState = GamePad.GetState(PlayerIndex.One) in if currentState.IsConnected then let wo2 = gamestate.screenSize.X / 2.0f in let ho2 = gamestate.screenSize.Y / 2.0f in let sticks = currentState.ThumbSticks in gamestate.worldMatrix <- Matrix.CreateRotationY(fPI * sticks.Left.X) * Matrix.CreateRotationX(fPI * sticks.Left.Y) * Matrix.CreateTranslation(3.0f * sticks.Right.X, 3.0f * sticks.Right.Y, 0.0f); gamestate.prevPadState <- currentState
let update (g : Game1) = update_input()
let draw (g : Game1) = let transform_matrix = gamestate.effect.Parameters.Item("WorldViewProj") in Idioms.using (new VertexDeclaration (g.graphics.GraphicsDevice, VertexPositionColor.VertexElements)) (fun vertexType -> gamestate.effect.CurrentTechnique <- gamestate.effect.Techniques.Item(0); gamestate.effect.Begin(EffectStateOptions.Default); Idioms.foreach gamestate.effect.CurrentTechnique.Passes (fun (pass : EffectPass) -> pass.Begin(); g.graphics.GraphicsDevice.VertexDeclaration <- vertexType; g.graphics.GraphicsDevice.RenderState.CullMode <- CullMode.None; transform_matrix.SetValue(gamestate.worldMatrix * gamestate.viewMatrix * gamestate.projMatrix); gamestate.effect.CommitChanges(); g.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, 12, gamestate.cube); pass.End(); ) ); gamestate.effect.End()
(*********************************************************************************) let main() = let game = new Game1(init, update, draw, Color.Black) in game.Run() [<STAThread>] do main()
|
|
-
09-14-2006, 8:24 |
-
palevich
-
-
-
Joined on 05-15-2006
-
-
Posts 15
-
-
|
Hey, very nice indeed!
I think it's reasonable to try and minimize class usage with F#. It seems that it could be somewhat easier to develop code interactively using fsi.exe if you use records-and-functions rather than classes, since you can incrementally define new functions that work on existing instantiated records, but you can't incrementally define new methods that work on existing instantiated classes.
However, the XNA Framework is designed around the creation and re-use of game components, which are implemented as subclasses of the GameComponent base class with component-specific methods and properties.
There isn't much advantage to doing this right now, but I think that future versions of the XNA framework will provide a UI to drag-and-drop game components and hook up their properties. And this UI will work through the GameComponent base class.
Here is a slightly modified version of your sample that uses a GameComponent instead of a record:
#I @"C:\Program Files\Microsoft XNA\XNA Game Studio Express\v1.0\References\Windows\x86"; #r "Microsoft.Xna.Framework.dll"; #r "Microsoft.Xna.Framework.Game.dll";
open System; open System.Collections.Generic; open Microsoft.Xna.Framework; open Microsoft.Xna.Framework.Audio; open Microsoft.Xna.Framework.Components; open Microsoft.Xna.Framework.Graphics; open Microsoft.Xna.Framework.Input; open Microsoft.Xna.Framework.Storage;
(**************************************************************************)
(* Framework code. Rather than embed logic in this class I provide callback functions for initialisation, update and drawing. Perhaps when I understand f# classes a bit better some boilerplate functionality can move into here *) type Game1 = class inherit Game val graphics : GraphicsComponent val bgColour : Color
new(aGameComponent, bgColour) as g = { graphics = new GraphicsComponent(); bgColour = bgColour; } then g.GameComponents.Add(g.graphics); g.GameComponents.Add(aGameComponent) override g.Update() = g.UpdateComponents() override g.Draw() = if g.graphics.EnsureDevice() then g.graphics.GraphicsDevice.Clear(g.bgColour); g.graphics.GraphicsDevice.BeginScene(); g.DrawComponents(); g.graphics.GraphicsDevice.EndScene(); g.graphics.GraphicsDevice.Present() end
let shader = System.Text.Encoding.ASCII.GetBytes " uniform extern float4x4 WorldViewProj : WORLDVIEWPROJECTION; struct VS_OUTPUT { float4 position : POSITION; float4 color : COLOR0; }; VS_OUTPUT Transform( float4 Pos : POSITION, float4 Color : COLOR0 ) { VS_OUTPUT Out = (VS_OUTPUT)0; Out.position = mul(Pos, WorldViewProj); Out.color = Color; return Out; } technique transform { pass P0 { vertexShader = compile vs_2_0 Transform(); } } "
let shaderCompilerFlags = Enum.combine [ CompilerOptions.Debug; CompilerOptions.SkipOptimization ]
let topLeftFront = new Vector3(-1.0f, 1.0f, 1.0f) let bottomLeftFront = new Vector3( -1.0f, -1.0f, 1.0f ) let topRightFront = new Vector3( 1.0f, 1.0f, 1.0f ) let bottomRightFront = new Vector3( 1.0f, -1.0f, 1.0f ) let topLeftBack = new Vector3( -1.0f, 1.0f, -1.0f ) let topRightBack = new Vector3( 1.0f, 1.0f, -1.0f ) let bottomLeftBack = new Vector3( -1.0f, -1.0f, -1.0f ) let bottomRightBack = new Vector3( 1.0f, -1.0f, -1.0f )
let textureTopLeft = new Vector2( 0.0f, 0.0f ) let textureTopRight = new Vector2( 1.0f, 0.0f ) let textureBottomLeft = new Vector2( 0.0f, 1.0f ) let textureBottomRight = new Vector2( 1.0f, 1.0f )
let fPI = 3.1415927f
(**************************************************************************)
type GameComponent1 = class inherit GameComponent val mutable screenSize : Vector2; val mutable prevPadState : GamePadState; val cube : VertexPositionColor array; val mutable effect : Effect; val mutable worldMatrix : Matrix; val mutable viewMatrix : Matrix; val mutable projMatrix : Matrix; new () = { screenSize = new Vector2(0.0f,0.0f); prevPadState = GamePad.GetState(PlayerIndex.One); cube = [| (* Front face *) new VertexPositionColor( topLeftFront, Color.Red ); new VertexPositionColor( bottomLeftFront, Color.Red ); new VertexPositionColor( topRightFront, Color.Red ); new VertexPositionColor( bottomLeftFront, Color.Red ); new VertexPositionColor( bottomRightFront, Color.Red ); new VertexPositionColor( topRightFront, Color.Red ); (* Back face *) new VertexPositionColor( topLeftBack, Color.Orange ); new VertexPositionColor( topRightBack, Color.Orange ); new VertexPositionColor( bottomLeftBack, Color.Orange ); new VertexPositionColor( bottomLeftBack, Color.Orange ); new VertexPositionColor( topRightBack, Color.Orange ); new VertexPositionColor( bottomRightBack, Color.Orange ); (* Top face *) new VertexPositionColor( topLeftFront, Color.Yellow ); new VertexPositionColor( topRightBack, Color.Yellow ); new VertexPositionColor( topLeftBack, Color.Yellow ); new VertexPositionColor( topLeftFront, Color.Yellow ); new VertexPositionColor( topRightFront, Color.Yellow ); new VertexPositionColor( topRightBack, Color.Yellow ); (* Bottom face *) new VertexPositionColor( bottomLeftFront, Color.Purple ); new VertexPositionColor( bottomLeftBack, Color.Purple ); new VertexPositionColor( bottomRightBack, Color.Purple ); new VertexPositionColor( bottomLeftFront, Color.Purple ); new VertexPositionColor( bottomRightBack, Color.Purple ); new VertexPositionColor( bottomRightFront, Color.Purple ); (* Left face *) new VertexPositionColor( topLeftFront, Color.Blue ); new VertexPositionColor( bottomLeftBack, Color.Blue ); new VertexPositionColor( bottomLeftFront, Color.Blue ); new VertexPositionColor( topLeftBack, Color.Blue ); new VertexPositionColor( bottomLeftBack, Color.Blue ); new VertexPositionColor( topLeftFront, Color.Blue ); (* Right face *) new VertexPositionColor( topRightFront, Color.Green ); new VertexPositionColor( bottomRightFront, Color.Green ); new VertexPositionColor( bottomRightBack, Color.Green ); new VertexPositionColor( topRightBack, Color.Green ); new VertexPositionColor( topRightFront, Color.Green ); new VertexPositionColor( bottomRightBack, Color.Green ); |]; effect = null; worldMatrix = Matrix.Identity; viewMatrix = Matrix.Identity; projMatrix = Matrix.Identity; } override gamestate.Start() = let graphicsDevice = gamestate.GraphicsDevice() in let compiledEffect = Effect.CompileEffectFromFile(new IO.MemoryStream(shader), null, null, shaderCompilerFlags, TargetPlatform.Windows) in gamestate.effect <- new Effect(graphicsDevice, compiledEffect.GetShaderCode(), CompilerOptions.None, null); gamestate.screenSize <- new Vector2( float32_of_int graphicsDevice.Viewport.Width, float32_of_int graphicsDevice.Viewport.Height); gamestate.viewMatrix <- Matrix.CreateLookAt(new Vector3(0.0f, 0.5f, 4.5f), Vector3.Zero, Vector3.Up); gamestate.projMatrix <- Matrix.CreatePerspectiveFieldOfView(fPI / 2.0f, gamestate.screenSize.X / gamestate.screenSize.Y, 0.1f, 1000.0f); override gamestate.Update() = let graphicsDevice = gamestate.GraphicsDevice() in let currentState = GamePad.GetState(PlayerIndex.One) in if currentState.IsConnected then let wo2 = gamestate.screenSize.X / 2.0f in let ho2 = gamestate.screenSize.Y / 2.0f in let sticks = currentState.ThumbSticks in gamestate.worldMatrix <- Matrix.CreateRotationY(fPI * sticks.Left.X) * Matrix.CreateRotationX(fPI * sticks.Left.Y) * Matrix.CreateTranslation(3.0f * sticks.Right.X, 3.0f * sticks.Right.Y, 0.0f); gamestate.prevPadState <- currentState; override gamestate.Draw() = let graphicsDevice = gamestate.GraphicsDevice() in let transform_matrix = gamestate.effect.Parameters.Item("WorldViewProj") in Idioms.using (new VertexDeclaration (graphicsDevice, VertexPositionColor.VertexElements)) (fun vertexType -> gamestate.effect.CurrentTechnique <- gamestate.effect.Techniques.Item(0); gamestate.effect.Begin(EffectStateOptions.Default); Idioms.foreach gamestate.effect.CurrentTechnique.Passes (fun (pass : EffectPass) -> pass.Begin(); graphicsDevice.VertexDeclaration <- vertexType; graphicsDevice.RenderState.CullMode <- CullMode.None; transform_matrix.SetValue(gamestate.worldMatrix * gamestate.viewMatrix * gamestate.projMatrix); gamestate.effect.CommitChanges(); graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, 12, gamestate.cube); pass.End(); ) ); gamestate.effect.End();
member gamestate.GraphicsDeviceService() : IGraphicsDeviceService = let (service : IGraphicsDeviceService) = gamestate.Game.GameServices.GetService() in service member gamestate.GraphicsDevice() : GraphicsDevice = let device = gamestate.GraphicsDeviceService().GraphicsDevice in device end
(*********************************************************************************) let main() = let game = new Game1(new GameComponent1(), Color.Black) in game.Run() [<STAThread>] do main()
|
|
-
|