-1
\$\begingroup\$

I have a problem which I need to resolve. I am making a Space Invaders game in C# XNA. I've been following this tutorial for the Invaders. But I also have my own code for animating the Invader spritesheet. And eventually here's what I'm drawing:

void Draw(SpriteBatch spriteBatch){
  for (int r = 0; r < m_InvaderRows; r++)
  {
    for (int c = 0; c < m_InvaderCollumns; c++)
    {
      spriteBatch.Draw(m_BotInvaderTex, m_BotInvaderPos, m_BotInvaderHitBox, Color.White, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0);

      // ???

      spriteBatch.Draw(m_BotInvaderTex, m_BotInvadersRect[r, c], Color.White);
    }
  }
}

So the first Draw() call draws only 1 animated sprite. The second draws 50 non-animated sprites. m_BotInvadersRect[r, c] is a rectangle I used as a multi-dimensional array. So I'm looking for a way to combine both Draw calls and have 50 animated sprites drawn on my screen. If you need to see my variables or Update(GameTime gameTime) just comment and I'll edit the question.

How can I animate the Bot Invaders using a texture atlas?

My code:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Storage;
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;
using Microsoft.Xna.Framework.Net;

namespace SpaceInvaders
{
class botInvader
{

    public botInvader()
    {

    }

    public static Texture2D g_BotInvaderTex;
    Rectangle m_BotInvaderHitBox;
    public static Vector2 g_BotInvaderPos = new Vector2(0, 24);
    Vector2 m_BotInvaderOrigin;

    int m_BotInvaderCurrentFrame = 1;
    int m_BotInvaderFrameWidth = 52;
    int m_BotInvaderFrameHeight = 88;

    float m_Timer = 0f;
    float m_Interval = 100;

    Rectangle[,] m_BotInvadersRect;
    int m_InvaderRows = 5;
    int m_InvaderCollumns = 10;
    String m_BotInvadersDirection = "RIGHT";

    public void Initialize()
    {

    }

    public void LoadContent(ContentManager Content)
    {
        g_BotInvaderTex = Content.Load<Texture2D>(".\\gameGraphics\\gameSprites\\botInvaders\\normalInvaders\\spritesheet"); // invaderShip1
        m_BotInvadersRect = new Rectangle[m_InvaderRows, m_InvaderCollumns];
        for (int r = 0; r < m_InvaderRows; r++)
        {
            for (int c = 0; c < m_InvaderCollumns; c++)
            {
                m_BotInvadersRect[r, c].Width = g_BotInvaderTex.Width;
                m_BotInvadersRect[r, c].Height = g_BotInvaderTex.Height;
                m_BotInvadersRect[r, c].X = 70 * c;
                m_BotInvadersRect[r, c].Y = 70 * r;
            }
        }
    }

    public void Update(GameTime gameTime)
    {
        m_BotInvaderHitBox = new Rectangle(m_BotInvaderCurrentFrame * m_BotInvaderFrameWidth, 0, m_BotInvaderFrameWidth, m_BotInvaderFrameHeight);
        m_BotInvaderOrigin = new Vector2(m_BotInvaderHitBox.X / 2, m_BotInvaderHitBox.Y / 2);

        m_Timer += (float)gameTime.ElapsedGameTime.Milliseconds;

        if (m_Timer > m_Interval)
        {
            m_BotInvaderCurrentFrame++;
            m_Timer = 0f;
        }

        if (m_BotInvaderCurrentFrame == 2)
        {
            m_BotInvaderCurrentFrame = 0;
        }

        m_BotInvaderHitBox = new Rectangle(m_BotInvaderCurrentFrame * m_BotInvaderFrameWidth, 0, m_BotInvaderFrameWidth, m_BotInvaderFrameHeight);
        m_BotInvaderOrigin = new Vector2(m_BotInvaderHitBox.Width / 2, m_BotInvaderHitBox.Height / 2);

        int m_RightSide = 800;
        int m_LeftSide = 0;

        for (int r = 0; r < m_InvaderRows; r++)
        {
            for (int c = 0; c < m_InvaderCollumns; c++)
            {
                if (m_BotInvadersDirection.Equals ("RIGHT"))
                {
                    m_BotInvadersRect[r, c].X = m_BotInvadersRect[r, c].X + 1;
                }

                if (m_BotInvadersDirection.Equals("LEFT"))
                {
                    m_BotInvadersRect[r, c].X = m_BotInvadersRect[r, c].X - 1;
                }
            }
        }

        String m_BotInvadersChangeDirection = "N";

        for (int r = 0; r < m_InvaderRows; r++)
        {
            for (int c = 0; c < m_InvaderCollumns; c++)
            {
                if (m_BotInvadersRect[r, c].X + m_BotInvadersRect[r, c].Width > m_RightSide)
                {
                    m_BotInvadersDirection = "LEFT";
                    m_BotInvadersChangeDirection = "Y";
                }

                if (m_BotInvadersRect[r, c].X < m_LeftSide)
                {
                    m_BotInvadersDirection = "RIGHT";
                    m_BotInvadersChangeDirection = "Y";
                }
            }

            if (m_BotInvadersChangeDirection.Equals("Y"))
            {
                    for (int c = 0; c < m_InvaderCollumns; c++)
                    {
                        m_BotInvadersRect[r, c].Y = m_BotInvadersRect[r, c].Y + 3;
                    }
            }
        }
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        for (int r = 0; r < m_InvaderRows; r++)
        {
            for (int c = 0; c < m_InvaderCollumns; c++)
            {
                spriteBatch.Draw(g_BotInvaderTex, g_BotInvaderPos, m_BotInvaderHitBox, Color.White, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0);
                spriteBatch.Draw(g_BotInvaderTex, m_BotInvadersRect[r, c], Color.White);
            }
        }         
    }
}

}

