I have to say one thing that has always confounded me is how to plot the objects. I can develop greatly compact gfx and easily display with full mask, but what rules do you use to govern which objects are plotted first.
I know it is basically the ones on the current level and furthest back moving to the front but not sure how to code this for my 3:1 aspect (Which incidentally i am working on aswell as the graphics for space99).
It is tricky indeed. I have tested many methods, but it seems that the best working (for my case) was the stacked drawing method. That means for a tile you draw from bottom to top all the objects at that position. Then you just loop through rows and then trough columns drawing every pile of tiles at every position, while you update the screen (x,y) coordinates, which depends on your aspect ratio.
The problem is when rendering free objects. I have not found a render loop that does not have inconsistences with them, so I used a trick: I create an occlussion mask before drawing the free object, using the masks of all the objects that have already been drawn but should NOT be hidden behind it.
It is quite difficult to explain without some drawings and code (which I should clean up, as it has many additions that are up to NOISE also), but there are many links out there where all this is explained, except the occlussion mask thing (or kludge, to name it properly).
If you want more information, I can provide it, just ask
Problem with the latter is that the more you have, the slower the program. I thought about putting "ornaments" and pickable objects as tiled, and characters and other objects that may move or have different sizes, as free objects. This way you could, for example, push a chair or a table.
Whilst that would be cool, it is not a 'given' for any first game.
It is nearly working, anyway. But not necessary to use...
No what you said sounds cool, send me what you have, and i'll do my best to do the mapping aswell.
Alright, at your own risk!. I suppose the first thing to do is to desing a format for the map then... I will tell you how mine was to provide an example. I had a separate file with the code to load a room and the room data, so it can be changed easily.
You surely want more information, as the actual code, a test version of the program to see the rooms and move around and also how flats and objects are stored and the needed data to make them work in NOISE. I will provide these later on, to keep this post just large, instead of HUGE
Basically my walls had a standard structure composed by alternating flats of one (U) and two tiles (whose graphics fit and make up one wider flat) (A and B), so a wall is defined as: ABUABUAB. There are 8 flats because the extremes are not visible.
I also thought about only drawing the north and west walls and (maybe) drawing a line where the others shoudl be for aesthetic purposes.
There is a table that specifies possible values (ID codes) for flats of U-type and up to four different A and B-type combinations (A1..A4 and B1..B4), so flats like simple walls, computers, windows, etc could be mixed easily. Also it is possible to alter the table in runtime to use different ambients.
The structure of the map in memory was at follows, where rooms are not stored in any particular order, so they should be searched for:
Code: Select all
== HEADER ==
Byte 1: Room ID
Byte 2: Length (of this entry in bytes, just add to the pointer to get the next entry)
== ROOM DATA ==
Byte 3: Room type:
bits 0-2: ink colour
bits 3-6: room code (to be defined, maybe size or size+type)
bit 7: camera change
Byte 4: Wall combination West Wall
7,6: Reserved
5,4: two-tile flat 1 type (first AB group)
3,2: two-tile flat 2 type (second AB group)
1,0: two-tile flat 3 type (third AB group)
Byte 5: Wall combination North Wall
idem
Bit 7 of byte 3 is free (even if I had in mind a nice use that maybe I will implement later)
Bits 7 and 6 of bytes 4 and 5 are free and could signal special kind of walls, for example walls all made up of AB groups (windows are quite nice this way).
Doors are made up with empty tiles (ID code 0), as white detects when the sprite is about to leave the room to load the destination room automatically. If a wall is not visible, but it is there (for example south and east walls if no lines in the floor are drawn) the special code (0 OR INVERTED -binary 10000000 and #defined as TRANSP_WALL-) is used.
So the basic wall is made up with two graphics (both sides) with an empty tile in the middle and another one at layer 4 (heigth 4) for the top part of the wall. However it is quite difficult to make the character exactly tresspass the door, so it works much better to have two empty tiles in the middle, which is possible with this system. In fact it is possible to have doors of any width.
In addition doors may be in different places, just one in the middle or one or two lateral doors. Graphics were designed to make this easy with combinations just as:
ABD__DAB or D__DD__D or D__DUABU or UABUD__D ...
Doors of different sizes may be mixed, i.e.: D_DABD_D or UD____DU or even D______D.
A possible way of coding this is:
Code: Select all
Bytes 6-7: Doors
Byte 7, bits 0-2: Doors East Wall
Byte 7, bits 3-5: Doors North Wall
Byte 7, bits 6,7: Reserved (door type?)
Byte 8, bits 0-2: Doors West Wall
Byte 8, bits 3-5: Doors South Wall
Byte 8, bits 6,7: Reserved (door type?)
Door types:
Possible combinations are:
000 : No doors
001 : One door at the left of the wall
010 : One door in the centre of the wall
100 : One door at the right of the wall
101 : Two doors in both sides of the wall
Special combinations are:
011 : Big opening in the wall (not really a door, more an arch)
110 : ...
111 : Set all wall to id=0, so there is no wall
Now onto other objects. I separated objects that might be placed on walls (like a torch or a label) from objects that appear in the inner 8x8 grid. That is interesting as it is expected that a room has more of the latter and their coordinates can be compressed:
Code: Select all
;== BKG WALL OBJECTS ==
;Byte 8: Num Objects
;Byte 8+1: obj ID
;Byte 8+2: position
; bits 7,6: wall West wall=00, North wall=10, East wall=01, South wall=11
; bits 5-2: position in wall (0-9)
; bits 1,0: heigth layer
;...
;
;Obj ID (type) (bit 7 set indicates mirror graphic, 6 sets SPECIAL flag)
Remember that WHITE will report collisions with every object with the SPECIAL flag set (except if ID is 0).
Code: Select all
;== BKG INNER OBJECTS ==
;Byte N: Num objects
;Byte N+1: object ID
;Byte N+2: repetitions
;Byte N+3: position (bits 7-5 pos i: bits 4-2 pos j: bits 1 and 0 pos k)
;...
;Byte M-1: position for each repetition
;...
Note that this does not permit more than 4 heigths (4 layers).
Finally free objects, with fine coordinates that are not bound to tiles...
Code: Select all
;== FREE OBJECTS ==
;Byte M: Num objects
;Byte M+1: ID
;Byte M+2: pos i
;Byte M+3: pos j
;Byte M+4: pos k
;Byte M+5: size byte
;Byte M+6: automov byte (byte that controls automatic movement)
;Byte M+7: direction
These are not yet implemented in my current version. In fact there are two additional arrays which hold free objects (normally characters) that might move from one room to another and tiled objects that also could be carried from one room to another. Those are set up after loading all the room data.
With this structure the room you start up in the demo (a bit modified, as I was testing thigs, but walls and overall layout is similar) is defined as:
Code: Select all
;; Room ID 0
.byte 0
.byte 27
.byte 6
.byte 0
.byte 21
.byte 0
.byte 63
.byte 0
.byte 4
.byte BRICK
.byte 6
.byte 184
.byte 176
.byte 32
.byte 64
.byte 96
.byte 128
.byte TABLE
.byte 1
.byte 180
.byte (LAMP|INVERT)
.byte 1
.byte 181
.byte (COMMPOST|SPECIAL)
.byte 1
.byte 36
.byte 0
;; End of room ID 0
where COMMPOST, LAMP, BRICK and SPECIAL and INVERT (I know it should have been MIRROR) are #defined.
I have a small C program that accepts a text file with an easier definition of a room and "compiles" it. This room is:
Code: Select all
ROOM 0 6 0 0
0 0 0
1 1 1
0 0 0
0 0 0
1 1 1
1 1 1
WOBJS
END_WOBJS
OBJS
BRICK 5 6 0
BRICK 5 4 0
TABLE 5 5 0
(LAMP|INVERT) 5 5 1
(COMMPOST|SPECIAL) 1 1 0
BRICK 1 0 0
BRICK 2 0 0
BRICK 3 0 0
BRICK 4 0 0
END_OBJS
When a new room has to be loaded, an asm function is called:
Setting a tile is calling NOISE function:
Do I forget anything?
Oh, and we have to talk about doors. They seem innocent and easy, but are not indeeed!
Well i don't think it is neccesary to make the hero walk through them, just detect the border where they exist. It should be easier to animate sliding doors, doors that open inwards would mean you'd have to worry about giving space infront of the door (to allow it to open).
I have tested that and it does not look very good. However there are little options. I want to think more about the problem anyway. One possibility is making it automatic: if the player hits the door, the engine takes control, moves your char to center it in the door and makes it walk through it. When exitting, it is something similar: just make it move a couple of steps. Problem here is what happens when there is something (or somebody) there that prevents this move...
As for the actual door animation, you might consider capturing all objects (Including the hero) infront and dumping them as some sort of permanent mask for the animation to be based on?
I already have something ready for that.. There is a function that changes the content of a tile and redraws, so don't worry about that.
So don't worry about all my suggestions, some may be good but the most important thing will be to get a game rolling
The steps of 8 pixels sounds fine, and i'll work on some more free objects tomorrow night.
Your suggestions are excellent ideas and points, and very refreshing. I have to admit I was nearly run out of new ideas at this point, so I kept on asking a friend of mine which gave me very interesting points of view and solutions for some problems.
Regards,