# TileBased Spiel schneckenlangsam



## Steev (17. Sep 2007)

Wie der titel schon sagt,
ich habe probiert ein kleines stückbasiertes Spiel zu programmieren.
Es funktioniert soweit auch alles nach meinen Wünschen auser das
das Spiel auf meinem Laptop (1.5 MHZ) furchtbar ruckelt.
Das kann doch nicht sein, weil andere Spiele ja auch ruckelfrei laufen und 
tilebased sind (GTA, GTA2, etc.)

Es wäre furchtbar nett wenn jemand ein paar Verbesserungsvorschläge hätte:

Geladen werden 200x200 große Stücke,
das Fenster ist 800x600 groß und die Stückdateien sind *.png-Bilddateien

Hier mal der Code:

Die Klasse map für die Karte:

```
package gamemanager;

import java.awt.Graphics2D;
import java.awt.image.ImageObserver;

public class Map extends PaintObject
{
	protected int		screenWidth, screenHeight;
	protected int[][]	map;
	protected int		tileWidth, tileHeight;
	
	public Map(int x, int y, int z, int width, int height, double xscale,
			double yscale, double rotation, PaintManager pmgt, int screenWidth,
			int screenHeight, int[][] map, int tileWidth, int tileHeight)
	{
		super(x, y, z, width, height, xscale, yscale, rotation, pmgt);
		this.screenWidth = screenWidth;
		this.screenHeight = screenHeight;
		this.map = map;
		this.tileWidth = tileWidth;
		this.tileHeight = tileHeight;
	} 
	public void paintObject(Graphics2D g2, ImageObserver imgobserver)
	{
		int tempW = ((int) (tileWidth / 100.0 * xscale)),
			tempH = ((int) (tileHeight / 100.0 * yscale));
		int tempx = Math.abs(x / tempW),
			tempy = Math.abs(y / tempH);
		for (int x = tempx; x < tempx + (screenWidth / tempW) + 1; x++)
			for (int y = tempy; y < tempy + (screenHeight / tempH) + 1; y++)
				if(x < width && y < height)
				{
					if (map[x][y] - 1 >= 0 && map[x][y] - 1 <= pmgt.getImg().size())
						if (pmgt.getImg().get(map[x][y] - 1) != null)
							g2.drawImage(pmgt.getImg().get(map[y][x] - 1),
								 		this.x + (tempW * x),
								 		this.y + (tempH * y),
								 		tileWidth, tileHeight, imgobserver);
				}
		// manage Range
		if (x > 0)x = 0;
		if (y > 0)y = 0;
		if (x + (tempW * width) < screenWidth)x = -((tempW * width) - screenWidth);
		if (y + (tempH * height) < screenHeight)y = -((tempH * height) - screenHeight);
	}
	public int[][] getMap()
	{
		return map;
	}
	public void setMap(int[][] map)
	{
		this.map = map;
	}
	public int getScreenHeight()
	{
		return screenHeight;
	}
	public void setScreenHeight(int screenHeight)
	{
		this.screenHeight = screenHeight;
	}
	public int getScreenWidth()
	{
		return screenWidth;
	}
	public void setScreenWidth(int screenWidth)
	{
		this.screenWidth = screenWidth;
	}
	public int getTileHeight()
	{
		return tileHeight;
	}
	public void setTileHeight(int tileHeight)
	{
		this.tileHeight = tileHeight;
	}
	public int getTileWidth()
	{
		return tileWidth;
	}
	public void setTileWidth(int tileWidth)
	{
		this.tileWidth = tileWidth;
	}
}
```

Für die Darstellung:

