In Farseer XNA4.0 C# Physics engine based on Box2D
if i use BodyFactory.CreateRectangle(world, w, h, density, new Vector2(x, y)); method to create a body
how can i get the width and height back from the body?
currently im saving the width and height but im wondering if it can be retrieved from a fixture or shape or something. have made some attempts but to no avail. it would save me two floats per entity that im creating.
thanks for any help.
Ill preface this by saying i've never used Farseer, but looking at the class definitions, it doesn't look like there's a simple way to get what you want. If you look at the BodyFactory.CreateRectangle method, it doesn't store directly the Height or Width values:
public static Body CreateRectangle(World world, float width, float height, float density, Vector2 position,
object userData)
{
if (width <= 0)
throw new ArgumentOutOfRangeException("width", "Width must be more than 0 meters");
if (height <= 0)
throw new ArgumentOutOfRangeException("height", "Height must be more than 0 meters");
Body newBody = CreateBody(world, position);
Vertices rectangleVertices = PolygonTools.CreateRectangle(width / 2, height / 2);
PolygonShape rectangleShape = new PolygonShape(rectangleVertices, density);
newBody.CreateFixture(rectangleShape, userData);
return newBody;
}
Instead, it creates a set of vertices that it assigns to the shape of the body (in this case a rectangle), however, it doesn't appear from a cursory glance that there is a way to get at these vertices.
So long answer short, there isn't a direct method that I can find that will give you the straight float value of the height or width of your rectangle. You might be able to get at the vertices in some way and calculate it back out, but this would require you taking the Body and parsing through its fixture list and figuring out which one is your rectangle.
At the end of the day, if you need to get directly at your Height and Width, I would recommend just creating a custom class and storing the body together with the float values, and using getters/setters. It's likely to be a less expensive operation than looping through every fixture on your objects and trying to determine which is your rectangle and then calculating it back out.
Related
I want to detect pixel-perfect collisions between 2 sprites.
I use the following function which I have found online, but makes total sense to me.
static bool PerPixelCollision(Sprite a, Sprite b)
{
// Get Color data of each Texture
Color[] bitsA = new Color[a.Width * a.Height];
a.Texture.GetData(0, a.CurrentFrameRectangle, bitsA, 0, a.Width * a.Height);
Color[] bitsB = new Color[b.Width * b.Height];
b.Texture.GetData(0, b.CurrentFrameRectangle, bitsB, 0, b.Width * b.Height);
// Calculate the intersecting rectangle
int x1 = (int)Math.Floor(Math.Max(a.Bounds.X, b.Bounds.X));
int x2 = (int)Math.Floor(Math.Min(a.Bounds.X + a.Bounds.Width, b.Bounds.X + b.Bounds.Width));
int y1 = (int)Math.Floor(Math.Max(a.Bounds.Y, b.Bounds.Y));
int y2 = (int)Math.Floor(Math.Min(a.Bounds.Y + a.Bounds.Height, b.Bounds.Y + b.Bounds.Height));
// For each single pixel in the intersecting rectangle
for (int y = y1; y < y2; ++y)
{
for (int x = x1; x < x2; ++x)
{
// Get the color from each texture
Color colorA = bitsA[(x - (int)Math.Floor(a.Bounds.X)) + (y - (int)Math.Floor(a.Bounds.Y)) * a.Texture.Width];
Color colorB = bitsB[(x - (int)Math.Floor(b.Bounds.X)) + (y - (int)Math.Floor(b.Bounds.Y)) * b.Texture.Width];
if (colorA.A != 0 && colorB.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
{
return true;
}
}
}
//If no collision occurred by now, we're clear.
return false;
}
(all the Math.floor are useless, I copied this function from my current code where I'm trying to make it work with floats).
It reads the color of the sprites in the rectangle portion that is common to both sprites.
This actually works fine, when I display the sprites at x/y coordinates where x and y are int's (.Bounds.X and .Bounds.Y):
View an example
The problem with displaying sprites at int's coordinates is that it results in a very jaggy movement in diagonals:
View an example
So ultimately I would like to not cast the sprite position to int's when drawing them, which results in a smooth(er) movement:
View an example
The issue is that the PerPixelCollision works with ints, not floats, so that's why I added all those Math.Floor. As is, it works in most cases, but it's missing one line and one row of checking on the bottom and right (I think) of the common Rectangle because of the rounding induced by Math.Floor:
View an example
When I think about it, I think it makes sense. If x1 is 80 and x2 would actually be 81.5 but is 81 because of the cast, then the loop will only work for x = 80, and therefore miss the last column (in the example gif, the fixed sprite has a transparent column on the left of the visible pixels).
The issue is that no matter how hard I think about this, or no matter what I try (I have tried a lot of things) - I cannot make this work properly. I am almost convinced that x2 and y2 should have Math.Ceiling instead of Math.Floor, so as to "include" the last pixel that otherwise is left out, but then it always gets me an index out of the bitsA or bitsB arrays.
Would anyone be able to adjust this function so that it works when Bounds.X and Bounds.Y are floats?
PS - could the issue possibly come from BoxingViewportAdapter? I am using this (from MonoExtended) to "upscale" my game which is actually 144p.
Remember, there is no such thing as a fractional pixel. For movement purposes, it completely makes sense to use floats for the values and cast them to integer pixels when drawn. The problem is not in the fractional values, but in the way that they are drawn.
The main reason the collisions are not appearing to work correctly is the scaling. The colors for the new pixels in between the diagonals get their colors by averaging* the surrounding pixels. The effect makes the image appear larger than the original, especially on the diagonals.
*there are several methods that may be used for the scaling, bi-cubic and linear are the most common.
The only direct(pixel perfect) solution is to compare the actual output after scaling. This requires rendering the entire screen twice, and requires the scale factor more computations. (not recommended)
Since you are comparing the non-scaled images your collisions appear to be off.
The other issue is movement speed. If you are moving faster than one pixel per Update(), detecting per pixel collisions is not enough, if the movement is to be restricted by the obstacle. You must resolve the collision.
For enemies or environmental hazards your original code is sufficient and collision resolution is not required. It will give the player a minor advantage.
A simple resolution algorithm(see below for a mathematical solution) is to unwind the movement by half, check for collision. If it is still colliding, unwind the movement by a quarter, otherwise advance it by a quarter and check for collision. Repeat until the movement is less than 1 pixel. This runs log of Speed times.
As for the top wall not colliding perfectly: If the starting Y value is not a multiple of the vertical movement speed, you will not land perfectly on zero. I prefer to resolve this by setting the Y = 0, when Y is negative. It is the same for X, and also when X and Y > screen bounds - origin, for the bottom and right of the screen.
I prefer to use mathematical solutions for collision resolution. In your example images, you show a box colliding with a diamond, the diamond shape is represented mathematically as the Manhattan distance(Math.Abs(x1-x2) + Math.Abs(y1-y2)). From this fact, it is easy directly calculate the resolution to the collision.
On optimizations:
Be sure to check that the bounding Rectangles are overlapping before calling this method.
As you have stated, remove all Math.Floors, since, the cast is sufficient. Reduce all calculations inside of the loops not dependent on the loop variable outside of the loop.
The (int)a.Bounds.Y * a.Texture.Width and (int)b.Bounds.Y * b.Texture.Width are not dependent on the x or y variables and should be calculated and stored before the loops. The subtractions 'y-[above variable]` should be stored in the "y" loop.
I would recommend using a bitboard(1 bit per 8 by 8 square) for collisions. It reduces the broad(8x8) collision checks to O(1). For a resolution of 144x144, the entire search space becomes 18x18.
you can wrap your sprite with a rectangle and use its function called Intersect,which detedct collistions.
Intersect - XNA
I recently posted a question on here
Randomly generated tunnel walls that don't jump around from one to the next
That pretty much said I was getting a "drunken walk" tunnel instead of a correlated one due to a lack of an algorithm.
The answer works to an extent for what I'm attempting to create but I couldn't figure out how to implement the algorithm properly for my game.
I believe step one is to create the offsets for the pairs of walls. I want the images that appear (named: "Left1" and "Right1" , "Left2 and "Right2".... "Left41" and Right41") on each side of the screen to be "re-spawning" slightly offset to the left or right in succession of each other in order to create the illusion of a tunnel in appearance. I attempted to use the code provided to me in an answer to create an offset for a set of my walls but I get few errors when attempting to build. It was telling me first that a local declaration of 'Left1' hides instance variable, which I believe is because it's been declared already as a variable within my function? That came up twice.. Along with that I have an error that says Assigning to 'UIImageView *__strong' from incompatible type 'float'. How can I fix these? Or is it something I'm doing wrong elsewhere?
I'm kind of lost on this part since I don't know how to really rearrange it, due to the semantic issues.
I can answer any questions about the program to make it easier to assess the situation.
-(void) TunnelMovement{
CGFloat Left1 = 14;
for( int i = 0; i < 41; i++ ){
CGFloat offset = (CGFloat)arc4random_uniform(2*100) - 100;
Left1 += offset;
Right1 = Left1 + 14;
//...
}
}
This is what I have exactly in my code after trying to fill in the blanks from the answer given. Not sure if I put in all the necessities or what. Thank you.
[I can give you quick correction on that error message, but you are trying to run and you can't even walk yet, you can barely crawl.
Stack Overflow is a programming forum but it is not the place to learn or teach programming, people won't have the patience to teach basics.]
You can't assign the value of one thing to another if they are totally different things. In the line:
Right1 = Left1 + 14;
Right1 is a UIImageView and Left1 is a CGFloat, they are completely different things. Left1 is just a simple number, Right1 is a complex object.
You're trying to position UIImageView so you need to assign the value of Left1 + 14 to a part of the UIImageView that will position it and which is also a CGFloat.
So you need to assign it to something like the bounds or the frame or the center's x value, such as
Right1.center.x = Left1 + 14;
The warning is because you have two different things with the same name, you have UIImageView called Left1 but then you have a CGFloat also called Left1, you shouldn't give the same name to two different things - the CGFloat Left1 hides the UIImageView called Left1. You should have called it Left1X or LeftWallX or similar as it is in the answer to your other question.
That's just the error and warning, you're probably not ready to move on to the semantics of how to draw the maze if you were strugging with these two. In particular you will as a minimum need to know what the bounds, frame, center, x, y, CGPoint, CGRect, CGSize all are in order to fully understand what value to change and what effect changing those values will have on the position of the UIImageViews.
---- EDIT --- object programming 101
When you have an object like UIImageView it is like a having a house, the house can have a name and have lots of rooms in it, each with a name, within each room there are lots of things. You are trying to do the equivalent of replace the old television in one room with a new television.
So Right1 is like a house
Right1.center is like a room in a house
Right1.center.x is like the television in the room called center which is in the house called Right1.
Right1 = Left1 + 14;
is like trying to swap a new television for a house, instead of trying to swap it for a televsion within a room within a house.
Is that analogy too dumb?
I'm learning Corona SDK and am new to lua as well (i mainly do ruby and some javascript).
I have a bar that i want to fill up as the user does stuff. I've set it up as follows:
--outer rectangle
powerBar = display.newRect(210, 6, 24, 9)
powerBar.strokeWidth = 1
powerBar:setStrokeColor(254,203,50)
powerBar:setFillColor(0,0,0,0)
--inner rectangle which fills up
powerBarFill = display.newRect(211,7,0,7)
powerBarFill:setFillColor(234,183,30)
When the "stuff" happens, i add 1 to powerBarFill.width, which i thought would make it grow from left to right. But, it's actually growing out from the centre, ie its x is in the centre and the width extends either side from that.
Whats the best way to keep the left side static and grow the right side? Can i set it so that it's x position is actually on the left hand side rather than in the middle? Seems like that might do it.
cheers in advance
I've run into this problem as well when creating a progress bar. The problem is with the rect's reference point. The default reference point is in the center of an object, as you've noticed. You can use object:setReferencePoint() to change it. I believe you want to use the display.BottomLeftReferencePoint value:
powerBar:setReferencePoint(display.BottomLeftReferencePoint)
Keep in mind that you have to set this value before you set your x,y values. So in your case you'll need to set the reference point after creating the rectangle, and then assign values to x,y again (even though you already did this in the newRect constructor):
powerBar = display.newRect(210, 6, 24, 9)
powerBar:setReferencePoint(display.BottomLeftReferencePoint)
powerBar.x, powerBar.y = 210, 6
If it's width is from the X position on both sides:
1) It should start at:
Centre - (Width when it's full / 2)
2) Every frame, add:
incrs = 1 --Amount to increase by
width = width + incrs
x = x + incrs / 2
I'm quite new to XNA so excuse me if I ask a 'silly' question but I couldn't find an answer.
I have a problem with the terrain rendered from a heightmap: the terrain I get is too small, I need something larger for my game but I'd like to keep the heigh tdata updated - so I can check for collisions later. (height data being a 2 dimensional array which holds the heights of each point - in my program it's called 'dateInaltime').
The problem is that if I modify the scale of the terrain, the collision checker will use the old values (from the original/small terrain) so I'll get wrong collision points.
My terrain class looks like this.
How can I make the terrain larger but also extend the height data array?
Change this part:
vertex[x + y * lungime].Position = new Vector3(x, dateInaltime[x, y], -y);
to:
vertex[x + y * lungime].Position = new Vector3(x, dateInaltime[x, y], -y) * new Vector3(10);
It should separate the vertices by a scale of 10 (or whatever number you choose).
How to make a 2d world with fixed size, which would repeat itself when reached any side of the map?
When you reach a side of a map you see the opposite side of the map which merged togeather with this one. The idea is that if you didn't have a minimap you would not even notice the transition of map repeating itself.
I have a few ideas how to make it:
1) Keeping total of 3x3 world like these all the time which are exactly the same and updated the same way, just the players exists in only one of them.
2) Another way would be to seperate the map into smaller peaces and add them to required place when asked.
Either way it can be complicated to complete it. I remember that more thatn 10 years ago i played some game like that with soldiers following each other in a repeating wold shooting other AI soldiers.
Mostly waned to hear your thoughts about the idea and how it could be achieved. I'm coding in XNA(C#).
Another alternative is to generate noise using libnoise libraries. The beauty of this is that you can generate noise over a theoretical infinite amount of space.
Take a look at the following:
http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile
There is also an XNA port of the above at: http://bigblackblock.com/tools/libnoisexna
If you end up using the XNA port, you can do something like this:
Perlin perlin = new Perlin();
perlin.Frequency = 0.5f; //height
perlin.Lacunarity = 2f; //frequency increase between octaves
perlin.OctaveCount = 5; //Number of passes
perlin.Persistence = 0.45f; //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;
//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);
//Get a section
_map.GeneratePlanar(left, right, top, down);
GeneratePlanar is the function to call to get the sections in each direction that will connect seamlessly with the rest of your world.
If the game is tile based I think what you should do is:
Keep only one array for the game area.
Determine the visible area using modulo arithmetics over the size of the game area mod w and h where these are the width and height of the table.
E.g. if the table is 80x100 (0,0) top left coordinates with a width of 80 and height of 100 and the rect of the viewport is at (70,90) with a width of 40 and height of 20 you index with [70-79][0-29] for the x coordinate and [90-99][0-9] for the y. This can be achieved by calculating the index with the following formula:
idx = (n+i)%80 (or%100) where n is the top coordinate(x or y) for the rect and i is in the range for the width/height of the viewport.
This assumes that one step of movement moves the camera with non fractional coordinates.
So this is your second alternative in a little bit more detailed way. If you only want to repeat the terrain, you should separate the contents of the tile. In this case the contents will most likely be generated on the fly since you don't store them.
Hope this helped.