Arjan Egges & Paul Bergervoet Tiles, File I/O, Switch Arjan Egges & Paul Bergervoet
Overzicht
Tiles Veel games gebruiken tiles om werelden/levels/puzzels voor te stellen
Tiles in Penguin Pairs #.......# #...r...# #. .#
Tiles in Penguin Pairs
Tile klasse enum TileType { Normal, Background, Wall }; class Tile : SpriteGameObject { protected TileType type; public Tile(string asset, int layer = 0, string id = "", int sheetIndex = 0) : base(asset, layer, id, sheetIndex) type = TileType.Normal; }
Tile klasse public override void Draw(GameTime gameTime, SpriteBatch s) { if (type == TileType.Background) return; base.Draw(gameTime, s); } public TileType TileType get { return type; } set { type = value; }
Uitgebreide definitie level Splash! Don’t let the penguins fall in the water! 1 9 9 4 2 2 #.......# #...r...# #. .#
Klassen om files te lezen en te schrijven Object Stream TextReader TextWriter FileStream StreamReader StreamWriter store MemoryStream StringReader StringWriter NetworkStream BinaryReader BinaryWriter XmlReader XmlWriter BufferedStream decorator GZipStream CryptoStream
Klassen om files te lezen en te schrijven leest en schrijft bytes leest string en char schrijft string en char Stream FileStream StreamReader StreamWriter MemoryStream leest/schrijft een file NetworkStream leest/schrijft het geheugen leest/schrijft het netwerk
levert getal tussen 0...255 of –1 als er niets meer is Methoden van Stream class FileStream { int Read (byte[] doel, int max) { for (int t=0; t<max; t++) { int b = this.ReadByte(); Stream if (b==–1) return t; FileStream doel[t] = (byte) b ; } MemoryStream return max; } NetworkStream } FileMode.Append FileStream fs; FileMode.Create fs = new FileStream ("test", FileMode.Open ); levert getal tussen 0...255 of –1 als er niets meer is int b = fs.ReadByte( ) ; byte [ ] ruimte = new byte[100]; int n = fs.Read ( ruimte, 100 ) ;
Methoden van Stream class FileStream Stream { int Read (byte[] doel, int max) { for (int t=0; t<max; t++) { int b = this.ReadByte(); Stream if (b==–1) return t; FileStream doel[t] = (byte) b ; } MemoryStream return max; } NetworkStream virtual int ReadByte ( ) { /* leeg */ } } class FileStream : Stream { override int ReadByte ( ) { return ............... } }
Methoden van Stream abstract class FileStream Stream { int Read (byte[] doel, int max) { for (int t=0; t<max; t++) { int b = this.ReadByte(); Stream if (b==–1) return t; FileStream doel[t] = (byte) b ; } MemoryStream return max; } NetworkStream abstract virtual int ReadByte ( ) ; { /* leeg */ } } class FileStream : Stream { override int ReadByte ( ) { return ............... } }
Abstracte methode/klasse Abstracte methode: methode zonder body Abstracte klasse: bevat abstracte methode kun je geen new object van maken alleen bedoeld als superklasse Stream FileStream MemoryStream NetworkStream Texture Texture2D Texture3D
Abstracte methode/klasse Abstracte methode: methode zonder body Abstracte klasse: bevat abstracte methode kun je geen new object van maken alleen bedoeld als superklasse Stream FileStream MemoryStream NetworkStream MotorVoertuig PersonenAuto VrachtWagen
Ander voorbeeld: GameObject abstract class GameObject { protected Vector2 position; public GameObject() this.position = Vector2.Zero; } public Vector2 Position get { return position; } set { position = value; } public abstract void Draw(GameTime gameTime, SpriteBatch s);
GameObject hierarchie Verschillende soorten game objecten Een tekst game object Een sprite game object Een game object dat een geanimeerde sprite bevat Alle soorten erven van de GameObject klasse GameObject zelf is een bijna lege klasse GameObject instanties zouden verboden moeten worden!
GameObject instanties verbieden Dit kunnen we doen door middel van abstracte klassen. Instanties maken is verboden! class GameObject { protected Vector2 position; protected Vector2 velocity; public GameObject(Vector2 position) this.position = position; this.velocity = Vector2.Zero; } … abstract class GameObject Maar erven mag wel!
GameObject klasse Dit mag niet: Erven mag wel: GameObject g = new GameObject(...); class SpriteGameObject : GameObject { protected Texture2D sprite; // enzovoorts… }
De familie Magdat void DoeIetsMetEenGameObject(GameObject g) { … } Dit mag! GameObject obj; GameObject[] lijst; lijst = new GameObject[10]; lijst[0] = new GameObject(…); GameObject obj2 = new SpriteGameObject(…); this.DoeIetsMetEenGameObject(obj2); Dit mag! Dit mag niet!
GameObject als interface Abstracte klassen kunnen als interface werken: En dan later: GameObject obj; if (animatedGame) obj = new AnimatedGameObject(); else obj = new SpriteGameObject(); Hier maakt het niet uit wat voor GameObject we hebben. … obj.Draw(gameTime, this.spriteBatch);
Decorator Streams Stream str; str = new FileStream ( "test", FileMode.Create ); Stream GZipStream klein; FileStream klein = new GZipStream ( str ) ; store MemoryStream klein.Write( mijnByteArray ); NetworkStream geef het onderliggende store-medium "in beheer" je kunt decorators combineren BufferedStream decorator GZipStream CryptoStream geheim; CryptoStream geheim = new CryptoStream ( klein ) ;
Reader en Writer leest en schrijft bytes leest string en char schrijft string en char Object Stream TextReader TextWriter FileStream StreamReader StreamWriter store MemoryStream StringReader StringWriter NetworkStream vanuit een Stream vanuit een String BufferedStream decorator GZipStream CryptoStream
Reader en Writer leest en schrijft bytes leest string en char schrijft string en char Object Stream TextReader TextWriter FileStream StreamReader StreamWriter store MemoryStream StringReader StringWriter NetworkStream Stream str; str = new FileStream ( "test.txt", FileMode.Open ); BufferedStream TextReader lezer; decorator GZipStream lezer = new StreamReader ( str ); CryptoStream string s; s = lezer . ReadToEnd ( );
Reader en Writer leest en schrijft bytes leest string en char schrijft string en char Object Stream TextReader TextWriter FileStream StreamReader StreamWriter store MemoryStream StringReader StringWriter NetworkStream Stream str; str = new FileStream ( "test.txt", FileMode.Open ); BufferedStream TextReader lezer; decorator GZipStream lezer = new StreamReader ( str ); lezer = new StreamReader ( "test.txt" ); CryptoStream convenience-methode string s; s = lezer . ReadToEnd ( );
Reader en Writer leest en schrijft bytes leest string en char schrijft string en char Object Stream TextReader TextWriter FileStream StreamReader StreamWriter store MemoryStream StringReader StringWriter NetworkStream BinaryReader BinaryWriter XmlReader XmlWriter BufferedStream leest XML-teksten leest int en double decorator GZipStream CryptoStream
statische methode die het constructie-werk doet (wat de constructor nodig heeft) Object Stream TextReader (Stream) (Filenaam ,FileMode) FileStream StreamReader (Filenaam) store Memory Stream StringReader ( ) (String) Network Stream BinaryReader (Socket) (Stream) XmlReader .Create(…) Buffered Stream (Stream, int) decorator GZip Stream statische methode die het constructie-werk doet (Stream, CompressionMode) Crypto Stream (Stream, CryptoStreamMode)
Publieksvraag Schrijf een methode WriteFibonacci die de eerste n waarden van de Fibonaccireeks naar een bestand schrijft Bestandsnaam en n zijn parameters
Uitwerking public void WriteFibonacci(string path, int n) { StreamWriter fileWriter = new StreamWriter(path, false); int first = 1, second = 1; fileWriter.WriteLine("1\n1"); for (int i = 0; i < n - 2; i++) int newval = first + second; first = second; second = newval; fileWriter.WriteLine(newval.ToString()); } fileWriter.Close();
Levels laden public void LoadLevels(string path) { StreamReader fileReader = new StreamReader(path); string line = fileReader.ReadLine(); int nrLevels = int.Parse(line); for (int currLevel = 1; currLevel <= nrLevels; currLevel++) levels.Add(new Level(currLevel, fileReader)); }
Uitgebreide definitie level Splash! Don’t let the penguins fall in the water! 1 9 9 4 2 2 #.......# #...r...# #. .#
In de Level constructor // read the level title, comment, nr of required pairs string levelTitle = reader.ReadLine(); string levelHelp = reader.ReadLine(); int nrpairs = int.Parse(reader.ReadLine()); // read the width and height of the level string[] stringlist = reader.ReadLine().Split(); int width = int.Parse(stringlist[0]); int height = int.Parse(stringlist[1]); // create the playingfield object GameObjectList playingFld = new GameObjectList(1, "playingField"); playingFld.Position = new Vector2((PenguinPairs.Screen.X - width * 73) / 2, 100); this.Add(playingFld);
In de Level constructor // read the hint information stringlist = reader.ReadLine().Split(); int hintx = int.Parse(stringlist[0]) - 1; int hinty = int.Parse(stringlist[1]) - 1; int hintdirection = int.Parse(stringlist[2]); SpriteGameObject hint = new SpriteGameObject("spr_arrow_hint@4", 2, "hint", hintdirection); hint.Position = new Vector2(hintx, hinty) * new Vector2(73, 72); playingFld.Add(hint); // attach a visibility timer to the hint arrow VisibilityTimer hintVisible = new VisibilityTimer(hint, 0, "hintVisible"); playingFld.Add(hintVisible);
Alle tiles lezen // read the grid GameObjectGrid tf = new GameObjectGrid(height,width, 1, "tilefield"); tf.CellHeight = 72; tf.CellWidth = 73; for (int row = 0; row < height; row++) { string currRow = reader.ReadLine(); for (int col = 0; col < currRow.Length; col++) // handle the tile ‘currRow[col]’ here }
Verschillende gevallen afhandelen char currCell = currRow[col]; if (currCell == '.') // laad een normale tile else if (currCell == '#') // laad een ijsberg else if (currCell == 'r' || currCell == 'g' || ...) // laad een penguin //... etc
De switch-opdracht Het geheel kan vrij lang worden met al die if-opdrachten Handiger: de switch-opdracht! if (x==1) Een(); else if (x==2) { Twee(); OokTwee(); } else if (x==3 || x==4) DrieOfVier(); else Meer();
else if (x==2) { Twee(); OokTwee(); } if (x==1) Een(); else if (x==2) { Twee(); OokTwee(); } else if (x==3 || x==4) DrieOfVier(); else Meer(); switch(x) { case 1: Een(); break; case 2: Twee(); OokTwee(); case 3: case 4: DrieOfVier(); default: Meer(); } Moet een constante zijn! Geeft het einde van de case aan (break is verplicht!) Een case mag uit meerdere opdrachten bestaan. Een case mag meer dan 1 geval afhandelen. Een default case voor de rest van de gevallen.
Syntaxdiagram switch
Verschillende gevallen afhandelen Tile t; switch (currRow[col]) { case '.' : t = new Tile("Sprites/spr_field@2", 0, "", (row+col) % 2); tf.Add(t, col, row); break; case ' ': t = new Tile("Sprites/spr_wall"); t.TileType = TileType.Background; ...
Publieksvraag Schrijf een methode RekenUit, die een string meekrijgt met daarin een berekening opgesteld als ‘waarde operator waarde’ De waarden en de operator zijn gescheiden door een spatie, waarden zijn gehele getallen De uitkomst is een geheel getal dat het resultaat van de berekening is De methode kent de *, +, - en / operatoren Gebruik switch! int voorbeeld = RekenUit("3 + 8"); // voorbeeld == 11 voorbeeld = RekenUit("12 / 4"); // voorbeeld == 3 voorbeeld = RekenUit("6 * 10"); // voorbeeld = 60
Uitwerking public static int RekenUit(string expressie) { string[] onderdelen = expressie.Split(' '); int resultaat = 0; int links = int.Parse(onderdelen[0]); int rechts = int.Parse(onderdelen[2]); switch (onderdelen[1]) case "+" : resultaat = links + rechts; break; case "-" : resultaat = links – rechts; break; case "*" : resultaat = links * rechts; break; case "/" : resultaat = links / rechts; break; } return resultaat;
Voortgang van de speler bijhouden Onthouden welk level af is gemaakt sinds de vorige keer Level status opslaan in file! false,false true,false
Status lezen public void LoadLevelsStatus(string path) { StreamReader fileReader = new StreamReader(path); for (int i = 0; i < levels.Count; i++) string line = fileReader.ReadLine(); string[] elems = line.Split(','); if (elems.Length == 2) levels[i].Locked = bool.Parse(elems[0]); levels[i].Solved = bool.Parse(elems[1]); } fileReader.Close();
Status schrijven public void WriteLevelsStatus(string path) { StreamWriter fileWriter = new StreamWriter(path, false); for (int i = 0; i < levels.Count; i++) string line = levels[i].Locked.ToString() + "," + levels[i].Solved.ToString(); fileWriter.WriteLine(line); } fileWriter.Close();
PlayingState constructor public PlayingState(ContentManager Content) { currentLevelIndex = -1; levels = new List<Level>(); LoadLevels(Content.RootDirectory + "/Levels/levels.txt"); LoadLevelsStatus(Content.RootDirectory + "/Levels/levels_status.txt"); }