|
Now just two functions left! Saving and loading the maps from a file! I'm actually going to be a !@#$% here and not explain these functions to you but just give you the code! The reason being that these functions use a lot of somewhat complex concepts that are irrelivent to this walkthrough, however if you can get to grips with the rest of this article then you should be able to understand how these functions work. These functions are also completely commented in the source code download which may help you to understand them! public void Load(string url) { if (File.Exists(url) == false) return; FileStream stream = new FileStream(url, FileMode.Open, FileAccess.Read); StreamReader reader = new StreamReader(stream);
if (reader.ReadLine().Trim() != "ZELDA MAP") return;
while (reader.EndOfStream == false) { string line = reader.ReadLine().Trim();
if (line != "") { string[] parameters = line.Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);
switch (parameters[0].ToUpper()) { case "WIDTH": MapWidth = int.Parse(parameters[1]); TileData = new Tile[LayerCount, MapWidth, MapHeight]; break;
case "HEIGHT": MapHeight = int.Parse(parameters[1]); TileData = new Tile[LayerCount, MapWidth, MapHeight]; break;
case "LAYERS": LayerCount = int.Parse(parameters[1]); TileData = new Tile[LayerCount, MapWidth, MapHeight]; break;
case "TILESET":
string originalPath = Environment.CurrentDirectory; Environment.CurrentDirectory = Path.GetDirectoryName(url); Tileset = GraphicsManager.LoadImage(parameters[5], int.Parse(parameters[1]), int.Parse(parameters[2]), int.Parse(parameters[3]), int.Parse(parameters[4])); Environment.CurrentDirectory = originalPath;
break;
case "LAYER":
int index = int.Parse(parameters[1]); for (int row = 0; row < MapHeight; row++) { string[] frames = reader.ReadLine().Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int x = 0; x < MapWidth; x++) { if (frames[x].Trim().EndsWith("+")) { TileData[index, x, row].Solid = true; frames[x] = frames[x].Substring(0, frames[x].Length - 1); } else TileData[index, x, row].Solid = false; TileData[index,x,row].Frame = int.Parse(frames[x].Trim()); } }
break; }
} } reader.Close(); }
public void Save(string url) { if (File.Exists(url) == false) return;
FileStream stream = new FileStream(url, FileMode.Create, FileAccess.Write); StreamWriter writer = new StreamWriter(stream);
writer.WriteLine("ZELDA LINE"); writer.WriteLine("WIDTH " + MapWidth); writer.WriteLine("HEIGHT " + MapHeight); writer.WriteLine("LAYERS " + LayerCount);
for (int i = 0; i < LayerCount; i++) { writer.WriteLine("LAYER " + i);
for (int y = 0; y < MapHeight; y++) { string rowData = "\t"; for (int x = 0; x < MapWidth; x++) { rowData += TileData[i, x, y].Frame; if (TileData[i, x, y].Solid == true) rowData += "+"; if (x < MapWidth - 1) rowData += ","; } writer.WriteLine(rowData); } } writer.Close(); }
However I am going to explain the format of the map files that they load and save. Here is an example file that can be saved and loaded by the above functions. ZELDA MAP TILESET 16 16 1 1 tileset.png LAYERS 2 WIDTH 16 HEIGHT 16 LAYER 0 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 LAYER 1 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,2+,2+,0,2+,2+,0,0,0,2+,2+,2+,2+,0,0 0,0,2+,2+,0,2+,2+,0,0,0,2+,2+,2+,2+,0,0 0,0,2+,2+,0,2+,2+,0,0,0,0,2+,2+,0,0,0 0,0,2+,2+,0,2+,2+,0,0,0,0,2+,2+,0,0,0 0,0,2+,2+,2+,2+,2+,0,0,0,0,2+,2+,0,0,0 0,0,2+,2+,2+,2+,2+,0,0,0,0,2+,2+,0,0,0 0,0,2+,2+,0,2+,2+,0,0,0,0,2+,2+,0,0,0 0,0,2+,2+,0,2+,2+,0,0,0,0,2+,2+,0,0,0 0,0,2+,2+,0,2+,2+,0,0,0,2+,2+,2+,2+,0,0 0,0,2+,2+,0,2+,2+,0,0,0,2+,2+,2+,2+,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Right so lets explain the file format shall we? Well the files always start with a line containing nothng but "ZELDA MAP" in capitals. This must never be modified and must be in all map files. This basically just tells the engine that this is a zelda map file and can be loaded by the engine, this is used to make sur ewe don't try loading invalid or corrupt files. The next line defines the tileset image that should be loaded and used to render the given map, its syntax is very simple, it always starts with the word TILESET and is followed by five parameters, the first two are the size of each tile contained in the image, the next two are the spacing between each tile and the last is the path to the actual image. The 3 lines following that define the dimensions of the map, specifically its width, height and how many layers it contains. The next lines are the most complicated. They define the properties of every tile on a given layer. A layer block always starts with the word LAYER followed by the layer index, after this line there is a line for each row in the map, each line consists of the frames used to render each tile in this row, seperated by commas, if a plus sign follows the frame index then the tile is considered to be solid. There must be enough layer blocks for each layer in the map! And thats it really! I designed the format to be as simple as possible for you to modify until we get round to writing a map editor. Try making a few and loading them (there are some tilesets and sample files in the source code download at the bottom)! Well then, thats the entire map class done! The only problem is we are not putting it to use. So lets modify our Zelda.cs file shall we so that we can actually use it! First thing we need to do is add a variable to store the current map of our game! So add this to the top of our zelda class; public static Map CurrentMap = null;
Now how about we create a map and load it from a file? So lets place the following code after the CreateWindow function shall we? CurrentMap = new Map(); CurrentMap.Load("Assets\\start.map");
So what does this do then? Well the first line create a new map object, the second then calls the Load function of the map so that it loads the map data from the Assets\Start.map file (an example map is contained in the source code download if you don't want to make your own)! That was easy wasn't it? unfortunatly we haven't added any code yet to render or update the map so all we are going to see is a blank screen when we start the game. So to rectify this lets add some code. Lets replace the following code; foreach (Actor actor in Actor.ActorList) { actor.Update(); }
With this new code. The reason we are replacing it is because our actors are now updated in our map class and as such we no longer need this code in the main loop; CurrentMap.Update();
*Whistles* Almost done. Now just replace the actor rendering code from the last tutorial with this new code; foreach (Actor actor in Actor.ActorList) { actor.Render(); }
to CurrentMap.Render();
Now just need to do one more quick thing before we can run, lets change the window resolution to 256x224 (the SNES resolution of LTTP) so that it better fits the map. You can do this simply by changing the CreateWindow function call so that it reads; GraphicsManager.CreateWindow("Zelda Game", 256,224, false);
Wow!!!! We finally have a working map class that is correctly rendered and updated on the screen! Pretty dam cool isn't it? However its not very interactive is it? So lets add some code to move the camera around the map using the arrow keys. Add the following code before the CurrentMap.Update() line; if (InputManager.KeyDown(System.Windows.Forms.Keys.Left)) CurrentMap.CameraPosition.X -= 1; if (InputManager.KeyDown(System.Windows.Forms.Keys.Right)) CurrentMap.CameraPosition.X += 1; if (InputManager.KeyDown(System.Windows.Forms.Keys.Up)) CurrentMap.CameraPosition.Y -= 1; if (InputManager.KeyDown(System.Windows.Forms.Keys.Down)) CurrentMap.CameraPosition.Y += 1;
This code is pretty simple, it just increased or decreases the camera position if any of the given arrow keys are currently pressed down. And how about our collision testing code? Lets check that works shall we! Add the following code just before we render the FPS display; if (CurrentMap.RectangleCollide(new Rectangle(InputManager.MouseX() + (int)CurrentMap.CameraPosition.X, InputManager.MouseY() + (int)CurrentMap.CameraPosition.Y, 1, 1))) GraphicsManager.RenderText("Mouse is over solid tile!", 10, 25, 0, null);
This will just check if the current position of the mouse on the map is over a solid tile, if it is we render a notification to the screen! Now RUN!! Kick ass isn't it? You now have a pretty neat map framework working, took a long article to do it though didn't it? And it was only a couple of hundred lines of code. Hope you enjoy that tutorial, as with all the other tutorials fully commented source code is available to download below. Make sure to check out the next article! Where we are going to get into some real zelda programming and add link to the map! Completed Source Code: http://www.zfgc.com/infinitus/tutorials/004/complete.rar
« Last Edit: March 30, 2008, 12:57:45 pm by Infinitus »
Logged
|