LWJGL Java Game Dev

Game Loop LWJGL Game Development

May 16, 2019

Dalam membuat game komponen paling dasar yang perlu di pelajari adalah game loop. Yang mana pada game loop ini di proses harus di lakukan dengan sangat hati - hati agar game kita tidar terlalu banyak memakan resource yang mana akan membuat game terasa lebih mulus.

Tulisan ini adalah lanjutan dari series tutorial game development dengan LWJGl. Silahkan baca tutorial sebelumnya yaitu setup project LWJGL disini.

LWJGL bukanlah game engine yang mana tidak mengurus logika dari game yang akan kita buat. Untuk itu kita perlu definisikan sendiri bagaimana caranya kita mengurus logic pada game yang akan di buat.

Dengan begini kita akan lebih leluasa untuk menentukan seperti apa logic dari game yang akan kita buat dan kita juga bisa mengoptimalisasikan game yang akan di develop.

Akan tetapi membuat game engine bukanlah pekerjaan yang mudah, untuk itu kita perlu belajar secara bertahap. Mari kita bahas untuk Game Loop pada game engine yang akan kita buat.

The Game Loop

Mari kita bicara teori sedikit ya. Yaitu Game Loop, jadi game loop ini adalah sebuah prosess yang berjalan terus menerus yang mana akan di close proses game di stop, seperti saat user menutup game maka game loop juga akan berhenti.

Berikut ini adalah contoh membuat game loop paling sederhana :

while (keepOnRunning) {
        handleInput();
        updateGameState();
        render();
}

Contoh game loop diatas sudah bisa digunakan untuk membuat game. Akan tetapi kita akan memiliki masalah dengan struktur game loop diatas. Yang pertama kecepatan loop pada setiap komputer itu berbeda - beda.

Apalagi saat ini sudah banyak sekali komputer yang kecepatanya sangat baik, yang mana bisa menyebabkan user kita tidak melihat apa - apa saat berinteraksi dengan game yang kita buat.

Untuk itu kita perlu mendesain game kita berjalan dengan konstan. Untuk itu kita perlu menentukan FPS ( Frame per Second ) atau jumlah gambar yang akan di produksi oleh VGA dalam waktu satu detik.

Pada umumnya ada dua standar FPS yang banyak di gunakan yaitu 30FPS dan 60FPS. Akan tetapi untuk tutorial kali ini kita akan coba menggunakan 50FPS. Semakin tinggi FPS akan semakin smooth game akan tetapi semakin berat untuk di proses oleh komputer untuk itu tentukan FPS sesuai dengan game kamu.

Untuk itu kira - kira begini adalah konsep untuk game loop untuk menjalankan konstan loop.

Game Loop Concept

Nah untuk kode yang akan kita buat kira - kira begini :

double FPS = 1.0d / 50.0d;

while (keepOnRunning) {
        double now = getTime();
        handleInput();
        updateGameState();
        render();
        sleep(now + FPS - getTime());
}

Game loop ini bisa digunakan untuk beberapa game, akan tetapi juga akan menimbulkan beberapa masalah. Yang pertama adalah penetapan blocking loop pada konstan 50FPS atau sama dengan 20 ms.

Yang berarti proses loop akan berhentik setiap 20ms, akan tetapi kita akan mengalami masalah bila dalam proses handleInput dan updateGamestate juga ngeblock loop juga karena melakukan prosess komputasi yang berat.

Dengan begitu bisa menyebabkan proses loop tidak akan di block dalam waktu 20ms lagi. Yang mana untuk game yang mengeksekusi proses kalkulasi fisika tidak akan berjalan dengan bagus.

Ada banyak sekali algoritma game loop, akan tetapi kali ini kita gunakan algoritma yang tidak terlalu kompleks.

Yang pertama kita akan mengkontrol secara terpisah untuk update game state dan render game . Kenapa kita harus memisahkan proses ini?

Well, melakukan proses game state dengan waktu yang konstan lebih penting daripada proses rendering. Dengan begini jika dalam periode waktu tertentu ada frame yang telat untuk di render maka tidak perlu di render.

Dengan begitu jika kita melakukan proses fisika dalam game state maka kita akan mendapatkan frame yang di render dengan tepat waktu dan game kita terasa lebih enak untuk dimainkan.

Kira - kira seperti ini visualisasinya.

Game Loop Fixed

Nah untuk koding nya sepert ini :

double secsPerUpdate = 1.0d / 30.0d;
double previouse = getTime();
double steps = 0.0;

