ผลต่างระหว่างรุ่นของ "Oop lab/simple ship game"
Jittat (คุย | มีส่วนร่วม) |
Jittat (คุย | มีส่วนร่วม) |
||
แถว 238: | แถว 238: | ||
=== ยานทะลุซ้ายขวา === | === ยานทะลุซ้ายขวา === | ||
+ | ตอนนี้ยานของเราวิ่งออกไปทางขอบจอแล้วจะหายไปเลย ให้ปรับให้ยานวิ่งทะลุด้านซ้านแล้วมาโผล่ด้านขวา และกลับกันด้วย | ||
== เกมของคุณ == | == เกมของคุณ == |
รุ่นแก้ไขเมื่อ 03:40, 25 สิงหาคม 2557
- หน้านี้เป็นส่วนหนึ่งของ oop lab
เราจะเขียนเกมยานอวกาศบน Slick2D กัน สำหรับส่วนนี้เราจะได้ทดลองใช้ Slick2D ในการแสดงรูปภาพและอ่านการกดปุ่มจากผู้ใช้แบบการถาม (polling)
เนื้อหา
โครงสร้างเกมพื้นฐาน
เกมใน Slick2D จะเป็นการทำงานร่วมกันของ object สอง object หลัก คือ
- Game (อ่าน javadoc) - เป็น object ที่มีเมท็อดพื้นฐานของวนรอบของเกม คือ init, update, และ render
- void init(GameContainer container)
- void update(GameContainer container, int delta)
- void render(GameContainer container, Graphics g)
- GameContainer (อ่าน javadoc) - เป็น object ที่จัดการเกี่ยวกับการอ่านข้อมูลจากผู้ใช้ (input) การวนรอบเกม และการจัดการเกี่ยวกับการแสดงค่า fps (frame per seconds)
หมายเหตุ: โดยทางเทคนิคแล้ว Game เป็น interface และ GameContainer เป็น abstract class เราจะได้เรียนแนวคิดดังกล่าวต่อไป
เราไม่มีความจำเป็นต้องเขียนคลาส GameContainer ขึ้นมาใหม่ โดยเราจะใช้วัตถุจากคลาส AppGameContainer (ซึ่งจัดเป็น GameContainer ประเภทหนึ่ง) ในโปรแกรมของเรา อย่างไรก็ตาม เราต้องสร้างวัตถุประเภท Game ขึ้นมาเอง เพราะว่านั่นคือแกนหลักของเกมของเรา
เมท็อดทั้งสามมีหน้าที่ดังนี้
- เมท็อด init จะถูกเรียกเพื่อให้เราเตรียมวัตถุต่าง ๆ ในเกม เราจะสร้างวัตถุต่าง ๆ เก็บไว้กับวัตถุคลาส Game ที่เราสร้าง
- เมท็อด render มีหน้าที่แสดงภาพหน้าจอ สังเกตว่าเมท็อดจะได้รับ Graphics g มาด้วย เราสามารถสั่งวาดรูปบนหน้าจอได้ผ่านทางวัตถุ g
- เมท็อด update จะถูกใช้เพื่อปรับค่าต่าง ๆ ของเกม ตัวแปร delta จะเก็บเวลาที่ผ่านไปจากการเรียกเมท็อดครั้งก่อน มีหน่วยเป็นมิลลิวินาที
ลำดับการเรียกเมท็อดจะเป็นดังนี้
init -> render -> update -> render -> update -> render -> update -> ...
เริ่มต้นด้วยเกมว่าง ๆ
สร้าง project ชื่อ shipgame อย่าลืมเพิ่ม Slick2D เข้าไปใน library ใน Build Path (ดูรายละเอียดที่ วิธีติดตั้ง)
จากนั้นให้สร้างคลาสชื่อ ShipGame เมื่อสร้างแล้ว ให้แก้หัวคลาสให้เป็น ดังด้านล่าง (เพิ่ม extends BasicGame)
public class ShipGame extends BasicGame {
เมื่อเพิ่มแล้ว IDE จะแจ้งว่าไม่รู้จัก BasicGame ให้ไปกดให้ IDE เพิ่มการ import ให้เรา โดย IDE น่าจะเพิ่มบรรทัดนี้มาให้ (อย่าลืมเลือกให้ถูก)
import org.newdawn.slick.BasicGame;
IDE จะยังคงบ่นต่อว่าเราไม่มี constructor (เมท็อดที่กำหนดค่าเริ่มต้นให้กับ object ของคลาส) เราก็กดให้มันเพิ่มให้ เราจะได้เมท็อดด้านล่างมา
public ShipGame(String title) {
super(title);
}
ยังไม่พอ IDE ยังบ่นต่อว่าเราไม่ได้ implement method ที่ต้องมี เช่นเคย เราก็กดให้ IDE เติมให้เราโดยอัตโนมัติ มันจะเติมมาให้ 3 เมท็อดดังนี้:
@Override
public void render(GameContainer arg0, Graphics arg1) throws SlickException {
// TODO Auto-generated method stub
}
@Override
public void init(GameContainer arg0) throws SlickException {
// TODO Auto-generated method stub
}
@Override
public void update(GameContainer arg0, int arg1) throws SlickException {
// TODO Auto-generated method stub
}
สังเกตว่าชื่อ argument นั่นดูไม่สื่อความหมายใด ๆ เลย ไปแก้ให้มันดูสื่อความหมายโดยแก้หัวเมท็อดต่าง ๆ ให้เป็นดังนี้
public void render(GameContainer container, Graphics g) throws SlickException { public void init(GameContainer container) throws SlickException { public void update(GameContainer container, int delta) throws SlickException {
สังเกตว่า ท้าย signature ของเมท็อด มีคำว่า throws SlickException อยู่ด้วย เราจะอธิบายแนวคิดของ exception ที่ตอนท้ายส่วนนี้
ตอนนี้ไล่ไปไล่มาใน IDE น่าจะไม่มี error อะไรแล้วนะครับ แต่เรายังไม่มีโปรแกรมหลักเลย
ใน Java เมท็อดที่จะเป็นโปรแกรมหลักได้จะต้องชื่อ main และเป็น static method ในคลาสบางคลาส ซึ่งเราก็จะให้อยู่ในคลาส ShipGame นี่เลย
โดยทั่วไปแล้วโปรแกรมหลักของโปรแกรมที่เขียนเชิงวัตถุจะทำหน้าที่แค่สร้าง object ที่จำเป็นแล้วก็จัดการทำให้มันรู้จักกัน แล้วก็เรียก object หลักให้ทำงาน โปรแกรมหลักของเราก็เช่นกัน เพิ่มเมท็อด main ด้านล่างลงในโค้ดของเรา
public static void main(String[] args) {
try {
ShipGame game = new ShipGame("Super Ship Game");
AppGameContainer appgc = new AppGameContainer(game);
appgc.setDisplayMode(640, 480, false);
appgc.start();
} catch (SlickException e) {
e.printStackTrace();
}
}
อย่าลืมไปสั่งให้ IDE import ของเพื่อให้เรารู้จัก AppGameContainer ด้วย
บรรทัดที่สำคัญมาก ๆ คือ
ShipGame game = new ShipGame("Super Ship Game"); AppGameContainer appgc = new AppGameContainer(game);
ซึ่งเป็นบรรทัดที่เราสร้าง object จากคลาส ShipGame และสร้าง AppGameContainer และส่ง game ของเราไปให้
ถ้าเราทดลองรัน เราจะเห็นหน้าจอสีดำเปล่า ๆ ขึ้นมา พร้อมด้วยจำนวน frame per second ที่มุมด้านซ้ายบน
แผนภาพของ object ทั้งสองแสดงด้านล่าง
แสดง sprite
ในเกม 2 มิติ ตัวละครที่แสดงบนจอมักจะเป็นรูปที่มีพื้นหลังเป็นสีใส ๆ แสดงทับกันไป รูปดังกล่าวอาจจะมีการเปลี่ยนไปมาเพื่อแสดง animation ก็ได้ เราเรียกรูปดังกล่าวว่า sprite
สร้างรูป sprite
Use a graphical software such as GIMP, Photoshop, Paint.NET to create a 64 pixel x 64 pixel image. Draw a simple spaceship whose head is in an upward direction. Make sure that the background is transparent.
Usually, when the image has transparent background, the graphic editor usually shows it like this:
If you background is not transparent, when you show the sprite you'll see it as an image in a white box.
We will create a directory res in your project directory for keeping all our image assets. Save the image as ship.png in directory res that we have just created.
คลาส Image
คลาสใน Slick2D ที่ใช้แสดงรูปคือคลาส Image (อ่าน javadoc) โดย object ของคลาสนี้จะต้องสร้างเมื่อโปรแกรมเริ่มมีการกำหนดค่าเริ่มต้นแล้ว ดังนั้นเราจะสร้างในเมท็อด init ของคลาส ShipGame
เราจะสร้าง field ที่ชื่อว่า shipImage เพื่อเป็น Image ของรูปยานอวกาศที่เราวาดขึ้น โดยประกาศที่ตอนต้นของคลาส จากนั้นในเมท็อด init เราจะสั่ง
shipImage = new Image("res/ship.png");
เพื่อสร้าง Image ของรูปเรือดังกล่าว สุดท้าย เราจะแสดงผล sprite ในเมท็อด render โดยเรียกเมท็อด draw
ด้านล่างแสดงโค้ดที่เพิ่มขึ้นที่ทำงานดังกล่าว
public class ShipGame extends BasicGame {
private Image shipImage;
public ShipGame(String title) {
// ..
}
@Override
public void render(GameContainer container, Graphics g) throws SlickException {
shipImage.draw(100,100);
}
@Override
public void init(GameContainer container) throws SlickException {
shipImage = new Image("res/ship.png");
}
}
แผนภาพของวัตถุทั้งสามแสดงด้านล่าง
ทำให้ยานเคลื่อนที่
สังเกตว่าในโปรแกรมที่ผ่านมา ในเมท็อด render เราวาดยานไปที่ตำแหน่ง (100,100) ถ้าเราต้องการให้ยานเคลื่อนที่ เราจะต้องวาดยานที่ตำแหน่งต่าง ๆ ไป เราจะแก้โปรแกรมดังนี้
- เราจะเพิ่ม field shipX และ shipY เพื่อเก็บตำแหน่งยานนี้
- เราจะเปลี่ยนตำแหน่งของยานที่ในเมท็อด update โดยปรับค่า field ทั้งสอง
- ใน render เราจะแสดงยานที่ตำแหน่ง shipX, shipY
ด้านล่างเป็นส่วนของโปรแกรมที่เราแก้
public class ShipGame extends BasicGame {
private Image shipImage;
private int shipX;
private int shipY;
@Override
public void init(GameContainer container) throws SlickException {
shipImage = new Image("res/ship.png");
shipX = 100;
shipY = 100;
}
@Override
public void render(GameContainer container, Graphics g) throws SlickException {
shipImage.draw(shipX, shipY);
}
@Override
public void update(GameContainer container, int delta) throws SlickException {
shipX += 1;
}
// ...
}
สังเกตว่าเรากำหนดค่าเริ่มต้นของตำแหน่งยานในเมท็อด init ไม่ใช่ใน constructor ของ ShipGame
คำถาม: (1) ถ้าต้องการให้ยานขยับเร็วขึ้นต้องทำอย่างไร (2) ถ้าต้องการให้ยานเคลื่อนที่ไปด้านหน้าต้องแก้อย่างไร
บังคับควบคุม: Polling
- อ่านรายละเอียดของการอ่าน input แบบสมบูรณ์ขึ้นที่เว็บ slick2d wiki
สังเกตว่าเราปรับตำแหน่งของยานในเมท็อด update ถ้าเราต้องการให้ยานเปลี่ยนตำแหน่งในทิศทางต่าง ๆ เราสามารถทำได้ในเมท็อดดังกล่าว
สิ่งที่เราต้องการคือการรู้ให้ได้ว่าขณะนี้ผู้ใช้กดปุ่มทิศทางใดอยู่ วิธีการแบบหนึ่งก็คือการถามไปที่ระบบว่าขณะนี้ผู้ใช้กดปุ่มใดอยู่หรือไม่ วิธีการแบบนี้เรียกว่าการ polling เนื่องจากโค้ดส่วนสอบถามของเราจะทำงานตลอดไม่ว่าจะมีการกดปุ่มหรือไม่
การสอบถามเกี่ยวกับการกดปุ่มนั้น เราจะทำผ่านทาง object Input (อ่าน javadoc) ซึ่่งเราจะอ้างถึง object input นี้ได้ผ่านทาง GameContainer
ด้านล่างแสดงตัวอย่างการรับปุ่มซ้ายขวา และให้ยานขยับ
@Override
public void update(GameContainer container, int delta) throws SlickException {
Input input = container.getInput();
if (input.isKeyDown(Input.KEY_LEFT)) {
shipX -= 1;
}
if (input.isKeyDown(Input.KEY_RIGHT)) {
shipX += 1;
}
}
แบบฝึกหัด: ให้แก้โปรแกรมข้างต้นให้บังคับยานได้ 4 ทิศทาง
หมุนยาน
เราสามารถระบุให้วัตถุคลาส Image หมุนได้ โดยสั่งเมท็อด setRotation เช่น ถ้าเราสั่ง
shipImage.setRotation(270);
ยานจะหมุนหัวไปด้านซ้าย
แบบฝึกหัด: ให้แก้โปรแกรมข้างต้นให้ยานหันหัวไปในทิศทางที่เคลื่อนที่
ยานทะลุซ้ายขวา
ตอนนี้ยานของเราวิ่งออกไปทางขอบจอแล้วจะหายไปเลย ให้ปรับให้ยานวิ่งทะลุด้านซ้านแล้วมาโผล่ด้านขวา และกลับกันด้วย