Ok, I’m sorry for any confusion I caused… it worked out better in my head. A lot of this is trial and error and without having access to UE4 at the time, I didn’t get any errors so I assumed it would work …
I ran through the steps I outlined myself and it didn’t work like I had thought. First problem, I never mentioned what the variable of TileStruct types in TileRow should be called. I simply called it “TileRows”… not super descriptive but it at least shows a different name and indicates it is multiple TileRow structs.
So the way I was trying to accomplish this is
- Add a “blank” row to the MapIndex
>> I’m not sure why you were not able to connect the “Make TileRow” to the Map Index “Add” node … maybe check the map index’s type? - Get that “blank” row, break it apart to get to the “TileRows” array, then loop 15 times, adding blank TileStruct entries to it.
The problem is if we get an element in the “Map Index” array with the “Get” node, it returns a copy of the data in that index. So for example, if we had this array:
Array = {{1,2,3},{4,5,6},{7,8,9}}
This is a 2D array. The Array itself has three entries and each of those has three entries. If I were to say “Get the second entry in Array”, I would get {4,5,6}. But if I then said “Get the second entry in Array, change the first value to an A”, it would LOOK like the array should now be
Array = {{1,2,3},{A,5,6},{7,8,9}}
But the problem is that when I “get” an element from the array, I only got a copy of the values contained there, not an actual reference to the array itself. So any modifications to that value do not reflect back on the original array.
Long story short, I was thinking about this the wrong way. I came up with a way to do this, but unfortunately I needed to create another variable. I can’t help but feel like I could have done it without one but I couldn’t figure it out.
So I created a new variable called “TileRowBuild”, which is an array of TileStruct. Basically the same as the “TileRow” struct created before. (NOTE: I realize now I should probably have made it a local variable as it will never be used outside that initialization, but it will work if you do or don’t) Then I changed the logic to be as follows:
- Loop 11 times. For each iteration of that loop …
- Loop 15 times. Add a blank entry to the TileRowBuild.
- Once done with the second loop, add the finished TileRowBuild to the Map Index. Clear out TileRowBuild for repeated use.
It’s simpler, cleaner and works. Adding a blank row then trying to populate it after wasn’t a good idea. Better to build the full row then add it. Here’s a screenshot of it:
So now we have the 11x15 array of blank tiles and we can continue.
Next step is to populate the MapIndex. We can do this right inside the logic we already have. As I mentioned above, there are three possible conditions that should result in a “1” being put in the array.
- Y is 0
OR - Y is 10
OR - X is 0
OR - X is 14
OR - X and Y are both even numbers
Note: I re-arranged the order slightly to make the logic flow better.
- Drag an “OR” node onto the event graph. Click “Add Pin” until we have five of them (one for each condition above).
- First condition is "Y is 0. Easy enough. Drag off the index in the first loop and look for an = node. Set the value to 0 and plug it into the OR.
- Second condition, “Y” is 10. Just as easy. Follow the same steps but use “10” instead of 0. (oops, in the screenshot below I still had it at 0).
- Third and forth condition are very similar, just drag off the index in the second loop.
- Finally, we need to check if both X AND Y are even. Drag off the final “or” pin and add an “AND” node. In order to check if a number is even, we need to use the modulus operator. If you don’t know what that is, it’s basically division except it only returns the remainder. So if we did 10/2, the remainder would be 0. So 10%2 (% is modulus) is 0. If a number is even, it can be divided evenly by 2. So if (number)%2=0, we know it’s even. So add two modulus nodes to the graph and add two = nodes to the graph to check that they’re 0. Plug them into the AND.
- So, if the returning boolean from that OR is true, it means any of the conditions are true and we need a 1 instead of a 0 for the tile type. Add a “Select int” node to the graph. Pass the result into “Make TileStruct” type and the “Pick A” pin to the OR result. What this does it it will pick either int A or int B based on the boolean passed in. The boolean will be TRUE when we want a wall so we want “A” to be 1. Leave B at 0.
NOTE: You could feed the result of the OR straight into the “Type” pin. A “TRUE” will be converted to a 1 and a “FALSE” will be converted to a 0. This will look cleaner but it may lead to confusion later. Also, this section may need to be added to if you want to dynamically create different types besides just 0 or 1.
At this point, I have not tested this. Mostly because there currently isn’t a good way to view the contents of an array unless we make a function to print it to the screen but I don’t want to go through that. I’d rather just use it and see if it works. So let’s do that.
I don’t know what your current map looks like, I currently have the starting map with the table. I deleted it all so it’s empty. You don’t have to delete everything if you don’t want to, maybe just shift it off to the side for now. We are going to be building a level at 0,0,0 based off the index.
- Functions are always useful. I think right now is a good time for one. In the level blueprint, click the “+ Function” button to add a new one. Name it “BuildLevelFromIndex”. The idea is we’re going to loop through the index one by one and place actors in the world that correspond to the type. So in this new function, drag MapIndex into the graph with a “Get”. Drag a “For each loop” node from that. The For Each loop is a For loop that will get each element in the array one at a time.
Hmm… as I type this, I think I see a problem we might have in the near future. I will need to find a way to obtain an array value by reference so that we can set properties of the array dynamically… hmm. Ok, well problem for in a little bit.
-
From the “Array Element”, drag off a “Break TileRow” node. Now we have our inner array which we need to drag off and select another “For each loop”
-
Now, we need to determine the type of tile that exists at this location. Drag off the “Array Element” node and break the TileStruct. Drag off “Type” and use a = node to check if it is “1”. Drag from the resulting boolean and add a “Branch”. If you’ve never used Branch before, it is essentially an “If” statement. “If boolean is true, do this. Else, do that”.
-
So if this index has a wall, we want to spawn a wall. So add a “Spawn Actor from class” node to the “True”.
But what are we spawning? We don’t have anything yet. Or at least, I don’t. You might. So at this point, I am going to create an actor to spawn.
- Right click in the content browser and create an actor. I named it “Wall” because I am very creative. In the viewport, add a static mesh component named WallMesh. Set it to be Shape_Cube. Now we have a cube, woo! It is important to note that the size of this cube is 100 units.
I don’t think we need to do anything else with this for now. So back to the level blueprint.
- In the Spawn Actor from Class blueprint, set the “class” to be “Wall”. Drag off the Spawn Transform and click “Make Transform”. We need to calculate where the wall will spawn.
So, if the wall is 100 units, every grid space is 100x100. So for every index, we will need to multiply by 100 to get the actual world coordinates.
-
Drag off the “Array Index” in both loops and multiply them by 100. Then drag off the “Location” node from the make transform and break a vector. Feed the products into the X and Y.
-
Final step - go back to the event graph and drag a node off the completion of the first loop. Use it to call BuildLevelFromIndex.
And when you start up the game… TADAA!!
… this doesn’t look right at all. What did I do? Gonna post this for now and keep looking at this.