เขียนเกมด้วย LibGDX 2 – Hello World
หลังจากนำเสนอ เขียนเกมด้วย LibGDX #1 - สร้างโปรเจ็ค ในบทความที่แล้ว ทำให้ตอนนี้สามารถที่จะตั้งค่า เซ็ตอัพโปรเจ็คด้วย LibGDX ได้แล้ว ต่อไปจะเป็นการเริ่มต้นใช้งาน LibGDX กันเลย โปรแกรมแรกที่จะแสดงก็ต้องเป็นแบบคลาสสิคสุดๆ นั้นก็คือ แสดงคำว่า Hello World ก่อนอื่นนั้น เราต้องมาเข้าใจภาพรวม เข้าใจคอนเซปของ LibGDX กันก่อนครับ
บทความตอนอื่นๆ ในซีรีย์ เขียนเกมด้วย LibGDX ติดตามอ่านได้ตามลิงค์ข้างล่างเลยครับ
- เขียนเกมด้วย libGDX #1 – สร้างโปรเจ็ค
- เขียนเกมด้วย LibGDX #2 – Hello World
- เขียนเกมด้วย LibGDX #3 – Render และการรับ input
- เขียนเกมด้วย LibGDX #4 – Simple Game ภาคแรก
- เขียนเกมด้วย LibGDX #5 – Simple Game ภาคจบ
- เขียนเกมด้วย LibGDX #6 – Simple Game ภาคพิเศษ
- เขียนเกมด้วย LibGDX #7 - Simple Game - scene2d.ui
- เขียนเกมด้วย LibGDX #8 - Simple Game - Actor
Life Cycle
การทำงานของ LibGDX เราจะมาพูดถึงขั้นตอนการ create
, pause
, start
กันครับ ว่ามันเริ่มต้นยังไง คลาสที่สำคัญเลย คือ ApplicationListener
อย่างเช่น เราตั้งชื่อคลาสเกมของเราว่า MyGame
และทำการ implements ApplicationListener
เมธอดทั้งหมดที่เรา override จะได้ดังนี้
import com.badlogic.gdx.ApplicationListener;
public class MyGame implements ApplicationListener {
@Override
public void create() {
}
@Override
public void resize(int width, int height) {
}
@Override
public void render() {
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void dispose() {
}
}
เมธอดแต่ละเมธอดจะถูกเรียกใช้งาน เรียงตามลำดับ ดังนี้ครับ
create()
: เมธอดนี้จะถูกเรียกครั้งเดียว คือตอนที่ Application ถูกสร้างresize(int width, int height)
: เมธอดนี้จะถูกเรียกทุกครั้งเมื่อมีการเปลี่ยนขนาดหน้าจอ เช่น ปรับหน้าจอวินโดว์ หรือบนมือถือ เช่นเปลี่ยนจากแนวตั้งเป็นแนวนอน และเมธอดนี้จะถูกเรียกครั้งแรก หลังจากcreate()
เช่นกัน parameter witdh, height คือขนาดกว้าง ยาว ของหน้าจออันใหม่ที่มีการเปลี่ยนแปลงrender()
: เมธอดนี้คือหัวใจของ Application เลยก็ว่าได้ เพราะมันคือ Game Loop ครับ เมธอดนี้จะถูกรันตลอดเวลา นึกถึงเวลา render เกมครับ เคยเห็นเกมที่มี FPS (Frame Per Second) มั้ยครับ บางเกมมี FPS 50-60 เช่น เมธอดนี้จะถูกเรียก 50-60 ครั้งต่อวินาที รูปภาพ หรือ ฉากต่างๆของเกมส์ จะถูกวาดที่เมธอดนี้pause()
: บน Android เมธอดนี้จะถูกเรียกเมื่อมีสายเข้า หรือว่าเวลายูเซอร์กดปุ่ม Home แต่ใน Desktop จะถูกเรียกก่อน เมธอดdispose()
เมื่อออกจากโปรแกรม เมธอดนี้เหมาะสำหรับเก็บค่า state ต่างๆ เช่นเซฟตำแหน่งปัจจุบันผู้เล่น บันทึกแต้มต่างๆresume()
: เมธอดนี้จะถูกเรียกเฉพาะบน Android เมื่อแอพพลิเคชันถูกเรียกกลับจาก pause state.dispose()
: เมธอดนี้จะถูกเรียกเมื่อกำลังจะปิดแอพพลิเคชัน สิ้นสุดโปรแกรม เป็นเมธอดที่ถูกเรียกหลังสุด
โอเคเมื่อเข้าใจว่าเมธอดแต่ละตัว ทำงานอย่างไรแล้ว คราวนี้กลับไปดูตัวเกมที่ได้ import มา จาก บทความบทแรกครับ ที่โฟลเดอร์ AhoyBoxBox-core
จะเห็นไฟล์ชื่อ MyGame
ลองเปิดตัวนั้นดูครับ จะเห็นดังนี้
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
@Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
@Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img, 0, 0);
batch.end();
}
}
extends ApplicationAdapter
ทำไมถึงเป็นคลาส ApplicationAdapter
จริงๆ คลาสนี้ implements มาจากอินเตอร์เฟซ ApplicationListener
อีกทีนึง ตอนนี้เรา extends คลาส ApplicationAdapter เพราะเราสนใจแค่เมธอด สองตัว นั่นก็คือ create()
กับ render()
เท่านั้นครับ เวลาทำงานมันก็จะไปเรียกเมธอด ApplicationListener.create()
และ ApplicationListener.render()
ครับ
ที่เมธอด create()
มีการสร้าง SpriteBatch
และ Texture
เพื่อที่จะวาดรูป ให้เราลองจินตนาการ Texture
ก็เปรียบเสมือนคลาสที่ทำการโหลดรูปมาเก็บไว้ใน memory แต่มันไม่สามารถแสดงรูปได้ จำเป็นต้องใช้ SpriteBatch
มาช่วยในการวาดรูปนั่นเอง
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
ต่อไปดูที่เมธอด render()
เมธอดนี้เป็น Game Loop จะถูกเรียกเรื่อยๆ วินาทีละหลายสิบครั้ง จนกว่าจะปิดแอพพลิเคชัน
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
2 คำสั่งด้านบน เป็นการเคลียร์หน้าจอ ให้เป็นสีแดง โดยใช้เมธอด Gdx.gl.glClearColor(red, green, blue, opacity)
ซึ่งค่าสีก็เป็น rgba มีค่าตั้งแต่ 0 - 1 หน่วยเป็น float ส่วนอีกบรรทัดเป็นการฟังค์ชั่นของ OpenGL สำหรับเคลียร์หน้าจอครับ
หรือจะใช้แทนรหัสสีแบบนี้ก็ได้
Gdx.gl.glClearColor(255 /255.0f, 0 /255.0f, 0 / 255.0f, 1)
ทุกครั้งที่เราจะสั่งให้ SpriteBatch
วาดรูปนั้น เราต้องให้วาดระหว่างเมธอด SpriteBatch.begin()
และ SpriteBatch.end()
ดังตัวอย่างนี้
batch.begin();
batch.draw(img, 0, 0);
batch.end();
จะเห็นได้ว่า SpriteBatch
ทำการวาดรูป Texture
ที่โหลดมาจาก `badlogic.jpg' ที่ตำแหน่ง x = 0 และ y = 0
Note: ตำแหน่ง x และ y เริ่มที่มุมซ้ายล่างนะครับ
ทีนี้พอกด Run มันก็จะได้รูปแบบ แบบเดียวกับบทความที่แล้วแบบนี้แหละครับ
ทีนี้ลองเปลี่ยนสีหน้าจอครับ เป็นสีเขียว ทำยังไง? (ผมลดสีเขียวลงหน่อยนึง รู้สึกแสบตาเกินครับ )
ง่ายมากครับ แค่เปลี่ยนเป็น Gdx.gl.glClearColor(0, 1, 0, 1);
แล้วเราจะแสดง Hello World ยังไงละเนี้ย?
LibGDX มีให้คุณอีกแล้วครับ LibGDX ได้เตรียม com.badlogic.gdx.graphics.g2d.BitmapFont
ไว้ให้แล้ว สำหรับแสดงตัวอักษรบนหน้าจอ
เริ่มแรก ผมทำการประกาศสร้างตัวแปร font
จากคลาส BitmapFont
เป็น private member ดังนี้
BitmapFont font;
จากนั้น ก็สร้างออปเจ็คขึ้นใหม่ที่เมธอด create()
และตั้งค่าสีเป็นสีขาว
@Override
public void create() {
...
font = new BitmapFont();
font.setColor(Color.WHITE);
}
ที่เมธอด render()
จะให้แสดงข้อความแทนรูปภาพ จะเปลี่ยนยังไง ?
BitmapFont จะมีเมธอด draw()
ครับ โดยรับพารามิเตอร์เป็น SpriteBatch, ข้อความ, ตำแหน่งแกนx, ตำแหน่งแกน y ตามลำดับครับ
batch.begin();
font.draw(batch, "Hello World", 300, 300);
batch.end();
ก็จะได้ผลลัพธ์ดังนี้ครับ
สุดท้ายโค๊ดใน MyGame
จะเป็นดังนี้
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
BitmapFont font;
@Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
font = new BitmapFont();
font.setColor(Color.WHITE);
}
@Override
public void render () {
Gdx.gl.glClearColor(0, 1, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
// batch.draw(img, 0, 0);
font.draw(batch, "Hello World", 300, 300);
batch.end();
}
}
ทีนี้มาดูที่คลาส Starter ของ Desktop และ Android กันครับ
ลองเปิด DesktopLauncher
ของ AhoyBoxBox-desktop
ก่อนครับ ก็คือ เราเขียนตัวเกมทุกอย่างไว้ที่ AhoyBoxBox-core
แล้ว ชื่อว่า MyGame
ส่วน Starter คลาสจะเป็นเหมือนแค่ สื่อกลางที่จะเปิดเกมใน Platform นั้นๆเอง ดังนี้
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
new LwjglApplication(new MyGame(), config);
}
}
คือข้างบน เราสามารถ คอนฟิคค่าต่างๆ ของ หน้าตา Desktop ได้ เช่น ชื่อไตเติ้ล ขนาดความกว้าง ความยาว เช่น
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = "Learn LibGDX #2 - Hello World ";
config.width = 500;
config.height = 500;
new LwjglApplication(new MyGame(), config);
ทีนี้มาดู Starter คลาสของแอนดรอยส์กันบ้าง ไปที่ AndroidLauncher
ในโฟลเดอร์ AhoxBoxBox-android
จะเห็นดังนี้ เข้าใจตรงกันนะ :)
public class AndroidLauncher extends AndroidApplication {
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
initialize(new MyGame(), config);
}
}
โดยปกติแล้ว เวลาเราเขียนแอนดรอยส์ เราจะทำการ extends Activity
ใช่มั้ยครับ แต่ว่าอันนี้เราจะทำการ extends AndroidApplication
ซึ่งเป็นคลาสของ LibGDX อันนี้ก็สามารถ config ค่าต่างๆได้เช่นกันครับ เช่น พวกเข็มทิศ Accelerometer ครับ ส่วนเมธอด initialize(Game, config);
ก็คือการเปิดแอพแอนดรอยส์ โดยใช้ตัวเกมที่เราได้สร้างไว้ครับ
สำหรับบทความนี้ จบเพียงเท่านี้ก่อนครับ วันนี้พวกคุณได้รู้จัก Life Cycle ของ LibGDX ได้รู้จัก ApplicationListener และ method ต่างๆของ ApplicationListener รวมถึงรู้จัก SpriteBatch, Texture, BitmapFont และการแสดงผลลัพธ์ผ่าน เมธอด ApplicationListener.render()
ไปแล้ว ก็ลองนำบทความไปประยุกต์ใช้ดูกันครับ เช่นต้องการโหลดรูปอื่นขึ้นมา ต้องการเปลี่ยนตำแหน่งรูป หรือโหลดรูปพร้อมแสดงข้อความ เป็นต้นครับ แล้วพบกันบทความต่อไปครับ ขอบคุณครับ
- Authors
- Name
- Chai Phonbopit
- Website
- @Phonbopit