Open DivFord opened 8 years ago
Actually, this may be a perfect opportunity to have someone write a level editor. If we had a level editor, rather than just two character codes for each thing, we could encode so much more information into each tile (e.g. a link between two things, a specific value for a variable (such as how long a tile might last for, if it's on a timer)).
Also, it'd just be nice, generally, to have some sort of level editor thing, come the later time when we're just adding content, rather than code.
I hate to say "this is a great thing that someone else should do!", but, I'm pretty busy with work (he says, procrastinating) and collisions, so someone else would have to do this if it were to be done.
I'm happy to give it a shot, though Matt would no doubt do a better job of it.
If we do make a level editor, we should probably decide now how we want the data it outputs to be organized. It can't just churn out the same text files we've been using, or it wouldn't actually do the things you mentioned.
Seeing as it's going to be completely separate, you don't have to use the same system (feel free to use Unity or whatever works. Or you could be cool like us cool kids and write it from scratch :P).
The two character codes work nicely for identifying each tile (will we want more later potentially?), but if we have complete freedom in code length, we could just always use the actual names of the things. Brackets are nice for grouping things, so I was picturing a tile (e.g. a Gate) being given by (prop, gate, keycode)
, where keycode would be some code that matches a (pickup, key, keycode)
somewhere else. a generic stone tile could just be (block, stone)
(trailing commas optional). The dimensions of the level would then be given at the start of the level, and Patterns could be done by (pattern, LR, 3, 4)
, for a left to right, 3x4 pattern (I don't actually know how that's stored or done at the moment...), with (none)
in the empty spaces.
Alternatively, you could just have a list of things, and coordinates giving their positions inside the brackets. I should probably also mention now that (provided I don't scrap this collision stuff) I've added a new Object: BlockGroup. This groups identical Blocks together, so they don't have to have separate hitboxes, and importantly, it reduces the number of Objects in a level from, e.g. for the menu screen, ~70 to ~4. Unfortunately, this has added a bit of a wait to the level loading (as may be visible in the .gif) as it has to sort all of the Blocks into groups, one by one. It'd probably be nice if the output could have things pre-grouped, but the wait isn't that bad.
Thoughts?
I had a think about this, and I reckon I will use Unity. That lets me get something cross-platform with a usable interface done pretty fast. Guess I'm just not cool enough…
I'm not sure we do have complete freedom. The longer the block identifiers are, the larger the level files are going to be. We should probably aim to keep them compact. Given that the level editor produces the files, and the game reads them, there's no need for them to be easily readable by a human.
Your suggestion is pretty close to what I thought of. I was thinking of something like "Pg(1)" for a gate prop with key code 1. Am I right in thinking that the block loading already works as a function call?
How do your block groups handle blocks that can change (eg. dirt, ice, water)?
EDIT: I'm quite keen not to be the one to implement the in-game level loading...
Eh, level loading is Matt's area. :P
Even before I put in BlockGroup, changing Blocks simply deleted themselves, then added a new Block in the same place. The only difference now is that Room::addBlock()
checks if the new Block fits into any existing BlockGroups, instead of just adding it to the vector.
Ah, Ok. I'll see about grouping things in the level-file then.
While we're on the topic of pre-calculating things, I thought it might be worth putting the tool requirements at the start of the file, rather than in the filename.
Going pretty well so far. I have a toolbox and a palette. I can draw, erase and flood-fill all the block types, and I can place all the props. Just about to add the inspector for changing prop data.
Couple of questions:
1 - We might need info for things other than linking (e.g. lifetime for the crumbling blocks from #85). They might also want to store a list of which Blocks they can connect to.
2 - Only contiguous regions. That way they can share one hitbox defined by its border.
Aren't the crumbling 'blocks' actually props? I feel like having blocks be things that tile, but don't have metadata, is probably a good definition. Obviously has nothing to do with the fact that I've already set the editor up that way :P Actually, now that I think about it, the block groups are another good reason not to have metadata on blocks. If we did, we'd need a different group whenever blocks had different metadata.
For my output format, I'm thinking of something like this:
<PROPERTIES>
Room; 20; 10; Generic; 1
<BLOCK GROUPS>
S (0,0) (1,0) (2,0) (2,1) (3,0); S (5,0) (5,1) (6,0); D (7,0) (8,0) (9,0);
<PROPS>
C (2,2); G (7,1,1);
<ENEMIES>
L (8,1);
<PICKUPS>
K (2,3,1);
<PATTERNS>
O3 (10,0)
First section is properties, listing data type (room or pattern), width, height, zone, and required tools bit mask (in this case, 1, i.e. cannon).
Next is block groups. Each group is given as a block identifier (eg. S = stone) followed by a series of coordinates.
Then we have props. Each prop is given as a prop identifier (eg. C = crate, G = gate), followed by coordinates (for the bottom-left of the prop, in the case of multi-tile props) and any metadata. In this case, crate has no metadata, while gate has the key code (1).
Enemies have the same thing. Identifier followed by coordinates. Not sure if they should have metadata.
Pickups follow the same format as props. In this case it's a key linked to the gate.
Patterns use the notation I laid out in the design docs, followed by the coordinates of the bottom-left of the pattern.
I'm using single letter codes to save memory, but the idea is that it could be any length of string (ends at the space). That way, if we need more codes, we can have them.
Entrance and exit must be in there somewhere. Do we want to continue treating them as blocks, or as a special case?
The current distinction between Blocks and Props is only that Blocks tile and are massless at the moment (I think). It might actually be a nice idea in the long run to just cut the Blocks out altogether, and replace them with BlockGroups, seeing as BGs rely on Blocks obeying several very specific properties (such as being a 1x1 massless square).
While it is just asking for trouble, you could probably remove the "titles" for each chunk, and just have each new object type on a new line, provided that they are always in the same order (maybe given by the order in ObjectTypes?).
Seeing as the weapons have codes, you could use those instead of the numbers. Whichever's easiest to be honest, as readability isn't all that important.
The entrance and exit are treated as special cases in the code already, so I don't see how it could hurt.
I assume this is using the same resources as the game (e.g. the same spritesheets)? If so, do you think it might be worth just linking over to the lists from the game itself (or moving the list files and the gfx and audio folders into a new "resource" folder or something)? That way, if we add a new Block to the game it would automatically be in the editor, and vice versa.
Yeah, it is using the same resources. I was planning to add in a "test" version of blocks, props, etc. so as to have something to represent new content until it's added to the editor, but shared data files would definitely be a better solution.
At the moment, the editor has all the data hardcoded into static classes, so it would be fairly easy to get it to read external data files instead. I think I'll hold off on actually doing it though, since setting up the level-parser is likely going to involve changing those list files. I don't want to go to the trouble of getting regex working only to have the format change and muck it up.
Speaking of list files, should they stay as .cpp files, or do we want them to be .json or something like that?
Well, seeing as they still need to be read by the C++ game, I don't think making them java files would be a good idea (correct me if there's something more to this).
I should probably get back to work on this... :(
Derp. Not .json. I meant .xml. You know, one of those formats meant for storing data, rather than code.
The question is, would it be compilable into the executable? If it's going to be kept outside, we may as well just use a .txt.
My take on it is; for development purposes, we want an easy to change data file that we can share between the game itself and the editor. When it comes to shipping (if?) we'll probably want to compile it into the executable, but presumably not until then. Having a development mode that ran directly from the data files would save us compiling whenever we want to test something.
Can C++ read .xml though? Otherwise, surely it's easier if one of the programs can read the file, rather than both having to interpret it?
Yeah… It's just that the current file looks pretty intimidating to parse. A compromise might be preferable.
That's just me trying to make things easier for me though...
Well, the editor won't need some of the info that the game does, and vice versa (I'd imagine), so a common file would have the advantage of not loading the game down with useless info... Feel free to have a go, but I think this is one of those times where our lack of forethought has come back to bite us.
I'm not ready to start on the import-export stuff yet, so by all means, let's discuss this further before diving in.
The editor cares which zones things can be used in (eg. lizards go in the desert). I think that's the only thing it wants that the game doesn't. Potentially the game could use that info, if we ever want to have random props/enemies/pickups.
To be honest, the main thing I'd want is to integrate the sprite coordinates into the main block data. It feels weird having them in separate functions, and the getBlockSpriteMap looks hard to parse.
From a quick Google around, it looks like java can call C++ functions using libraries like JNI, JNA, or SWIG. Would that solve the problem?
Possibly. Unity script is a weird variant of javascript, but it does have some C++ integration. I'll have a look.
I can access the command line from Unity… Not sure if that helps.
I reckon txt files are the best solution. Maybe with a python script to write them into headers, similar to what Matt made for the level files.
Hmm. Well, if we were to write a separate program that could somehow supply whoever asked with everything they wanted, then both the editor and the game would be able to call it. That way, we'd be able to have all of the lists in one place, but be available to both programs.
EDIT: Bleh, didn't fully read your two line post. Writing in .txt files, then converting to headers does seem fine. Now we need to think of a common syntax for the lists.
I assume the data types we can pass out of a program are fairly limited. String, int, float and boolean should be doable, but we won't be able to pass out your vector class. I suppose we'd just have to have separate calls for fetching the x and the y coordinate.
Is air included in the block groups system?
If I can chime in:
Depending on how sophisticated the block and/or level data is going to get, I think having some structured way of storing it outside the program would be beneficial. We can always progress to some more compact format if/when it comes to incorporating the block data into a format to be distributed with the main program.
EDIT: I have some experience with melding different languages, primarily Python and C++. If this is the path you decide to go down I can offer some advice if needed.
EDIT2: We can output any object from the program as text if there's a corresponding iostream function for the type we want to output. For the vector case we could easily implement one ourselves. Here's an example: https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx. Similarly you overload the >> operator to pipe a string into an object.
People have definitely made xml readers for Unity, so that's a viable option.
As for the txt route, if I were to write up a list of all the data the level editor needs to access, could you create an interface with which I could access them? Outputting text would be fine.
In response to your earlier question, yes, air Blocks get grouped together to.
I've set things up so that multi-block entities (eg. patterns, props) are situated according to their bottom-left corner. So in:
0 1 2
2 BsBsBs
1 BsPgBs
0 BsPgBs
the gate (Pg) is located at (1,0), and has dimensions of (1,2).
I can see this causing some problems when it comes to flipping patterns round. Would it be worthwhile flipping them in the level-editor, and exporting both versions, rather than trying to do it in-engine?
Obviously this increases the size of the pattern files, but it would make our lives easier, and might reduce loading times, which is probably more important.
I can do my best. I'm pretty busy these days, but I'll try to find the time. Could this just be a simple program that generates a text file with all the block data?
Yeah. Hang on, I'll write a list of the data I need to access.
I have to admit I'm a little confused. The block data is all in one of the source files. Could you not just look at that? Or is this more about having some consistent data format for use by both the game and the level editor?
Sorry, our discussion meandered a bit. The aim is to have some way for the level editor to access the block, prop, enemy and pickup data from the game files, so that it automatically stays up to date. As I see it, our options are:
Your call on which is the better solution. Not urgent, since Iain is still overhauling the engine, and the level editor can run on the data I've hard-coded into it for now.
Below is the list of data I need.
Blocks:
Props:
Enemies:
Pickups - Since there aren't any yet, we may as well ignore them for now. PickupList does exist though, so if you want to add them, this is what I'll (eventually) need.
Okay. Personally I think storing it independently of the game executable is going to make things easier in the long run, particularly since we could potentially edit the text file without having to recompile the game. If we eventually get to distribution, then we could invent some format where this is stored within the binary or some such.
Seems sound. The only problem I see is that we would no longer be able to refer to constants. This bit from BlockLists.cpp, for example.
false,
getConstant<float>("AIR_DRAG"),
0.0f,
Ah... yes good point. I'll have a think...
I guess the solution would be to move all game constants and settings into a text file. Not sure how you guys feel about this. It would make tweaking settings and so forth easier in the long run.
Would you need that though? Surely the editor wouldn't need to know the drag on the blocks.
The editor wouldn't, but the whole point is that the game uses the same files, and it would.
@mspraggs Honestly, that sounds like a good idea generally. Constants are another of those things that we fiddle with, so not re-compiling every time we adjust them would be nice.
@DivFord What do you mean by Y-index and allowed zones?
Allowed zones isn't in the data at the moment. I thought we should sort items by where they can be used. For example, lizards can go in the desert, but wouldn't go in whatever zones we add later on. Currently, there is only one zone, so this is just planning for the future.
Y-index is the y coordinate of the sprite sheet lookup. I believe that in:
BlockData("Air",
",,",
false,
false,
getConstant<float>("AIR_DRAG"),
0.0f,
0,
getBlockSpriteMap(0),
std::vector<EffectResult> { }),
The "0" before "getBlockSpriteMap(0)" is the y-index.
EDIT: No, wait. The 0 in the bracket of "getBlockSpriteMap(0)" is the y-index.
Hmm... according to the code that 0 actually refers to the layer that the block is rendered into, so basically the z-index.
Did you see my edit?
Ah yeah, no I didn't. Thanks!
EDIT: I think that's actually going to be a bit tricky to extract from the program without just going through the code and manually extracting that information. Perhaps I can automate with Python in some way.
Are we going to want metadata for enemies? I can't think of any cases where we would.
Which way they initially face?
Good point. That must apply to all enemies. Are we ever likely to need to add data for a specific type of enemy? Stuff like patrol paths, I suppose.
While patrol paths may be nice in one or two places, I don't think it'll be worth the effort of twisting everything around to fit them in. The closest thing I can think of would be to orbit around a given point in some way, but that would be more easily done as metadata I'd think.
Umm… I think we have a misunderstanding somewhere. With props, I'm setting it up so that a prop in the level file is written as a prop code, followed by the coordinates, then any metadata. So a crate might be Pc(0,0), and a gate Pg(0,0,1), with the third value being the key code. For this to work, the prop data file (the part Matt is working on) needs to be able to return a string listing the metadata categories for the prop. In the case of the crate, it would return "", because there is no metadata, while for the gate, it would return "Keycode". The point of this being that each metadata field in the editor will be labelled, so we won't have to dig around finding what value it's meant to be.
With the enemies, every enemy would need coordinates and facing. That is, facing isn't metadata in the same way that key codes are. Since every enemy has it, we don't need the data files to tell the editor to include it. So my question was: will any enemy ever require us to set values unique to that enemy type? If so, we need to include that information in the data files, and I need to add a metadata editing feature for enemies.
Hope that clarifies things. It's hard to explain without explaining the entire editor...
I think I understand.
Orbiting enemies are the closest thing to an enemy with metadata that I can think of (the extra data being the position of the point to orbit around, as well as the inner and outer radii possibly).
I've been thinking about how to make the puzzles more… puzzling.
One thing that's come up is the idea of setting up interactions between specific props. For example, the gate prop comes with an assumption that some external factor causes it to open. This could be a key pickup, a lever, a pressure plate, etc., but we need some way of linking the two (I say "need", though we could potentially have non-unique keys, and just require the player to work out the right order to open the gates in).
The caves we discussed for the mole gauntlet would also benefit from linking.
Also, the proposed Wind Turbine prop is presumably powering something, so being able to link it to that thing would be useful.
I'm stuck on how this could actually be accomplished. Within a level, I can see matching codes being a viable option, but patterns couldn't work that way. Unless we prefix the codes with an identifier for the pattern? Any ideas?