10 Replies - 1325 Views - Last Post: 27 January 2010 - 04:16 PM Rate Topic: -----

#1 Zapi  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 27-January 10

Requesting "advanced" StreamReader help

Post icon  Posted 27 January 2010 - 12:29 PM

Hi!
I'm currently working on a 2D game project, as of now I can load a map from a textfile without a problem. Though the items read is listed line by line, and it is initialized by the map name so to say and how many tiles X and Y, like this:

Plain
8
8
S
1
G
0
G
0
G
0


And so on for 64 tiles, where G and S is for a grass and stone texture, and the 0 and 1 values below represents if it's not blocked or blocked.

Code that reads in tile informations (Not Plain, 8, 8):
public void Read(object o)
		{
			StreamReader reader = (StreamReader)o;

			string tiles = reader.ReadLine();
			string blocked = reader.ReadLine();

			if (tiles.Equals("G"))
			{
				tileTexture = TextureManager.RequestTexture("G");
			}

			else if (tiles.Equals("S"))
			{
				tileTexture = TextureManager.RequestTexture("S");
			}

			if (blocked.Equals("1"))
			{
				Blocking = true;
			}
			else
				Blocking = false;
		}


The map txt file is loaded through Alt + L
			if (state[DXInput.Key.LeftAlt] && state[DXInput.Key.L])
			{
				map.Read(@"Map/MapFile.txt");
			}

With this layout of the text file and code I have absolutely no problem with loading the map into the game using ReadLine.

Now to the catch, I want the text file to look as following (alt. "," instead of spaces between each S0/1, G0/1):

Plain
8
8
S0 G0 G0 G0 G0 G0 G0 S0
G0 G0 G0 G0 G0 G0 G0 G0
G0 G0 G0 G0 G0 G0 G0 G0
G0 G0 S1 G0 G0 G0 G0 G0
G0 G0 G0 G0 G0 G0 S1 G0
G0 G0 G0 G0 G0 G0 G0 G0
G0 G0 G0 G0 G0 G0 G0 G0
S0 G0 G0 G0 G0 G0 G0 S0


I want to read it all separately like it does now, e.g. It should read one character at a time as it does now though with one line at a time.
This makes the text files easier to read and edit by hand.

Note: I don't want any code that control how many columns or lines should be in the text file or so, just how to load the map similar to how I currently am doing it but instead from a text file like that latest shown example.
Also if needed, how do I tell the streamreader to start from line 4 in the text file? As that is where the "tile" code starts.

Code snippets of how I should approach this is very much appreciated.

-Pierre

Is This A Good Question/Topic? 0
  • +

Replies To: Requesting "advanced" StreamReader help

#2 Sergio Tapia  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1252
  • View blog
  • Posts: 4,168
  • Joined: 27-January 10

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 12:46 PM

Using this code, you'll be able to have your users create a map file as you want, in a grid. (Note, that they will be able to create it in whatever shape they like)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // Create an instance of StreamReader to read from a file.
                // The using statement also closes the StreamReader.
                using (StreamReader sr = new StreamReader("TestFile.txt"))
                {
                    string line;
                    // Read and display lines from the file until the end of 
                    // the file is reached.
                    while ((line = sr.ReadLine()) != null)
                    {
                        //Split the read line at every space, and place them into
                        //a string[] array.
                        string[] tiles = line.Split(' ');
                        //Iterate through the tile collections (strings)
                        foreach (string tile in tiles)
                        {
                            switch (tile)
                            {
                                case "G":
                                    tileTexture = TextureManager.RequestTexture("G");
                                    break;
                                case "S":
                                    tileTexture = TextureManager.RequestTexture("S");
                                    break;
                                case "1":
                                    Blocking = true;
                                    break;
                                case "0":
                                    Blocking = false;
                                    break;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                // Let the user know what went wrong.
                Console.WriteLine("The file could not be read:");
                Console.WriteLine(e.Message);
            }
        }
    }
}


Now, to read from a specific line in a .txt file, do something like this:

StreamReader reader = new StreamReader("file.ini");
string line = "";

while ((line = reader.ReadLine()) != null)
{
    if (!line.StartsWith("Plain") || !line.StartsWith("8"))
    {
        string[] info = line.Split(' ');
    }
}

