# "Projekt Library"



## Androbin (16. Mai 2014)

Hallo Leute, :meld:
ich habe dieses Thema eröffnet, um hier mit euch ein kleines Projekt voranzutreiben *8* :hihi:
Das "Projekt Library" :toll: Ja, ich weiß, dass der Name beschissen ist :bahnhof:
Darum geht's *8* Im Rahmen des Java-Forum's möchte ich mit euch eine etwas komplexere 2D-Spiele-Bibliothek erstellen, :idea:
beziehungsweise erweitern, denn ich habe in diese Richtung bereits selbst etwas vorangearbeitet; und nun möchte ich es mit eurer Hilfe ausbauen 

Hier ein paar Klassen *8* :rtfm:



Spoiler: AnimationLib





```
package de.androbin.lib.games;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import javax.imageio.ImageIO;
import com.sun.xml.internal.ws.api.ResourceLoader;

public class AnimationLib {
	
	protected HashMap<String, ArrayList<BufferedImage>> animations;
	
	public void addAnimation( String name, ArrayList<BufferedImage> animation ) { animations.put( name, animation ); }
	
	public void loadAnimation( String name, String path ) {
		
		if ( animations.containsKey( name ) ) return;
		
		ArrayList<BufferedImage> animation = new ArrayList<BufferedImage>();
		
		for ( int i = 0; new File( path + i + ".png" ).exists(); i++ ) {
			
			try { animation.add( ImageIO.read( ResourceLoader.class.getResource( path + i + ".png" ) ) ); }
			catch ( IOException e ) { break; }
			
		}
		
		animations.put( name, animation );
		
	}
	
	public ArrayList<BufferedImage> getAnimation( String name ) { return animations.get( name ); }
	
}
```






Spoiler: Direction





```
package de.androbin.lib.games;

public enum Direction { Up, Down, Left, Right, UpLeft, UpRight, DownLeft, DownRight; }
```






Spoiler: Drawable





```
package de.androbin.lib.games;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.HashMap;

public interface Drawable {
	
	// Deklarationen
	
	int x = 0, y = 0, width = 0, height = 0;
	
	HashMap<Integer, BufferedImage> tiles = new HashMap<Integer, BufferedImage>();
	int pic = 0;
	
	boolean remove = false;
	
	// Methoden
	
	public void paintComponent( Graphics g );
	
}
```






Spoiler: Mob





```
package de.androbin.lib.games;

public class Mob extends Player {
	
	// Deklarationen
	
	private static final long serialVersionUID = 1L;
	
	// Methoden
	
	protected static Direction randomDir() { return Direction.values()[ (int) ( Math.random() * 4 ) ];  }
	
}
```






Spoiler: Movable





```
package de.androbin.lib.games;

public interface Movable {
	
	// Deklarationen
	
	double x = 0, y = 0, width = 0, height = 0;
	boolean remove = false;
	
	// Methoden
	
	public boolean checkCollision( Sprite s );
	
	public void moveTo( Direction dir );
	public void moveTo( double x, double y  );
	
}
```






Spoiler: Player





