Hi, I have a theory that might be interesting - especially for those of you who are thinking about RTS-Games.
What always bugged me when playing them or looking at engines was that they were so limited! Most RTS-Games today are hardly RTS at all, but more about tactics than strategy (yes, there is a difference ).
So, what I am striving to do is creating a sandbox-RTS and virtually "eliminating" limits for unit count and map sizes.
This theory is about the latter.
For the sake of clearing things up, we will use a top-down space RTS as an example.
Usually Construct limits you to using Layouts. You can make them pretty big (ie. 20.000 x 20.000), but when flying around in a fighter, even those can get pretty small after a while.
Sure, you can use more than one layout, allowing the player to switch between them, but that has several drawbacks:
1. Changing layouts means having to load a new one. Depending on your machine, the size of the layout and the textures the game uses, this can take a few moments.
2. When being promised a world to conquer, it would actually be nice to HAVE a world to conquer and not just a simple set of regions. Moving seamlessly in a game, especially through large areas, is an amazing experience for every gamer that shouldn't be underestimated! =)
So, here comes the theory:
We can create a virtually "unlimited" game area by abandoning the built-in scrolling on layouts and instead let the game "scroll" around us. To do so, we would need an Array that holds the environment of our game.
Let's say you have a "Player"-Object which initializes in the center of your screen, has the "Center Screen"-attribute checked (so we cannot scroll away from it) and has opacity set to 0% (why would we want to see a player-sprite in an RTS-game?). In that sprite we store the private variables mapX and mapY, which will be our "virtual" coordinates in the gameworld.
Alternatively you can leave the sprite and use global variables instead.
What we now want to do is: When the player presses any of the direction-keys, we want to "scroll" there, without actually scrolling on the layout. Instead however, we scroll through the array, which holds the positions of every game object, and we check the array-coordinates against width and height of our layout to see if any objects enter or leave our viewport.
Now, those of you who used arrays for any mapping purposes already, might be tempted to use the array like this:
array( x-coordinate, y-coordinate, property-index )
That however would defeat the purpose - if we stored game-objects like this, our array would look something like that:
array( 1, 1, 1 ) = ""
array( 1, 2, 1 ) = ""
array( 1, 3, 1 ) = ""
array( 2, 1, 1 ) = Ship.UID
...
What you can see at first glance is that three out of the four elements I was showing you are empty!
Considering that with every tick, we have to go trough each element of the array, check it for existance and then handle it with our game mechanics, this technique unneccessarily eats up memory and cpu-time!
Instead the array we are going to use should look like this:
array( list-index, property-index )
Resulting in something like this:
array( 1, 1 ) = "2032" // x-coordinate of the object
array( 1, 2 ) = "1060" // y-coordinate of the object
array( 1, 3 ) = "42" // UID of the object
This would eliminate the need to go through empty elements, allowing us to store more actual in-game objects in the array and saving us precious cpu-time when scanning through it.
So now, whenever the player scrolls, his REAL position on the layout stays fixed. Instead, we change the mapX and mapY coordinates accordingly and scan through our array to see if any objects appear or disappear.
The latter should be done every tick, by the way - objects like ships and other units are supposed to be able to move, so their coordinates will change and they might show up on screen even if the player isn't scrolling.
So, with every tick we go through the array and look at the coordinates for each object we stored there. If it is on screen (x > mapX - DisplayWidth, x < mapX + Displaywidth, y > mapY - DisplayHeight, y < mapY - DisplayHeight), we will show it. If it is off screen, we either won't show it or hide it if it was shown until now.
Alternatively you could have two arrays with identical structure, one holding objects currently shown, one holding objects that are off screen and then move array elements back and forth as objects go on and off screen. That way you would have to scroll through two arrays (which shouldn't make much of a difference, considering it's still the same number of objects), but it would probably clean up your code a little bit. Also this would allow you to perform actions that are limited to objects on screen more easily, as you wouldn't have to sort through one large array, but you could just check the array with the on-screen-objects.
Also you might want to think about adding a threshold of some kind to the above equation.
Why? Imagine you have a ship, especially a big one like a carrier of some kind. It is about to go on screen. Our array stores the position of objects - in case of sprites that would usually be the center (so sprite.width / 2, sprite.height / 2). This means when said ship is determined as "on screen" by the above formula, one half of it actually already is, thus making it suddenly "pop up". We don't want that, so we have to add the height and width of the sprite to the equation above, or create a general threshold where we start showing objects, even though the player won't see them yet but is about to.
So, this is how I would do it. However, this presents us with a number of questions and problems.
1. What do we ACTUALLY do when making objects appear and disappear as they enter and leave the screen?
We could create and destroy them. But that presents us with the problem that we don't have access to their privates variables and that the sensitive data in those variables would be lost as soon as they left screen. This is a big deal, especially when it comes to information like hitpoints, as you can imagine.
We could make them visible and turn them invisible if they left the screen. The problem with that is: when you want to use this technique in your game, it usually means you want to make a game with a HUGE scope where you can do all sorts of things. So this probably means that you want to have a lot of objects in it too, otherwise why use unlimited game areas in the first place if it only means lots and lots of empty space? So, by switching the visibility of objects, we keep them in memory ALL THE TIME! Think 20 Carriers, 60 Destroyers, 500 Fighters and maybe lots of other ships in an early(!) gamestate and imagine how totally not smooth the game will start to behave.
An alternative could be the first method - creating and destroying objects as they go on and off screen. We would have to stop using private variables though and use our array to store object-related data. This means however that everytime we want to access this data, we have to scan through the array and find the UID of the object we would like to deal with.
2. How do we handle off-screen events?
Let's say you think about attacking and taking this planet. Your fleet is standing by and you finally give the command to attack. Your ships approach the enemy planet and suddenly you realize that you have to go elsewhere - maybe to build something, maybe because you are being attacked somewhere. As you scroll through the map, the game will determine your ships as being "off screen" at some point. And what happens then? Even when they are off-screen, they are supposed to be moving towards that planet and at some point start firing on enemy ships (which means spawning lots and lots of bullets, that we ALSO have to store in our array).
If until now you thought the size of those arrays would be managable, imagine 200 ships spamming the battlefield with laserfire and think about the number of projectiles this would produce per SECOND. Every single one being an entry in your array, of course. An entry you check for visibility and changes of object-dependant variables every single tick(!).
I personally see only one way to solve this problem: while you update the positions of each object in the array every tick, no matter if they are on screen or not, you don't let them actually DO anything else while they are off screen, but instead calculate what happens there.
That means our attacking fleet wouldn't spawn bullets, wouldnt even really attack while they are off screen. But the system would realize the AI is engaged there, pick targets and calculate how much damage who takes when.
So, you can see after this incredibly long text that there are a lot of problems with this technique. The questions are: do you see more? How would you solve them? Do you think such a feature is worth all the trouble?
Anyway, I will be waiting to hear what you think about this very eagerly, and thank you for taking the time of reading this wall of text
Cheers,
Sebastian