Efficient way to manage a large amount of behaviours for a tile based game

Started by
8 comments, last by Kirlim 6 years, 3 months ago

So I am building a turn based rogue-like (think CDDA). The game is going to have a very large map (up to 1000's x 1000's) however to alleviate most of that I obviously can't render everything so there will just be render a certain radius around the player and just load in and out data as the player moves.

The next major system I am prototyping is making interactive tiles destructible and pretty much everything will be destructible besides basic landscape (cars, doors, windows, structures, etc. will be destructible)

While I am only rendering a certain amount of tiles around the player, I want to keep the amount of colliders active at one time to be as small as possible for performance and currently the tilemap tool I use automatically merges colliders together.

So instead of creating a separate colliders for each of these tiles and having the destructible behavior tied to that object (which my tilemap tool would allow me to do) I was thinking that I would store an array of all the X and Y locations for the interactive tilemap layer and let the tilemap manage the colliders. 

Then when I hit a collider on the interactive tilemap layer, instead of of getting the behavior for how to deal with the destruction for that tile from that game object, I would pull it from the array I mentioned earlier based on the tile I attempt to interact with which I already have.

Does this sound like a good approach? Any other recommendations would be welcomed.

Advertisement

Is there an issue having the colliders and destructible behavior tied to the tile object, that makes you think having the array of locations would be better? Have you tried both of these out? 

I'm going to guess that the tile tool you r using is Super Tile Editor.

If the above phrase is correct then you can store properties inside your tiles and this props will ve passed to you on the collision or triggering.

@Spool My concern is that I still want to able to to render 4500 - 5000 tiles at any given time and there could easily be time where there are anywhere from 1000 - 3000 destructible tiles and having that many colliders active at one time I would think would be a bit much (though I am pretty new to game dev so not sure).

@AlbertoFdzM  No, I am using Super Tilemap Editor though it has similar functionality but I concern above applies to doing that.

Well, premature optimization is the root of all evil but...

Imagine you have a map och 1000x1000 tiles, each tiles stores a number of objects. If you have a bullet of whatever, traversing from tile to tile, you can avoid Unity's default collision detection triggers in favor of just doing manual collision detection tests on all objects in its own tile.

Also, for background tiles, if you just have all quads that are marked as static meshes, you will most likely not have a problem "rendering" thousands of them, actually, just placing a camera pointing down on the tiles will most likely just render a few all tiles assuming Unity has some sort of quad tree of whatever, this should be working without you having to do anything.

If you have one or mayle multiple Monobehaviours attach to each tile, having a couple of thousands of them might be laggy, specially mobiles, but I do not see the reason why each tile would needs its own logic and its own update function.

@flodihn Yea, I know premature optimization is the root of all evil but that does not mean you should always just write whatever unoptimized code first pops in your head. Over planning is generally not a good thing but under planning can be just as bad.

The reason each tile needs it own MonoBehaviour instance (though in reality, I could probably just use a regular Class) it that each tile should have it own set of data. For example, one type of destruction would be fire. One tile catches fire and then each turn a percentage of the structure's HP (for lack of a better term) is decreased and the tiles next to it have a chance to also catch on fire which just repeats the process until either no tiles nearby can be caught on fire or the fire stops for whatever reason on all tiles. I don't see how I would be able to have this kinda of functionality without each tile having it own set of data (but maybe there are ways)

Another reason I failed to mention originally for that array structure is that I am going to need something like this anyways. The map is ultimately going to be generated procedurally and I am going to need to save the maps data out anyways including this data (so that If I save the game and a building is on fire, that same building should be on fire when I load it back up).

1 hour ago, 3dmodelerguy said:

I don't see how I would be able to have this kinda of functionality without each tile having it own set of data (but maybe there are ways)

Each tile needing it's own set of data does not imply that every tile should be a MonoBehavior, or even have it's own GameObject. As you mentioned, you can use a common class (or even struct, to enforce value type) on some type of organized containers. Then you can process it anytime and any way you need, without the memory overhead of managing lots of behaviors (or even the memory overhead). 

About the collision detection, I've got really bad experiences trying to make Unity's one working the way I wanted on 2D games. It felt like using Unity against itself. I've ended up rolling my own collisions systems (one was based on simple bounding box - unity already have most of the code for this, and other was a Sweep SAT). Took more time, but overall, felt better than fighting the engine - I believe it's collision detection is better fit for real time and/or realistic simulations.

Are the colliders really needed, or that is an assumption? Assumptions will tend to lead to premature optimization or pessimization of something that you actually don't ever need! Since it is a turn-based rogue like, having colliders on map feels weird. Maybe there is a simpler solution, without troublesome collision systems?

@Kirlim Thanks for the feedback, let me respond.

