Welcome to MSDN Blogs Sign in | Join | Help

Nerd Herder

Dean Johnson’s blog about life on the XNA platform and tools team

Syndication

Custom Avatar Animations PLUS 21 New Animations

We have just posted three new avatar items to the XNA Creators Club Online site.

Custom Avatar Animation Sample
This sample shows users how to import and play custom avatar animations though the content pipeline. http://creators.xna.com/en-US/sample/customavataranimation

Avatar Animation Rig
We have provided both an Softimage Mod Tool 7.5 version and Maya 2008 version of the animation rig that can be used to create custom animations. http://creators.xna.com/en-US/utility/avataranimationrig Please take a look at the “readme” to understand how to load and export animations.

Avatar Animation Pack
This pack contains 21 new animations that users can use in their games. We provided them in FBX format for direct use in your games or in source format for both Softimage Mod Tool 7.5 and Maya 2008. http://creators.xna.com/en-US/utility/avataranimationpack Please take a look at the “readme” to understand how to load and export animations.

Enjoy

Posted Monday, July 20, 2009 9:15 PM by dejohn | 0 Comments

Simple example on how to load a signed in gamers avatar

Now that XNA Game Studio 3.1 has been released I have had time to notice the types of questions people are asking on the forums about avatars. One question that has come up a few times is how to load a signed in gamers avatar. There are a couple of pitfalls. One thing that can be confusing to developers is that a SignedInGamer from the Gamer.SignedInGamers collection may be null for the first few frames when the game loads. So when a developer tries to read the Avatar property of a signed in gamer they will receive a null reference exception. The developer needs to wait until the gamer is recognized as being signed in and then attempt to load the players avatar. You game should not just check for the signed in gamer in a tight loop. Your game should go about doing other things like updating input and rendering. Games also need to handle the case where the signed in gamer doesn't have an avatar.

There are a few ways to wait for the SignedInGamer to not be null and load the appropriate avatar. I will show a couple of methods. The first just polls and checks to see if the signed in gamer is not null and when it is not it loads the players avatar. If the player doesn't have an avatar a random one is created. Also if the system takes longer than 3 seconds for the gamer to sign then the code will load a random one instead. Below is an example of the first method.

public class SimpleAvatarGame : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;

    AvatarDescription avatarDescription;
    AvatarRenderer avatarRenderer;
    AvatarAnimation avatarAnimation;

    public SimpleAvatarGame()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";

        graphics.PreferMultiSampling = true;
        graphics.PreferredBackBufferWidth = 1280;
        graphics.PreferredBackBufferHeight = 720;

        Components.Add(new GamerServicesComponent(this)); 
    }

    protected override void LoadContent()
    {
        avatarAnimation = new AvatarAnimation(AvatarAnimationPreset.Celebrate);
    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        LoadUserAvatar(gameTime);

        // You may want to check that the avatar renderer has loaded
        // so that the animation does not start playing until it has loaded
        avatarAnimation.Update(gameTime.ElapsedGameTime, true);

        base.Update(gameTime);
    }

    /// <summary>
    /// Load player one's avatar
    /// </summary>
    private void LoadUserAvatar(GameTime gameTime)
    {
        // Avatar may already be loaded
        if (avatarRenderer != null)
            return;

        // Check to see if the user is signed in
        if (Gamer.SignedInGamers[PlayerIndex.One] != null)
        {
            // Get the users avatar description
            avatarDescription = Gamer.SignedInGamers[PlayerIndex.One].Avatar;

            // If this is not valid the user doen't have an avatar
            if (!avatarDescription.IsValid)
            {
                avatarDescription = AvatarDescription.CreateRandom();
            }

            avatarRenderer = new AvatarRenderer(avatarDescription);
        }
        // Check to see if it has been longer than 3 seconds
        else if (gameTime.TotalGameTime.TotalSeconds > 3)
        {
            avatarDescription = AvatarDescription.CreateRandom();
            avatarRenderer = new AvatarRenderer(avatarDescription);
        }
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        if (avatarRenderer != null)
        {
            avatarRenderer.World = Matrix.CreateRotationY(MathHelper.ToRadians(180.0f));
            avatarRenderer.View = Matrix.CreateLookAt(new Vector3(0, 1, 3), new Vector3(0, 1, 0), Vector3.Up);
avatarRenderer.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
GraphicsDevice.Viewport.AspectRatio, .01f, 200.0f); avatarRenderer.Draw(avatarAnimation.BoneTransforms, avatarAnimation.Expression); } base.Draw(gameTime); } }