while (keepOnRunning) {
        double loopStartTime = getTime();
        double elapsed = loopStartTime - previous;

        previous = loopStartTime;
        steps += elapsed;

        handleInput();
        while (steps >= secsPerUpdate) {
            updateGameState();
                steps -= secsPerUpdate;
        }
        render();
        sync(loopStartTime);
}

Dengan begini update state dilakukan dalam waktu yang fixed untuk itu kita perlu mencegah proses rendering sesuai dengan fps yang kita ingin kan. Maka kita tambahkan proses untuk blocking loop seperti berikut ini.

private void sync(double loopStartTime) {
    float loopSlot = 1f / 50;
        double endTime = loopStartTime + loopSlot;
        while(getTime() < endTime) {
            try {
                    Thread.sleep(1);
                }catch (InterupptedException ie) {}
        }
}

Jadi sebenarnya apa yang kita lakukan dengan kode di atas? Singkatnya kita menghitung berapa detik perulangan game loop harus berjalan ( yang mana di simpan pada variable loopSlot ) and kita menunggu berapa waktu yang di perlu dihabiskan dalam game loop kita.

Akan tetapi kita tidak melakukan blocking thread dalam sekali kita lakukan dalam waktu singkat yaitu 1ms. Dengan begini kita bisa menghindari proses blocking loop dengan waktu fixed, sehingga kita tidak melulu menunggu waktu block yang 20ms seperti yang sudah kita bahas sebelumnya.

Dengan begini bisa saja waktu blocking nya hanya butuh 2ms atau 10ms atau yang lainya yang mana dengan begini kita bisa menghasilkan FPS yang lebih akurat.

Cara lain untuk menghandle proses rendering selain cara di atas dimana kita melakukan micro sleep untuk mengontrol berapa waktu untuk blocking game loop yang sesuai fps game kita.

Kita bisa menggunakan pendekatan lain untuk membatasi frame rate. Dengan menggunakan v-sync ( sinkronisasi vertikal ). Dengan v-sync ini kita bisa menghindari screen tearing.

Screen taring adalah efet visual yang di hasilkan ketika kita memperbarui memori video saat sedang memproduksi gambar. Untuk mengaktifkan v-sync seperti berikut ini :

glfwSwapInterval(1);
glfwSwapBuffers(windowHandle);

Seperti yang sudah kita bahas, standar frame rate VGA itu 30fps dan 60fps. Disini kita set swap intervalnya 1 maka kita akan mendapatkan frame rate 60fps. Jika gwlSwapInterval kita set menjadi 2 maka kita akan mendapatkan frame rate 30fps.

Let's create a stupid game

Sekarang mari kita buat sebuah game sederhana yang mana jika kita tekan tombol Arrow Up atau panah keatas dan Arraw Down atau panah kebawah maka warna window nya akan berubah.

Sangat simple sekali, dibawah ini contoh demo aplikasinya :

Game Engine

Nah kita akan membuat game engine sederhana untuk mengorganisasi kodingan yang akan kita buat. Untuk itu pertama kita buat blue print dari game logic nya.

Buat interface beri nama IGameLogic.java :

GLFW

Seperti yang sudah kita bahas sebelumnya LWJGL ini adalah wrapper untuk beberapa software manipulasi tampilan seperti OpenGL, OpenGL ES, dan VULKAN.

Disini kita akan menggunakan OpenGL sebagai API untuk manipulasi tampilan dari game yang akan kita buat, untuk itu kita perlu belajar sedikit tentang proses yang terjadi dibelakang layar.

Apa itu GLFW? Well, GLFW ini adalah framework untuk mengakses window dan input yang mana window ini nanti menjadi tempat untuk kita membuat interface dari game kita. Dan input untuk mengolah interaksi dengan game kita.

OpenGL itu adalah API untuk graphic rendering yang mana dia tidak ada urusan dengan window, mouse dan keyboard. Agar kita punya akses untuk input dan display maka kita perlu menggunakan GLFW ini.

Dan satu hal lagi OpenGL itu adalah software tentang manipulasi tampilan yang multi platform. Nah untuk itu OpenGL tidak terpaku dengan context. Yang mana context disini bisa berbeda.

Apa itu context? Jadi context ini adalah sebuah data yang merepresentasikan OpenGL state. Context akan mengolah state dari OpenGL untuk di tampilkan pada sistem dan device tertentu.

Untuk itu context dari android, ios, macos, linux dan windows bisa berbeda. Karena mereka menghandle context dari OpenGL ini dengan caranya masing - masing sesuai dengan desain dari setiap sistemnya.

