3
\$\begingroup\$

I have two issues which I wish to solve by creating 2D primitives in XNA.

  1. In my game, I wish to have a "debug mode" which will draw a red box around all hitboxes in the game (Red outline, transparent inside). This would allow us to see where the hitboxes are being drawn AND still have the sprite graphics being drawn.

  2. I wish to further understand how matrices work within computer graphics. I have a basic theoretical grasp of how they work, but I really just want to apply some of my knowledge or find a good tutorial on it. To do this, I wish to draw my own 2D primitives (With Vertex3's) and apply different transormation matrices to them.

I was trying to find a tutorial on drawing primitives using Direct3D, but most tutorials are only for c++, and just tell me to use XNA's Spritebatch. I wish to have more control over my program than just with Spritebatch.

Any Help on using Direct3D or any other suggestions would greatly be appreciated.

Thank you.

\$\endgroup\$
3
  • \$\begingroup\$ I don't see a question nor can I tell what you are trying to accomplish. If you simply want a wire frame of all your hit boxes that is very easy but I'm not sure what your goal is. \$\endgroup\$ Commented Nov 20, 2011 at 23:01
  • \$\begingroup\$ I'm trying to wireframe my hitboxes, yes. However, i'm trying to do that by drawing primitive shapes, which im unsure of how to do. If I can figure out how to draw primitive shapes, I can both draw my hitboxes wireframes AND start applying different kinds of matrix translations to them (to mess around with) \$\endgroup\$
    – MintyAnt
    Commented Nov 21, 2011 at 3:34
  • \$\begingroup\$ You are probably looking for DrawUserIndexedPrimitives in conjunction with LineList. \$\endgroup\$ Commented Nov 22, 2011 at 10:19

5 Answers 5

2
\$\begingroup\$

If you are dead-set on using Matrix math and wanting a wireframe hitbox, DrawUserIndexedPrimitives is probably the way to go. (As Jonathan said)

However, the whole point of a debug mode is to be quick, with little overhead, is it not? I wish to suggest an alternative that I personally use in my game:

public void CollisionRectToDraw(GameObject G, int RedValue, int GreenValue, int BlueValue)
    {
        SpriteBatch.Draw(Pixel, new Rectangle((int)G.position.X - (int)G.center.X, (int)G.position.Y - (int)G.center.Y, G.sprite.Width, G.sprite.Height),
            new Color((byte)RedValue, (byte)GreenValue, (byte)BlueValue, (byte)120));
    }

Now, this will stretch a single-pixel Texture2D (that's a white pixel) to the size of your sprite's width and height. It will be solid, however. If you want to only draw the wire-frame of it (which may take more time) you simply need to make the stretched rectangle slightly larger, and use a Rectangular Region (which would be your Sprite.Position.X, Sprite.Position.Y, Sprite.Width, Sprite.Height) to "cut out" the part that overlaps your sprite.

This method is both quick, and accurate. While not as pretty as what I'm sure you're imagining in your head, it gets the job done and could be much less expensive than actually having to drawing many LineLists.


EDIT:

This will draw four lines as Drackir suggested.

public void CollisionWireFrameToDraw(Gameobject G, int LineThickness, int RedValue, int GreenValue, int BlueValue)
    {
        Rectangle ObjectRect = new Rectangle((int)G.position.X - (int)G.center.X, (int)G.position.Y - (int)G.center.Y, G.sprite.Width, G.sprite.Height);

        game.SpriteBatch.Draw(Pixel, new Rectangle(ObjectRect.Left - LineThickness, ObjectRect.Y,LineThickness,ObjectRect.Height),
            new Color((byte)RedValue, (byte)GreenValue, (byte)BlueValue, (byte)255));//This is the line on the Left

        game.SpriteBatch.Draw(Pixel, new Rectangle(ObjectRect.Right, ObjectRect.Y, LineThickness, ObjectRect.Height),
            new Color((byte)RedValue, (byte)GreenValue, (byte)BlueValue, (byte)255)); //This is the line on the Right

        game.SpriteBatch.Draw(Pixel, new Rectangle(ObjectRect.X, ObjectRect.Top - LineThickness, ObjectRect.Width, LineThickness),
            new Color((byte)RedValue, (byte)GreenValue, (byte)BlueValue, (byte)255)); //This is the line on the Top

        game.SpriteBatch.Draw(Pixel, new Rectangle(ObjectRect.X, ObjectRect.Bottom, ObjectRect.Width, LineThickness),
            new Color((byte)RedValue, (byte)GreenValue, (byte)BlueValue, (byte)255)); //This is the line on the Bottom

    }

This draws a line on each of the four sides (left, right, top, bottom). You can change the thickness of the line by passing in a larger (or smaller) LineThickness.

The important thing to note here is that on the Left and Top line, you have to subtract the LineThickness from it's position. (X for Left, Y for Top).

But why?

Because if you don't, the line will "creep" on top of your source sprite. By subtracting your LineThickness, you move the line back out (by offsetting it, so the width of the line doesn't widen onto the sprite.

One thing to note here is that you will have a gap in the four corners where the lines don't meet up. You simply have to choose two lines (Left and Right, or Top and Bottom) and add an offset to them to make it a complete Wire Frame. If you need help with that, I can update my code - but I thought it would be fun for you to solve.

\$\endgroup\$
8
  • 1
    \$\begingroup\$ You could also draw four lines using the same technique. \$\endgroup\$ Commented Nov 28, 2011 at 17:14
  • \$\begingroup\$ @Drackir Updated my answer, thanks for the suggestion! I hadn't thought of using it like that. \$\endgroup\$ Commented Nov 28, 2011 at 21:03
  • \$\begingroup\$ You claim that SpriteBatch is going to be less overhead than drawing user indexed lines. This is ridiculous. Drawing a colored line doesn't do any alpha blending or color interpolation, using SpriteBatch obviously does. Also, SpriteBatch is drawing textured quads. This is texture interpolation, AND it's occurring even for pixels that never get drawn. Using a simple primitive drawer has MUCH less overhead. \$\endgroup\$
    – cwharris
    Commented Nov 28, 2011 at 22:31
  • \$\begingroup\$ Also, since when is drawing 4 textured quads (16 vertices and 24 indicies) faster than drawing 4 line segments (4 vertices and 8 indicies) ? \$\endgroup\$
    – cwharris
    Commented Nov 28, 2011 at 22:33
  • \$\begingroup\$ @xixonia I have this very code in my game, so I can say with 100% certainty that there is little to no overhead with this method. The single-pixel sprite is assigned and allocated when the game does it's first initial load. All I'm doing while the game is running is scaling it, coloring it, and drawing it. Your method below may prove to be faster, but I went with what I know will work and what will be fast 100% of the time. I'm not saying that your method won't be fast, but I posted an easy-to-implement solution, that is fast with most, if not all, of it's initial allocations upfront. \$\endgroup\$ Commented Nov 28, 2011 at 23:19
0
\$\begingroup\$

Debug Drawing

My suggestion is to create a simple "DebugDraw" class with a few methods for drawing lines, circles, and convex polygons. You can have two flavors of these methods: One for drawing outlines and the other for drawing solids.

The solid shapes would be stored in one set of vertex and index lists, and the outline shapes would be stored in the second.

All shapes should be converted to either line segments or triangles (depending on outline vs solid), and stored in the appropriate vertex or index list. When the lists get to their maximum size, automatically draw them using the BasicEffect with vertex coloring turned on.

I would use VertexPositionColor for the vertices, and I would allow the color of each shape to be specified when drawing ("myDebugDraw.DrawCircle(24, 128, 255, 0);" - where 24 is the number of sides).

I'll try to post an actual class later.

Matricies

I don't have any matrix resources, other than my math books. :)

I will say this though... Unless your doing something complex with matricies that no-one has thought of yet, as a game designer, you should probably just look at matrices as "black boxes"... It doesn't matter how they work, it just matters that you know when you transform a certain vector by a certain matrix, you get a certain result. Take a world space vector, multiply it by a view and projection matrix, and magically it's turned into a screen space vector. What more do you need to know?

\$\endgroup\$
0
\$\begingroup\$

If you are still looking for a simple way to draw shapes check this out:

http://drippingflames.com/xna/

\$\endgroup\$
0
\$\begingroup\$

I also use SpriteBatch to render 2D "hitboxes"; supply values for "_" variables.

Rectangle rectangle = _absoluteBounds;
if (_drawBorder)
{
    SpriteBatch.Draw(WhiteTexture, rectangle, null, _borderColor, _rotation, Vector2.Zero, SpriteEffects.None, 0.0f);
    rectangle.Inflate(-2, -2); //<=== border thickness
}
if (_texture == null) SpriteBatch.Draw(WhiteTexture, rectangle, null, _backColor, _rotation, Vector2.Zero, SpriteEffects.None, 0.0f);
else SpriteBatch.Draw(_texture, rectangle, null, _backColor, _rotation, Vector2.Zero, SpriteEffects.None, 0.0f);

-WhiteTexture is a 1x1 Texture2D

1) Fills _absoluteBounds with WhiteTexture using _borderColor for the tint

2) Shrinks rectangle by border width

