Main forum > Tutorials

Horizontal Shooter Tutorial

<< < (2/2)

bigsofty:
Excellent tutorial, well done!  I like the way this is evolving on a difficulty level  :good:

Steinbock:

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) ---        IF ScrollX=128 THEN ObjectADD(ID_ENEMY1,320,120)
        IF ScrollX=160 THEN ObjectADD(ID_ENEMY2,320,120)

--- End code ---
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) ---        IX%             =   0
        ID%[256]
        Pos%[256]
        X%[256]
        Y%[256]

--- End code ---
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) ---    // 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

--- End code ---
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) ---//=============================================================================
  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

--- End code ---
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) ---        CollTop
        CollBottom
        CollLeft
        CollRight

--- End code ---
The size of the collisionbox of objects we specify with function

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

--- End code ---
For test pruposes you can outcomment the following 3 lines. Then the collisionboxes appear half-transparent.

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

--- End code ---

For the player we build a separate box.

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

--- End code ---
the same for the 8 shots in ShotHANDLER.

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

--- End code ---
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) ---//=============================================================================
  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

--- End code ---
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) ---//=============================================================================
  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

--- End code ---
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) ---            SetCollBox(ix,0,0,15,15)

--- End code ---
Sets the size of collisionbox.
Then we check if ENEMY1 was hitted by a playershot.

--- Code: (glbasic) ---            IF CheckCollShot(ix)=COLL_SHOT
                Object.State[ix]=0
                EffectADD(ID_EFFECT1,Object.PosX[ix],Object.PosY[ix])
            ENDIF

--- End code ---
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) ---        GOSUB KeyHANDLER
        GOSUB LayerBGDRAW
        GOSUB ShotHANDLER
        GOSUB EventHANDLER
        GOSUB ObjectHANDLER
        GOSUB PlayerHANDLER
        GOSUB LayerFGDRAW
        GOSUB EffectHANDLER
        GOSUB StatusDRAW

--- End code ---
that we draw effects AFTER objects. Otherwise perhaps we can' see the effects behind other objects.









[attachment deleted by admin]

Steinbock:
In the last part we look, how to add a power-up and thus change the shotpower.
Then finally we add a backgroundmusic and some soundeffects. As always there is an attachement with samplecode.

If you worked through the 6th part, you know, we have 2 different enemytypes: ENEMY1 and ENEMY2.
In the program we can see ENEMY2 as RED enemy. Now this one shall, if it is hitted, leave a power-up.
For this we just have to add an object in our EnemySCRIPT.

--- Code: (glbasic) ---                ObjectADD(ID_POWERUP,Object.PosX[ix],Object.PosY[ix])

--- End code ---
The script for the power-up item on the other hand you will find in file items.gbas.

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

--- End code ---
We check this item on collision with the player. If a collision occurs, we close the item in the objectlist and change our shot.

Now to the sound. This is an easy thing under GLBasic.
In file level000.gbas we load our soundeffects and allocate them a constant.

--- Code: (glbasic) ---    LOADSOUND "./isdo/sfx/shot.wav",SFX_SHOT,4
    LOADSOUND "./isdo/sfx/explosion.wav",SFX_EXPLOSION,4
    LOADSOUND "./isdo/sfx/extra.wav",SFX_EXTRA,4

--- End code ---
Thereafter we load our backgroundmusic, setting the volume and play it.

--- Code: (glbasic) ---    PLAYMUSIC "./isdo/sfx/music.wav",TRUE
    MUSICVOLUME 0.9

--- End code ---

In GLBasic a soundeffect will be played with the command PLAYSOUND.
If you look on the ItemSCRIPT for example

--- Code: (glbasic) ---
                PLAYSOUND(SFX_EXTRA,GetPan(Object.PosX[ix]),0.7)

--- End code ---
the soundeffect with ID SFX_EXTRA will be played with a volume of 0.7.
Function GetPan in our library causes, the effect sounds depending of objectposition (left/right).

Ok. That's it. Now i hope to helped some of you with this tutorial. And as i said, if i can help someone, i will try to do.

As a little bonus you can download here isdo_090406.zip - 11.13MB a 1min section of my project I.S.D.O. The playercollision is off. The backgroundmusics origin: "MOD-Archiv".

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

--- End code ---
And now enjoy!!  =D


   

[attachment deleted by admin]

sf-in-sf:
Hi!
Using CONSTANT is safer than GLOBAL or LOCAL when declaring genuine constants.

Navigation

[0] Message Index

[*] Previous page

Go to full version