Another technique for determining when the gamer has signed in and to read their Avatar property is to subscribe to the SignedInGamer.SignedIn event handler. Below is an example how to do this.

public class SimpleAvatarGame : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;

    AvatarDescription avatarDescription;
    AvatarRenderer avatarRenderer;
    AvatarAnimation avatarAnimation;

    public SimpleAvatarGame()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";

        graphics.PreferMultiSampling = true;
        graphics.PreferredBackBufferWidth = 1280;
        graphics.PreferredBackBufferHeight = 720;

        Components.Add(new GamerServicesComponent(this));

        SignedInGamer.SignedIn += new EventHandler<SignedInEventArgs>(LoadGamerAvatar); 
    }

    void LoadGamerAvatar(object sender, SignedInEventArgs e)
    {
        avatarDescription = e.Gamer.Avatar;

        // Check to see if the player has an avatar
        if (!avatarDescription.IsValid)
        {
            avatarDescription = AvatarDescription.CreateRandom();
        }

        avatarRenderer = new AvatarRenderer(avatarDescription);
    } 

    protected override void LoadContent()
    {
        avatarAnimation = new AvatarAnimation(AvatarAnimationPreset.Celebrate);
    }

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        // You may want to check that the avatar renderer has loaded
        // so that the animation does not start playing until it has loaded
        avatarAnimation.Update(gameTime.ElapsedGameTime, true);

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        if (avatarRenderer != null)
        {
            avatarRenderer.World = Matrix.CreateRotationY(MathHelper.ToRadians(180.0f));
avatarRenderer.View = Matrix.CreateLookAt(new Vector3(0, 1, 3), new Vector3(0, 1, 0), Vector3.Up); avatarRenderer.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
GraphicsDevice.Viewport.AspectRatio, .01f, 200.0f); avatarRenderer.Draw(avatarAnimation.BoneTransforms, avatarAnimation.Expression); } base.Draw(gameTime); } }

If you have any questions please ask on the forums here http://forums.xna.com

Posted Wednesday, June 24, 2009 9:27 PM by dejohn | 1 Comments

AvatarDescription.Description

In a couple of previous posts here and here I talked about the AvatarDescription.Description property that returns a byte[].

The AvatarDescription.Description property is provided to allow developers to recreate a previously created AvatarDescription object. The byte[] from the property is used in conjunction with the AvatarDescription(byte[] ) constructor to recreate the description object.

There are three main scenarios this enables.

Avatars in online games

When a player joins an online game their game sends their AvatarDescription.Description to all of the other players in the session. Each of the other players can then create the AvatarDescription object using the AvatarDescription(byte[] ) constructor.

Saving a players random avatar choice

You may want to allow a player to select some of the characters in their game. For example a user may pick party members in an RPG. The developer can continue to generate random AvatarDescription objects by calling AvatarDescription.CreateRandom(). The developer can then save the AvatarDescription.Description for the random avatars the player as selected along with other save game data. The data can then be read when the player loads the game so that they see the same characters they had in a previous game.

Loading a avatar designed by the developer

The final scenario allows the developer to design a specific avatar for use as a character in their game. This can be done by creating the desired look for the avatar in the Xbox 360 dash avatar editor. Then use an Xbox 360 XNA Game Studio game to load the AvatarDescription object from the SignedInGamer.Avatar property for the profile associated with the avatar you want to use. Put a breakpoint somewhere in your code after loading the AvatarDescription. Debug the code and put the AvatarDescription object you created in the watch window in Visual Studio.

clip_image002

Next you will need to copy the contents of the Description property and format the text. You can then use these values to construct a new byte[] that can be used to create the AvatarDescription in your game. You can either hard code this byte[] in your source code or save it to a file and create a content processor.

Posted Thursday, May 28, 2009 7:54 PM by dejohn | 3 Comments

AvatarDescription.IsValid… When & Why the description returned is invalid?

In previous posts I talked about how the AvatarDescription contains data to define what an avatar should look like so that the AvatarRenderer knows what assets to load in order to render the avatar. The AvatarDescription type contains a property called IsValid which is a read only bool. So what does this property tell the developer? It tells the developer if the description is valid of course! So when can a developer expect a description to be valid and invalid?

First let’s go over the different ways a user can create an AvatarDescription object.

Gamer.SignedInGamers[PlayerIndex.One].Avatar – Returns the avatar description from a gamers profile.

