fleur: SVG-Game: Problem mit der Kollisionserkennung/ceckIntersection()

Hallo ihr Lieben,

Ich hoffe das mir vielleicht irgendwer helfen kann.
Ich versuche im Moment mit Hilfe von SVG und Java-Script ein kleines Spiel zu entwerfen. Geplant ist das man einen kleinen Fisch durch eine Unterwasserwelt steuern und dabei Münzen oä. einsammeln muss.
Bewegen lässt sich der Fisch mittlerweile mit Hilfe der Pfeiltasten (das war vielleicht ein Kampf^^).

Das Problem ist, dass ich es nicht schaffe, dass die Kollision mit einem Hindernis auch bemerkt wird.
Ich habe versucht das Ganze mit Hilfe von:
   boolean checkIntersection ( in SVGElement element, in SVGRect rect );
da es nicht funktioniert, bin ich mir ziemlich sicher, das ich irgendwas falsch mache.

Wenn ich das richtig verstehe kann man damit feststellen, ob ein beliebiges Objekt (z.B. ein Kreis) sich mit einem beliebigen Rechteck schneidet, oder hab ich da was falsch verstanden?
Hat vielleicht irgendwer ein einfaches Beispiel zur Hand?

Vielen Dank schon mal für die hoffentlich sehr hilfreichen Antworten! ;)

Fleur

  1. Hallo Fleur,

    Wenn ich das richtig verstehe kann man damit feststellen, ob ein beliebiges Objekt (z.B. ein Kreis) sich mit einem beliebigen Rechteck schneidet, oder hab ich da was falsch verstanden?
    Hat vielleicht irgendwer ein einfaches Beispiel zur Hand?

    Wenn es überhaupt unterstützt wird, dann so:

    <?xml version="1.0" encoding="UTF-8"?>  
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">  
    <svg xmlns="http://www.w3.org/2000/svg">  
      
      <defs>  
        <script type="text/javascript"><![CDATA[  
      
        window.onload=function()  
        {  
          // SVG-Wurzelelement-Objekt  
          var r=document.documentElement;  
      
          // Kreis-Objekt  
          var k=document.getElementById("k");  
      
          // Rechteck-Objekt = zu testender Intersection-Bereich  
          var rect=r.createSVGRect();  
          rect.x=5;  
          rect.y=5;  
          rect.width=300;  
          rect.height=200;  
      
          var check=r.checkIntersection(k,rect);  
          alert(check); // true bzw. false  
      
          // Hinweis: analoges Vorgehen mit r.checkEnclosure()  
        }  
      
        ]]></script>  
      
      </defs>  
      
      <!-- das Test-Objekt -->  
      <circle id="k" r="20" cx="100" cy="100" fill="#F00"/>  
      <!-- der Prüfbereich, nur zur Veranschaulichung -->  
      <rect x="5" y="5" width="300" height="200" fill="none" stroke="#CCC"/>  
      
    </svg>
    

    Funktioniert mit aktuellen Versionen von Opera und Safari, nicht im Firefox (obwohl dieser die Methode grundsätzlich anbietet) und auch nicht mit dem ASV-Plugin.

    Grüße,
    Thomas

    1. Hey Thomas,

      Danke erst mal für die Antwort. Leider funktioniert deine Lösung bei mir nicht.. bekomme immer false als alert, egal ob der Kreis das rect schneidet oder nicht :/ (benutze Opera, neuste Version)
      (ja ich habe sowohl die Maße für das rect im script, als auch für das angezeigt geändert^^)

      Gibt es vielleicht noch eine andere Möglichkeit mein Problem zu lösen?
      Es sollte wenigstens die Möglichkeit bestehen, dass ein Hindernis erkannt wird wenn der Fisch (Grundform ist ein Kreis) dagegenschwimmt, so dass man darauf reagieren kann (zB mit Gameover oder indem der Punkte zähler höher gesetzt wird..)

      Viele Grüße,
      Fleur

      1. Hallo Fleur,

        Leider funktioniert deine Lösung bei mir nicht.. bekomme immer false als alert, egal ob der Kreis das rect schneidet oder nicht :/ (benutze Opera, neuste Version)

        Opera 10.10 unter Vista: true.

        Gibt es vielleicht noch eine andere Möglichkeit mein Problem zu lösen?
        Es sollte wenigstens die Möglichkeit bestehen, dass ein Hindernis erkannt wird wenn der Fisch (Grundform ist ein Kreis) dagegenschwimmt, so dass man darauf reagieren kann (zB mit Gameover oder indem der Punkte zähler höher gesetzt wird..)

        Ansatz: Gehe alle Objekte durch und teste mittels getBBox() die aktuelle Ausdehnung und vergleiche die Lage/Koordinaten mit dem gesuchten Objekt. Wird einige Rechnerei erfordern.

        Grüße,
        Thomas

        1. Hey Thomas,

          Recht hast du.. bei mir funktioniert es plötzlich auch. Ich bitte tausendmal um Entschuldigung.^^
          Hab jetzt auch nen kleinen Prototypen fertig:
          Ein Kreis den ich mit den Pfeiltasten bewegen kann (indem ich cx und cy ändere). Wenn er auf das Rechteck trifft sollte ein alert ausgelöst werden und das ganze zurück gesetzt werden (mit entsprechender reset-Funktion).

          Mir ist allerdings aufgefallen, dass der bereich der geprüft wird scheinbar immer ein wenig verschoben ist, im vergleich zu dem sichtbaren Rechteck. Woran liegt das? Kann ich das irgendwie noch korrigieren?

          Zum besseren Verständnis werd ich einfach mal besagten Prototyp hier Posten (bei mir scheint der tatsächliche Testbereich etwas höher und ein Stück weiter links zu liegen...) :

          1. die datei testlevel.svg

            
          <?xml version="1.0" encoding="utf-8"?>  
          <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"[]>  
          <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1000 500"  
          	preserveAspectRatio="xMinYMin meet" onload="Init(evt)" xmlns:xlink="http://www.w3.org/1999/xlink"  
          	zoomAndPan="magnify" version="1.0" contentScriptType="text/ecmascript" contentStyleType="text/css">  
            <defs>  
          	<script type="text/ecmascript" xlink:href="testgamescript.js"/>  
            </defs>  
            	  
          		<text id="laufzeit" x="20" y="20" style="font-size:15px;font-family:Papyrus, Arial;  
             			 font-weight:bold;font-style:oblique;stroke:darkblue;stroke-width:0;fill:darkblue" >03:00</text>  
          			  
          		<circle id="fish" cx="300" cy="200" r="40" fill="orange"/>  
          		<rect id="rect" x="200" y="100" width="40" height="30" fill="none" stroke="#CCC"/>  
          	  
          </svg>  
          
          

          2. Die Datei testgamescript.js

            
          var svgdoc,svgroot,timeToLive,laufzeit,fish,startCY,startCX,fishCY,fishCX;  
            
          	function Init(load_evt) {  
          		alert("load");  
          		svgdoc=load_evt.target.ownerDocument;  
          		svgroot=svgdoc.rootElement;  
            
          		laufzeit=svgdoc.getElementById("laufzeit");  
          		timeToLive=180;  
          		  
          		if(!window.XML)setInterval("showTime()",1000);  
          		if(!window.XML)setInterval("Collision_Detection()",200);  
          		  
          		fish=svgdoc.getElementById("fish");  
          		fishCY=fish.getAttribute("cy");  
          		fishCX=fish.getAttribute("cx");  
          		  
          		startCY=fish.getAttribute("cy");  
          		startCX=fish.getAttribute("cx");  
          		  
          		svgroot.addEventListener("keydown",KeyCheck,false);  
          		  
          	}  
            
          	function showTime(){  
          		var std,min,sek;  
          		  
          		timeToLive-=1;  
          		sek=timeToLive;  
          		min=Math.floor(sek/60);//berechnet die Minuten  
          		sek-=min*60; //zieht die vollen Minuten von den Sekunden ab  
          		sek=Math.floor(sek);  
          		  
          		//Sicherstellen, dass sowohl die Minuten als auch die Sekunden aus zwei Ziffern bestehen  
          		min=(min<10)?"0"+min:min;  
          		sek=(sek<10)?"0"+sek:sek;  
          		  
          		laufzeit.firstChild.data=min+":"+sek;  
          		  
          		if(timeToLive==0){  
          			alert("GAMEOVER");  
          			Reset();  
          		}  
          	}  
          	function setTime(t){  
          		timeToLive=t;  
          	}  
            
          	function KeyCheck(keydown_evt) {  
          	  
          		var keynum;  
          		keynum=keydown_evt.keyCode;   //Tastencode verarbeiten  
          		  
          		if((keynum==38)||(keynum==40)){  
          								  
          			if(keynum==38)fishCY-=10;   //Pfeiltaste oben  
          			if(keynum==40)fishCY+=10; //Pfeiltaste unten  
          		  
          			fish.setAttribute("cy",fishCY);  
          			  
          		}  
          		if((keynum==37)||(keynum==39)){  
            
          			if(keynum==37)fishCX-=10;   //Pfeiltaste links  
          			if(keynum==39)fishCX+=10; //Pfeiltaste rechts  
          			  
          			fish.setAttribute("cx",fishCX);  
          		}  
          		if(keynum==27){ //Esc  
          			  
          			Reset();			  
          			  
          		}  
          		keydown_evt.stopPropagation(); //Tastaturereignis nicht weiter geben  
          		  
          	}  
          	  
          	function Collision_Detection(){  
          	  
          	  // SVG-Wurzelelement-Objekt  
          	  var d=document.documentElement;  
          	  // Kreis:  
          	  var k=document.getElementById("fish");  
          	  // Rechteck:  
          	  var r=document.getElementById("rect");  
          	  // Rechteck-Objekt = zu testender Intersection-Bereich  
          	  var rect=d.createSVGRect();  
          	  
          	  rect.x=r.getAttribute("x");  
          	  rect.y=r.getAttribute("y");  
          	  rect.width=r.getAttribute("width");  
          	  rect.height=r.getAttribute("height");  
            
          	  var check=d.checkIntersection(k,rect);  
          	  //var check=d.checkEnclosure(k,rect);  
          	  if(check){  
          		alert("GAMEOVER");  
          		Reset();  
          	  }  
            
          	  // Hinweis: analoges Vorgehen mit r.checkEnclosure()  
            
          	}  
          	  
          	function Reset()  {  
          	  
          		//Objekt-Gruppe mittels Basismatrix auf den Ausgangspunkt setzen  
          		fishCY=startCY;  
          		fishCX=startCX;  
          		fish.setAttribute("cy",fishCY);  
          		fish.setAttribute("cx",fishCX);  
          		setTime(180);  
          	  
          	}  
          
          

          Ich hoffe mal es sind nicht zu viele blöde Anfängerfehler drin ;) und natürlich, dass dir oder jemand anderem dazu noch etwas einfällt.

          Viele Grüße,
          fleur

          1. Liebe fleur,

            1. Die Datei testgamescript.js

            var svgdoc,svgroot,timeToLive,laufzeit,fish,startCY,startCX,fishCY,fishCX;

            function Init(load_evt) {[...]

            
            >   
            > Ich hoffe mal es sind nicht zu viele blöde Anfängerfehler drin ;) und natürlich, dass dir oder jemand anderem dazu noch etwas einfällt.  
              
            das JavaScript würde ich lieber kompakter aufbauen. Vielleicht findest Du ja eine Anregung in meinem Artikel? [Gerüst für ein JavaScript-Projekt](http://aktuell.de.selfhtml.org/artikel/javascript/fader-framework/framework.htm#framework_geruest)  
              
            Liebe Grüße,  
              
            Felix Riesterer.
            
            -- 
            ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)
            
            1. Hallo Felix,

              Danke für deine Rückmeldung zu meinem Programm. Ich hab versucht mein Programm mal "kompakter" zuschreiben:

                
              var game = {  
              		  
              		init: function(load_evt){  
              			game.svgdoc = load_evt.target.ownerDocument;  
              			game.svgroot=this.svgdoc.rootElement;  
              			game.fish = this.svgdoc.getElementById("fish");	  
              				game.fishCX = this.fish.getAttribute("cx");  
              				game.fishCY = this.fish.getAttribute("cy");  
              			game.countdown = this.svgdoc.getElementById("laufzeit");  
              			game.countdownRESET = 180;  
              			game.countdownTIME = this.countdownRESET;  
              			if(!window.XML)setInterval("game.showTime()",1000);  
              			if(!window.XML)setInterval("game.collDetection()",200);  
              			game.svgroot.addEventListener("keydown",game.keyCheck,false);  
              		},  
              		reset: function(){		  
              			game.fish.setAttribute("cy",game.fishCY);  
              			game.fish.setAttribute("cx",game.fishCX);  
              			game.countdownTIME = game.countdownRESET;  
              		},  
              		showTime: function() {  
              			var std,min,sek;  
              			sek=game.countdownTIME;  
              			//ist das "Math.floor(..)" notwendig?  
              			min=Math.floor(sek/60);//berechnet die Minuten  
              			sek-=min*60; //zieht die vollen Minuten von den Sekunden ab  
              			sek=Math.floor(sek);  
              			//Sicherstellen, dass sowohl die Minuten als auch die Sekunden aus zwei Ziffern bestehen  
              			min=(min<10)?"0"+min:min;  
              			sek=(sek<10)?"0"+sek:sek;  
              			game.countdown.firstChild.data=min+":"+sek;  
              			if(game.countdownTIME==0){  
              				alert("GAMEOVER");  
              				game.reset();  
              			}  
              			game.countdownTIME-=1;  
              		},  
              		keyCheck: function(keydown_evt) {  
              	  
              			var keynum,c;  
              			keynum=keydown_evt.keyCode;   //Tastencode verarbeiten  
              	  
              			if((keynum==38)||(keynum==40)){  
              				c=parseInt(game.fish.getAttribute("cy"));  
              				if(keynum==38)c-=10; //Pfeiltaste oben  
              				if(keynum==40)c+=10; //Pfeiltaste unten  
              				game.fish.setAttribute("cy",c);  
              			}  
              			if((keynum==37)||(keynum==39)){  
              				c=parseInt(game.fish.getAttribute("cx"));  
              				if(keynum==37)c-=10; //Pfeiltaste links  
              				if(keynum==39)c+=10; //Pfeiltaste rechts  
              				game.fish.setAttribute("cx",c);  
              			}  
              			/*  
              			if(keynum==80){ //P  
              				//setTime(5);  
              				//pauseAnimation();  
              				alert("Pause");	  
              			}  
              			if(keynum==32){ //Leertaste  
              				alert("Leertaste: ");  
              			}*/  
              			if(keynum==27)game.reset();			  
              			keydown_evt.stopPropagation(); //Tastaturereignis nicht weiter geben  
              		},  
              		collDetection: function(){  
              			// SVG-Wurzelelement-Objekt  
              			var d=document.documentElement;//kann ich stattdessen auch game.svgdoc (siehe init) benutzen?  
              			// Rechteck:  
              			var r=document.getElementById("rect");  
              			// Rechteck-Objekt = zu testender Intersection-Bereich  
              			var rect=d.createSVGRect();  
              				rect.x=r.getAttribute("x");  
              				rect.y=r.getAttribute("y");  
              				rect.width=r.getAttribute("width");  
              				rect.height=r.getAttribute("height");  
              			//var check=d.checkIntersection(game.fish,rect);  
              			// Hinweis: analoges Vorgehen mit r.checkEnclosure():  
              			//var check=d.checkEnclosure(k,rect);			  
                
              			if(d.checkIntersection(game.fish,rect)){  
              				alert("GAMEOVER");  
              				game.reset();  
              			}			  
              		}  
              	}  
                
              
              

              Die svg-Datei bleibt die gleiche nur das onload muss geändert werden (onload="game.init(evt)).
              Entspricht es jetzt eher deinen Vorstellungen?

              Viele Grüße,
              fleur

              1. Liebe fleur,

                Ich hab versucht mein Programm mal "kompakter" zuschreiben

                gefällt mir sehr! Fast perfekt! ;-)

                Die svg-Datei bleibt die gleiche nur das onload muss geändert werden (onload="game.init(evt)).

                Könnte man das auch noch ändern? Im Idealfall geschieht das onload-Starten "automatisch", ohne dass da etwas im SVG-Dokument steht, analog zu den HTML-Dokumenten in meinem Artikel.

                Entspricht es jetzt eher deinen Vorstellungen?

                Wesentlich! Bis auf das "hartkodierte" onload-Attribut im SVG-Quelltext.

                Liebe Grüße,

                Felix Riesterer.

                --
                ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)