```
package de.androbin.lib.games;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import javax.imageio.ImageIO;
import com.sun.xml.internal.ws.api.ResourceLoader;

public class Player extends Sprite {
	
	// Deklarationen
	
	private static final long serialVersionUID = 1L;
	
	protected HashMap<String, BufferedImage> tiles = new HashMap<String, BufferedImage>();
	protected String name = "";
	protected Direction dir = Direction.Down;
	
	// Konstruktoren
	
	public Player() { }
	
	public Player( String name, String url, Direction dir, double x, double y, double width, double height ) {
		
		this.name = name; this.dir = dir; this.x = x; this.y = y; this.width = width; this.height = height;
		
		for ( int i = 0; i < Direction.values().length; i++ ) {
			
			for ( int j = 0; j != -1; j++ ) {
				
				try { tiles.put( dirToString( Direction.values()[ i ] ) + j,
						ImageIO.read( ResourceLoader.class.getResource( url + dirToString( Direction.values()[ i ] ) + j + ".png" ) ) ); }
				catch ( Exception e ) { j = -1; }
			}
		
		}
		
	}
	
	// Methoden
	
	public String dirToString() { return dirToString( dir ); }
	
	public static String dirToString( Direction dir ) {
		
		switch ( dir ) {
			
			default : return "";
			
			case Up    : return "U";
			case Down  : return "D";
			case Left  : return "L";
			case Right : return "R";
			
			case UpLeft    : return "UL";
			case UpRight   : return "UR";
			case DownLeft  : return "DL";
			case DownRight : return "DR";
			
		}
		
	}
	
	public void turn( Direction dir ) { this.dir = dir; }
	
	@Override
	public boolean checkCollision( Sprite s ) {
		
		Rectangle2D.Double cut = (Double) this.createIntersection( s );
		
		if ( ( cut.width < 1 ) || ( cut.height < 1 ) ) return false;
		
		Rectangle2D.Double sub_me  = getSubRec( this, cut );
		Rectangle2D.Double sub_him = getSubRec( s   , cut );
		
		BufferedImage img_me  =   tiles.get( dirToString() + pic ).getSubimage( (int) sub_me .x, (int) sub_me .y, (int) sub_me .width, (int) sub_me .height );
		BufferedImage img_him = s.tiles.get(               s.pic ).getSubimage( (int) sub_him.x, (int) sub_him.y, (int) sub_him.width, (int) sub_him.height );
		
		for ( int x = 0; x < img_me.getWidth(); x++ ) {
			
			for ( int y = 0; y < img_him.getHeight(); y++ ) {
				
				int rgb1 = img_me .getRGB( x, y );
				int rgb2 = img_him.getRGB( x, y );
				
				if ( isOpaque( rgb1 ) && isOpaque( rgb2 ) ) { return true; }
				
			}
			
		}
		
		return false;
		
	}
	
	@ Override
	public void moveTo( Direction dir ) { super.moveTo( dir ); this.dir = dir; }
	
	@ Override
	public void moveTo( double x, double y ) { super.moveTo( x, y ); dir = Direction.Down; }
	
	public void moveTo( Direction dir, int x, int y ) { super.moveTo( x, y ); this.dir = dir; }
	
	@Override
	public void paintComponent( Graphics g ) {
		
		Graphics2D g2 = (Graphics2D) g;
		
		if ( pic >=  tiles.size() / Direction.values().length ) pic = 0;
		g2.drawImage( tiles.get( dirToString() + pic++ ), (int) x, (int) y, (int) width, (int) height, null );
		
		if ( animpic == -1 || playAnimation ) {
			
			if ( animpic == -1 ) { animpic = 0; playAnimation = true; }
			
			if ( animpic >=  animation.size() ) { animpic = 0; playAnimation = false; animation.clear(); return; }
			g2.drawImage( animation.get( animpic++ ), (int) x, (int) y, (int) width, (int) height, null );
			
		}
		
		g2.setColor( Color.WHITE );
		g2.setFont( new Font( "TimesRoman", Font.PLAIN, 20 ) );
		g2.drawString( name, (int) x, (int) y - 20 );
		
	}
	
}
```






Spoiler: SoundLib





```
package de.androbin.lib.games;

import java.applet.Applet;
import java.applet.AudioClip;
import java.net.URL;
import java.util.Hashtable;
import java.util.Vector;

public class SoundLib {
	
	Hashtable<String, AudioClip> sounds       = new Hashtable<String, AudioClip>();
	Vector           <AudioClip> loopingClips = new Vector           <AudioClip>();
	
	public void loadSound( String name, String path ) {
		
		if ( sounds.containsKey( name ) ) return;
		
		URL sound_url = getClass().getClassLoader().getResource( path );
		sounds.put( name, Applet.newAudioClip( sound_url ) );
		
	}
	
	public void playSound( String name ) { sounds.get( name ).play(); }
	
	public void loopSound( String name ) {
		
		AudioClip audio = sounds.get( name );
		loopingClips.add( audio );
		audio.loop();
		
	}
	
	public void stopLoopingSounds() { for ( AudioClip c : loopingClips ) c.stop(); }
	
}
```






