-1
\$\begingroup\$

Because other people are using too complicated code for me to understand, I decided to ask a question from an XNA noob. So, I'm using the spaceship thing that Microsoft use in their tutorials of Going Beyond: XNA Game Studio in 3D, but I decided to create Asteroids (like in lesson 4) without the tutorials. Here's the code:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace WindowsGame2
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

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

    /// <summary>
    /// Allows the game to perform any initialization it needs to before starting to run.
    /// This is where it can query for any required services and load any non-graphic
    /// related content.  Calling base.Initialize will enumerate through any components
    /// and initialize them as well.
    /// </summary>
    protected override void Initialize()
    {
        // TODO: Add your initialization logic here

        base.Initialize();
    }

    /// <summary>
    /// LoadContent will be called once per game and is the place to load
    /// all of your content.
    /// </summary>
    Model myModel;
    SoundEffect soundEngine;
    SoundEffectInstance soundEngineInstance;
    SoundEffect soundHyperspaceActivation;


    float aspectRatio;

    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);

        myModel = Content.Load<Model>("Models\\p1_wedge");
        soundEngine = Content.Load<SoundEffect>("Audio\\Waves\\engine_2");
        soundEngineInstance = soundEngine.CreateInstance();
        soundHyperspaceActivation =
            Content.Load<SoundEffect>("Audio\\Waves\\hyperspace_activate");


        aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;
    }

    KeyboardState keyState;



    /// <summary>
    /// UnloadContent will be called once per game and is the place to unload
    /// all content.
    /// </summary>
    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>

    Vector3 modelVelocity = Vector3.Zero;


    protected override void Update(GameTime gameTime)
    {
        // Allows the game to exit
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        // Get some input.
        UpdateInput();

        // Add velocity to the current position.
        modelPosition += modelVelocity;

        // Bleed off velocity over time.
        modelVelocity *= 0.95f;



        base.Update(gameTime);
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>

    Vector3 modelPosition = Vector3.Zero;
    float modelRotation = 0.0f;

    Vector3 cameraPosition = new Vector3(0.0f, 50.0f, 5000.0f);

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

        Matrix[] transforms = new Matrix[myModel.Bones.Count];
        myModel.CopyAbsoluteBoneTransformsTo(transforms);

        foreach (ModelMesh mesh in myModel.Meshes)
        {
            foreach (BasicEffect effect in mesh.Effects)
            {
                effect.EnableDefaultLighting();
                effect.World = transforms[mesh.ParentBone.Index] *
                    Matrix.CreateRotationY(modelRotation)
                    * Matrix.CreateTranslation(modelPosition);
                effect.View = Matrix.CreateLookAt(cameraPosition,
                    Vector3.Zero, Vector3.Up);
                effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45.0f), aspectRatio,
                    1.0f, 10000.0f);
            }
                mesh.Draw();
        }

        base.Draw(gameTime);
    }

    Vector3 modelVelocityAdd = Vector3.Zero;

    protected void UpdateInput()
    {
        keyState = Keyboard.GetState();
        // Rotate the model using the left arrow button and scale it down
        if (keyState.IsKeyDown(Keys.Left))
            modelRotation += 0.10f;
        else if (keyState.IsKeyDown(Keys.Right))
            modelRotation -= 0.10f;
        else

        // Find out what direction we should be thrusting, 
        // using rotation.
        modelVelocityAdd.X = -(float)Math.Sin(modelRotation);
        modelVelocityAdd.Z = -(float)Math.Cos(modelRotation);

        // Now scale our direction by how hard the trigger is down.
        if (keyState.IsKeyDown(Keys.Up))
            modelVelocityAdd *= 1;
        else


            // Finally, add this vector to our velocity.
            modelVelocity += modelVelocityAdd;

        if (keyState.IsKeyUp(Keys.Up))
        {
            if (soundEngineInstance.State == SoundState.Stopped)
            {
                soundEngineInstance.Volume = 0.75f;
                soundEngineInstance.IsLooped = true;
                soundEngineInstance.Play();
            }
            else
                soundEngineInstance.Resume();
        }
        else if (keyState.IsKeyDown(Keys.Up))
        {
            if (soundEngineInstance.State == SoundState.Playing)
            soundEngineInstance.Pause();

        }
        // In case you get lost, press A to warp back to the center.
        if (keyState.IsKeyDown(Keys.Space))
        {
            modelPosition = Vector3.Zero;
            modelVelocity = Vector3.Zero;
            modelRotation = 0.0f;
            soundHyperspaceActivation.Play();
        }
    }
}

}

