[RELEASED] GSpawn - Level Designer (3D Tile Rules, Curve Spawn, Scatter Brush, Modular Walls ... )

I gave this a go and created the same rules as you did (using dummy prefabs). In my case this works as expected.

Can you please give me a link to the assets you are using?

Regarding the prefab previews, have you tried rotating the previews? It may be possible that in the default rotation, the prefabs point with their back-side towards the camera and that’s why they don’t show up.

The tiles i used are from Tile World Creator 3, the Cliff tile

For the preview… i’ve tried rotating but it’s still empty all around
Also tested rotating other prefab just to test that i can rotate

I’m currently looking around methods like rowColToBitIndex
Tbh i haven’t fully understood how the bits fills an ulong in ur case, but from TileRuleMask.debugLog, it seems like this is already correct. Just thinking where else to check from here…

@XGT08
U don’t have to buy it

The 4-tile basic shape is this
Maybe i can share this prototype tile set actually…?

Hello,

It would probably be a good idea to contact the developer of the asset and ask them if it’s ok to share the tile set with me.

You can send them an e-mail explaining the situation.

If the developer doesn’t agree, I will purchase the asset and I will be able to look closer into this.

Cheers,
Andrew

1 Like

OH. My previewer works now
My artist just mentioned that his unity GPU crashed, then opened the same scene and said the scene broke. The background’s all black and previewer invisible

I just realized i’ve been working on that kinda scene… Tried a new scene and did GS initialize again and now the previewer works
He said:
“theres an option to reset data cache, I didnt save the scene but it would be a good idea to figure out a solution before it happens on one we want to keep”

But the interior rule tile still doesn’t work…

About the tile, here’s my version’s prototype. It’s even more basic than TWC’s
Look for the 4 mesh: Floor, Interior, Exterior, Edge
Prototype tile rule test.unitypackage (160.5 KB)
This package already sets it to prefabs. Just need to make the rule tile for them

edit: I actually haven’t tried GS with this prototype tileset…
edit: Actually, the interior corner and floor looks the same here… uhh
edit: Ok updated the blend file
Now the shape of the 4 tiles are all distinct. I think it’s valid tileset
Sry i’m not at home and on the move, so kinda updating this in a rush
edit: Updated the link again, now it’s a unity package, already set them to have 4 prefabs for each tile

With this prototype tile, perhaps this is the clearest screenshot to show. The cursor is on cell top right from the floor tile. It’s empty. Yet the result is a floor tile which should require the cell on the cursor to have a tile

Ok i may have found the bug


Top one is reqOn, bottom is reqOff
This is the log of each rule tile being checked in TileRuleMask.match


This is the rule tile mask for Interior
I did notice that if i click the middle cell, every cell turns green. Even when cell check radius is set to One, when i switch to Two, everything turns green too apparently
So i made sure to switch to Two, unchecked all the outer cells to be neutral, then switched back to One

What is shown in the log, however, still shows the outer cells are all still on 1. Why? Cached?
Anyways, this causes any next bit mask checks to fail or at least, not as expected when choosing radius One

edit:
And so, i deleted that rule tile and recreated it. Now things works
But… why did that happen in the first place? Or can the “click the middle cell” be clamped to the radius of the selected radius?
Or maybe better, can the bit mask check turn the irrelevant outer radius to 0 first before doing the check?
edit:
Oh u can shift click to disable all. Ok that’s handy
U can also click and drag to multi set cells. Also handy. Very neat
But yea if this case happens to others, just be careful with setting all

1 Like

Wow! That’s very helpful :smiley: Thank you for providing this kind of information. It may have saved me quite a bit of debugging time.

I believe that the bit mask should be reset to 0 when changing the neighbor radius. Also, when using the middle button, only the bits within the current neighbor radius should be affected.

These changes will be reflected in the next update.

Cheers,
Andrew

1 Like

Hi. I’m wondering about ramps. Does rule tile apply to ramps, or it’s completely manual?
It seems i can only place 1 type of ramp, regardless of the situation of the cells around it?

Also, in the pic, i’m trying to place a ramp in the cursor, but it doesn’t let me
I’ve prepared a ramp exterior for this cell

Just for clarity, the cyan is standard, dark blue is platform, yellow is ramp. Yes, there are holes. I wanna see how far i can go before deciding to make each tile fully enclosed

edit:
My goal is this (in Blender)

In GS, yes apparently the rule tile does have effect
But the rule tile doesn’t consider other placed ramps tiles?
In this pic, i can’t place a ramp on the cursor. Not sure why…

I have just started looking into this. I will update you as soon as I have some news. Thanks!

Hello,

