Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - Steinbock

#46
Im letzten Teil schauen wir, wie ein Power-Up hinzugef?gt werden kann und wie dadurch die Schusskraft ver?ndert wird.
Und zum Abschluss f?gen wir noch eine Hintergrundmusik sowie einige Soundeffekte dazu. Wie immer findet ihr im Anhang den Beispiel-Code.

Wenn ihr den letzten Teil durchgearbeitet habt, wisst ihr ja, dass wir 2 verschiedene Gegnertypen haben: ENEMY1 und ENEMY2.
Im Programm selber kann man ENEMY2 als ROTEN Gegner erkennen. Nun soll dieser, falls er getroffen wird, ein Power-Up hinterlassen.
Dazu m?ssen wir nur in unserm EnemySCRIPT ein neues Objekt hinzuf?gen.
Code (glbasic) Select

                ObjectADD(ID_POWERUP,Object.PosX[ix],Object.PosY[ix])

Das Script f?r das Power-Up findet ihr wiederum in der Datei items.gbas.
Code (glbasic) Select

        CASE ID_POWERUP
            SELECT Object.State[ix]
                CASE 1
                    IF Object.PosX[ix]>-16
                        DEC Object.PosX[ix],0.5
                    ELSE
                        Object.State[ix]=0
                    ENDIF
            ENDSELECT
            DRAWSPRITE SPR_POWERUP,Object.PosX[ix],Object.PosY[ix]
            SetCollBox(ix,0,0,15,15)
            IF CheckCollPlayer(ix)=COLL_PLAYER
                Object.State[ix]=0
                PLAYSOUND(SFX_EXTRA,GetPan(Object.PosX[ix]),0.7)
                SPR_SHOT=24
            ENDIF
    ENDSELECT

Dieses Item ?berpr?fen wir nun auf die Kollision mit dem Spieler und falls eine stattfindet l?schen wir das Item in der Objektliste und ver?ndern unseren Schuss.

Nun Zum Thema Sound. Das is unter GLBasic eine einfache Sache.
In der Datei level000.gbas laden wir unsere Soundeffekte und weisen ihnen gleich eine Konstante zu.
Code (glbasic) Select

    LOADSOUND "./isdo/sfx/shot.wav",SFX_SHOT,4
    LOADSOUND "./isdo/sfx/explosion.wav",SFX_EXPLOSION,4
    LOADSOUND "./isdo/sfx/extra.wav",SFX_EXTRA,4

Dann laden wir unsere Hintergrundmusik, stellen die Lautst?rke ein und lassen sie laufen.
Code (glbasic) Select

    PLAYMUSIC "./isdo/sfx/music.wav",TRUE
    MUSICVOLUME 0.9


Eine Soundeffekt wird in GLBasic mit dem Befehl PLAYSOUND abgespielt.
Wenn Ihr zum Beispiel das ItemSCRIPT beachtet wird mit
Code (glbasic) Select

                PLAYSOUND(SFX_EXTRA,GetPan(Object.PosX[ix]),0.7)

Der Sound mit der ID SFX_EXTRA mit der Lautst?rke 0.7 abgespielt.
Die Funktion GetPan in unserer Library bewirkt, dass der Soundeffekt je nach Position des Objekts eher Links oder Rechts ert?nt.

So. Nun hoffe ich, dass ich dem einen oder anderen mit diesem Tutorial etwas geholfen habe. Und wie gesagt wenn ich irgendwo helfen kann, versuch ich's.

Als kleinen Bonus k?nnt Ihr hier isdo_090406.zip - 11.13MB einen ca. 1-min?tigen Ausschnitt aus meinem Projekt I.S.D.O herunterladen.
Die Spielerkollision ist noch ausgeschaltet. Die Hintergrundmusik stamm aus dem "MOD-Archiv".
Code (glbasic) Select

Steuerung->WIN:
  startmenu:  cursorkey up/down = menucursor up and down
              "x"-key           = select menuentry
  game:       cursorkey up/down = move
              "x"-key           = shoot
              ENTER-key         = return to startmenu

Viel Spass!!  =D


   

[attachment deleted by admin]
#47

In the second last part of this tutorial we will first automate the adding of objects. Thereafter we will concerne with collisions.

Last time we added with
Code (glbasic) Select

        IF ScrollX=128 THEN ObjectADD(ID_ENEMY1,320,120)
        IF ScrollX=160 THEN ObjectADD(ID_ENEMY2,320,120)
in level00.gbas the objects manually. But in a completed game are much more required, and it would be very laborious to call every single object separatly. Therefore we read out the datas from a predefined file level00.evt. For this we must expand our existing event-type (TEvent).
Code (glbasic) Select

        IX%             =   0
        ID%[256]
        Pos%[256]
        X%[256]
        Y%[256]
IX is a pointer to the actual listentry, ID is the objectID, Pos is the scrollposition WHEN the object shall appear and X,Y the position WERE the object shall appear.
With
Code (glbasic) Select

    // init events
    OPENFILE(1,"./isdo/level00.evt",TRUE)
    FOR p=0 TO 254
        READUWORD 1,val
        Event.Pos[p]=val
    NEXT
    FOR p=0 TO 254
        READUWORD 1,val
        Event.ID[p]=val
    NEXT
    FOR p=0 TO 254
        READUWORD 1,val
        Event.X[p]=val
    NEXT
    FOR p=0 TO 254
        READUWORD 1,val
        Event.Y[p]=val
    NEXT
in level00.gbas we load these datas into our eventlist. The request when a object shall appear, we do in the subroutine EventHANDLER in library.gbas.
Code (glbasic) Select

//=============================================================================
  SUB EventHANDLER:
//=============================================================================

LOCAL   ix%,typ%

    ix=Event.IX
    IF ScrollX=Event.Pos[ix]
        SELECT Event.ID[ix]
            CASE ID_ENEMY1 TO ID_ENEMY2
                ObjectADD(Event.ID[ix],Event.X[ix],Event.Y[ix])
        ENDSELECT
        INC Event.IX,1
    ENDIF

ENDSUB // EventHANDLER

First we request the current index in the eventlist, then we check the type and add the corresponding object to the scene. The rest goes as if by magic.

And now to the collisions. There are different possibilities in GLBasic.

    BOXCOLL cheks, if 2 rectangles interleaf eachother
    SPRCOLL check, if visible pixels of 2 sprites interleaf eachother
We use a routine based on BOXCOLL. For this we define a so-called collisionbox for player, playershots and objects. This box can also be bigger or smaller then the sprite.
We have to expand the types.
Code (glbasic) Select

        CollTop
        CollBottom
        CollLeft
        CollRight

The size of the collisionbox of objects we specify with function
Code (glbasic) Select

//=============================================================================
  FUNCTION SetCollBox: ix%,left,top,right,bottom
//=============================================================================

    Object.CollLeft[ix]=Object.PosX[ix]+left
    Object.CollTop[ix]=Object.PosY[ix]+top
    Object.CollRight[ix]=Object.PosX[ix]+right
    Object.CollBottom[ix]=Object.PosY[ix]+bottom

For test pruposes you can outcomment the following 3 lines. Then the collisionboxes appear half-transparent.
Code (glbasic) Select

    //ALPHAMODE 0.5
    //DRAWRECT Object.CollLeft[ix],Object.CollTop[ix],Object.PosX[ix]+right-Object.CollLeft[ix],Object.PosY[ix]+bottom-Object.CollTop[ix],0xFFFFFF
    //ALPHAMODE 0.0


For the player we build a separate box.
Code (glbasic) Select

//=============================================================================
  SUB PlayerHANDLER:
//=============================================================================

    Player.CollTop=Player.PosY
    Player.CollBottom=Player.PosY+31
    Player.CollLeft=Player.PosX
    Player.CollRight=Player.PosX+16
    DRAWSPRITE SPR_PLAYER+(INTEGER((bAND(Player.Timer,3)/2))),Player.PosX,Player.PosY

ENDSUB // PlayerHANDLER

the same for the 8 shots in ShotHANDLER.
Code (glbasic) Select

                    Shot.CollTop   [ix]=Shot.PosY[ix]
                    Shot.CollBottom[ix]=Shot.PosY[ix]+6
                    Shot.CollLeft  [ix]=Shot.PosX[ix]
                    Shot.CollRight [ix]=Shot.PosX[ix]+6

It's important to make the assignements with every change of position.

Now the check if an object collides eather with player or with playershot.
Code (glbasic) Select

//=============================================================================
  FUNCTION CheckCollPlayer: ix%
//=============================================================================

    IF Player.CollTop>Object.CollBottom[ix] OR Player.CollBottom<Object.CollTop[ix] OR Player.CollLeft>Object.CollRight[ix] OR Player.CollRight<Object.CollLeft[ix]
        RETURN COLL_NONE
    ELSE
        RETURN COLL_PLAYER
    ENDIF

    RETURN COLL_NONE
With ix we pass the index of object wich we check. If the object touches the player we return COLL_PLAYER, else we return COLL_NONE.
The same for playershots.
Code (glbasic) Select

//=============================================================================
  FUNCTION CheckCollShot: ix%
//=============================================================================

LOCAL   i%

    FOR i=0 TO Shot.Count-1
        IF Shot.State[i]
            IF Shot.CollTop[i]>Object.CollBottom[ix] OR Shot.CollBottom[i]<Object.CollTop[ix] OR Shot.CollLeft[i]>Object.CollRight[ix] OR Shot.CollRight[i]<Object.CollLeft[ix]
                RETURN COLL_NONE
            ELSE
                Shot.State[i]=0
                RETURN COLL_SHOT
            ENDIF
        ENDIF
    NEXT

    RETURN COLL_NONE

ENDFUNCTION // CheckCollShot
But this time we return COLL_SHOT and clear also the current shotentry.

We give the command for collisiondetection for every object in the scripts. As example we take ID_ENEMY1 in enemies.gbas:
Code (glbasic) Select

            SetCollBox(ix,0,0,15,15)
Sets the size of collisionbox.
Then we check if ENEMY1 was hitted by a playershot.
Code (glbasic) Select

            IF CheckCollShot(ix)=COLL_SHOT
                Object.State[ix]=0
                EffectADD(ID_EFFECT1,Object.PosX[ix],Object.PosY[ix])
            ENDIF
If yes, we clear the object out of the list and add an effect (explosion).
Effect are nothing else than object but they do not need a collisionbox. The reason to distinguish between effects and objects is
Code (glbasic) Select

        GOSUB KeyHANDLER
        GOSUB LayerBGDRAW
        GOSUB ShotHANDLER
        GOSUB EventHANDLER
        GOSUB ObjectHANDLER
        GOSUB PlayerHANDLER
        GOSUB LayerFGDRAW
        GOSUB EffectHANDLER
        GOSUB StatusDRAW
that we draw effects AFTER objects. Otherwise perhaps we can' see the effects behind other objects.









[attachment deleted by admin]
#48
Hallo BumbleBee

QuoteAber bevorzuge doch lieber den einfacheren Weg
Was meint du damit? :|
#49
Im zweitletzten Teil von diesem Tutorial werden wir zuerst das Hinzuf?gen von Objekten automatisieren. Danach befassen wir uns mit der Kollisionsabfrage.

Beim letzten Teil haben wir ja mit
Code (glbasic) Select

        IF ScrollX=128 THEN ObjectADD(ID_ENEMY1,320,120)
        IF ScrollX=160 THEN ObjectADD(ID_ENEMY2,320,120)
in level00.gbas die Objekte manuell hinzugef?gt. In einem fertigen Spiel ben?tigt man jedoch viel mehr und es w?re sehr umst?ndlich, diese alle einzeln aufzurufen. Deshelb lesen wir die ben?tigten Daten aus einer vordefinierten Datei level00.evt. Dazu m?ssen wir unseren bereits vorhandenen Event-Type erweitern.
Code (glbasic) Select

        IX%             =   0
        ID%[256]
        Pos%[256]
        X%[256]
        Y%[256]
Dabei ist IX ein Zeiger auf den aktuellen Listeneintrag, ID ist die ObjektID, Pos ist die Scrollposition wann das Objekt erscheinen soll und X,Y ist die Position an der das Objekt erscheinen soll.
Mit
Code (glbasic) Select

    // init events
    OPENFILE(1,"./isdo/level00.evt",TRUE)
    FOR p=0 TO 254
        READUWORD 1,val
        Event.Pos[p]=val
    NEXT
    FOR p=0 TO 254
        READUWORD 1,val
        Event.ID[p]=val
    NEXT
    FOR p=0 TO 254
        READUWORD 1,val
        Event.X[p]=val
    NEXT
    FOR p=0 TO 254
        READUWORD 1,val
        Event.Y[p]=val
    NEXT
in level00.gbas laden wir diese Daten in unsere Eventliste. Die Abfrage wann ein Objekt erscheinen soll machen wir im Unterprogramm EventHANDLER in library.gbas.
Code (glbasic) Select

//=============================================================================
  SUB EventHANDLER:
//=============================================================================

LOCAL   ix%,typ%

    ix=Event.IX
    IF ScrollX=Event.Pos[ix]
        SELECT Event.ID[ix]
            CASE ID_ENEMY1 TO ID_ENEMY2
                ObjectADD(Event.ID[ix],Event.X[ix],Event.Y[ix])
        ENDSELECT
        INC Event.IX,1
    ENDIF

ENDSUB // EventHANDLER

Erst fragen wir den aktuellen Index der Eventliste ab, dann ?berpr?fen wir den Typ und f?gen der Szene das entsprechende Objekt hinzu. Der Rest geht dann wie von alleine.

So, und nun die Kollisionsabfrage. Da gibt es verschiedene M?glichkeiten in GLBasic.

    BOXCOLL ?berpr?ft, ob sich 2 Rechtecke ?berlappen
    SPRCOLL ?berpr?ft, ob sich einzelne Pixel von 2 Sprites ?berlappen
Wir benutzen eine Routine, die auf BOXCOLL basiert. Dazu definieren wir f?r den Spieler, die Sch?sse und sowie die Objekte eine sogenannte Kollisionsbox. Diese Box kann auch kleiner oder gr?sser als das Sprite sein.
Also f?gen wir f?r die verschiedenen Types jeweils
Code (glbasic) Select

        CollTop
        CollBottom
        CollLeft
        CollRight
hinzu.
Die Gr?sse der Box bestimmen wir bei den Objekten mit der Funktion
Code (glbasic) Select

//=============================================================================
  FUNCTION SetCollBox: ix%,left,top,right,bottom
//=============================================================================

    Object.CollLeft[ix]=Object.PosX[ix]+left
    Object.CollTop[ix]=Object.PosY[ix]+top
    Object.CollRight[ix]=Object.PosX[ix]+right
    Object.CollBottom[ix]=Object.PosY[ix]+bottom

Zu Testzwecken k?nnt ihr ja mal die folgenden 3 Zeilen  auskommentieren und etwas herumexperimentieren. Dann werden die Boxen halbtransparent sichtbar.
Code (glbasic) Select

    //ALPHAMODE 0.5
    //DRAWRECT Object.CollLeft[ix],Object.CollTop[ix],Object.PosX[ix]+right-Object.CollLeft[ix],Object.PosY[ix]+bottom-Object.CollTop[ix],0xFFFFFF
    //ALPHAMODE 0.0


F?r den Spieler erstellen wir eine separate Box.
Code (glbasic) Select

//=============================================================================
  SUB PlayerHANDLER:
//=============================================================================

    Player.CollTop=Player.PosY
    Player.CollBottom=Player.PosY+31
    Player.CollLeft=Player.PosX
    Player.CollRight=Player.PosX+16
    DRAWSPRITE SPR_PLAYER+(INTEGER((bAND(Player.Timer,3)/2))),Player.PosX,Player.PosY

ENDSUB // PlayerHANDLER

ebenso f?r die 8 Sch?sse im ShotHANDLER.
Code (glbasic) Select

                    Shot.CollTop   [ix]=Shot.PosY[ix]
                    Shot.CollBottom[ix]=Shot.PosY[ix]+6
                    Shot.CollLeft  [ix]=Shot.PosX[ix]
                    Shot.CollRight [ix]=Shot.PosX[ix]+6

Wichtig ist, dass die Zuweisung der Box bei jeder Positions?nderung gemacht wird.

Nun kommt die Abfrage ob ein Objekt mit dem Spieler oder mit einem Schuss kollidiert.
Code (glbasic) Select

//=============================================================================
  FUNCTION CheckCollPlayer: ix%
//=============================================================================

    IF Player.CollTop>Object.CollBottom[ix] OR Player.CollBottom<Object.CollTop[ix] OR Player.CollLeft>Object.CollRight[ix] OR Player.CollRight<Object.CollLeft[ix]
        RETURN COLL_NONE
    ELSE
        RETURN COLL_PLAYER
    ENDIF

    RETURN COLL_NONE
Mit ix ?bergeben wir den Index f?r das Objekt mit welchem wir pr?fen. Falls das Objekt den Spieler ber?hrt geben wir den wert COLL_PLAYER zur?ck, ansonsten COLL_NONE.
Dasselbe gilt f?r die Sch?sse.
Code (glbasic) Select

//=============================================================================
  FUNCTION CheckCollShot: ix%
//=============================================================================

LOCAL   i%

    FOR i=0 TO Shot.Count-1
        IF Shot.State[i]
            IF Shot.CollTop[i]>Object.CollBottom[ix] OR Shot.CollBottom[i]<Object.CollTop[ix] OR Shot.CollLeft[i]>Object.CollRight[ix] OR Shot.CollRight[i]<Object.CollLeft[ix]
                RETURN COLL_NONE
            ELSE
                Shot.State[i]=0
                RETURN COLL_SHOT
            ENDIF
        ENDIF
    NEXT

    RETURN COLL_NONE

