I followed a unity tutorial to make a memory game but after adding more cards, they now go off the right of the screen.
The code uses a single card in the Unity designer and all other cards are instantiated and positioned from that (using startpos)
public const float offset = 2.5f;
Vector3 startpos = originalCard.transform.position;
for (int i = 0; i < numCols; i++)
{
for (int j = 0; j < numRows; j++)
{
card = Instantiate(originalCard) as MemoryCard;
float posX = (offset * i) + startpos.x;
float posY = - (offset * j) + startpos.y;
card.transform.position = new Vector3(posX, posY, startpos.z);
}
}
I would like to center the cards in the screen but not sure how given that the screen dimensions via Screen.width are in pixels but the Vector code is not and the x component of the original card (startpos.x) is actually negative.
Easiest way is to move the original card in your scene view. If you click your camera u can see it's bounds so click the camera, then move you starting card as far left as you can and still see it. If they still don't all fit in camera, you can change the card width and do some math to see how many cards you can fit.
The alternative solution, but also easy, is to adjust your camera bounds to encompass the cards where they are.
Related
I want to find the 2D bounding box (BB) of the visible part of an Game-object in Unity3D.**
Thanks to having no reputation i cant post images, so here are links to Imgur:
Starting Point: Image
Showing the initial scene camera. For example i want to get the BB of the wheels of the passing car.
Goal: Image
Showing the quite small BB i want to get in an efficient way. The method used is very costly and i want to avoid it. Its works on a segmented version of the input picture, looking similar to this: Image.
Current Approach:
Image
This uses the bounds of all GameObjects (GO) tagged as wheels and calls a function similar like this:
Rect GetBoundingBox(GameObject go, Camera camera, float margin = 0f)
{
Rect rect = new Rect(-1f, -1f, -1f, -1f);
Vector3[] v; // Vertices
// Object is behind camera
if (camera.WorldToScreenPoint(go.GetComponentInChildren<Renderer>().bounds.center).z < 0)
return rect;
// get first mesh filter
v = go.GetComponentInChildren<MeshFilter>().mesh.vertices;
// maybe the game object hast no mesh yet... and is empty
if (v.Length < 1 || v == null)
return rect;
for (int i = 0; i < v.Length; i++)
{
//World space
v[i] = go.transform.TransformPoint(v[i]);
//GUI space
v[i] = camera.WorldToScreenPoint(v[i]);
v[i].y = Screen.height - v[i].y;
}
Vector3 min = v[0];
Vector3 max = v[0];
for (int i = 1; i < v.Length; i++)
{
min = Vector3.Min(min, v[i]);
max = Vector3.Max(max, v[i]);
}
//Construct a rect of the min and max positions and apply some margin
rect = Rect.MinMaxRect(min.x, min.y, max.x, max.y);
// apply optional margin
rect.xMin -= margin;
rect.xMax += margin;
rect.yMin -= margin;
rect.yMax += margin;
return rect;
}
Two problems appear.
Its getting all BB even if the GO is not visible by the camera.
Even if its visible, the boundaries are as large as the GO it self, not as the visible part.
I am very happy about every hint in a good direction!!
For interested people the background is the development of an application to generate/simulate labeled training data for an deep learning project.
Alright, so today I decided to try to further optimize my collision detection code for my tile engine.
This is what I did:
Circle class checks if there are points within range. If there are, then check for collision between player and tile.
Code:
int tileWidth = 128;
int tileHeight = 128;
int[,] Layer3 = { 1, 1, 1, etc... };
int tileMapWidth = Layer3.GetLength(1);
int tileMapHeight = Layer3.GetLength(0);
Rectangle tile, tile2;
for (int x = 0; x < tileMapWidth; x++)
{
for (int y = 0; y < tileMapHeight; y++)
{
int wallIndex = Layer3[y, x];
if (wallIndex == 1) //Full-sized Tile Collision (128 x 128)
{
if (collisionCircle.Contains(new Vector2(x * tileWidth + (tileWidth / 2) + (int)Player.camera.Position.X,
y * tileHeight + (tileHeight / 2) + (int)Player.camera.Position.Y))) //+ tile / 2 is for centering the point
{
tile = new Rectangle(x * tileWidth + (int)Player.camera.Position.X, y * tileHeight + (int)Player.camera.Position.Y, tileWidth, tileHeight);
Collide(tile);
}
}
}
}
This would check throughout layer3 if there is a "1". If there is, assign rectangle and check for collision if point is inside collision radius.
Also, I checked this code(with a draw method), and I know it's working properly, at least the behavior.
I added in about 120,000(32 x 3888) tiles to try to make it lag, and before the code, it lagged a little bit. But after I added in the code, it lagged even more so.
I thought that since it would only check for collision between tiles(points) that are within the radius it wouldn't even remotely lag, but that's not the case...
Any help/ideas on how to optimize this would be great.
Thanks a lot,
Shyy
EDIT:
Cirlce.Contains() code:
public bool Contains(Vector2 Point)
{
return ((Point - position).Length() <= radius);
}
I used a circle because I've heard it's faster than using a rectangle.
Another possible optimization is instead of
return ((Point - position).Length() <= radius);
use
return ((Point - position).LengthSquared() <= radius * radius);
This is faster because Vector2.Length() has to perform a costly square root operation. Vector2.LengthSquared() does not have to perform that slow operation. The radius has to be multiplied by itself to account for the length from the vector being squared.
It sounds like you're trying to determine what tiles you don't need to use for collision with the player. Another optimization you could do is that if a tile at (X=5,Y=5) is above and to the left of the player, then you don't need to check a tile at (X=4,Y=4). Similarly if (X=5,Y=5) is below and to the right, (X=6,Y=6) is guaranteed to be too far as well. Try to determine when you've passed the player and no longer need to check collisions.
I suggest to loop only over visible tiles in screen to check collision using movement offset.
i will try something from my head..
for x as integer = 0 + offSetX to tilesInWidth + offSetX
for y as integer = 0 + offSetY to tilesInHeight + offSetY
if player.insideCircle(player.position, radius) '
object = layer(y,x);
if player.collideWith(object) then Collide()
end if
next
next
So I'm new to XNA and am attempting to draw multiple sprites to the screen in the easiest way. I want each sprite to be incremented in the X axis so that I am left with multiple sprites drawn across the screen.
I have defined:
Vector2 pos;
In the LoadContent function I have:
pos = new Vector2(0, 0);
and in Draw I have:
spriteBatch.Draw (circle, pos, Color.White); //Draws sprite to screen in correct position
spriteBatch.Draw(circle, (pos.X += 1), Color.White); //causes error and doesnt draw
Hopefully I've explained this well enough and you can see what I'm trying to do, the compiler doesn't agree with (pos.X += 50) (me trying to increment the X position by 50).
I know I could go about this a longer way and create a new Vector2 for each draw but that would create multiple lines of what I think surely is unnecessary code and there must be a quick way like this to go about doing it?
The method signature of Draw expects the second parameter to be a Vector2, right?
If so, then (pos.X += 1) isn't a Vector2. It's a statement which increments the X parameter of the pos Vector2 variable, but the statement doesn't return an instance of a Vector2 object.
Edit: Code is as follows:
public void DrawSprites()
{
// setup circle here
// setup spritebatch here
// setup initial pos here
// setup MAX_ITERATIONS here
var INCREMENT_VALUE = 50;
for (var i = 0; i < MAX_ITERATIONS; i++) {
var iteratedPos = pos + new Vector2((INCREMENT_VALUE * i), 0); // per Nikola's comment
spriteBatch.Draw(circle, iteratedPos, 0), Color.White);
}
}
You need to make a list of the draw rectangles of your textures.
//implement Texture2D (called "image") and SpriteBatch (called "spriteBatch")
List<Rectangle> rectangles = new List<Rectangle>();
const int ITERATIONS = 10; //or whatever you want the iterations to be
const int INCREMENT_VALUE = 50; //again, whatever you want it to be
for (int i = 0; i < ITERATIONS; i++)
{
for (int j = 0; j < rectangles.Count; j++)
{
rectangles[j].X += INCREMENT_VALUE * i;
spriteBatch.Draw(image, rectangles[j], Color.White);
}
}
Like said earlier, you need a list of Rectangles that have all the image rectangles in them. Hope I helped, and good luck!
I am currently developing a game for windows phone 7. The map for my game consists of a 2 dimensional array that holds a voxel in each element. The code for the rendering of the map is as follows.
//Draw Map
for (int i = 0; i < 499; i++)
{
for (int j = 0; j < 499; j++)
{
spriteBatch.Draw(groundVoxelTexture, voxels[i, j].Position, Color.White);
}
}
I should also mention that the player stays centered on the screen and the map moves around the player.
The problem is that this creates A LOT of textures for the phone to render and ends up causing so much lag that the phone I run it on locks up. So is there a way to make the objects that are off screen not render?
Sounds like you want to do some Frustum Culling
I'm not familier with xna for a phone app, but I'm sure it's the same proccess.
//Draw Map
BoundingFrustum bf = new BoundingFrustum(View * Projection);
for (int i = 0; i < 499; i++)
{
for (int j = 0; j < 499; j++)
{
if (bf.Intersects(new BoundingSphere(voxels[i, j].Position, voxelRadius)))
spriteBatch.Draw(groundVoxelTexture, voxels[i, j].Position, Color.White);
}
}
If the view stays in place and the map moves around, you should be able to use the same BoundingFrustum from frame to frame.
Using boundingfrustum is a bit hardcore in this situation.
is much better calculate the grid indexes that need to be drawn.
you are using spritebatch so you are working in 2D, the easy to me would be to use a view transform and work with it inverse to get indexes, but I think you are not using it.
So I suppose that your voxels have a size "S" and your player is in position P, and the screen has size (W,H) and there is no zoom option.
MinX= (int) ((P.X - W * 0.5) / S) - 1;
MinY= (int) ((P.Y - H * 0.5) / S) - 1;
MaxX= MinX+ W/S + 2;
MaxY= MinY+ H/S + 2;
for (int i = MinX; i < MaxX; i++)
{
for (int j = MinY; j < MaxY; j++)
{
spriteBatch.Draw(groundVoxelTexture, voxels[i, j].Position, Color.White);
}
}
Okay, I have a 2d Tile Map editor I'm working on in xna c#.
In the Draw method I loop through (with a 'for' loop) my 2 dimensional array of tiles so that
my map updates and draws all the tiles every frame.
My question is, how do you draw only the tiles that are seen on screen.
Also is there a better way to draw the tile map (Rather than updating every frame).
In the platformer demo I played around with the visible tiles were calculated and then only those tiles were drawn. I believe you will have to include them in the draw method to be drawn in each time.
Here is a snippet (this only had left to right scrolling so no vertical range was calculated). This kept track of the camera position to calculate it.
Edit:: Added the second method shows how it updated camera position based on the player position stored in a player object.
private void DrawTiles(SpriteBatch spriteBatch)
{
// Calculate the visible range of tiles.
int left = (int)Math.Floor(cameraPosition / Tile.Width);
int right = left + spriteBatch.GraphicsDevice.Viewport.Width / Tile.Width;
right = Math.Min(right, Width - 1);
// For each tile position
for (int y = 0; y < Height; ++y)
{
for (int x = left; x <= right; ++x)
{
// If there is a visible tile in that position
Texture2D texture = tiles[x, y].Texture;
if (texture != null)
{
// Draw it in screen space.
Vector2 position = new Vector2(x, y) * Tile.Size;
spriteBatch.Draw(texture, position, Color.White);
}
}
}
}`
private void ScrollCamera(Viewport viewport)
{
const float ViewMargin = 0.35f;
// Calculate the edges of the screen.
float marginWidth = viewport.Width * ViewMargin;
float marginLeft = cameraPosition + marginWidth;
float marginRight = cameraPosition + viewport.Width - marginWidth;
// Calculate how far to scroll when the player is near the edges of the screen.
float cameraMovement = 0.0f;
if (Player.Position.X < marginLeft)
cameraMovement = Player.Position.X - marginLeft;
else if (Player.Position.X > marginRight)
cameraMovement = Player.Position.X - marginRight;
// Update the camera position, but prevent scrolling off the ends of the level.
float maxCameraPosition = Tile.Width * Width - viewport.Width;
cameraPosition = MathHelper.Clamp(cameraPosition + cameraMovement, 0.0f, maxCameraPosition);
}
int MapSizeX = 20;
int MapSizeY = 20;
int LeftCornerX = 0; //the position of the Tile in the 2Darray that is going
int LeftCornerY = 0; //to be drawn in the left corner of the screen.
int ScreenSizeX = 10;
int ScreenSizeY = 10;
public Tiles[,] tiles = new Tile[MapSizeX, MapSizeY]; //list of all Tiles
//then you can draw it like this....
int counterX = 0; //represents the position on screen
int counterY = 0;
// y and x inside the for loops represents the position in tiles
for(int y = LeftCornerY; y < MapSizeY < y++)
{
for(int x = LeftCornerX; y < MapSizeX < x++)
{
if(counterX < ScreenSizeX && counterY < ScreenSizeY)
{
tiles[x, y].draw(tiles[counterX , counterY]);
}
counterX ++;
//when you do like this you draw the tiles you want
//at the position you want. In the draw method you just
// drawn the tile you want at the position of the tile you
// send as in parameter to the draw method.
}
counterY++;
counterX = 0;
}
then you just have to increase the LeftCorner variables to draw another part of the map