AvatarDescription.CreateRandom() – Returns a randomly created avatar description.

AvatarDescription(byte[] data) – Constructor that allows the developer to construct a description from a byte[] that was returned from the AvatarDescription.Description property of an already created AvatarDescription object. A future post will talk more about this constructor and why it is useful.

Now for the rules on when descriptions are valid vs. invalid.

· On Windows the profile and random avatar descriptions will return as invalid descriptions because Windows does not support avatars.

· On Xbox the random avatar description will return valid descriptions.

· On Xbox the profile avatar description can return both valid and invalid descriptions. If a user does not have an avatar associated with their profile then the avatar description returned will be invalid.

Not too confusing right?

So how do invalid AvatarDescriptions behave?

The properties on the AvatarDescription such as the Height, BodyType, and Description return default values. IsValid returns false as you would expect.

So what happens if you use an invalid AvatarDescription to create an AvatarRenderer? Does it throw an exception? No. You will get back an AvatarRenderer object. You can do everything that you would do normally with an AvatarRenderer created with a valid AvatarDescription. The difference is when you call draw nothing will be displayed to the screen. This was done so that while you develop your game on Windows you will not need to wrap all of your AvatarRenderer constructor or draw calls in try/catch statements. While the avatar will not draw on Windows there is also a chance that the users profile on the Xbox will also be invalid. The developer should detect that the game can’t display an avatar and do something appropriate. For example the developer may create a stand in model to use when a user doesn’t have a valid avatar description.

Posted Wednesday, May 20, 2009 9:51 PM by dejohn | 4 Comments

Lights, Camera… Avatar!

Sometimes you may not want your avatar flawlessly lit from above. What if your game takes place at night? What if the sun in your game is green? What if you need the light to move?

The Avatar APIs expose an ambient light and one directional light. The AvatarRenderer class has properties for AmbientLightColor, LightDirection, and LightColor. All are of the type Vector3.

The APIs also allow the developer to over saturate these values. This allows for some cool lighting effects like the one pictured below. I used the following code to product the results below.

avatarRenderer.LightDirection = new Vector3(1, 1, 0);
avatarRenderer.LightColor = new Vector3(0, 10, 0);
avatarRenderer.AmbientLightColor = new Vector3(1, 0, 0); 

clip_image002

So what happens if you set all of the values to zero? You should get a completely black area in the shape of your avatar correct?

avatarRenderer.LightDirection = new Vector3(0, 0, 0);
avatarRenderer.LightColor = new Vector3(0, 0, 0);
avatarRenderer.AmbientLightColor = new Vector3(0, 0, 0); 

clip_image004

Dark but not totally black. The avatar system uses a rim light also called a back light to create an edge around the avatar. But it is still possible to make the avatar completely black. Just set the ambient values all to a negative number. This will subtract from the final lighting values.

avatarRenderer.LightDirection = new Vector3(0, 0, 0);
avatarRenderer.LightColor = new Vector3(0, 0, 0);
avatarRenderer.AmbientLightColor = new Vector3(-1, -1, -1);

clip_image006

As you can see from the picture above there is still a small amount of light on the avatars shoes. This can be removed with a larger negative number for the ambient light value.

Posted Monday, May 18, 2009 10:57 PM by dejohn | 1 Comments

Avatar animation updates

Playback of one of the 31 built in animations is simple with the AvatarAnimation class. As I discussed in a previous post the BoneTransforms and Expression properties on the AvatarAnimation can be used with the AvatarRenderer.Draw method to animate an avatar. These two properties return the bone transforms and expression at a time in the animation based on the CurrentPosition property. CurrentPosition is a TimeSpan value that represents where in the animation playback is currently located. Valid values for CurrentPosition can be from zero to AvatarAnimation.Length.

There are two ways to set the CurrentPosition. The first and indirect way is to use the AvatarAnimation.Update(TimeSpan elapsedAnimationTime, bool loop) method that was used in the previous post. This allows the developer to specify the amount of time that has passed since the last update call. This delta can be provided by the gameTime.ElapsedGameTime parameter in the Game.Update method. It also allows the developer to specify if they want the animation to loop or not. The developer can also specify a negative elapsedAnimationTime value to play the animation backwards.

The other way is to use the CurrentPosition property directly. This allows the developer to implement any type of playback that they wish. While this is flexible it leaves managing looping and bounds checking to the developer.

