5
\$\begingroup\$

I am trying to achieve a pseudo-3D camera effect similar to this:

Battle Rotation

I have gotten the following results using a 3D camera and billboards:

Battle Rotation Battle Rotation Battle Rotation

I am now running into the following problems:

  1. In the first billboard example, the size discrepancy between when the sprites are at the "front" and "back" is too great. I need to have them be more similar to the above.
  2. The bottom y-coordinate of the sprites never seem to change in my example, but they do (slightly) in the example that I want.
  3. I need to be able to control how close to the right and left edge of the screen the sprites go to.

In the first billboard example I setup my camera like so:

// (float fovxDegrees, float aspect, float znear, float zfar)
this.simpleCamera.Perspective(75f, 1.5f, 0.01f, 400);

// (Vector3 eye, Vector3 target, Vector3 up)
this.simpleCamera.LookAt(new Vector3(0, 5f, 10f), Vector3.Zero, Vector3.Up);

My sprites are positioned at the following locations:

new Vector3(-4f, 1f, 0f);
new Vector3(-4f, 1f, 1f);

Full CSharp code: http://pastebin.com/9G0FDNLs

Billboard HLSL: http://pastebin.com/uB8e3gXU

If you want the grid: http://pastebin.com/R5UCBNWt

\$\endgroup\$
  • \$\begingroup\$ this image is all you are trying to accomplish ? seems like 8 2D points moving on an ellipse and their scale is based of their Y position. you are over complicating this \$\endgroup\$ – dimitris93 Mar 3 '15 at 2:52
  • \$\begingroup\$ @Shiro No, this image is not all I want. I need help building a generic 3D battle world where I can render a ton of 2D sprites in 3D space with the ability to rotate around them. Battle effects, etc... \$\endgroup\$ – test Mar 3 '15 at 2:54
  • \$\begingroup\$ those old pseudo 3D games are done in 2D with "homemade" algorithms. apart from your question where I gave you a brief idea of how it can be implemented, (probably 10 but very well thought lines of code), what else you think would be hard to make in the pseudo 3D game you are trying to make ? \$\endgroup\$ – dimitris93 Mar 3 '15 at 3:02
  • \$\begingroup\$ @Shiro I need to be able to render battle effects and be able to rotate around those battle effects. Say for example a lava eruption on an enemy, where I then move the camera in behind the sprites to show them in the foreground and the lava in the background. I also need to have the scaling that happens with perspective. \$\endgroup\$ – test Mar 3 '15 at 3:06
  • \$\begingroup\$ It seems like you'd be best off just seeing your project as a 3d one. Use x,y, and z coordinates to position everything, create a camera, and draw billboarded sprites at the xyz-coordinates. To find out how to do all this, please refer to Riemer's XNA tutorials. This is how I've learned to do things like this, and I feel it's a good place to start. This one explains billboarding (a technique I think you should use): riemers.net/eng/Tutorials/XNA/Csharp/Series4/Billboarding.php \$\endgroup\$ – Peethor Mar 3 '15 at 10:02
3
\$\begingroup\$

You can do this in two ways, both of which require 3D cameras and projection:

First, give all of your objects a 3D position in the world.

// XYZ
Vector3 Position;

Next, create a camera with a position and orientation:

class Camera
{
    // Position of the camera in the world
    Vector3 Position;
    // Place where the camera is looking
    Vector3 Target;
    // Matrices representing the camera's parameters
    Matrix ViewMatrix;
    Matrix ProjectionMatrix;
}

You can update the camera's parameters like this:

void UpdateCameraParams()
{
  // Creates a view matrix from where the camera is looking and its position.
  ViewMatrix = Matrix.CreateLookAt(Position, Target, Vector3.Up);
  // Creates a perspective matrix with 1.2 radian FOV, 16:9 aspect ratio,
  // near plane of 0.01 units, and far plane of 10 units.
  ProjectionMatrix = 
        Matrix.CreatePerspectiveFOV(1.2f, 16.0f / 9.0f, 0.01f, 10.0f);
}

Now, the two ways of drawing.

SpriteBatch, the Hacky Way

You're probably familiar with SpriteBatch already. It's a way of drawing sprites to the screen in screen space. One thing you can do is simply project all of your objects onto the screen, scale them by their distance to the camera, and then render them. This is a hacky way of doing things, because you have to fake perspective and the depth buffer.

Here's how to do it:

SpriteBatch.Begin();

// Draw each entity in the scene
foreach(Entity thing in Scene)
{
    // Project its position onto the screen using the camera.
    Vector3 screenPos 
      = GraphicsDevice.Viewport.Project(thing.Position, camera.ProjectionMatrix,
                                        camera.ViewMatrix, Matrix.Identity);

    // Determine if its on the screen.
    bool isOnScreen = Viewport.Bounds.Contains((int)screenPos.X, (int)screenPos.Y);

    if(!isOnScreen) continue;

    // Compute the depth and scale of the object.
    float depth = screenPos.Z;
    float scale = 1.0f / depth;

    // Draw it at the appropriate depth and scale.
    SpriteBatch.Draw(thing.Image, new Vector2(screenPos.X, screenPos.Y),
                     thing.SourceRect, thing.Tint, thing.Rotation, thing.Origin,
                     new Vector2(scale, scale), thing.SpriteEffects, depth);

}

SpriteBatch.End();

Textured Billboards, the Correct Way

You can also just create for each sprite a physical 3D model, just a 3D billboard, oriented to face the camera. Then, you can render it like any other XNA 3D geometry. The depth buffer will work flawlessly, you won't have to guess the scale, and perspective will be taken care of correctly. I suggest this tutorial to get you started.

\$\endgroup\$
  • \$\begingroup\$ Thanks, I've actually started implemented the billboard method. I have been using this project as an example since it targets XNA 4.0 (whereas Reimer's targets 3.1). \$\endgroup\$ – test Mar 3 '15 at 18:35
  • \$\begingroup\$ If possible, can you look at my edit regarding the billboard technique? \$\endgroup\$ – test Mar 3 '15 at 20:54
  • \$\begingroup\$ Simply change the FOV of your camera. Larger FOV will result in less depth distortion. Smaller FOV results in more. \$\endgroup\$ – mklingen Mar 3 '15 at 21:26
  • \$\begingroup\$ Hmm... I must be missing some concept. If I go with a larger FOV that will make the objects very distant. So how would I create a larger FOV and then zoom(?) in I guess, to the now distant objects? \$\endgroup\$ – test Mar 3 '15 at 22:09
  • \$\begingroup\$ Yes, you need to increase the FOV and decrease the distance of the camera. \$\endgroup\$ – mklingen Mar 4 '15 at 0:16
1
\$\begingroup\$

I would agree that it would be best to go fully 3D billboard with this. You can do this without shaders (well, you would use the built in BasicEffect which is an already written shader for you).

  1. Move the camera away from the scene and decrease the FOV. here's why:

enter image description here When you move the camera close to the scene and widen the FOV, the close fighter (blue square) is taking up approx. 1/10 of the screen, but the farther fighter is only 1/15 to 1/20 of the screen which is drawn as a much smaller object. When moving back from the scene and narrowing the FOV, the close and far fighters appear about the same size with the farther one only slightly smaller instead of a lot smaller. Experiment to find the sweet spot.

  1. Raise the position of the camera a bit and point it down to keep scene centered. Experiment to find the sweet spot.

  2. Once the camera is looking at the center of the scene, the FOV and the camera distance from the scene will contribute to that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.