3) Fills new rectangle with either the face texture, if supplied (scaled to fit in the shrunken rectangle), or WhiteTexture using _backColor for the tint

Swapping a repeating sequence of textures for _texture can produce a few basic/dirty effects, like the border lighting up and scrolling while the hitbox is active/hovered.

Using a custom Effect with SpriteBatch enables further bling.

\$\endgroup\$
0
\$\begingroup\$

Extensive research on matrix math is often quoted and, probably, gets mostly ignored. You might read and read and read and still never get it. The important thing is that XNA/DirectX/video cards DO get it. Rather than learning how and why they work, you need to learn how and when to use them. In graphic programming, Matrices are primarily used to move "Object Space" coordinates to "Screen Space".

It is important to note that this IS math so standard order-of-operations applies with a few extras. In any space, matrix transforms are performed around the resulting coordinate system's (0, 0, 0), not the source's. If you move a coordinate away from (0, 0, 0), then scale the coordinate, it will move even further away. Usually, you want to scale first, then translate(move).

Coordinate * Scale * Translation != Coordinate * Translation * Scale

In 3D games, this happens in 3 phases and uses 3 matrices, World, View, and Projection. They are actually applied together, but it is easier to visualize their use one at a time.

The World matrix allows you to move and size geometry in the world. You can use geometry created with any actual scale/size/units, from any software. Object Space is typically, but not necessarily, centered at (0, 0, 0). Object Space units, axis angles, etc., are all user-defined. There is no software-defined upper or lower limits for coordinate values in Object Space.