Most developers will most likely use the Update method but the CurrentPosition property is exposed to allow for greater animation playback flexibility.

Posted Thursday, May 14, 2009 8:40 PM by dejohn | 3 Comments

Filed under: ,

A few design changes

I made a few layout changes to the blog today to allow for more room for the text on the site and more specifically the code samples. If the code blocks or images are too wide please comment and let me know. I went back and updated a couple of my last posts to make the code more readable now there is more space.

Posted Tuesday, May 12, 2009 11:31 PM by dejohn | 1 Comments

What to do while an avatar loads?

When you create a new AvatarRenderer object load all of the models and textures associated with the AvatarDescription have to be loaded. The time it takes to load goes up if you are creating a number of AvatarRenderer objects at the same time.

So what do developers need to do while the avatar loads?

In most cases nothing! Just call the avatar draw method as if the avatar was already loaded. By default we will draw a loading effect in place of the avatar while it loads. Once the avatar has completed loading the loading effect will fade out to reveal the avatar.

If you want to determine if the avatar has loaded fully you can use the AvatarRenderer.IsLoaded property.

If you don’t want to use the loading effect you can use the AvatarRenderer constructor overload below.

AvatarRenderer(AvatarDescription avatarDescription, bool useLoadingEffect);

So what does this loading effect look like? The loading effect is made up of a number of transparent quads that display an animating texture.

clip_image001

Posted Monday, May 11, 2009 7:08 PM by dejohn | 5 Comments

Filed under: ,

Avatar API preview for XNA Game Studio 3.1

Since the announcement of the 3.1 features a number of people have had questions about the new Avatar API’s. I wanted to take a few minutes to preview some of Avatar API’s to help developers better understand what to expect in the 3.1 release.

There are three main classes that are used to render and animate an avatar.

AvatarDescription
Each avatar has a description that describes how the avatar looks and what clothing the avatar has on. This description tells the system what models, textures, colors, and scales to use when rendering an avatar. This description is mostly opaque to the developer but does allow for the developer to get the height and body type of the avatar. The height is in meters and the body type allows the developer to know if the avatar is female or male. The AvatarDescription object can be created in three ways. The first way is to read the Avatar property on any of the SingedInGamers. The second is to use the static methods on AvatarDescription to create random descriptions. The third is to send an AvatarDescription over the network or save it to a file by looking up a byte array from the AvatarDescription, transferring those bytes, and then creating a new AvatarDescription from the byte array. I will create a future post to talk more about the third method.

AvatarRenderer
The AvatarRenderer is used to draw an avatar to the screen. You create a new AvatarRenderer by passing in an AvatarDescription object. To draw the avatar to the screen you set the World, View, and Projection matrix properties on the AvatarRenderer object and then call the Draw method. The draw method takes two parameters. The first is an IList<Matrix> called bones. This is a list of the bone transforms to use when rendering the avatar. The transforms are in local bone space relative to their parent. The avatar has a total of 71 bones. The second parameter is an AvatarExpression. Avatars use textures for the mouth, eyes, and eyebrows. The AvatarExpression is a structure that holds enum values that specify the current state of the eyes, eyebrows, and mouth. Now you may be asking where you get the list of bone matrices and the avatar expression. The easiest way is to use the AvatarAnimaion class.

AvatarAnimation
There are 31 built in animations that developers can use in their games. Most of these animations are used in the dash. An AvatarAnimation object is created by passing an enum value from AvatarAnimationPreset. Once created the developer updates the animations current time. After setting the current time in the animation the developer can access the bones and expression needed by the AvatarRednerer.Draw call by using the BoneTransforms and Expression properties.

Now let’s check out how to code this up.

In the games initialize method load the description, renderer, and animation.

AvatarDescription avatarDescription = AvatarDescription.CreateRandom();
AvatarRenderer avatarRenderer = new AvatarRenderer(avatarDescription);
AvatarAnimation avatarAnimation = new AvatarAnimation(AvatarAnimationPreset.Clap);

Now in the games update method update the animation.

avatarAnimation.Update(gameTime.ElapsedGameTime, true);

Finally in the draw method set the world, view, and projection matrix and call the draw method.

avatarRenderer.World = Matrix.CreateRotationY(MathHelper.ToRadians(180.0f));
avatarRenderer.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
GraphicsDevice.Viewport.AspectRatio,
.01f, 200.0f); avatarRenderer.View = Matrix.CreateLookAt(new Vector3(0, 1, 3),
new Vector3(0, 1, 0),
Vector3.Up); avatarRenderer.Draw(avatarAnimation.BoneTransforms, avatarAnimation.Expression);

