Ok, there comes a point in everyone's clicking career when they stop using the MMF frame editor/TGF level editor to make levels and start making their own level editors for their games.
Make no mistake, this is a big step. To put it simply custom level editors give you the flexibility to greatly increase level interactivity and almost always do so with less code than it would take with a standard frame-designed level.
All of a sudden you are doing things that would have been a nightmare to do normally, and you are doing it in 2-3 events as opposed to 20, and you are doing it all with background objects- no AOs in site. How is this done?
Custom level editors give you the power to store not just the position of graphics and obstacles in you level, but any other kind of data you wish. You might want to store the surface type of each tile then use this data to play different sounds when something hits that tile (hitting a wooden barrel and hearing a metallic clang is a little cheap IMO), or maybe make different tiles effect objects movement speed - players could move fast on paths, slow in bogs and regularly on grass - there are virtually no limitations to level interaction.
There are 3 fundamental tiers to level editors, which are:
[1] The data -> [2] The data parser/interperater -> [3] The game engine
This article only focuses on the middle tier: the data parser, that is, the code that reads the level data and does actions accordingly to create information that the game engine can interract with (on the most basic level this is pasting in background objects).
If I get any requests I will write articles on the two other tiers as well.
There is one essential tool to make this data parser: Fast Loop, without fastloop level editors are very sloppy and limitted indeed, especially for big levels. We are going to make a very simple data parser in this article, it will basically read data from an Array Object and then paste in background objects and some sprites (AOs) for the game to use (I'd like to point out by no means is the Array Object the only method of storing level data - you can use List objects or even INIs, but I'm avoiding that here because you need to know how to parse delimitted strings to do that)
There are 2 formulae that will be used in this example to save using 2 loops to scan through data. Generally you need a loop for each dimension of the the table of data you are scanning, but as a matter of preference I will only be using 1 loop for this example and 2 formulae. The formulae are:
To get the X coordinate: Floor(Absolute coordinate / Y dimension of array)
To get the Y coordinate: Absolute coordinate Mod Y dimension of array
In this example we will scanning every single absolute coordinate, so the absolute coordinate will be the loop index of the loop.
And just for the curious ones (these won't be used in the example):
To get the Z coordinate: Floor(Absolute coordinate / (X dimension of array * Y dimension of array))
To get the absolute coordinate: (Z coordinate * X dimension * Y dimension) + (X coordinate * Y dimension) + Y coordinate
To get the number of absolute coordinates: X dimension * Y dimension * Z dimension
AOs (Active Objects):
Player - some kind of movement - 8dir or Platform is fine
Sprite spawner - Not visible at start
Other objects:
APO (active picture object)
Fastloop Object (or just use the one in MMF)
Array Object - 0 based index, Stores values, 10x10x2 (we have a Z dimension of 2 so we have 2 layers- one layer for the background, 1 layer for the sprites (AOs)
Files:
Tileset bitmap - download example one here (ripped from Zelda 3):
1.
Start of Frame:
♦ Start loop generate level for 100 loops
♦ Start loop paste level for 100 loops
♦ Start loop create sprites for 100 loops
//--------------♦-TIER 1-♦-------------- //The next 5 events are just to make up some dummy level data, usually you would load the level data from a file that you made with your level editor.
2.
On loop generate level:
+ Floor([loop index of generate level] /10) <> 1
+ ([loop index of generate level] Mod 10) <> 1
♦ Array- Set value at ( Floor([loop index of generate level] /10), ([loop index of generate level] Mod 10) ) to Random([number of tiles in tileset])
3.
On loop generate level:
+ Floor([loop index of generate level] /10) = 1
+ ([loop index of generate level] Mod 10) <> 1
♦ Array- Set value at ( Floor([loop index of generate level] /10), ([loop index of generate level] Mod 10) ) to Random([number of tiles in tileset])
4.
On loop generate level:
+ Floor([loop index of generate level] /10) <> 1
+ ([loop index of generate level] Mod 10) = 1
♦ Array- Set value at ( Floor([loop index of generate level] /10), ([loop index of generate level] Mod 10) ) to Random([number of tiles in tileset])
5.
On loop generate level:
+ Floor([loop index of generate level] /10) = 1
+ ([loop index of generate level] Mod 10) = 1
♦ Array- Set value at ( Floor([loop index of generate level] /10), ([loop index of generate level] Mod 10) ) to 0)
//Place the player
6.
Run this event once:
♦ Array- Set the value at (1,1,1) to 1
//End dummy generation
//--------------♦-TIER 2-♦-------------- //The loops paste level and create sprites are what we are really concerned about in this example. paste level looks at the data in the array and pastes the appropriate image down with the appropriate collision mask. create sprites looks at the data in the array and creates the corresponding AOs
//There are 2 pasting events needed: one for obstacles and one for non-obstacles
7.
On loop paste level
+ [Array value at ( [loop index of paste level] /10, ([loop index of paste level] Mod 10) )] < [Value of first tile in tileset that is an obstacle, eg 2]
♦ APO- set value A to ([loop index of paste level] / 10)
♦ APO- set value B to ([loop index of paste level] mod 10)
♦ APO- set value C to [Array Value at ([value A of APO], [value B of APO], 0)] mod [ X dimension (number of tile columns) of Tileset image eg 7]
♦ APO- set value D to [Array Value at ([value A of APO], [value B of APO], 0)] / [ X dimension of Tileset image]
♦ APO- Create Backdrop object ( [value A of APO]*[width of tile, eg 24], [value B of APO]*[height of tile, eg 24], [value C of APO]*[width of tile], [value D of APO]*[height of tile], [width of tile], [height of tile], as not an obstacle )
8.
On loop paste level
+ [Array value at ( [loop index of paste level] /10, ([loop index of paste level] Mod 10) )] >= [Value of first tile in tileset that is an obstacle, eg 2]
♦ APO- set value A to ([loop index of paste level] / 10)
♦ APO- set value B to ([loop index of paste level] mod 10)
♦ APO- set value C to [Array Value at ([value A of APO], [value B of APO], 0)] mod [ X dimension (number of tile columns) of Tileset image eg 7]
♦ APO- set value D to [Array Value at ([value A of APO], [value B of APO], 0)] / [ X dimension of Tileset image]
♦ APO- Create Backdrop object ( [value A of APO]*[width of tile, eg 24], [value B of APO]*[height of tile, eg 24], [value C of APO]*[width of tile], [value D of APO]*[height of tile], [width of tile], [height of tile], as obstacle )
//Unfortunately usually more than 2 events are needed for sprite creation, to the sum of ([number of types of sprites] + 1) events, so total number of events for a data parser using this system should always be ([number of types of sprites] + 3) events. This first event may look pointless, but it is required to make to loop operate correctly.
9.
On loop create sprites
+ [Array value at ( Floor([loop index of paste level] /10), ([loop index of paste level] Mod 10) )] = 0:
10.
On loop create sprites
+ [Array value at ( Floor([loop index of paste level] /10), ([loop index of paste level] Mod 10) )] = 1:
♦ Set X position of Sprite spawner to (Floor([loop index of paste level] /10) * [width of tile])
♦ Set Y position of Sprite spawner to (([loop index of paste level] Mod 10) * [height of tile])
♦ Create Player at (0,0) from Sprite spawner
//--------------♦-TIER 3-♦-------------- //Then there's just some small supplement for the third tier (game engine)
11.
Player collides with background:
♦ Player- Bounce
yeh it can be done more easily I'm sure, but I wanted to make it this way because I think its a little easier to grasp than nested loops and such (except for Phizzy, my MSN is biax666@hotmail.com if you want me to explain it to you mate)
The extensions: Um, all the extensions used are shipped with MMF 1.5, but they might be available for download at the extentions list:
http://extensions.cyber-gaming.co.uk/
Sure thing AfterStar itd be my pleasure, been a while since I did any MMF coding this is gonna take some brain-warming-up, hehehe. Expect the 2 other articles soon, and expect an example file some time...