reader.Close();


Was This Post Helpful? 0
  • +
  • -

#3 scalt  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 63
  • View blog
  • Posts: 342
  • Joined: 22-November 07

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 12:56 PM

You could approach this a couple of ways. I'm not sure why you can't just read the line, then split it by your delimiter (' ' or ',')

ie
string[] tiles = reader.ReadLine().Split(' ');
foreach(string tile in tiles)
{
    if(tiles[0] == 'S')
        ;//do stuff
    else if(tiles[0] == 'G')
        ;//do stuff

    if(tiles[1] == '0')
        ;//do stuff
    else
        ;//do stuff
}




However, if you realy want to just read it character by character you have two options. One is to just read the whole line in, then run through the string character by character (you can get to a character in a string by treating the string like an array of characters - ie mystring[0] gives you the first character). THe other option is to use the StreamReader.Read method, as such:
char[] mychars = new char[1];

while(!reader.EndOfStream)
{
    reader.Read(mychars, 0, 1);
    //do stuff with mychars[0] here - ie if(mychars[0] == 'G')
}



As shown above, to use 'Read' you simply give it an array that it will write some characters to, then tell it where to start in the array (generally always 0), then tell it how many characters to read. You can read any number of characters you want, not just one, but make sure you initialise your array to at least that many, otherwise you will get errors.


Hope this helps

This post has been edited by scalt: 27 January 2010 - 12:57 PM

Was This Post Helpful? 0
  • +
  • -

#4 MentalFloss  Icon User is offline

  • "ADDICTED"[2:5]
  • member icon

Reputation: 526
  • View blog
  • Posts: 1,397
  • Joined: 02-September 09

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 12:56 PM

I just want to add that with stapia.gutierrez's code, you definitely want to have a default condition in your switch. There's no telling what other characters may be in your mapping file either by user interaction or expansion of map definition or some other opportunity.

switch (tile)
							{
								case "G":
									tileTexture = TextureManager.RequestTexture("G");
									break;
								case "S":
									tileTexture = TextureManager.RequestTexture("S");
									break;
								case "1":
									Blocking = true;
									break;
								case "0":
									Blocking = false;
									break;

								default: 
									 /* handle your "unknown" characters that might come up. */
							}



Was This Post Helpful? 0
  • +
  • -

#5 Sergio Tapia  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1252
  • View blog
  • Posts: 4,168
  • Joined: 27-January 10

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 12:58 PM

I left the default out on purpose because he is using this to create a map texture from user created .txt file.

If none of the characters are recognized, he shouldn't even try to generate it. Maybe he should generate an error message though: 'The map contains unrecognized characters.'

Edit: I changed my mind. You should leave in the 'default' case and handle your error there.

This post has been edited by stapia.gutierrez: 27 January 2010 - 01:26 PM

Was This Post Helpful? 0
  • +
  • -

#6 Zapi  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 27-January 10

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 02:26 PM

I manage to run the code without getting any errors, though instead of having my tiles placed out when loading my map I just get a white screen instead and can freely walk around with my character in that area. But the tiles are gone.

I tried both stapia.gutierrez codes and scalts, modified them also but could not get the tiles loaded. Except for one piece of code:
char[] mychars = new char[1];

while(!reader.EndOfStream)
{
	reader.Read(mychars, 0, 1);
	//do stuff with mychars[0] here - ie if(mychars[0] == 'G')
}



Though with all the functions in, I could load the first tile in the window. The rest became white.

EDIT: That code looks as following, it only reads in the first value of the text files code, I guess I missed some really big thingy here :P
		public void Read(object o)
		{
			StreamReader reader = (StreamReader)o;

			char[] mychars = new char[1];

			while (!reader.EndOfStream)
			{
				reader.Read(mychars, 0, 1);
				if (mychars[0] == 'G')
				{
					tileTexture = TextureManager.RequestTexture("G");
				}
				if (mychars[0] == 'S')
				{
					tileTexture = TextureManager.RequestTexture("S");
				}
				if (mychars[0] == '1')
				{
					Blocking = true;
				}
				else
					Blocking = false;
			}

		}



I also applied a blocking value to the first tile, it doesn't apply though.


