Unity - instantiate objects in a loop with spacing between each other - c#

I generate a grid of cubes by 10x10. My cells are different, I choose them out of an array.
What I need is a small spacing between each cell.
This is my code so far
private void Start()
{
for (int x = 0; x < data.MapSize.x; x++)
{
for (int y = 0; y < data.MapSize.y; y++)
{
Instantiate(data.Cells[Random.Range(0, data.Cells.Length)]), new Vector3(x, 0, z), Quaternion.identity); // Create a specific cell on position (x,y)
}
}
}
So what I've tried out:
new Vector3(x + 1, 0, z + 1)
new Vector3(x * 0.1f, 0, z * 0.1f)
but obviously it won't change anything.
So I tried out this:
Before the start method I created a variable
int counter = 0;
and within the loops
counter += 0.1f;
new Vector3(x + counter, 0, z + counter)
but then there appears no grid, I get an parallelogram.

First define your spacing length, so you can easily change it later.
float spacing = 0.1f;
Then use this to get the desired result.
Instantiate(data.Cells[Random.Range(0, data.Cells.Length)]), new Vector3(x + (x*spacing), 0, y + (y*spacing)), Quaternion.identity); // Create a specific cell on position (x,y)
Notice I changed z to y.

The answer to this depends on whether or not you want your grid to remain exactly 10 units across, in world space.
If the size of the grid doesn't matter, you can just add the padding to your x and y increments (and use a floating point instead of integer). You will also have to increase data.MapSize to take into account the padding:
for (float x = 0; x < data.MapSize.x; x += 1.1f)
{
for (float y = 0; y < data.MapSize.y; y += 1.1f)
{
Alternatively, you might want to consider scaling down the objects that you're storing in data.Cells in order to create the space without affecting the grid size. You could either scale down the prefab, or do it as they're being instantiated:
GameObject newCell = (GameObject)Instantiate(data.Cells[Random.Range(0, data.Cells.Length)]), new Vector3(x, 0, y), Quaternion.identity); // Create a specific cell on position (x,y)
newCell.transform.localScale = new Vector3(0.9, 0.9, 0.9);
Edit: Lestat's solution is a cleaner version of the first option, so if the final size of the grid doesn't matter then I would go with that instead of modifying the x and y variables.

Related

Raycast not capturing all Vector coordinates

I have a gameobject that occupies the whole screen just for testing purposes. I'm drawing a line btw. What I'm trying to achieve is if the mouse position hits a gameobject it will store the vector2 coordinates in a list. But raycast is not storing all the coordinates. Below is my code
private void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 mousePos = Input.mousePosition;
Vector2 Pos = _camera.ScreenToWorldPoint(mousePos);
if(!mousePositions.Contains(Pos))
mousePositions.Add(Pos);
if (Physics.Raycast(Camera.main.ScreenPointToRay(mousePos), out RaycastHit hit))
{
Vector2 textureCoord = hit.textureCoord;
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
Vector2Int paintPixelPosition = new Vector2Int(pixelX, pixelY);
if (!linePositions.Contains(paintPixelPosition))
linePositions.Add(paintPixelPosition);
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
_templateDirtMask.SetPixel(
pixelXOffset + x,
pixelYOffset + y,
Color.black
);
}
}
}
_templateDirtMask.Apply();
}
}
}
Everytime I checked the element count mousePositions are always greater than linePositions. I don't know what's causing this
the element count mousePositions are always greater than linePosition
well it is quite simple: In
int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
you are casting to int and cut off any decimals after the comma (basically like doing Mathf.FloorToInt).
So you can totally have multiple mouse positions which result in float pixel positions like e.g.
1.2, 1.2
1.4, 1.7
1.02, 1.93
...
all these will map to
Vector2Int paintPixelPosition = new Vector2Int(1, 1);
Besides, you might want to look at some better line drawing algorithms like e.g. this simple one
And then note that calling SetPixel repeatedly is quite expensive. You want to do a single SetPixels call like e.g.
var pixels = _templateDirtMask.GetPixels();
foreach (Vector2Int pos in linePositions)
{
int pixelXOffset = pos.x - (_brush.width / 2);
int pixelYOffset = pos.y - (_brush.height / 2);
for (int x = 0; x < _brush.width; x++)
{
for (int y = 0; y < _brush.height; y++)
{
pixels[(pixelXOffset + x) + (pixelYOffset + y) * _templateDirtMask.width] = Color.black;
}
}
}
_templateDirtMask.SetPixels(pixels);
_templateDirtMask.Apply();
It happens because there is really could be a case, when several elements from mousePositions are associated with one elment from linePositions.
Rough example: your texture resolution is only 1x1px. In this case you linePositons will contain only one element. And this element will be associated with all elements from mosePositions.
So, relation of the number of elements in these lists depends on relation of your texture and screen resolutions.

Instantiate but I want floats

I am currently instantiating a GetTile prefab using Vector3 but I am trying to plot it in a hex grid so I want Vector3 to output floats. ie I want to be able to divide y by 2 and get 1.5, 2 , 2.5 etc. but it can only output int values (1,2,2).
For context this is the code:
public void Draw(Cell[,] state) /// this is creates the board by using the tiles given from GetTile and SetTile
{
int width = state.GetLength(0);
int height = state.GetLength(1);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Cell cell = state[x, y];
//tilemap.SetTile(cell.position, GetTile(cell));
Instantiate(GetTile(cell),new Vector3(x,y,0), Quaternion.identity) ;
}
}
}
Is there a workaround for this? Or this because of the nature of Instantiate. Honestly lost so would appreciate the help.
Instantiate receives Vector3 and it supports floats.
If I'm reading your issue correctly, you're trying to divide the value by 2. Since x and y are defined in the for loop as int, you are getting int values.
So instead of dividing by 2, try dividing by 2f, e.g.
Instantiate(GetTile(cell),new Vector3(x / 2f, y / 2f, 0), Quaternion.identity);

Two different Vector3.zero's, but no parent?

UPDATE
I found out that the mesh center of the mesh object is not at 0,0,0. Does that do anything?
I have the following problem. I am generating a terrain from Perlin noise and that works fine. However, as soon as I try to instantiate any objects on it, some are spawned in the terrain area and some completely outside. When I reset the object's transform, it teleports to (0,0,0) as expected, but when I reset another object, that was not instantiated at runtime, the (0,0,0) is at a completely different location! I have no parent set to these objects and no parent set to the other object as well. Below is my code for generating the objects:
private void AddRocks(Terrain terrain, int count)
{
for (int i = 0; i < count; i++)
{
float randX = Random.Range(0, 256); //256 is my terrain size, the transform is all zeros and 1 for the transform size.
float randZ = Random.Range(0, 256);
GameObject newGameObject = Instantiate(rockPrefab,
new Vector3(randX, terrain.terrainData.GetHeight((int)randX, (int)randZ),
randZ), Quaternion.identity);
}
}
This is my code for generating the perlin noise terrain:
TerrainData GenerateTerrain(TerrainData terrainData)
{
terrainData.heightmapResolution = width + 1;
terrainData.size = new Vector3(width, depth, height);
terrainData.SetHeights(0, 0, GenerateHeights());
return terrainData;
}
float[,] GenerateHeights()
{
float[,] heights = new float[width, height];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
heights[x, y] = CalculateHeight(x, y);
}
}
return heights;
}
float CalculateHeight(int x, int y)
{
float xCoord = (float)x / width * scale + offsetX;
float yCoord = (float)y / height * scale + offsetY;
return Mathf.PerlinNoise(xCoord, yCoord);
}
This is how I call them in Start:
terrain.terrainData = GenerateTerrain(terrain.terrainData);
AddRocks(terrain: terrain, count: 20);
This is how it looks after generating:
This is how the rocks look:
The rocks are generated from a script that lies on the mainterrain itself.
I have no parent set to these objects and no parent set to the other object as well.
Actually, you do set parent:
GameObject newGameObject = Instantiate(rockPrefab,
new Vector3(randX, terrain.terrainData.GetHeight((int)randX, (int)randZ),
randZ), Quaternion.identity, rockHolder.transform);
The last parameter ( rockHolder.transform) is a transform to which the instantiated object will be attached and the position you set will become a localPosition of the instantiated object relative to the parent ( rockHolder).
But I don't see the rockHolder object in the hierarchy view screenshot. Seems like you have rockHolder.transform = null, in other words it's not initialized. So, when you call Instantiate (...) and pass the rockHolder.transform as a desired parent for the rocks, it is null, so Unity spawns the objects and assign them to null (no parent).
Can't tell if this is the root of the problem but it's certainly not okay anyway.

Rotating a 3D coordinate

I need to rotate a single coordinate in WPF - C#.
The values x, y,z stored in GeometryModel3D[] points.
For example, coordinate(x, y, z) rotate at speficic-axis.
[UPDATE] Rotation transformation using quaternion. The problem are I don't get the new vector value and when I view the pointcloud, It seem drag away in Meshlab.
Matrix3D m = Matrix3D.Identity;
Quaternion q = new Quaternion(new Vector3D(320 / 2, y, maxDepth - minDepth), 90);
m.Rotate(q);
Vector3D myVectorToRotate = new Vector3D(((TranslateTransform3D)points[i].Transform).OffsetX, ((TranslateTransform3D)points[i].Transform).OffsetY, ((TranslateTransform3D)points[i].Transform).OffsetZ);
m.Transform(myVectorToRotate);
pointcloud.Add(new Point3D(myVectorToRotate.X,myVectorToRotate.Y,myVectorToRotate.Z));
I'm still can't get the correct value transformation.
I want to apply rotation transformation for 2nd point cloud scanned from kinect. Since the 1st scan data don't involved rotation, the code for capture data and usage is like below:
for (int y = 0; y < 240; y += resolution)
{
for (int x = 0; x < 320; x += resolution)
{
if (((TranslateTransform3D)points[i].Transform).OffsetZ >= minDepth
&& ((TranslateTransform3D)points[i].Transform).OffsetZ <= maxDepth)
{
pointcloud.Add(new Point3D(((TranslateTransform3D)points[i].Transform).OffsetX, ((TranslateTransform3D)points[i].Transform).OffsetY, ((TranslateTransform3D)points[i].Transform).OffsetZ));
}
i++;
}
}
Create any kind of matrix. For example a rotation matrix and then use the static method Vector.Multiply(...)
See also this post and the MSDN general transformation overview.
Examples for Vector3D:
3D transformation WPF
Rotate a vector by quaternion
Vector3D v = new Vector3D(1.0, -1.0, 2.0);
...
AxisAngleRotation3D axisAngle = new AxisAngleRotation3D(axis, angle);
RotateTransform3D myRotateTransform = new RotateTransform3D(axisAngle, centerVector);
v.Multiply(myRotateTransform);

XNA is pulling the wrong texture?

I am making (another) MineCraft clone, and I've run into an interesting problem. I have a public enum that lists all the cube types a particular cube can be, and I have a 3d array that holds cubes. Each cube has a specific type, and I iterate through this array to get the vertices for each cube, then pass those vertices to a vertex buffer designated for a particular cube type. When I create a random array of cubes, or a single cube, and tell it what texture it should be everything draws as expected. I'm now trying to figure out how to draw a random "surface" of grass cubes, and fill everything below those on the y-axis with dirt cubes. The strangest thing is happening though, the top most cube is dirt and it fills all the bottom ones with grass cubes! When I disable the loop to fill the underground with dirt, the top most cube is displaying grass as intended.
Here is what I believe to be the relevant parts of the code. Here is where the cube type is set:
// Create a random surface level
Perlin perlin = new Perlin();
for (int x = 0; x < Game.ChunkWidth_X; x++)
{
for (int z = 0; z < Game.ChunkDepth_Z; z++)
{
double XVal = Convert.ToDouble(x) * 1.1;
double ZVal = Convert.ToDouble(z) * 1.1;
double YVal = Game.ChunkHeight_Y / 2 * 1.1;
double PerlinValue = perlin.GetValue(XVal, YVal, ZVal);
int YVal_new = Convert.ToInt32(YVal + (PerlinValue * 10));
if (YVal_new > Game.ChunkHeight_Y - 1) { YVal_new = Game.ChunkHeight_Y - 1; }
if (YVal_new < 0) { YVal_new = 0; }
// Set the grass cube
Cube NewCube = new Cube(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(x, YVal_new, z));
NewCube.cubeType = CubeType.Grass;
CubeGrid[x, YVal_new, z] = NewCube;
// Fill below it with dirt
for (int y = YVal_new - 1; y >= 0; y--)
{
Cube NewCube2 = new Cube(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(x, y, z));
NewCube2.cubeType = CubeType.Dirt;
CubeGrid[x, y, z] = NewCube2;
}
// Fill above it with air
for (int y = YVal_new + 1; y < Game.ChunkHeight_Y; y++)
{
Cube NewCube2 = new Cube(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(x, y, z));
NewCube2.cubeType = CubeType.Air;
CubeGrid[x, y, z] = NewCube2;
}
}
}
This is where I pull the vertices to put into the appropriate buffer:
Dictionary<CubeType, List<VertexPositionNormalTexture>> DrawableVertices = new Dictionary<CubeType, List<VertexPositionNormalTexture>>();
// Get the proper vertices for each cube type and put in the appropriate dictionary
for (int x = 0; x < Game.ChunkWidth_X; x++)
{
for (int z = 0; z < Game.ChunkDepth_Z; z++)
{
for (int y = 0; y < Game.ChunkHeight_Y; y++)
{
CubeGrid[x,y,z].CreateVertices();
string test = CubeGrid[x, y, z].cubeType.ToString();
foreach (VertexPositionNormalTexture TargetVertex in CubeGrid[x, y, z].DisplayableVertices)
{
if (!DrawableVertices.ContainsKey(CubeGrid[x, y, z].cubeType))
{
List<VertexPositionNormalTexture> NewList = new List<VertexPositionNormalTexture>();
NewList.Add(TargetVertex);
DrawableVertices.Add(CubeGrid[x, y, z].cubeType, NewList);
}
else
{
DrawableVertices[CubeGrid[x, y, z].cubeType].Add(TargetVertex);
}
}
}
}
}
Here is the second part of it:
foreach (KeyValuePair<CubeType, List<VertexPositionNormalTexture>> KVP in DrawableVertices)
{
VertexBuffer cubeBuffer = new VertexBuffer(device, typeof(VertexPositionNormalTexture), KVP.Value.Count, BufferUsage.WriteOnly);
cubeBuffer.SetData(KVP.Value.ToArray());
// Update our collection of vertex buffers
CubeType_VertexBuffers[KVP.Key] = cubeBuffer;
// Get the triangle count for the buffer
CubeType_TriangleCount[KVP.Key] = KVP.Value.Count / 3;
}
Lastly, here is my draw:
// Go through each vertex buffer we have created, and draw it.
foreach (KeyValuePair<CubeType, VertexBuffer> KVP in CubeType_VertexBuffers)
{
foreach (EffectPass pass in testEffect.CurrentTechnique.Passes)
{
if (CubeType_TriangleCount[KVP.Key] > 0) // if this buffer has triangles, draw it.
{
pass.Apply();
testEffect.View = camera.ViewMatrix;
testEffect.TextureEnabled = true;
testEffect.Projection = camera.ProjectionMatrix;
testEffect.World = worldMatrix;
testEffect.Texture = CubeType_Texture[KVP.Key];
device.SetVertexBuffer(KVP.Value);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, CubeType_TriangleCount[KVP.Key]);
}
}
}
base.Draw(gameTime);
The weirdest thing is that when I manually set cube types everything draws with the proper texture as expected. What other things should I try to troubleshoot? I tried making a specific effect for each cube type to no avail.
After trying a bunch of random things in desperation, I found a fix for this. It turns out that if you use the same BasicEffect for different textures, it only uses the last texture assigned to it. I was iterating through a list of VertexBuffers and assigning a different texture for each one. By the time everything made it over to the video card, only the last texture used was rendered, or so it appears.
The solution was to create a separate BasicEffect for each texture I needed and assign only the VertexBuffers needed to the particular BasicEffect.

Categories