Getting started with SunBurn – part 3
At this point in the series, we have a tank and a piece of ground being drawn with some shadows. That’s fine if you’re making a 3D model viewer, but games usually have things that move around and animate. This time, let’s go ahead and animate the tank.
First, though, we need to update the code a little bit. I just upgraded from SunBurn 1.2.4 to 1.2.5 and there is one breaking change in the API. In the calls to SubmitRenderableObject where we used to specify a bool for whether or not to draw shadows, there is now an ObjectVisibility enum that we use to specify whether or not to render the object and whether or not it casts shadows. The purpose for this is that you could have something casting a shadow and not visible or visible and not casting a shadow. Or both of course.
So to update we first update our references (both the game and content project) to point at the new DLLs. Next we have to change a couple places in our code. First in our method for submitting the tank, we want to change that from this:
renderManager.SubmitRenderableObject(
Content.Load<Model>("tank"),
Matrix.Identity,
true,
ObjectLifeSpan.Scene);
To this:
renderManager.SubmitRenderableObject(
Content.Load<Model>("tank"),
Matrix.Identity,
ObjectVisibility.CastShadows | ObjectVisibility.Rendered,
ObjectLifeSpan.Scene);
Next we’ll need to update our call for submitting the terrain from this:
renderManager.SubmitRenderableObject( new BoundingSphere(Vector3.Zero, new Vector3(size, 0f, size).Length()), effect, indexBuffer, vertBuffer, new VertexDeclaration(GraphicsDevice, VertexPositionNormalTexture.VertexElements), 0, PrimitiveType.TriangleList, 2, 0, 4, 0, VertexPositionNormalTexture.SizeInBytes, Matrix.Identity, null, true, ObjectLifeSpan.Scene);
To this
renderManager.SubmitRenderableObject( new BoundingSphere(Vector3.Zero, new Vector3(size, 0f, size).Length()), effect, indexBuffer, vertBuffer, new VertexDeclaration(GraphicsDevice, VertexPositionNormalTexture.VertexElements), 0, PrimitiveType.TriangleList, 2, 0, 4, 0, VertexPositionNormalTexture.SizeInBytes, Matrix.Identity, null, ObjectVisibility.Rendered, ObjectLifeSpan.Scene);
As you can see, pretty minimal changes required to compile against the newest version. Now you can run the project with 1.2.5 and see the same thing we had at the end of the second article.
With that in place, let’s start animating our tank. First let’s do a basic animation: rotating the tank as a whole. This is a rather easy process. First let’s add a new field to our game class:
private Model tankModel;
Next we head down to LoadContent to load in the tank. This time, though, we aren’t going to submit it to the RenderManager, so delete this:
renderManager.SubmitRenderableObject(
Content.Load<Model>("tank"),
Matrix.Identity,
ObjectVisibility.CastShadows | ObjectVisibility.Rendered,
ObjectLifeSpan.Scene);
And replace it with just this:
tankModel = Content.Load<Model>("tank");
Next head all the way down to the Draw method where we’ll be inserting code. I’ll note here that all code we’re going to add will be after the check for the splash screen but before anything else. In other words, where this comment line is:
protected override void Draw(GameTime gameTime)
{
if (!SplashScreenGameComponent.DisplayComplete)
{
base.Draw(gameTime);
return;
}
// all of our new code in the Draw method will go here
sceneState.BeginFrameRendering(view, projection, gameTime, environment);
renderManager.BeginFrameRendering(sceneState);
renderManager.Render();
renderManager.EndFrameRendering();
sceneState.EndFrameRendering();
base.Draw(gameTime);
}
So with that in mind, insert this line into your Draw method:
renderManager.SubmitRenderableObject( tankModel, Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds), ObjectVisibility.Rendered | ObjectVisibility.CastShadows, ObjectLifeSpan.Frame);
That will submit the tank for rendering each frame and we’re using the total game time to create a rotational matrix we’ll use as the tank’s world matrix.
Last thing is to change our view matrix. Since we’re going to animate the tank, it’s probably OK to stick with a stationary camera. So remove the code we wrote for making the spinning view matrix from the Draw method and insert this into your LoadContent:
view = Matrix.CreateLookAt( new Vector3(-7f, 10f, 7f), Vector3.Zero, Vector3.Up);
That will give us a nice vantage point for viewing the tank. Now hit F5 and we’ll get a sweet spinning tank.
The dynamic shadows start being even more fun now that we have moving stuff. Let’s take it up a notch by animating the individual components of the tank. For this we’re going to go back and steal a file from that XNA Creator’s Club Simple Animation Sample, specifically the Tank.cs file. If you don’t want to go download it, here’s a copy of just that file as it exists in the sample: TankStart.cs.
Next we want to make a few changes to meet our needs since we’re using SunBurn. First up, let’s remove the boneTransforms field. Since SunBurn does our rendering, we don’t have to worry about that field at all. Along with that remove the initialization expression from the Load method.
Next we want to update the Draw method declaration. We no longer need the view or projection matrices, but we do need some other information such as render manager, visibility, and lifespan. So change this:
public void Draw(Matrix world, Matrix view, Matrix projection)
To this:
public void Draw( Matrix world, RenderManager renderManager, ObjectVisibility visibility, ObjectLifeSpan lifeSpan)
Next remove the line that sets the root transform of the tank since SunBurn handles that for us. This is the line to remove in case you weren’t sure:
tankModel.Root.Transform = world;
Lastly we remove the old drawing code:
// Look up combined bone matrices for the entire model.
tankModel.CopyAbsoluteBoneTransformsTo(boneTransforms);
// Draw the model.
foreach (ModelMesh mesh in tankModel.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.World = boneTransforms[mesh.ParentBone.Index];
effect.View = view;
effect.Projection = projection;
effect.EnableDefaultLighting();
}
mesh.Draw();
}
And replace that with a call to SubmitRenderableObject instead:
renderManager.SubmitRenderableObject(tankModel, world, visibility, lifeSpan);
And that’s that. If you’re feeling particularly lazy, here’s the final tank file: Tank.cs. I also went ahead and turned all the properties into public fields just to shorten it all up a bit.
Next let’s update our game to use this new class instead of our old Model. Remove the tankModel field and instead let’s put in a Tank object:
private readonly Tank tank = new Tank();
And of course then update our LoadContent method to call the tank.Load method instead of loading in the model:
tank.Load(Content);
And lastly we update our Draw method to call tank.Draw instead of submitting the object directly:
tank.Draw( Matrix.CreateRotationY((float)gameTime.TotalGameTime.TotalSeconds), renderManager, ObjectVisibility.CastShadows | ObjectVisibility.Rendered, ObjectLifeSpan.Frame);
Now go ahead and run the game. You should see the exact same thing as last time. That’s a good thing. Now let’s make things a bit more interesting. Insert the following lines before your call to tank.Draw:
tank.TurretRotation += (float)gameTime.ElapsedGameTime.TotalSeconds; tank.WheelRotation += (float)gameTime.ElapsedGameTime.TotalSeconds; tank.SteerRotation = (float)Math.Sin(gameTime.TotalGameTime.TotalSeconds * 2f) * .5f; tank.HatchRotation = (float)Math.Sin(gameTime.TotalGameTime.TotalSeconds * 3f) * MathHelper.PiOver2; tank.CannonRotation = (float)Math.Min(Math.Sin(gameTime.TotalGameTime.TotalSeconds) * MathHelper.Pi / 3f, 0f);
Now run the game again. You should see all sorts of crazy movement of the tank parts.
Note that all the shadows still work just as you’d expect without us having to worry. I know I keep hammering this in, but it’s worth pointing out that these are real, dynamic shadows SunBurn provides. It’s quite powerful and cool to watch.
One last little tweak. Let’s make our tank as a whole move around a little as well. Let’s update our drawing code a little bit to use a more elaborate world matrix:
Matrix world =
Matrix.CreateRotationY(-(float)gameTime.TotalGameTime.TotalSeconds * .125f) *
Matrix.CreateTranslation(
0f, 0f, (float)Math.Sin(gameTime.TotalGameTime.TotalSeconds));
tank.Draw(
world,
renderManager,
ObjectVisibility.CastShadows | ObjectVisibility.Rendered,
ObjectLifeSpan.Frame);
And now the tank spins and moves around a little, along with all the individual pieces rotating about.
As you can see it’s pretty easy to get mesh movement supported with SunBurn. It also shows how fun and easy it is to utilize some of the XNA Creator’s Club samples with SunBurn. Just a few changes and you’re good to go.
I think that’s pretty much where I’m going to end this “series”. I have lots more SunBurn related posts coming, for sure, but they’re likely going to be one-off posts about a specific technique or fun effect or something like that. These three posts were just to help kick start people with SunBurn. Check back for some of the fun stuff I have planned.
Download: BasicSunBurn_03.zip
Possibly Related Posts
(Automatically Generated)Split screen in SunBurn
Getting started with SunBurn – part 2
Getting started with SunBurn – part 1
Getting started with SunBurn – part 1
Extending SunBurn with physics




Thanks for the great tutorials. You should see if the Sunburn folks have an affiliate program, ‘cuz you just sold a copy
Thanks for the great tutorials. You should see if the Sunburn folks have an affiliate program, ‘cuz you just sold a copy
Awesome. I’m actually thinking on buying a license too. Which are you using? Because some versions lack some features.
Awesome. I’m actually thinking on buying a license too. Which are you using? Because some versions lack some features.
I’m using the Community version because I hope to use it to make a game for XBLIG. I’d say you could jump in to the $80 Indie version if you wanted to try it out. It lacks a few features but nothing major. Then if you like it you could jump to Pro (for commercial PC) or Community (for commercial PC and Xbox) and keep going. Not sure if they do a discounted upgrade price, but I’d assume if you asked about it someone could tell you.
I’m using the Community version because I hope to use it to make a game for XBLIG. I’d say you could jump in to the $80 Indie version if you wanted to try it out. It lacks a few features but nothing major. Then if you like it you could jump to Pro (for commercial PC) or Community (for commercial PC and Xbox) and keep going. Not sure if they do a discounted upgrade price, but I’d assume if you asked about it someone could tell you.
This is great tutorial.
With the new support for AA and Xbox with Deferred I chose that route but the only changes are with the LightingEffect becoming DeferredObjectEffect and the RenderManger becoming DeferredRenderManager and DeferredBufferManager for those who are interested (and of course changing your content processor).
Thanks again Nick. Look forward to reading your future posts.
This is great tutorial.
With the new support for AA and Xbox with Deferred I chose that route but the only changes are with the LightingEffect becoming DeferredObjectEffect and the RenderManger becoming DeferredRenderManager and DeferredBufferManager for those who are interested (and of course changing your content processor).
Thanks again Nick. Look forward to reading your future posts.
FYI: This part also works with the free Sunburn Framework. Thanks!
FYI: This part also works with the free Sunburn Framework. Thanks!