Indeed ramps don’t affect the rule. I’ve looked over the code and there is an explicit check I make inside the calcNeighborMask function to prevent ramps from affecting the rules. I am not sure why I made this decision, I will play around with the alternative and see if there is a specific reason for doing so.

There are many implementation details which I honestly don’t remember unfortunately :slight_smile:

So let me play around with this for a bit and I will get back to you.

Until then, you could modify the code inside the calcNeighborMask from this:

ulong ruleMask = TileRuleMask.defaultReqOnMask;
for (int i = 0; i < _neighborOffsets.Length; ++i)
{
    var offset  = _neighborOffsets[i];

    var coords  = cellCoords;
    coords.x    += offset.x;
    coords.z    += offset.y;

    var tile    = getTileObject(coords);

    if ((tile != null && (!paintParams.paintingRamp || !isRamp(coords))) || _occupiedCells.Contains(coords))
    {
        // Note: Subtract offset.y instead of adding, because in bit mask space, rows decrease upwards.
        ruleMask |= TileRuleMask.setBit(ruleMask, TileRuleMask.middleBitRow - offset.y, TileRuleMask.middleBitCol + offset.x);
    }
}

return ruleMask;

to this:

ulong ruleMask = TileRuleMask.defaultReqOnMask;
for (int i = 0; i < _neighborOffsets.Length; ++i)
{
    var offset  = _neighborOffsets[i];

    var coords  = cellCoords;
    coords.x    += offset.x;
    coords.z    += offset.y;

    var tile    = getTileObject(coords);

    if (tile != null || _occupiedCells.Contains(coords))
    {
        // Note: Subtract offset.y instead of adding, because in bit mask space, rows decrease upwards.
        ruleMask |= TileRuleMask.setBit(ruleMask, TileRuleMask.middleBitRow - offset.y, TileRuleMask.middleBitCol + offset.x);
    }
}

return ruleMask;

Yes that’s very intuitive now

There’s 1 last problem i think


These 2 pics are showing the same cell but from different angle: where the ramp meets the cliff. There’s holes because the geometry is not fully enclosed. I’d like to avoid having geom that’s fully enclosed bcoz it’ll make every tile have double faces (even tho the examples in the docs all uses full enclosed tile shape)

Gonna mention something else first. So I see that u can press Y on Ramp tiles to rotate it manually. I guess ramps are a trickier subject, so on top rule tile choosing the appropriate tile for u, the rotation might be too hard for the system to determine
So i wanna ask about the prefab variant on a rule tile. Maybe a feature request. Can there be a manual hotkey to cycle over the prefab options for a given rule tile tile? Something like the Y to rotate? Instead of spawning a random prefab from the list, it always picks the 1st one, and then we can manually choose which one should use the variant
If this is added, then i can have a variant with an enclosed shape, to be used only for the ends of the Ramp, or some other situations

Ok i went ahead and made the feature request
So i reckon, manually changing an existing tile to the next prefab, 1 by 1, is tedious. The fact that placed tile have no recollection of what prefab index it is also makes it complicated

So i added a “prefabIndex” in the brush settings, which if it’s more than -1, it’ll use that index to bypass the probability/condition process of which prefab for the rule tile to use for any prefab being spawned
This allows for exact specific control of which prefab variant i want to paint on, without resorting to have to make another grid + its own tile rule, just to get the variant that i want

Hi. I just tested pushing to repo with my artist, and we’re getting a lot of bugs
On fresh project: making a scene, make stuff with tile rule tool, then restart editor (or simply by undo in his case), and come back to it. All the tiles are no longer connected to GS. Delete tile doesn’t delete anything, placing new tiles are not connected with the tiles previously placed, etc. All the tiles are parented to the grid, can be toggled visible, and cleared from the GS inspector. But can’t interact with them any more
This also happens in our working repo, on both machines

Bug on different machines:
The black scene + prefab previews not showing. On mine, re-open the scene fixes it. On his, re-open doesn’t fix this. Tools work tho

Changing around my repo to his repo, then back to my repo, involves a single type of error log spam. They’re different depending on the situation, but mostly nullref. Below is an example
The effect is no GS tool can be activated, some windows fail to draw (in 1 case it’s the Prefab Manager, in another it’s the Tile Rule).