ENDFUNCTION // CheckCollShot
Hier geben wir jedoch den Wert COLL_SHOT zur?ck und l?schen gleichzeitig noch den aktuellen Schusseintrag.

Den Befehl zur Kollisionsabfrage geben wir f?r jedes Objekt in den Scripts. Als Beispiel nehmen wir einmal ID_ENEMY1 in enemies.gbas:
Code (glbasic) Select

            SetCollBox(ix,0,0,15,15)
legt die Gr?sse der Kollisionsbox fest.
Dann ?berpr?fen wir ob wir (das ENEMY1) von einem Schuss getroffen wurden.
Code (glbasic) Select

            IF CheckCollShot(ix)=COLL_SHOT
                Object.State[ix]=0
                EffectADD(ID_EFFECT1,Object.PosX[ix],Object.PosY[ix])
            ENDIF
Falls ja, l?schen wir das Objekt aus der Liste und f?gen noch einen Effekt (Explosion) hinzu.
Effekte sind genau gleich aufgebaut wie Objekte. Sie ben?tigen aber keine Kollisionsbox. Der Grund dass wir hier zwischen Effekten und Objekten einen Unterschied machen ist
Code (glbasic) Select

        GOSUB KeyHANDLER
        GOSUB LayerBGDRAW
        GOSUB ShotHANDLER
        GOSUB EventHANDLER
        GOSUB ObjectHANDLER
        GOSUB PlayerHANDLER
        GOSUB LayerFGDRAW
        GOSUB EffectHANDLER
        GOSUB StatusDRAW
dass wir die Effekte NACH den Objekten Zeichnen. Sonst s?he man unter Umst?nden ja die Effekte gar nicht mehr.




[attachment deleted by admin]
#50
Vielen Dank f?r die R?ckmeldung

Hab's (hoffentlich) ?berall korrigiert  :whistle:
#51

In this part we come to the most important part of this tutorial, the objects. If you understood this part, then you will see, gameprogramming isn't as difficult as it seems.

This time in the attached sample code there are new graphicdatas isdo/gfx/player.png for our objects and enemies.gbas.
Objects can be for example:

    Enemies
    Powerups
    Effects
    Enemyshots

For this we insert a new type in isdo.gbas:
Code (glbasic) Select

TYPE TObject
        State%[32]
        Typ%[32]
        PosX[32]
        PosY[32]
        Timer%[32]
        Count%          =   32
ENDTYPE
With this we define a type for 32 objects. This means we will be able to display max. 32 objects at the same time.
State describes the current objectstate, PosX and PosY the position and Timer is used as an animation-conter. Count is the maximum of objects.
We build the corresponding variable.
Code (glbasic) Select

GLOBAL  Object  AS  TObject

Then we expand our spriteID list.
Code (glbasic) Select

GLOBAL  SPR_ENEMY1%     =   512
GLOBAL  SPR_ENEMY2%     =   514


Because we need many different objects in a game, it's an advantage to assing a constant to every object.
Code (glbasic) Select

// object ID's
GLOBAL  ID_ENEMY1%      =   0x11
GLOBAL  ID_ENEMY2%      =   0x12
We define 2 enemies:

    ENEMY1  yellow enemy
    ENEMY2  red enemy

In level00.gbas we load our objectgraphics and "grab" out the single sprites.
Code (glbasic) Select

    // make objects
    LOADSPRITE "./isdo/gfx/object.png",0
    id=512
    FOR y=0 TO 15
        FOR x=0 TO 15
            DRAWRECT 0,0,16,16,COL_TRANSPARENT
            DRAWSPRITE 0,-x*16,-y*16
            GRABSPRITE id,0,0,16,16
            INC id,1
        NEXT
    NEXT


Then we expand our levelloop with the ObjectHANDLER
Code (glbasic) Select

        GOSUB ObjectHANDLER
wich we will find in library.gbas.
Code (glbasic) Select

//=============================================================================
  SUB ObjectHANDLER:
//=============================================================================

LOCAL   ix%

    FOR ix=0 TO Object.Count-1
        IF Object.State[ix]
            SELECT Object.Typ[ix]
                CASE ID_ENEMY1
                    EnemyScripts(ix)
                CASE ID_ENEMY2
                    EnemyScripts(ix)
            ENDSELECT
        ENDIF
    NEXT

ENDSUB // ObjectHANDLER
This small program section is indeed a main component of our program!
As with the shots we running through a list and check first, if the listentry is already free. If yes, we look wath kind of objecttype it is.
Depending on objecttype then we refer to an objectscript, where the object would be handled separatly (attitude, moving, collision etc).
On call-up function EnemyScripts we pass with ix the current objectlist entry and hence the control over the corresponding object.

At this point there are already a few values in the objects entries, because with
Code (glbasic) Select

        IF ScrollX=128 THEN ObjectADD(ID_ENEMY1,320,120)
        IF ScrollX=160 THEN ObjectADD(ID_ENEMY2,320,120)
in level00.gbas we added some objects to our scene.
With this command we pass type and position of a new object to the ObjectHANDLER.
Code (glbasic) Select

//=============================================================================
  FUNCTION ObjectADD: typ%,posX,posY
//=============================================================================

LOCAL   ix%

    ix=0
Loop1:
    IF ix=Object.Count THEN RETURN
    IF Object.State[ix]=0
        Object.State[ix]=1
        Object.Typ[ix]=typ
        Object.PosX[ix]=posX
        Object.PosY[ix]=posY
    ELSE
        INC ix,1
        GOTO Loop1
    ENDIF
    RETURN
ENDFUNCTION // ObjectADD


Now let's have a closer look to a script. The most important variable is Object.State. It describes the phase in which the object is located. In our example we have 3 phases. In each of this phase we change for example the position. If we finished the last phase, then we release the object again.
Code (glbasic) Select

                CASE 1
                    IF Object.PosX[ix] > 160
                        DEC Object.PosX[ix],2
                    ELSE
                        Object.State[ix]=2
                    ENDIF
                CASE 2
                    IF Object.PosX[ix] > 64
                        DEC Object.PosX[ix],2
                        DEC Object.PosY[ix],1
                    ELSE
                        Object.State[ix]=3
                    ENDIF
                CASE 3
                    IF Object.PosX[ix] > -16
                        DEC Object.PosX[ix],2
                    ELSE
                        Object.State[ix]=0
                    ENDIF

Now we only have to draw the object.
Code (glbasic) Select

            DRAWSPRITE SPR_ENEMY1+frame,Object.PosX[ix],Object.PosY[ix]



[attachment deleted by admin]
#52

Im 5. Teil kommen wir nun zu dem wichtigsten Bestandteil dieses Tutorials, den Objekten. Wenn ihr diesen Teil verstanden habt, werdet ihr sehen, dass die Gameprogrammierung eigentlich gar keine allzu grosse Hexerei ist.

Im angeh?ngten Beispielcode befinden sich neu die Grafikdaten isdo/gfx/player.png f?r unsere Objekte und enemies.gbas.
Dies k?nnen z.B. sein:

    Gegner
    Powerups
    Effekte
    gegnerische Sch?sse

In isdo.gbas f?gen wir einen neuen Typ hinzu:
Code (glbasic) Select

TYPE TObject
        State%[32]
        Typ%[32]
        PosX[32]
        PosY[32]
        Timer%[32]
        Count%          =   32
ENDTYPE
damit definieren wir einen Typ f?r 32 Objekte. D.h. wir werden maximal 32 Objekte gleichzeitig anzeigen k?nnen.
State beschreibt den aktuellen Objektstatus, PosX und PosY die Position und Timer ist wieder ein Animationsz?hler. Count ist die Anzahl der maximalen Objekte.
Weiter unten erstellen wir die dazugeh?rige Variable.
Code (glbasic) Select

GLOBAL  Object  AS  TObject

Dann erweitern wir unsere SpriteID's noch um
Code (glbasic) Select

GLOBAL  SPR_ENEMY1%     =   512
GLOBAL  SPR_ENEMY2%     =   514


Da wir bei einem Spiel viele verschiedene Objekte verwenden, ist es von Vorteil, jedem Objekt eine Konstante zuzuweisen.
Code (glbasic) Select

// object ID's
GLOBAL  ID_ENEMY1%      =   0x11
GLOBAL  ID_ENEMY2%      =   0x12
Wir definieren einmal 2 Gegner:

    ENEMY1  gelber Gegner
    ENEMY2  roter Gegner

In level00.gbas laden wir unsere Objektgrafik und "grabben" die einzelnen Sprites heraus.
Code (glbasic) Select

    // make objects
    LOADSPRITE "./isdo/gfx/object.png",0
    id=512
    FOR y=0 TO 15
        FOR x=0 TO 15
            DRAWRECT 0,0,16,16,COL_TRANSPARENT
            DRAWSPRITE 0,-x*16,-y*16
            GRABSPRITE id,0,0,16,16
            INC id,1
        NEXT
    NEXT


Dann erweitern wir unsere Levelschleife mit dem ObjectHANDLER
Code (glbasic) Select

        GOSUB ObjectHANDLER
