Solitaire with Greenfoot #4

822 views

Published on

Greenfoot接龍(四)—遊戲的操作與控制
發表於:Greenfoot教師工作坊(六)
時間:2012/6/29(五) 14:00
地點:南港高中
(共四集,請續點閱)
http://www.slideshare.net/imacat/solitaire-with-greenfoot-1
http://www.slideshare.net/imacat/solitaire-with-greenfoot-2
http://www.slideshare.net/imacat/solitaire-with-greenfoot-3
http://www.slideshare.net/imacat/solitaire-with-greenfoot-4

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
822
On SlideShare
0
From Embeds
0
Number of Embeds
9
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Solitaire with Greenfoot #4

  1. 1. Greenfoot 接龍(四) 遊戲的操作與控制 依瑪貓/楊士青imacat@mail.imacat.idv.tw 2012/6/29
  2. 2. 「接龍遊戲—由上而下的物件導向程式設計」簡報由 依瑪貓╱楊士青 製作, 以 創用CC Attribution-ShareAlike 3.0 Unported 授權條款 釋出。
  3. 3. 自我介紹依瑪貓╱楊士青臺灣師範大學資訊教育研究所碩一研究生
  4. 4. 如果你前面的練習順利完成, 可以繼續做下去。 不然請開啟 solitaire-4 。
  5. 5. 把接龍寫成遊戲
  6. 6. 想一想接龍遊戲寫到現在, 我們完成了什麼? 還缺什麼?
  7. 7. 我們完成了什麼?遊戲的設計遊戲的邏輯與規則
  8. 8. 我們還缺什麼?遊戲的操作與控制 接龍不是這樣子玩的!
  9. 9. 想一想接龍是怎麼玩的?
  10. 10. (本頁留白)(請勿往後偷看)
  11. 11. 接龍的玩法在撲克牌背上點一下滑鼠翻牌。用滑鼠拖拉撲克牌移牌。
  12. 12. 在牌背上點一下滑鼠翻牌
  13. 13. 在牌背上點一下滑鼠翻牌其實是點在撲克牌上,不是點在牌疊上。 寫在 Card 的 act() 方法中,不是寫在 Pile 的 act() 方法中。
  14. 14. 翻牌的條件規則被點到的牌,牌面原來朝下,未翻開。被點到的牌,是牌疊最上面的一張牌。只有兩種牌疊有關: 未翻開的牌疊:翻開到已翻開的排疊。我們之前 已經寫過 flipNextCard() 暫存區的牌疊:翻開之後,不需要移動牌疊。
  15. 15. 翻牌的條件規則public class Card extends Actor{ public void act() { // 牌面朝下,翻牌。 if (!isFaceUp) { if (Greenfoot.mouseClicked(this) && this == pile.getTopCard()) { if (pile instanceof UnflippedPile) { ((UnflippedPile) pile).flipNextCard(); } else if (pile instanceof WorkingPile) { ((WorkingPile) pile).flipTopCard(); } } } }}
  16. 16. instanceof判斷某物件是不是某類別。 WorkingPile x instanceof WorkingPile WorkingPile x instanceof Pile Card y instanceof Pile?
  17. 17. ((WorkingPile) pile).flip()原來 pile 欄位的型態是 Pile , Pile 沒有 flipTopCard() 方法,只有 WorkingPile 有 flipTopCard() 方法。不過我們知道這時的 pile 是個 WorkingPile (由 instanceof 檢查)。先強制型別轉換: (WorkingPile) pile 。轉換後再執行 flipTopCard() 方法。
  18. 18. 例外:重新翻牌未翻開牌都翻出來時,再點一次重新翻牌。 這時候再點一次時,點到的地方沒有牌了 ,露出的被壓在底下的未翻開牌疊。 點到未翻開牌疊時,退回所有的牌,這時候點到 的不是撲克牌,而是牌疊。
  19. 19. 例外:重新翻牌試著自己實作看看!
  20. 20. (本頁留白)(請勿往後偷看)
  21. 21. 重新翻牌public class UnflippedPile extends Pile{ … public void act() { // 點到牌疊的空位時,就已經沒有牌了,重新翻牌。 if (Greenfoot.mouseClicked(this)) { Table table = (Table) getWorld(); table.getFlippedPile().returnAllCards(); } }}
  22. 22. 試著執行看看! 要先按暫停,按滑鼠右鍵執行 moveToWorking() 將 牌移開,再繼續執行, 才能試翻下面的牌。
  23. 23. 用滑鼠拖拉撲克牌移牌
  24. 24. 用滑鼠拖拉撲克牌移牌試著用滑鼠移動拖拉 撲克牌移牌,你觀 察到了什麼?
  25. 25. 用滑鼠拖拉撲克牌移牌撲克牌拖拉時,分裂 出了一個牌疊,分 裂出來的牌疊,隨 著滑鼠而移動。
  26. 26. 我們其實還有一種牌疊, 平常沒有, 拖拉移牌時才會出現, 並且會隨著滑鼠移動。
  27. 27. 用滑鼠拖拉撲克牌移牌請新增一個牌疊 subclass 子類別, 取名叫做 MovingPile 。
  28. 28. 用滑鼠拖拉撲克牌移牌讓牌疊的位置,跟著滑鼠的位置移動。
  29. 29. 用滑鼠拖拉撲克牌移牌 每回合 act() 的時候,都把牌疊移到目前滑鼠的位子上。
  30. 30. MovingPilepublic class MovingPile extends Pile{ /** * 每回合執行的動作。 */ public void act() { MouseInfo mouse = Greenfoot.getMouseInfo(); setLocation(mouse.getX(), mouse.getY()); }}
  31. 31. MovingPile (如果是 Greenfoot 2.1.1 )public class MovingPile extends Pile{ /** * 每回合執行的動作。 */ public void act() { MouseInfo mouse = Greenfoot.getMouseInfo(); If (mouse != null) { setLocation(mouse.getX(), mouse.getY()); } }}
  32. 32. MouseInfo
  33. 33. MouseInfo查看 Greenfoot API 文件,看看 MouseInfo 是什麼 ?有哪些工具功能 ?
  34. 34. MouseInfo可以用 getX() 和 getY() 取得現在的滑鼠座 標。
  35. 35. 用滑鼠拖拉撲克牌移牌試著執行看看! 按執行。 執行時由右邊產生 MovingPile 。 滑鼠移到桌面上新增 移動牌堆。
  36. 36. 剩下的工作
  37. 37. 想一想有了會隨滑鼠移動的移動牌疊, 剩下的工作,是什麼?
  38. 38. (本頁留白)(請勿往後偷看)
  39. 39. 剩下的工作拖拉開始時,開始移動: 產生新的移動牌疊。 把牌移到移動牌疊上。拖拉結束後,結束移動: 把牌移到滑鼠所在的牌疊,或退回到原牌疊。 刪掉結束功能的移動牌疊。
  40. 40. 拖拉結束後,如果無法移牌時, 撲克牌要退回原牌疊。 所以要記得原牌疊是哪一疊。
  41. 41. 拖拉開始時,開始移動
  42. 42. 拖拉開始時,開始移動和前面翻牌同理,拖拉時拉的是撲克牌,不 是牌疊。
  43. 43. 拖拉開始時,開始移動只要翻開來的牌,都可以開始移動。其實不 限於哪個牌疊。 所以我們把「 startMoving() 開始移動」寫在 Pile 裏,所有牌疊共同繼承共用,不限於哪個 牌疊。
  44. 44. 拖拉開始時,開始移動點到一疊中間的牌時(暫存牌區),那張牌 起,疊在它上面的牌都需一起移動。
  45. 45. 拖拉開始時,開始移動 (這裏蓋掉之前寫的前兩行)public class Card extends Actor{ … private boolean isDragging = false; public void act() { // 牌面朝上,移動撲克牌。 if (isFaceUp) { if (!isDragging && Greenfoot.mouseDragged(this)) { isDragging = true; pile.startMoving(this); } // 牌面朝下,翻牌。 } else { if (Greenfoot.mouseClicked(this) && this == pile.getTopCard()) { … } } }}
  46. 46. 拖拉開始時,開始移動public class Pile extends Actor{ public void startMoving(Card starCard) { int index = -1; for (int i = 0; i < getSize(); i++) { if (getCard(i) == starCard) { index = i; break; } } if (index == -1) { return; } MovingPile target = new MovingPile(this); MouseInfo mouse = Greenfoot.getMouseInfo(); getWorld().addObject(target, mouse.getX(), mouse.getY()); while (index < getSize()) { Card card = takeCard(index); target.addCard(card); } }}
  47. 47. 拖拉開始時,開始移動public class MovingPile extends Pile{ /** 撲克牌是由哪一疊移動的。 */ Pile from = null; /** * 建立一疊新的移動牌疊。 * * @param from 撲克牌是由哪一疊移動的。 */ public MovingPile(Pile from) { this.from = from; }}
  48. 48. 試著執行看看!
  49. 49. 怪怪的…
  50. 50. 想一想雖然是移動了,但是 撲克牌動一下就不 動了,空留下牌疊 框隨滑鼠移動。為什麼呢?
  51. 51. (本頁留白)(請勿往後偷看)
  52. 52. 撲克牌疊移動時, 撲克牌沒有跟著移動。撲克牌應該要跟著移動。
  53. 53. 撲克牌跟著牌疊一起移動public class MovingPile extends Pile{ /** * 每回合執行的動作。 */ public void act() { MouseInfo mouse = Greenfoot.getMouseInfo(); setLocation(mouse.getX(), mouse.getY()); redrawCards(); }}
  54. 54. 撲克牌跟著牌疊一起移動public class MovingPile extends Pile{ /** * 重繪牌疊中撲克牌的位置。 * */ public void redrawCards() { for (int i = 0; i < getSize(); i++) { getCard(i).setLocation(getX(), getY() + 3 + 12 * i); } }}
  55. 55. 試著執行看看!
  56. 56. 拖拉結束後,結束移動
  57. 57. 拖拉結束後,結束移動拖拉結束後,看滑鼠落在哪個牌疊, 檢查看能不能把牌放到那個牌疊。 如果可以,就把牌放到那個牌疊。 如果不行,就把牌退回去。
  58. 58. 拖拉結束後,結束移動public class Card extends Actor { public void act() { if (isFaceUp) { if (!isDragging && Greenfoot.mouseDragged(this)) { … } else if (isDragging && Greenfoot.mouseDragEnded(this)) { isDragging = false; MovingPile movingPile = (MovingPile) pile; Pile target = movingPile.getNowOnPile(); if (target == null) { movingPile.returnCards(); } else if (!target.isAcceptCard(this)) { movingPile.returnCards(); } else { movingPile.moveTo(target); } } } … }}
  59. 59. 拖拉結束後,結束移動import java.util.List;public class MovingPile extends Pile{ /** * 取得現在移到哪個牌疊。 * * @return 目前滑鼠所在的牌疊。若滑鼠所在沒有牌疊,則傳回 null 。 */ public Pile getNowOnPile() { MouseInfo mouse = Greenfoot.getMouseInfo(); List<Pile> piles = getWorld().getObjectsAt(mouse.getX(), mouse.getY(), Pile.class); piles.remove(this); // 排除 掉自己 MovingPile 。 if (piles.size() == 0) { return null; } return piles.get(0); }}
  60. 60. isAcceptCard()原本我們只有暫存牌疊 WorkingPile 和歸整 牌疊 ResultPile 有 isAcceptCard() 。為方便起見,我們寫下父類別 Pile 的 isAcceptCard() ,預設是拒絕。然後再讓 WorkingPile 及 ResultPile 覆寫 override 父類別的 isAcceptCard() 。
  61. 61. isAcceptCard()public class Pile extends Actor{ /** * 是否接受移入撲克牌?一定會傳回否。 * * @param card 要移入的撲克牌 * @return 是否接受移入撲克牌。一定會傳回否。 */ public boolean isAcceptCard(Card card) { return false; }}
  62. 62. returnCards() 和 moveTo()撲克牌由 MovingPile 移出去的時候,是由 下往上抽出來,不是由上往下抽牌。
  63. 63. takeBottomCard()public class MovingPile extends Pile{ /** * 抽掉下面的撲克牌。會同時把牌從牌桌移掉。牌疊空了的時候,會得到 null 。 * * @return 最下面的撲克牌 */ public Card takeBottomCard() { if (getSize() == 0) { return null; } return takeCard(0); }}
  64. 64. returnCards() 和 moveTo() 試著實作看看!
  65. 65. (本頁留白)(請勿往後偷看)
  66. 66. returnCards()public class MovingPile extends Pile{ /** * 把撲克牌退回來源牌疊。牌退回去後,會把這個移動牌疊從牌桌刪掉。 * */ public void returnCards() { while (getSize() > 0) { Card card = takeBottomCard(); from.addCard(card); } getWorld().removeObject(this); }}
  67. 67. moveTo()public class MovingPile extends Pile{ /** * 把撲克牌移到目的地的牌疊。牌移過去後,會把這個移動牌疊從牌桌刪掉。 * * @param target 目的地的牌疊 */ public void moveTo(Pile target) { while (getSize() > 0) { Card card = takeBottomCard(); target.addCard(card); } getWorld().removeObject(this); }}
  68. 68. 試著執行看看!
  69. 69. 如果你上面的練習都做得正確,程式應該會長得像 solitaire-5 一樣。
  70. 70. 想一想還有什麼地方可以改進呢? 可以怎麼修改?
  71. 71. (本頁留白)(請勿往後偷看)
  72. 72. 可以改進的地方 移到暫存區時,滑鼠要移到最上方的牌疊方框才可以。 和我們的視覺操作的直覺不一樣, 可以怎麼改進?
  73. 73. 可以改進的地方 移牌時,牌疊會晃一下,移動牌疊中心點,對齊到滑鼠位置。有沒有辦法避免這個不必要的晃動?
  74. 74. 可以改進的地方為了教學,才使用左上角凸一塊的牌疊底 圖, 可以改用正常的牌疊底圖嗎?
  75. 75. 可以改進的地方可以加個計時器嗎?
  76. 76. 謝謝大家。歡迎提出問題。

×