NullReferenceException: Object reference not set to an instance of an object
GSpawn_Pro.TileRuleProfileDbUI.createRuleUIs () (at Assets/GSpawn - Level Designer (PRO)/Scripts/Level Design/Object Spawn/TileRuleProfileDbUI.cs:593)
GSpawn_Pro.TileRuleProfileDbUI.onBuild () (at Assets/GSpawn - Level Designer (PRO)/Scripts/Level Design/Object Spawn/TileRuleProfileDbUI.cs:121)
GSpawn_Pro.PluginUI.build (UnityEngine.UIElements.VisualElement rootElement, GSpawn_Pro.PluginWindow targetWindow) (at Assets/GSpawn - Level Designer (PRO)/Scripts/UI/PluginUI.cs:66)
GSpawn_Pro.TileRuleProfileDbWindow.onBuildUI () (at Assets/GSpawn - Level Designer (PRO)/Scripts/Level Design/Object Spawn/TileRuleProfileDbWindow.cs:14)
GSpawn_Pro.PluginWindow.OnGUI () (at Assets/GSpawn - Level Designer (PRO)/Scripts/UI/PluginWindow.cs:81)
UnityEditor.HostView.InvokeOnGUI (UnityEngine.Rect onGUIPosition) (at <1650f973f32045d6bc81eca6beea8ea9>:0)
UnityEditor.DockArea.DrawView (UnityEngine.Rect dockAreaRect) (at <1650f973f32045d6bc81eca6beea8ea9>:0)
UnityEditor.DockArea.OldOnGUI () (at <1650f973f32045d6bc81eca6beea8ea9>:0)
UnityEngine.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt, UnityEngine.Matrix4x4 parentTransform, UnityEngine.Rect clippingRect, System.Boolean isComputingLayout, UnityEngine.Rect layoutSize, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <b47ea342d1544b0d882d830fdc1f4f55>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)

One time, deleting the GS object and re initialize the scene (and other things maybe?) fixed the bug above. But now i tried that, and now ending up with this error whenever my mouse is on top of inspector

ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
System.Collections.Generic.List`1[T].get_Item (System.Int32 index) (at <694551e795764b938030a3128fac2b36>:0)
GSpawn_Pro.EditorUIEx.profileNameSelectionField[TProfileDb,TProfile] (TProfileDb profileDb, System.String label, System.Single labelWidth, System.String selectedName, System.Boolean showMixedValue) (at Assets/GSpawn - Level Designer (PRO)/Scripts/Core/Extensions/EditorUIEx.cs:159)
GSpawn_Pro.TileRuleGridSettings.<buildUI>b__37_0 () (at Assets/GSpawn - Level Designer (PRO)/Scripts/Level Design/Object Spawn/TileRuleGridSettings.cs:77)
UnityEngine.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt, UnityEngine.Matrix4x4 parentTransform, UnityEngine.Rect clippingRect, System.Boolean isComputingLayout, UnityEngine.Rect layoutSize, System.Action onGUIHandler, System.Boolean canAffectFocus) (at <b47ea342d1544b0d882d830fdc1f4f55>:0)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)

edit:
Perhaps it’s bcoz TileRuleGrid._tileMap is Dictionary, and that’s not serialized to scene save

Hello,

Judging from the error messages, it looks like the plugin was not able to find the active tile rule profile. This means the profile was somehow deleted. You may have somehow deleted it from the Data folder.

When deleting profiles, please use the Profile UI in the Tile Rule Profile Window.

To recover, you will have to delete the GSpawn object and the Data folder and then re-initialize.

Yes that fixed it, altho i dont remember deleting any data file
Deleting the whole data folder meant i lost the tile rules, and other things too i imagine. Is there no way to initialize only certain missing data files? Or try find the file and re-connect them?

Also, i thought the tiles of tile rule is saved in scene. But there was a time (during the bug?) i edited the tiles in the scene, saved, and went out and back to the scene, and the tiles are back to before i edited. And 1 of the warning when i was gonna delete data was… scene objects created by GS will also be lost or something
So… where are they saved?
If i’m making a multiplayer or player made content, how should the data of the level be sent around?

The tile rules are saved as assets that reside in the Data folder. This allows you to reuse the same rules, settings, configurations etc in multiple scenes. Otherwise, you would have to redo the profiles for each scene.

This is how it was done in an older version. All settings/profiles etc were saved at the scene level and people had to redo everything for each new scene.

The problem with assets is that you need to be careful not to mess around with the Data folder and its contents or you risk loosing your work. :frowning:

1 Like

Still tho, i dont remember deleting any data file. This bug with tiles starting to disconnect is so risky, could lose a lot of work, but i feel this kinda case can be handled in a simple way somehow

Maybe i add a button to… repopulate the Dictionary _tileMap, with all the “disconnected” tiles in the scene? Is the _tileMap the only place the tiles are saved?
I don’t see anywhere how this dictionary is saved in scene tho? Isn’t dictionary not serializable by default?

edit:
Actually it might be in OnEnable, registerTilesWithGrid…
But i dont get how this can also fail (at least, when the bug was happening)
If the GS object needs to be recreated, then there was no way to re-connect the GS object’s Tile Rule grid with the existing grid parent object in the scene