den wir wiederum in der library.gbas finden.
Code (glbasic) Select

//=============================================================================
  SUB ObjectHANDLER:
//=============================================================================

LOCAL   ix%

    FOR ix=0 TO Object.Count-1
        IF Object.State[ix]
            SELECT Object.Typ[ix]
                CASE ID_ENEMY1
                    EnemyScripts(ix)
                CASE ID_ENEMY2
                    EnemyScripts(ix)
            ENDSELECT
        ENDIF
    NEXT

ENDSUB // ObjectHANDLER
Dieser kurze Programmabschnitt ist tats?chlich ein Hauptbestandteil von unserem Programm!
Wie bei den Sch?ssen durchlaufen wir eine Liste und pr?fen als Esrtes, ob der entsprechende Listeneintrag noch frei ist. Falls ja, schauen wir um was f?r einen Objekttyp es sich handelt. Je nach Objektyp verweisen wir dann auf ein Objektscript, wo dann das Objekt separat behandelt wird (Verhalten, Bewegung, Kollision etc.).
Beim Aufruf der Funktion EnemyScripts ?bergeben wir mit ix den aktuellen Objektlistenindex und somit die Kontrolle ?ber das entsprechende Objekt.

Zu diesem Zeitpunkt sind schone mehrere Objekteintr?ge vorhanden, da wir mit
Code (glbasic) Select

        IF ScrollX=128 THEN ObjectADD(ID_ENEMY1,320,120)
        IF ScrollX=160 THEN ObjectADD(ID_ENEMY2,320,120)
in level00.gbas bereits Objekte zu unserer Szene hinzugef?gt haben. Mit diesem Befehl ?bergeben wir den Typ und die Position eines neuen Objekts an den ObjectHANDLER.
Code (glbasic) Select

//=============================================================================
  FUNCTION ObjectADD: typ%,posX,posY
//=============================================================================

LOCAL   ix%

    ix=0
Loop1:
    IF ix=Object.Count THEN RETURN
    IF Object.State[ix]=0
        Object.State[ix]=1
        Object.Typ[ix]=typ
        Object.PosX[ix]=posX
        Object.PosY[ix]=posY
    ELSE
        INC ix,1
        GOTO Loop1
    ENDIF
    RETURN
ENDFUNCTION // ObjectADD


Schauen wir uns ein Script nun etwas genauer an. Die wichtigste Variable ist Object.State. Sie beschreibt die Phase in der sich das Objekt befindet. In unserem Beispiel sind das 3 Phasen. In jeder dieser Phasen ver?ndern wir z.B. die Position. Wenn wir die letzte Phase durchlaufen haben, geben wir das Objekt wieder frei.
Code (glbasic) Select

                CASE 1
                    IF Object.PosX[ix] > 160
                        DEC Object.PosX[ix],2
                    ELSE
                        Object.State[ix]=2
                    ENDIF
                CASE 2
                    IF Object.PosX[ix] > 64
                        DEC Object.PosX[ix],2
                        DEC Object.PosY[ix],1
                    ELSE
                        Object.State[ix]=3
                    ENDIF
                CASE 3
                    IF Object.PosX[ix] > -16
                        DEC Object.PosX[ix],2
                    ELSE
                        Object.State[ix]=0
                    ENDIF

Nun brauchen wir das Objekt nur noch zu zeichnen
Code (glbasic) Select

            DRAWSPRITE SPR_ENEMY1+frame,Object.PosX[ix],Object.PosY[ix]








[attachment deleted by admin]
#53
In the forth part we breathe life into our hero and teach him how to shoot.

In the attached samplecode there is a new graphicfile for playersprites and shots isdo/gfx/player.png.

In isdo.gbas first we define again some new types, constants and variables:
Code (glbasic) Select

TYPE TPlayer
        PosX            =   64
        PosY            =   64
        Timer%          =   0
        Dir%            =   0
        Speed           =   1.0
ENDTYPE
PosX und PosY is the position of our playersprite. Timer is a counter we increase every screenrefresh by 1. Dir is the move-direction and Speed defines the move-speed.
Code (glbasic) Select

TYPE TShot
        State%[8]
        PosX[8]
        PosY[8]
        Dir%[8]
        Count%          =   8
ENDTYPE
Here we define a type for 8 shots. This means we will bable to show max. 8 shots at the same time.
State describes the current shot-state, PosX and PosY the shot-position and Dir the shot-direction. Count is the maximum of shots we can display at the same time.
Code (glbasic) Select

// sprites
GLOBAL  SPR_PLAYER%     =   3
GLOBAL  SPR_SHOT%       =   18
In order that we don't have to memorize the appropriate spritenumber, we describe them with a clear constant.
Code (glbasic) Select

// 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
These are direction values wich we use for playersprite and shots.
Then we must define the variables:
Code (glbasic) Select

GLOBAL  Player  AS  TPlayer
GLOBAL  Shot    AS  TShot
and then
Code (glbasic) Select

GLOBAL  ButtonB         =   TRUE
this provide us the state of button B or "x"-key.

In level00.gbas we initialise some of the new variables.
Code (glbasic) Select

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


Code (glbasic) Select

    // 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
here we load graphicdatas for our hero. Nr.1+2 are animation-steps if the player flies straightforward. Nr.3+4 if he moves upright. You can expand this for the rest of directions, if you like.
Code (glbasic) Select

    // 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
With these lines we build sprites for the shots.

In the levelloop we must first set the state of button-B to TRUE.
Code (glbasic) Select

    ButtonB=TRUE
This is important, otherwise the following happens.
First we are in the startmenu. With button-B we confirm our choice. If the level starts and our button-B is still pressed, we shoot instantly.
Through setting on TRUE the program will wait until the release of button.

We increase the counter for spriteanimation.
Code (glbasic) Select

        INC Player.Timer,1


Code (glbasic) Select

        GOSUB ShotHANDLER
        GOSUB PlayerHANDLER

With these two subprograms we manage the shots and the display of hero.

Now let's change to our library in library.gbas.

First we look in the code of PlayerHANDLER.
Code (glbasic) Select

//=============================================================================
  SUB PlayerHANDLER:
//=============================================================================

    DRAWSPRITE SPR_PLAYER+(INTEGER((bAND(Player.Timer,3)/2))),Player.PosX,Player.PosY

ENDSUB // PlayerHANDLER
This is responsible for display the playersprite at the current position.
Code (glbasic) Select

INTEGER((bAND(Player.Timer,3)/2))
gives back according to Player.Timer 0 or 1, wich corresponds to each animation-step. In our program we have only 2.

In the KeyHANDLER there are also a few changes.
First we must define some local variables
Code (glbasic) Select

LOCAL   kr%,kl%,kd%,ku%
They store if the corresponding direction is pressed or not. For this, first initialize.
Code (glbasic) Select

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

Now we check if directions are pressed or not. If yes, we write the value in the local variable. Simultaneously we change the position of player-sprite.
Code (glbasic) Select

    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

Afterwards we add the single variables to a bitmask. This means we have a binary string of "00000". If we press right-button the bitmask looks like "00010", if we press right-button+up-button "10010". That way it's possible to request multiple buttonstates. This mask we save in Player.Dir.
Code (glbasic) Select

    Player.Dir=kr+kl+kd+ku


Now to something unusual. SPR_PLAYER is per definition a constant spriteID. But here we use it as a variable, so by a change of direction we can change the basissprite for animation with just one command. As an example i've done it here for upright.
Code (glbasic) Select

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


Now we teach our hero to shoot.
Code (glbasic) Select

    // player shoot
    IF KEY(BUTTON_B)
        IF ButtonB=FALSE
            ButtonB=TRUE
            GOSUB ShotADD
        ENDIF
    ELSE
        ButtonB=FALSE
    ENDIF
By pressing button-B we jump to routine ShootADD which adds a shot to our scene.

In this routine we first define a local counte variable and init with 0.
Code (glbasic) Select

LOCAL   ix%

    ix=0

Then we run 8 times through a loop wich checks for every possible shot.
Code (glbasic) Select

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

We chek if the counter reaches the las shot.
Code (glbasic) Select

    IF ix=Shot.Count THEN RETURN
If yes, we add no other shot.
Thereafter we check if the current shot-entry is still in using.
Code (glbasic) Select

    IF Shot.State[ix]=0

Then we pass dhe direction-mask and startposition of the new shot.
Code (glbasic) Select

        Shot.Dir[ix]=Player.Dir
        Shot.PosX[ix]=Player.PosX+8
        Shot.PosY[ix]=Player.PosY+10
        Shot.State[ix]=1
Shot.State is a state variable wich gives us the current step of the shot.

In ShootHANDLER we define some local variables and running in a loop all 8 shots again.
Code (glbasic) Select

//=============================================================================
  SUB ShotHANDLER:
//=============================================================================
LOCAL   ix%,ev%,x%,y%

    FOR ix=0 TO Shot.Count-1
        IF Shot.State[ix]
            ....
            ....
        ENDIF
    NEXT

