Saturday, 26 December 2009

SDL : Deteksi Tabrakan (1)

Hal yang paling vital dalam banyak program game adalah deteksi tabrakan/tubrukan. Dimana satu atau lebih objek bersentuhan secara visual, dan teknik yang paling mudah adalah dengan bentuk kotak (box). Berikut ini contoh program yang akan mengimplementasikannya :

/*
Source Code ini merupakan modifikasi
dari template SDL Code::Blocks dan
Source Code ini adalah bahan/materi
untuk blog:

http://membuatgamedengancpp.blogspot.com/

*/
#ifdef __cplusplus
    #include <cstdlib>
#else
    #include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif
#include <string>

const int lebar_layar = 640;
const int tinggi_layar = 480;

/*membuat fungsi untuk memeriksa terjadinya tabrakan (collision)
    antara kotak pertama (A) dengan kotak kedua (B)
*/
bool f_PeriksaTabrakan(SDL_Rect A, SDL_Rect B)
{
    int kiriA, kiriB;
    int kananA, kananB;
    int atasA, atasB;
    int bawahA, bawahB;

    kiriA = A.x;
    kananA = A.x + A.w;
    atasA = A.y;
    bawahA = A.y + A.h;

    kiriB = B.x;
    kananB = B.x + B.w;
    atasB = B.y;
    bawahB = B.y + B.h;

    if (bawahA <= atasB) return false;
    if (atasA >= bawahB) return false;
    if (kananA <=kiriB) return false;
    if (kiriA >=kananB) return false;

    return true;
}

//clsKotak = class Kotak
class clsKotak
{
    SDL_Rect kotak;
    int red,green,blue;
    int kecX, kecY;

public:
    clsKotak(int,int,int,int,int);
    SDL_Rect getKotak();
    void bergerak(int,SDL_Rect O_[]);
    void tampil(SDL_Surface *_O);
};

//konstruktor kelas kotak
clsKotak::clsKotak(int _red, int _green, int _blue, int _X, int _Y)
{
    //variabel red, green, blue (rgb) untuk
    //membedakan warna dengan kotak yang lain
    red = _red;

    //diperiksa nilainya jika ada kesalahan
    if( red < 0) red = 0;
    else if( red > 255) red = 255;

    green = _green;
    if( green < 0 ) green = 0;
    else if( green > 255) green = 255;

    blue = _blue;
    if( blue < 0 ) blue = 0;
    else if( blue > 255) blue = 255;

    kecX = 5+_X%3;
    kecY = 5+_Y%3;

    kotak.x = _X;
    kotak.y = _Y;
    kotak.w = 20;
    kotak.h = 20;
}

SDL_Rect clsKotak::getKotak()
{
    return kotak;
}

void clsKotak::bergerak(int banyakKotak, SDL_Rect kotakLainnya[])
{
    //menggerakan kotak dengan sumbu x
    kotak.x += kecX;

    //memeriksa apakah sih kotak masih ada dilayar
    //atau sudah keluar dari layar
    if( kotak.x < 0){
        kotak.x = 0;
        kecX = -kecX;
    }
    else if( kotak.x + kotak.w > lebar_layar){
        kotak.x = lebar_layar - kotak.w;
        kecX = -kecX;
    }

    //pemeriksaan terhadap kotak lainnya
    for(int c=0;c<banyakKotak;c++)
    {
        if( f_PeriksaTabrakan( kotak, kotakLainnya[c]))
        {
            kecX = -kecX;
            kotak.x += kecX;
            break;
        }
    }

    //kali ini bergerak searah dengan sumbu Y
    kotak.y += kecY;

    if( kotak.y < 0){
        kotak.y = 0;
        kecY = -kecY;
    }
    else if( kotak.y + kotak.h > tinggi_layar){
        kotak.y = tinggi_layar - kotak.h;
        kecY = -kecY;
    }

    //pemeriksaan terhadap kotak lainnya
    for(int c=0;c<banyakKotak;c++)
    {
        if( f_PeriksaTabrakan( kotak, kotakLainnya[c]))
        {
            kecY = -kecY;
            kotak.y += kecY;
            break;
        }
    }
}