EDIT 2: Quickly noticed I didn't make the array larger, but didn't give any results after enlargening the array.

This post has been edited by Zapi: 27 January 2010 - 02:43 PM

Was This Post Helpful? 0
  • +
  • -

#7 Sergio Tapia  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1252
  • View blog
  • Posts: 4,168
  • Joined: 27-January 10

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 02:30 PM

Without actually seeing your method it's going to be very hard for me to try to help you out.

The code I posted above doesn't work as you wanted it to. I just noticed right now.

You have something like this:

G1 G1 G1
G1 G1 G1
G0 G0 G1

I was thinking you had something like this:

G 1 G 0
G G 1 0
0 0 1 G

Modify my existing code, by adding a second .Split(''); so each string in the tiles[] array is further divided into something you can use.

Also, try using breakpoints and figure out what's happening after the first tile is loaded.


Edit:

I'd really suggest you create a Tile class.
public class Tile
{
    public Image tileImage;
    public bool isBlocked;

    //Any other methods that directly act on this Tile class should go here.
}



If you create a tile class, you can easily create as many tiles as you need, set their image, and set their Blocked status. It'll make your code cleaner and give you a much easier time down the line.

This post has been edited by stapia.gutierrez: 27 January 2010 - 02:36 PM

Was This Post Helpful? 0
  • +
  • -

#8 Zapi  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 27-January 10

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 02:59 PM

I'll try that out, I'm already working object oriented. I have a map class, tile class, texturemanager class and a interface class holding the Read and Write functions. Those are just the classes that I use for the map itself. I'll share my original code for those classes:

Map.cs
using System;
using System.IO;
using System.Windows.Forms;

namespace TimeAndWalk
{
	public class Map : IStorable
	{
		public Tile[] tiles;
		private int area;
		private int width, height;

		public Map (int Width, int Height)
		{
			width = Width;  
			height = Height;
			area = width * height;

			tiles = new Tile[area];

			GeneratePlain();
		}


		public void Read(object o)
		{
			String inputFile = (string)o;

			using (StreamReader sr = new StreamReader(inputFile))
			{
				string textureSet = sr.ReadLine();
				int newHeight = int.Parse(sr.ReadLine());
				int newWidth = int.Parse(sr.ReadLine());

				height = newHeight;
				width = newWidth;
				area = width * height;

				TextureManager.BufferTextures("Plain");

				tiles = new Tile[area];
				for (int i = 0; i < area; i++)
					tiles[i] = new Tile();

				foreach (IStorable t in tiles)
				{
					t.Read(sr);
				}

			}
		}

		public void Write(object o)
		{
			String outputFile = (string)o;

			try
			{
				using (StreamWriter sw = new StreamWriter(outputFile))
				{
					sw.WriteLine("Plain"); // should be a variable somewhere :0
					sw.WriteLine(width);
					sw.WriteLine(height);
					foreach (IStorable t in tiles)
					{
						t.Write(sw);
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show("Error writing map file: " + e.ToString(),
				 "Write Error");
			}
		}

		public int tileTotal
		{
			get
			{
				return area;
			}
		}

		public int rowLength
		{
			get
			{
				return width;
			}
		}

		public int GetTileIndex(float posX, float posY)
		{
			float tileLengthPercentage = 1f / 8f;
			float percentagePosition = (posX + 1f) / 2f;
			int xRowNumber = (int)Math.Floor(percentagePosition / tileLengthPercentage);

			float percentagePositionY = Math.Abs((posY - 1f) / 2f);
			int yRowNumber = (int)Math.Floor(percentagePositionY / tileLengthPercentage);

			return (yRowNumber * 8) + xRowNumber;
		}

		public void GeneratePlain()
		{
			TextureManager.BufferTextures("Plain");
			for(int i = 0; i < area; i++)
			{
				tiles[i] = new Tile(TextureManager.RequestTexture("G"));
			}
		}

		public bool CanMove(float x, float y)
		{
			if (x > 1 || y > 1 || x < -1 || y < -1)
				return false;

			int newTile = GetTileIndex(x, y);
			if (newTile >= area)
				return false;

			if (tiles[newTile].IsBlocked())
				return false;

				return true;
		}
	}
}



Tile.cs
namespace TimeAndWalk
{
	public class Tile : IStorable
	{
		static public VertexBuffer vertexBuffer;
		public Texture tileTexture;
		private bool Blocking = false;

