ผลต่างระหว่างรุ่นของ "Oop lab/objects co-ordination"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
 
(ไม่แสดง 13 รุ่นระหว่างกลางโดยผู้ใช้คนเดียวกัน)
แถว 15: แถว 15:
  
 
== Object ติดต่อกันเอง ==
 
== Object ติดต่อกันเอง ==
 +
การที่ object จะจัดการกิจกรรมอื่น ๆ ได้เองนั้น object จะต้องอ้างถึง object อื่น ๆ ที่เกี่ยวข้องได้ด้วย นอกจากนี้ ในบางครั้งที่เกิดเหตุการณ์ที่สำคัญ เช่น game over แล้ว  object จะต้องสามารถแจ้งผลต่าง ๆ ให้กับ Game ได้ด้วย
 +
 +
=== การอ้างถึงวัตถุอื่น ๆ ในเกม ===
 +
สามารถดำเนินการได้หลายแบบ
 +
 +
==== 1. เก็บไว้เป็น field ====
 +
ในกรณีที่ object ของคุณมีจำนวนไม่มาก คุณอาจจะเก็บ field ของวัตถุอื่นไว้ใน object ก็ได้ ยกตัวอย่างเช่น ถ้าคุณมีผู้เล่นสองคน อาจจะเก็บผู้เล่นอีกฝ่ายเป็น field ได้
 +
 +
<syntaxhighlight lang="java">
 +
