Memantulkan Objek

Lanjut lagi belajar SDL-nya!? Kalau kalian belum mau lanjut karena belum paham dasar SDL, sebaiknya balik lagi ke bagian yang belum kalian pahami. Kalau kalian mau lanjut, mari kita lanjutkan belajarnya!

Selanjutnya, kita akan belajar tentang cara membuat bola terlihat memantul bolak balik saat menabrak dinding atau tepian window. Untuk membuat bola yang berbentuk lingkaran, kalian bisa menggunakan function untuk menggambar lingkaran.

Kali ini, kita tidak akan menggunakan gambar. Tapi kalian bisa mengganti lingkarannya dengan gambar dari file image jika kalian mau.
#include <SDL2/SDL.h>
#include <math.h>
#define playar 640
#define tlayar 480

SDL_Renderer *trender=NULL;
struct Lingkaran{
    int x;
    int y;
    int r;
};

int warnai_lingkaran(SDL_Renderer *trender, int xpos, int ypos, int r){
int tx, ty, i;
    for(i=0;i<r;i++){
        tx=i;
        ty=sqrt(r*r-i*i);
        SDL_RenderDrawLine(trender, xpos+tx, ypos+ty, xpos+tx, ypos-ty);
        SDL_RenderDrawLine(trender, xpos-tx, ypos+ty, xpos-tx, ypos-ty);
    }
    return 0;
}

int gambar_lingkaran(SDL_Renderer *trender, int xpos, int ypos, int r){
int tx1, ty1, tx2, ty2, i;
    for(i=0;i<r;i++){
        tx1=i;
        ty1=sqrt(r*r-tx1*tx1);

        tx2=i+1;
        ty2=sqrt(r*r-tx2*tx2);
        SDL_RenderDrawLine(trender, xpos+tx1, ypos+ty1, xpos+tx2, ypos+ty2);
        SDL_RenderDrawLine(trender, xpos-tx1, ypos+ty1, xpos-tx2, ypos+ty2);
        SDL_RenderDrawLine(trender, xpos+tx1, ypos-ty1, xpos+tx2, ypos-ty2);
        SDL_RenderDrawLine(trender, xpos-tx1, ypos-ty1, xpos-tx2, ypos-ty2);
    }
    return 0;
}

int main(int argc, char* args[]){
    SDL_Window* window = NULL;
    SDL_Event evt;
    Lingkaran lk;
    SDL_Rect rct;
    int xvel=2, yvel=2, berikutnya=0;
    
    lk.x=playar/2;
    lk.y=tlayar/2;
    lk.r=12;
    
    rct.x=lk.r;
    rct.y=lk.r;
    rct.w=playar-20;
    rct.h=tlayar-20;

    if( SDL_Init( SDL_INIT_VIDEO ) < 0){
        printf( "Error: %s\n", SDL_GetError() );
    }else{
        window = SDL_CreateWindow( "SDLku", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, playar, tlayar, SDL_WINDOW_SHOWN );
        if( window == NULL ){
            printf( "Error : %s", SDL_GetError() );
        }else{
            trender = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
            
            while(evt.type!=SDL_QUIT){
                while(SDL_PollEvent(&evt)){
                }

                SDL_SetRenderDrawColor(trender, 0xFF, 0xFF, 0xFF, 0xFF);
                SDL_RenderClear(trender);
            
                SDL_SetRenderDrawColor(trender, 0xFF, 0x00, 0x00, 0xFF);
                warnai_lingkaran(trender, lk.x, lk.y, lk.r);
            
                SDL_SetRenderDrawColor(trender, 0xFF, 0xFF, 0x00, 0xFF);
                SDL_RenderDrawRect(trender, &rct);
        
                SDL_RenderPresent(trender);
                if(SDL_GetTicks() >= berikutnya){
                    berikutnya=SDL_GetTicks()+25;
                    if(lk.x <= rct.x || lk.x >= rct.x+rct.w){
                        xvel*=-1;
                    }
                    if(lk.y <= rct.y || lk.y >= rct.y+rct.h){
                        yvel*=-1;
                    }
                    lk.x+=xvel;
                    lk.y+=yvel;
                }
            }
        }
    }

    SDL_DestroyWindow( window );
    SDL_Quit();

    return 0;
}