		public bool block
		{
			set
			{
				Blocking = value;
			}
		}

		public bool IsBlocked()
		{
			return Blocking;
		}
		public Tile()
		{

		}
		public Tile(Texture texture)
		{
			tileTexture = texture;
		}

		public void Read(object o)
		{
			StreamReader reader = (StreamReader)o;

			string tiles = reader.ReadLine();
			string blocked = reader.ReadLine();

			if (tiles.Equals("G"))
			{
				tileTexture = TextureManager.RequestTexture("G");
			}

			else if (tiles.Equals("S"))
			{
				tileTexture = TextureManager.RequestTexture("S");
			}

			if (blocked.Equals("1"))
			{
				Blocking = true;
			}
			else
				Blocking = false;
		}

		public void Write(object o)
		{
			StreamWriter writer = (StreamWriter)o;
			writer.Write(TextureManager.LookUpTexture(tileTexture));

			if (Blocking)
				writer.Write("1");
			else
				writer.Write("0");
		}

		static public void IntializeVertexBuffer(Device device)
		{
			device.RenderState.Lighting = false;
			vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionTextured),
			 4,
			 device,
			 0,
			 CustomVertex.PositionTextured.Format,
			 Pool.Default);

			vertexBuffer.Created += new System.EventHandler(OnCreateVertexBuffer);
			OnCreateVertexBuffer(vertexBuffer, null);
		}

		static private void OnCreateVertexBuffer(object sender, System.EventArgs e)
		{
			VertexBuffer buffer = (VertexBuffer)sender;
			CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[4];

			verts[0].Position = new Vector3(0.25f, 0, 1f);
			verts[1].Position = new Vector3(0.25f, -.25f, 1f);
			verts[2].Position = new Vector3(0, -.25f, 1f);
			verts[3].Position = new Vector3(0, 0, 1f);

			verts[0].Tu = 1;
			verts[0].Tv = 0; // Övre högra hörnet
			verts[3].Tu = 0;
			verts[3].Tv = 0;
			verts[1].Tu = 1;
			verts[1].Tv = 1;
			verts[2].Tu = 0;
			verts[2].Tv = 1;

			buffer.SetData(verts, 0, LockFlags.None);
		}
	}
}



TextureManager.cs
namespace TimeAndWalk
{
	public class TextureManager
	{
		static private ArrayList textureList = new ArrayList();
		static private Device device;
		static private bool Plain = false;

		public TextureManager()
		{

		}

		static public void SetDevice(Device d)
		{
			device = d;
		}

		static public void BufferTextures(string textureSet)
		{
			if (textureSet.Equals("Plain"))
			{
				if (Plain)
					return;
				else
					Plain = true;

				try
				{
					textureList.Add(TextureLoader.FromFile(device, @"bilder/gräs.bmp"));
					textureList.Add(TextureLoader.FromFile(device, @"bilder/grus.bmp"));
				}
				catch (Exception e)
				{
					if (device == null)
					{
						MessageBox.Show("TextureManager's Device variable needs"
											+ "to be set before being used!"
											+ e.ToString(), "oops");
					}
					else
					{
						MessageBox.Show("There has been an error loading the textures:"
							+ e.ToString(), "oops");
					}
				}
			}
		}

		static public Texture RequestTexture(string textureName)
		{
			if (textureName.Equals("G"))
			{
				return (Texture)textureList[0];
			}
			else if (textureName.Equals("S"))
			{
				return (Texture)textureList[1];
			}
			else
			{
				MessageBox.Show("Requested texture cannot be found!: ");
				return null;
			}

		}

		static public string LookUpTexture(Texture t)
		{
			if (t.Equals(textureList[0]))
			{
				return "G";
			}
			else
				return "S";
		}
	}
}


My leftalt + L loading function is in another class, my playinggamestate class holding my controls.

View Poststapia.gutierrez, on 27 Jan, 2010 - 01:30 PM, said:

Without actually seeing your method it's going to be very hard for me to try to help you out.

The code I posted above doesn't work as you wanted it to. I just noticed right now.

You have something like this:

G1 G1 G1
G1 G1 G1
G0 G0 G1

I was thinking you had something like this:

G 1 G 0
G G 1 0
0 0 1 G

Modify my existing code, by adding a second .Split(''); so each string in the tiles[] array is further divided into something you can use.

Also, try using breakpoints and figure out what's happening after the first tile is loaded.


Edit:

I'd really suggest you create a Tile class.
public class Tile
{
    public Image tileImage;
    public bool isBlocked;

    //Any other methods that directly act on this Tile class should go here.
}



If you create a tile class, you can easily create as many tiles as you need, set their image, and set their Blocked status. It'll make your code cleaner and give you a much easier time down the line.

Was This Post Helpful? 0
  • +
  • -

#9 Sergio Tapia  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1252
  • View blog
  • Posts: 4,168
  • Joined: 27-January 10

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 03:03 PM

So, since you fixed your 'images aren't loading after the first' issue, what problems are you having? :)
Was This Post Helpful? 0
  • +
  • -

#10 Zapi  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 27-January 10

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 03:13 PM

View Poststapia.gutierrez, on 27 Jan, 2010 - 02:03 PM, said:

So, since you fixed your 'images aren't loading after the first' issue, what problems are you having? :)


The problem is that the code only works with the G, S, 0, 1's being on their own lines. It's not working with the

G0 S1 G0 G0 G0 G0 S1

Things, it just gives me a blank window/background leftout with my character on the screen. I will check more into it tomorrow, and return if I can't figure it out with your latest tip with the extra split.

This post has been edited by Zapi: 27 January 2010 - 03:15 PM

Was This Post Helpful? 0
  • +
  • -

#11 scalt  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 63
  • View blog
  • Posts: 342
  • Joined: 22-November 07

Re: Requesting "advanced" StreamReader help

Posted 27 January 2010 - 04:16 PM

Just guessing here, but is your map an 8x8 square? If so, here is what I would do for your map/tile classes (this is just a skeleton but hopefully you get the idea):


class Map
    {
        public Tile[,] tiles;
        private int area;
        private int width, height;

        public Map(int Width, int Height, string mapfile)
        {
            width = Width;
            height = Height;
            area = width * height;

            tiles = new Tile[height, width];

            GeneratePlain(mapfile);
        }

        private void GeneratePlain(string mapfile)
        {
            //here is where you load the information from the file
            List<string[]> rows = new List<string[]>();
            using (StreamReader sr = new StreamReader(mapfile))
                while (!sr.EndOfStream)
                    rows.Add(sr.ReadLine().Split(' '));

            //if file didn't have enough rows, throw an exception
            if (rows.Count < height)
                throw new Exception("Not enough rows in file");

            for(int i=0;i< height; i++)
            {
                 //if this row didn't have enough columns, throw an exception
                if (rows[i].Length < width)
                    throw new Exception("Row " + i + " didn't have enough columns");
                
                //here is where you actually create the tiles
                for(int j=0;j< width; j++)
                    tiles[i,j] = new Tile(rows[i][j]);
            }
        }

    }

class Tile
    {

        public Tile(string info)
        {
            if (info.Length != 2)
                throw new Exception("'info' string was not in correct format");

            switch (info[0])
            {
                case 'G':
                    //do grass things here
                    break;
                case 'S':
                    //do stone things here
                    break;
                default:
                    throw new Exception("Unknown texture type " + info[0]);
            }

            switch (info[1])
            {
                case '0':
                    //do blocking things here
                    break;
                case '1':
                    //do blocking things here
                    break;
                default:
                    throw new Exception("Unknown 'block' state " + info[1]);
            }
        }
    }




To use these you would have an input file with the format:
G1 G1 G1
G1 G1 G1
G0 G0 G1

Keep in mind that to use the 'Map' class, you have to give it the path to the file as well as the width and the height. There are checks in there to make sure that the file isn't too small. If the file is too big (ie has more rows and/or cols than needed, this code will only load what the grid needs, so if you feed a file for a 10x10 map, but the map only wanst 8x8, it will only load the first 8 rows/cols.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1