```
package gamemanager;

import javax.imageio.ImageIO;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Image;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

public class PaintManager extends JPanel implements Runnable
{
	private static final long			serialVersionUID	= -1213544970195227776L;
	protected final double				rotationFactor		= 0.0174584;
	protected LinkedList<PaintObject>	objects				= new LinkedList<PaintObject>();
	protected File[]					buffer				= null;
	protected LinkedList<Image>			img					= new LinkedList<Image>();
	protected File						sourcePath			= null;
	protected int						totalimg			= 0;
	protected Color						background			= Color.BLACK;
	protected Boolean					antialiasing		= true;
	protected AffineTransform			trans				= new AffineTransform();
	protected String[]					allowedimgending	= {".png", ".jpg", ".gif"};
	protected boolean					loadformtxt			= true;
	protected int						priority			= Thread.NORM_PRIORITY;
	protected int						waitingtime			= 60;
	private Image						dbImage				= null;
	private Graphics					dbg					= null;
	public PaintManager(){}
	public PaintManager(int x, int y, int width, int height)
	{
		this.setBounds(x, y, width, height);
		startManager();
	}
	public PaintManager(int x, int y, int width, int height, Color background)
	{
		this.setBounds(x, y, width, height);
		this.background = background;
		startManager();
	}
	public PaintManager(int x, int y, int width, int height, Color background,
			Boolean antialiasing, AffineTransform trans)
	{
		this.setBounds(x, y, width, height);
		this.background = background;
		this.antialiasing = antialiasing;
		this.trans = trans;
		startManager();
	}
	public PaintManager(int x, int y, int width, int height, Color background,
			Boolean antialiasing, AffineTransform trans,
			boolean loadformtxt, int priority, int waitingtime)
	{
		this.setBounds(x, y, width, height);
		this.background = background;
		this.antialiasing = antialiasing;
		this.trans = trans;
		this.loadformtxt = loadformtxt;
		this.priority = priority;
		this.waitingtime = waitingtime;
		startManager();
	}
	public void startManager()
	{
		Thread th = new Thread(this);
		th.setDaemon(true);
		th.setPriority(priority);
		th.start();
	}
	public void run()
	{
		while(true)
		{
			this.paintImmediately(getX(), getY(), getWidth(), getHeight());
			try{
				Thread.sleep(waitingtime);
			} catch (InterruptedException e){}
		}
	}
	public void paintComponent(Graphics g)
	{
		BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics2D g2dbi = bi.createGraphics();
		g2dbi.setColor(background);
		g2dbi.fillRect(0, 0, getWidth(), getHeight());
		if(antialiasing)
		{
			g2dbi.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g2dbi.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		}
		for (int a = 0; a < objects.size(); a++)
			objects.get(a).paintObject(g2dbi, this);
		
		Graphics2D g2 = (Graphics2D)g;
		g2.drawImage(bi, 0, 0, getWidth(), getHeight(), this);
	}
	public void update(Graphics g)
	{
		if(dbImage == null)
		{
			dbImage = createImage(this.getSize().width, this.getSize().height);
			dbg		= dbImage.getGraphics();
		}
		dbg.setColor(getBackground());
		dbg.fillRect(200, 100, this.getSize().width, this.getSize().height);
		dbg.setColor(getForeground());
		paint(dbg);
		g.drawImage(dbImage, 0, 0, this);
	}
	public String[] getAllowedimgending()
	{
		return allowedimgending;
	}
	public void setAllowedimgending(String[] allowedimgending)
	{
		this.allowedimgending = allowedimgending;
	}
	public Boolean getAntialiasing()
	{
		return antialiasing;
	}
	public void setAntialiasing(Boolean antialiasing)
	{
		this.antialiasing = antialiasing;
	}
	public Color getBackground()
	{
		return background;
	}
	public void setBackground(Color background)
	{
		this.background = background;
	}
	public LinkedList<Image> getImg()
	{
		return img;
	}
	public void setImg(LinkedList<Image> img)
	{
		this.img = img;
	}
	public boolean isLoadformtxt()
	{
		return loadformtxt;
	}
	public void setLoadformtxt(boolean loadformtxt)
	{
		this.loadformtxt = loadformtxt;
	}
	public LinkedList<PaintObject> getObjects()
	{
		return objects;
	}
	public void setObjects(LinkedList<PaintObject> objects)
	{
		this.objects = objects;
	}
	public int getPriority()
	{
		return priority;
	}
	public void setPriority(int priority)
	{
		this.priority = priority;
	}
	public File getSourcePath()
	{
		return sourcePath;
	}
	public void setSourcePath(File sourcePath)
	{
		this.sourcePath = sourcePath;
	}
	public int getTotalimg()
	{
		return totalimg;
	}
	public void setTotalimg(int totalimg)
	{
		this.totalimg = totalimg;
	}
	public AffineTransform getTrans()
	{
		return trans;
	}
	public void setTrans(AffineTransform trans)
	{
		this.trans = trans;
	}
	public int getWaitingtime()
	{
		return waitingtime;
	}
	public void setWaitingtime(int waitingtime)
	{
		this.waitingtime = waitingtime;
	}
	public double getRotationFactor()
	{
		return rotationFactor;
	}
	public void add(int index, PaintObject element)
	{
		objects.add(index, element);
	}
	public boolean add(PaintObject o)
	{
		return objects.add(o);
	}
	public boolean addAll(Collection<? extends PaintObject> c)
	{
		return objects.addAll(c);
	}
	public boolean addAll(int index, Collection<? extends PaintObject> c)
	{
		return objects.addAll(index, c);
	}
	public void addFirst(PaintObject o)
	{
		objects.addFirst(o);
	}
	public void addLast(PaintObject o)
	{
		objects.addLast(o);
	}
	public void clear()
	{
		objects.clear();
	}
	public Object clone()
	{
		return objects.clone();
	}
	public boolean contains(Object o)
	{
		return objects.contains(o);
	}
	public boolean containsAll(Collection<?> c)
	{
		return objects.containsAll(c);
	}
	public PaintObject element()
	{
		return objects.element();
	}
	public boolean equals(Object o)
	{
		return objects.equals(o);
	}
	public PaintObject get(int index)
	{
		return objects.get(index);
	}
	public PaintObject getFirst()
	{
		return objects.getFirst();
	}
	public PaintObject getLast()
	{
		return objects.getLast();
	}
	public int hashCode()
	{
		return objects.hashCode();
	}
	public int indexOf(Object o)
	{
		return objects.indexOf(o);
	}
	public boolean isEmpty()
	{
		return objects.isEmpty();
	}
	public Iterator<PaintObject> iterator()
	{
		return objects.iterator();
	}
	public int lastIndexOf(Object o)
	{
		return objects.lastIndexOf(o);
	}
	public ListIterator<PaintObject> listIterator()
	{
		return objects.listIterator();
	}
	public ListIterator<PaintObject> listIterator(int index)
	{
		return objects.listIterator(index);
	}
	public boolean offer(PaintObject o)
	{
		return objects.offer(o);
	}
	public PaintObject peek()
	{
		return objects.peek();
	}
	public PaintObject poll()
	{
		return objects.poll();
	}
	public PaintObject remove()
	{
		return objects.remove();
	}
	public PaintObject objectsRemove(int index)
	{
		return objects.remove(index);
	}
	public boolean remove(Object o)
	{
		return objects.remove(o);
	}
	public boolean removeAll(Collection<?> c)
	{
		return objects.removeAll(c);
	}
	public PaintObject removeFirst()
	{
		return objects.removeFirst();
	}
	public PaintObject removeLast()
	{
		return objects.removeLast();
	}
	public boolean retainAll(Collection<?> c)
	{
		return objects.retainAll(c);
	}
	public PaintObject set(int index, PaintObject element)
	{
		return objects.set(index, element);
	}
	public int objectsSize()
	{
		return objects.size();
	}
	public List<PaintObject> subList(int fromIndex, int toIndex)
	{
		return objects.subList(fromIndex, toIndex);
	}
	public Object[] toArray()
	{
		return objects.toArray();
	}
	public <T> T[] toArray(T[] a)
	{
		return objects.toArray(a);
	}
	public String toString()
	{
		return objects.toString();
	}
	// ---> Datei-Management-Methoden
	public void loadImages()
	{
		if (sourcePath.isDirectory() && loadformtxt)
		{
			loadformtxt = false;
			System.err.println("Illegal Argument for sourcePath, importImgFromTxtData == true.\n" +
							   "But sourcePath is a folder.");
		}
		if (sourcePath.isFile() && !loadformtxt)
		{
			loadformtxt = true;
			System.err.println("Illegal Argument for sourcePath, importImgFromTxtData == false.\n" +
							   "But sourcePath is a file.");
		}
		if(loadformtxt)
			loadImagesFromTXT();
		else
			loadImagesFromDirectory();
	}
	public void loadImagesFromTXT()
	{
		// ---> suche die Bilder
		final int maxImg = 10000;
		BufferedReader bf;
		String line;
		buffer = new File[maxImg];
		try{
			bf = new BufferedReader(new FileReader(sourcePath.getAbsoluteFile()));
			while ((line = bf.readLine()) != null)
				for (int i = 0; i < allowedimgending.length; i++)
					if (line.indexOf(allowedimgending[i])>=0)
						if(totalimg<=maxImg)
							buffer[totalimg++] = new File(line).getAbsoluteFile();
			bf.close();
		} catch (IOException e){}
		
		// ---> lade die Bilder
		for (int a = 0; a < buffer.length; a++)
			try{
				if (buffer[a] != null)
					img.addLast(ImageIO.read(buffer[a]));
			} catch (IOException e){}
		buffer = null;
	}
	public void loadImagesFromDirectory()
	{
		buffer = sourcePath.getAbsoluteFile().listFiles();
		int len = buffer.length;
		File[] tempBuffer = new File[10000];
		for (int a = 0; a < len; a++)
			for (int b = 0; b < allowedimgending.length; b++)
				if (buffer[a].getAbsolutePath().endsWith(allowedimgending[b]))
					if (a <= tempBuffer.length)
						tempBuffer[totalimg++] = buffer[a].getAbsoluteFile();
		buffer = null;
		for (int a = 0; a <= totalimg; a++)
			try{
				if (tempBuffer[a] != null)
					img.addLast(ImageIO.read(tempBuffer[a].getAbsoluteFile()));
			} catch (IOException e){}
	}
	public void deleteAllImages()
	{
		img = null;
	}
}
```

Und schließlich die Hauptdateien:

```
import gamemanager.PaintManager;
import gamemanager.PaintObject;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;

import javax.imageio.ImageIO;


public class Mainfigure extends PaintObject{
	// Attribute
	public LinkedList<Mainfigure>	figures	= new LinkedList<Mainfigure>();
	private PaintObject				shadow;
	boolean							running, forwards;
	Image							fire	= null;
	// Methoden
	public Mainfigure(int x, int y, int z,
				  int width, int height,
				  double rotation,
				  PaintManager pmgt)
	{
		super(x, y, z, width, height, 100, 100, rotation, pmgt);
		shadow = new PaintObject(x, y, z, width, height, 100, 100, rotation, pmgt);
		shadow.setX(x+5);
		shadow.setY(y+5);
		shadow.setXscale(xscale);
		shadow.setYscale(yscale);
		try
		{
			fire = ImageIO.read(new File("data\\images\\game_shooting\\fire_001.png"));
		} catch (IOException e){}
	}
	public boolean radialHitTest(double d, double e, double f,  double g, double minRadius)
	{
		if(Math.sqrt(Math.pow(Math.abs(d - f), 2) + Math.pow(Math.abs(d - f), 2))<=minRadius)
			return true;
		return false;
	}
	public void manageRotation()
	{
		double myRadians = Math.atan2(cursorx - this.x, cursory - this.y);
		double myDegrees = Math.round((myRadians * -180 / Math.PI)) + 180;
		this.rotation = myDegrees;
	}
	@Override
	public void onBeforePaintObject(Graphics2D g2, ImageObserver imageobserver)
	{
		if(alive)
		{
			manageRotation();
			shadow.setRotation(rotation);
			shadow.paintObject(g2, imageobserver);
			if(rotate)
			{
				if(right)
					rotation+=8;
				else
					rotation-=8;
			}
		}
	}
	int		aktframe	= 0;
	int		aktFire		= 0;
	boolean	fromsh		= false;
	double	startTime	= System.currentTimeMillis();
	@Override
	public void onAfterPaintObject(Graphics2D g2, ImageObserver imageobserver)
	{
		if (alive)
		{
			if(shoot)
				if (dauerschuss || (System.currentTimeMillis() - startTime) / 1000 > 0.5)
				{
					startTime	= System.currentTimeMillis();
					if(aktbullets-1>=0)	
					{
						// ---[ Kugeln
						// bullets.add(new bullet(x+10, y+50, rotation));
						bullets.add(new bullet(
								x+10 + ((Math.random()>0.5)?(1):(-1)*5),
								y+50 + ((Math.random()>0.5)?(1):(-1)*5),
								rotation));
						// ]---
						if(aktFire<0)aktFire=0;
						aktani=1;
						aktbullets--;
						aktFire++;
						if(aktFire <= 3)
						{
							if (aktFire == 1)
								g2.drawImage(fire, 383, 283, 30, 25, imageobserver);
							if (aktFire == 2)
								g2.drawImage(fire, 379, 288, 35, 20, imageobserver);
							if (aktFire == 3)
							{
								g2.drawImage(fire, 379, 278, 35, 30, imageobserver);
								aktFire = 0;
							}
						}
					}
				}
			if(reload)
			{
				if(shoot)
				{
					fromsh = true;
					shoot = false;
				}
				if(reloadc==0)
				{
					aktani = 0;
				}
				else if(reloadc==2)
				{
					aktani = 1;
				}
				else if(reloadc==6)
				{
					if(!fromsh)
						aktani = 0;
					else aktani = 1;
				}
				reloadc++;
				if(reloadc>6)
				{
					reloadc=0;
					reload=false;
					aktbullets=maxbullets;
					if(fromsh)shoot=true;
				}
			}
			
			if(running)
			{
				if (aktframe+1 < animation[0].length)
					aktframe++;
				else
					aktframe = 0;
			} else
			{
				aktframe = 0;
			}
		}
		else
		{
			if (aktframe+1 < animation[2].length-1)
				aktframe++;
			else
				aktframe = animation[2].length-2;
		}
		aktimg = animation[aktani][aktframe];
		shadow.setAktimg(aktimg + 14);
	}
	public void die(int aktani)
	{
		if(alive)
		{
			alive = false;
			this.aktani=aktani;
			aktframe=0;
		}
	}
	public void shoot(boolean b)
	{
		shoot = b;
	}
	public void reload()
	{
		if (aktmagazin - 1 >= 0)
		{
			reload = true;
			aktmagazin--;
		}
	}
	public boolean	reload		= false;
	public int 		reloadc		= 0;
	public boolean	dauerschuss	= false;
	public int 		maxmagazin	= 99;
	public int		aktmagazin	= maxmagazin;
	public int		maxbullets	= 25;
	public int		aktbullets	= maxbullets;
	public boolean	rotate		= false;
	public boolean	right		= true;
	public boolean	shoot		= false;
	public int 		cursorx		= 400;
	public int 		cursory		= 300;
	
	public LinkedList<bullet> bullets = new LinkedList<bullet>();
	
	@Override
	public void onAfterPaintObjectNeutral(Graphics2D g2, ImageObserver imgobserver)
	{
		// Anzeigen:
		g2.setColor(Color.RED);
		g2.setFont(new Font("MisterEarl BT", 0, 20));
		g2.drawString("Magazine " + aktmagazin, 10, 20);
		g2.setFont(new Font("MisterEarl BT", 0, 15));
		g2.drawString("Kugeln      " + aktbullets, 10, 40);
		g2.drawString("Schussmodes " + ((dauerschuss)?("Dauerschuss"):("Einzelschuss")), 10, 55);
		
		// Kugel
		for(int i=0; i<bullets.size(); i++)
		{
			int xx = x+22;
			int yy = y+55;
			bullets.get(i).x -= Math.cos((90 + bullets.get(i).r) * (Math.PI / 180)) * 12;
			bullets.get(i).y -= Math.sin((90 + bullets.get(i).r) * (Math.PI / 180)) * 12;
			if(bullets.get(i).x>800 || bullets.get(i).x<0 || bullets.get(i).y>600 || bullets.get(i).y<0)
				bullets.remove(i);
			else
				if (Math.sqrt(Math.pow(Math.abs(bullets.get(i).x - xx), 2)
						+ Math.pow(Math.abs(bullets.get(i).y - yy), 2)) >= 40)
					g2.fillOval(bullets.get(i).x - 1, bullets.get(i).y - 1, 2, 2);
		}
	}
}
class bullet
{
	public int x, y, i;
	double r;
	public bullet(int x, int y, double r)
	{
		this.x = x;
		this.y = y;
		this.r = r;
	}
}
```

Das Spiel selber:

```
import gamemanager.Key;
import gamemanager.LoadWindow;
import gamemanager.Map;
import gamemanager.PaintManager;
import java.awt.Color;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;

@SuppressWarnings("serial")
public class Java2D_Spiel_Test extends JFrame
{
	public static void main(String[] args)
	{
		new Java2D_Spiel_Test();
	}
	public Java2D_Spiel_Test()
	{
		super("Steev City");
		setSize(800, 600);
		Image icon = null;
		try{
			icon = ImageIO.read(new File("data\\images\\window\\icon.png"));
		} catch (IOException e){}
		setIconImage(icon);
		Image cursor = null;
		try
		{
			cursor = ImageIO.read(new File("data\\images\\game_shooting\\cursor.png").getAbsoluteFile());
		} catch (IOException e1){}
		setCursor(getToolkit().createCustomCursor(new ImageIcon(cursor).getImage(),new Point(0, 0), "Fadekreuz"));
		setLocation(
				(Toolkit.getDefaultToolkit().getScreenSize().width-getWidth())/2,
				(Toolkit.getDefaultToolkit().getScreenSize().height-getHeight())/2);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setResizable(false);
		manageGame();
		setVisible(true);
	}
	
	// Das Spiel:
	private PaintManager	pmgt		= new PaintManager(0, 0, getWidth(),
										  getHeight(), new Color(74, 57,38));
	private boolean			shootmode	= false;
	private Mainfigure		mainfigure	= new Mainfigure(375, 275, 0, 50, 100, 0, pmgt);
	private Map				map;
	public void manageGame()
	{
		LoadWindow lw = new LoadWindow("Bitte warten", "Daten werden geladen...", 0, 73);
		// Laden der Bilder in den Manager
		pmgt.setLoadformtxt(true);
		pmgt.setSourcePath(new File("data\\game\\tiles.txt"));
		pmgt.loadImages();
		int mapimgtotal=pmgt.getTotalimg();
		lw.update("Daten werden geladen...", 43);
		
		pmgt.setSourcePath(new File("data\\game\\figure_001.txt"));
		pmgt.loadImages();
		lw.update("Daten werden geladen...", 58);
		
		pmgt.setSourcePath(new File("data\\game\\figure_shadow_001.txt"));
		pmgt.loadImages();
		lw.update("Daten werden geladen...", 73);
		lw.close();
		// Erstellen der Map
		int[][] map01 =
		{
			{40, 1, 1, 1, 1, 1, 1, 1, 1,41},
			{ 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},
			{42, 1, 1, 1, 1, 1, 1, 1, 1,43},
		};
		map = new Map(0, 0, 0, map01.length, map01[0].length, 100, 100, 0,
						  pmgt, 800, 600, map01, 200, 200);
		pmgt.add(map);
		
		// Erstellen der Animation
		int[][] ani = {{mapimgtotal+1, mapimgtotal+1, mapimgtotal+2, mapimgtotal+2, mapimgtotal+3, mapimgtotal+3, mapimgtotal+4, mapimgtotal+4, mapimgtotal+3, mapimgtotal+3, mapimgtotal+2, mapimgtotal+2},
					   {mapimgtotal+6, mapimgtotal+6, mapimgtotal+7, mapimgtotal+7, mapimgtotal+8, mapimgtotal+8, mapimgtotal+9, mapimgtotal+9, mapimgtotal+8, mapimgtotal+8, mapimgtotal+7, mapimgtotal+7},
					   {mapimgtotal+8, mapimgtotal+8, mapimgtotal+9, mapimgtotal+9, mapimgtotal+10, mapimgtotal+10, mapimgtotal+mapimgtotal, mapimgtotal+mapimgtotal, mapimgtotal+12, mapimgtotal+12, mapimgtotal+13, mapimgtotal+13, mapimgtotal+14, mapimgtotal+14}};
		
		// Verknüpfe die Hauptfigure mit dem Manager
		mainfigure.setAnimation(ani);
		mainfigure.setAktani(0);
		pmgt.add(mainfigure);
		
		// Füge den Manager zu dem Fenster hinzu
		add(pmgt);
		
		// Steuerung
		addSteering();
		addMouseListener();
	}
	public void addMouseListener()
	{
		this.addMouseMotionListener(new MouseMotionListener(){
			public void mouseDragged(MouseEvent e){
				mainfigure.cursorx = e.getX() - 8;
				mainfigure.cursory = e.getY() - 80;
			}
			public void mouseMoved(MouseEvent e)
			{
				mainfigure.cursorx = e.getX() - 8;
				mainfigure.cursory = e.getY() - 80;
			}});
		this.addMouseListener(new MouseListener(){
			public void mouseClicked(MouseEvent e){}
			public void mouseEntered(MouseEvent e){}
			public void mouseExited(MouseEvent e){}
			public void mousePressed(MouseEvent e){
				if(e.getButton()==1)
				{
					shootmode=true;
					mainfigure.setAktani(1);
					mainfigure.shoot(true);
				}
				if(e.getButton()==3)
				{
					shootmode=false;
					mainfigure.setAktani(0);
					if (mainfigure.isAlive())
						if (!mainfigure.reload)
							mainfigure.reload();
				}
			}
			public void mouseReleased(MouseEvent e){
				mainfigure.shoot(false);
				mainfigure.setAktani(0);
				shootmode=false;
			}});
	}
	public void addSteering()
	{
		this.addKeyListener(new KeyListener()
		{
			public void keyPressed(KeyEvent e)
			{
				if(!shootmode && mainfigure.shoot)mainfigure.shoot=false;
				// Schussmodus: GROSS
				if(e.getKeyCode() == Key.GROSS)
				{
					mainfigure.dauerschuss = !mainfigure.dauerschuss;
				}
				// Waffe verbergen/ziehen: E
				if (e.getKeyCode() == Key.E)
				{
					shootmode = !shootmode;
					if (mainfigure.isAlive())
						if (shootmode)
							mainfigure.setAktani(1);
						else
							mainfigure.setAktani(0);
				}
				// Selbstmord: X
				if (e.getKeyCode() == Key.X)
				{
					if (mainfigure.isAlive())
						mainfigure.die(2);
				}
				if (e.getKeyCode() == Key.W)
				{
					map.setX((int)(map.getX() + Math.cos((90+mainfigure.getRotation())*(Math.PI/180))*2));
					map.setY((int)(map.getY() + Math.sin((90+mainfigure.getRotation())*(Math.PI/180))*2));
					mainfigure.forwards = true;
					if (mainfigure.isAlive())
						mainfigure.running = true;
				}
				if (e.getKeyCode() == Key.S)
				{
					map.setX((int)(map.getX() - Math.cos((90+mainfigure.getRotation())*(Math.PI/180))*2));
					map.setY((int)(map.getY() - Math.sin((90+mainfigure.getRotation())*(Math.PI/180))*2));
					mainfigure.forwards = false;
					if (mainfigure.isAlive())
						mainfigure.running = true;
				}
				// Nachladen: R
				if (e.getKeyCode() == Key.R)
				{
					if (mainfigure.isAlive())
						if (!mainfigure.reload)
							mainfigure.reload();
				}
			}
			public void keyReleased(KeyEvent e)
			{
				if(!shootmode && mainfigure.shoot)mainfigure.shoot=false;
				if (e.getKeyCode() == Key.W)
				{
					if (mainfigure.isAlive())
						mainfigure.running = false;
				}
				if (e.getKeyCode() == Key.S)
				{
					mainfigure.running = false;
					if (mainfigure.isAlive())
						mainfigure.running = false;
				}
				// Schiesen/Aktion: Strg
				if (e.getKeyCode() == Key.STRG)
				{
					if (mainfigure.isAlive())
						if (shootmode)
							mainfigure.shoot(false);
				}
			}
			public void keyTyped(KeyEvent e){}
		});
	}
}
```

Danke schon mal im voraus.


----------



## Quaxli (17. Sep 2007)

Ist ja brutal viel Code, wer soll das alles durchackern?   
Es will keiner Dein Spiel komplett nachvollziehen.....

Nichtsdestotrotz habe ich mal ein bißchen drüber geguckt:


```
public void paintComponent(Graphics g)
   {
      BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
      Graphics2D g2dbi = bi.createGraphics();
      g2dbi.setColor(background);
      g2dbi.fillRect(0, 0, getWidth(), getHeight());
      if(antialiasing)
      {
         g2dbi.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
         g2dbi.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
      }
      for (int a = 0; a < objects.size(); a++)
         objects.get(a).paintObject(g2dbi, this);
      
      Graphics2D g2 = (Graphics2D)g;
      g2.drawImage(bi, 0, 0, getWidth(), getHeight(), this);
   }
```

Warum legst Du hier jedesmal ein BufferedImage an? Das sollte man doch weglassen können.

Außerdem würde mich mal die Stelle im Code interessieren, wo Du die Grafikdaten einliest. Sprich die JPG's oder welches Format auch immer, die die Tiles repräsentieren.


----------



## Steev (17. Sep 2007)

Die Daten werden aus txt-Dateien geladen und dann über ImageIO in eine Linked List gepackt.
Der Code ist in der Klasse Paint-Manager in dieser Methode:


```
public void loadImagesFromTXT()
	{
		// ---> suche die Bilder
		final int maxImg = 10000;
		BufferedReader bf;
		String line;
		buffer = new File[maxImg];
		try{
			bf = new BufferedReader(new FileReader(sourcePath.getAbsoluteFile()));
			while ((line = bf.readLine()) != null)
				for (int i = 0; i < allowedimgending.length; i++)
					if (line.indexOf(allowedimgending[i])>=0)
						if(totalimg<=maxImg)
							buffer[totalimg++] = new File(line).getAbsoluteFile();
			bf.close();
		} catch (IOException e){}
		
		// ---> lade die Bilder
		for (int a = 0; a < buffer.length; a++)
			try{
				if (buffer[a] != null)
					img.addLast(ImageIO.read(buffer[a]));
			} catch (IOException e){}
		buffer = null;
	}
```


und wird in der Klasse Java2D_Spiel_Test über die Zeilen:

```
LoadWindow lw = new LoadWindow("Bitte warten", "Daten werden geladen...", 0, 73); 
      // Laden der Bilder in den Manager 
      pmgt.setLoadformtxt(true); 
      pmgt.setSourcePath(new File("data\\game\\tiles.txt")); 
      pmgt.loadImages(); 
      int mapimgtotal=pmgt.getTotalimg(); 
      lw.update("Daten werden geladen...", 43); 
       
      pmgt.setSourcePath(new File("data\\game\\figure_001.txt")); 
      pmgt.loadImages(); 
      lw.update("Daten werden geladen...", 58); 
       
      pmgt.setSourcePath(new File("data\\game\\figure_shadow_001.txt")); 
      pmgt.loadImages(); 
      lw.update("Daten werden geladen...", 73); 
      lw.close();
```

aufgerufen.

Ich hoffe ich habe nicht zu große Fehler gemacht...


----------



## Steev (17. Sep 2007)

Ich habe jetzt mal das Neuanlegen des BufferedImages bi verändert aber es ist immer noch sehr langsam und ruckelig. :autsch: 

Als ich das Zeichnen der Map auskommentiert hatte war das ruckeln weg. Also muss es an der Map liegen.

Vieleich liegt der Fehler in dieser überschriebenen Methode die aus dem PaintManager aufgerufen wird:


```
public void paintObject(Graphics2D g2, ImageObserver imgobserver) 
   { 
      int tempW = ((int) (tileWidth / 100.0 * xscale)), 
         tempH = ((int) (tileHeight / 100.0 * yscale)); 
      int tempx = Math.abs(x / tempW), 
         tempy = Math.abs(y / tempH); 
      for (int x = tempx; x < tempx + (screenWidth / tempW) + 1; x++) 
         for (int y = tempy; y < tempy + (screenHeight / tempH) + 1; y++) 
            if(x < width && y < height) 
            { 
               if (map[x][y] - 1 >= 0 && map[x][y] - 1 <= pmgt.getImg().size()) 
                  if (pmgt.getImg().get(map[x][y] - 1) != null) 
                     g2.drawImage(pmgt.getImg().get(map[y][x] - 1), 
                               this.x + (tempW * x), 
                               this.y + (tempH * y), 
                               tileWidth, tileHeight, imgobserver); 
            } 
      // manage Range 
      if (x > 0)x = 0; 
      if (y > 0)y = 0; 
      if (x + (tempW * width) < screenWidth)x = -((tempW * width) - screenWidth); 
      if (y + (tempH * height) < screenHeight)y = -((tempH * height) - screenHeight); 
   }
```

Ich weis aber ehrlich gesagt nicht wie ich diese Methode optimieren sollte...


-------------------

Fällt mir gerade noch ein:

meint ihr man könnte das ganze durch VolatileImage verschnellern?


----------



## Steev (19. Sep 2007)

Danke für die rege Beteiligung  (Scheint ja keinen zu interessieren)

Das ruckeln habe ich jetzt soweit gelößt, das ich im map-Konstrucktor ein BufferedImage zusammenbaue und dieses zeichne und nicht jedesmal das aktuelle Bild zeichne.

Bei Minimaps von 2.000.000 Pixeln (10x10 Tiles) funktioniert das ja auch ganz schön, aber ich möchte ja gerne Welten bis 1.000x1.000 Tiles darstellen können.
Aber schon bei einer Map von 40*40 Tiles gibt es logischerweise mit dieser Methode einen Heap-Überlauf.
Ich habe daran gedacht die Maps am Anfang in Größere Tiles aufzuteilen und diese einzeln zu rendern. Damit ich diese aus dem Heap bekomme dachte ich daran die Tiles auf die Platte zu schreiben und bei bedarf im Hintergrund zu laden.
Die Bilddarstellung geht jetzt auch viel schneller/besser weil ich das per VolatileImage über die Grafikkarte ablaufen lasse.

Vieleich kann mir ja jemand sagen wie er dies programmieren würde. Ich weis nämlich langsam keine Lösung mehr.

Danke für euer Antwort schonmal im voraus.


----------



## tuxedo (19. Sep 2007)

Steev hat gesagt.:
			
		

> ... auf meinem Laptop (1.5 MHZ) furchtbar ruckelt.



Mit 1,5Mhz würd's bei mir auch ruckeln.. Da ist ja mein Handy etliche hundert male schneller ;-)
Sogar mein SAT-Receiver würde deinen Laptop in den Schatten stellen, ach was sag, ich... Schatten...... Das wäre ne absolut Sonnenfinsternis für die nächsten 50 Jahre ;-)

*Sorry* Das musste sein ;-)

Ohne deinen Code angesehen zu haben: Versuche möglichst das "new XYZ()" Konstrukt in Schleifen zu vermeiden.. Das bremst ungemeint.

- Alex


----------



## Steev (19. Sep 2007)

UPS,
*cry*

Tippfehler, das muss Gig heisen,...

Äh, was für ein "new XYZ()-Konstrukt"?
Ich zeichne in der Schleife die Bilder, was natürlich auch ziemlich bremst,...

Der jetzige Code für das erstellen der Maps (Beschreibung s.O.) sieht wie folgt aus:

```
public Map(int x, int y, int z, int width, int height, double xscale,
			double yscale, double rotation, PaintManager pmgt, int screenWidth,
			int screenHeight, int[][] map, int tileWidth, int tileHeight)
	{
		super(x, y, z, width, height, xscale, yscale, rotation, pmgt);
		this.screenWidth = screenWidth;
		this.screenHeight = screenHeight;
		this.map = map;
		this.tileWidth = tileWidth;
		this.tileHeight = tileHeight;
		
		tempW = ((int) (tileWidth / 100.0 * xscale));
		tempH = ((int) (tileHeight / 100.0 * yscale));
		tempBi = new BufferedImage(width*tempW, height*tempH, BufferedImage.TYPE_INT_RGB);
		gra2d = tempBi.createGraphics();
		for (int xx = 0; xx < width; xx++)
			for (int yy = 0; yy < height; yy++)
				if (map[xx][yy] - 1 >= 0 && map[xx][yy] - 1 <= pmgt.getImg().size())
					if (pmgt.getImg().get(map[xx][yy] - 1) != null)
						gra2d.drawImage(pmgt.getImg().get(map[yy][xx] - 1),
							 			this.x + (tempW * xx),
							 			this.y + (tempH * yy),
							 			tileWidth, tileHeight, null);
	}
	public void paintObject(Graphics2D g2, ImageObserver imgobserver)
	{
		tempW = ((int) (tileWidth / 100.0 * xscale));
		tempH = ((int) (tileHeight / 100.0 * yscale));
		if(tempBi!=null)
			g2.drawImage(tempBi, x, y, width*tempW, height*tempH, imgobserver);
		// manage Range
		if (x > 0)x = 0;
		if (y > 0)y = 0;
		if (x + (tempW * width) < screenWidth)x = -((tempW * width) - screenWidth);
		if (y + (tempH * height) < screenHeight)y = -((tempH * height) - screenHeight);
	}
```