Untuk itu GLFW ini juga digunakan untuk membuat Context untuk OpenGL state.

Hal ini lah yang membuat kita untuk memungkinkan menulis sebuah kode program yang bisa berjalan pada banyak sistem yang mana sistem yang berbeda itu tadi mengolah data dari OpenGL.

GLFW ini adalah sebuah framework atau library yang ditulis dengan bahasa C kamu bisa baca dokumentasi lengkapnya di sini.

Sekarang kita buat window manager menggunakan glfw, untuk mengolah display window dari game kita. Window.java . Agar tutorial ini tidak terlalu panjang silahkan lihat disini untuk kode lengkapnya.

Berikut ini penjelasan dari bagian - bagian yang perlu di pahami. Yang pertama kita buat konfigurasi windows kita, berapa versi OpenGL yang kita support kemudian berapa ukuran with dan hright dari windowsnya dan apa title dari windowsnya.

glfwDefaultWindowHints();
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

windowHandle = glfwCreateWindow(width, height, title, NULL, NULL);
if (windowHandle == NULL) {
    throw new RuntimeException("Failed to create the GLFW Window");
}

Kita juga perlu mendeteksi ukuran dari windows jika di buat fullscreen oleh user dan kita menyimpan state dari width dan height.

glfwSetFramebufferSizeCallback(windowHandle, (windowHandle, width, height) -> {
            this.width = width;
            this.height = height;
            this.setResized(true);
});

Dan kita juga perlu mendeteksi input keyboard. Yang mana ini nanti digunakan untuk mengolah interaksi dari game kita.

glfwSetKeyCallback(windowHandle, (window, key, scancode, action, mods) -> {
            if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) {
                glfwSetWindowShouldClose(window, true);
            }
});

Dan ketika game kita dibuka kita ingin posisi window berada di tengah layar. Untuk itu kita bisa melakukanya seperti berikut ini.

GLFWVidMode vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(
        windowHandle,
        (vidMode.width() - width) / 2,
        (vidMode.height() - height) / 2
);

Game Loop Engine

Sekarang kita buat kode dari teori yang sudah kita bahas panjang lebar tadi yaitu tentang Game Loop.

Dari sini kita sudah selesai membuat Game Engine sederhana dan kita siap untuk membuat game.

Colored Game

Nah mari buat logika dari game yang sudah kita lihat bersama demonya diatas. Idenya cukup simple, kita merubah warna dari window saat kita mendeteksi user input. Yaitu pada saat tombol arrow keyboard di tekan.

Maka yang kita lakukan pertama adalah mendeteksi input :

@Override
public void input(Window window) {
    if (window.isKeyPressed(GLFW_KEY_UP)){
        direction = 1;
    }else if(window.isKeyPressed(GLFW_KEY_DOWN)){
        direction = -1;
    }else {
        direction = 0;
    }
}

Selanjutnya kita merubah state dari game :

@Override
public void update(float interval) {
    color += direction * 0.01f;
    if (color > 1) {
        color = 1.0f;
    }else if(color < 0) {
        color = 0.0f;
    }
}

Dan yang terakhir adalah menampilkan di window :

@Override
public void render(Window window) {
    if (window.isResized()) {
        glViewport(0,0,window.getWidth(), window.getHeight());
        window.setResized(false);
    }

    window.setClearColor(color, 0.5f, color, 0.0f);
    renderer.clear();

    window.update();

}

Dan dengan begini kita sudah selesai membuat game sederhana kita. Untuk baris kode lengkapnya disini.

Summary

Ok kita sudahi dulu ya tutorial ini. Saya menulisnya ada sekitar 6 jam. Semoga kamu dapat memahami point dari materi yang saya sampaikan. Untuk itu mari kita ingat kembali point dari materi yang kita bahas ini.

Dalam membuat game kita perlu mendesain game loop yang mana disini adalah mesin dari game kita di konstruksikan.

Selanjutnya kita juga perlu mengelola OpenGL context, input, dan display ke multi platform yang mana ini bisa di selesaikan dengan GLFW.

Dan yang terkahir kita kombinasikan teori yang kita bahas menjadi sebuah praktek untuk membuat game sederhana.

Nah sekian dulu sampai jumpa pada tulisan - tulisan saya berikutnya. Tetep semangat ngoding. Akses full source codenya disini.

Subscribe to My Newsletter

Thank you for your interest in my blog. Sign up to my newsletter to stay current on the latest news and information me and to be the first to see new blog posts.