I am brand new to XNA and C# (I started 2 days ago, so please forgive the sloppy programming), and I have run into a problem with a simple game I am making. I can't seem to find the solution in any of the online tutorials. I am trying to utilize rectangle collision detection but I cannot get it to work properly. The character falls completely through the floor and it seems there is no collision ever registering in any place. I cannot find my logic error here. Below I posted a bit of my code that pertains to the collision detection. I can post more if necessary. Thank you for the help in advance!
Level.cs
//The method below is called in the initial LoadContent method in Game1.cs. 'LoadBlocks()' is supposed to read in a text file and setup the level's "blocks" (the floor or platforms in the game).
public void LoadBlocks(int level){
if (level == 0)
{
fileName = "filepath";
}
System.IO.StreamReader file = new System.IO.StreamReader(fileName);
while ((line = file.ReadLine()) != null)
{
for (int i = 0; i < 35; i++)
{
rectBlock = new Rectangle((int)positionBlock.X, (int)positionBlock.Y, width / 30, height / 30);
if (line.Substring(i, 1).Equals(","))
{
positionBlock.X += (width / 100) * 24;
}
if (line.Substring(i, 1).Equals("#"))
{ //block -> (bool isPassable, Texture2D texture, Rectangle rectangle, Vector2 position)
block = new Block(false, textureBlock, rectBlock, positionBlock);
blocks.Add(block);
positionBlock.X += (width / 100) * 24;
}
}
positionBlock.Y += (height / 100) * 8;
positionBlock.X = 0;
}
}
//This method below updates the character 'bob' position and velocity.
public void UpdatePlayer()
{
bob.position += bob.velocity;
bob.rectangle = new Rectangle((int)bob.position.X, (int)bob.position.Y, width / 30, height / 30);
float i = 1;
bob.velocity.Y += 0.15f * i;
foreach (Block block in blocks)
{
if (bob.isOnTopOf(bob.rectangle, block.rectangle))
{
bob.velocity.Y = 0f;
bob.hasJumped = false;
}
}
}
Character.cs
//Here is my whole Character class for 'bob'
public class Character
{
int height = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
int width = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
int health;
String name;
bool gender;
Texture2D texture;
public Vector2 position;
public Vector2 velocity;
public bool hasJumped;
public Rectangle rectangle;
public Character(int newHealth, String newName, bool newGender, Texture2D newTexture, Vector2 newPosition)
{
health = newHealth;
gender = newGender;
name = newName;
texture = newTexture;
position = newPosition;
hasJumped = true;
rectangle = new Rectangle(0,0, width/30,height/30);
velocity = Vector2.Zero;
}
public bool isOnTopOf(Rectangle r1, Rectangle r2)
{
const int penetrationMargin = 5;
return (r1.Bottom >= r2.Top - penetrationMargin &&
r1.Bottom <= r2.Top &&
r1.Right >= r2.Left + 5 &&
r1.Left <= r2.Right - 5);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, rectangle,null, Color.White,0,position,SpriteEffects.None,0);
}
}
Sorry if this is too much posted code or is confusing, but any help is greatly appreciated! Thanks!
Your test condition for intersection appears to be incorrect, at least. There might be other problems with the code (there are several just giving a quick look at it), but let's focus on this:
r1.Bottom >= r2.Top - penetrationMargin &&
r1.Bottom <= r2.Top &&
r1.Right >= r2.Left + 5 &&
r1.Left <= r2.Right - 5;
First, don't code what exists in the library for you. Xna Rectangles have an Intersects method, so scrap that code and use it instead.
We'll look at the condition anyway:
r1's Bottom is below r2's Top minus a margin AND
r1's Bottom is above r2's Top AND (...)
This is already impossible. r1.Bottom cannot be both below and above r2.Top.
Further, even if all the comparisons were correct, you are using AND everywhere (&&), which means that the condition is only true if all 4 of them are true. Basically, you are not testing for intersection, you are testing for containement, i.e. you are testing that r1 is entirely within r2. An intersection test would join the conditions using OR (||) not AND (&&).
But again, although you might want to code it yourself for educative purposes, the best solution from an engineering perspective is to use what the library provides, here Rectangle.Intersects.
I figured it out, but I'm not sure why this actually works. Something was wrong with my draw method. I originally had:
spriteBatch.Draw(texture, rectangle, null, Color.White, 0, position, SpriteEffects.None, 0);
as my method but then I changed this to
spriteBatch.Draw(texture, rectangle, Color.White);
and now everything works fine.
Related
Very new developer here.
In my program I have a randomly generating world map using a simplex noise library. On top of this I am attempting to draw a tilemap of transparent 4x4 tiles that appear slightly translucent when the mouse is hovering over one.
I've got this working but it takes about 3 whole seconds for the highlighted tile to update to the mouse's current position. Is there anything I could do to solve this?
This is my code for the MouseState check in the tile class:
public override void Update(GameTime gameTime)
{
_previousMouse = _currentMouse;
_currentMouse = Mouse.GetState();
var mouseRectangle = new Rectangle(_currentMouse.X, _currentMouse.Y, 1, 1);
_isHovering = false;
if (mouseRectangle.Intersects(Rectangle))
{
_isHovering = true;
if (_currentMouse.LeftButton == ButtonState.Released && _previousMouse.LeftButton == ButtonState.Pressed)
{
Click?.Invoke(this, new EventArgs());
}
}
}
Sorry if this is formatted wrong or badly asked, first post so still getting to grips with everything :)
Invert the logic:
Instead of checking thousands of tile objects against the mouse, apply the mouse to a single object.
Assuming you have a list or array of tile objects:
Add a new object to check for mouse hover and click:
public class MouseDetect(Tile[] tiles) // replace with List<> as needed
{
int PrevHover = -1; // used to unHover
// if (Area == Screen) make the next two lines `const`, so the compiler will remove all uses...
int AreaX = 0; //Area x offset
int AreaY = 0; //Area y offset
int AreaW = 800; //Area width
int AreaH = 480; //Area height
const int Grid = 4; // assumes square
const int GridW = AreaW / Grid;
// I Will assume the `Delegate Click` in `Tile` is public
public void Update(MouseState ms, MouseState oms), //_currentMouse = ms and _previousMouse = oms;
{
int mouseIndex = (ms.X - AreaX) % Gridw + (ms.Y - AreaY) / GridW;
tiles[PrevHover].Hover = false;
PrevHover = mouseIndex;
tiles[PrevHover].Hover = true;
//Check Release
if(tiles[PrevHover].Hover && ms.LeftButton == ms.ButtonState.Released && oms.LeftButton == ButtonState.Pressed)
tiles[PrevHover].Click(tiles[PrevHover], new EventArgs());
}
}
Remove the Update from the Tile class.
Notes for anyone reading this later:
Never call Mouse.GetState(); more than once per step.
Predefined or framework names such as Rectangle should never be used as an identifier.
i.e renamed and corrected to CollRectangle
if (CollRectangle.Contains(ms.Position))
I recently got into using MonoGame, and I love the library.
However, I seem to be having some issues with drawing bezier curves
The result that my code produces looks something like this
Look bad, no?
The lines aren't smooth at all.
Let me show you some of the code:
//This is what I call to get all points between which to draw.
public static List<Point> computeCurvePoints(int steps)
{
List<Point> curvePoints = new List<Point>();
for (float x = 0; x < 1; x += 1 / (float)steps)
{
curvePoints.Add(getBezierPointRecursive(x, pointsQ));
}
return curvePoints;
}
//Calculates a point on the bezier curve based on the timeStep.
private static Point getBezierPointRecursive(float timeStep, Point[] ps)
{
if (ps.Length > 2)
{
List<Point> newPoints = new List<Point>();
for (int x = 0; x < ps.Length-1; x++)
{
newPoints.Add(interpolatedPoint(ps[x], ps[x + 1], timeStep));
}
return getBezierPointRecursive(timeStep, newPoints.ToArray());
}
else
{
return interpolatedPoint(ps[0], ps[1], timeStep);
}
}
//Gets the linearly interpolated point at t between two given points (without manual rounding).
//Bad results!
private static Point interpolatedPoint(Point p1, Point p2, float t)
{
Vector2 roundedVector = (Vector2.Multiply(p2.ToVector2() - p1.ToVector2(), t) + p1.ToVector2());
return new Point((int)roundedVector.X, (int)roundedVector.Y);
}
//Method used to draw a line between two points.
public static void DrawLine(this SpriteBatch spriteBatch, Texture2D pixel, Vector2 begin, Vector2 end, Color color, int width = 1)
{
Rectangle r = new Rectangle((int)begin.X, (int)begin.Y, (int)(end - begin).Length() + width, width);
Vector2 v = Vector2.Normalize(begin - end);
float angle = (float)Math.Acos(Vector2.Dot(v, -Vector2.UnitX));
if (begin.Y > end.Y) angle = MathHelper.TwoPi - angle;
spriteBatch.Draw(pixel, r, null, color, angle, Vector2.Zero, SpriteEffects.None, 0);
}
//DrawLine() is called as following. "pixel" is just a Texture2D with a single black pixel.
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
for(int x = 0; x < curvePoints.Count-1; x++)
{
DrawExtenstion.DrawLine(spriteBatch, pixel, curvePoints[x].ToVector2(), curvePoints[x + 1].ToVector2(), Color.Black, 2);
}
spriteBatch.End();
base.Draw(gameTime);
}
I managed to make the line a bit smoother by adding some manual Math.Round() calls to my interpolatedPoint method
//Gets the linearly interpolated point at t between two given points (with manual rounding).
//Better results (but still not good).
private static Point interpolatedPoint(Point p1, Point p2, float t)
{
Vector2 roundedVector = (Vector2.Multiply(p2.ToVector2() - p1.ToVector2(), t) + p1.ToVector2());
return new Point((int)Math.Round(roundedVector.X), (int)Math.Round(roundedVector.Y));
}
This produces the following result:
I had to remove one picture since Stackoverflow doesn't let me use more than two links
Are there any ways I can get this curve to be absolutely smooth?
Perhaps there is a problem with the DrawLine method?
Thanks in advance.
EDIT:
Okay, I managed to make the curve look a lot better by doing all the calculations with Vector2Ds and only converting it to a Point at the moment that it needs to be drawn
It still isn't perfect though :/
As Mike 'Pomax' Kamermans said,
it seems to have been a problem with the 2D surface not allowing subpixel drawing and thus causing rounding issues
Following craftworkgames' advice, I adapted the algorithm to draw the curve in 3D using a BasicEffect. This also allows for antialiasing, which smoothes out the curve a lot.
The result is the following:
A lot better!
Thank you very much for the helpful advice!
EDIT:
Here is the code I used for doing this.
I would also like to add that this webpage (http://gamedevelopment.tutsplus.com/tutorials/create-a-glowing-flowing-lava-river-using-bezier-curves-and-shaders--gamedev-919) helped me a lot while writing this code.
Also, please note that some of the names I used for defining the methods might not really make sense or can be confusing. This was something I quickly put together on an evening.
//Used for generating the mesh for the curve
//First object is vertex data, second is indices (both as arrays)
public static object[] computeCurve3D(int steps)
{
List<VertexPositionTexture> path = new List<VertexPositionTexture>();
List<int> indices = new List<int>();
List<Vector2> curvePoints = new List<Vector2>();
for (float x = 0; x < 1; x += 1 / (float)steps)
{
curvePoints.Add(getBezierPointRecursive(x, points3D));
}
float curveWidth = 0.003f;
for(int x = 0; x < curvePoints.Count; x++)
{
Vector2 normal;
if(x == 0)
{
//First point, Take normal from first line segment
normal = getNormalizedVector(getLineNormal(curvePoints[x+1] - curvePoints[x]));
}
else if (x + 1 == curvePoints.Count)
{
//Last point, take normal from last line segment
normal = getNormalizedVector(getLineNormal(curvePoints[x] - curvePoints[x-1]));
} else
{
//Middle point, interpolate normals from adjacent line segments
normal = getNormalizedVertexNormal(getLineNormal(curvePoints[x] - curvePoints[x - 1]), getLineNormal(curvePoints[x + 1] - curvePoints[x]));
}
path.Add(new VertexPositionTexture(new Vector3(curvePoints[x] + normal * curveWidth, 0), new Vector2()));
path.Add(new VertexPositionTexture(new Vector3(curvePoints[x] + normal * -curveWidth, 0), new Vector2()));
}
for(int x = 0; x < curvePoints.Count-1; x++)
{
indices.Add(2 * x + 0);
indices.Add(2 * x + 1);
indices.Add(2 * x + 2);
indices.Add(2 * x + 1);
indices.Add(2 * x + 3);
indices.Add(2 * x + 2);
}
return new object[] {
path.ToArray(),
indices.ToArray()
};
}
//Recursive algorithm for getting the bezier curve points
private static Vector2 getBezierPointRecursive(float timeStep, Vector2[] ps)
{
if (ps.Length > 2)
{
List<Vector2> newPoints = new List<Vector2>();
for (int x = 0; x < ps.Length - 1; x++)
{
newPoints.Add(interpolatedPoint(ps[x], ps[x + 1], timeStep));
}
return getBezierPointRecursive(timeStep, newPoints.ToArray());
}
else
{
return interpolatedPoint(ps[0], ps[1], timeStep);
}
}
//Gets the interpolated Vector2 based on t
private static Vector2 interpolatedPoint(Vector2 p1, Vector2 p2, float t)
{
return Vector2.Multiply(p2 - p1, t) + p1;
}
//Gets the normalized normal of a vertex, given two adjacent normals (2D)
private static Vector2 getNormalizedVertexNormal(Vector2 v1, Vector2 v2) //v1 and v2 are normals
{
return getNormalizedVector(v1 + v2);
}
//Normalizes the given Vector2
private static Vector2 getNormalizedVector(Vector2 v)
{
Vector2 temp = new Vector2(v.X, v.Y);
v.Normalize();
return v;
}
//Gets the normal of a given Vector2
private static Vector2 getLineNormal(Vector2 v)
{
Vector2 normal = new Vector2(v.Y, -v.X);
return normal;
}
//Drawing method in main Game class
//curveData is a private object[] that is initialized in the constructor (by calling computeCurve3D)
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
var camPos = new Vector3(0, 0, 0.1f);
var camLookAtVector = Vector3.Forward;
var camUpVector = Vector3.Up;
effect.View = Matrix.CreateLookAt(camPos, camLookAtVector, camUpVector);
float aspectRatio = graphics.PreferredBackBufferWidth / (float)graphics.PreferredBackBufferHeight;
float fieldOfView = MathHelper.PiOver4;
float nearClip = 0.1f;
float farClip = 200f;
//Orthogonal
effect.Projection = Matrix.CreateOrthographic(480 * aspectRatio, 480, nearClip, farClip);
foreach (var pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
effect.World = Matrix.CreateScale(200);
graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList,
(VertexPositionTexture[])curveData[0],
0,
((VertexPositionTexture[])curveData[0]).Length,
(int[])curveData[1],
0,
((int[])curveData[1]).Length/3);
}
base.Draw(gameTime);
}
Also, this image may be able to show what the code does a little bit better
So, I needed something like this working with SpriteBatch, so I poked around at the original code a bit (with the Point -> Vector2 and rounding changes.
If you render every other segment as a different color, and with a large enough width and low enough steps, you can see why it resulted in jagged lines with larger values of width. It turns out the lines go past where they should end!
Lines going past their end:
This is because the DrawLine function adds width to length of the segment. However, without this, you see a bunch of disconnected segments for anything that actually curves.
Lines being disconnected:
There's probably some math you can do to get the appropriate value to add here, based on the angle of the connecting points. I don't know math well enough for that, so I'm just using a fixed value for them all. (10 seems to be the sweet spot for the image I posted, although it isn't perfect due to the low step count.)
(The following is DrawLine adjusted with the width being added, to using a constant instead.)
// Method used to draw a line between two points.
public static void DrawLine(this SpriteBatch spriteBatch, Texture2D pixel, Vector2 begin, Vector2 end, Color color, int width = 1)
{
Rectangle r = new Rectangle((int)begin.X, (int)begin.Y, (int)(end - begin).Length() + 10, width);
Vector2 v = Vector2.Normalize(begin - end);
float angle = (float)Math.Acos(Vector2.Dot(v, -Vector2.UnitX));
if (begin.Y > end.Y) angle = MathHelper.TwoPi - angle;
spriteBatch.Draw(pixel, r, null, color, angle, Vector2.Zero, SpriteEffects.None, 0);
}
Im a C#/XNA student and I've recently been working on an isometric tile engine and so far it works fairly well. But im having problem trying to figure out on how to do collision, this is what my tile engine does at the moment:
Draws the world from an image and place a tile depending on what color is on my image. For instance color red would draw a grass tile. (Tiles are 64x32)
Camera following player, and my draw loop only draws what the camera sees.
This is how my game looks if that would be of any help:
I don't know what sort of collision would work best. Should i do collision points, or intersects or any other sort of collision. I've read somewhere that you could do Worldtoscreen/Screentoworld but im far to inexperienced and don't know how that works nor how the code would look like.
Here is my code drawing tiles etc:
class MapRow
{
public List<MapCell> Columns = new List<MapCell>();
}
class TileMap
{
public List<MapRow> Rows = new List<MapRow>();
public static Texture2D image;
Texture2D tileset;
TileInfo[,] tileMap;
Color[] pixelColor;
public TileMap(string TextureImage, string Tileset)
{
tileset = Game1.Instance.Content.Load<Texture2D>(Tileset);
image = Game1.Instance.Content.Load<Texture2D>(TextureImage);
pixelColor = new Color[image.Width * image.Height]; // pixelColor array that is holding all pixel in the image
image.GetData<Color>(pixelColor); // Save all the pixels in image to the array pixelColor
tileMap = new TileInfo[image.Height, image.Width];
int counter = 0;
for (int y = 0; y < image.Height; y++)
{
MapRow thisRow = new MapRow();
for (int x = 0; x < image.Width; x++)
{
tileMap[y, x] = new TileInfo();
if (pixelColor[counter] == new Color(0, 166, 81))
{
tileMap[y, x].cellValue = 1;//grass
}
if (pixelColor[counter] == new Color(0, 74, 128))
{
tileMap[y, x].cellValue = 2;//water
}
if (pixelColor[counter] == new Color(255, 255, 0))
{
tileMap[y, x].cellValue = 3;//Sand
}
tileMap[y, x].LoadInfoFromCellValue();//determine what tile it should draw depending on cellvalue
thisRow.Columns.Add(new MapCell(tileMap[y, x]));
counter++;
}
Rows.Add(thisRow);
}
}
public static int printx;
public static int printy;
public static int squaresAcross = Settings.screen.X / Tile.TileWidth;
public static int squaresDown = Settings.screen.Y / Tile.TileHeight;
int baseOffsetX = -32;
int baseOffsetY = -64;
public void draw(SpriteBatch spriteBatch)
{
printx = (int)Camera.Location.X / Tile.TileWidth;
printy = (int)Camera.Location.Y / Tile.TileHeight;
squaresAcross = (int)Camera.Location.X / Tile.TileWidth + Settings.screen.X / Tile.TileWidth;
squaresDown = 2*(int)Camera.Location.Y / Tile.TileHeight + Settings.screen.Y / Tile.TileHeight + 7;
for (printy = (int)Camera.Location.Y / Tile.TileHeight; printy < squaresDown; printy++)
{
int rowOffset = 0;
if ((printy) % 2 == 1)
rowOffset = Tile.OddRowXOffset;
for (printx = (int)Camera.Location.X / Tile.TileWidth; printx < squaresAcross; printx++)
{
if (tileMap[printy, printx].Collides(MouseCursor.mousePosition))
Console.WriteLine(tileMap[printy, printx].tileRect);
foreach (TileInfo tileID in Rows[printy].Columns[printx].BaseTiles)
{
spriteBatch.Draw(
tileset,
tileMap[printy, printx].tileRect = new Rectangle(
(printx * Tile.TileStepX) + rowOffset + baseOffsetX,
(printy * Tile.TileStepY) + baseOffsetY,
Tile.TileWidth, Tile.TileHeight),
Tile.GetSourceRectangle(tileID.cellValue),
Color.White,
0.0f,
Vector2.Zero,
SpriteEffects.None,
tileID.drawDepth);
}
}
}
}
}
Why don't you just draw stuff just like in normal tile based games, and then rotate the camera with a 45degree? Of course then you'd need to make your graphics a bit odd, but would be easier to handle the tiles.
But if you prefer your way, then I'd suggest using simple math to calculate the "tile to the right", "tile to the left" , "tile to the up" and "tile to the down" ones, you know, the tiles around the player(or another tile). You can simply work with your lists, and with some math, basic math, like getting the next tile, is quite simple.
Edit:
You could get the player's next position's tile value with a code something like this:
tileMap[Math.Floor((player.y+playerVelociy.Y)/tileHeight)]
[Math.Floor((player.x+playerVelocity.X)/tileWidth)]
In this code, I assume that the first tile is at 0,0 and you're drawing to right and down. (If not, then just change the Math.Floor to Math.Ceil)
THIS link could help you get the idea, however it's in AS3.0, only the syntax is different.
I am working on a space shooter game using XNA and have followed multiple tutorials to create a parallax background. So far I can get it to go along one axis, either X or Y, but not both at the same time. I have a camera class that follows the player, which the player moves (instead of the 'world'), since I figured it would be easier to just move the player versus moving everything else around the player.
So far, the background doesn't keep up with the player, and it also can't comprehend both axis at the same time. I thought about a tile engine, but that wouldn't let me parallax different layers would it?
Could anyone help me understand what I need to do, or recommend a tutorial that can do both axis at the same time? I can't seem to find the answer on my own this time.
Here is the code for my background class:
class Background
{
// Textures to hold the two background images
Texture2D spaceBackground, starsParallax;
int backgroundWidth = 2048;
int backgroundHeight = 2048;
int parallaxWidth = 2048;
int parallaxHeight = 2048;
int backgroundWidthOffset;
int backgroundHeightOffset;
int parallaxWidthOffset;
int parallaxHeightOffset;
public int BackgroundWidthOffset
{
get { return backgroundWidthOffset; }
set
{
backgroundWidthOffset = value;
if (backgroundWidthOffset < 0)
{
backgroundWidthOffset += backgroundWidth;
}
if (backgroundWidthOffset > backgroundWidth)
{
backgroundWidthOffset -= backgroundWidth;
}
}
}
public int BackgroundHeightOffset
{
get { return backgroundHeightOffset; }
set
{
backgroundHeightOffset = value;
if (backgroundHeightOffset < 0)
{
backgroundHeightOffset += backgroundHeight;
}
if (backgroundHeightOffset > backgroundHeight)
{
backgroundHeightOffset -= backgroundHeight;
}
}
}
public int ParallaxWidthOffset
{
get { return parallaxWidthOffset; }
set
{
parallaxWidthOffset = value;
if (parallaxWidthOffset < 0)
{
parallaxWidthOffset += parallaxWidth;
}
if (parallaxWidthOffset > parallaxWidth)
{
parallaxWidthOffset -= parallaxWidth;
}
}
}
public int ParallaxHeightOffset
{
get { return parallaxHeightOffset; }
set
{
parallaxHeightOffset = value;
if (parallaxHeightOffset < 0)
{
parallaxHeightOffset += parallaxHeight;
}
if (parallaxHeightOffset > parallaxHeight)
{
parallaxHeightOffset -= parallaxHeight;
}
}
}
// Constructor when passed a Content Manager and two strings
public Background(ContentManager content,
string sBackground, string sParallax)
{
spaceBackground = content.Load<Texture2D>(sBackground);
backgroundWidth = spaceBackground.Width;
backgroundHeight = spaceBackground.Height;
starsParallax = content.Load<Texture2D>(sParallax);
parallaxWidth = starsParallax.Width;
parallaxHeight = starsParallax.Height;
}
public void Draw(SpriteBatch spriteBatch)
{
// Draw the background panel, offset by the player's location
spriteBatch.Draw(
spaceBackground,
new Rectangle(-1 * backgroundWidthOffset,
-1 * backgroundHeightOffset,
backgroundWidth,
backgroundHeight),
Color.White);
// If the right edge of the background panel will end
// within the bounds of the display, draw a second copy
// of the background at that location.
if (backgroundWidthOffset > backgroundWidth)
{
spriteBatch.Draw(
spaceBackground,
new Rectangle(
(-1 * backgroundWidthOffset) + backgroundWidth, 0,
backgroundWidth, backgroundHeight),
Color.White);
}
else //(backgroundHeightOffset > backgroundHeight - viewportHeight)
{
spriteBatch.Draw(
spaceBackground,
new Rectangle(
0, (-1 * backgroundHeightOffset) + backgroundHeight,
backgroundHeight, backgroundHeight),
Color.White);
}
// Draw the parallax star field
spriteBatch.Draw(
starsParallax,
new Rectangle(-1 * parallaxWidthOffset,
0, parallaxWidth,
parallaxHeight),
Color.SlateGray);
// if the player is past the point where the star
// field will end on the active screen we need
// to draw a second copy of it to cover the
// remaining screen area.
if (parallaxWidthOffset > parallaxWidth)
{
spriteBatch.Draw(
starsParallax,
new Rectangle(
(-1 * parallaxWidthOffset) + parallaxWidth,
0,
parallaxWidth,
parallaxHeight),
Color.White);
}
}
}
The main game then has ties to move the backgrounds, along with the player.
background.BackgroundWidthOffset -= 2;
background.ParallaxWidthOffset -= 1;
Visually, the background is sort of jumpy, and seems to randomly skip or overlap background tiles.
I've used this method in the past with great results:
http://www.david-gouveia.com/scrolling-textures-with-zoom-and-rotation/
It uses a shader to accomplish the effect, resulting in a fast implementation.
There is a complete example here.
Alright, so I am pretty new to XNA programming and I am trying to code a platformer. I have implemented pixel-perfect collision but it seems to fail for no apparent reason sometimes (I couldn't figure out a pattern) and the hero sprite goes through platforms.
static bool IntersectsPixel(Rectangle rect1, Color[] data1, Rectangle rect2, Color[] data2)
{
int top = Math.Max (rect1.Top, rect2.Top);
int bottom = Math.Min(rect1.Bottom, rect2.Bottom);
int left = Math.Max (rect1.Left,rect2.Left);
int right = Math.Min(rect1.Right,rect2.Right);
//Top
for(int y = top; y<bottom;y++)
for (int x = left; x < right; x++)
{
Color color1 = data1[x-rect1.Left + (y-rect1.Top) * rect1.Width];
Color color2 = data2[x - rect2.Left + (y - rect2.Top) * rect2.Width];
if (color1.A != 0 && color2.A != 0)
return true;
}
return false;
}
And here's the Update Method
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
foreach(Platform platform in platformList)
{
Rectangle check = new Rectangle(hero.GetRectangle().X - 300, hero.GetRectangle().Y - 300, 600, 600);
if(check.Intersects(platform.rectangle))
if (IntersectsPixel(hero.GetRectangle(), hero.textureData, platform.rectangle, platform.platformTextureData))
{
int direction = CheckDirection(platform,hero);
if (hero.hasJumped == true && direction == 3 && hero.velocity.Y <= 0 )
{
hero.velocity.Y = 0f;
hero.SetPosition(new Vector2((float)hero.GetPosition().X, (float)platform.rectangle.Bottom));
break;
}
else
if (direction == 4 && hero.velocity.X >= 0)
{
hero.velocity.X = 1;
hero.SetPosition(new Vector2((float)platform.rectangle.Left - (float)hero.GetRectangle().Width, (float)hero.GetPosition().Y));
break;
}
else
if (direction == 2 && hero.velocity.X <= 0)
{
hero.velocity.X = -1;
hero.SetPosition(new Vector2((float)platform.rectangle.Right - 1, (float)hero.GetPosition().Y));
break;
}
else
if (direction == 1 && hero.velocity.Y >= 0)
{
hero.velocity.Y = 0;
hero.hasJumped = false;
hero.SetPosition(new Vector2((float)hero.GetRectangle().X, (float)platform.rectangle.Y - (float)hero.GetRectangle().Height + 1));
hero.SetRectangle(new Rectangle((int)hero.GetPosition().X, (int)hero.GetPosition().Y, (int)hero.GetSize().X, (int)hero.GetSize().Y));
break;
}
}
}
hero.Update(gameTime);
camera.Update(gameTime, hero, screenBounds);
base.Update(gameTime);
}
And here's the direction check:
private int CheckDirection(Platform platform,Hero hero)
{
int distance = Math.Abs(platform.rectangle.Top - hero.GetRectangle().Bottom);
int direction = 1; //Top
if (distance > Math.Abs(platform.rectangle.Right - hero.GetRectangle().Left))
{
distance = Math.Abs(platform.rectangle.Right - hero.GetRectangle().Left);
direction = 2;
}
if (distance > Math.Abs(platform.rectangle.Bottom - hero.GetRectangle().Top))
{
distance = Math.Abs(platform.rectangle.Bottom - hero.GetRectangle().Top);
direction = 3;
}
if (distance > Math.Abs(platform.rectangle.Left - hero.GetRectangle().Right))
{
direction = 4;
distance = Math.Abs(platform.rectangle.Left - hero.GetRectangle().Right);
}
return direction;
}
Those are all my functions related to collision detection. If you happen to have any ideea of what might cause this, please let me know.
Thank you very much !
One thing I see that might be a possible reason is that right in the beginning where you build a box around both objects:
int top = Math.Max (rect1.Top, rect2.Top);
int bottom = Math.Min(rect1.Bottom, rect2.Bottom);
int left = Math.Max (rect1.Left,rect2.Left);
int right = Math.Min(rect1.Right,rect2.Right);
Unless you flipped it, typically XNA's Y grows positive as you go down (not up). This means you would actually want to take the Max of Bottom, and the Min of Top, since Top will always be less than Bottom. It looks like you already knew that though by the look of this line:
for(int y = top; y<bottom;y++)
I'll keep looking (it's difficult to test without the actual project). Another recommendation I'd give you is to do some debug drawing. Draw where the intersection functions thinks that pixels are touching or not touching.
Because of lag, Your character could be moving more than the blocks height per frame, so the collision may think it is in between blocks, and cause it to fly through.
https://gamedev.stackexchange.com/questions/30458/platformer-starter-kit-collision-issues
Check that out, It solved the problem for me.
Either slowing down movement, or clamping the change in position to the size of a block should solve this.