Hello,
I made a 2D game using XNA 3.1 in visual studio 2008 professional.
In the game you can control a cannon and shoot cannonballs at ufo's.
It worked when I had a Rectangle collision but when I followed a tutorial for per-pixel collision, and I got errors I have no idea what to do cuz I've tried so many things.
(im quite new to XNA)
I get 6 errors saying: Error 1 'System.Array' does not contain a definition for 'Width' and no extension method 'Width' accepting a first argument of type 'System.Array' could be found (are you missing a using directive or an assembly reference?)
(6 more of those about "Width" and "Height" and "Data")
this is my code after my per-pixel collision attempt:
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;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
namespace UFO
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D backgroundTexture;
Rectangle viewportRect;
GameObject cannon;
const int maxCannonBalls = 3;
GameObject[] cannonBalls;
KeyboardState previousKeyboardState = Keyboard.GetState();
const int maxEnemies = 3;
GameObject[] enemies;
const float maxEnemyHeight = 0.1f;
const float minEnemyHeight = 0.5f;
const float maxEnemyVelocity = 5.0f;
const float minEnemyVelocity = 1.0f;
Random random = new Random();
int score;
SpriteFont font;
Vector2 scoreDrawPoint = new Vector2(0.1f, 0.1f);
float interval = 1000f / 25f;
int frameCount = 16;
int spriteWidth = 64;
int spriteHeight = 64;
Explosion[] explosions;
AudioEngine audioEngine;
SoundBank soundbank;
WaveBank wavebank;
Color[] cannonBallsData;
Color[] enemiesData;
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>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
backgroundTexture =
Content.Load<Texture2D>("Sprites\\background");
viewportRect = new Rectangle(0, 0,
graphics.GraphicsDevice.Viewport.Width,
graphics.GraphicsDevice.Viewport.Height);
cannon = new GameObject(
Content.Load<Texture2D>("Sprites\\cannon"));
cannon.position = new Vector2(120,
graphics.GraphicsDevice.Viewport.Height - 80);
cannonBalls = new GameObject[maxCannonBalls];
for (int i = 0; i < maxCannonBalls; i++)
{
cannonBalls[i] = new
GameObject(Content.Load<Texture2D>(
"Sprites\\cannonball"));
}
enemies = new GameObject[maxEnemies];
for (int i = 0; i < maxEnemies; i++)
{
enemies[i] = new GameObject(
Content.Load<Texture2D>("Sprites\\enemy"));
}
font = Content.Load<SpriteFont>("Fonts\\GameFont");
enemies = new GameObject[maxEnemies];
explosions = new Explosion[maxEnemies];
for (int i = 0; i < maxEnemies; i++)
{
enemies[i] = new GameObject(
Content.Load<Texture2D>("Sprites\\enemy"));
explosions[i] = new Explosion(
Content.Load<Texture2D>(
"Sprites\\sprite_sheet"));
}
audioEngine = new
AudioEngine("Content\\Audio\\audio.xgs");
wavebank = new WaveBank(audioEngine,
"Content\\Audio\\Wave Bank.xwb");
soundbank = new SoundBank(audioEngine,
"Content\\Audio\\Sound Bank.xsb");
}
/// <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>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
KeyboardState keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.Left))
{
cannon.rotation -= 0.1f;
}
if (keyboardState.IsKeyDown(Keys.Right))
{
cannon.rotation += 0.1f;
}
cannon.rotation = MathHelper.Clamp(cannon.rotation, -MathHelper.PiOver2, 0);
if (keyboardState.IsKeyDown(Keys.Space) &&
previousKeyboardState.IsKeyUp(Keys.Space))
{
FireCannonBall();
}
previousKeyboardState = keyboardState;
UpdateCannonBalls();
UpdateEnemies();
UpdateExplosions((float)gameTime.ElapsedGameTime.TotalMilliseconds);
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
spriteBatch.Draw(backgroundTexture, viewportRect,
Color.White);
foreach (GameObject ball in cannonBalls)
{
if (ball.alive)
{
spriteBatch.Draw(ball.sprite,
ball.position, Color.White);
}
}
spriteBatch.Draw(cannon.sprite,
cannon.position,
null,
Color.White,
cannon.rotation,
cannon.center, 1.0f,
SpriteEffects.None, 0);
foreach (GameObject enemy in enemies)
{
if (enemy.alive)
{
spriteBatch.Draw(enemy.sprite,
enemy.position, Color.White);
}
}
spriteBatch.DrawString(font,
"Score: " + score.ToString(),
new Vector2(scoreDrawPoint.X * viewportRect.Width,
scoreDrawPoint.Y * viewportRect.Height),
Color.Yellow);
foreach (Explosion explosion in explosions)
{
if (explosion.alive)
{
spriteBatch.Draw(explosion.sprite, explosion.position, explosion.sourceRect, Color.White, 0f, Vector2.Zero, 1.5f, SpriteEffects.None, 0);
}
}
base.Draw(gameTime);
spriteBatch.End();
}
public void FireCannonBall()
{
foreach (GameObject ball in cannonBalls)
{
if (!ball.alive)
{
ball.alive = true;
soundbank.PlayCue("missilelaunch");
ball.position = cannon.position -
ball.center;
ball.velocity = new Vector2(
(float)Math.Cos(cannon.rotation),
(float)Math.Sin(cannon.rotation)) *
5.0f;
return;
}
}
}
public void UpdateCannonBalls()
{
foreach (GameObject ball in cannonBalls)
{
if (ball.alive)
{
ball.position += ball.velocity;
if (!viewportRect.Contains(new Point(
(int)ball.position.X,
(int)ball.position.Y)))
{
ball.alive = false;
continue;
}
Rectangle cannonBallRect = new Rectangle(
(int)ball.position.X,
(int)ball.position.Y,
ball.sprite.Width,
ball.sprite.Height);
foreach (GameObject enemy in enemies)
{
Rectangle enemyRect = new Rectangle(
(int)enemy.position.X,
(int)enemy.position.Y,
enemy.sprite.Width,
enemy.sprite.Height);
if (IntersectPixels(cannonBallRect,cannonBallsData,enemyRect, enemiesData))
{
ball.alive = false;
enemy.alive = false;
explosions[1].alive = true;
explosions[1].timer = 0; explosions[1].currentFrame = 0;
explosions[1].position = enemy.position;
score += 1;
soundbank.PlayCue("explosion");
break;
}
}
}
}
}
public void UpdateEnemies()
{
foreach (GameObject enemy in enemies)
{
if (enemy.alive)
{
enemy.position += enemy.velocity;
if (!viewportRect.Contains(new Point(
(int)enemy.position.X,
(int)enemy.position.Y)))
{
enemy.alive = false;
}
}
else
{
enemy.alive = true;
enemy.position = new Vector2(
viewportRect.Right,
MathHelper.Lerp(
(float)viewportRect.Height *
minEnemyHeight,
(float)viewportRect.Height *
maxEnemyHeight,
(float)random.NextDouble()));
enemy.velocity = new Vector2(
MathHelper.Lerp(
-minEnemyVelocity,
-maxEnemyVelocity,
(float)random.NextDouble()), 0);
}
}
}
public void UpdateExplosions(float timertijd)
{
foreach (Explosion explosion in explosions)
{
if (explosion.alive)
{
explosion.timer += timertijd;
if (explosion.timer > interval)
{
explosion.currentFrame++;
if (explosion.currentFrame > frameCount - 1)
{
explosion.alive = false;
}
explosion.timer = 0f;
}
explosion.sourceRect = new
Rectangle(explosion.currentFrame * spriteWidth,
0, spriteWidth, spriteHeight);
}
}
}
static bool IntersectPixels(Rectangle rectangleA, Color[] dataA,
Rectangle rectangleB, Color[] dataB)
{
// Find the bounds of the rectangle intersection
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
// Check every point within the intersection bounds
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
// Get the color of both pixels at this point
Color colorA = dataA[(x - rectangleA.Left) +
(y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) +
(y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent,
if (colorA.A != 0 && colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
// No intersection found
return false;
}
}
}