# 2D platformer - enemy damage -> TIMER PROBLEM



## RailRunner (26. Nov 2014)

Halli Hallo,

Ich bin neu hier und auch mit Java noch Anfänger, allerdings mit der Hilfe von Büchern, youtube und google in den letzten Monaten gut alleine voran gekommen.

Nach diversen angefangenen Spielkonzepten habe ich nun mit der Grundlegenden Materie beschäftigt um den Kern der Sache stück für stück zu checken.

Ich arbeite mit einem Applet und habe ein 2D platformer im visier, alles läuft bisher gut voran, auch wenn der code langsam verschlungener wird. Ich habe bisher einen Gameloop, einen steuerbaren Spieler mit gekoppelter Grafik (Background) und meinen ersten Gegner.

Die Kollision ist noch ein Problem dazu später mehr, denn nachdem alles soweit lief wollte ich mich mit Timern beschäftigen und dieses Werkzeug im Gebrauch lernen.

Dazu habe ich mir überlegt (am besten Anhang runterladen und mitlesen, der code ist (glaube ich) sehr übersichtlich ,
ruer die faulen hier die Gegner classe:

[Java]
package maincode;

import java.awt.Rectangle;
import java.util.Timer;
import java.util.TimerTask;

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GEGNER - KLASSE										////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////


public class Gegner {




	static int GegnerAnzahl = 0;

	private int gegnerx;
	private int gegnery;

	private int hp;
	public static int dmg = 10;

	private int runningspeed = 1;
	private int speedx = runningspeed;
	public Rectangle rectG = new Rectangle(gegnerx - 16, gegnery - 16,  35, 35); 
	private boolean att = false;
	public static Timer timer ;
	private TimerTask attacking;

	public Gegner(){	


		if (GegnerAnzahl<1){

			//gegner soll ueber dem screen und vor dem spieler spawnen
			gegnerx = Starting.getspielerX()+300;
			gegnery = -20;

			//gegner anzahl zaehlen
			GegnerAnzahl++;

			//timer initialisieren
			attacking = new TimerTask() {

		        @Override
		        public void run() {
		            if(!att) {

		            } else {

		            	System.out.println("Kollision");
		            	Starting.setspielerhp(dmg);
		            	Starting.clip.play();

		            }
		        }

			};


		}
	}



	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GEGNER - UPDATE POSITION							////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////



	public void update() {

		//checkt ob spieler in der naehe und falls true -> att==true -> timer maccht dmg starte timer
		if(gegnerx <= Starting.getspielerX() + 40 && gegnerx >= Starting.getspielerX() -40 && Starting.getspielerY() >= 384){

			if(att==false){
				att=true;
				timer = new Timer();
				timer.schedule(attacking, 10, 1000);
			}
			//erhalte dmg
			att = true;

		}else{
			att = false;
		}



		//fallen lassen nach spawnen
		if(gegnery + 2 >= 412){
			gegnery = 412;
		}else {
			gegnery += 5;
		}

		//spieler folgen
		if(gegnerx -32 > Starting.getspielerX()){
			speedx = runningspeed;
			gegnerx -= speedx;

		}else if(gegnerx +32 < Starting.getspielerX()){
			speedx = runningspeed;
			gegnerx += speedx;
		}else if (gegnerx +32 == Starting.getspielerX()){
			speedx=0;
		}else if (gegnerx -32 == Starting.getspielerX()){
			speedx=0;

		}


	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GETTER - SETTER - METHODEN							////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	public static int getGegnerAnzahl() {
		return GegnerAnzahl;
	}


	public int getGegnerx() {
		return gegnerx;
	}


	public int getGegnery() {
		return gegnery;
	}


	public int getHp() {
		return hp;
	}


	public int getDmg() {
		return dmg;
	}


	public int getSpeedx() {
		return speedx;
	}

	public Rectangle getRectG() {
		return rectG;
	}

	public static void setGegnerAnzahl(int gegnerAnzahl) {
		GegnerAnzahl = gegnerAnzahl;
	}


	public void setGegnerx(int gegnerx) {
		this.gegnerx = gegnerx;
	}


	public void setGegnery(int gegnery) {
		this.gegnery = gegnery;
	}


	public void setHp(int hp) {
		this.hp = hp;
	}

	public void setSpeedx(int speedx) {
		this.speedx = speedx;
	}





}
[/Java]

Der Gegner der spawnt und auf den Spieler läuft soll sobald er neben bzw im spieler steht dem spieler 10 hp abziehen, im Takt von 1ner Sekunde.
->kollision:
zuerst habe ich um den Spieler und den Gegner 1 Rechteck gezeichnet. diese dann in einer Methode rect.intersects(rect) geprüft, allerdings hat er mir dann ausgegeben (Konsole) kollison!. allerdings hat er timer.schedule nicht gemacht. bzw auf nichts anderes reagiert Oo

also habe ich zunächst die Kollision mit Koordinaten Überprüfung abgefragt und dann
den timer gestartet. das laeuft! nun zum problem:

die Timertask attacking initialisiere ich im constructor des Gegners. ich habe zuerst den timer auch
im konstructor mit (=new Timer) erzeugt und mit timer.schedule gestartet.
dann in der Kollisionsabfrage den wert von der run() Bedingung geändert dass er dmg macht.
das problem hier ist das es ungenau ist, denn der timer läuft bereist am Anfang wenn der Gegner spawnt. renne ich nun in den Gegner dann kann es sein dass vor 0,1 sec (letzte startinterval) der letzte durchlauf war und ich 0,9sec +0,1sec (wiederholungsinterval)+(startinterval) , also 1 sec warten muss bevor die dmg anfängt.

daraufhin habe ich den timer in der kollsions abfrage Methode erzeugt und die Bedingung true
gesetzt. die kolliosonsabfrage ist in der update Methode und wird ständig aufgerufen.

lauf ich nun in den Gegner fängt der timer direkt an, lauf ich raus hört er auf.........
ABER lauf ich wieder rein fanget er auf und das applet hängt. 


Ich habe euch anbei den gesamten Ordner beigefügt (entschuldigt die schlechte Grafiken) 
damit ihr es ausprobieren könnt.

ich brauche eine Lösung den timer zu canceln oder löschen (habe gestern stunden nachgelesen und gegooglet aber werde schlau daraus wie dieser timer thread läuft bzw wann er gelöscht ist wann man keinen neuen starten kann bzw wann man das muss um ihn neuzustarten) wenn der spieler
aus dem Gegner läuft und einen neuen zu Starten wenn er reinläuft.

Liebe Grüsse


----------



## RailRunner (26. Nov 2014)

ladet euch den Anhang runter und öffnet ihn (zb eclipse) dann könnt ihr es "spielen" und dann versteht sich der fehler am einfachsten...


----------



## Androbin (27. Nov 2014)

Anstatt eines Timers könntest du doch auch einfach einen Thread benutzen...


----------



## RailRunner (27. Nov 2014)

kannst du das genauer erklären? bisher habe ich einen thread ---den game loop.
zu threads weiss ich nur dass sie "simultan" ablaufen... aber startet man thread1 und zwei dann arbeiten sie quasi hintereinander. leider fehlt mir da noch das wissen darum.

ich habe es nun ziemlich aufwendig gelöst aber wäre echt dankbar wenn mir jemand eine einfachere Variante zeigt, da ich diese menge an code nicht für jedes Objekt wiederholen kann das einen timer braucht.

hier dazu die beiden Klassen die ich dazu verändert habe. ich starte quasi einen timer den ich im selben aufruf ( run Methode) auch wieder cancel, setze dabei eine timerZ(ahl) variable auf 1, spring in den nächsten block, führe den zweiten, sich wiederholenden timer aus, setze dabei timerZ auf 2 dass er nicht während der update Methode neue timer macht, und wenn der spieler dann den Gegner verlässt springt er in den (if (timerZ==2)) block und löscht ihn.

stehe ich nun im Gegner und drücke pause, läuft der dmg timer weiter was ja nicht sinn und zweck der sache ist, deshalb wird im gameloop für den fall das pause eintritt wieder ein Haufen code ausgeführt der verkoppelt mit der playeralive (gameloop-run) Methode den timer löscht und neu startet (timerZ=0)

aber ich will mir gar nicht vorstellen wie viele falle es gibt die man bei einer pause dann berücksichtigen muss, so kann das aufjeden fall keines Wegs effizient sein, aber laufen tuts:

anbei die beiden veränderten Klassen Starting und Gegner, der rest ist der selbe wie oben im  Paket:


Gegner
[Java]package maincode;

import java.awt.Rectangle;
import java.util.Timer;
import java.util.TimerTask;

	////////------------------------------------------------------------------------------------------------////////
	////////									GEGNER - KLASSE												////////
	////////------------------------------------------------------------------------------------------------////////


public class Gegner {




	static int GegnerAnzahl = 0;

	private int gegnerx;
	private int gegnery;

	private int hp;
	public static int dmg = 10;

	private int runningspeed = 1;
	private int speedx = runningspeed;
	public Rectangle rectG = new Rectangle(gegnerx - 16, gegnery - 16,  35, 35); 
	public boolean att = false;
	public static Timer timer ;
	private TimerTask attacking;

	public static int timerZ = 0;

	public Gegner(){	


		if (GegnerAnzahl<1){

			//gegner soll ueber dem screen und vor dem spieler spawnen
			gegnerx = Starting.getspielerX()+300;
			gegnery = -20;

			//gegner anzahl zaehlen
			GegnerAnzahl++;



		}
	}



	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GEGNER - UPDATE POSITION							////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////



	public void update() {

		//checkt ob spieler in der naehe und falls true -> att==true -> timer maccht dmg starte timer
		if(gegnerx <= Starting.getspielerX() + 40 && gegnerx >= Starting.getspielerX() -40 && Starting.getspielerY() >= 384){

			if(att == false){
				att = true;


				if(timerZ == 0){
			//----------------------
				timer = new Timer();
					timer.schedule(
					//timer initialisieren
					attacking = new TimerTask() {

					        @Override
					        public void run() {
					            timerZ = 1;
					            timer.cancel();
					        	if(!att) {

					            } else {

					            	System.out.println("Kollision");
					            	Starting.setspielerhp(dmg);
					            	Starting.clip.play();

					            }
					        }

							},10);
			//----------------------

			}}else if (att == true){


				//erhalte dmg
				if(timerZ == 1){
					timerZ = 2;
			//----------------------
					timer = new Timer();
					timer.schedule(
					attacking = new TimerTask() {

						        @Override
						        public void run() {

						        	if(!att) {

						            } else {

						            	System.out.println("Kollision");
						            	Starting.setspielerhp(dmg);
						            	Starting.clip.play();

						            }
						        }

						},1000,1000);
			//----------------------
				}
			}

		}else{
			if(timerZ == 2){
				att = false;
				timer.cancel();
				timerZ = 0;
			}
		}






		//fallen lassen nach spawnen
		if(gegnery + 2 >= 412){
			gegnery = 412;
		}else {
			gegnery += 5;
		}

		//spieler folgen
		if(gegnerx -32 > Starting.getspielerX()){
			speedx = runningspeed;
			gegnerx -= speedx;

		}else if(gegnerx +32 < Starting.getspielerX()){
			speedx = runningspeed;
			gegnerx += speedx;
		}else if (gegnerx +32 == Starting.getspielerX()){
			speedx=0;
		}else if (gegnerx -32 == Starting.getspielerX()){
			speedx=0;

		}


	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GETTER - SETTER - METHODEN							////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////


	public static int getGegnerAnzahl() {
		return GegnerAnzahl;
	}


	public int getGegnerx() {
		return gegnerx;
	}


	public int getGegnery() {
		return gegnery;
	}


	public int getHp() {
		return hp;
	}


	public int getDmg() {
		return dmg;
	}


	public int getSpeedx() {
		return speedx;
	}

	public Rectangle getRectG() {
		return rectG;
	}

	public static void setGegnerAnzahl(int gegnerAnzahl) {
		GegnerAnzahl = gegnerAnzahl;
	}


	public void setGegnerx(int gegnerx) {
		this.gegnerx = gegnerx;
	}


	public void setGegnery(int gegnery) {
		this.gegnery = gegnery;
	}


	public void setHp(int hp) {
		this.hp = hp;
	}

	public void setSpeedx(int speedx) {
		this.speedx = speedx;
	}





}[/Java]

Starting
[Java]package maincode;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;

public class Starting extends Applet implements Runnable, KeyListener {

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							PLATFORM RUNNER GAME								////////////////
	////////////////							STARTING CLASS										////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	boolean gameopen=true;
	boolean playerlives=false;

	private static Spieler Spieler1;
	private static Gegner Gegner1;
	private Image image, character ,brickwand, backgroundbild, gegner ;
	private Graphics second;
	private URL base ;

	public static AudioClip clip, jump;

	private static Background background1, background2;
	private static boden boden1;
	private static PauseMenue PauseObj;

	private String SpielerName="Player 1";
	private Font spielerschrift = new Font("Arial Black", Font.BOLD, 15);

	private int pausezaehler = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							APPLET - INIT START STOP DESTROY					////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	@Override
	public void init() {
		setSize(800, 480);
		setBackground(Color.BLACK);
		setFocusable(true);
		addKeyListener(this);
		Frame frame = (Frame) this.getParent().getParent();
		frame.setTitle("Brickrunner 0.1");
		frame.setResizable(false);
		try {
			base = getDocumentBase();
		} catch (Exception e) {
			// TODO: handle exception
		}

		// Bildeinstellung
		character = getImage(base, "data/characterhead.jpg");
		brickwand = getImage(base, "data/brickwand.jpg");
		backgroundbild = getImage(base, "data/background.png");
		gegner = getImage(base, "data/gegner.gif");
		clip = getAudioClip(base, "data/enemyhit.wav");
		jump = getAudioClip(base, "data/jump.wav");
	}

	@Override
	public void start() {
		Spieler1 = new Spieler();
		Rectangle rectSt = Spieler1.getRectS();
		Gegner1 = new Gegner();
		background1 = new Background(0,0);
		background2 = new Background(2160, 0);
		boden1 = new boden(0,428);
		PauseObj =new PauseMenue();
		Thread thread = new Thread(this);
		thread.start();

	}

	@Override
	public void stop() {

	}

	@Override
	public void destroy() {

	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GAME LOOP - RUN METHODE								////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	@Override
	public void run() {

		while(gameopen){

			if(playerlives){

				//aktion

				//System.out.println("running");


				//time restart handling nach pause
				if(pausezaehler == 1){
					System.out.println("timer neustart");
					Gegner.timerZ = 0;
					pausezaehler = 0;

				}

				//objekt update
				background1.update();
				background2.update();
				Spieler1.update();
				Gegner1.update();
				boden1.update();


				repaint();


				 try {
					Thread.sleep(17);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}else{

				//timer stoppen
				if(Gegner.timerZ == 2 || Gegner.timerZ == 1){
				Gegner.timer.cancel();
				pausezaehler = 1;
				Gegner1.att=false;
				}

				//pause grafik
				PauseObj.PauseZeichnen(getGraphics());

				try {
					Thread.sleep(100);

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}


		}

	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GRAFIK - UPDATE METHODE								////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	@Override
	public void update(Graphics g) {
		if (image == null) {
			image = createImage(this.getWidth(), this.getHeight());
			second = image.getGraphics();
		}

		second.setColor(getBackground());
		second.fillRect(0, 0, getWidth(), getHeight());
		second.setColor(getForeground());
		paint(second);

		g.drawImage(image, 0, 0, this);

	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							GRAFIK - PAINT METODE								////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	@Override
	public void paint(Graphics g) {
		if(playerlives){

		g.drawImage(backgroundbild, background1.getBgx(), background1.getBgy(), this);
		g.drawImage(backgroundbild, background2.getBgx(), background2.getBgy(), this);
		g.drawImage(gegner, Gegner1.getGegnerx() - 16, Gegner1.getGegnery() - 16, this);
		g.drawImage(character, Spieler1.getCenterX() - 16, Spieler1.getCenterY() - 16, this);
		//g.drawRect(Gegner1.getGegnerx()-16, Gegner1.getGegnery() - 16,  35, 35);
		//g.drawRect(Spieler1.getCenterX()-16, Spieler1.getCenterY() - 16,  35, 35);
		g.drawImage(brickwand, boden1.getBodenx(), boden1.getBodeny(), this);

		g.setColor(Color.BLACK);
		g.setFont(spielerschrift);
		int SpielerNameLaenge = (int) g.getFontMetrics().getStringBounds(SpielerName, g).getWidth();
		g.drawString(SpielerName, Spieler1.getCenterX() -(SpielerNameLaenge/2) , Spieler1.getCenterY() - 30);
		g.drawString("Health: " + String.valueOf(Spieler1.getHP()), 20, 30);
		}


	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							KEYLISTENER - METHODE								////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	@Override
	public void keyTyped(KeyEvent e) {
	// TODO Auto-generated method stub

	}

	@Override
	public void keyPressed(KeyEvent e) {
	//System.out.println(e.getKeyCode());


		switch (e.getKeyCode()) {

		   case KeyEvent.VK_S:
			   playerlives=!playerlives;
		   break;

		   case KeyEvent.VK_UP:
			   System.out.println("Hoch");
			   Spieler1.jump();

		   break;

		   case KeyEvent.VK_DOWN:
			   System.out.println("Runter");

		   break;

		   case KeyEvent.VK_LEFT:
			   System.out.println("Links");
			   Spieler1.moveLeft();
		   break;

		   case KeyEvent.VK_RIGHT:
			   System.out.println("Rechts");
			   Spieler1.moveRight();
		   break;

		   case KeyEvent.VK_SPACE:
		   break;
		   }

	}

	@Override
	public void keyReleased(KeyEvent e) {

		switch (e.getKeyCode()) {
		   case KeyEvent.VK_UP:
			   System.out.println("Stop dich zu bewegen");
		      break;

		   case KeyEvent.VK_DOWN:
			   System.out.println("Stop dich zu bewegen");
		      break;

		   case KeyEvent.VK_LEFT:
			   System.out.println("Stop dich zu bewegen");
			   Spieler1.stop();
		      break;

		   case KeyEvent.VK_RIGHT:
			   System.out.println("Stop dich zu bewegen");
			   Spieler1.stop();
		      break;

		   case KeyEvent.VK_SPACE:
			   System.out.println("Stop zu springen");
		      break;

		   }
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	////////////////							SETTER - GETTER	- METHODEN							////////////////
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	public static Background getBackground1() {
		return background1;
	}

	public static Background getBackground2() {
		return background2;
	}

	public static int getspielerspeed() {
		return Spieler1.getSpeedX();
	}

	public static int getspielerhp(){
		return Spieler1.getHP();
	}

	public static int getspielerX() {
		return Spieler1.getCenterX();
	}

	public static int getspielerY() {
		return Spieler1.getCenterY();
	}

	public static Rectangle getRectSt() {
		return Starting.Spieler1.rectS;
	}

	public static void setspielerhp(int damage){
		Spieler1.setHP(damage);
	}

	public static void setBackground1(Background background1) {
		Starting.background1 = background1;
	}

	public static void setBackground2(Background background2) {
		Starting.background2 = background2;
	}

}
[/Java]


----------