Now we calculate dthe actual shot-position in our eventmap and read out this value.
Code (glbasic) Select

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


As we alredy know, the eventmap contain special attributes. Such an attribute can be a wall or a block at the same position in the LayerFG.Map. Practicaly this means, we end the shot as soon as there is a wall and don't let it fly through.
If the shot hit the wall we free the current shotentry.
Code (glbasic) Select

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

According to the flightdirection we adjust the shotposition, draw the corresponding sprite and check if the shot reach screenborders.
Code (glbasic) Select

                        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


In the next part we will fight against enemies.


[attachment deleted by admin]
#54
Im 4. Teil werden wir unserem Helden Leben einhauchen und ihm beibringen, wie man schiesst.

Im angeh?ngten Beispielcode befinden sich neu die Grafikdaten f?r unser Spielersprite und die Sch?sse isdo/gfx/player.png.

In isdo.gbas definieren wir zuerst wieder einige neue Types, Konstanten und Variablen:
Code (glbasic) Select

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.
Code (glbasic) Select

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.
Code (glbasic) Select

// 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.
Code (glbasic) Select

// 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:
Code (glbasic) Select

GLOBAL  Player  AS  TPlayer
GLOBAL  Shot    AS  TShot
und dann noch
Code (glbasic) Select

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.
Code (glbasic) Select

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


Code (glbasic) Select

    // 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.
Code (glbasic) Select

    // 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.
Code (glbasic) Select

    ButtonB=TRUE
Das ist wichtig da ansonsten folgendes passiert:
Wir befinden uns ja zuerst im Startmenu. Mit dem B-Button best?tigen wir dann unsere Auswahl. Beginnt nun der 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.
Code (glbasic) Select

        INC Player.Timer,1


Code (glbasic) Select

        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.
Code (glbasic) Select

//=============================================================================
  SUB PlayerHANDLER:
//=============================================================================

    DRAWSPRITE SPR_PLAYER+(INTEGER((bAND(Player.Timer,3)/2))),Player.PosX,Player.PosY

ENDSUB // PlayerHANDLER
Diseser ist zust?ndig f?r das jeweilige Anzeigen des Spieler-Sprites an der aktuellen Position.
Code (glbasic) Select

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
Code (glbasic) Select

LOCAL   kr%,kl%,kd%,ku%
diese speichern, ob die jeweilige Richtungstaste gedr?ck wird oder nicht. Dazu erstmal initialisieren.
Code (glbasic) Select

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

Nun pr?fen wir ob in die jeweilige Richtung gedr?ckt wrid. Falls ja schreiben wir den Wert in die lokale Variable. Gleichzeitig ?ndern wir die aktuelle Position des Spieler-Sprites.
Code (glbasic) Select

    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.
Code (glbasic) Select

    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.
Code (glbasic) Select

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


Nun bringen wir unserem Held das Schiessen bei.
Code (glbasic) Select

    // 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.
Code (glbasic) Select

LOCAL   ix%

    ix=0

Dann durchlaufen wir 8x eine Schleife welche f?r jeden m?glichen Schuss eine Pr?fung durchf?hrt.
Code (glbasic) Select

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

Wir pr?fen ob der lokale Z?hler beim letzten Schuss angekommen ist.
Code (glbasic) Select

    IF ix=Shot.Count THEN RETURN
Falls ja, wird kein weiterer Schuss hinzugef?gt.
Anschliessend schauen wir ob der aktuelle Schusseintrag schon belegt ist.
Code (glbasic) Select

    IF Shot.State[ix]=0

Dann ?bergeben wir die Rchtungsmaske und die Startposition vom neuen Schuss.
Code (glbasic) Select

        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.
Code (glbasic) Select

//=============================================================================
  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.
Code (glbasic) Select

            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
Code (glbasic) Select

            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.
Code (glbasic) Select

                        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.

[attachment deleted by admin]
#55
In the third part we handle the levelloop and add the scrolling.

Because some things will seem to be more complex, i can give you following tips:

- make a printout from the code. For me code on paper is more readable than on screen.
- mark the code you already understand respectively what you don't understand.
- try to change a value just to see what happens.

In the attachement you will find again the sample code. New added are the two files level00.gbas which contains the levelloop and library.gbas which contains our generally functions and subroutines. Then, isdo/gfx/level00.png contains back- and foregroundgraphics and also isdo/level00.dat where the indexlists for the tilemaps (see later) are stored.

Ok. In isdo.gbas we free our out commented line again.
Code (glbasic) Select

            CASE AS_LEVEL00 ; AppState=RunLevel00()

Then we change following line from AS_INTRO to AS_MENU so we can skip the intro.
Code (glbasic) Select

GLOBAL  AppState%       =   AS_MENU


We will draw our graphics after each visual display in this order:
1. Backgroundgrapchic (LayerBG)
2. Foregroundgraphic (LayerFG)
3. Statusdisplay on top (at the moment only a black filled rectangle)

For this we have to define some new types.
Code (glbasic) Select

TYPE TLayerFG
        Map%[4096]
ENDTYPE
is an indexlist which contain the tilenumbers of the foregroundgraphic.
Code (glbasic) Select

TYPE TLayerBG
        Map%[2048]
ENDTYPE
is an indexlist which contain the tilenumbers of the backgroundgraphic.
The tilegraphic is stored in isdo/gfx/level00.png and consists on 16x16 tiles and every single one of them has a size of 16x16 pixels.

Our tilemap is build as follows:
Code (glbasic) Select

        foreground                  background
    [00][16][..][4080]          [00][16][..][2032]
    [01][17][..][4081]          [01][17][..][2033]
    [02][18][..][4082]          [02][18][..][2034]
    [03][19][..][4083]          [03][19][..][2035]
    [..][..][..][ .. ]          [..][..][..][ .. ]
    [..][..][..][ .. ]          [..][..][..][ .. ]
    [15][31][..][4095]          [15][31][..][2047]
For example:
Code (glbasic) Select

LayerFG[0]=255
with this we put the bottommost right tile at the uppermost position in or tilemap (foreground).
If you still have any questions according the tilemaps, then please ask again.

Code (glbasic) Select

TYPE TEvent
        Map%[4096]
ENDTYPE
This index-list contain special tileattributes. We need them later. Bute since they are icluded in the file level00.dat we have to define them now.

Now we must define this 3 lists as global variables.
Code (glbasic) Select

//=============================================================================
//  V A R I A B L E S
//=============================================================================

GLOBAL  LayerFG AS  TLayerFG
GLOBAL  LayerBG AS  TLayerBG
GLOBAL  Event   AS  TEvent


New added is also the global variable AppTimer. This is a counter wich will be icreased by 1 on every screen refresh.
Code (glbasic) Select

GLOBAL  AppTimer%       =   0

Then we have the two global variables ScrollX and ScrollY. We need them for scrolling.
Code (glbasic) Select

GLOBAL  ScrollX         =   0
GLOBAL  ScrollY         =   0

The global constant COL_TRANSPARENT defines our transparent color value. In or program this means RGB(248,0,248).
Code (glbasic) Select

GLOBAL  COL_TRANSPARENT =   0xF800F8

   
... ok, let's take a deep breath... and go on...

We look now in the file level00.gbas.

First we create some local auxiliary variables.
Code (glbasic) Select

LOCAL   done%       =   FALSE
LOCAL   x%,y%
LOCAL   ix%,id%,p%
LOCAL   val%
Then we set the scrollposition to startpoint.
Code (glbasic) Select

    ScrollX=0
    ScrollY=0


With the following lines we load our leveldatas in our 3 indexlists.
Code (glbasic) Select

    // load layers
    OPENFILE(1,"./isdo/level00.dat",TRUE)
    FOR p=0 TO 4095
        READUWORD 1,val
        LayerFG.Map[p]=val
    NEXT
    FOR p=0 TO 2047
        READUWORD 1,val
        LayerBG.Map[p]=val
    NEXT
    FOR p=0 TO 4095
        READUWORD 1,val
        Event.Map[p]=val
    NEXT
    CLOSEFILE 1
To create leveldatas ether you can make your own leveleditor or you can create them in worst case by a HEX-editor.

Now we set the general transparencycolor to our predefined value.
Code (glbasic) Select

    SETTRANSPARENCY COL_TRANSPARENT

With
Code (glbasic) Select

    LOADBMP ""
we ensure the backgroundbuffer is empty. Otherwise it is possible that the new graphic will overlay an other.

Now we load our tilegraphic and assing to each of the 16x16 pixel size tiles an own spriteID. We start with the number 256 in case we have to expand if we need more tiles later.
Code (glbasic) Select

    LOADSPRITE "./isdo/gfx/level00.png",0
    id=256
    FOR y=0 TO 15
        FOR x=0 TO 15
            DRAWRECT 0,0,16,16,COL_TRANSPARENT
            DRAWSPRITE 0,-x*16,-y*16
            GRABSPRITE id,0,0,16,16
            INC id,1
        NEXT
    NEXT
