GLB:Steuerung und Schießen

Aus PandoraWiki

Wechseln zu: Navigation, Suche

Im 4. Teil werden wir unserem Helden Leben einhauchen und ihm beibringen, wie man schiesst.

Im Beispielcode befinden sich neu die Grafikdaten für unser Spielersprite und die Schüsse isdo/gfx/player.png: http://dl.openhandhelds.org/cgi-bin/pandora.cgi?0,0,0,0,46,29

In isdo.gbas definieren wir zuerst wieder einige neue Types, Konstanten und Variablen:

  TYPE TPlayer
          PosX            =   64
          PosY            =   64
          Timer%          =   0
          Dir%            =   0
          Speed           =   1.0
  ENDTYPE

PosX und PosY ist die Position, wo unser Spieler-Sprite angezeigt wird. Timer ist ein Zähler der bei jedem Bildaufbau um 1 erhöht wird. Dir ist die Bewegungsrichtung und Speed bestimmt die Bewegungsgeschwindigkeit.

  TYPE TShot
          State%[8]
          PosX[8]
          PosY[8]
          Dir%[8]
          Count%          =   8
  ENDTYPE

Hier definieren wir einen Typ für 8 Schüsse. D.h. wir werden maximal 8 Schüsse gleichzeitig anzeigen können. State beschreibt den aktuellen Schussstatus, PosX und PosY die Position und Dir die Schussrichtung. Count ist die Anzahl der maximalen Schüsse.

   // sprites
   GLOBAL  SPR_PLAYER%     =   3
   GLOBAL  SPR_SHOT%       =   18

Damit wir uns beim Spritezeichnen nicht jedes Mal die entsprechenden Nummern merken müssen, bezeichnen wir diese mit einer übersichtlicheren Konstante.

   // directions
   GLOBAL  DIR_UP%         =   16
   GLOBAL  DIR_RIGHTUP%    =   18
   GLOBAL  DIR_RIGTH%      =   2
   GLOBAL  DIR_RIGHTDOWN%  =   6
   GLOBAL  DIR_DOWN%       =   4
   GLOBAL  DIR_LEFTDOWN%   =   12
   GLOBAL  DIR_LEFT%       =   8
   GLOBAL  DIR_LEFTUP%     =   24
   GLOBAL  DIR_NONE%       =   0

Das sind Richtungsangaben welche wir für das Spielersprite und die Schüsse benutzen werden. Dann müssen wir noch die Variablen definieren:

   GLOBAL  Player  AS  TPlayer
   GLOBAL  Shot    AS  TShot

und dann noch

   GLOBAL  ButtonB         =   TRUE

diese liefert uns den Status des B-Buttons bzw der "x"-Taste.

In level00.gbas müssen wir noch einige der neuen Variablen initialisieren.

    Player.PosX=8
    Player.PosY=64
    SPR_SHOT=18

(...)

    // make player sprites
    LOADSPRITE "./isdo/gfx/player.png",0
    id=1
    FOR x=0 TO 3
        DRAWRECT 0,0,16,32,COL_TRANSPARENT
        DRAWSPRITE 0,-x*16,0
        GRABSPRITE id,0,0,16,32
        INC id,1
    NEXT

Hier laden wir die Grafikdaten für unseren Helden. Nr.1+2 sind die Animationsphasen, wenn der Spieler geradeaus fliegt. Nr.3+4 wenn er sich nach oben rechts bewegt. Ihr könnt das Ganze ja noch für die restlichen Richtungen erweitern.

    // make shots
    DRAWRECT 0,0,128,32,COL_TRANSPARENT
    DRAWSPRITE 0,0,-64
    GRABSPRITE 16,0,0,8,8
    GRABSPRITE 17,8,0,8,8
    GRABSPRITE 18,16,0,8,8
    GRABSPRITE 19,24,0,8,8
    GRABSPRITE 20,32,0,8,8
    GRABSPRITE 24,16,16,16,16

Mit diesen Zeilen erstellen wir die Sprites für die Schüsse.

In der Levelschleife müssen wir zuerst den Status des B-Buttons auf TRUE setzen.

    ButtonB=TRUE

Das ist wichtig da ansonsten folgendes passiert: Wir befinden uns ja zuerst im Startmenü. Mit dem B-Button bestätigen wir dann unsere Auswahl. Beginnt nun das Level und der B-Button ist noch gedrückt, wird gleich scharf geschossen. Durch setzen von TRUE wird jedoch zuerst gewartet bis der Button losgelassen wird.

Den Zähler für die Sprite-Animation müssen wir auch noch erhöhen.

        INC Player.TIMER,1
        GOSUB ShotHANDLER
        GOSUB PlayerHANDLER

Mit diesen zwei Unterprogrammen managen wir die Schüsse und die Heldenanzeige.

Wechseln wir nun zu unserer Library in library.gbas.

Zuerst betrachten wir den PlayerHANDLER.

   //=============================================================================
     SUB PlayerHANDLER:
   //=============================================================================
 
        DRAWSPRITE SPR_PLAYER+(INTEGER((bAND(Player.TIMER,3)/2))),Player.PosX,Player.PosY
 
    ENDSUB // PlayerHANDLER

Dieser ist zuständig für das jeweilige Anzeigen des Spieler-Sprites an der aktuellen Position.

   INTEGER((bAND(Player.TIMER,3)/2))

gibt je nach Player.Timer 0 oder 1 zurück, was der jeweiligen Animationsphase entspricht. In unserem Programm haben wir nur deren 2.

Im KeyHANDLER gibt es auch Änderungen. Zuerst müssen wir lokale Variablen definieren

   LOCAL   kr%,kl%,kd%,ku%

diese speichern, ob die jeweilige Richtungstaste gedrückt wird oder nicht. Dazu erstmal initialisieren.

    kr=0;kl=0;kd=0;ku=0

Nun prüfen wir ob in die jeweilige Richtung gedrückt wird. Falls ja, schreiben wir den Wert in die lokale Variable. Gleichzeitig ändern wir die aktuelle Position des Spieler-Sprites.

    IF KEY(BUTTON_RIGHT)
        kr=2
        INC Player.PosX,Player.Speed
    ENDIF
    IF KEY(BUTTON_LEFT)
        kl=8
        DEC Player.PosX,Player.Speed
    ENDIF
    IF KEY(BUTTON_DOWN)
        kd=4
        INC Player.PosY,Player.Speed
    ENDIF
    IF KEY(BUTTON_UP)
        ku=16
        DEC Player.PosY,Player.Speed
    ENDIF

Anschliessend addieren wir die einzelnen Variablen zu einer Bit-Maske. D.h. wir haben die binäre Zeichenfolge "00000". Wenn nach rechts gesteuert wird sieht die Maske so aus: "00010". Wenn nach oben und rechts gesteuert wird, so: "10010". So ist es auch möglich abzufragen, ob mehrere Tasten gedrückt werden. Die Maske speichern wir in Player.Dir.

    Player.Dir=kr+kl+kd+ku

Jetzt kommt etwas unübliches. SPR_PLAYER ist ja eigentlich eine KONSTANTE SpriteID. Wir benutzen sie hier jedoch ausnahmsweise als Variable und können somit bei einer Richtungsänderung mit nur einem Befehl das Basissprite für die Animation verändern. Ich hab's hier mal als Beispiel für oben rechts gemacht.

    SPR_PLAYER=1
    SELECT Player.Dir
        CASE DIR_RIGHTUP
            SPR_PLAYER=3
    ENDSELECT

Nun bringen wir unserem Held das Schiessen bei.

    // player shoot
    IF KEY(BUTTON_B)
        IF ButtonB=FALSE
            ButtonB=TRUE
            GOSUB ShotADD
        ENDIF
    ELSE
        ButtonB=FALSE
    ENDIF

Wenn der B_Button oder die "x"-Taste gedrückt wird springen wir zur Routine ShootADD, welche einen Schuss zu unserer Szene hinzufügt.

Zuerst definieren wir einen lokale Zählvariable und initialisieren diese mit 0.

   LOCAL   ix%
 
      ix=0

Dann durchlaufen wir 8x eine Schleife welche für jeden möglichen Schuss eine Prüfung durchführt.

  Loop1:
          ....
          ....
          GOTO Loop1
      ENDIF

Wir prüfen ob der lokale Zähler beim letzten Schuss angekommen ist.

    IF ix=Shot.Count THEN RETURN

Falls ja, wird kein weiterer Schuss hinzugefügt. Anschliessend schauen wir ob der aktuelle Schusseintrag schon belegt ist.

    IF Shot.State[ix]=0

Dann übergeben wir die Rchtungsmaske und die Startposition vom neuen Schuss.

        Shot.Dir[ix]=Player.Dir
        Shot.PosX[ix]=Player.PosX+8
        Shot.PosY[ix]=Player.PosY+10
        Shot.State[ix]=1

Shot.State ist eine Statusvariable die uns die aktuellen Phase des Schusses widergibt.

Im ShootHANDLER definieren wir einige lokale Variablen und durchlaufen dann in einer Schleife wieder alle 8 möglichen Schüsse.

   //=============================================================================
     SUB ShotHANDLER:
   //=============================================================================
   LOCAL   ix%,ev%,x%,y%
 
       FOR ix=0 TO Shot.Count-1
           IF Shot.State[ix]
               ....
               ....
           ENDIF
       NEXT

Wir berechnen nun die Position des aktuellen Schusses in unserer Eventmap und lesen deren Wert aus.

            x=INTEGER(ScrollX+Shot.PosX[ix]) / 16
            y=INTEGER(ScrollY+Shot.PosY[ix]-32) / 16
            ev=Event.Map[x*16+y]

Wie wir ja bereits wissen, besteht die Eventmap aus speziellen Attributen. Solch ein Attribut kann zum Beispiel sein, ob an derselben Stelle in der Vordergrundmap sich ein Hindernis (Wand oder ähnliches) befindet. Praktisch bedeutet dies, dass wir den Schuss beenden sobald er auf eine Wand trifft und nicht einfach durch sie hindurchfliegt. Trifft der Schuss auf eine Wand geben wir die aktuelle Schussmöglichkeit in der Indexliste mit

            IF ev>0 THEN Shot.State[ix]=0

wieder frei.

Je nach Flugrichtung verändern wir die Schussposition, zeichnen das entsprechende Sprite und überprüfen noch, ob der Schuss am Bildrand angekommen ist.

                        CASE DIR_RIGHTUP
                            INC Shot.PosX[ix],2
                            DEC Shot.PosY[ix],2
                            DRAWSPRITE SPR_SHOT-1,Shot.PosX[ix],Shot.PosY[ix]
                            IF Shot.PosX[ix]>320
                                Shot.State[ix]=0
                            ENDIF

Im nachsten Teil behandeln wir dann die Gegner.