Auslesefehler beim ObjectInputStream(Array)

  • Themenstarter Themenstarter Oneric
  • Beginndatum Beginndatum
O

Oneric

Gast
Hallo,

ich hab vor kurzer Zeit angefangen ein einfaches 2D-Game im Terraria/Minecraft Style in Java zu schreiben. Ich bin gerade dabei ein Savesytem zu schreiben damit man seine Bauten auch speichern kann.
Allerdings tritt beim Auslesen eines Saves immer das Problem auf das die BlockID nicht mehr korrekt erkannt wird, wodurch auf einmal die Luft solid ist, etc..


Hier ist mein SourceCode

Java:
package net.MC_2D.src;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.StringBuilder;


public class SaveManager {


	public SaveManager()
	{
		
	}
	
	public  void saveWorld(String directionName)
	{
		
		System.out.println("Beginne speichern...");
		
		StringBuilder builder = new StringBuilder();
		
		//String saveOrder =  builder.append("saves/").append(directionName).toString();
		
		/**String levelSave =  builder.append("saves/").append(directionName).append("/level.dat").toString();*/
		
		/*if(!new File(levelSave).exists())
		{
			new File(levelSave).mkdirs();
		}*/
		
		try {
			FileOutputStream fileSchreib =  new FileOutputStream("saves/level.region");
			
			ObjectOutputStream schreiben = new ObjectOutputStream(fileSchreib);
			
			for(int x = 0; x < Level.worldW; x++)
			{
				for(int y = 0; y < Level.worldH; y++)
				{
					
					int[] id = Component.level.block[x][y].id;
					
				schreiben.writeObject(id);
				//schreiben.write(sign);
				//schreiben.writeInt(Component.level.block[x][y].id[1]);
				//schreiben.write((byte)strich);
			
				}
				
			}
			
			
			//schreiben.write(-99);
			schreiben.flush();
			schreiben.close();
			System.out.println("Gespeichert");
			
		} 	
		catch (IOException e) {
			
			e.printStackTrace();
		}
		
	}
	
	
	
	public static void loadWorld()
	{
		Component.isMenu = false;
		
		for(int x = 0; x < Level.worldW; x++)
		{
			for(int y = 0; y <  Level.worldH; y++)
			{
				Component.level.block[x][y].id = Tile.air;
			}
		}
		
		int[] id;
		
		int[] air = {-1, -1};
		int[] solidAir = {-1, -2};
		
		
		 try {
				FileInputStream leseFile = new FileInputStream("saves/level.region");
				ObjectInputStream leseStrom = new ObjectInputStream(leseFile);
				
				/*do{*/
				
				for(int x = 0; x < Level.worldW; x++)
				{
					for(int y = 0; y <  Level.worldH; y++)
					{
						
						 id = (int[]) leseStrom.readObject();
						 

                                                                    /**
                                                                      Diese Abfragen würden das Problem zwar       
                                                                      auch lösen werden aber mit mehreren Blöcken  
                                                                      immer aufwendiger(Hier wurden auch nur die Luft 
                                                                      ausgetauscht)

                                                                      */
						 if(id[0] == air[0] && id[1] == air[1])
						 {
							 Component.level.block[x][y].id = Tile.air;
						 }
						 
						
						 else
						 {
							 Component.level.block[x][y].id = id;
						 }
					      
					    	    
					      
						
					}	
				}
				
				
				
					leseStrom.close();
				     System.out.println( Component.level.block[1][1].id[0]);
				
				     System.out.println( Component.level.block[1][1].id[1]);
				     
				  
				
			} catch (IOException | ClassNotFoundException e) {
				
				e.printStackTrace();
			}
	}
	
			
		
		  
		
		
	

}



Danke für eure Hilfe,


Oneric
 
ganz schön an Erklärungen gespart, ich denke mir mal selber den Rest:
es werden die korrekten Werte gespeichert, etwa -1,-1 für Air usw.,
es werden auch die korrekten Werte geladen,
du willst aber nicht diese fremden Arrays übernehmen sondern genau Tile.air usw., andere Arrays?

das scheint etwas unnötig, wird damit im Programm auf == verglichen?
verwende lieber Enum-Werte, dann sind die aus der Datei gelesenen gleich die richtigen, jeden Enum-Wert gibt es nur einmal,

bleibt es eher wie bisher, könntest du die drohenden vielen ifs durch Einsatz von Listen behandeln:
füge Tile.air usw. in eine Liste, gehe mit einer Schleife durch,
wenn das gelesene Array einem aus der Liste gleicht, dann den Listeneintrag setzen

------

allgemeiner Tipp für ObjectStream: nicht so viele Objekte speichern und lesen, sondern genau EINES:
kann ruhig ein großes sein, welches alle anderen Daten enthält, in großen Arrays, Listen usw.,

du musst nicht genau deine Programm-Hauptdaten wie das Component.level.block-2D-Array speichern, obwohl evtl. möglich und günstig,
selbst wenn du separat speichern willst, kannst du deine Doppelschleife usw. behalten, aber um die Daten auf eine zweite Struktur zu übertragen, ohne catch (IOException e) usw.,
dieses Objekt dann speichern und später lesen, nur ein Objekt ist schnell gemacht,
nach dem Lesen wenn nötig wieder Doppelschleife wie bisher, aber nicht aus der Datei sondern aus dem Daten-Objekt,
rein in Java, wiederum nix mit catch (IOException | ClassNotFoundException e)