All this talk about how to render an avatar without a picture would be wrong. So below is one of the first screen shots taken while working on this feature.

clip_image001

Posted Friday, May 08, 2009 11:19 PM by dejohn | 9 Comments

Filed under: ,

How to set the effect (.fx) used on Model

When you use the model processor build into the XNA Framework Content Pipeline you will notice that each ModelMeshPart on a Model has an Effect already loaded. By default this is an instance of BasicEffect with the models textures and other material properties already set. This is great when you want to get a model loaded and rendering quick but sometimes you want to set a custom effect that you have previously created.

To help show users how to load a custom effect file on a model we created a sample called Custom Model Effect found here. The sample sets the new effect file in a custom content processor before saving the model out into an .xnb file. In this post I hope to show how to can extend this idea to allow you to create a custom processor that will accept a processor parameter for the effect file. This will allow you to reuse the same processor for many of your models even if they use separate effect files.

The first step is to create a new Content Pipeline Extension Library project if you do not already have one setup for your project. You will also need to setup a reference to the new project in your content project. The XNA Docs have some great resources that show you how to do this.

Next you will need to overwrite the standard model processor.

        [ContentProcessor]
        public class CustomEffectModelProcessor : ModelProcessor

Your new class needs a processor parameter that will take in your new effect file name.

        [DisplayName("Custom Effect")]
        [DefaultValue("")]
        [Description("The custom effect applied to the model.")]
        public string CustomEffect
        {
            get { return customEffect; }
            set { customEffect = value; }
        }
        private string customEffect;

You will need to add a using statement for System.ComponentModel for the new parameter.

Next you will need to override the ConvertMaterial method of the standard model processor so we can call our new material processor. Also notice that we pass along the processor parameter we defined above.

protected override MaterialContent ConvertMaterial(MaterialContent material, ContentProcessorContext context)
{
    OpaqueDataDictionary processorParameters = new OpaqueDataDictionar();
    processorParameters.Add("CustomEffect", customEffect);

    return context.Convert<MaterialContent, MaterialContent>(material, "CustomEffectMaterialProcessor", processorParameters);
}

Now our new model processor will be looking for a material processor called "CustomEffectMaterialProcessor". The next step is to create this new material processor.

[ContentProcessor]
class CustomEffectMaterialProcessor : MaterialProcessor

Our material processor needs two parts. The first is the same processor parameter as we created above for the model processor to accept the filename of the custom effect file. The other is an override of the Process method.

public override MaterialContent Process(MaterialContent input, ContentProcessorContext context)

In the process method we need to create the new EffectMaterialContent and load the custom effect from the file.

// Create a new effect material.
EffectMaterialContent customMaterial = new EffectMaterialContent();

// Point the new material at the custom effect file.
string effectFile = Path.GetFullPath(customEffect);
customMaterial.Effect = new ExternalReference<EffectContent>(effectFile);

// Loop over the textures in the current material adding them to the new material.
foreach (KeyValuePair<string, ExternalReference<TextureContent>> textureContent in input.Textures)
{
    customMaterial.Textures.Add(textureContent.Key, textureContent.Value);
}

// Loop over the opaque data in the current material adding them to the new material.
foreach (KeyValuePair<string, Object> opaqueData in input.OpaqueData)
{
    customMaterial.OpaqueData.Add(opaqueData.Key, opaqueData.Value);
}

// Call the base material processor to continue the rest of the processing.
return base.Process(customMaterial, context);

Now all you will need to do is set the Content Processor of your model to your new CustomEffectModelProcessor and set the Custom Effect to the effect file that you wish to use.

image

Posted Sunday, June 01, 2008 10:24 PM by dejohn | 1 Comments

Community Games and the new Creators Club Online Website

It has been over a year since my last blog post. As one colleague put it I was starting to get “Blogger Guilt”. The last post was all about the new Creators Club Online website. Well it turns out this post is about the same topic but much better. The original site did a great job at delivering information and educational content to our users.

The new site does that all a lot more. It is still home to the XNA forums and a bunch of great XNA educational content but it is also now home to the Community Games on Xbox LIVE Beta. Creators now have the ability to submit, peer review, and play community games. Go check out the new site at http://creators.xna.com for more information.

clip_image002

Posted Thursday, May 29, 2008 6:18 AM by dejohn | 0 Comments

