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 );
}
}
Related
I have a similar problem to:
stackoverflow
But in my case I'm using Unity3D + TiledMapEditor + Tiled2Unity.
I'm loading my map to Unity3D by Tiled2Unity program and as a player parameter Order in Layer I can change easily by:
Renderer renderer = GetComponent<Renderer>();
renderer.sortingOrder = -(int)(transform.position.y * 100);
Object "map" can only change the parameter Order In Layer for the individual layers.
For example: floor = 0, wall = 1, collision = 2. I have no idea how to get to a single "tile" the map and change its Order In Layer because of where it is located. To map was drawn from top to bottom (The lower the Order in Layer increased).
The script hooked the object "map":
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace Tiled2Unity
{
public class TiledMap : MonoBehaviour
{
public int NumTilesWide = 0;
public int NumTilesHigh = 0;
public int TileWidth = 0;
public int TileHeight = 0;
public float ExportScale = 1.0f;
// Note: Because maps can be isometric and staggered we simply can't multply tile width (or height) by number of tiles wide (or high) to get width (or height)
// We rely on the exporter to calculate the width and height of the map
public int MapWidthInPixels = 0;
public int MapHeightInPixels = 0;
public float GetMapWidthInPixelsScaled()
{
return this.MapWidthInPixels * this.transform.lossyScale.x * this.ExportScale;
}
public float GetMapHeightInPixelsScaled()
{
return this.MapHeightInPixels * this.transform.lossyScale.y * this.ExportScale;
}
private void OnDrawGizmosSelected()
{
Vector2 pos_w = this.gameObject.transform.position;
Vector2 topLeft = Vector2.zero + pos_w;
Vector2 topRight = new Vector2(GetMapWidthInPixelsScaled(), 0) + pos_w;
Vector2 bottomRight = new Vector2(GetMapWidthInPixelsScaled(), -GetMapHeightInPixelsScaled()) + pos_w;
Vector2 bottomLeft = new Vector2(0, -GetMapHeightInPixelsScaled()) + pos_w;
Gizmos.color = Color.red;
Gizmos.DrawLine(topLeft, topRight);
Gizmos.DrawLine(topRight, bottomRight);
Gizmos.DrawLine(bottomRight, bottomLeft);
Gizmos.DrawLine(bottomLeft, topLeft);
}
}
}
to better understand (because my level of English is poor):
mesh.png
map.png
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);
}
Here is my situation. I have four classes: Inventory.cs, InventoryTab.cs, ItemDatabase.cs, BaseItem.cs (and a few items that derive from BaseItem such as Weapon, Consumable, etc)
Inventory creates new InventoryTabs (Consumable, Weapon, Armor), and in each InventoryTab there is a list of BaseItems.
Inventory.AddItemByID(int ID, int Amount) is then called, which checks the IDs of ItemDatabase.cs, and has items pre-loaded into a list when the game is started.
Ok, now that you have basic info on how my inventory runs, here my problem:
In the InventoryTab:
int Column, Row; //Declared at the top of the class
for (int i = 0; i < ItemList.Count; i++)
{
Column++;
if (Column > gridSize.X)
{
Column = 0; Row++; //Row is not limited because my inventory will be unlimited in height
}
ItemList[i].gridLocation = new Point(column, row);
}
While I thought this would work, instead it creates one item at the top, and rapidly skips to the right, and then skips down one row, and repeats. If I add more items via a KeyboardState, it flashes a huge list of items, then disappears.
I'm certain it's because it is assigning the value of gridLocation in a loop, but then again I have no idea how I would go about it any other way.
What I need it to do is assign the gridLocation per BaseItem in ItemList only once.
EDIT:
InventoryTab.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace InventoryEngine
{
public class InventoryTab
{
public List<BaseItem> ItemList = new List<BaseItem>();
int itemSize; //In Pixels
Point gridSize = new Point(5, 4);
int LocY, Column, Row;
public float Scale;
//Region Clipping
Rectangle scissorRect;
RasterizerState rState = new RasterizerState()
{
ScissorTestEnable = true
};
Vector2 gridOffset;
Texture2D slot;
SpriteFont sf;
MouseState ms;
public void LoadContent(ContentManager c, GraphicsDevice g)
{
ItemDatabase.LoadItemData(c);
slot = c.Load<Texture2D>("inventory_slot");
sf = c.Load<SpriteFont>("SpriteFont1");
Scale = 4.0f;
itemSize = 32 * (int)Scale;
gridOffset = new Vector2(g.Viewport.Width / 2 - (itemSize * gridSize.X / 2), g.Viewport.Height / 2 - (itemSize * gridSize.Y / 2));
LocY = g.Viewport.Height / 2;
}
public void Update(GameTime gt, GraphicsDevice g)
{
ms = Mouse.GetState();
LocY += ms.ScrollWheelValue / 10;
if (LocY >= g.Viewport.Height / 2 - 252)
LocY = g.Viewport.Height / 2 - 252;
for (int i = 0; i < ItemList.Count / 1; i++)
{
Column++;
if (Column > gridSize.X)
{
Column = 0; Row++;
}
ItemList[i].gridLocation = new Point(Column, Row);
}
foreach (BaseItem item in ItemList)
{
item.UpdateValues(gridSize, itemSize, gridOffset, LocY);
}
}
public void DrawTab(SpriteBatch sb, GraphicsDevice g)
{
scissorRect = new Rectangle((int)gridOffset.X, g.Viewport.Height / 2 - 256, gridSize.X * itemSize, gridSize.Y * itemSize);
sb.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointClamp, DepthStencilState.Default, rState);
//g.ScissorRectangle = scissorRect;
foreach (BaseItem i in ItemList)
{
sb.Draw(slot, new Vector2(i.itemRect.X, i.itemRect.Y), new Rectangle(0, 0, 32, 32), Color.White, 0, Vector2.Zero, Scale, SpriteEffects.None, .95f);
sb.Draw(i.Icon, new Vector2(i.itemRect.X, i.itemRect.Y), new Rectangle(0, 0, i.Icon.Width, i.Icon.Height), Color.White, 0, Vector2.Zero, Scale, SpriteEffects.None, .95f);
//if (i.currentAmount > 1)
sb.DrawString(sf, "" + i.currentAmount, new Vector2(i.itemRect.X, i.itemRect.Y), Color.White, 0f, Vector2.Zero, Scale, SpriteEffects.None, .95f);
}
sb.End();
}
}
}
BaseItem.cs:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
namespace InventoryEngine
{
public class BaseItem
{
public Texture2D Icon;
public string name;
string description;
public int id, currentAmount, maxAmount;
public enum TabType { Consumable, Weapon, Armor, Ammo, Jewellery, Resources, Misc }
public TabType tabType;
public bool isSelected, isUsable;
public Vector2 positionOffset;
public Point gridLocation;
public Rectangle itemRect;
public BaseItem(Texture2D IconName, int ID, string Name, string Description, int MaxAmount, TabType TabType)
{
Icon = IconName;
id = ID;
name = Name;
description = Description;
maxAmount = MaxAmount;
tabType = TabType;
}
public void UpdateValues(Point GridSize, int itemSize, Vector2 GridOffset, int OffsetY)
{
currentAmount = (int)MathHelper.Clamp(currentAmount, 0, maxAmount);
itemRect = new Rectangle(gridLocation.X * itemSize + (int)GridOffset.X, gridLocation.Y * itemSize + OffsetY, itemSize, itemSize);
}
}
}
Going with #deathismyfriend you could run a nested for loop going through each position:
int pos; //Declared at the top of the class
pos = 0;
for (int x = 0; x < gridsize.X; x++) // x represents column
{
for (int y = 0; y < ItemList.Count / gridsize.X; y++) // y represents row
{
// In the above (ItemList.count / gridsize.X) = number of rows needed
ItemList[pos].gridLocation = new Point(x, y);
pos++;
}
}
And that SHOULD do what you want it to.
Mona
I have a map that is 1280 by 1280 pixels.
I am drawing tiles on the screen that are 32 by 32 pixels.
I also give each tile a rectangle of 32 by 32 pixels.
Each tile also has a bool telling me if it is blocked or not.
My sprite has a rectangle of 32 by 32 pixels.
When the sprite and tile intersect the top left most pixel of the tile is the only one that makes contact with the sprites rectangle.
I'm tried inflating the rectangle with no change.
Core.playerOneSpriteRectangle = new Rectangle((int)Core.playerOneSprite.Position.X, (int)Core.playerOneSprite.Position.Y, Engine.TileWidth, Engine.TileHeight);
#region Player One Controls
// Player One Controls
Tile tile;
Rectangle destination = new Rectangle(0, 0, Engine.TileWidth, Engine.TileHeight);
if (keyState.IsKeyDown(Keys.W) || (gamePadOneState.DPad.Up == ButtonState.Pressed))
{
Core.playerOneSprite.CurrentAnimation = AnimationKey.Up;
foreach (var layer in tileMapLayers)
{
for (var y = 0; y < layer.Height; y++)
{
destination.Y = y * Engine.TileHeight;
for (var x = 0; x < layer.Width; x++)
{
tile = layer.GetTile(x, y);
destination.X = x * Engine.TileWidth;
// Check Collision With Tiles
if (Core.playerOneSpriteRectangle.Intersects(destination) && tile.TileBlocked)
{
playerOneMotion.Y = destination.Bottom;
//Console.WriteLine("Intersected with" + destination + tile.TileBlocked);
}
else if (Core.playerOneSpriteRectangle.Intersects(destination) && tile.TileBlocked == false)
{
playerOneMotion.Y = -1f;
}
}
}
}
}
I'm trying to create rectangles for each tile so my sprite doesn't walk through them.
UPDATE
This is the code I currently have after modifying it with suggestions.
Tile tile;
Point playertile = new Point((int)Core.playerOneSprite.Position.X / Engine.TileWidth, (int)Core.playerOneSprite.Position.Y / Engine.TileHeight);
if (keyState.IsKeyDown(Keys.W) || (gamePadOneState.DPad.Up == ButtonState.Pressed))
{
Core.playerOneSprite.CurrentAnimation = AnimationKey.Up;
foreach (var layer in GraelenAreaOne.graelenAreaOneSplatterTileMapLayers)
{
tile = layer.GetTile(playertile.X, playertile.Y - 1);
// Check Collision With Tiles
if (tile.TileBlocked)
{
playerOneMotion.Y = 0;
//Console.WriteLine("Intersected with" + destination + tile.TileBlocked);
}
else
{
playerOneMotion.Y = -1;
}
}
}
I am now able to collide with tiles however my sprites rectangle is not intersecting properly. Position 0,0 of the sprite texture is in the top left most corner and it only a 1x1 pixel.
Why isn't the rectangle encompassing the entire texture as I have it set to 32x32?
You should just check whether the tile you want to go to is walkable or not, without having to bother yourself with rectangles.
Some pseudo code :
// Convert pixel coordinates to tile coords
Point playerTile = new Point(player.X / tileWidth, player.Y / tileHeight);
Point targetTile = new Point(target.X / tileWidth, target.Y / tileHeight);
// Take a decision
if(Direction == Up)
var tile = layer.GetTile(playerTile.X, playerTile.Y - 1);
if(tile == walkable)
// Move to your tile
else
...
Additionally, here's some code I wrote a while ago which might interest you in optimizing the drawing of your level by drawing only draws what's visible.
https://gamedev.stackexchange.com/questions/29121/organize-a-game-set/29930#29930
Note : https://gamedev.stackexchange.com/ is definitely a better place for these kind of questions.
EDIT
Here's a quick example that works for me :
Note that I return when player cannot move to tile.
Tiles : 0 is walkable, 1 is a wall, 2 is player
using System;
using System.Linq;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
using Point = Microsoft.Xna.Framework.Point;
namespace SlXnaApp1
{
public partial class GamePage : PhoneApplicationPage
{
private readonly GameTimer _timer;
private int[] _levels;
private Point _player;
private Texture2D _t1;
private Texture2D _t2;
private Texture2D _t3;
private Texture2D[] _textures;
private ContentManager _contentManager;
private SpriteBatch _spriteBatch;
public GamePage()
{
InitializeComponent();
// Get the content manager from the application
_contentManager = (Application.Current as App).Content;
// Create a timer for this page
_timer = new GameTimer();
_timer.UpdateInterval = TimeSpan.FromTicks(333333);
_timer.Update += OnUpdate;
_timer.Draw += OnDraw;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// Set the sharing mode of the graphics device to turn on XNA rendering
var graphicsDevice = SharedGraphicsDeviceManager.Current.GraphicsDevice;
graphicsDevice.SetSharingMode(true);
// 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
_t1 = new Texture2D(graphicsDevice, 32, 32);
_t1.SetData(Enumerable.Repeat(Color.Red, 32*32).ToArray());
_t2 = new Texture2D(graphicsDevice, 32, 32);
_t2.SetData(Enumerable.Repeat(Color.Green, 32*32).ToArray());
_t3 = new Texture2D(graphicsDevice, 32, 32);
_t3.SetData(Enumerable.Repeat(Color.Blue, 32*32).ToArray());
_textures = new[] {_t1, _t2, _t3};
_levels = new int[4*4]
{
2, 0, 0, 0,
0, 0, 1, 0,
1, 1, 1, 0,
0, 0, 0, 1,
};
_player = new Point();
TouchPanel.EnabledGestures = GestureType.Flick;
// Start the timer
_timer.Start();
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Stop the timer
_timer.Stop();
// Set the sharing mode of the graphics device to turn off XNA rendering
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
/// <summary>
/// Allows the page to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
private void OnUpdate(object sender, GameTimerEventArgs e)
{
Vector2 vector = new Vector2();
while (TouchPanel.IsGestureAvailable)
{
var gestureSample = TouchPanel.ReadGesture();
var vector2 = gestureSample.Delta;
vector2.Normalize();
vector = new Vector2((float) Math.Round(vector2.X), (float) Math.Round(vector2.Y));
}
Direction direction = new Direction();
if (vector.X > 0) direction = Direction.Right;
else if (vector.X < 0) direction = Direction.Left;
else if (vector.Y < 0) direction = Direction.Up;
else if (vector.Y > 0) direction = Direction.Down;
Point newPos = new Point();
switch (direction)
{
case Direction.None:
return;
case Direction.Up:
if (GetTile(_player.X, _player.Y - 1) == 0)
newPos = new Point(_player.X, _player.Y - 1);
else return;
break;
case Direction.Down:
if (GetTile(_player.X, _player.Y + 1) == 0)
newPos = new Point(_player.X, _player.Y + 1);
else return;
break;
case Direction.Left:
if (GetTile(_player.X - 1, _player.Y) == 0)
newPos = new Point(_player.X - 1, _player.Y);
else return;
break;
case Direction.Right:
if (GetTile(_player.X + 1, _player.Y) == 0)
newPos = new Point(_player.X + 1, _player.Y);
else return;
break;
default:
throw new ArgumentOutOfRangeException();
}
SetTile(_player.X, _player.Y, 0);
SetTile(newPos.X, newPos.Y, 2);
_player = newPos;
}
private int GetTile(int x, int y)
{
return _levels[y*4 + x];
}
private void SetTile(int x, int y, int value)
{
_levels[y*4 + x] = value;
}
/// <summary>
/// Allows the page to draw itself.
/// </summary>
private void OnDraw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
_spriteBatch.Begin();
for (int i = 0; i < _levels.Length; i++)
{
var tile = _levels[i];
Point point = new Point(i%4, i/4);
var texture2D = _textures[tile];
_spriteBatch.Draw(texture2D, Vector2.Multiply(new Vector2(point.X, point.Y), 32), Color.White);
}
_spriteBatch.End();
}
private enum Direction
{
None,
Up,
Down,
Left,
Right
}
}
}
Also, due to the way I built my level as a 1-dimensional array, the tile moves down when for instance it is at x=3, y=0 and going to the right; this is unintended at all but is normal as I don't check bounds. You'd want to keep your level as a 2-dimensional array for simplicity.
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.