so riesig wird der Unterschied letzlich nicht sein, nur als Denkansatz
 
Ja, ich hätte das Problem vl etwas ausführlicher erklären sollen, stimmt schon.

Aber du hattest ganz recht mit deiner Vermutung. Ich brauche genau diese Arrays da diese mit == verglichen werden.
Die beiden Werte des Arrays dienen aber gleichzeitig auch als render index. Sprich wo in dem 400x400 px großem png sich dieser Block-spirite befindet.

Ich habe jetzt vor erst mal das Ganze umgestellt und jede BlockID in eine ArrayList gepackt und vergleiche die ausgelesenen Werte damit.

Mir steht jetzt aber auch die Möglichkeit offen das Block Id System so umzustellen dass es nur noch ein normaler integer ist.
Diese sollten beim Auslesen doch korrekt erkannt werden, oder ?

Zuwas würdest du mir denn raten:

- weiter einen int[] als Id und if-Abfragen

oder

- Id system umschreiben so dass es normale integer benutzt


Hier auch noch mal mein aktueller Code:

Java:
package net.MC_2D.src;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.StringBuilder;
import java.util.ArrayList;


public class SaveManager {


	public SaveManager()
	{
		
	}
	
	public  void saveWorld(String directionName)
	{
		
		ArrayList<int[]> list = new ArrayList<int[]>();
		
		System.out.println("Beginne speichern...");
		
		StringBuilder builder = new StringBuilder();
		
		//String saveOrder =  builder.append("saves/").append(directionName).toString();
		
		/**String levelSave =  builder.append("saves/").append(directionName).append("/level.dat").toString();*/
		
		/*if(!new File(levelSave).exists())
		{
			new File(levelSave).mkdirs();
		}*/
		
		try {
			FileOutputStream fileSchreib =  new FileOutputStream("saves/level.region");
			
			ObjectOutputStream schreiben = new ObjectOutputStream(fileSchreib);
			
			for(int y = 0; y < Level.worldH; y++)
			{
				
				for(int x = 0; x < Level.worldW; x++)
				{
					
					list.add(Component.level.block[x][y].id);
					
				
				//schreiben.write(sign);
				//schreiben.writeInt(Component.level.block[x][y].id[1]);
				//schreiben.write((byte)strich);
			
				}
				
			}
			
			schreiben.writeObject(list);
			
			schreiben.flush();
			schreiben.close();
			System.out.println("Gespeichert");
			
		} 	
		catch (IOException e) {
			
			e.printStackTrace();
		}
		
	}
	
	
	
	public static void loadWorld()
	{
		Component.isMenu = false;
		
		for(int x = 0; x < Level.worldW; x++)
		{
			for(int y = 0; y <  Level.worldH; y++)
			{
				Component.level.block[x][y].id = Tile.air;
			}
		}
		
		ArrayList<int[]> list;
		
		int[] air = {-1, -1};
		int[] solidAir = {-1, -2};
		
		
		 try {
				FileInputStream leseFile = new FileInputStream("saves/level.region");
				ObjectInputStream leseStrom = new ObjectInputStream(leseFile);
				
				/*do{*/
				
				list = (ArrayList<int[]>)leseStrom.readObject();
				
				int x = 0,y = 0;
				
				for(int i = 0; i < list.toArray().length; i++)
				{
					
					int[] id = list.get(i);
					
					for(int e = 0; e < Component.blockList.blockIDs.toArray().length; e++)
					{
						if(id[0] == Component.blockList.blockIDs.get(e)[0] && id[1] == Component.blockList.blockIDs.get(e)[1])
						{
							id = Component.blockList.blockIDs.get(e);
						}
					}
					
					Component.level.block[x][y].id = id;
					
					x++;
					if(x == Level.worldW)
					{
						x = 0;
						y++;
					}
					
				}
					
						
						 
					/**	 
						 if(id[0] == air[0] && id[1] == air[1])
						 {
							 Component.level.block[x][y].id = Tile.air;
						 }
						 
						 else if(id[0] == solidAir[0] && id[1] == solidAir[1])
						 {
							 Component.level.block[x][y].id = Tile.solidAir;
						 }
						 else
						 {
							 Component.level.block[x][y].id = id;
						 }
					      
					    	 */   
					      
						
						
				
				
				
				
					leseStrom.close();
				     System.out.println( Component.level.block[1][1].id[0]);
				
				     System.out.println( Component.level.block[1][1].id[1]);
				     
				  
				
			} catch (IOException | ClassNotFoundException e) {
				
				e.printStackTrace();
			}
	}
	
	
	
  
		
		
	

}


Danke.
 
ich hatte ja schon eine eigene Enum vorgeschlagen, das ist etwas mächtiger,
man kann direkt bei diesem Wert das int[] ablegen statt mit dem int id in einer anderen Datenstruktur nachzuschauen usw.

