Oop lab/maze game

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
หน้านี้เป็นส่วนหนึ่งของ oop lab
เนื้อหาส่วนนี้ ถ้าต้องการดูเป็นภาษา JavaScript กรุณาดูที่ 01219245/cocos2d/Maze

ในส่วนนี้เราจะพิจารณาการเขียนเกมที่เป็นตารางและผู้เล่นเครื่องที่ไปมาในตาราง เกมที่เป็นตัวอย่างคลาสสิกของเกมตระกูลนี้คือ PacMan หน้าตาของเกมนี้แสดงดังด้านล่าง

Java-mazegame.png

ขั้นตอน

เราจะค่อย ๆ เขียนโปรแกรมไปทีละขั้น ๆ ดังนี้

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

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

โค้ดทั้งหมดอยู่ที่: https://github.com/jittat/slick2d-mazegame

แสดงแผนที่

หน้าจอเปล่า/คลาส MazeGame

เช่นเคย เราจะเริ่มโดยสร้างโปรแกรมที่แสดงหน้าจอเปล่า

public class MazeGame extends BasicGame {

  public static final int GAME_WIDTH = 640;
  public static final int GAME_HEIGHT = 480;

  public MazeGame(String title) {
    super(title);
  }
  
  @Override
  public void init(GameContainer container) throws SlickException {
  }

  @Override
  public void render(GameContainer container, Graphics g) throws SlickException {
  }

  @Override
  public void update(GameContainer container, int delta) throws SlickException {
  }
  
  public static void main(String[] args) {
    try { 
      MazeGame game = new MazeGame("Maze Game");
      AppGameContainer container = new AppGameContainer(game);
      container.setDisplayMode(GAME_WIDTH, GAME_HEIGHT, false);
      container.setMinimumLogicUpdateInterval(1000 / 60);
      container.start();
    } catch (SlickException e) {
      e.printStackTrace();
    }
  }
}

คลาส Maze: แสดงหนึ่งช่อง

เราจะสร้างคลาส Maze ที่แสดงแผนที่ โดยในการแสดงแผนที่เราจะแสดงโดยใช้รูปเล็ก ๆ ขนาด 40 x 40 มาประกอบกันเพื่อแสดงเป็นแผนที่ ดังนั้นในขั้นแรกให้สร้างไฟล์ wall.png ที่เป็นรูปกำแพงขนาด 40 x 40 และเก็บไว้ในไดเร็กทอรี res

ก่อนที่เราจะจัดการเรื่องการเก็บข้อมูลต่าง ๆ เรามาทำคลาส Maze ให้แสดงรูปนี้ให้ได้ก่อน คลาส Maze จะมี constructor เราจะสร้าง Image ของช่องที่จะเป็นผนัง

public class Maze {
  private Image wallImage = null;

  public Maze() {
    try {
      wallImage = new Image("res/wall.png");
    } catch (SlickException e) {
      e.printStackTrace();
    }
  }

  // ...
}

เราจะสร้างเมท็อด render เพื่อให้คลาส MazeGame มาเรียกให้ Maze แสดงผล เราจะแสดงผลแบบง่าย ๆ ก่อน ดังนี้

  public void render() {
    wallImage.draw(100, 100);
  }

จากนั้นในคลาส MazeGame เราก็ไปเพิ่มการสร้าง maze และการสั่งให้ maze แสดงผล

ด้านล่างแสดงโค้ดในคลาส MazeGame ที่เราแก้ไข

class MazeGame extends BasicGame {
  private Maze maze;
  // ...
  
  @Override
  public void init(GameContainer container) throws SlickException {
    maze = new Maze();
  }

  @Override
  public void render(GameContainer container, Graphics g) throws SlickException {
    maze.render();
  }
  // ...
}

การเก็บและจัดการแผนที่

สำหรับเกมนี้ เพื่อความง่าย เราจะระบุขนาดของแผนที่ให้เหมาะสมกับหน้าจอของเกมของเราไปเลย สังเกตว่าหน้าจอของเราขนาด 640 x 480 ถ้าช่องของเราขนาด 40 x 40 เราจะแสดง maze ได้ 12 แถว x 16 คอลัมน์ เราจะเว้นแถวบนกับแถวล่างไว้เพื่อใช้แสดงข้อมูลอื่น ๆ ดังนั้นเราจะได้ว่าเราจะใช้แผนที่ขนาด 16 คอลัมน์ x 10 แถว

เราจะเพิ่มค่าเหล่านี้เป็นค่าคงที่ของคลาส ดังนี้

public class Maze {
  static public int ROWS = 10;
  static public int COLS = 16;
  static public int BLOCK_SIZE = 40;

  // ...
}

เกม maze จำนวนมากมายสามารถเปลี่ยนแผนที่ได้ สำหรับเกมนี้แม้เรายังไม่ได้พัฒนาไประดับนั้น แต่เราก็จะเก็บแผนที่แยกไว้เป็นค่าคงที่ของคลาส ถ้าในอนาคตต้องการเปลี่ยนแผนที่ก็น่าจะทำได้โดยง่าย ในคลาส Maze ประกาศค่าคงที่ของคลาสเพิ่มเติมเป็นแผนที่ โดยเราจะเก็บเป็นอาร์เรย์ของสตริง ดังนี้

  static private String[] MAP = {
    "################",
    "#..............#",
    "#.#.###..###.#.#",
    "#...#......#...#",
    "#.#...#..#...#.#",
    "#.#...#..#...#.#",
    "#...#......#...#",
    "#.#.###..###.#.#",
    "#..............#",
    "################"
  };

ในการเก็บข้อมูลดังกล่าว ถ้าต้องการทราบข้อมูลของแผนที่แถวที่ r คอลัมน์ที่ c (นับดัชนีเริ่มที่ 0) เราสามารถสั่งได้ดังนี้

MAP[r].charAt(c)

ด้วยวิธีดังกล่าว เมท็อด render ของคลาส Maze แก้ใหม่ได้เป็นดังนี้

  public void render() {
    for (int r = 0; r < ROWS; r++) {
      for (int c = 0; c < COLS; c++) {
        if (MAP[r].charAt(c) == '#') {
          wallImage.draw(leftX + (c * BLOCK_SIZE), 
              topY + (r * BLOCK_SIZE));
        }
      }
    }
  }

ตัว Pacman และการเคลื่อนที่

จุดและการกิน

คะแนน