Kode program di atas menggunakan variabel xvel dan yvel untuk mengatur jarak dan perpindahan objek. Saat terjadi tabrakan dengan dinding atau sisi-sisi dari window, arah dari xvel atau yvel akan dibalik sesuai dengan bagian mana yang ditabrak. Pembalikan arah dilakukan dengan melakukan perkalian variabel dengan -1. Pengecekan tabrakan dilakukan saat objek bergerak dengan jeda yang sudah ditentukan.

Dalam kode program di atas, kita menggunakan SDL_GetTick untuk mengatur jeda waktu gerakan objek. Dalam percabangan yang menggunakan SDL_GetTick, kita menggunakan dua percabangan untuk melakukan pengecekan tabrakan sebelum objek digerakkan.  Jika  objek menabrak sisi kiri atau kanan, maka arah xvel diubah dengan dikalikan negatif. Jika yang ditabrak adalah atas atau bawah, maka nilai yang diubah adalah nilai yvel. Pada kode program ini, objek dinilai menabrak dinding jika titik pusat objek yang berbentuk lingkaran berada pada jarak kurang dari jari-jari lingkaran.

Supaya pengecekan lebih mudah, saya menggunakan variabel rct untuk untuk menyimpan koordinat x dan y yang jaraknya 10 cm dari "dinding". Selain itu, saya menampilkan persegi panjang untuk menandai area tersebut. Intinya, bagian kode yang berguna untuk mengecek tabrakan adalah bagian di bawah ini.
if(lk.x < rct.x || lk.x > rct.x+rct.w){
    xvel*=-1;
}
if(lk.y < rct.y || lk.y > rct.y+rct.h){
    yvel*=-1;
}
lk.x+=xvel;
lk.y+=yvel;

Pantulan berdasarkan Sudut
Ada cara lain yang bisa digunakan selain cara di atas, yaitu dengan menggunakan sudut, sin dan cos. Kode program yang menggunakan sin dan cos bisa kalian lihat di bawah ini.
#include <SDL2/SDL.h>
#include <math.h>
#define playar 640
#define tlayar 480
#define grad 3.14285714286/180

SDL_Renderer *trender=NULL;

struct Lingkaran{
    float x;
    float y;
    int r;
};

int warnai_lingkaran(SDL_Renderer *trender, int xpos, int ypos, int r){
int tx, ty, i;
    for(i=0;i<r;i++){
        tx=i;
        ty=sqrt(r*r-i*i);
        SDL_RenderDrawLine(trender, xpos+tx, ypos+ty, xpos+tx, ypos-ty);
        SDL_RenderDrawLine(trender, xpos-tx, ypos+ty, xpos-tx, ypos-ty);
    }
    return 0;
}

int gambar_lingkaran(SDL_Renderer *trender, int xpos, int ypos, int r){
int tx1, ty1, tx2, ty2, i;
    for(i=0;i<r;i++){
        tx1=i;
        ty1=sqrt(r*r-tx1*tx1);

        tx2=i+1;
        ty2=sqrt(r*r-tx2*tx2);
        SDL_RenderDrawLine(trender, xpos+tx1, ypos+ty1, xpos+tx2, ypos+ty2);
        SDL_RenderDrawLine(trender, xpos-tx1, ypos+ty1, xpos-tx2, ypos+ty2);
        SDL_RenderDrawLine(trender, xpos+tx1, ypos-ty1, xpos+tx2, ypos-ty2);
        SDL_RenderDrawLine(trender, xpos-tx1, ypos-ty1, xpos-tx2, ypos-ty2);
    }
    return 0;
}