Spoiler: Sprite





```
package de.androbin.lib.games;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import javax.imageio.ImageIO;
import com.sun.xml.internal.ws.api.ResourceLoader;

public class Sprite extends Rectangle2D.Double implements Drawable, Movable {
	
	// Deklarationen
	
	private static final long serialVersionUID = 1L;
	
	protected double x = 0, y = 0, width = 0, height = 0;
	protected HashMap  <Integer, BufferedImage> tiles     = new HashMap  <Integer, BufferedImage>();
	protected ArrayList<         BufferedImage> animation = new ArrayList<         BufferedImage>();
	protected int pic = 0, animpic = 0;
	
	protected AnimationLib animationlib = new AnimationLib();
	protected     SoundLib     soundlib = new SoundLib    ();
	
	protected boolean playAnimation = false;	
	protected boolean remove        = false;
	
	// Konstruktoren
	
	public Sprite() { }
	
	public Sprite( String url, double x, double y, double width, double height ) {
		
		this.x = x; this.y = y; this.width = width; this.height = height;
		
		for ( int i = 0; i != -1; i++ ) { try { tiles.put( i, ImageIO.read( ResourceLoader.class.getResource( url + i + ".png" ) ) ); } catch ( Exception e ) { i = -1; } }
		
	}
	
	public Sprite( HashMap<Integer, BufferedImage> tiles, double x, double y, double width, double height ) { this.tiles = tiles; this.x = x; this.y = y; this.width= width; this.height = height; }
	
	public Sprite( ArrayList<BufferedImage> animation, double x, double y, double width, double height ) { this.animation = animation; this.x = x; this.y = y; this.width = width; this.height = height; }
	
	public Sprite( String url, Sprite parent ) { this( url, parent.x, parent.y, parent.width, parent.height ); }
	
	// Methoden
	
	@ Override
	public void paintComponent( Graphics g ) {
		
		Graphics2D g2 = (Graphics2D) g;
		
		if ( pic >=  tiles.size() ) pic = 0;
		g2.drawImage( tiles.get( pic++ ), (int) x, (int) y, (int) width, (int) height, null );
		
		if ( animpic == -1 || playAnimation ) {
			
			if ( animpic == -1 ) { animpic = 0; playAnimation = true; }
			
			if ( animpic >=  animation.size() ) { animpic = 0; playAnimation = false; animation.clear(); return; }
			g2.drawImage( animation.get( animpic++ ), (int) x, (int) y, (int) width, (int) height, null );
			
		}
		
	}
	
	public static Rectangle2D.Double getSubRec( Rectangle2D.Double source, Rectangle2D.Double part ) {
		
		Rectangle2D.Double sub = new Rectangle2D.Double();
		
		if ( source.x > part.x ) sub.x = 0;
		else sub.x = part.x - source.x;
		
		if( source.y > part.y ) sub.y = 0;
		else sub.y = part.y - source.y;
		
		sub.width = part.width;
		sub.height = part.height;
		
		return sub;
	}
	
	public static boolean isOpaque( int rgb ) { return !( ( ( rgb >> 24 ) & 0xff ) == 0 ); }
	
	@Override
	public boolean checkCollision( Sprite s ) {
		
		Rectangle2D.Double cut = (Double) this.createIntersection( s );
		
		if ( ( cut.width < 1 ) || ( cut.height < 1 ) ) return false;
		
		Rectangle2D.Double sub_me  = getSubRec( this, cut );
		Rectangle2D.Double sub_him = getSubRec( s   , cut );
		
		BufferedImage img_me  =   tiles.get(   pic ).getSubimage( (int) sub_me .x, (int) sub_me .y, (int) sub_me .width, (int) sub_me .height );
		BufferedImage img_him = s.tiles.get( s.pic ).getSubimage( (int) sub_him.x, (int) sub_him.y, (int) sub_him.width, (int) sub_him.height );
		
		for ( int x = 0; x < img_me.getWidth(); x++ ) {
			
			for ( int y = 0; y < img_him.getHeight(); y++ ) {
				
				int rgb1 = img_me .getRGB( x, y );
				int rgb2 = img_him.getRGB( x, y );
				
				if ( isOpaque( rgb1 ) && isOpaque( rgb2 ) ) { return true; }
				
			}
			
		}
		
		return false;
		
	}
	
	@ Override
	public void moveTo( Direction dir ) {
		
		switch ( dir ) {
			
			default : return;
			
			case Up        :      y--; return;
			case Down      :      y++; return;
			case Left      : x--;      return;
			case Right     : x++;      return;
			
			case UpLeft    : x--; y--; return;
			case UpRight   : x++; y--; return;
			case DownLeft  : x--; y++; return;
			case DownRight : x++; y++; return;
			
		}
		
	}
	
	@ Override
	public void moveTo( double x, double y ) { this.x = x; this.y = y; }
	
	protected void playAnimation( ArrayList<BufferedImage> animation ) { this.animation = animation; animpic = -1;; }
	
	protected void loadSound( String name, String path ) { soundlib.loadSound( name, path ); }
	protected void playSound( String name              ) { soundlib.playSound( name       ); }
	
	protected boolean  remove() { return remove       ; }
	protected void    destroy() {        remove = true; }
	
	protected void explode() { playAnimation( animationlib.getAnimation( "explosion" ) ); destroy(); }
	
}
```