ObjectCoordinate * Objects_WorldMatrix = WorldCoordinate

Once everything has been translated to World Space, the entire world acts as a single model, centered at (0, 0, 0). You can optionally move/rotate/scale the entire universe with an additional matrix. There is no software-defined upper or lower limits for coordinate values in World Space.

WorldCoordinate * Worlds_WorldMatrix = FinalWorldCoordinate

The View matrix typically represents the Camera. The View matrix moves, rotates, and scales the entire world into its' own coordinate system. The Camera's location becomes (0, 0, 0). View Space coordinates range from (-1, -1, -1) to (1, 1, 1). Values that are out-of-range, are not visible. Coordinates are not discarded whether they are visible or not.

FinalWorldCoordinate * View = ViewCoordinate

The Projection matrix specifies the portion of the Camera's 3D view volume to draw to the current Viewport, set on the current RenderTarget. By default, the entire "screen". The Projection matrix defines the distance at which "behind the camera" occurs, as well as how far into the scene to draw. Geometry that is behind the camera, too far away, too far left, etc., gets discarded.

ViewCoordinate * Projection = ViewportCoordinate

There are only a few more options to consider. By default, the "screen" is the background texture of a Windows.Forms.Form. You can specify another RenderTarget with a different size. Additionally, setting a Viewport specifies a portion of the current RenderTarget to move/scale draw calls to.

In 2D games, the same things happens, except you draw with ObjectCoordinates that are already in Screen Space units. A square, defined at (0, 0) with a width and height of 1, can be used to draw all squares by adding a "World" matrix. The World matrix scales the square to the correct size, then moves it into position. You can draw the one square over and over, supplying each of your object's World matrices, one per draw.

ObjectCoordinate_AlreadyInScreenSpace * World = ViewportCoordinate
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .