Theory: (Virtually) Unlimited Game Area

New releases and general discussions.

Post » Thu Nov 26, 2009 3:05 pm

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
B
3
G
3
Posts: 14
Reputation: 903

Post » Thu Nov 26, 2009 3:50 pm

Another crazy german dude you should talk to PixelRebirth he is just like u :P
B
11
S
3
G
4
Posts: 622
Reputation: 3,186

Post » Fri Nov 27, 2009 12:48 am

...have you tried enabling 'unbounded scrolling'?
Scirra Founder
B
359
S
214
G
72
Posts: 22,952
Reputation: 178,580

Post » Fri Nov 27, 2009 2:57 am

I didn't very much, because I was still trying to get the hang of Construct. I wasn't sure how far I could go without it hitting me with exceptions. How far does it go? Do you use int or long int for the coordinates?
B
3
G
3
Posts: 14
Reputation: 903

Post » Fri Nov 27, 2009 5:08 am

Been working on something like this. Haven't got a decent example yet. Will post when I do.
B
55
S
12
G
8
Posts: 339
Reputation: 9,314

Post » Fri Nov 27, 2009 5:29 am

[quote="impact":17b06fr2]How far does it go? Do you use int or long int for the coordinates?[/quote:17b06fr2]

I think it uses a signed 32-bit integer, so the maximum layout size is on the order of 2,000,000,000 x 2,000,000,000; this is most likely far more than anybody really needs, although I could be wrong. :P
Moderator
B
98
S
37
G
19
Posts: 1,584
Reputation: 17,817

Post » Fri Nov 27, 2009 10:09 am

No, it uses 32-bit floats for co-ordinates, which allows an insanely high maximum (about 340282340000000000000000000000000000000), but with reducing precision the further out you go.
Scirra Founder
B
359
S
214
G
72
Posts: 22,952
Reputation: 178,580

Post » Fri Nov 27, 2009 11:42 am

[quote="impact":37z809g7]Most RTS-Games today are hardly RTS at all, but more about tactics than strategy (yes, there is a difference ;) ).
[/quote:37z809g7]

Are you able to expand on why this is? (I'm not an RTS expert)
B
134
S
65
G
16
Posts: 1,766
Reputation: 19,190

Post » Fri Nov 27, 2009 11:50 am

Just split the game map up into "sectors", whenever the player enters a sector, shift layout coordinates to use that sector as the center.
Example:
Player is at 400,400
Sector A1 is at 0,0, spans 500 pixels in both directions
Sector B2 is at 1000,1000
Player crosses coordinates 500,500, toward sector B2, the game engine shifts coordinates; Sector B2 becomes 0,0, while Sector A1 becomes -1000,-1000 and the player is teleported. May notice a slight stutter during teleport. However, typically sector shifts should occur when the player is in transit and there is nothing else on the screen (he's in open space far away from any landmarks), so there wouldn't be any pause.

Alternatively, set the player at 0,0 permanently - and shift the game around him as needed. That would mean manipulating coordinates in relation to the player. Too much of maths.
B
62
S
21
G
12
Posts: 1,910
Reputation: 13,155

Post » Fri Nov 27, 2009 12:52 pm

[quote:355es1tp]So, what I am striving to do is creating a sandbox-RTS and virtually "eliminating" limits for unit count and map sizes.[/quote:355es1tp]

Still your going to be limited somewhere in that.
I was thinking to deal with this you might treat the map like solar systems. I mean in essence they are round. So if you think in terms of staying within one system at a time, and if you were to go one direction you would eventually wrap around to the same coords you started from. You could then have different layouts for different solar systems.

Now as to unit counts, you could, in theory, use one sprite for all, and then have a separate frame for a different unit type. Of course for logic's sake you probably wouldn't want just one sprite, but you could cut down on extra sprites using separate frames.
Image Image
B
161
S
48
G
90
Posts: 7,356
Reputation: 66,767

Next

Return to Construct Classic Discussion

Who is online

Users browsing this forum: No registered users and 0 guests