Spoiler: World





```
package de.androbin.lib.games;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.JPanel;

public class World extends JPanel implements KeyListener, Runnable, Serializable {
	
	// Deklarationen
	
	private static final long serialVersionUID = 1L;
	
	protected ArrayList<Sprite> objects = new ArrayList<Sprite>();
	protected ArrayList<Mob   > mobs    = new ArrayList<Mob   >();
	protected Player            player  = new Player( "", "", Direction.Down, 0, 0, 0, 0 );
	
	protected BufferedImage background;
	
	protected AnimationLib animationlib = new AnimationLib();
	protected SoundLib         soundlib = new SoundLib    ();
	
	// Konstruktoren
	
	public World() { }
	
	public World( Player player ) { this.player = player; new Thread( this ).start(); }
	
	// Methoden
	
	public void changeBackground( BufferedImage background ) { this.background = background; }
	
	public void addObject( Sprite s ) { objects.add( s ); }
	public void addMob   ( Mob    m ) { mobs   .add( m ); }
	
	public boolean moveTo( Sprite s, Direction dir ) {
		
		for ( int i = 0; i < objects.size(); i++ ) if ( !s.checkCollision( objects.get( i ) ) ) return false;
		for ( int i = 0; i < mobs   .size(); i++ ) if ( !s.checkCollision( mobs   .get( i ) ) ) return false;
		
		s.moveTo( dir ); return true;
		
	}
	
	public void moveAt( Sprite s, int x, int y ) { s.moveTo( x, y ); }
	
	@Override
	public void paintComponent( Graphics g ) {
		
		super.paintComponent( g );
		
		Graphics2D g2 = (Graphics2D) g;
		
		g.drawImage( background, 0, 0, getWidth(), getHeight(), null );
		
		for ( int i = 0; i < objects.size(); i++ ) { objects.get( i ).paintComponent( g2 ); }
		for ( int i = 0; i < mobs   .size(); i++ ) { mobs   .get( i ).paintComponent( g2 ); }
		                                             player          .paintComponent( g2 );
		
	}
	
	public void  addAnimation( String name, ArrayList<BufferedImage> animation ) { animationlib. addAnimation( name, animation ); }
	public void loadAnimation( String name, String                   path      ) { animationlib.loadAnimation( name, path      ); }
	public void playAnimation( Sprite s   , ArrayList<BufferedImage> animation ) { s.playAnimation(                  animation ); }
	
	public void loadSound( String name, String path ) { soundlib.loadSound( name, path ); }
	public void playSound( String name              ) { soundlib.playSound( name       ); }
	public void loopSound( String name              ) { soundlib.loopSound( name       ); }
	public void stopLoopingSounds(                  ) { soundlib.stopLoopingSounds(    ); }
	
	@Override
	public void run() {
		
		while ( true ) {
			
			try { Thread.sleep( 500 ); } catch ( InterruptedException e ) { e.printStackTrace(); }
			
			for ( int i = 0; i < objects.size(); i++ ) if ( objects.get( i ).remove() ) objects.remove( i );
			for ( int i = 0; i < mobs   .size(); i++ ) if ( mobs   .get( i ).remove() ) mobs   .remove( i );
			
			for ( int i = 0; i < mobs   .size(); i++ ) moveTo( mobs.get( i ), Mob.randomDir() );
			
			repaint();
			
		}
		
	}
	
	@ Override
	public void keyPressed( KeyEvent e ) {
		
		switch ( e.getKeyCode() ) {
			
			case KeyEvent.VK_W     : moveTo( player, Direction.Up    ); break;
			case KeyEvent.VK_A     : moveTo( player, Direction.Left  ); break;
			case KeyEvent.VK_S     : moveTo( player, Direction.Down  ); break;
			case KeyEvent.VK_D     : moveTo( player, Direction.Right ); break;
			
			case KeyEvent.VK_UP    : moveTo( player, Direction.Up    ); break;
			case KeyEvent.VK_DOWN  : moveTo( player, Direction.Down  ); break;
			case KeyEvent.VK_LEFT  : moveTo( player, Direction.Left  ); break;
			case KeyEvent.VK_RIGHT : moveTo( player, Direction.Right ); break;
			
		}
		
	}
	
	@ Override
	public void keyReleased( KeyEvent e ) { }
	
	@ Override
	public void keyTyped( KeyEvent e ) { }
	
}
```




