Devahoy Logo
PublishedAt

Game Development

เขียนเกมด้วย LibGDX :3 – Render และการรับ input

เขียนเกมด้วย LibGDX :3 –  Render และการรับ input

สวัสดีครับ บทความนี้เป็นบทความที่ 3 ครับ จากซีรีส์ สอน LibGDX ครับ

บทความตอนอื่นๆ ในซีรีย์ เขียนเกมด้วย LibGDX ติดตามอ่านได้ตามลิงค์ข้างล่างเลยครับ


สำหรับบทความนี้จะมานำเสนอ วิธีการทำให้รูปภาพเคลื่อนที่ได้ และการรับ input ต่างๆ เช่นเมาท์คลิ๊ก กดคีย์บอร์ด หรือว่า ทัชสกรีนบนหน้าจอมือถือแอนดรอยส์กันครับ

ตัวอย่างนี้ผมใช้วิธีง่ายๆ คือโหลดรูปมาแสดงบนหน้าจอ จากนั้น ก็จะให้มันขยับไปทางขวา จนสุดจอ แล้วก็วนมาเริ่มต้นไป วนแบบนี้ไปเรื่อยๆครับ

ก็อบปี้รูปของท่านเอง ไปไว้ที่ project-android/assets/ หรืออยากจะโหลดรูปตัวอย่าง ก็ตามนี้ครับ

chai.png

ไฟล์ image ผมใช้ไฟล์ของผมเอง ชื่อ chai.png Credit: Thaweepong on Behance

ตัวอย่างใช้คลาส MyGame จาก บทความที่ 2 ได้เลยครับ ข้างล่างคือคลาส MyGame เดิมครับ

1
import com.badlogic.gdx.ApplicationAdapter;
2
import com.badlogic.gdx.Gdx;
3
import com.badlogic.gdx.graphics.GL20;
4
import com.badlogic.gdx.graphics.Texture;
5
import com.badlogic.gdx.graphics.g2d.BitmapFont
6
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
7
8
public class MyGame extends ApplicationAdapter {
9
SpriteBatch batch;
10
Texture img;
11
BitmapFont font;
12
13
@Override
14
public void create () {
15
batch = new SpriteBatch();
16
img = new Texture("badlogic.jpg");
17
font = new BitmapFont();
18
font.setColor(Color.WHITE);
19
}
20
21
@Override
22
public void render () {
23
Gdx.gl.glClearColor(0, 1, 0, 1);
24
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
25
batch.begin();
26
// batch.draw(img, 0, 0);
27
font.draw(batch, "Hello World", 300, 300);
28
batch.end();
29
}
30
}

จากด้านบน ผมจะเปลี่ยนให้เป็นแบบนี้