Then we clear the temporary sprite again.
Code (glbasic) Select
  LOADSPRITE "",0


From now on we are in the mainloop. That means, 60x per second we draw the whole screen in the backbuffer, and draw it just after that on the visual display.
Code (glbasic) Select

//-----------------------------------------------------------------------------
//  M A I N L O O P
//-----------------------------------------------------------------------------

    WHILE done=FALSE

        IF KEY(BUTTON_START) THEN RETURN AS_MENU

        INC AppTimer,1
        INC ScrollX,0.5

        GOSUB KeyHANDLER
        GOSUB LayerBGDRAW
        GOSUB LayerFGDRAW
        GOSUB StatusDRAW

        SHOWSCREEN
    WEND

We go on step bei step.

First we program an emergency exit. So we don't always have to turn on and off our device. Through pressing START-button (GP2X) or ENTER-key (WIN) we can return to the startmenu.
Code (glbasic) Select

        IF KEY(BUTTON_START) THEN RETURN AS_MENU

With every screen refresh we increase a general counter by 1.
Code (glbasic) Select

        INC AppTimer,1

Then we determine on how many pixels we will scroll each time (foregrund layer). If you like you can try to change this value.
Code (glbasic) Select

        INC ScrollX,0.5

Afterwards let's do the KeyHANDLER in library.gbas his work. His task is to handle all user inputs.
Code (glbasic) Select

//=============================================================================
  SUB KeyHANDLER:
//=============================================================================

    // debug options
    IF KEY(2) THEN LIMITFPS 2
    IF KEY(3) THEN LIMITFPS 30
    IF KEY(4) THEN LIMITFPS 60
    IF KEY(5) THEN LIMITFPS 80

ENDSUB // KeyHANDLER

These 4 lines are only for test pruposes. Through pressing the keys "1"-"4" we can change the display refresh manually. This has the benefit to slow down everything, so we can see for example, if the collisions occours as accurate as possible.

With
Code (glbasic) Select

        GOSUB LayerBGDRAW
        GOSUB LayerFGDRAW

we call 2 subroutines in library.gbas. These are drawing our two layers.

We calculate the position in LayerBG.Map from where we draw the first tile on top left.
Code (glbasic) Select

    // draw background layer
    p=INTEGER(ScrollX / 16 /2)*16
Then we calculate the exact startposition in pixels, where we draw the first column of tiles.
Code (glbasic) Select

    dx=0-(bAND(ScrollX,31) /2)
In opposite to the foreground we have to divide by 2 on the background, so we reach a 2-layer effect.
Now we defin how many columns have to be drawn.
Code (glbasic) Select

    FOR x=0 TO 20
Since our screen resolution is 320x240 pixels this value results in: 320/16=20. But depending on scrollposition drawing starts outside the visible area on the left side. In this cases on the right side you can see the image-buildup. That's not very aesthetic. So we have to draw one more column.
Code (glbasic) Select

        dy=32