[TIPP]
Inspiriert dazu wurde ich durch Quaxli's Spiele-Tutorial
[/TIPP]

Im Anhang befindet sich außerdem der zugehörige Eclipse-workspace opcorn:


----------



## MrXYF (16. Mai 2014)

Find ich ne gute Sache, werd mal schaun, ob ich was beitragen kann :toll:


----------



## Tobse (16. Mai 2014)

Also das kommt jetzt wieder total s******e von mir... aber ganz im Ernst: Das Design der Klassen, die du da präsentierst ist völlig unbrauchbar. Das Beginnt schon damit dass das "abstrahieren" einer Animation durch das zerlegen in mehrere BufferedImages einen Vorteil von exakt 0 hat und geht damit weiter, dass die Welt von der Darstellungs-Bilbiothek erbt. Sorry, aber das Teil wird auf dieser Basis nichts ausser ein instabiles irgendwas voller Pflaster und Bugfixes.

Aber anstadt blos rum zu meckern sag ich euch auch, wo lang es in einen bessere Richtung geht:

1. Die Klasse Direction. Es giebt hier nur 8 verschiedene Richtungen - das ist völlig unzureichend und damit lässt sich auch nicht entsprechend Rechnen.

```
class Vector
{
    protected float dX;
    protected float dY;

    public Vector(float dX, float dY)
    {
        this.dX = dX;
        this.dY = dY;
    }
    
    // müssen implementiert werden
    public Vector add(Vector v);
    
    public Vector subtract(Vector v);
    
    // etliche andere Operationen der Vektorrechnung
}
```

Und damit wird dann aus dem Animations-Teil auch ein Schuh:

```
class AnimatedMovement
{
     protected Drawable object;
     protected Vector direction;
     protected float distance;
     
     // plus eine Klasse über welche der Verlauf der Bewegung beeinflusst werden kann,
     // änlich wie das Easing-Prinzip bei jQuery UI: [url=http://api.jqueryui.com/easings/]Easings | jQuery UI API Documentation[/url]
}
class Animation
{
    protected Movement[] movements;
    
    public void play();
    public void play(int durationMillis);
}
```

2. Die Klasse World
Es beginnt schon damit, dass eine 2D-Library möglichst vielfältig einsetzbar sein muss. Indem man sie an ein Framework wie Swing bindet ist das schonmal unmöglich.

```
class World
{
    protected ArrayList<Drawable> objects;

    public void render(Graphics g, Rectangle visibleArea);
}
```


----------



## lord239123 (17. Mai 2014)

Hier sind  2 Soundklassen. 
Die eine spielt einmalig einen Sound, währed die andere ihn dauernd wiederholt.


```
package main;

import java.io.File; 

import java.io.IOException; 
import javax.sound.sampled.*; 
 
/*
 * Spielt ein Musikstück oder einen Sound mehrmals hintereinander ab
 */
public class MusikPlayer extends Thread { 
    private File sf;
    private final int EXTERNAL_BUFFER_SIZE = 524288;
    private SourceDataLine line = null;
    private AudioInputStream ais = null;
             
    public MusikPlayer(String path) 
    {
        try {
            sf = new File(path);
        } catch (Exception e) {
            System.out.println("No Wave file found at :" + path);
        }
        start();
    }
    
    public void init() 
    {
        long now = System.currentTimeMillis();
        if (!sf.exists()) {
            System.out.println("Wave file not found: " + sf.getName());
            return;
        }
        
        try {
            ais = AudioSystem.getAudioInputStream(sf);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        
        AudioFormat format = ais.getFormat();
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
        
        try {
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(format);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        FloatControl gain = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
        gain.setValue(gain.getMaximum());
    }
    
    @Override
    public void run() 
    {
    	while(true)
    	{
            init();

	    	try {
				sleep(500);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
	    	
	        line.start();
	        int nBytesRead = 0;
	        byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
	        try {
	            while(nBytesRead != -1) {
	                nBytesRead = ais.read(abData, 0, abData.length);
	                if (nBytesRead >= 0) {
	                    line.write(abData,  0, nBytesRead);
	                } 
	            }
	        } catch (Exception e) {
	            e.printStackTrace();
	            return;
	        } finally {
	            line.drain();
	            line.close();
	        }
    	}
    }
}
```


```
package main;

import java.io.File;
import javax.sound.sampled.*;

/*
 * Spiel ein Musikstück oder einen Sound einmalig ab
 */
public class SoundPlayer extends Thread { 
    private File sf;
    private final int EXTERNAL_BUFFER_SIZE = 524288;
    private SourceDataLine line = null;
    private AudioInputStream ais = null;
             
    public SoundPlayer(String path) 
    {
        try {
            sf = new File(path);
        } catch (Exception e) {
            System.out.println("No Wave file found at :" + path);
        }
        init();
        start();
    }
    
    public void init() 
    {
        long now = System.currentTimeMillis();
        if (!sf.exists()) {
            System.out.println("Wave file not found: " + sf.getName());
            return;
        }
        
        try {
            ais = AudioSystem.getAudioInputStream(sf);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        
        AudioFormat format = ais.getFormat();
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
        
        try {
            line = (SourceDataLine) AudioSystem.getLine(info);
            line.open(format);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        FloatControl gain = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN);
        gain.setValue(gain.getMaximum());
    }
    
    @Override
    public void run() 
    {
    	try {
			sleep(1500);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
    	
        line.start();
        int nBytesRead = 0;
        byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
        try {
            while(nBytesRead != -1) {
                nBytesRead = ais.read(abData, 0, abData.length);
                if (nBytesRead >= 0) {
                    line.write(abData,  0, nBytesRead);
                } 
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        } finally {
            line.drain();
            line.close();
        }
    }
}
```


----------