int main(int argc, char* args[]){
    SDL_Window* window = NULL;
    SDL_Event evt;
    Lingkaran lk;
    SDL_Rect rct;
    int xawal, yawal;
    int sudut=45;
    int jradius=0, berikutnya=0;
    
    lk.x=playar/2;
    lk.y=tlayar/2;
    lk.r=12;
    
    xawal=lk.x;
    yawal=lk.y;
    
    rct.x=lk.r;
    rct.y=lk.r;
    rct.w=playar-20;
    rct.h=tlayar-20;

    if( SDL_Init( SDL_INIT_VIDEO ) < 0){
        printf( "Error: %s\n", SDL_GetError() );
    }else{
        window = SDL_CreateWindow( "SDLku", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, playar, tlayar, SDL_WINDOW_SHOWN );
        if( window == NULL ){
            printf( "Error : %s", SDL_GetError() );
        }else{
            trender = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
            
            while(evt.type!=SDL_QUIT){
                while(SDL_PollEvent(&evt)){
                }

                SDL_SetRenderDrawColor(trender, 0xFF, 0xFF, 0xFF, 0xFF);
                SDL_RenderClear(trender);
            
                SDL_SetRenderDrawColor(trender, 0xFF, 0x00, 0x00, 0xFF);
                warnai_lingkaran(trender, lk.x, lk.y, lk.r);
            
                SDL_SetRenderDrawColor(trender, 0xFF, 0xFF, 0x00, 0xFF);
                SDL_RenderDrawRect(trender, &rct);
        
                SDL_RenderPresent(trender);
                if(SDL_GetTicks() >= berikutnya){
                    berikutnya=SDL_GetTicks()+25;
                    if(lk.x < rct.x || lk.x > rct.x+rct.w || lk.y < rct.y || lk.y > rct.y+rct.h){
                        jradius=0;
                        xawal=lk.x;
                        yawal=lk.y;
            
                        if(lk.x < rct.x){
                            sudut=360-sudut;
                            xawal=rct.x;
                        }else if(lk.x > rct.x+rct.w){
                            sudut=360-sudut;
                            xawal=rct.x+rct.w;
                        }

                        if(lk.y < rct.y){
                            sudut=180-sudut;
                            yawal=rct.y;
                        }else if(lk.y > rct.y+rct.h){
                            sudut=180-sudut;
                            yawal=rct.y+rct.h;
                        }
                    }
   
                    jradius+=2;
                    lk.x=sin(sudut*grad)*jradius+xawal;
                    lk.y=cos(sudut*grad)*jradius+yawal;
                }
            }
        }
    }

    SDL_DestroyWindow( window );
    SDL_Quit();

    return 0;
}
Hasil kode program di atas hampir sama dengan kode program sebelumnya kalau kalian menggunakan sudut 45 derajat. Bola / lingkaran akan bergerak lurus berdasarkan sudut yang sudah kalian tentukan. Setelah menabrak dinding, sudut akan diubah sehingga arahnya juga berubah. Kode program di atas menggunakan penerapan "sudut datang sama dengan sudut pantul". Kalian bisa mengganti nilai variabel sudut dengan sudut lain kalau kalian mau.

Perhatikan angka 180 dan 360! Kalau kalian menukar sin dan cos saat menghitung koordinat x dan y, maka dua angka tersebut juga perlu ditukar. Kedua angka tersebut dikurangi dengan sudut sebelumnya supaya bisa menghasilkan sudut pantul dan sudut datang yang sama.

Pada kode program yang terakhir, pengecekan tabrakan lebih panjang. Walaupun begitu, gerakan dari bola bisa terlihat lebih alami dan bebas ditentukan sudutnya.
if(lk.x < rct.x || lk.x > rct.x+rct.w || lk.y < rct.y || lk.y > rct.y+rct.h){
    jradius=0;
    xawal=lk.x;
    yawal=lk.y;
            
    if(lk.x < rct.x){
        sudut=360-sudut;
        xawal=rct.x;
    }else if(lk.x > rct.x+rct.w){
        sudut=360-sudut;
        xawal=rct.x+rct.w;
    }

    if(lk.y < rct.y){
        sudut=180-sudut;
        yawal=rct.y;
    }else if(lk.y > rct.y+rct.h){
        sudut=180-sudut;
        yawal=rct.y+rct.h;
    }
}