\$\endgroup\$
  • \$\begingroup\$ Clarification Question : You have everything drawing as intended? As of the time I'm writing this comment, it looks to me like you are asking how you can restructure your draw method to have only one draw call, but keep the result the same. Also, from what it sounds like, you only want to make that first draw call once. In the code shown, you are making the first draw call inside the loop, which I'm assuming is your player's sprite, which would result in the player sprite being drawn 50 times. \$\endgroup\$ – Shroeder Nov 12 '14 at 14:21
  • \$\begingroup\$ @Shroeder No, it's the invader sprite. The rectangle Rectangle[,] m_BotInvadersRect; is an array that holds the 50 invaders. I HAVE TO use an rectangle Rectangle m_BotInvaderHitBox; to draw the current frame of my animation. So in a way you are right I want only 1 draw call but use it with both rectangles so that I can have the 50 invaders drawn on screen and also being animated from the spritesheet. \$\endgroup\$ – PowerUser Nov 12 '14 at 15:08
  • \$\begingroup\$ Are all 50 sprites different in your Sprite Sheet? \$\endgroup\$ – Shroeder Nov 12 '14 at 15:48
  • \$\begingroup\$ In my spritesheet there are only 2 frames/images. They are collected in the array Rectangle[,] m_BotInvadersRect; and I draw the animated sprite 50 times on the screen. \$\endgroup\$ – PowerUser Nov 12 '14 at 15:50
  • \$\begingroup\$ @Shroeder do you want to see my spritesheet. \$\endgroup\$ – PowerUser Nov 12 '14 at 17:28
3
\$\begingroup\$

You need a Source Rectangle and Destination Rectangle.

The Source Rectangle will pull a rectangle defined by you out of the sprite sheet, then draw it at the specified position.

PsuedoCode

        int width = txtHero.Width / Columns;
        int height = txtHero.Height / Rows;
        int row = (int)((float)currentFrame / (float)Columns);
        int column = currentFrame % Columns;
        Rectangle sourceRectangle = new Rectangle(width * column,height*row,width,height);
        spriteBatch.Draw(txtHero, DrawLocation, sourceRectangle, Color.White);

However you choose to iterate through your sprites in the sprite sheet is entirely up to you.

A great reference when I first learned XNA can be found here

\$\endgroup\$
  • \$\begingroup\$ Yes, I've already done that but what I need is to have the 50 invaders animate using the images of my spritesheet. I got the animation logic but I need to implement it into the Draw() call, which I don't know how to do. \$\endgroup\$ – PowerUser Nov 11 '14 at 20:57
  • \$\begingroup\$ @PowerUser You need to implement the 'Update()' method in the link Shroeder gave. It changes the frame (currentFrame) to draw in the spritesheet every frame. \$\endgroup\$ – RdeGier Nov 12 '14 at 7:59
  • \$\begingroup\$ OK looks like nobody understood me so I'm posting my entire code in the question... \$\endgroup\$ – PowerUser Nov 12 '14 at 14:07

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.