03.072013

Tutorialreihe R-Type Flashgame selbst machen - Part 2

Und da bin ich schon wieder dran, um den zweiten Teil meiner Tutorialreihe für das R-Type Flashgame zu schreiben. Dieses mal geht es um Kollisionserkennung für die Asteroiden und das Raumschiff selbst. Wenn ihr nicht wisst, wo der erste Part ist oder diesen verpasst habt, hilft euch dieser Link weiter: Tutorialreihe R-Type Flashgame selbst machen - Part 1

Fangen wir erstmal an, die Klasse TheShip.as zu bearbeiten, um diese für die nächsten Schritte bereit zu stellen.

Wir fügen als aller erstes im Konstruktor von TheShip folgende Zeile ein, um ein Enter_Frame Event zu machen, welches dazu dient immer die Kollisionen abzufragen.

this.addEventListener( Event.ENTER_FRAME, shipCollision );

Diese Funktion müssen wir natürlich auch noch erstellen. Ebenfalls in TheShip.as. Diese soll wie folgt aussehen:

function shipCollision( evt:Event ) {
  if (this.mainClip.asteroidArray.length > 0) {
    for (var i:int=0; i<this.mainClip.asteroidArray.length; i++) {
      if (this.mainClip.asteroidArray[i].hitTestObject(this)) {
        if (tempAsteroid != this.mainClip.asteroidArray[i]){
          tempAsteroid = this.mainClip.asteroidArray[i];
          this.mainClip.asteroidArray[i].asteroidHit(0);
        }
      }
    }
  }
}

Außerdem muss noch zusätzlich eine globale Variable hinzugefügt werden als temporären Platzhalter. Dies kann man sicherlicht mittlerweile besser realisieren, jedoch habe ich ja bereits geschrieben, dass ich dies wärend meine Ausbildung zum Softwareentwickler geschrieben habe und dies auch etwas veraltet ist.

var tempAsteroid:Asteroid;

Nun haben wir die Klasse mit den nötigen Änderungen erweitert. Als nächstes müssen wir die Asteroiden erstellen und natürlich hierfür auch die nötigen Funktionen bereitstellen. Die Idee hierfür war es, verschiedene Level zu machen und verschiedene Modelle bereit zu stellen. Dies werden wir nun erstmal weglassen, evtl. werden wir das später noch erweitern. Aber der Übersichtlichkeit halber lassen wir das erstmal weg. Die Klasse sieht erstmal wie folgt aus:

public class Asteroid extends MovieClip {
  private var mainClip:MovieClip;
  var loader:Loader;
  var url:URLRequest;
  var dateiName:String;
  var asteroidFlying:Tween;
  var timeHitted:Number = 0;
  var neededHits:Number;
  var tempLaser:Laser;

  public function Asteroid(mainClip:MovieClip) {
    this.rotation = Math.random() * 360;
    this.mainClip = mainClip;
    this.addEventListener( Event.ENTER_FRAME, asteroidDispose );
    dateiName = "images/asteroid-1.png";
    loader=new Loader();
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE,completeListener);
    loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,ioErrorListener);
    url = new URLRequest(dateiName);
    loader.load(url);
    this.y = 100 + Math.random() * 250;
    var geschwindigkeit:Number = level - Math.random() * 2;
    asteroidFlying = new Tween(this,"x",None.easeIn,850,-150,geschwindigkeit,true);
  }

  function completeListener(e:Event) {
    addChild(loader);
  }

  function asteroidHit(hit:Number) {
    var z:int = 0;
    for (var i:int=0; i<mainClip.laserArray.length; i++) {
      if (mainClip.asteroidArray[i] == this) {
        z = i;
        break;
      }
    }
    this.mainClip.asteroidArray[z] = null;
    this.mainClip.asteroidArray.splice(z,1);
    if(hit == 1) {
      mainClip.score += 10*(10-level);
    }
    this.removeEventListener( Event.ENTER_FRAME, asteroidDispose );
    this.parent.removeChild(this);
  }

  function ioErrorListener(e:IOErrorEvent) {
    trace("IO Error: "+e.text);
  }

  function asteroidDispose( evt:Event ) {
    for (var i:int=0; i<this.mainClip.laserArray.length; i++) {
      if (this.mainClip.laserArray[i].hitTestObject(this)) {
        if (tempLaser != this.mainClip.laserArray[i]) {
          timeHitted++;
        }
        if(timeHitted == neededHits) {
          this.asteroidHit(1);
          this.mainClip.laserArray[i].laserHit();
          break;
        } else {
          tempLaser = this.mainClip.laserArray[i];
          this.mainClip.laserArray[i].laserHit();
        }
      }
    }
    asteroidFlying.resume();
    this.rotation +=  1;
    if (this.x < -50) {
      this.asteroidHit(0);
    }
  }
}

Die Logik hierhinter ist relativ einfach, jedoch werde ich dies ganz am Ende erläutern, wenn die Main erweitert wurde. Womit wir nun auch schon zu der fast letzten Änderung kommen in unserem kleinen Projekt. Als erstes müssen in unserer Main.as zwei neue globale Variable erstellt werden

public var asteroidArray:Array = new Array();
var asteroidTimer:Timer;

Wenn dies passiert ist, muss der Konstruktur um folgende Zeilen erweitert werden:

stage.addEventListener( Event.ENTER_FRAME, onEnterFrameEvent );
asteroidTimer = new Timer(1000);
asteroidTimer.addEventListener(TimerEvent.TIMER,asteroidSpawn);
asteroidTimer.start();

Diese dienen dazu, dass wir auch asteroiden spawnen können. Wir benötigen aber auch noch weitere Funktionen, die ich hier mal aufliste und am Ende alles zusammenfassend erkläre:

function asteroidSpawn(e:TimerEvent):void {
  var asteroid:Asteroid = new Asteroid(this);
  addChild(asteroid);
  asteroidArray.push(asteroid);
}

public function removeMe(asteroid:Asteroid) {
  stage.removeChild(asteroid);
}

So damit sollten wir auch schon fertig sein. Ihr müsst logischer weise alle benötigten Bilder so wie ihr sie haben wollt, in die dazu passenden Ordner legen. Ich kann hier leider keine Bilder posten, da diese Lizenzrechtlich geschützt sind.

Die Logik hierbei ist, dass wenn ein Schiff vom Asteroiden getroffen wird dieser gelöscht wird. Man kann dabei noch einen Zähler/Counter einbauen oder einen Überhitzungsschutz etc... Ihr könnt ja selbst euch mal durch den Code wühlen und gucken, ob ihr evtl. schon Sachen verbessern könnt. Ich hatte leider bisher keine wirkliche Zeit gehabt, das Game nochmal neu zu coden. Aber ich denke als erstes und als kleine Hilfestellung ist dies schon sehr gut zu gebrauchen.

Beim nächsten mal werde ich wohl das gesamte Projekt nochmal vollständig zeigen und erklären. Hierfür fehlt leider gerade ein wenig die Zeit, aber auch das wird definitiv nachgeholt!

Soweit erstmal bis hierhin, euer Mark!