Ideas for a new top-down adventure, need advice

BlitzMax Forums/BlitzMax Beginners Area/Ideas for a new top-down adventure, need advice

abelian_grape(Posted 2010) [#1]
Recently I just finished a simple, side-scrolling, arcade-style game as a jumping-off point for my new found BlitzMax obsession. I went at it thinking that I would code everything on my own and fix all of my own problems (of course I occasionally came here for help when I was just banging my head against a wall), and that resulted in some really nasty code overall, but gave me some insight into what to do and what not to do in future projects.

I am ready to start my new project, and I'd like to do an adventure game with a bird's eye view, much like the mechanics of the NES Legend of Zelda. I have sat and thought long about how to go about creating a framework for the game, and I have a couple ideas that I would like people to comment on. There is one route, which is using a map editor (like Universal Map Editor) to quickly create tile maps for my 'world'.

The other route, which to me would be much more fulfilling, is programming everything entirely by myself, which I think would involve creating my own tileset, and then loading tiles to specific locations on the screen when on a particular screen. I could store the information for the screen maybe in a Type so we could treat each screen as an individual place (like Legend of Zelda, in which you can't just freely scroll into the next screen, but rather load an entirely new screen when you reach the edge ). Then as a field in the main character Type, use the field to store a number that determines what screen in the world should be loaded. The problem I see is that using a high number of tiles on a screen would be complicated to store and then load and draw onto the screen methodically so it is all displayed quickly.

My solution to this was to use solid colors for the backgrounds (so this could maybe represent the ground, and gets rid of having to have a ground tile) and then create types for specific objects that appear in tiles (bushes, staircases, rocks, etc.). Then I can use collision detection in each individual type of objects and give them their own characteristics (burning a bush eliminates it, where as bombing a rock destroys it, etc.). This would cut down the number of tiles by only loading in tiles that the character directly interacts with and doesn't just "walk over." Then, in the screen Type, I can define a Method that checks what screen the character is on, which in turn would call a function that loads all of the tiles and draws them that needs to be on that particular screen (creates the objects, loads them to certain coordinates, draws them).

I would like to know from all of you what makes more sense, what you think is viable, and if you have any suggestions. I want to reiterate that doing it myself would be much more fulfilling because using a map editor would just feel like cheating. In my first game, none of the graphics I used were created by myself, but instead borrowed from various video games that were available on sprite websites. As such I can't legally sell or exchange my game for money, which is fine since my intent was to make it freeware. While I intend to make my next game free, I would also want to do draw everything myself, and I am TERRIBLE at drawing. I will release my first game here on the forums, along with the source code, once my friend finishes the music and sound effects for the game.


abelian_grape(Posted 2010) [#2]
Really, if you can, try and keep in mind the Legend of Zelda game as you go through my description. Here is a video to give you an idea of what the gameplay is like, what I mean by having each screen be a certain place, etc.


Legend of Zelda gameplay


Czar Flavius(Posted 2010) [#3]
call a function that loads all of the tiles and draws them that needs to be on that particular screen (creates the objects, loads them to certain coordinates, draws them).
Creating objects is slow. It will be better to store your map as a big grid ie 2d array, and work out which cells of the grid are visible.

The grid would be made of TCell which has two fields, TFloor and TFeature (for example). The TFloor contains the colour (or tile) of the background, and whether it is passible. TFeature is an Abstract type for all your rocks and bushes. When you draw a TCell, draw the TFloor first and then TFeature.

Extend from TFeature your bushes and rocks, but call them things like TPassableFeature or TDestroyable Feature. You might have pillars which work like rocks too etc. It's a good idea to keep type names generic rather than tie them down to a specific physical item.

When checking if the character can enter a particular cell, you'll need to check both the TFloor and TFeature for passability. So both would have a passable:Int() method which returns either true or false. And so on.


abelian_grape(Posted 2010) [#4]
That makes sense, and I hadn't thought of that. This is just the type of feedback I need :)

I think the big hurdle for me here will be creating the big 2D array and how to decide what is visible. I had not really considered doing it that way, and I'm not sure how to implement that.

What I'm imagining is loading this huge 2D array into memory, and then only drawing certain portions of it if it is say, in the 1024x768 window of view. To me that sounds like it would be a drain on system resources, but I also don't know much about what is happening there. Could you explain the idea of this array in a little more detail? I just haven't worked a lot with arrays. My current understanding of them is that they are kind of like a matrix (if we're talking 2D arrays) in which each entry is indexed by its row and column. Other than that...not sure how to use them in a programming environment. I'm going for a masters in Math so I at least understand the abstract ideas, its the programming implementation that is new to me.


shinkiro1(Posted 2010) [#5]
I think you should use a map-editor because it will reduce the time
to create maps significantly. It's not cheating, just funnier and not so time consuming. Mathematicians are also not cheating when using a calculator, are they ^^

Have you thought about integrating a scripting language? because hardcoding such a game isn't a good idea.

EDIT:
An array is like a variable, but it can hold more than one value at a time.
For instance:
Local array:Int[] = [23,56,32,53,22,67]

The above code initializes an array with 6 values. Then you get the value by it's index:
Print array[2] 'prints out 32
Print array[0] 'prints out 23

An arrays index is starting at 0 and not one, you have to remember that.
So you can store all your map data in an array
I think there are tutorials which explain them further and show there use (like in a tilemap).


Czar Flavius(Posted 2010) [#6]
Here's a bunch of code in no particular order. I haven't tested it but hopefully will give you ideas.




abelian_grape(Posted 2010) [#7]
@Espada: I appreciate the advice but to me it just wouldn't feel like I did it myself because the majority of the work was done for me. Plus, I do the type of maths that doesn't require a calculator ;) (I'm into algebraic topology). Plus, I know nothing about scripting :[

@Czar Flavius: I appreciate the code, and for the most part I understand what you're trying to achieve, but I honestly cannot follow the idea of turning cells into pixels...I'm not sure what that accomplishes. What especially threw me off was that the pixel_to_cell function wasn't utilized. Could you expand on that more?


Czar Flavius(Posted 2010) [#8]
It's so each cell knows where to draw itself on the screen. If you have a cell at row 6, column 5, where does that draw itself on the screen? cell_to_pixel gives you the position on the screen to join it. I noticed a mistake - I forgot to take account of the current scroll position, so I'll update that.

Pixel to cell is useful is the player clicks on a tile, perhaps to aim a weapon? You take the pixel clicked by the mouse and -bam- you got the row and column (x and y) of the cell he clicked on!


abelian_grape(Posted 2010) [#9]
Ah, alright. It's beginning to make more sense. So just to be sure we're on the same page:

(For this I'm referring to cells as tiles)

There should be a black border that are 32 pixels wide around the perimeter of the screen. This black space could be used for displaying inventory, etc., but primarily acts as a buffer zone for the tiles.

The screen resolution is 1024x768, but what will actually be composed of tiles is a rectangle in the middle of the screen, and so the visible map in the middle of the screen will be 960 x 704 pixels, and with tiles that are 64 x 64 pixels, the visible map will then be of dimensions 15 tiles-by-11 tiles. The entire map, however, is 50 tiles-by-50 tiles (as it stands in your code).

Now the bit of code in the main loop of the game runs through the 2D map array, tile by tile, updates each tile, and if it is visible, draws it. The visibility function determines this by taking the x,y position in the map array, and calls the tile_to_pixel functions, which determine the x and y coordinates of the tile depending on how much we have moved in the x, y directions causing the tiles to be scrolled.

In this sense, if we had a character move to the top of the screen and hit the top of the screen and continue to try to move, we would then have to increment the scroll_y variable so the map moves down, giving the illusion the character is moving "north." Likewise, if we had the character press against the left side of the screen, we have to increment scroll_x to move the map right, giving the illusion the character is moving "west." Additionally, this scrolling is happening in real time, so as long as I'm pressing against a side of the screen, the map is going to continue to be scrolled.

Assuming I have all of this correct, my main concerns are:

- I am using AutoMidHandle, so I would imagine that your code is assuming the image handles for the tiles are in the upper left corners of each tile? Or will it still work?

- I am still unsure as to how I am loading objects or colors into each tile. Do I load each individual tile upon loading the game? This method of displaying the map seems to lend itself to pre-loading everything and then only drawing it if it falls within our "window of view." Is this the only way to go about it, or is there a way to dynamically create and destroy tiles depending on what we can see?


Czar Flavius(Posted 2010) [#10]
Yes that's right! You've got it! :D

The numbers I used are just for example purposes. You can change the size of the map etc

I looked at the video you posted and it seems that the game is a collection of smaller maps (each the size of the screen) and you move between them and no action takes place in maps you cannot yet see.

The code I posted is more for a generic tile-based game, where you move fluidly around a large map rather than preset increments and action is updated everywhere.

Which of the two do you most want to do? If you want it more like the game you posted, some tweaking will be required.

I'm not sure how mid handle will affect the outcome. I think you can selectively enable and disable it for individual images.

For the way I posted, you load the entire map. Let's say each cell uses 1kb of memory each (and this is a very HIGH estimate) then a 100x100 map will use up 10MB or memory. Even a very poor computer these days will have 128MB or memory or more. You shouldn't worry in the slightest about memory usage. (0.2kb would be a more reasonable estimate but it depends entirely what you store on each tile) Creating and destroying tiles requires reallocation of memory. It is faster to allocate/reserve the maximum amount of memory required (so long as it is reasonable and it is in this case) and leave 'idle' what is not required (yet...) In a game, speed is important.


Have an array of floor types and give each a unique id starting from 0. So if you have floors:TFloor[10] (assuming 10 floor types) then grass could have id 0 and so would be stored at floors[0], water 1, wall 2 and so on. When you create a new map, set each tile to grass. Then in the map editor press a number to select a tile type eg 1 for water, and then click on a tile (using pixel_to_tile) to change its floor to that type. This way you can directly alter the map!

to change a tile's floor at x, y map[x, y].floor = floors[id_num]

you can store the colour values r g b (or image) to draw in floor eg
DrawImage floor.image, cell_to_pixel_x(x), cell_to_pixel_y(y)
in the draw method now

Have a save function which spews out the data of your map into a text file. You can write the id numbers of the floors in sequence into the text file. Then load from the text file in the same order and put the id numbers back into the tile map. The first 2 numbers of the save file should be the size of the map, so you can create the map array to the right size. Saving positions of characters, items and stats is a little bit trickier but worry about it later when you have mastered this.

At the start of your game you should pre-load the TYPES of floors, but not the whole map. So set up the floors array and put in grass into [0] and so on and load the grass image file if you are using one. Then ask the user to pick a map (http://en.wikibooks.org/wiki/BlitzMax/Modules/System/System#RequestFile that's a very quick and handy way) and load the map array and place the tiles in. Then your game is ready.

OK that's a lot to take in but you look like a quick learner and once you've got your head around it you're well on the way to a comprehensive tile engine!


Czar Flavius(Posted 2010) [#11]
Here's some old code I dug up which might interest you. I had an obsession with semi-colons back then, please ignore :D




abelian_grape(Posted 2010) [#12]
So it sounds like what you had in mind was for me to create my own map editing utility so that I could crank out new maps whenever I wanted, and therefore it would be easier in later games to make an entirely new game with a different map. Really what I had in mind was having a "world" where the maps didn't change...more of a situation where I just make the entire map and that is just how it is for the game. I do like the possibility of having a map editing utility, so I will create a separate project just for that so I could implement it in a future game, but for now I'm trying to keep things relatively simple so I can tackle one thing at a time. I suppose that still begs the question of how to load an entire map's worth of tiles and information even when the map is pre-determined. I still don't quite understand what to do there.


abelian_grape(Posted 2010) [#13]
Upon further contemplation it would seem that creating a text file with the 2D map array information stored in it would be the best way to go about this. At the start-up of the game, I could have the game read that file into the array, thus initializing the map. Ooh boy, you're right, creating a save game in which it recalls all of the player information will be quite interesting.


abelian_grape(Posted 2010) [#14]
So I will post my code below...currently I am getting an "Unhandled Exception:Attempt to access field or method of Null object" error at the line that says "map[x,y].r = 255". I really can't figure out what is going wrong...basically I'm trying to draw a bunch of tiles all of one color so I know this is working. I feel really bad that I can't debug this on my own, so any help I can get is appreciated :[

Thanks Czar for all the help so far! You've been great!




Jesse(Posted 2010) [#15]
you need to create an instance of the TTile before you can use it:
map[x,y] = new TTile



Czar Flavius(Posted 2010) [#16]
Yes that's right. You've put the floors and features as fields but they should be globals. You want to store the "library" of floor types only once, and each tile simply points to the one floor it contains.

It's handy to give each type a Create function that sets up the object with its starting values. It's also a good idea to put updating and drawing code into function to keep code tidy. The draw_list has been made global so it can be remembererd from the update function to the draw function.

I've got to dash so can't explain any further but here is some idea code for you to look at.




abelian_grape(Posted 2010) [#17]
With a bit of tweaking of your code to fit to what I have so far (i.e., in the Load() function, changing floor_num to floor_num - 1 so as not to index beyond the array length), and integrating your ideas into my code, I have successfully made it display random tiles! I think from here I should be able to integrate pre-defined maps. Thank you so much for your time and help, Czar Flavius. It truly is appreciated. I will post periodic updates in my worklog (linked in my signature) if you're curious to see how the project is progressing :) I'm not sure what is considered protocol, but should I credit you in the source code? You certainly gave me a significant amount of help and workable code to get me started on this project.


Czar Flavius(Posted 2010) [#18]
Thank you! You're welcome. No I don't need to be credited but thanks for asking :) A good deed is its own reward!


Czar Flavius(Posted 2010) [#19]
I've had a look at your worklog code and it's coming along nicely, but there is a slight issue with the code I gave you.

First of all, add this into your main game loop

	If KeyDown(KEY_LEFT) Then xScroll :- 5
	If KeyDown(KEY_RIGHT) Then xScroll :+ 5
	If KeyDown(KEY_UP) Then yScroll :- 5 
	If KeyDown(KEY_DOWN) Then yScroll :+ 5
It will allow you to scroll around the map using the arrow keys, just for testing purposes for the time being. You'll notice the edge tiles display an undesirable effect when scrolling.

To fix this we need to - draw an extra layer of tiles on the screen than that which would be normally visable, and then place a black border around the screen that is the size of the offset. This will push the graphical effect off the main viewing area and then hide it under a black border, which you can place your interface graphics over later.

The amended code looks like this:


There are changes in the main game loop, the drawtiles function and the visible function. I had to change the delay to a lower number otherwise scrolling was not smooth. I also commented out the titlescreen as I don't have that graphic on my computer :D

One more thing, it is usually not standard practice to specify a variables type after you have declared it. So when you use Local or Global, put :Int but when you simply use the variable later on, it's not usual to put :Int after it.


abelian_grape(Posted 2010) [#20]
Ah, yes. That first bit of code I actually did add in, except under the main character's input because I drew up the animations for the main character yesterday and implemented them, so he was able to walk around the map.

I just compiled and ran the amended code, and I must say it is much smoother. I did notice that delay in displaying a tile when I had implemented the main character that could walk around and scroll in the screen by controlling him, but I thought it was just a limitation I would have to live with. I do like this smoother scrolling though, it's great. It will also be really useful when I go to make the map generator.

On your last comment, I'm glad you said something because this is my first time using SuperStrict (I didn't use SuperStrict or even Strict on my last project...and the code didn't look good), so I didn't really know what it expected. I more or less just tried to compile what I thought looked good, and then waited for the compiler to yell at me so I could fix any identifier problems. I see now that in using the variables you don't need the identifiers...they're just expected at declarations of variables, methods, and functions, as well as the parameters to methods and functions. Thanks for the tip! And thanks again for looking back at that code and suggesting those fixes, it looks much better now :)

-muffins


abelian_grape(Posted 2010) [#21]
As a matter of fact, I think it might behoove me to just work on the map editor now, finish it, and that way I can quickly throw together some levels.

-muffins