class Player {
 +
  private Player otherPlayer = null;
 +
 +
  //...
 +
  public setOtherPlayer(player) {
 +
    otherPlayer = player;
 +
  }
 +
  //...
 +
  public isHit() {
 +
    //... now you can access the other player with otherPlayer field.
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 +
ในส่วน init ใน Game อาจเป็นดังนี้
 +
 +
<syntaxhighlight lang="java">
 +
  public init() {
 +
    player1 = new Player();
 +
    player2 = new Player();
 +
    player1.setOtherPlayer(player2);
 +
    player2.setOtherPlayer(player1);
 +
  }
 +
</syntaxhighlight>
 +
 +
==== 2. อ้างผ่านทาง Game ====
 +
ถ้าเราต้องการอ้าง object ในเกมที่เปลี่ยนไปมา เช่น monster เพิ่มขึ้นเรื่อย ๆ หรือกระสุนที่เปลี่ยนไปมา ตัว object เอง จะไม่สามารถเก็บวัตถุพวกนี้ได้ (เพราะว่ามีการแก้ไขตลอด) อีกทางที่เราทำได้คืออ้างถึงผ่านทาง Game
 +
 +
อย่างไรก็ตามถ้าเราต้องการใช้วิธีนี้ เราจะต้องให้ object อ้างถึง Game ได้ด้วย โดยทำได้สองแบบหลัก ๆ คือ
 +
 +
'''2.1 เพิ่ม field game ในวัตถุ'''
 +
 +
<syntaxhighlight lang="java">
 +
class Player {
 +
  private Game game;
 +
 +
  public Player(Game game) {
 +
    //...
 +
    this.game = game;
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 +
และกำหนดค่าให้เมื่อสร้าง object เหล่านี้
 +
 +
<syntaxhighlight lang="java">
 +
  public void init() {
 +
    //...
 +
    player1 = new Player(this);
 +
  }
 +
</syntaxhighlight>
 +
 +
'''2.2 อ้างผ่านคลาส Game'''
 +
 +
ในคลาส game ของเรา เราอาจจะมี static method เพื่ออ้างถึง instance ของคลาสได้
 +
 +
<syntaxhighlight lang="java">
 +
class MySampleGame extends BasicGame {
 +
  private static MySampleGame currentGame = null;
 +
 +
  //...
 +
  public static getCurrentGame() {
 +
    return MySampleGame.currentGame;
 +
  }
 +
 +
  public static void main(String[] args) {
 +
    try {
 +
      MySampleGame.currentGame = new MySampleGame("Super Ship Game");
 +
      AppGameContainer appgc = new AppGameContainer(MySampleGame.currentGame);
 +
      appgc.setDisplayMode(640, 480, false);
 +
      appgc.start();
 +
    } catch (SlickException e) {
 +
      e.printStackTrace();
 +
    }
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 +
โดยคลาส Player จะสามารถอ้างถึง game ได้ โดยสั่งเช่น
 +
 +
  game = MySampleGame.getCurrentGame();
 +
 +
ลักษณะการเขียนแบบนี้ ถ้าเขียนให้รัดกุมขึ้นสามารถอ่านรายละเอียดเพิ่มได้ที่ [http://en.wikipedia.org/wiki/Singleton_pattern Singleton Pattern] จาก wikipedia
 +
 +
{{กล่องเทา|'''ข้อควรระวัง''' เราควรจะใช้ static เท่าที่จำเป็น ไม่ควรเก็บทุกสิ่งทุกอย่างเป็น static public หมดเพื่อความง่ายในการอ้างถึง เพราะว่าการใช้ตัวแปรแบบ static คือการสร้างการขึ้นต่อกันกับคลาส ซึ่งจะทำให้เราทดสอบโปรแกรมยากมากในอนาคต  ตัวแปรแบบ static ก็เปรียบเสมอตัวแปร global ในรูปแบบหนึ่งนั่นเอง}}
 +
 +
เมื่อเราอ้างถึง game ได้แล้ว เราอาจจะเพิ่มเมท็อดในการอ้างถึงวัตถุต่าง ๆ ในเกม เช่น
 +
 +
<syntaxhighlight lang="java">
 +
class MySampleGame extends BasicGame {
 +
  private LinkedList<Monster> monsters;
 +
 +
  public List<Monster> getMonsters() {
 +
    return monsters;
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 +
โค้ด Player อาจเป็นดังนี้
 +
 +
<syntaxhighlight lang="java">
 +
  public boolean isHit() {
 +
 +
    //--------------------------------------
 +
    // approach 1: keep field
 +
    List<Monster> monsters = game.getMonsters();
 +
 +
    //--------------------------------------
 +
    // approach 2: get it from class Game
 +
    List<Monster> monsters = MySampleGamge.getMonsters();
 +
 +
    for (Monster monster : monsters) {
 +
      // doSomething....
 +
    }
 +
  }
 +
</syntaxhighlight>
 +
 +
=== การแจ้งเหตุการณ์ ===
 +
เมื่อมีเหตุการณ์ที่เราจำเป็นต้องแจ้งกับ game วิธีที่ทำได้ง่าย ๆ ถ้าเราสามารถอ้างถึงคลาส game ได้ คือการให้ object เรียกเมท็อดที่ต้องการในคลาส game 
 +
 +
ยกตัวอย่างเช่น เราอาจจะสร้างเมท็อด monsterHit ในคลาส MySampleGame
 +
 +
<syntaxhighlight lang="java">
 +
class MySampleGame externs BasicGame {
 +
  public void monsterHit(Monster monster) {
 +
    //....
 +
  }
 +
}
 +
</syntaxhighlight>
 +
 +
จากนั้นในเมท็อดที่ object เราก็สามารถเรียกเมท็อดดังกล่าวได้
 +
 +
<syntaxhighlight lang="java">
 +
  public boolean isHit() {
 +
    //...
 +
    for (Monster monster : monsters) {
 +
      if (monster.closeTo(this)) {  //... any condition you like.
 +
        game.monsterHit(monster)
 +
      }
 +
    }
 +
  }
 +
</syntaxhighlight>

รุ่นแก้ไขปัจจุบันเมื่อ 09:33, 22 กันยายน 2557

หน้านี้เป็นส่วนหนึ่งของ oop lab

มีสองแนวทางหลัก ๆ ในการจัดการประสานงานระหว่าง object ในเกม

  • Game เป็นตัวประสานงานจัดการทั้งหมด
  • ให้ object ต่าง ๆ จัดการกันเอง และแจ้ง Game เฉพาะเมื่อเกิดเหตุการณ์สำคัญ

Game-control.png

อย่างไรก็ตาม ไม่ใช่ว่าแต่ละเกมจะต้องมีรูปแบบในการติดต่อแบบเดียว ในเกมหนึ่ง ๆ อาจจะมีทั้งส่วนที่ Game เป็นคนจัดการและส่วนที่ object จัดการกันเองด้วยก็ได้

Game เป็นตัวประสานงานทั้งหมด

เกมที่เราเขียนมาทั้งหมด โดยมากจากอยู่ในรูปแบบนี้ ทั้ง Ship game และ Flappy dot

Object ติดต่อกันเอง

การที่ object จะจัดการกิจกรรมอื่น ๆ ได้เองนั้น object จะต้องอ้างถึง object อื่น ๆ ที่เกี่ยวข้องได้ด้วย นอกจากนี้ ในบางครั้งที่เกิดเหตุการณ์ที่สำคัญ เช่น game over แล้ว object จะต้องสามารถแจ้งผลต่าง ๆ ให้กับ Game ได้ด้วย

การอ้างถึงวัตถุอื่น ๆ ในเกม

สามารถดำเนินการได้หลายแบบ

1. เก็บไว้เป็น field

ในกรณีที่ object ของคุณมีจำนวนไม่มาก คุณอาจจะเก็บ field ของวัตถุอื่นไว้ใน object ก็ได้ ยกตัวอย่างเช่น ถ้าคุณมีผู้เล่นสองคน อาจจะเก็บผู้เล่นอีกฝ่ายเป็น field ได้

class Player {
  private Player otherPlayer = null;

  //...
  public setOtherPlayer(player) {
    otherPlayer = player;
  }
  //...
  public isHit() {
    //... now you can access the other player with otherPlayer field.
  }
}

ในส่วน init ใน Game อาจเป็นดังนี้

  public init() {
    player1 = new Player();
    player2 = new Player();
    player1.setOtherPlayer(player2);
    player2.setOtherPlayer(player1);
  }

2. อ้างผ่านทาง Game

ถ้าเราต้องการอ้าง object ในเกมที่เปลี่ยนไปมา เช่น monster เพิ่มขึ้นเรื่อย ๆ หรือกระสุนที่เปลี่ยนไปมา ตัว object เอง จะไม่สามารถเก็บวัตถุพวกนี้ได้ (เพราะว่ามีการแก้ไขตลอด) อีกทางที่เราทำได้คืออ้างถึงผ่านทาง Game

อย่างไรก็ตามถ้าเราต้องการใช้วิธีนี้ เราจะต้องให้ object อ้างถึง Game ได้ด้วย โดยทำได้สองแบบหลัก ๆ คือ

2.1 เพิ่ม field game ในวัตถุ

class Player {
  private Game game;

  public Player(Game game) {
    //...
    this.game = game;
  }
}

และกำหนดค่าให้เมื่อสร้าง object เหล่านี้

  public void init() {
    //...
    player1 = new Player(this);
  }

2.2 อ้างผ่านคลาส Game

ในคลาส game ของเรา เราอาจจะมี static method เพื่ออ้างถึง instance ของคลาสได้

class MySampleGame extends BasicGame {
  private static MySampleGame currentGame = null;

  //...
  public static getCurrentGame() {
    return MySampleGame.currentGame;
  }

  public static void main(String[] args) {
    try {
      MySampleGame.currentGame = new MySampleGame("Super Ship Game");
      AppGameContainer appgc = new AppGameContainer(MySampleGame.currentGame);
      appgc.setDisplayMode(640, 480, false);
      appgc.start();
    } catch (SlickException e) {
      e.printStackTrace();
    }
  }
}

โดยคลาส Player จะสามารถอ้างถึง game ได้ โดยสั่งเช่น

  game = MySampleGame.getCurrentGame();

ลักษณะการเขียนแบบนี้ ถ้าเขียนให้รัดกุมขึ้นสามารถอ่านรายละเอียดเพิ่มได้ที่ Singleton Pattern จาก wikipedia

ข้อควรระวัง เราควรจะใช้ static เท่าที่จำเป็น ไม่ควรเก็บทุกสิ่งทุกอย่างเป็น static public หมดเพื่อความง่ายในการอ้างถึง เพราะว่าการใช้ตัวแปรแบบ static คือการสร้างการขึ้นต่อกันกับคลาส ซึ่งจะทำให้เราทดสอบโปรแกรมยากมากในอนาคต ตัวแปรแบบ static ก็เปรียบเสมอตัวแปร global ในรูปแบบหนึ่งนั่นเอง

เมื่อเราอ้างถึง game ได้แล้ว เราอาจจะเพิ่มเมท็อดในการอ้างถึงวัตถุต่าง ๆ ในเกม เช่น

class MySampleGame extends BasicGame {
  private LinkedList<Monster> monsters;

  public List<Monster> getMonsters() {
    return monsters;
  }
}

โค้ด Player อาจเป็นดังนี้

  public boolean isHit() {

    //--------------------------------------
    // approach 1: keep field
    List<Monster> monsters = game.getMonsters();

    //--------------------------------------
    // approach 2: get it from class Game
    List<Monster> monsters = MySampleGamge.getMonsters();

    for (Monster monster : monsters) {
      // doSomething....
    }
  }

การแจ้งเหตุการณ์

เมื่อมีเหตุการณ์ที่เราจำเป็นต้องแจ้งกับ game วิธีที่ทำได้ง่าย ๆ ถ้าเราสามารถอ้างถึงคลาส game ได้ คือการให้ object เรียกเมท็อดที่ต้องการในคลาส game

ยกตัวอย่างเช่น เราอาจจะสร้างเมท็อด monsterHit ในคลาส MySampleGame

class MySampleGame externs BasicGame {
  public void monsterHit(Monster monster) {
    //....
  }
}

จากนั้นในเมท็อดที่ object เราก็สามารถเรียกเมท็อดดังกล่าวได้

  public boolean isHit() {
    //...
    for (Monster monster : monsters) {
      if (monster.closeTo(this)) {  //... any condition you like.
        game.monsterHit(monster)
      }
    }
  }