\$\endgroup\$
6
  • \$\begingroup\$ What exactly are you having difficulty understanding? Because the resources in the tutorial you mentioned and this page describing only the third person camera part: msdn.microsoft.com/en-us/library/… are pretty informative, and if they aren't helping you and we don't know why, there's not really anything we can do besides repeat what's in those articles. \$\endgroup\$ Commented Apr 21, 2016 at 22:38
  • \$\begingroup\$ Do you know basic trigonometry and linear algebra? \$\endgroup\$ Commented Apr 22, 2016 at 7:30
  • \$\begingroup\$ @Bálint I have no idea what trigonometry is and I'm very bad at algebra. \$\endgroup\$ Commented Apr 22, 2016 at 7:34
  • \$\begingroup\$ Then learn it, it's essential for game developement. Also, linear algebra (I hope I'm correct) is not the same as normal algebra, this is where matrices and vectors belong. Also, an essential part of game developement. \$\endgroup\$ Commented Apr 22, 2016 at 7:39
  • \$\begingroup\$ @Bálint If that's linear algebra (It is, I looked it up) then I know it. Vectors, matrices. And I guess Vector2 and Vector3 would be the two and three dimensional real coordinate space. \$\endgroup\$ Commented Apr 22, 2016 at 15:46

2 Answers 2

0
\$\begingroup\$

So, there are 2 solutions, one with trigonometry and one with linear algebra. Depends on you wich one you use.

I assume you have a vector for the player position (p with p.x, p.y and p.z as coordinates) and a rotation around the y axis (yAngle). Also, declare a distance from the player (d) and the angle between the camera and the ground (hAngle)

Trigonometry

pseudo code:

let x := cos(yAngle) * d + p.x
let y := cos(hAngle) * d + p.y
let z := sin(yAngle) * d + p.z

let cameraPos := Vector3(x, y, z) 

In C#:

float x = Math.Cos(yAngle) * d + p.x;
float y = Math.Cos(hAngle) * d + p.y;
float z = Math.Sin(yAngle) * d + p.z

Vector3 vec = new Vector3(x, y, z);

(I don't know C#, if you can't translate this to it, then I will try my hardest to do it)

Also, make sure the angles are in radians. Use cameraPos to place the camera somewhere.

If the camera looks in a weird direction, try adding/subtracting 0.5 * pi to/from the yAngle.

Linear algebra

With linear algebra, it's quite a bit harder, and slower, O post it here because it's possible.

First, create a rotation matrix with the same rotation around the yAngle as the player (yAngle), and the rotation around the x Axis (hAngle).

Then create a vector, with the z component of the distance between the player and the camera times -1. (Vector3(0, 0, -d)), and multiply it with the rotationMatrix.

You should receive the position for the camera.

(Seriously, this last method is slow as hell, don't use it)

\$\endgroup\$
3
  • \$\begingroup\$ Sorry, but I can't convert whatever that language is to C#, so... \$\endgroup\$ Commented Apr 22, 2016 at 18:34
  • \$\begingroup\$ cameraPos isn't a variable/field that's already been created by XNA, aswell. \$\endgroup\$ Commented Apr 22, 2016 at 18:36
  • \$\begingroup\$ @HarryTheProgrammer it's pseudo code, it's not a language. Of course it isn't you need to use it for the camera's position. \$\endgroup\$ Commented Apr 22, 2016 at 21:34
0
\$\begingroup\$

Here is a quick and easy way to implement a 3rd person camera within the code base of your project. Normally, I'd recommend placing these changes in the Update method instead of the Draw method but that would require a bit of refactoring of your code base that is beyond the scope of this question.

Change this section:

    Matrix[] transforms = new Matrix[myModel.Bones.Count];
    myModel.CopyAbsoluteBoneTransformsTo(transforms);

    foreach (ModelMesh mesh in myModel.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.EnableDefaultLighting();
            effect.World = transforms[mesh.ParentBone.Index] *
                Matrix.CreateRotationY(modelRotation)
                * Matrix.CreateTranslation(modelPosition);
            effect.View = Matrix.CreateLookAt(cameraPosition,
                Vector3.Zero, Vector3.Up);
            effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.ToRadians(45.0f), aspectRatio,
                1.0f, 10000.0f);
        }
            mesh.Draw();
    }

To this:

        Matrix[] transforms = new Matrix[myModel.Bones.Count];
        myModel.CopyAbsoluteBoneTransformsTo(transforms);

        Matrix modelWorld = Matrix.CreateRotationY(modelRotation) * Matrix.CreateTranslation(modelPosition);

       cameraPosition = modelPosition + (modelWorld.Backward * 10f);//set the distance (the value, 10f in this case) to whatever distance you want your camera to be behind your model. 
       Vector3 cameraTarget = modelPosition;//the camera is always looking at the model (and beyond) 
       Vector3 cameraUp = modelWorld.Up;// whatever is up for the model is up for the camera
       Matrix view = Matrix.CreateLookAt(cameraPosition, cameraTarget,cameraUp);

      foreach (ModelMesh mesh in myModel.Meshes)
        {
            foreach (BasicEffect effect in mesh.Effects)
            {
                effect.EnableDefaultLighting();
                effect.World = transforms[mesh.ParentBone.Index] *
                    modelWorld;//simplify this line.
                effect.View = view;//no need to make the thing twice...
                effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45.0f), aspectRatio,
                    1.0f, 10000.0f);
            }
                mesh.Draw();
        }
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.