This is the exact startposition in pixels where we draw the first line of tiles (remember status display is 32 pixel heigh).
We draw 13 lines ((240-32)/16=13.
Code (glbasic) Select

        FOR y=0 TO 12
Here we don't have to add a line because we are not scrolling up or down.
Now we draw the calculated tile to its position (in pixel) on screen.
Code (glbasic) Select

            DRAWSPRITE LayerBG.Map[p],dx,dy

From now on we go line for line downwards until we reach the buttom border of screen.
Code (glbasic) Select

            INC dy,16
            INC p,1
        NEXT

Then we take the next column.
Code (glbasic) Select

        INC dx,16
        INC p,3
    NEXT
Since our layermap consit a dimension of 16 lines but we draw only 13 of them, we have to overjump 3 listpositions.

Still remains the statusdisplay.
Code (glbasic) Select

        GOSUB StatusDRAW

At the moment we take it easy and draw only a black filled rectangle with a subroutine in our library.
Library auf.
Code (glbasic) Select

    DRAWRECT 0,0,320,32,RGB(0,0,0)


I hopei have everything explained fairly understandable. If not just go into it.

Next time we take control of our "Hero".


[attachment deleted by admin]
#56
In the second part of our tutorial we build executable code. In the attachement you will find the associated GLBasic project.
After unpacking the ZIP-file there should be the following files:
Code (glbasic) Select

[isdo]                             ->    this folder contains the datas from our program
      [gfx]                          ->    here are required graphic datas located
          intro.png                  ->    graphic for intro
          menu.png                   ->    graphic for startmenu         
  2D Shooter Tutorial Part 2.gbap    ->    our GLBasic project file
  isdo.gbas                          ->    program module with program loop
  intro.gbas                         ->    program module with code for visual program start
  menu.gbas                          ->    program module with code for a small startmenu


First we comment out following line in the mainloop (isdo.gbas). At the moment we don't need it.
Code (glbasic) Select

  //CASE AS_LEVEL00 ; AppState=RunLevel00()


After this let's have a closer look to the file intro.gbas.
Code (glbasic) Select

//-----------------------------------------------------------------------------
//  I N I T   I N T R O
//-----------------------------------------------------------------------------

    LOADBMP "./isdo/gfx/intro.png"

Here we load the graphic for our small intro in the backbuffer, so afterwards we can "grab out" then different sprites. That looks like this:
Code (glbasic) Select

    GRABSPRITE 1,0,0,306,52
    GRABSPRITE 2,0,52,75,21
    GRABSPRITE 3,75,52,18,17
    GRABSPRITE 4,112,52,193,32

If you change the graphics on top of intro.png, you will be able to appear your own logo.

Now we let fade in the singel graphic parts.
Code (glbasic) Select

    // mountainsoft
    FOR i=-0.01 TO -1 STEP -0.02
        ALPHAMODE 0
        DRAWRECT 0,0,320,240,RGB(255,255,255)
        ALPHAMODE i
        DRAWSPRITE 1,7,94
        SHOWSCREEN
    NEXT

In the FOR..NEXT loop we define in variable i how much the graphic would be fade in. DRAWRECT is necessary that the grapchic will not overlay itself. Right after this; let's have a break
Code (glbasic) Select

    SLEEP 3000

Then we fade out the graphic.
Code (glbasic) Select

    FOR i=-1 TO 0 STEP 0.02
        ALPHAMODE 0
        DRAWRECT 0,0,320,240,RGB(255,255,255)
        ALPHAMODE i
        DRAWSPRITE 1,7,94
        SHOWSCREEN
    NEXT


At the end we clear the backbuffer again.
Code (glbasic) Select

    ALPHAMODE 0
    BLACKSCREEN

With
Code (glbasic) Select

    RETURN AS_MENU

we pass the control to the mainloop in isdo.gbas again. What would happen if we return the constant AS_INTRO instead of AS_MENU?


Now the startmenu. This code is written in file menu.gbas. Here we need first som local constants and variables.
Code (glbasic) Select

// constants
LOCAL MS_STARTGAME% =   0
LOCAL MS_OPTIONS%   =   1
LOCAL MS_PASSWORD%  =   2
LOCAL MS_EXIT%      =   3
// variables
LOCAL menuState%    =   0
LOCAL buttonUp%     =   FALSE
LOCAL buttonDown%   =   FALSE
LOCAL done%         =   FALSE

MS_ stands for MenuState and describes the appropriate menupoint. buttonUp and buttonDown are used for button states, especially with GP2X.
Code (glbasic) Select

//-----------------------------------------------------------------------------
//  I N I T   M E N U
//-----------------------------------------------------------------------------

    LOADBMP "./isdo/gfx/menu.png"
    GRABSPRITE 0,0,0 ,128,14
    GRABSPRITE 1,0,22,128,14
    GRABSPRITE 2,0,44,128,14
    GRABSPRITE 3,0,66,128,14
    GRABSPRITE 4,0,80,128,80

Like in the intro, first we load the menugraphic and then "grab" the single sprites out of it.

In the loop
Code (glbasic) Select

    WHILE done=FALSE
      ....
      ....
    WEND
    RETURN AS_EXIT

we check the value of done. If it is set to TRUE the program ends. If we look closer we can see that this query is not absolute necessary. But this way we have the possibility to break the program with done=TRUE for test purposes.

Now the first input check. For this we check B-button (GP2X) or "x"-key and look if it is pressed.
Code (glbasic) Select

        IF KEY(BUTTON_B)
        ....
        ....
        ENDIF

According to this we jump to the particular menupoint. For the moment it's only EXIT.
Code (glbasic) Select

            SELECT menuState
                CASE MS_EXIT
                    RETURN AS_EXIT
            ENDSELECT

And here again: What would happen if we return the constant AS_INTRO instead of AS_EXIT?

When we check the cursorkeys or pad for up and down movement, we have to pay attention, that we jump olny to the next menupoint if we have released the cursorkeys or pad. Otherwise the menucursor is running like a wild horse.
Code (glbasic) Select

        IF KEY(BUTTON_DOWN)
            IF menuState<MS_EXIT
                IF buttonDown=FALSE
                    INC menuState,1
                    buttonDown=TRUE
                ENDIF
            ENDIF
        ELSE
            buttonDown=FALSE
        ENDIF


With
Code (glbasic) Select

        DRAWRECT 0,0,320,240,0x0
        DRAWSPRITE 4,96,128
        DRAWSPRITE menuState,96,menuState*22+128

        SHOWSCREEN

we visualise the whole thing.

That's it for the second part. Doesn't hurt, right?
In case you have questions to this tutorial or do not understand something, please ask. If i can help, i will try.







[attachment deleted by admin]
#57
Hi, all

With this tutorial i would like to share my experiences with programming in GLBasic.
It's a framework for a horizontal shooter game and contains routines like intro, startmenu, 2-layer scrolling, collision or sound.

My english is not very well. But nevertheless i hope you will understand it.
If you have any questions or suggestions then please do not post them in this thread but here:http://www.glbasic.com/forum/index.php?topic=2949.0 Thanks.

I will split this tutorial into different parts. So you will have time to learn step by step.
A short example of how it looks at the end is avalable for download here:
Windows version:http://www.zshare.net/download/5735249659b47d37/
GP2X version:http://www.zshare.net/download/57352743c3fbf5c9/

EDIT 27.07.2009: added a simple tilemap editor with sourcecode in attachement

Code (glbasic) Select

control->GP2X:
  startmenu:  pad up/down       = menucursor up and down
              B-button          = select menuentry
  game:       pad               = move
              B-button          = shoot
              START-button      = return to startmenu
control->WIN:
  startmenu:  cursorkey up/down = menucursor up and down
              "x"-key           = select menuentry
  game:       cursorkey up/down = move
              "x"-key           = shoot
              ENTER-key         = return to startmenu


In the first part we start with the program body:

Code (glbasic) Select

//=============================================================================
//  T Y P E S
//=============================================================================


//=============================================================================
//  C O N S T A N T S
//=============================================================================

// application states
GLOBAL  AS_INTRO%       =   1
GLOBAL  AS_MENU%        =   2
GLOBAL  AS_LEVEL00%     =   10
GLOBAL  AS_EXIT%        =   255
// button map
GLOBAL  BUTTON_RIGHT%   =   205
GLOBAL  BUTTON_LEFT%    =   203
GLOBAL  BUTTON_DOWN%    =   208
GLOBAL  BUTTON_UP%      =   200
GLOBAL  BUTTON_A%       =   44
GLOBAL  BUTTON_B%       =   45
GLOBAL  BUTTON_R%       =   54
GLOBAL  BUTTON_START%   =   28
GLOBAL  BUTTON_VOLI%    =   201
GLOBAL  BUTTON_VOLD%    =   209


//=============================================================================
//  V A R I A B L E S
//=============================================================================

// application
GLOBAL  AppState%       =   AS_INTRO

LOCAL   done            =   FALSE


//-----------------------------------------------------------------------------
//  I N I T
//-----------------------------------------------------------------------------
    DRAWRECT 0,0,320,240,0
    SHOWSCREEN
    DRAWRECT 0,0,320,240,0
    SHOWSCREEN

//-----------------------------------------------------------------------------
//  M A I N L O O P
//-----------------------------------------------------------------------------
    WHILE done=FALSE
        SELECT AppState
            CASE AS_INTRO   ; AppState=RunIntro()
            CASE AS_MENU    ; AppState=RunMenu()
            CASE AS_LEVEL00 ; AppState=RunLevel00()
            CASE AS_EXIT    ; done=TRUE
            DEFAULT         ; done=TRUE
        ENDSELECT
    WEND
//-----------------------------------------------------------------------------
//  D E I N I T
//-----------------------------------------------------------------------------
    DRAWRECT 0,0,320,240,0
    SHOWSCREEN
    DRAWRECT 0,0,320,240,0
    SHOWSCREEN



First we reserve place for type definitions which we will add later.
Thereafter follows the constants which we will write always BIG in our program. With AS_INTRO etc. we describe the current state of the program (AS = Application State). Then follows some constants for assignment of keys.
Now we define the variables, whereby we write GLOBALS with a big and LOCALS with a small initial letter. We initialise the global variable AppState with AS_INTRO because we start our program with a little intro.
Then we clear the framebuffer.

Now the mainloop of the program:

If inside the loop
Code (glbasic) Select

WHILE done=FALSE
  ...
WEND

the value of variable done is set to TRUE, we clear the framebuffer again and the program ends.

Code (glbasic) Select

        SELECT AppState
            CASE AS_INTRO   ; AppState=RunIntro()
            CASE AS_MENU    ; AppState=RunMenu()
            CASE AS_LEVEL00 ; AppState=RunLevel00()
            CASE AS_EXIT    ; done=TRUE
            DEFAULT         ; done=TRUE
        ENDSELECT

Here we check the program module to be performed. As we have setted the value of AppState to AS_INTRO it calls the module RunIntro().
The different modules are FUNCTIONS which will return a value. This value will be stored again in variable AppState. On next pass of loop, it will be checked again.

So. That's for first. In the second part we will deal with the intro.





[attachment deleted by admin]
#58
Here you can post your questions and suggestions according to the Horizontal Shooter Tutorial
#59
Hier k?nnt Ihr eure Fragen, Meinungen oder Anregungen zum Horizontal Shooter Tutorial http://www.glbasic.com/forum/index.php?topic=2937.0 posten.
#60
Im dritten Teil gehts nun ans Eingemachte. Wir behandeln die Levelschleife und nehmen gleich noch das Scrolling mit hinzu.

Da manche Sachen nun im ersten Moment vielleicht etwas komplex erscheinen, kann ich euch folgende Tips geben:
- macht eine Ausdruck vom Code. Ich z.B. kann auf Papier ?bersichtlicher lesen als auf dem Bildschirm
- kreuzt an, was ihr vom Code bereits versteht bzw. was ihr noch nicht versteht
- versucht mal den einen oder andern Wert zu ver?ndern um zu sehen, was das bewirkt.

Im Anhang findet ihr wieder den aktuellen Beispiel-Code. Neu hinzugekommen sind die beiden Dateien level00.gbas welche die Levelschleife beinhaltet, library.gbas die allgemeine Funktionen und Subroutinen beinhalten wird. Dann noch isdo/gfx/level00.png (enth?lt Hinter- und Vordergrundgrafik) sowie isdo/level00.dat wo die Indexlisten f?r die Tilemaps (siehe weiter unten) gespeichert sind.

So. In isdo.gbas "befreien" wir wieder unsere auskommentierte Zeile.
Code (glbasic) Select

            CASE AS_LEVEL00 ; AppState=RunLevel00()

Dann ?ndern wir folgende Zeile von AS_INTRO zu AS_MENU damit wir das Intro jeweils ?berspringen k?nnen.
Code (glbasic) Select

GLOBAL  AppState%       =   AS_MENU


Wir werden unsere Grafik nach jeder Anzeige wieder neu aufbauen und zwar in dieser Reihenfolge:
1. Hintergrundgrafik (LayerBG)
2. Vordergrundgrafik (LayerFG)
3. Statusanzeige oben (im Moment nur schwarzes gef?lltes Rechteck)

Dazu m?ssen wir einige neue TYPE's definieren.
Code (glbasic) Select

TYPE TLayerFG
        Map%[4096]
ENDTYPE
ist eine Index-Liste welche die Tilenummern der Vordergrundgrafik enth?lt.
Code (glbasic) Select

TYPE TLayerBG
        Map%[2048]
ENDTYPE
ist eine Index-Liste welche die Tilenummern der Hintergrundgrafik enth?lt.
Die Tilegrafik ist in isdo/gfx/level00.png gespeichert und besteht aus 16x16 Tiles von welchen jedes 16x16 Pixel gross ist.

Unsere Tilemaps sind folgendermassen aufgebaut:
Code (glbasic) Select

        Vordegrund                 Hintergrund
    [00][16][..][4080]          [00][16][..][2032]
    [01][17][..][4081]          [01][17][..][2033]
    [02][18][..][4082]          [02][18][..][2034]
    [03][19][..][4083]          [03][19][..][2035]
    [..][..][..][ .. ]          [..][..][..][ .. ]
    [..][..][..][ .. ]          [..][..][..][ .. ]
    [15][31][..][4095]          [15][31][..][2047]
Als Beispiel:
Code (glbasic) Select

LayerFG[0]=255
Damit setzen wir das unterste rechte Tile an die oberste linken Stelle in unserer Tilemap (Vordergrund).
Falls ihr zu den Tilemaps noch weitere Infos ben?tigt, dann fragt einfach nochmals nach.

Code (glbasic) Select

TYPE TEvent
        Map%[4096]
ENDTYPE
Diese Index-Liste enth?lt besondere Tilemap-Attribute auf welche wir erst zu einem sp?teren Zeitpunkt eingehen werden. Da sie aber auch in der Datei level00.dat integriert sind m?ssen wir sie schon jetzt definieren.

Nun m?ssen diese 3 Listen noch als globale Variablen definiert werden.
Code (glbasic) Select

//=============================================================================
//  V A R I A B L E S
//=============================================================================

GLOBAL  LayerFG AS  TLayerFG
GLOBAL  LayerBG AS  TLayerBG
GLOBAL  Event   AS  TEvent


Neu hinzugekommen ist noch die globale Variable AppTimer. Dies ist ein Z?hler, der bei jedem Bildaufbau um 1 erh?ht wird.
Code (glbasic) Select

GLOBAL  AppTimer%       =   0

Dann gibt's noch die beiden globalen Variablen ScrollX und ScrollY die wir f?r das Scrolling ben?tigen.
Code (glbasic) Select

GLOBAL  ScrollX         =   0
GLOBAL  ScrollY         =   0

Die globale Konstante COL_TRANSPARENT definiert unsere Transparent-Farbe. In unserem Programm w?re das RGB(248,0,248).
Code (glbasic) Select

GLOBAL  COL_TRANSPARENT =   0xF800F8

   
... so, nochmals kurz durchatmen... und weiter...

Schauen wir uns mal die Datei level00.gbas an.

Zuerst erstellen wir einige lokale Hilfsvariablen.
Code (glbasic) Select

LOCAL   done%       =   FALSE
LOCAL   x%,y%
LOCAL   ix%,id%,p%
LOCAL   val%
Dann setzen wir die Scrollposition an den Anfangspunkt.
Code (glbasic) Select

    ScrollX=0
    ScrollY=0


Mit den folgenden Zeilen laden wir die Leveldaten in unsere 3 Listen.
Code (glbasic) Select

    // load layers
    OPENFILE(1,"./isdo/level00.dat",TRUE)
    FOR p=0 TO 4095
        READUWORD 1,val
        LayerFG.Map[p]=val
    NEXT
    FOR p=0 TO 2047
        READUWORD 1,val
        LayerBG.Map[p]=val
    NEXT
    FOR p=0 TO 4095
        READUWORD 1,val
        Event.Map[p]=val
    NEXT
    CLOSEFILE 1
Um die Leveldaten zu erstellen kann man sich entweder selbst einen Level-Editor basteln oder erstellt die Daten notfalls mit einem HEX-Editor.

Jetzt setzen wir die allgemeine Transparenzfarbe auf unseren vorgegebenen Wert.
Code (glbasic) Select

    SETTRANSPARENCY COL_TRANSPARENT

Mit
Code (glbasic) Select

    LOADBMP ""
stellen wir sicher das der Hintergrundbuffer leer ist. Sonst kann sich die neue Grafik mit einer noch eventuell vorhandenen ?berlagern.

Nun Laden wir unsere Tilegrafik und weisen jedem der 16x16 Pixel grossen Tiles eine eigene SpriteID zu. Dabei beginnen wir mit der Nummer 256 damit wir nach oben noch etwas Luft haben, falls wir sp?ter noch mehr Tiles ben?tigen w?rden.
Code (glbasic) Select

    LOADSPRITE "./isdo/gfx/level00.png",0
    id=256
    FOR y=0 TO 15
        FOR x=0 TO 15
            DRAWRECT 0,0,16,16,COL_TRANSPARENT
            DRAWSPRITE 0,-x*16,-y*16
            GRABSPRITE id,0,0,16,16
            INC id,1
        NEXT
    NEXT
und l?schen wieder das tempor?re Sprite.
Code (glbasic) Select
  LOADSPRITE "",0


Ab jetzt folgt die Levelschleife. Das heisst, dass wir 60x pro Sekunde das Bild jedes Mal im Hintergrund aufbauen und anschliessend auf dem Bildschirm anzeigen lassen.
Code (glbasic) Select

//-----------------------------------------------------------------------------
//  M A I N L O O P
//-----------------------------------------------------------------------------

    WHILE done=FALSE

        IF KEY(BUTTON_START) THEN RETURN AS_MENU

        INC AppTimer,1
        INC ScrollX,0.5

        GOSUB KeyHANDLER
        GOSUB LayerBGDRAW
        GOSUB LayerFGDRAW
        GOSUB StatusDRAW

        SHOWSCREEN
    WEND

Gehen wir das Ganze Schritt f?r Schritt durch.

Zuerst programmieren wir uns einen Notausgang damit wir z.B. beim GP2X das Ger?t nicht immer ein- und ausschalten m?ssen. Also gelangen wir durch dr?cken des START-Buttons (GP2X) bzw. Eingabetaste (WIN) zur?ck zum Startmenu.
Code (glbasic) Select

        IF KEY(BUTTON_START) THEN RETURN AS_MENU

Bei jedem Bildaufbau erh?hen wir einen allgemeinen Z?hler jeweils um 1.
Code (glbasic) Select

        INC AppTimer,1

Danach legen wir fest um wieviele Pixel pro Bildaufbau sich der Vordergrund zur Seite bewegt. Ihr k?nnt ja Mal versuchen diesen Wert zu erh?hen oder zu senken.
Code (glbasic) Select

        INC ScrollX,0.5

Anschliessend lassen wir den KeyHANDLER in der library.gbas seine Arbeit verrichten. Dieser hat die Aufgebe alle Usereingaben zu verwalten.
Code (glbasic) Select

//=============================================================================
  SUB KeyHANDLER:
//=============================================================================

    // debug options
    IF KEY(2) THEN LIMITFPS 2
    IF KEY(3) THEN LIMITFPS 30
    IF KEY(4) THEN LIMITFPS 60
    IF KEY(5) THEN LIMITFPS 80

ENDSUB // KeyHANDLER

Diese 4 Zeilen sind nur f?r Testzwecke gedacht. Durch die Tasten "1"-"4" kann die Bildwiederholfrequenz manuell ge?ndert werden. Das hat den Nutzen, dass man zum Beispiel genauer sehen kann, ob etwaige Kollisionen auch m?glichst exakt ausgef?hrt werden.
Mit
Code (glbasic) Select

        GOSUB LayerBGDRAW
        GOSUB LayerFGDRAW

rufen wir 2 Unterprogramme in der library.gbas auf, die unsere beiden Layer zeichnen.

Wir berechnen die Position in der LayerBG.Map von welchem wir das erste Tile oben links zeichnen.
Code (glbasic) Select

    // draw background layer
    p=INTEGER(ScrollX / 16 /2)*16
Dann berechnen wir die genaue Startposition in Pixeln, wo die erste Spalte gezeichnet werden soll.
Code (glbasic) Select

    dx=0-(bAND(ScrollX,31) /2)
Im Gegensatz zum Vodergrund m?ssen wir beim Hintergrund jeweils noch durch 2 teilen damit wir einen 2-Ebenen Effekt erzielen.
Nun definieren wir wieviele Spalten gezeichnet werden m?ssen.
Code (glbasic) Select

    FOR x=0 TO 20
Da unsere Ausgabe eine Aufl?sung von 320x240 Pixeln hat ergibt sich dieser Wert durch: 320/16=20. Da aber je nach Scrollposition schon vor dem sichtbaren Ausschnitt gezeichnet wird, w?rde man auf der rechten Seite den Bidaufbau sehen, was nat?rlich unsch?n ist. Also zeichnen wir noch eine Spalte mehr.
Code (glbasic) Select

        dy=32
Das ist die genaue Startposition in Pixeln, wo die erste Zeile gezeichnet werden soll. (Statusanzeige 32 Pixel hoch)
Wir zeichnen 13 Zeilen ((240-32)/16=13.
Code (glbasic) Select

        FOR y=0 TO 12
Hier m?ssen wir keine Zeile mehr dazu addieren, weil wir ja nicht nach oben oder unten scrollen.
Nun zeichnen wir das errechnete Tile an den pixelgenauen Zielort.
Code (glbasic) Select

            DRAWSPRITE LayerBG.Map[p],dx,dy

Jetzt gehen wir Zeile f?r Zeile nach unten bis wir am unteren Rand der Bildausgabe angekommen sind.
Code (glbasic) Select

            INC dy,16
            INC p,1
        NEXT

Danach geht's zur n?chsten Spalte.
Code (glbasic) Select

        INC dx,16
        INC p,3
    NEXT
Da unsere Layermap eine vertikale Dimension von 16 Zeilen hat, wir aber nur 13 Zeilen zeichnen, m?ssen wir noch 3 Listenpositionen ?berspringen.

Bleibt noch die Statusanzeige.
Code (glbasic) Select

        GOSUB StatusDRAW

Da machen wir's uns im Moment noch einfach und zeichnen nur ein schwarz gef?lltes Rechteck und rufen das entsprechende Unterprogramm in userer Library auf.
Code (glbasic) Select

    DRAWRECT 0,0,320,32,RGB(0,0,0)


Ich hoffe ich hab's einigermassen verst?ndlich erkl?rt. Ansonsten einfach nachhaken.

Das n?chste Mal k?mmern wir uns um die Steuerung unseres Helden.


[attachment deleted by admin]