I am currently creating a space shooter game. To this game I want a background with stars that keeps looping for eternity.
In order to create this, I made a tile based system that simply creates a rectangle for each texture. I have been able to make said textures kinda loop, but when they do I get a really weird bug.
As you can see, my tiles are sometimes generating a small gap between each other, and I am not sure as of why.
The problem probably lies within my Background.cs file, but I am unsure of where.
Also, I would like a better name for "Background.cs". Do you think that something like "Tile.cs" is better?
Background.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 SpaceShooter
{
class Background
{
public Texture2D texture;
public List<Rectangle> tiles = new List<Rectangle>();
public Color color;
public int tileSize;
public int speed;
GraphicsDevice graphicsDevice;
public Background(Texture2D texture, GraphicsDevice graphicsDevice, int tileSize)
{
this.texture = texture;
this.graphicsDevice = graphicsDevice;
this.tileSize = tileSize;
speed = 3;
this.color = Color.White;
CalculateTiles();
}
public void Update()
{
for (int i = 0; i < tiles.Count(); i++)
{
tiles[i] = new Rectangle(tiles[i].X, tiles[i].Y + speed, tiles[i].Width, tiles[i].Height);
if (tiles[i].Y >= graphicsDevice.Viewport.Height)
{
tiles[i] = new Rectangle(tiles[i].X, 0 - tiles[i].Height, tiles[i].Width, tiles[i].Height);
}
}
}
public void CalculateTiles()
{
if (tiles.Count() > 0)
{
for (int i = 0; i < tiles.Count(); i++)
{
tiles.Remove(tiles[i]);
}
}
int XT = (int)(graphicsDevice.Viewport.Width / (tileSize / 1.5f));
int YT = (int)(graphicsDevice.Viewport.Height / (tileSize / 1.5f));
for (int i = 0; i < XT; i++)
{
tiles.Add(new Rectangle(tileSize * i, 0, tileSize, tileSize));
for (int j = 0; j < YT; j++)
{
tiles.Add(new Rectangle(tileSize * i, tileSize * j, tileSize, tileSize));
}
}
}
public void Draw(SpriteBatch spriteBatch)
{
for (int i = 0; i < tiles.Count(); i++)
{
spriteBatch.Draw(texture, tiles[i], color);
}
}
}
}
Game.cs
public class Game1 : Microsoft.Xna.Framework.Game
{
Background background;
protected override void LoadContent()
{
// Assign Background
background = new Background(Content.Load<Texture2D>("Stars"),GraphicsDevice, 128);
}
protected override void Update(GameTime gameTime)
{
if(Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
this.Exit();
background.Update();
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(bgc);
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
// Call all drawing functions here
background.Draw(spriteBatch);
player1.Draw(spriteBatch);
player2.Draw(spriteBatch);
//
spriteBatch.End();
base.Draw(gameTime);
}
I think your problem can be easily solved with LinearWrap
if you do the following in your Game1.draw.
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearWrap);
background.Draw(spriteBatch);
spriteBatch.End();
to get the scale simply do this calculation:
float scale = 128 / 512; // Where 128 is your desired size. and 512 your actual texture size.
and in your Background.cs draw method.
spriteBatch.Draw(texture, new Vector2(0,0), new Rectangle(pos.X, pos.Y, graphicsDevice.Viewport.Width / scale, graphicsDevice.Viewport.Height / scale), Color.White, 0, Vector2.Zero, scale, SpriteEffects.None, 0);
this should repeat your texture over the entire screen.
EDIT:
To make the image dynamic you could use a Camera. But using the system you have you would simply on Update do this :
pos += new Vector2(movementX, movementY);
Note that the spriteBatch.Draw has a parameter "Source Rectangle". If you change the position of this rectangle your texture will shift.
The problem has been solved.
I am now putting the tile up relative to the screen height instead of raw programming it to 0.
if (tiles[i].Y >= graphicsDevice.Viewport.Height)
{
tiles[i] = new Rectangle(tiles[i].X, tiles[i].Y - graphicsDevice.Viewport.Height - tiles[i].Height, tiles[i].Width, tiles[i].Height);
}
Related
I have two classes, Main and Grid. Grid simply makes a grid of square pixels. In my Main class, I want to get the list that was create in the Grid class. I managed to figure it out, but I'm wondering if there's a way to optimize the code.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Diagnostics;
using System.Collections.Generic;
namespace teeeest
{
public class Grid
{
Texture2D image;
Color color;
int rows;
int columns;
float outerThickness;
float innerThickness;
Vector2 size;
Vector2 origin;
Vector2 dotSize;
List<Pixel> pixels = new List<Pixel>(0);
public Grid(Texture2D image, int rows, int columns, float outerThickness, float innerThickness, Vector2 size, Vector2 origin, Vector2 dotSize, Color color)
{
this.dotSize = dotSize;
this.origin = origin;
this.color = color;
this.image = image;
this.rows = rows;
this.columns = columns;
this.outerThickness = outerThickness;
this.innerThickness = innerThickness;
this.size = size;
}
public void Update()
{
float sizeX = size.X / (columns - 1);
float sizeY = size.Y / (rows - 1);
for (int i = 0; i < rows; i++)
{
for (int g = 0; g < columns; g++)
{
Pixel p = new Pixel(image, 3, new Vector2((g * sizeX) + origin.X, sizeY * i + origin.Y), new Vector2(image.Width / 2, image.Height / 2), color);
pixels.Add(p);
}
}
}
public virtual void Draw(SpriteBatch hspritebatch, List<Grid> grids)
{
foreach (Pixel p in pixels)
{
hspritebatch.Draw(
texture: p.getImage(),
position: p.getPosition(),
sourceRectangle: null,
p.getColor(),
rotation: 0,
origin: new Vector2(image.Width / 2, image.Height),
scale: new Vector2(dotSize.X * .02f, dotSize.Y * .02f),
SpriteEffects.None,
0);
}
}
public Texture2D getImage()
{
return image;
}
public Vector2 getPosition()
{
return origin;
}
public Vector2 getOrigin()
{
return new Vector2(image.Width / 2, image.Height);
}
public Color getColor()
{
return color;
}
public List<Pixel> getList()
{
Update(); # This seems unnecessary. Is it?
return pixels;
}
}
}
The problem lies in the getList() function. In order to return the correct pixel list that was edited in the Update function, my solution there is to call that function right before returning the list. However, this seems costly for no reason. Is there a way around this without calling the Update function, or is this the only way?
I realize there's been posts similar to this, but I just don't understand them. I'm very much a beginner at coding. Here is my Main class.
using System;
using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
namespace teeeest
{
public class Game1 : Game
{
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
private SpriteFont font;
private Texture2D ball;
private Texture2D square;
private Color color = Color.White * .1f;
private Vector2 MouseCoords;
private Vector2 winMiddle;
private Vector2 ballOrigin;
private bool leftDown;
private bool eDown;
private int winWidth;
private int winHeight;
List<Line> lines = new List<Line>(0);
List<Grid> grids = new List<Grid>(0);
List<Pixel> pixels = new List<Pixel>(0);
List<Pixel> test = new List<Pixel>(0);
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
_graphics.PreferredBackBufferWidth = 800;
_graphics.PreferredBackBufferHeight = 600;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
ball = Content.Load<Texture2D>("ball");
square = Content.Load<Texture2D>("square");
font = Content.Load<SpriteFont>("File");
ballOrigin = new Vector2(ball.Width / 2, ball.Height / 2);
winWidth = _graphics.PreferredBackBufferWidth;
winHeight = _graphics.PreferredBackBufferHeight;
winMiddle = new Vector2(winWidth / 2, winHeight / 2);
}
protected override void Update(GameTime gameTime)
{
lines.Clear();
grids.Clear();
pixels.Clear();
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
if (Mouse.GetState().LeftButton == ButtonState.Released)
{
leftDown = false;
}
if (Mouse.GetState().LeftButton == ButtonState.Pressed && !leftDown)
{
Pixel g = new Pixel(ball, 5, MouseCoords, new Vector2(ball.Width / 2, ball.Height / 2), Color.Blue);
pixels.Add(g);
}
if (Keyboard.GetState().IsKeyUp(Keys.E))
{
eDown = false;
}
if (Keyboard.GetState().IsKeyDown(Keys.E) && !eDown)
{
color *= 1.1f;
eDown = true;
}
MouseCoords = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);
Grid q = new Grid(ball, 10, 10, 7, 3, new Vector2(500, 500), new Vector2(30, 30), new Vector2(.2f, .2f), Color.White);
grids.Add(q);
# Here is where I'm calling the getList() function.
System.Console.WriteLine(q.getList()[7].getPosition());
# Here is where I'm calling the getList() function.
foreach (Line s in lines)
{
s.Update();
}
foreach (Grid gh in grids)
{
gh.Update();
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
foreach (Line l in lines)
{
l.Draw(_spriteBatch, lines);
}
foreach (Grid g in grids)
{
g.Draw(_spriteBatch, grids);
}
foreach (Pixel p in pixels)
{
_spriteBatch.Draw(
texture: p.getImage(),
position: p.getPosition(),
sourceRectangle: null,
p.getColor(),
rotation: 0,
origin: p.getOrigin(),
scale: new Vector2(.02f, .02f),
SpriteEffects.None,
0);
}
_spriteBatch.DrawString(font,
MouseCoords.ToString(),
new Vector2 (winWidth - 100, 10),
Color.White,
rotation: 0,
origin: new Vector2(0, 0),
scale: new Vector2(1, 1),
SpriteEffects.None,
0);
_spriteBatch.End();
base.Draw(gameTime);
}
}
}
You could only call Update() when the List is EMPTY?
public List<Pixel> getList()
{
if (pixels.Count == 0)
{
Update(); // now it only gets called when pixels is EMPTY
}
return pixels;
}
This type of check may need to be done in Update() as well if it can be called directly from other places so you don't end up with more Pixel instances in it than you were expecting.
I am trying to make a very simple terraria-like game in C# XNA for a school project. I have very limited time otherwise I would probably have spent more time trying to figure this out myself. I created a tilemap but I just can't figure out how to make the tiles "solid" and not passable.
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 TileEngine {
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D character;
Vector2 cPosition;
Rectangle cBounds, t1Bounds;
List<Texture2D> tileTextures = new List<Texture2D>();
int[,] tileMap = new int[,]
{
{ 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
{ 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
{ 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
{ 0, 1, 1, 0, 2, 1, 1, 1, 0, 1, },
};
int tileWidth = 64;
int tileHeight = 36;
int cameraPositionX = 0;
int cameraPositionY = 0;
int vSpeed = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
IsMouseVisible = true;
graphics.IsFullScreen = false;
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
graphics.ApplyChanges();
cPosition = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2 - 15, graphics.GraphicsDevice.Viewport.Height / 2 - 20);
cBounds = new Rectangle((int)(cPosition.X), (int)(cPosition.Y), character.Width, character.Height);
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
Texture2D texture;
character = Content.Load<Texture2D>("Tiles/character");
texture = Content.Load<Texture2D>("Tiles/green");
tileTextures.Add(texture);
texture = Content.Load<Texture2D>("Tiles/red");
tileTextures.Add(texture);
texture = Content.Load<Texture2D>("Tiles/blue");
tileTextures.Add(texture);
cBounds = new Rectangle((int)(cPosition.X), (int)(cPosition.Y),
character.Width, character.Height);
// TODO: use this.Content to load your game content here
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
KeyboardState keyState = Keyboard.GetState();
vSpeed += 1;
cameraPositionY += vSpeed;
if (keyState.IsKeyDown(Keys.Right))
cameraPositionX += 5;
if (keyState.IsKeyDown(Keys.Left))
cameraPositionX -= 5;
if (keyState.IsKeyDown(Keys.Space))
vSpeed = -15;
if (cBounds.Intersects(t1Bounds))
{
cameraPositionY = 0;
vSpeed = 0;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
int tileMapWidth = tileMap.GetLength(1);
int tileMapHeight = tileMap.GetLength(0);
spriteBatch.Draw(character, cPosition, Color.White);
for (int x = 0; x < tileMapWidth; x++)
{
for (int y = 0; y < tileMapHeight; y++)
{
int textureIndex = tileMap[y, x];
Texture2D texture = tileTextures[textureIndex];
spriteBatch.Draw(
texture, t1Bounds =
new Rectangle(
320 + x * tileWidth - cameraPositionX,
540 + y * tileHeight - cameraPositionY,
tileWidth,
tileHeight),
Color.White);
}
}
spriteBatch.End();
base.Draw(gameTime);
}
} }
As you can see I tried to make Rectangles around all the sprites and detect when they intersect, but it doesn't seem to work. And even if I get the Rectangle-thing to work I just don't know what to do if they intersect. If I set the velocity to 0 then it will still slowly "fall" through the blocks as there is a default vertical acceleration.
First, you need to create a simple class for your tiles like this:
Class Tile
{
public int type; //Holds the ID to the specific texture.
public bool collision; //If true you should check for collision if the player is close.
public int health; //You probably need this since rock is harder to break then dirt.
}
Then create an array with Tiles. Now you have to check if the player is close to a collidable tile. You need a function that converts world space to tile space and put your player coordinates in it, check each frame for a couple of tiles around the player. If you check the complete map for collision your FPS will drop to .001, likewise for drawing all the tiles. Some pseudo code (already on tile level):
for (int y = player.y-4;y <= player.y+4;y++)
{ //If your player is just the size of a single tile then just -2/+2 will do. 9*9 is already an expensive 81 rectangles to check.
for (int x = player.x-4;x <= player.x+4;x++)
{
if (map[x,y].collision){
if (new Rectangle(x*tilewidth,y*tileheight,tilewidth,tileheight).intersects(player.rectangle)){
//Check farthest valid position and put player there
}}
}
}
The best thing to do is add in a newPosition property and before moving the player to this newPosition you have to check if the position is valid.
Other then that, if you do not have the time then the best advice is to not create a terraria like game. Even the simplest of terraria like game will be time consuming. Since you do not know the basics of collision i suggest making a pong or arkanoid clone that is pretty much how we all started out.
I have an isometric tile engine written in XNA (Monogame). It can only draw tile map surface. But when I have bigger map (for example 50x50 tiles) then is very slow (about 15 FPS). When I have small map (for example 10x10 tiles) than framrate is perfect (60 FPS).
I'm trying to find way how to optimise my code but I have no idea how to do it.
This is my code:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System;
namespace IsoEngine
{
public class Game1 : Game
{
GraphicsDeviceManager _graphics;
SpriteBatch _spriteBatch;
Texture2D Tile1;
Texture2D Tile2;
MouseState mouseState;
bool isMousePressed = false;
int[,] map = { {1, 1, 1, 1},
{1, 0, 0, 1},
{1, 0, 0, 1},
{1, 1, 1, 1} };
int tileWidth = 64;
int tileHeight = 32;
Vector2 scrollSpan = new Vector2(0, 0);
Vector2 mouseDragPos = new Vector2(0, 0);
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
base.IsMouseVisible = true;
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
Tile1 = Content.Load<Texture2D>("1");
Tile2 = Content.Load<Texture2D>("2");
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
mouseState = Mouse.GetState();
if (mouseState.LeftButton == ButtonState.Pressed && !isMousePressed)
{
isMousePressed = true;
mouseDragPos.X = mouseState.X;
mouseDragPos.Y = mouseState.Y;
}
if (mouseState.LeftButton == ButtonState.Pressed && isMousePressed)
{
if (mouseDragPos.X < mouseState.X)
{
scrollSpan.X += mouseState.X - mouseDragPos.X;
mouseDragPos.X = mouseState.X;
}
if (mouseDragPos.X > mouseState.X)
{
scrollSpan.X -= mouseDragPos.X - mouseState.X;
mouseDragPos.X = mouseState.X;
}
if (mouseDragPos.Y < mouseState.Y)
{
scrollSpan.Y += (mouseState.Y - mouseDragPos.Y) * 2;
mouseDragPos.Y = mouseState.Y;
}
if (mouseDragPos.Y > mouseState.Y)
{
scrollSpan.Y -= (mouseDragPos.Y - mouseState.Y) * 2;
mouseDragPos.Y = mouseState.Y;
}
}
if (mouseState.LeftButton == ButtonState.Released && isMousePressed)
isMousePressed = false;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
_spriteBatch.Begin();
DrawMap();
_spriteBatch.End();
base.Draw(gameTime);
}
private void DrawMap()
{
for (int osaY = 0; osaY < map.GetLength(0); osaY++)
{
for (int osaX = 0; osaX < map.GetLength(1); osaX++)
{
int x = osaX * 32;
int y = osaY * 32;
Texture2D thisTile = Tile1;
if (map[osaY, osaX] == 0)
thisTile = Tile1;
if (map[osaY, osaX] == 1)
thisTile = Tile2;
PlaceTile(thisTile, CartToIso(new Vector2(x, y)), new Vector2(osaX, osaY));
}
}
}
public void PlaceTile(Texture2D tileImage, Vector2 tilePos, Vector2 tileCoords)
{
_spriteBatch.Draw(tileImage, new Vector2(tilePos.X - (tileWidth / 2), tilePos.Y - tileHeight), Color.White);
}
public Vector2 CartToIso(Vector2 cartCoords)
{
Vector2 isoCoords = new Vector2(0, 0);
isoCoords.X = (cartCoords.X + scrollSpan.X) - cartCoords.Y;
isoCoords.Y = (cartCoords.X + scrollSpan.Y + cartCoords.Y) / 2;
return isoCoords;
}
public Vector2 IsoToCart(Vector2 isoCoords)
{
Vector2 cartCoords = new Vector2(0, 0);
cartCoords.X = (2 * isoCoords.Y + isoCoords.X - scrollSpan.X - scrollSpan.Y) / 2;
cartCoords.Y = (2 * isoCoords.Y - isoCoords.X + scrollSpan.X - scrollSpan.Y) / 2;
return cartCoords;
}
}
}
I'd suggest you to take a look at an answer I wrote a while ago, it does draw only the only the visible part of a level, no matter how big the level is :
I'm not copying and pasting the answer here as I wrote it already, so go and have a look at it here :
https://gamedev.stackexchange.com/a/29930/16262
Generally, to increase performace avoid creating unessesary objects and dont do anything you dont have to. For example you create one useless local texture in DrawMap(), also for single call of method PlaceTile you create 3 new vectors, while you need one.etc. also note, these are only minor improvements.
Another ways of speeding up might be using buffers(not sure what is default for XNA)
But most importantly, parallelize wherever you can.
Before doing anything else, profile your code and see where the most time is being spent. Only then can you begin to optimize
Situation: Within my game, I have tiles that collide with the player which is detected however when I attempt to cause an event to occur when they collide, nothing will happen.
Problem: I currently have a Collision class within each tile class. Within the tile class I have a method which passes in the player rectangle (x, y, width and height) using parameters. I then call the tiles Collision class check collision method. After some testing, I discovered that the collision IS being recognised however within my tileMap class (holds a 2D array of the Tile class) when I check through each tile (using foreach) and call the update method within each tile class, only the first tile collides. Nothing else.
Here is my code:
Tile:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
namespace Project
{
class Tile
{
Texture2D tileTexture;
Rectangle tileRect;
public Collision collision;
private Vector2 pos;
public int tileNumber;
private SpriteFont tileT;
bool box;
public enum tileCollision
{
passable,
nonpassable,
trigger,
}
public tileCollision tileValue;
public Tile(string tileType,int number, Rectangle newTileRect, ContentManager Content)
{
tileTexture = Content.Load<Texture2D>(tileType);
tileT = Content.Load<SpriteFont>("TimesNewRoman");
tileRect = newTileRect;
tileNumber = number;
pos.X = tileRect.X;
pos.Y = tileRect.Y;
//check if tile is within draw block
if (tileNumber >= 9 && tileNumber <= 13)
{
tileValue = tileCollision.passable;
}
if (tileNumber >= 0 && tileNumber <= 8 )
{
tileValue = tileCollision.nonpassable;
System.Console.WriteLine("NON PASSABLE TILE: " + " x: " + tileRect.X + " y: " + tileRect.Y);
}
collision = new Collision(tileRect.X, tileRect.Y, tileRect.Width, tileRect.Height);
}
public void update(Rectangle rect)
{
//System.Console.WriteLine(playerRect.X, playerRect.Y);
if (collision.boundingBoxCollisionCheck(rect.X, rect.Y, rect.Width, rect.Height))
{
//if (tileValue == tileCollision.nonpassable)
//{
// System.Console.WriteLine("COLLISION!!!!!");
//}
box = true;
}
if (box == true)
{
System.Console.WriteLine("COLLISION!!!!! at x: " + tileRect.X + "y: " + tileRect.Y );
}
}
public void draw(SpriteBatch spritebatch)
{
//if tile is within draw block then draw
spritebatch.Draw(tileTexture, tileRect, Color.White);
spritebatch.DrawString(tileT, pos.X.ToString(), pos, Color.White);
}
}
}
tileMap:
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content;
using System.IO;
namespace Project
{
class TileMap
{
//public Tile[,] tile = new Tile[10,10];
public List<Tile> tiles = new List<Tile>();
int width;
int height;
string[] tileType = { "Tiles/BrickGrassTile1", "Tiles/BrickGrassTile2", "Tiles/BrickGrassTile3", "Tiles/BrickGrassTile4",
"Tiles/BrickGrassTile5", "Tiles/BrickRoofTile1", "Tiles/BrickRoofTile2", "Tiles/BrickRoofTile3",
"Tiles/BrickTile1", "Tiles/GrassPuddleTile1", "Tiles/GrassRockTile1", "Tiles/WallTile1", "Tiles/GrassTile1", "Tiles/GrassTile2", "Tiles/BrickDoor1" };
public void generateMap(int [,]map, int size, ContentManager content)
{
for (int x = 0; x < map.GetLength(1); x++)
{
for (int y = 0; y < map.GetLength(0); y++)
{
int number = map[y, x];
if (number >= 0)
{
tiles.Add(new Tile(tileType[number],number, new Rectangle(x * size, y * size, size, size), content));
}
width = (x + 1) * size;
height = (y + 1) * size;
}
}
}
public void update(Rectangle playerRect)
{
foreach (Tile tile in tiles)
{
tile.update(playerRect);
}
}
public void draw(SpriteBatch spritebatch)
{
foreach (Tile tile in tiles)
{
tile.draw(spritebatch);
}
}
}
}
... each tile (using foreach) and call the update method within each tile class, only the first tile collides. Nothing else.
I suspect your player rectangle is smaller than one of your tiles, which is why you are only getting 1 collision.
EDIT
The way your logic works now, once you collide with an item, it permanently says you are colliding with the item. This is incorrect. Use this:
public void update(Rectangle rect)
{
//System.Console.WriteLine(playerRect.X, playerRect.Y);
if (collision.boundingBoxCollisionCheck(rect.X, rect.Y, rect.Width, rect.Height))
{
System.Console.WriteLine("COLLISION!!!!! at x: " + tileRect.X + "y: " + tileRect.Y );
}
}
When you click on the top most button it is suppose to draw a string to the screen, but it's no showing up. I moved mainMenu.UpdateButtons(); to the Draw method in Main.cs but the string is drawn then the background image is drawn again. Making it appear as the string appears for a split second and disappear. Why is it doing this?
Main.cs
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 TestGame.Controls;
using TestGame.GameStates;
namespace TestGame
{
public class Main : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
InputHandler inputHandler;
public SpriteBatch spriteBatch;
public SpriteFont spriteFont;
MainMenu mainMenu;
Vector2 position;
public Main()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
inputHandler = new InputHandler();
mainMenu = new MainMenu(this);
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
}
protected override void Initialize()
{
this.IsMouseVisible = true;
base.Initialize();
mainMenu.MenuInitialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
spriteFont = Content.Load<SpriteFont>(#"Fonts\MainFont");
mainMenu.MenuLoadContent();
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
inputHandler.Update();
if (inputHandler.currentKeyState.IsKeyDown(Keys.Escape))
this.Exit();
mainMenu.frameTime = gameTime.ElapsedGameTime.Milliseconds / 1000;
MouseState mouseState = Mouse.GetState();
mainMenu.mouseX = mouseState.X;
mainMenu.mouseY = mouseState.Y;
mainMenu.previouslyPressed = mainMenu.mousePressed;
mainMenu.mousePressed = mouseState.LeftButton == ButtonState.Pressed;
mainMenu.UpdateButtons();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
spriteBatch.Begin();
mainMenu.MenuDraw();
spriteBatch.End();
base.Draw(gameTime);
}
}
}
MainMenu.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace TestGame.GameStates
{
public class MainMenu
{
enum buttonState { hover, up, released, down }
const int numberOfButtons = 4, newGameButtonIndex = 0, loadGameButtonIndex = 1, optionsButtonIndex = 2, quitButtonIndex = 3, buttonHeight = 48, buttonWidth = 80;
Color[] buttonColor = new Color[numberOfButtons];
Rectangle[] buttonRect = new Rectangle[numberOfButtons];
buttonState[] buttonSt = new buttonState[numberOfButtons];
Texture2D[] buttonTexture = new Texture2D[numberOfButtons];
double[] buttonTimer = new double[numberOfButtons];
public bool mousePressed, previouslyPressed = false;
public int mouseX, mouseY;
public double frameTime;
int buttonPadding;
Main main;
Texture2D backgroundImage;
Texture2D backgroundImageFade;
public MainMenu(Game game)
{
main = (Main)game;
}
public void MenuInitialize()
{
for (int i = 0; i < numberOfButtons; i++)
{
buttonSt[i] = buttonState.up;
buttonColor[i] = Color.White;
buttonTimer[i] = 0.0;
buttonRect[i] = new Rectangle(0, buttonPadding, buttonWidth, buttonHeight);
buttonPadding += buttonHeight;
}
}
public void MenuLoadContent()
{
backgroundImage = main.Content.Load<Texture2D>(#"Backgrounds\titlescreen");
backgroundImageFade = main.Content.Load<Texture2D>(#"Backgrounds\titlescreenfade");
buttonTexture[newGameButtonIndex] = main.Content.Load<Texture2D>(#"Sprites\desktop");
buttonTexture[loadGameButtonIndex] = main.Content.Load<Texture2D>(#"Sprites\desktop");
buttonTexture[optionsButtonIndex] = main.Content.Load<Texture2D>(#"Sprites\desktop");
buttonTexture[quitButtonIndex] = main.Content.Load<Texture2D>(#"Sprites\desktop");
}
public void MenuDraw()
{
main.spriteBatch.Draw(backgroundImage, new Vector2(0, 0), Color.White);
for (int i = 0; i < numberOfButtons; i++)
{
main.spriteBatch.Draw(buttonTexture[i], buttonRect[i], buttonColor[i]);
}
}
Boolean targetImageAlpha(Rectangle rect, Texture2D texture, int x, int y)
{
return targetImageAlpha(0, 0, texture, texture.Width * (x - rect.X) / rect.Width, texture.Height * (y - rect.Y) / rect.Height);
}
Boolean targetImageAlpha(float tx, float ty, Texture2D texture, int x, int y)
{
if (targetImage(tx, ty, texture, x, y))
{
uint[] data = new uint[texture.Width * texture.Height];
texture.GetData<uint>(data);
if ((x - (int)tx) + (y - (int)ty) * texture.Width < texture.Width * texture.Height)
{
return ((data[(x - (int)tx) + (y - (int)ty) * texture.Width] & 0xFF000000) >> 24) > 20;
}
}
return false;
}
Boolean targetImage(float tx, float ty, Texture2D texture, int x, int y)
{
return (x >= tx && x <= tx + texture.Width && y >= ty && y <= ty + texture.Height);
}
public void UpdateButtons()
{
for (int i = 0; i < numberOfButtons; i++)
{
if (targetImageAlpha(buttonRect[i], buttonTexture[i], mouseX, mouseY))
{
buttonTimer[i] = 0.0;
if (mousePressed)
{
buttonSt[i] = buttonState.down;
buttonColor[i] = Color.Blue;
}
else if (!mousePressed && previouslyPressed)
{
if (buttonSt[i] == buttonState.down)
{
buttonSt[i] = buttonState.released;
}
}
else
{
buttonSt[i] = buttonState.hover;
buttonColor[i] = Color.LightBlue;
}
}
else
{
buttonSt[i] = buttonState.up;
if (buttonTimer[i] > 0)
{
buttonTimer[i] = buttonTimer[i] - frameTime;
}
else
{
buttonColor[i] = Color.White;
}
}
if (buttonSt[i] == buttonState.released)
{
onButtonClick(i);
}
}
}
void onButtonClick(int i)
{
switch (i)
{
case newGameButtonIndex:
main.spriteBatch.Begin();
//main.spriteBatch.DrawString(main.spriteFont, "Creating new game", new Vector2(100, 200), Color.White);
main.spriteBatch.Draw(backgroundImageFade, new Vector2(0, 0), Color.White);
main.spriteBatch.End();
break;
default:
break;
}
}
}
}
It is being drawn, but then you proceed to erase it in your Draw method. That's the issue you'll get when you start to mix your drawing code in with your updating code.
So here's an example of what's happening in your game right now.
Update Main Game
Update Buttons
Draw Main Game
Draw Buttons
Then a click occurs and here's what happens.
Update Main Game
Update Buttons
onButtonClick -> this is where you draw your text
Draw Main Game -> the screen now clears and your draw your buttons
Draw Buttons
So it's all "working" just not how you really intended it. You're going to want to separate your drawing code so that you're drawing from Draw method calls. Basically check to see if the buttonState has become "released" in your Draw method and THEN draw the text you want.