Reputation: 127
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.
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?
Upvotes: 0
Views: 498
Reputation: 16652
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.
Upvotes: 3