void clsKotak::tampil(SDL_Surface *_screen)
{
    red+=rand()%5;
    if( red>220) red = 0;

    green+=rand()%7;
    if( green>220) green = 0;

    blue+=rand()%9;
    if( blue>220) blue = 0;

    //warna diisi sesuai dengan variabel red, green, blue
    SDL_FillRect( _screen, &kotak, SDL_MapRGB( _screen->format, red, green, blue));
}

int main ( int argc, char** argv )
{
    srand((unsigned)time(NULL));

    // initialize SDL video
    if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
    {
        printf( "Unable to init SDL: %s\n", SDL_GetError() );
        return 1;
    }

    // make sure SDL cleans up before exit
    atexit(SDL_Quit);

    // create a new window
    SDL_Surface* screen = SDL_SetVideoMode(lebar_layar, tinggi_layar, 16,
                                           SDL_HWSURFACE|SDL_DOUBLEBUF);
    if ( !screen )
    {
        printf("Unable to set 640x480 video: %s\n", SDL_GetError());
        return 1;
    }

    //bykKotak = banyak Kotak
    const int bykKotak = 50;
    clsKotak *kotakKu[bykKotak];
    for( int c=0;c<bykKotak;c++){
        int X = c*30%(lebar_layar-40);
        kotakKu[c] = new clsKotak(c*200%255,150,c*100%255,X,c*2);
    }

    //membuat variabel fps (frame per second)
    //dan jumlah frame setiap detiknya adalah 24
    int fps = 0;
    const int frame_per_second = 24;

    // program main loop
    bool done = false;
    while (!done)
    {
        //memperoleh sekian milidetik dari SDL_GetTicks()
        fps = SDL_GetTicks();

        // message processing loop
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            // check for messages
            switch (event.type)
            {
                // exit if the window is closed
            case SDL_QUIT:
                done = true;
                break;

                // check for keypresses
            case SDL_KEYDOWN:
                {
                    // exit if ESCAPE is pressed
                    if (event.key.keysym.sym == SDLK_ESCAPE)
                        done = true;
                    break;
                }
            } // end switch
        } // end of message processing

        // DRAWING STARTS HERE

        // clear screen
        SDL_FillRect(screen, 0, SDL_MapRGB(screen->format,255,255,255));

        for( int b=0;b<bykKotak;b++){
            //_ktkLain = kotak lainnya
            SDL_Rect _ktkLain[bykKotak];
            for( int c=0;c<bykKotak;c++){
                if( c != b){
                    _ktkLain[c] = kotakKu[c]->getKotak();
                }
                else{
                    _ktkLain[c].x=0;
                    _ktkLain[c].y=0;
                    _ktkLain[c].w=0;
                    _ktkLain[c].h=0;
                }
            }

            kotakKu[b]->bergerak(bykKotak,_ktkLain);
        }

        for( int c=0;c<bykKotak;c++){
            kotakKu[c]->tampil(screen);
        }

        // DRAWING ENDS HERE

        // finally, update the screen :)
        SDL_Flip(screen);

        //perhitungan frame rate
        int getDetik = SDL_GetTicks() - fps;
        if( getDetik<(1000/frame_per_second)){
            SDL_Delay((1000/frame_per_second) - getDetik);
        }

    } // end main loop

    for( int c=0;c<bykKotak;c++){
        delete kotakKu[c];
    }

    // all is well ;)
    printf("Exited cleanly\n");
    return 0;
}

// - krofz
Atau download file dibawah ini yang berisi project (Code::Blocks) program diatas dan
hasil build/kompilasi-nya:

Link : http://www.mediafire.com/?nrki2l896hba2vs
Size : 132.17 KB

Program diatas nggak perlu gambar yang aneh-aneh dalam proses "Drawing-nya", cuma memanfaatkan "SDL_FillRect()" dan "SDL_Rect (ini yang penting)". Kotak-kotak tersebut akan memantul setelah tertabrak dengan kotak lain atau ingin keluar dari layar. Buat yang masih bingung , nanti penjelasan lengkapnya nyusul yah... hahaha....

Next Part 2
Update 18 April 2013
- krofz

Thursday, 10 December 2009

SDL : Istirahat Sejenak

Kalau program berikut kita kompilasi/build :

#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif

int main ( int argc, char** argv )
{
if( SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return 1;
}

SDL_Surface *screen = NULL;
screen = SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);

if( screen == NULL)
{
return 1;
}

bool selesai = false;
while( selesai == false)
{
SDL_Event event;
while( SDL_PollEvent(&event))
{
switch( event.type)
{
case SDL_QUIT: selesai = true; break;
case SDL_KEYDOWN:
if( event.key.keysym.sym == SDLK_ESCAPE)
{
selesai = true;
} break;
default:;
}
}
SDL_FillRect(screen,0, SDL_MapRGB(screen->format,0,0,255));
SDL_Flip(screen);
}

SDL_Quit();
return 0;
}
Lalu dijalankan dapat dipastikan program akan menggunakan CPU Usage (di Windows dapat dilihat dengan Task Manager - [Ctrl]+[Alt]+[Del]) sebesar 100% alias hampir semuanya digunakan. Bukan berarti program kita amat sangat berat (padahal cuma nampilin layar warna biru polos) tapi karena prosesnya berjalan terus tanpa ada istirahat. Frame yang dihasilkan setiap detiknya sesuai dengan kemampuan komputer untuk mengolahnya, bisa 20, bisa 100 atau 1000 lebih!. Kalau game kita terlalu cepat bisa bikin permainan kurang nyaman begitu pula kalau terlalu lambat (kalau CPU Usage 100% tapi lambat nggak perlu istirahat lagi, soalnya komputer lagi sibuk ngolah! - perlu optimasi!! ). Kalau sudah begitu Frame Per Second (fps) atau Frame Rate harus turun tangan. Membuat fps gampang kok, berikut kodenya:

#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif

int main ( int argc, char** argv )
{
if( SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return 1;
}

SDL_Surface *screen = NULL;
screen = SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);

if( screen == NULL)
{
return 1;
}

//membuat variabel fps (frame per second)
//dan jumlah frame setiap detiknya adalah 20
int fps = 0;
const int frame_per_second = 20;

bool selesai = false;
while( selesai == false)
{
//memperoleh sekian milidetik dari SDL_GetTicks()
fps = SDL_GetTicks();

SDL_Event event;
while( SDL_PollEvent(&event))
{
switch( event.type)
{
case SDL_QUIT: selesai = true; break;
case SDL_KEYDOWN:
if( event.key.keysym.sym == SDLK_ESCAPE)
{
selesai = true;
} break;
default:;
}
}
SDL_FillRect(screen,0, SDL_MapRGB(screen->format,0,0,255));
SDL_Flip(screen);

//perhitungan frame rate
int getDetik = SDL_GetTicks() - fps;
if( getDetik < (1000/frame_per_second)){
SDL_Delay((1000/frame_per_second) - getDetik);
}
}

SDL_Quit();
return 0;
}
Pertama kita membuat variabel (fps) untuk menampung waktu berupa milidetik dari SDL_GetTicks(). Lalu setelah sampai di ujung pengulangan program akan istirahat jika pekerjaannya sudah beres sesuai waktu yang ditentukan. Dalam contoh diatas program harus menampilkan 20 frame (putaran pengulangan) setiap detiknya. Atau sekitar 50 milidetik setiap framenya, jika program menyelesaikannya dalam 15 milidetik itu berarti program punya waktu 35 milidetik untuk istirahat. Tetapi tidak sebaliknya jika program menyelesaikan dalam 50 atau lebih milidetik. Selanjutnya membandingkan dengan variabel getDetik seberapa banyak milidetik yang diperlukan setiap frame. Misalnya fps bernilai 12300 milidetik, lalu getDetik bernilai (misal) 20 didapat dari nilai SDL_GetTicks() saat ini misalnya 12320 dan dikurangi fps (12300). Jika getDetik (20) lebih kecil dari 50 (1000/20) maka program dapat istirahat sebanyak sisa waktu yaitu 30 milidetik (1000/20 - 20). Moga-moga bener perhitungannya hahah... . Berikut deklarasi SDL_GetTicks() :

/* Get the number of milliseconds since the SDL library initialization.
* Note that this value wraps if the program runs for more than ~49 days.
*/
extern DECLSPEC Uint32 SDLCALL SDL_GetTicks(void);


- krofz
 

back to top

back to top