int sehe ich aber dahinter auch vorn dem Array
 
Danke SlaterB,

ich hab jetz die Block-klasse etwas umgeschrieben, so dass es eigentlich funktionieren sollte. Die Collision mit dingen etc funktioniert auch, aber alle Blöcke benutzen nur wird die Textur falsch definiert. Als Bildquelle benutze ich eine große Block.png
welche in 20x20 20x20px große Texturen unterteilt ist.

Hier die Block-Klasse:

Java:
package net.MC_2D.src;

import java.awt.Graphics;
import java.awt.Rectangle;

public class Block extends Rectangle{

	private static final long serialVersionUID = 1L;
	
	public int[] blockIndex = {0, 0}; 
	public int id = 0;
	
	
	private Block()
	{
		
	}
	
	
	public Block(Rectangle size, int id)
	{
		this.setBounds(size);
		
		this.id = id;
		
		this.blockIndex = this.getRenderIndex(id);
		
		
		
	}
	
	
	
	
	
	
	
	
	public void render(Graphics g)
	{
		if(id != Tile.air && id != Tile.solidAir)
		{
			g.drawImage(Tile.tile_terrain, x - (int)Component.sX, y - (int)Component.sY, x - (int)Component.sX + width, y - (int)Component.sY + height, (blockIndex[0] * Tile.blockSize), (blockIndex[1] * Tile.blockSize), (blockIndex[0] * Tile.blockSize + Tile.blockSize), (blockIndex[1] * Tile.blockSize + Tile.blockSize), null);
			
		}
	}
	
	public Enum<EnumObject> getEnumType()
	{
		if(id == Tile.air)
		{
			return EnumObject.Air;
		}
		
		else
		{
			return EnumObject.Block;
		}
	}
	
	public void tick()
	{
		
	}
	
	public void backgroundRendering(Graphics g)
	{
		if(id != Tile.air && id != Tile.solidAir)
		{
			//g.drawImage(Tile.tile_terrain, x - (int)Component.sX, y - (int)Component.sY, x - (int)Component.sX + width, y - (int)Component.sY + height, (id[0] * Tile.blockSize), (id[1] * Tile.blockSize), (id[0] * Tile.blockSize + Tile.blockSize), (id[1] * Tile.blockSize + Tile.blockSize), null);
			g.drawImage(Tile.tile_terrain, x - (int)Component.sX, y - (int)Component.sY, x - (int)Component.sX + width, y - (int)Component.sY + height, (blockIndex[0] * Tile.blockSize), (blockIndex[1] * Tile.blockSize), (blockIndex[0] * Tile.blockSize + Tile.blockSize), (blockIndex[1] * Tile.blockSize + Tile.blockSize), null);
			g.drawImage(Tile.tile_backgroundBlock, x - (int)Component.sX, y - (int)Component.sY, null);
			
		}
	}

           /**
           *Methode die nun den render Index berechen SOLLTE. Funzt aber nicht ;D
           */
	
	private int[] getRenderIndex(int id)
	{
		if(id == Tile.air)
		{
			int[] renderIndex = {-1, -1};
			return renderIndex;
		}
		else if(id == Tile.solidAir)
		{
			int[] renderIndex = {-1, -2};
			return renderIndex;
		}
		else
		{
			
			int cont = id;
			int[] renderIndex = {0, 0};
			int wdh = 0;
			
			while(cont > 19)
			{
				cont -= 20;
				wdh++;
			}
			
			renderIndex[0] = cont-1;
			renderIndex[1] = wdh;
			
			return renderIndex;
			
		}
	}

}


Danke.

EDIT::

Ok. Ich hab jetz mal das
Java:
blockIndex = this.getRenderIndex
in die Render Methode geschrieben und es funktioniert.
Allerdings bedeutet dies dass bei jedem neu rendern alle Indexe neu ausgerechnet werden müssen.
WEiß jemnad warum ich es nicht in den Konstruktor schreiben kann, bzw. wie ich das Problem löse ? ???:L
Danke
 
Zuletzt bearbeitet von einem Moderator:
ich kann da im Moment nichts erkennen,

wenn die Methode getRenderIndex() privat ist und immer die eigene Id braucht, dann besser Parameter weglassen,
aber das sollte kein Grund sein,

debugge/ logge ein wenig, wird am Anfang das richtige Array berechnet und später ist aus unbekannten Gründen ein
anderes Array an dieser Stelle bei einem bestimmten Objekt X?
 
Habe jetz mir noch mal allles genauer angeschaut.
Es war eigentlich ein ganz einfacher logischer Fehler. Beim generiren des Levels werden alle Positionen erst mal mit einem Luftblock belegt und anschließend wird die Id geändert. Dabei wird aber nicht der Konstruktor aber nicht erneut aufgerufen. Das ganze wurde nun durch eine simple if abrfarge in dder Render Methode gelöst die immer wenn sich die ID ändert den RenderIndex neu berechnet.

Auch der SaveManager funktioniert nun ohne vergleichen der IDs mit einer Liste einwandfrei.

Danke


~Oneric
 

Neue Themen


Zurück
Oben