Getting Started with Ajaxna - Spinning 3D Cube

Coordinator
Nov 2, 2008 at 11:31 PM
Edited Nov 4, 2008 at 12:08 AM

This tutorial is deprecated. Please follow the "Tutorials" link from the Ajaxna home page

Do steps 1 to 10 of previous tutorial entitled, "Getting Started with Ajaxna - Hello World."

This will get you an aspx page with an instance of the AjaxnaControl on it, and the Ajaxna javascript references stored in a file called "_namespace.js".

Add the following at the end of the _namespace.js file:

$addNamespace("myApp");

You do this because you want to create your own namespace to work in this time.

Next, right click your project in Solution explorer and add a new file called "Game1.js". This will be a class that inherits from ajax.xna.Game, in a similar way to the real XNA.
 
Once the file is opened in the code editor, drag the "_namespace.js" file into the top of your new "Game1.js" file. Now, type in the following (after the reference):

/// <reference path="_namespace.js" />

$addNamespace("myApp");
$imports("ajax.geometry.Mesh");

myApp.Game1 = function(doc) 
{
    myApp.Game1.superConstructor.call(this, doc);
}
$extend("myApp.Game1", "ajax.xna.Game");
if ($getDesignMode()) myApp.Game1.prototype = ajax.xna.Game.prototype;

// Members
myApp.Game1.prototype._mesh = ajax.geometry.Mesh.prototype;

myApp.Game1.prototype.initialize = function()
{
    var Matrix = ajax.math.Matrix;
    this.backColour = ajax.drawing.Colour.Black;   
    // Call base
    myApp.Game1.superClass.initialize.call(this);
    
    // Setup Matrices
    this.graphicsDevice.transform.view = Matrix.translation(new ajax.math.Vector3(0, 0, 5));
    this.graphicsDevice.transform.world = Matrix.identity();    
    
    // load mesh
    this._mesh = ajax.geometry.Mesh.createBox(1, 1, 1, ajax.drawing.Colour.Green);
}

myApp.Game1.prototype.update = function(gameTime)
{
    gameTime = ajax.drawing.Timer.cast(gameTime);
    var Matrix = ajax.math.Matrix;
    var rotSpeed = 0.001 * gameTime.elapsedTime;
    this.graphicsDevice.transform.world.multiply(Matrix.rotateY(rotSpeed));
}

myApp.Game1.prototype.draw = function(gameTime)
{
    gameTime = ajax.drawing.Timer.cast(gameTime);
    
    this.graphicsDevice.clear(this.backColour);
    this._mesh.render(this.graphicsDevice);    
}

myApp.Game1.cast = function(obj)
{///<returns type="myApp.Game1" />
    return obj;
}



Next, add a new javascript file called "Program.js". Again drag in the _namespace.js file AND ALSO drag in "game1.js" (so you now have Two references), and type in the following code:
 

/// <reference path="_namespace.js" />

/// <reference path="Game1.js" />


$addApplication("myApp", ""); function main(e, r) { var game = $new("myApp.Game1", document); game = myApp.Game1.cast(game); game.showCentered(); game.run(); } $getEvent(window, window, "onload").addListener(main);

Next, go back to Default.aspx and select design or split mode. In the Smart Tag for AjaxnaControl, click on "Scripts...", then select "Add", then set the Path property for the new script reference to "Program.js". Click OK to close the dialog and save.

Press F5 to run the application.

You should now see a Spinning wirefame cube, with its backfaces being culled (not drawn as they are not needed).

If you want to render a solid cube instead, try adding this line in the "initialize" override:

this.graphicsDevice.renderState.fillMode = ajax.drawing.FillModes.Solid;

After doing that, you may want to try adding a light in the "onGraphicsCreated" override, something like this:

 

myApp.Game1.prototype.onGraphicsCreated = function(e, r)
{
    this.graphicsDevice.renderState.fillMode = ajax.drawing.FillModes.Solid;
    var light = new ajax.drawing.Light(
        ajax.drawing.LightTypes.Omni,
        new ajax.math.Vector3(-5, 5, -5),
        ajax.drawing.Colour.White,
        new ajax.drawing.Colour(20, 20, 20),
        true
    );
    this.graphicsDevice.lights[0] = light;
    this.graphicsDevice.renderState.lighting = true;
}



Now your cube is drawn as a solid, and is lit by a white Omnidirectional light source, eminating from world coordinate -5, -5, -5. Not too shabby :-)

Finally (Code Explained) 

The Game1 class shows how you  can write classes for Ajaxna. In this case we inherited from ajax.xna.Game. Because of this we called the superConstructor of the class, using our context (this) and passed through the "doc" variable. We knew what variables to pass because we can type "ajax.xna.Game(" and intellisense will tell us what params are needed.

The line: 
if ($getDesignMode()) myApp.Game1.prototype = ajax.xna.Game.prototype;
is there to enable us to get intellisense on most things, as we write our class - including the base class API.

We then declared the "_mesh" variable and set it to the prototype of the Mesh class. we could do this because we previously $import'ed that class. If we had not then we should have set that member to null.

We then overrode some of the ajax.xna.Game class methods: initialize, update and draw. Again we knew if we should call base, and what params to pass if we did because if we type in something like: "myApp.Game1.prototype.initialize(", intellisense tells us what we need to know.

In "initialize" we set the background color, setup our matrices (XNA'ers, forget the Projection Matrix for now), and loaded our mesh.

In "update" we just multiplied our World matrix by a rotation around the Y axis, based on the elapsed time since the last update.

In "draw" we just cleared the screen to our background color and rendered our mesh, using our graphics device.

You should also supply a static "cast" method, as shown.

Please note: I will be providing Visual Studio templates for all this in due course, and some are already checked into the source control area.

Program.js just mimicks the class of the same name created in C# to get our game running. It also contains the statement: $addApplication("myApp", ""); This tells Ajaxna to add your namespace as a top level application, thus enabling you to dynamically load your own classes.

Default.aspx just loads in Program.js during the initial rendering of the page, and then Program.js dynamically (by way of the $new method) loads in your "myApp.Game1" class and creates an instance of it, which it then shows and calls "run()" on, to start the game loop.