Das übergebene Graphics2D-Objekt für die Methode paintObject kommt zZ aus dem PaintManager und gehört zu einem VolatileImage.

Wie gesagt, dieser Code ist halt nur für Minimaps tauglich...

Danke für deinen Beitrag,
ich hoffe mir kann jemand zu diesem Problem helfen.


----------



## Marco13 (19. Sep 2007)

Ja, was erwartest du? Dass sich jemand zig-tausend Zeilen Code durchliest? Hier lungern ja nicht nur gelangweilte Hartz4ler rum...

Mit
g2.drawImage(tempBi, x, y, width*tempW, height*tempH, imgobserver); 
wird das Bild jedenfalls _kleiner gezeichnet_ - effektiv heißt das, dass es bei jedem Zeichnen suzusagen neu skaliert wird. Das passiert zwar auf einer sehr low-leveligen ebene, und ist darum SEHR schnell, aber immernoch um ein vielfaches langsamer, als einfach ein Bild der passenden Größe zu zeichnen. 

Du könntest evtl. dann, wenn sich width und height ändern, die Bilder so skalieren, dass sie die passende Größe haben, und sie dann nurnohc unskaliert zeichnen. Aber wie und woman das am besten macht... irgendwo in den Weiten deines Codes :wink:


----------



## Steev (19. Sep 2007)

Dankeschön erstmal für deine Antwort, ich werde deinen Vorschlag auf jedem Fall mal ein die Tat umsetzten.

das mit den zig 1000. Zeilen Code am Anfang ist ein Missverständniss,
die wichtigste Funktion habe ich ein paar Beiträge früher gepostet.
Ich dachte am Anfang nur das man mir am schnellsten helfen kann wenn ich die wichtigsten Klassen anhänge,
da man hier aber keine Anhänge posten kann habe ich eben alles in den erste Beitrag gepostet.

Ich hoffe mir kann auch jemand etwas zu obigen Beitrag mit dem neuen Problem etwas sagen.
Wie ist bei so etwas die gängige Vorgehensweise?

Danke schonmal im Voraus

PS:


> Hier lungern ja nicht nur gelangweilte Hartz4ler rum...


 Habe ich nie behauptet, und ich bin auch keiner.


----------



## C++ Pr0gg0r (17. Okt 2007)

Den Batzen Code liest sich keiner durch. Jag' das Ding doch mal durch nen Profiler. Dann sehen wir weiter.


----------



## domar (4. Dez 2007)

Also ich hab den Quelltext mal grob überflogen den du im 1. Beitrag gepostet hast muss aber sagen, dass dieser schon sehr schön und effektiv ist. Die Verbesserungsvorschläge der anderen sind auch toll, aber werden nicht so große auswirkungen  haben.

Das Problem ist eigentlich Java selbst! Java wurde für Officeanwendungen und dergleichen entworfen. Gut, man findet es auch in Handygames, aber in welcher Auflösung und Größe?
Das einzige was mir da noch einfällt ist, alle Dateien, Bilder, Videos, Musik etc. schon bei Programmstart zu laden, oder eine andere Programmiersprache verwenden!

Viel Glück noch mit deinem Spiel! Ich hoffe es wird die gelingen.


----------



## Gast (4. Dez 2007)

Ich denke der Unterschied zu anderen Sprachen wie C++ sind nicht so groß? Der JIT müsste doch grade sowas schon vorhersagbares wunderbar optimieren können.

Wo liegt denn hier der Flaschenhals? Ich will auch was in der Richtung in Java machen, aber wenn das nicht drinn ist habe ich auch kein Problem es gleich in C++ und SDL coden.


----------



## tuxedo (4. Dez 2007)

Es kommt nur auf die richtige Technik an... Wenn Dinge wie ein Quake2-Klon teilweise schneller laufen wie die original C-Implementierung, dann kann man nicht unbedingt sagen dass Java  ungeeignet für Spiele ist. 

Aber ich will hier keinen Glaubenskrieg anzetteln. Wer der meinug ist Java wäre langsam, der soll doch anderswohin wechseln. Jedem das seine, uns das Java ;-)

- Alex


----------



## Quaxli (5. Dez 2007)

Also, ich hab auch schon mal ein Tile-basierten Spielen rumgebastelt und es liegt definitiv nicht an Java.  :noe: 


Ich hab diese Menge an Code jetzt nochmal überflogen und folgendes ist mir noch aufgefallen:

Im PaintManager hast Du folgenden Code:


```
public void update(Graphics g)
   {
      if(dbImage == null)
      {
         dbImage = createImage(this.getSize().width, this.getSize().height);
         dbg      = dbImage.getGraphics();
      }
      dbg.setColor(getBackground());
      dbg.fillRect(200, 100, this.getSize().width, this.getSize().height);
      dbg.setColor(getForeground());
      paint(dbg);
      g.drawImage(dbImage, 0, 0, this);
   }
```

Das ist für eine Doppelbufferung von Hand gedacht. PaintManager erbt von JPanel das schon doppelt gebuffert ist. Den Part solltest Du schon mal rausschmeißen können.


----------