49 minutes ago, Kirlim said:

Each tile needing it's own set of data does not imply that every tile should be a MonoBehavior, or even have it's own GameObject. As you mentioned, you can use a common class (or even struct, to enforce value type) on some type of organized containers. Then you can process it anytime and any way you need, without the memory overhead of managing lots of behaviors (or even the memory overhead). 

I absolutely agree with that and my original post was trying to explain that as my general original idea (I used bad wording in using behaviour to mean general functionality and not MonoBehaviour).

Also thinking about this more, I am going to need to be able to have destructible tiles that don't have colliders on them too so something like this I think is going to be needed no matter what.

49 minutes ago, Kirlim said:

About the collision detection, I've got really bad experiences trying to make Unity's one working the way I wanted on 2D games. It felt like using Unity against itself. I've ended up rolling my own collisions systems (one was based on simple bounding box - unity already have most of the code for this, and other was a Sweep SAT). Took more time, but overall, felt better than fighting the engine - I believe it's collision detection is better fit for real time and/or realistic simulations.

I have not had much issue with using colliders but then again I might be using it in a non-standard way. I am not using it for physics or anything like that as all movement is just manual position manipulation. What I am using colliders to be able to raycast to detect what objects are in a particular location.

I would like to avoid creating my own collision system unless 100% needed. I am using Unity in order to not have to create those lower / mid level game engine systems that I don't have an interest in building nor do I believe I have the technical knowledge required to build them in any meaningful or efficient way.

49 minutes ago, Kirlim said:

Are the colliders really needed, or that is an assumption? Assumptions will tend to lead to premature optimization or pessimization of something that you actually don't ever need! Since it is a turn-based rogue like, having colliders on map feels weird. Maybe there is a simpler solution, without troublesome collision systems?

While I can't say for sure, I think they are (and by needed, I mean they are much easier to work with that other alternatives). For one, the path finding solution I am using makes it calculations based on colliders to know where impassable areas are. 

Another case where I think they are very useful is for tracking enemies. Without colliders I would have to have an array of all enemies and where they are and such but seems like a bit of data to manage manually (not to mention they are accounted for in the path finding).

 

I am making a number of assumptions and I am willing to consider other ideas however one thing I don't want to do is reinvent solutions in which I don't need a unique solution.

Building a custom collision system seems like a bit of work and I can't imagine any custom work I would need that Unity's collision system does not have. It also seems like building a custom collision system before the Unity's collision system is giving me problems might be a bit premature.

Building a path finding solution is pretty much the same. The path finding I am going to need for this game is relatively basic in comparison to what other game might need to have.

 

I hoped I am not coming off as "I don't want to do any programming and just have a create game button for me" too much but I am using Unity because I don't want to deal with all the lower level stuff and be able to focus the unique parts of my game as much as possible. I understand there are going to be some lower level stuff I might have to build but I don't think the collision system is really giving me any issue right now.

22 minutes ago, 3dmodelerguy said:

I hoped I am not coming off as "I don't want to do any programming and just have a create game button for me" too much

I hope that I didn't answer in a way that made you feel like that. It looks like you've got a problem on architecture level, where all kind of opinions and alternatives are worthy considering. That being said, even though its a highly abstract choice, it will deeply impact the more concrete levels as you code. Choosing a bad architectural design might end up in technical debt or some good time of refactoring. Choosing a good one is hard because everything has drawbacks. When working with assumptions, it's hard to find a solid ground to evaluate any options, and an assumption can simply blind us to better approaches.

For example, if all you need are boxes for raycasting, I'm pretty sure the bounding box classes already have raycasting on them implemented. If you have few enemies to scan (lets say, up to 100), then spatial partitioning might be meaningless. With a list of all enemies, you can iterate that much without ever noticing a cost (and you can distance as a culling method).

The best solution is the simplest one that lets you get started, but we can only help on that as you cut off assumptions. For example, making hand-picked solutions might seem costly and time consuming, but maybe managing active colliders based on player position might be even more. There are tricky things like the cost of changing the static colliders of a scene, and other annoying things like having a code to detect which collider belongs to which tile, to then know which behaviors to activate in which situation (you get the idea). There is also the possibility of not using some features of your current tools, and add a few more to your game.

 

41 minutes ago, 3dmodelerguy said:

I don't want to deal with all the lower level stuff and be able to focus the unique parts of my game as much as possible.

Its perfectly fine on going with what seems fastest and easiest right now, too. Quickly validating game ideas is valuable. If you find a problem later on, you'll know for sure what you want to solve. It's a valid approach and tradeoff. Just be sure to code in a way that is easy to change. If you go with this approach, the suggestion is to "fail fast, fail often" to quickly react each iteration.

This topic is closed to new replies.

Advertisement