Filed under: ,

XNA Creators Club Online

The new XNA Creators Club Online site went live today. You can view the site by visiting http://creators.xna.com.

Posted Monday, March 05, 2007 9:50 PM by dejohn | 1 Comments

Filed under: ,

You Spin Me Round

Often when working with 3D objects like characters and space ships you will need to point one object at the other. In this post we will cover two ways you can point objects at each other. The first way creates a matrix to turn your object towards the other. The second covers a way to determine the direction your object needs to turn.

Before we start we will cover some vector operations or what they represent. So here are a couple of the operations we will be using.

The vector cross product of X and Y creates a third vector Z that is orthogonal to both of them. Here are two resources for further information.

http://mathworld.wolfram.com/CrossProduct.html

http://en.wikipedia.org/wiki/Cross_product

The vector dot product represents a couple of things. Geometrically it represents the projection of X onto the unit length vector of Y. So you get the length of the X vector in the Y direction. This will be very useful for us later.

http://mathworld.wolfram.com/DotProduct.html

http://en.wikipedia.org/wiki/Dot_product

In order to turn the character towards another object (in this case, a sphere), we first create a vector between the two objects. Because we are in a right handed coordinate system we will be using –Z as our forward direction. To get the new forward vector of our object we need to take our position and subtract the position of the object.

characterLocalMatrix.Forward = characterLocalMatrix.Translation 
- spherePosition;

Now we need to normalize the forward vector because we want our local matrix to be orthonormalized. If the object had been scaled this would be a good time to add the scale back to the object by multiplying the forward vector by the objects scale in the Z direction.

characterLocalMatrix.Forward = Vector3.Normalize(
characterLocalMatrix.Forward);

Next we need to create a new right vector for the object. To do this we are going to cross the new forward vector with the world up vector. We do this because we don’t yet know what the up vector is going to be so we will come back later and fix the up vector. Note that if your objects have abnormal orientations this will not work because it assumes that world up is close to your eventual up vector. The new right vector also needs to be normalized.

characterLocalMatrix.Right = Vector3.Cross(characterLocalMatrix.Forward, 
Vector3.Up); characterLocalMatrix.Right = Vector3.Normalize(characterLocalMatrix.Right);

Finally we need to create our up vector by crossing the new right and forward vectors.

characterLocalMatrix.Up = Vector3.Cross(characterLocalMatrix.Right, 
characterLocalMatrix.Forward); characterLocalMatrix.Up = Vector3.Normalize(characterLocalMatrix.Up);

Our characters local matrix is now ready to use. If you are using the BasicEffect your draw code will look something like this.

effect.World = characterLocalMatrix;
effect.View = viewMatrix;
effect.Projection = projectionMatrix;

Now that you know how to turn directly towards another object, what happens when you want to know what direction your AI should turn in order to face you? We need a way to determine if it is better to turn left or right. We will use the dot product to determine if we should turn left or right. Like the last example the first thing we need to do is to create a vector to the target. We also want the vector to be unit length so we also normalize the vector.

Vector3 turnAt = spherePosition - characterLocalMatrix.Translation;
turnAt.Normalize();

Next we calculate the dot product with our right vector. You might wonder why the right vector when we are trying to turn our forward vector. The reason is because of the behavior of the dot product. By using the right vector the result of the dot product with the turnAt vector will result in the projection of the turnAt vector in the right direction. This will get of the length of the turnAt in the right direction. If the result is positive it means we should turn left and if the result is negative we should turn right.

// Determine direction we need to turn using right vector
float dotResult = Vector3.Dot(turnAt, characterLocalMatrix.Right);
// Turn left
if (dotResult > 0.1f)
{
    characterLocalMatrix *= Matrix.CreateRotationY(turnSpeed * 
gameTime.ElapsedGameTime.Milliseconds); } // Turn right else if (dotResult < -0.1f) { characterLocalMatrix *= Matrix.CreateRotationY(-turnSpeed *
gameTime.ElapsedGameTime.Milliseconds); }

You will notice that we don’t check dotResult with 0. This is because the character will turn past the object and then turn the other direction. This cycle will go back and forth causing the character to shake. So this technique uses a dead zone. Another option if to turn directly at the target when you get in this zone.

Well that’s the end of my first post. While the information here was geared towards people new to 3D graphics I hope it helped everyone.

Posted Monday, January 15, 2007 11:05 PM by dejohn | 8 Comments

Filed under: , ,

Page view tracker