1
import com.badlogic.gdx.ApplicationAdapter;
2
import com.badlogic.gdx.Gdx;
3
import com.badlogic.gdx.graphics.GL20;
4
import com.badlogic.gdx.graphics.Texture;
5
import com.badlogic.gdx.graphics.g2d.BitmapFont
6
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
7
8
public class MyGame extends ApplicationAdapter {
9
SpriteBatch batch;
10
Texture img;
11
12
@Override
13
public void create () {
14
batch = new SpriteBatch();
15
img = new Texture(Gdx.files.internal("chai.png"));
16
}
17
18
@Override
19
public void render () {
20
Gdx.gl.glClearColor(0, 1, 0, 1);
21
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
22
batch.begin();
23
batch.draw(img, 0, 0);
24
batch.end();

จากด้านบน ผมได้ทำการลบ BitmapFont ออก และ Texture ทำการโหลดรูปภาพใหม่เข้ามา โดยใช้ Gdx.files.internal("chai.png") ซึ่งเป็น Module ของทาง LibGDX ครับ เป็นการโหลดไฟล์จาก โฟลเดอร์ AhoyBoxBox-android/assets/ นั่นเอง ส่วนเมธอด render() ก็สั่งให้ SpriteBatch ทำการวาดภาพอันใหม่ ที่ตำแหน่ง 0, 0 จะได้ดังนี้

Example

ต่อไป ผมต้องการให้รูปภาพที่แสดงมันขยับไปด้านขวาของหน้าจอเรื่อยๆ ผมจะทำอย่างไร? ก็ต้องเปลี่ยนค่า ที่เมธอด render() ตรง batch.draw(img, x, y) นั่นเองครับ ซึ่งค่า x และ y ก็คือ ตำแหน่งของแกน x และแกน y ที่ต้องการจะให้รูปแสดง โดยนับจากมุมซ้ายล่างของรูป

อันนี้เป็นตัวอย่างตำแหน่งของรูป และแกน x, y คร่าวๆครับ ฮ่าๆ รีบทำ ภาพไม่สวย ^^

Screen

ทีนี้ เราจะให้รูปมันขยับ เราก็ต้องทำเปลี่ยนค่า x, y ไปเรื่อยๆ เพราะ render() มันจะรันเป็น Game Loop อยู่แล้ว คือมันจะวนทำซ้ำไปเรื่อยๆ วินาทีอาจจะ 40 ครั้ง 50 ครั้ง หรือ 60 ครั้ง โดยเราจะเพิ่ม ค่า x และ y เข้าไปในเมธอด render() ดังนี้ โดยประกาศ int x = 0; int y = 0 เป็น member variable ไว้ภายในคลาส

1
batch.begin();
2
batch.draw(img, x, y);
3
batch.end();
4
5
x += 100 * Gdx.graphics.getDeltaTime();

ทีนี้เมื่อ render() ถูกเรียก มันก็จะ batch.draw() ตำแหน่ง x เปลี่ยนไปทุกๆครั้ง หากลองสั่งรันโปรแกรมดู ก็จะเห็นรูปขยับกันนะครับ

Delta Time

ทีนี้มาดูทำไมถึงเป็น Gdx.graphics.getDeltaTime(); อันนี้เป็น Module ของทาง LibGDX อีกเช่นกันครับ ไว้สำหรับแสดงเวลา DeltaTime มันคือเวลา จากเฟรมล่าสุด ถึงเฟรมปัจจุบันครับ ลองจินตนาการดู ภาพการเคลื่อนไหว ปกติจะแบ่งเป็น เฟรมๆใช่มั้ยครับ เช่น เฟรม 0 - 50 เป็นภาพแสดงการก้าวเท้าขวา ทุกๆเฟรม มันก็คือการเคลื่อนไหวทีละเล็กทีละน้อย 0 - 20 อาจจะกำลังยกขา 21-50 อาจเป็นช่วงที่ขากำลังแตะพื้น DeltaTime ก็คือช่วงเวลาระหว่างเฟรมสุดท้าย กับเฟรมล่าสุด หน่วยอาจจะเป็นเสี้ยววินาที หากอยากรู้ว่า DeltaTime มีค่าเท่าไหร่ ลองเพิ่มอันนี้เข้าไปครับ

1
...
2
batch.end();
3
Gdx.app.log("LOG", "Delta Time: " + Gdx.graphics.getDeltaTime());

ที่ console จะได้ผลลัพธ์ประมาณนี้

1
...
2
LOG: Delta Time: 0.015866747
3
LOG: Delta Time: 0.01686707
4
LOG: Delta Time: 0.016484985
5
LOG: Delta Time: 0.016276209

จากด้านบน เห็นเวลามั้ยครับ เสี้ยววินาที หาก เราเพิ่มค่า x ทีละ 100 ลองจินตนาการดูครับ เราแทบมองไม่เห็น รูปเลยครับ เนื่องจาก มันถูกเพิ่ม 100 แค่ 8 ครั้ง มันก็สุดจอไปแล้ว โดยใช้เวลา ไม่ถึง 0.1 วินาทีด้วยซ้ำ ตามองไม่ทัน ฉะนั้น ก็เลยต้องใช้วิธี x += 100 * ค่า deltaTime ครับ ก็คือ เพิ่มที 1 - 2 pixel เท่านั้น ทีนี้ก็มองทันแล้ว

Show Log

Gdx.app.log() นั้นก็คือ Module ของ LibGDX อีกแล้วครับท่าน ไว้สำหรับแสดง Log ในโปรแกรม หากเขียนแอนดรอยส์มา ก็จะคุ้นเคยกับ Log.i, Log.e หรือบ้านๆเลย ใครใช้ System.out.prinln() ก็เปลี่ยนมาให้ ล็อคดีกว่าครับ ^^

จริงๆ LibGDX มี Module อีกหลายตัวที่อำนวยความสะดวกให้เรานะครับ พูดแล้วจะหาว่าคุย ลองไปศึกษาเพิ่มเติมได้ที่นี่ LibGDX Module

การรับ Input

การรับ Input ทาง LibGDX ก็มี Module ที่เตรียมมาให้ครับ เช่น หากเราต้องการรู้ว่า มีการคลิกเมาท์ หรือว่ามีการแตะที่หน้าจอมือถือหรือไม่ ก็จะใช้คำสั่งนี้ครับ

1
if (Gdx.input.isTouched()) {
2
// Touched
3
}

ส่วนการรับ Input จากแป้นคีย์บอร์ด ก็ใช้คำสั่งนี้ เพื่อเช็คว่ามีการพิมพ์คีย์บอร์ดหรือไม่

1
if (Gdx.input.isKeyPressed(Keys.SPACE)) {
2
// Spacebar is pressed.
3
}

อย่าลืม import import com.badlogic.gdx.Input;

Note: short cut สำหรับ import Eclipse คือ Ctrl + Alt + O ส่วน Intelij IDEA มัน auto import on fly ^^

สุดท้าย ผมได้ทำการ implement ทั้งหมดข้างต้น โดยทำเป็นคล้ายๆ Flappy Bird คือ รูปจะมีการเคลื่อนที่ไปข้างหน้า (ไปด้านขวา ค่า x ค่อยๆเพิ่ม) และจะตกลงเรื่อยๆ(ค่า y ค่อยๆลด) หากคลิ๊กเมาท์หรือแตะหน้าจอ ก็จะให้รูปมันลอยขึ้น ครับ (ค่า y เพิ่มขึ้น) โค๊ดที่ได้ก็จะประมาณนี้ ลองๆนำไปเล่นกันดูนะครับ :)

1
import com.badlogic.gdx.ApplicationAdapter;
2
import com.badlogic.gdx.Gdx;
3
import com.badlogic.gdx.graphics.GL20;
4
import com.badlogic.gdx.graphics.Texture;
5
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
6
7
public class MyGame extends ApplicationAdapter {
8
9
public static final int WIDTH = 800;
10
public static final int HEIGHT = 480;
11
12
SpriteBatch batch;
13
Texture img;
14
15
int x = 0;
16
int y = HEIGHT / 2;
17
18
@Override
19
public void create () {
20
batch = new SpriteBatch();
21
img = new Texture(Gdx.files.internal("chai.png"));
22
23
}
24
25
@Override
26
public void render () {
27
Gdx.gl.glClearColor(0, 0, 0, 1);
28
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
29
30
y -= 200 * Gdx.graphics.getDeltaTime();
31
x += 100 * Gdx.graphics.getDeltaTime();
32
33
batch.begin();
34
batch.draw(img, x, y);
35
batch.end();
36
37
Gdx.app.log("LOG", "Delta Time: " + Gdx.graphics.getDeltaTime());
38
39
if (Gdx.input.isTouched()) {
40
y += 500 * Gdx.graphics.getDeltaTime();
41
}
42
43
// เช็คหาก รูปตกขอบทั้งด้านข้างหรือด้านล่าง ก็ให้ไปเริ่มใหม่
44
if (y < 0 || x > WIDTH) {
45
x = 0;
46
y = HEIGHT / 2;
47
}
48
}
49
}

โดยตัวเกม คือ เริ่มต้นสตาร์ทที่ตำแหน่ง x = 0 และ y ที่ตำแหน่งกึ่งกลางของความสูงหน้าจอ และจะตกลงเรื่อยๆ ต้องใช้เมาท์กดที่หน้าจอ หรือว่าใช้มือแตะที่หน้าจอมือถือ หากรันบนแอนดรอยส์ ก็จะทำให้รูป มีการลอยตัวขึ้นครับ ลองๆ นำไปประยุกต์ปรับใช้ หรือลองเล่นกันดูครับ หากใครมีข้อสงสัย สอบถามได้ครับ

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts