Creating a Simple 3D Game with XNA/Adding NPCs

From Wikibooks, open books for an open world
Jump to: navigation, search

Creating Other Fish[edit]

The simplest way to create other fish is to copy the model file and modify the local textures to it. In my case, I created a new folder in the 'Content'/'Models' folder called 'OtherFish', and modified the textures so that all of the textures are shades of orange instead of blue. Ensure the new texture files are the same name as the previous ones, and that the 'Content Processor' is set to 'SkinnedModelProcessor', and it will work.

Next up, create a new array of your usermade 'ModelRenderer' type called OtherFishes alongside your other class variables.

ModelRenderer[] OtherFishes = new ModelRenderer[16];

And initialise them by placing the following 'for' statement inside your LoadContent() method alongside the others.

//Initialises the NPC fish
currentModel = Content.Load<Model>("Models\\OtherFish\\FishModel");
for (int i = 0; i < OtherFishes.Length; i++)
{
    OtherFishes[i] = new ModelRenderer(currentModel);
    OtherFishes[i].Translation = new Vector3(0f);//Move it to the centre
}

In the same way as the original fish we created, we need to place a call to each of these fishes draw and update methods. Place in the following statements, this in alongside the 'FishMen' Update method call;

for (int i = 0; i < OtherFishes.Length; i++)
{
    OtherFishes[i].Update(gameTime);
}

And this alongside the it's Draw call.

for (int i = 0; i < OtherFishes.Length; i++)
{
    OtherFishes[i].ModelDraw(GraphicsDevice, cameraPosition, cameraTarget, farPlaneDistance);
}

This covers the routine aspects to displaying the fish.

Moving the Fish[edit]

Create a new method called 'ResetNPCFish' as below;

//Resets NPC fishes
void ResetNPCFish(int input)
{
    Random random = new Random();//Allows for the creation of random numbers
    Vector2 Boundarys = new Vector2(14f);//Defines the boundarys of where the fish start
 
    //Rotates the fish so they rotate towards the screen
    OtherFishes[input].Rotation.Y = (float)((Math.PI / 180.0) * 180);//Put simply, this will rotate it by 180 degrees
    OtherFishes[input].Rotation.Z = (float)((Math.PI / 180.0) * 180);
    //Moves the fish into a random position defined by the 'Boundarys' variable
    OtherFishes[input].Translation.X = random.Next((int)Boundarys.X * -1, (int)Boundarys.X);
    OtherFishes[input].Translation.Y = random.Next((int)Boundarys.Y * -1, (int)Boundarys.Y);
    //Moves the fish beyond the viewing horizon
    OtherFishes[input].Translation.Z = random.Next((int)farPlaneDistance + 100);
 
    OtherFishes[input].PlayAnimation("Frown");
}

And place a call to it inside your initialisaion routine as below.

//Initialises the NPC fish
currentModel = Content.Load<Model>("Models\\OtherFish\\FishModel");
for (int i = 0; i < OtherFishes.Length; i++)
{
    OtherFishes[i] = new ModelRenderer(currentModel);
    OtherFishes[i].Translation = new Vector3(0f);//Move it to the centre
    OtherFishes[i].PlayAnimation("Swim");//Play the default swimming animation
    ResetNPCFish(i);
}

If you run the game as it is, you should see 16 fish initialised swim past the controllable fish, and little else.

XNA Orange Fish.gif

To ensure that the fish swim indefinitely, add the following changes to the 'OtherFishes's update code, which will check the fishes position on the screen, and restart it if needed.

for (int i = 0; i < OtherFishes.Length; i++)
{
    float speed = 0.02f;
 
    OtherFishes[i].Translation.Z -= speed * (float)gameTime.ElapsedGameTime.TotalMilliseconds;
 
    if (OtherFishes[i].Translation.Z < -10f)
        ResetNPCFish(i);
 
    OtherFishes[i].Update(gameTime);
}

Run it now, and it will run indefinitely.

Hitting the Fish[edit]

Next up, we will place in code which will default the fish to displaying a frown, then when it is bumped into by the player, will change the fishes frowns upside down. Add the following line at the bottom of the 'ResetNPCFish' method.

OtherFishes[input].PlayAnimation("Frown");

To check a collision between the player and the enemy, create the following two new methods.

bool CheckCollision(Vector3 PlayerPosition, Vector3 NPCPosition)
{
    double DifferenceNeeded = 4;//Feel free to adjust, affects how close the NPC has to be before the expression changes
 
    double X = Difference(PlayerPosition.X, NPCPosition.X);
    X *= X;
    double Y = Difference(PlayerPosition.Y, NPCPosition.Y);
    Y *= Y;
    double Z = Difference(PlayerPosition.Z, NPCPosition.Z);
    Z *= Z;
    if (Math.Sqrt(X + Y + Z) < DifferenceNeeded)
        return true;
 
    return false;
}
 
float Difference(float int1, float int2)
{
    if (int1 > int2)
        return int1 - int2;
    else
        return int2 - int1;
}

And the following alongside the other pieces of update code.

if(CheckCollision(FishMen.Translation, OtherFishes[i].Translation))
    OtherFishes[i].PlayAnimation("Smile");

Play it now, and you should see the fish change when you touch them.