Unity 2D array tile map - c#

I am new to Unity as well as C#. Yet, I am trying to make a simple 2D platform game where I made a prefab of an object called Block. What I want to be able to do is to create an array tile map with 0s and 1s where the 1s are blocks and 0s are nothing. Also, I don't want the tile map to be random. I want the blocks to be instantiated from another object called GameController. A perfect example of what I would like to achieve is something like this.
But I don't really know how to do this with an array. I want to keep things simple since I am trying to learn how unity and c# work. Any help would be appreciated.

So you can use some assets from the asset store( for example: https://www.assetstore.unity3d.com/en/#!/list/2965-procedural-generation) This is a pretty hard challange. I would recomand this video: https://www.youtube.com/watch?v=k1pWpYEt2UE , but the closest one to what you want to achieve is this one: https://www.youtube.com/watch?v=gIUVRYViG_g
Hope it helped.

You can make a 2 dimensional array, e.g. int[40, 100] and loop through it twice, and if the number in the array is one, multiply your position in the array by the length or width of your block respectively. For example:
int[,] positions = new int[40,100];
for (int i = 0; i < 41; i++) {
for (int j = 0; j < 100; j++) {
if (positions[i,j] = 1) {
GameObject temp = Instantiate(block, new Vector3(j * blockWidth, i * blockHeight, 0), Quaternion.identity) as GameObject;
}
}
}
It would take a long while to set all of the coordinates for an array this large, but you could loop through with parameters, or just do it the hard way if it is smaller. Otherwise, I would just try doing it without a script.

Related

Unity 3D: How to make object stack on top of each other and check for acquired space?

im bad at explaining so i ill ask through this picture, what i want to do is, when the player hits the gameobject(cube) i want it to transform.position to different place and start getting stacked on top of each other. now i get the transform part, i can do it by onCollisionEnter and then move the object to the place. but thats it, i can only do it for one object as others will keep moving inside of each other, now i want them to check if the place is acquired and then stack on top of the one that is already there, and if there are 3 cubes stacked on top of each other already then move to the next location beside them. Also, this is all in 3D.
I would create a 2d arrays, or a jagged array.
Go to jagged array or 2d arrays to learn more
Basically if you know what an array is, it is a set of information. A jagged array is a set of arrays that can have varying lengthed arrays. A 2d (you can do 3d, 4d, 5d, so on) is an array of arrays of all the same length.
So basically if you want each stack to be the same length, use a 2d array, if they can vary in height, use a jagged array.
In this example I am going to use a 2d array.
Vector3 start = new Vector3 (0, 0, 0); //the stack starts here
Vector3 stackOffset = new Vector3 (2, 0, 0); //each stack is 2 units right
Vector3 heightOffset = new Vector3(0, 1, 0); //each object is 1 unit higher.
bool[3][5] stacks = new GameObject[3][5]; //3 stacks of 5 height
void OnCollisionEnter(Collision obj)
{
if (obj.tag == "thing")
{
for (int i = 0; i < stacks.Length; i++)//loop 3 times (3 stacks)
{
for (int ii = 0; ii < stacks[].Length; ii++) //loop 5 times (5 in each stack)
{
if (stacks[i][ii] == false)
{
stacks[i][ii] = true;
obj.transform.position = start + (i * stackOffset) + (ii * heightOffset);
return;
}
}
}
print("all stacks filled");
}
}
Hope this works, if you have any errors, can you tell them to me in comments, or edit original post with an image.

Why does CombineMesh delete parts of my mesh?

I'm working on creating a building generator, and am trying to combine all walls of the building into a single mesh. The building itself is made of quite a lot of individual gameobjects, all parented under one gameobject. In play mode, the constructed building looks like this.
I can export the gameobjects as a prefab just fine.
The issue arises when I attempt to combine everything to a single mesh. It shows like this, with a great deal of vertices appearing to be missing. I've already considered that I could be running up against the vertice limit, but even after raising that limit, the issue remains unresolved. Recalculating the normals hasn't helped, either. Code below.
MeshFilter[] filters = building.GetComponentsInChildren<MeshFilter>();
Mesh finalMesh = new Mesh();
finalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
Debug.Log("Filters " + filters.Length);
CombineInstance[] combiners = new CombineInstance[filters.Length];
for (int i = 0; i < filters.Length; i++)
{
Debug.Log(filters[i].gameObject.name);
combiners[i].subMeshIndex = 0;
combiners[i].mesh = filters[i].sharedMesh;
combiners[i].transform = filters[i].transform.localToWorldMatrix;
}
finalMesh.CombineMeshes(combiners);
finalMesh.RecalculateNormals();
building.AddComponent<MeshFilter>();
building.GetComponent<MeshFilter>().sharedMesh = finalMesh;
string localPath2 = buildingFolderPath + "/Building.asset";
AssetDatabase.CreateAsset(finalMesh, localPath2);
GameObject finalBuilding = PrefabUtility.SaveAsPrefabAsset(building, localPath);
I have no clue what could be happening. I've considered that it could be automatically removing intersecting polygons (unsure if that's even a factor with CombineMesh(), the documentation doesn't say anything about it) but even changing the gameobjects involved to very small sizes, to the point where intersection would be impossible, hasn't helped a bit.
A possible clue is that the mesh is only displaying T-junction walls, which are the largest of the walls. Could there be a connection between size and this issue?
As it stands, I've exhausted all of my possible ideas. What could be going on here? For reference, the models in question are simple blender models, more or less just planes, exported in fbx with read/write enabled.
EDIT: As suggested by shingo, I have counted the vertices in the building using the following code.
MeshFilter[] filters = building.GetComponentsInChildren<MeshFilter>();
Mesh finalMesh = new Mesh();
finalMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
CombineInstance[] combiners = new CombineInstance[filters.Length];
long count = 0;
for (int i = 0; i < filters.Length; i++)
{
combiners[i].subMeshIndex = 0;
combiners[i].mesh = filters[i].sharedMesh;
combiners[i].transform = filters[i].transform.localToWorldMatrix;
count += filters[i].sharedMesh.vertexCount;
}
Debug.Log("Final count is " + count);
In one experimental example, the final count given by the code was 39174 vertices, while the vertices recorded in the inspector of the corresponding mesh counted 28372. So there's a significant disparity, meaning that the vertices are actually being deleted, instead of just not displaying properly.

Instantiate blocks evenly between two instantiated objects

I'm instantiating obstacles at random X seconds and they fall down the screen. I was looking for a way to instantiate blocks in between the falling obstacles.
Here’s the falling obstacle:
GIF Example
IEnumerator ObstacleSpawner()
{
while (true)
{
obstacleSpawn = Random.Range(0.25f, 0.9f);
yield return new WaitForSeconds(obstacleSpawn);
GameObject newObstacle = Instantiate(obstacle, new Vector2(-1, 6.5f), Quaternion.identity);
}
}
And this is what I’m looking for:
Example
I’m looking for two things.
To have the blocks created if the falling objects are within a certain distance. So in my code, if the obstacleSpawn time is between 0.25 – 0.6 secs, the block wont spawn. After that, the larger the obstacleSpawn time, the more blocks created.
To have the block spawn evenly in between the obstacles. If one block is instantiated, its in the middle of the two obstacles. If its two, three and so on, they’re spread out evenly as the above pic shows.
I’ve tried to do this using the following code but obviously with this, it’s instantiating every X secs as I’m a beginner and I honestly don’t know where to start to do this, so would appreciate some help.
IEnumerator BlockSpawner()
{
while (true)
{
yield return new WaitForSeconds(Random.Range(0.25f, 0.9f));
if (obstacleSpawn >= 0.5f && obstacleSpawn < 0.7f)
{
GameObject newBlock = Instantiate(block, new Vector2(Random.Range(-1.65f, 1.65f), 6.5f), Quaternion.identity);
}
else if (obstacleSpawn >= 0.7f && obstacleSpawn < 0.8f)
{
for (int i = 0; i < 1; i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), 6.5f);
}
}
else if (obstacleSpawn >= 0.8f && obstacleSpawn < 0.9f)
{
for (int i = 0; i < 2; i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), 6.5f);
}
}
else if (obstacleSpawn >= 0.9f)
{
for (int i = 0; i < 3; i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), 6.5f);
}
}
}
}
Hopefully I am getting your question right please add more detail if I am wrong, but right now your variable "obstacleSpawn" is a random time and then the less this variable is, the closer together the objects are spawning. So you are using time to measure the distance between the objects. This works well but since you want to spawn the objects evenly this makes things a bit more complicated.
The easiest way is probably to just mess around with the y position of the red blocks while they spawn until you get it right. This would look something like -
for (int i = 0; i < 2 i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), newObstacle.transform.postition.y + (1 * i));
}
Now you would need a reference to the newObstacle from your other Coroutine, or you could change the "newObstacle" gameobject/variable's scope to be the entire script (declare the variable at the top and change it in ObstacleSpawner() if that makes sense). Then you can just play around with the 1 in "(1 * i)" to get different spacing.
I recommend this ^ but I will explain another way too -
If you really want to use the obstacles spawning, and divide the distance between them to get exact measurements you can, but it'll be more complicated. You would need a reference both to the most recent spawned obstacle (say object1), and the second most recent (say object2), then you could divide their y distance by however many blocks you have, say if you have 3 blocks you want to spawn - ((object1.transform.position.y - object2.transform.position.y) / 3) - this will return the increment of y each block will be, you could then substitute the (1 * 1) from before with this value.
Edit - More explanation for second way -
The easiest way would be to make a list of the obstacles you spawn and then use the last 2 obstacles in the list and calculate the distance between them.
To add the list, at the top of your script you would add something that would look like
private List<GameObjects> listOfObstacles;
Then in the ObstacalSpawner() function you would add the gameobject to that list "listOfObjects.Add(newObstacle)", this will create a list of the obstacles from first spawned to last (most recent). Now you can use the way I said before to split them up -
float distanceBetweenBlocks = ((listOfObjects[listOfObjects.Count - 2].transform.position.y - listOfObjects[listOfObjects.Count - 1].transform.position.y) / 3)
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), listOfObjects[listOfObjects.Count - 2].transform.postition.y + (distanceBetweenBlocks * i));
the "listOfObjects[listOfObjects.Count - 1]" will get you the last item in that list (most recently spawned object) then -1 to the length will get you the second to last, then you just do what I said before, getting the distance between each and dividing it by however many blocks you want (you will want to change the 3 to however many blocks you want)
Hopefully that clears up how that works and it makes sense. Good Luck!

Civilization 1 like tilemap generation

Okay so, I have looked at a lot of question from people like my self who are beginners in programming. And most of the time their questions are hard to answer or understand what the question really is. I will do my best to be as specific as possible about my problems so the above mentioned doesn't happen.
First off I have been following the tutorials over at http://xnagpa.net/xna4rpg.php. And my tile engine is based off the one that Jamie McMahon makes in his rpg tutorial series. Just so you know what the general structure of my tile engine is like.
Secondly I will try to explain what I'm trying to do inside the tile engine. I recently found an article about how the original Civilization generated their maps. http://forums.civfanatics.com/showthread.php?t=498630
And I rather like this approach to generating a "world" style map if you will. ie: oceans, continents, islands ect. So I decided to try to take this and implement it into my tile engine. It works for the most part. The parts that I added to the tile engine are supposed to randomly pick a location in the specified map layer (y,x) and then from that location generate a chunk of land(or tiles) and then replace the tiles in the map layer with the tiles created in the chunk of land. (ill show the code in a minute) and then do that for a desired amount of either number of landmasses(chunks) or continue creating chunks of land until the number of land tiles is equal to a desired amount of land tiles.
My Problem:
My program does what its supposed to (as mentioned above) except it only ever makes one landmass.(Chunk of land tiles) It does everything else just fine but it for some reason will not make more than one landmass. Now I suspect that it actually is making the other landmasses but somehow the way the tile engine is set up to display map layers is causing the landmass's to be covered up with water. Maybe its a layering issue. But It shouldn't be because the landmass's are all part of the same layer. So I'm completely baffled as to why its doing this.
public void GenerateLandChunks(MapLayer layer)
{
Tile tile = new Tile(0, 3);
Random random = new Random();
int x = random.Next(8, layer.Width - 10);
int y = random.Next(10, layer.Height - 20);
int length = random.Next(10, 70);
for (int i = 0; i < length; i++)
{
if (length != 0 && x > 8 || x < layer.Width - 10 && y > 10 || y < layer.Height - 20)
{
layer.SetTile(y, x, tile);
layer.SetTile(y, x + 1, tile);
layer.SetTile(y + 1, x, tile);
}
x = random.Next(x - 1, x + 2);
y = random.Next(y - 1, y + 2);
}
}
This is my method for generating the actual chunks it does what I want it to. (ABOVE)
MapLayer randomLayer = new MapLayer(100, 100);
for (int y = 0; y < randomLayer.Height; y++)
{
for (int x = 0; x < randomLayer.Width; x++)
{
Tile tile = new Tile(1, 3);
randomLayer.SetTile(x, y, tile);
}
}
int landMasses = 5;
for (int i = 0; i < landMasses; i++)
{
randomLayer.GenerateLandChunks(randomLayer);
}
This is where I create the map layer. I initially set the entire map to water tiles(tile (1,3)) then I tell it to generate 5 landmasses.
It seems like this should work. And it does but like I said only for the first one. It doesn't display the other 4 land masses.
My Question:
Is there anything you can see here that I'm doing wrong in order to accomplish what I'm trying to do?
If you need more of the code to understand whats going on let me know and ill post what ever you need. And like I said everything other than what I have posted is the exact same as it is in Jamie McMahon's tile engine.
I'm sorry if I have come off as unclear or if my question is hard to answer. I tried to make it as straight forward as possible. Thank you for your time.
So much text for such a simple answer. The problem is that a new Random object is generated every time, and so the "random" values are the same every time. That is how random number generators work, the numbers are not actually random, but "pseudorandom", and if you use the same random function and the same seed you will get the same progression of seemingly random numbers.
Default Random constructor seeds the generator with some value based on time, but if two generators are created with very small time period then that time value will be the same.
Move the Random object outside the function, so that it is used for all random generations, or move the loop inside (but Random outside the loop).

Using List Indicies is not object oriented? c#

In the homework I am doing:
It is stated: "You may NOT use a number to access a tile - if you are using a number to find a tile, then it is most likely your design is not object oriented."
So essentially, my question is this:
If one were to not use an index to get an object from a list of objects, would the only solution be to use a foreach loop, every time you needed to retrieve a specific object?
If not, could you please provide an example (e.x. using a "for loop in an object oriented way", or other solutions instead of using an index)?
My concern is the amount of bloat (in lines) that might be created when not using for loops - and the affect that might have on speed.
e.g., I have the following:
for (int i = dimensions-1; i >= 0; i--)
{
map += "-";
for (int j = 0; j < dimensions; j++)
{
//For 4X4Y is 24. 3X4Y is 19. and so on.
//For 4X3Y is 23. 3X3Y is 18. and so on.
//As you can see: j * dimensions represents a "generalized" x value. adding it by i, represents its y value.
Tile currentTile = _game.TileSet[(j * dimensions) + i];
if (currentTile.Visitor == null)
map += "X-"; //X implies no robot
else
map += "R-"; //R implies robot
}
}
Yes this code is kind of ugly, but my list is formatted in such a way that I can't just use one foreach loop, and split the line on each x amount of tiles per line, as the list is stored vertically, and then horizontally, as opposed to horizontally, and then vertically (which would allow a simple foreach to suffice).
My concern is that if this is "not object oriented" - then using a foreach loop, would require a LOT more iterations.
Anyway, if anyone has an answer to the question I would really appreciate your thoughts.
Thanks.
Edit: Instead of using X and Y values, in a 2D List/Array, the list is 1d, and we are required to use association by having a list of connecting tiles, 4 elements, each element representing a typical compass direction in which the adjacent tile is located. There are no properties - however there is one method which can return an adjacent tile at a specified direction GetTileAtDirection(Direction).
The tileSet is a list of 25 elements, with 0 through 4 being the first 5 elements, X = 0, Y = 0 X = 0, Y = 1 X = 0, Y = 2 X = 0, Y = 3 X = 0, Y = 4. For the next five elements, X is incremented by one, representing the next five values in this "Y line"...
Note that there are no X and Y variables in which each tile can be accessed. This is a requirement in the homework.
If one were to not use an index to get an object from a list of
objects, would the only solution be to use a foreach loop, every time
you needed to retrieve a specific object?
No. I think the idea is that each tile should have a mapping of Direction to Tile, and GetTileAtDirection simply looks it up. The code that creates the tiles would need to assign the relationships, something like this:
nextTile = new Tile();
nextTile.AdjacentTiles[Direction.Left] = previousTile;
previousTile.AdjacentTiles[Direction.Right] = nextTile;
and so on. Of course, this code would probably be looking up the indices in TileSet, but the key is that an individual tile object can be completely independent from it -- the Tile class wouldn't even need a reference to a Game object or TileSet.

Categories

Resources