418383: การเขียนโปรแกรมเกม เขียนเกม breakout
DESCRIPTION
418383: การเขียนโปรแกรมเกม เขียนเกม Breakout. ประมุข ขันเงิน [email protected]. Breakout. เผยแพร่ครั้งแรกในเครื่อง Arcade ในปี 1976 โดยบริษัท Atari ออกแบบโดย Nolan Bushnell และ Steve Bristow ได้รับแรงบรรดาลใจมาจากเกม Pong. Screenshot. วิดีโอ. - PowerPoint PPT PresentationTRANSCRIPT
418383: การเขี�ยนโปรแกรมเกมเขี�ยนเกม Breakout
ประม�ขี ขี�นเงิ�น[email protected]
Breakout
• เผยแพร�คร��งิแรกในเคร��องิ Arcade ในป� 1976 โดยบร�ษั�ท Atari
• ออกแบบโดย Nolan Bushnell และ Steve Bristow• ได#ร�บแรงิบรรดาลใจมาจากเกม Pong
Screenshot
วิ�ด�โอ• http://www.youtube.com/watch?v=JRAPnuwnpRs
กติ�กา• ม�บล'อกอย(� 8 แถวิ• ผ(#เล�นบ�งิค�บแป*นท��อย(�ด#านล�างิขีองิจอให้#เคล��อนไปทางิ
ซ้#ายขีวิา• เม��อล(กบอลโดนบล'อก ม�นจะเด#งิกล�บและบล'อกจะถ(ก
ท-าลาย• เม��อล(กบอลโดนแป*นม�นจะเด#งิกล�บขี.�นขี#างิบน• ผ(#เล�นติ#องิเล��ยงิล(กบอลเพ��อท-าลายบล'อกให้#มากท��สุ�ด
โดยไม�ให้#ล(กบอลติกลงิไปใติ#แป*น• ถ#าล(กบอลติกลงิไปใติ#แป*น ผ(#เล�นจะเสุ�ยติาเล�น• ผ(#เล�นม�ติาเล�นเร��มติ#นอย(�สุามติา ถ#าติาห้มดจะ game over
Arkanoid
• เกมสุร#างิเล�ยนแบบ Breakout• เผยแพร�โดยบร�ษั�ท Taito ในป� 1986• ประสุบควิามสุ-าเร'จมากและม� Arkanoid clone
ออกมาห้ลายติ�วิ• ฟี�เจอร1เพ��มเติ�ม– ม�ห้ลายด�าน– ม�ไอเทมให้#เก'บ– ม�ยานอวิกาศศ�ติร(ให้#ย�งิท-าลาย
วิ�ด�โอ• http://www.youtube.com/watch?v=44rceRqY8_k
BREAKOUT IMPLEMENTATION
เกมท��เราจะเขี�ยน• Arkanoid clone• ม�ไอเทมติ�อไปน��ให้#เก'บ– ท-าให้#แป*นยาวิขี.�น– ท-าให้#แป*นด(ดล(กบอล แล#วิให้#ผ(#ใช้#ปล�อยล(กบอลได#ท�
ห้ล�งิ• ม�เพ�ยงิแค�ด�านเด�ยวิ• ไม�ม�ยานขีองิศ�ติร(
Transition Diagram ขีองิเกม
Title
Play
Game Over
“จบเกม”
“จบเกม”
“กล�บ title”
“เล�นให้ม�”ช้�วิ�ติห้มด
“เร� �มเกม”
Game Clear
บล'อกห้มด
“จบเกม”
“กล�บ title”“เล�นให้ม�”
Play Screen
• สุ�วินท��ซ้�บซ้#อนท��สุ�ดขีองิเกม– ม�พฤติ�กรรมห้ลายแบบ– คล#ายก�บเกมท��ม�ห้ลายห้น#าจอ
• ใช้# State Design Pattern ในการออกแบบ• State ใน Play Screen– Play State = ผ(#ใช้#บ�งิค�บแป*นได# เกมด-าเน�นไปติามกลไก– Bar Dying State = แสุดงิอน�เมช้�นแป*นติาย ผ(#ใช้#บ�งิค�บไม�ได#– Pause State = เกมห้ย�ดน��งิ ม�เมน(ให้#ผ(#ใช้#เล�อกวิ�าจะท-าอะไร
Transition Diagram ขีองิ Play Screen
Play
Pause
Bar Dying
บล'อกห้มด
ช้�วิ�ติห้มด
บอลติกขีอบ
กด Esc
“กล�บสุ(�เกม”
ช้�วิ�ติเห้ล�อ
การเขี�ยนเกมที่��ม� ระบบฟิ�สิ�กสิ�“ ”
เกมท��ม� ระบบฟี5สุ�กสุ1“ ”
• ม� วิ�ติถ�“ ”• วิ�ติถ�สุามารถ ช้นก�น“ ”• ห้ล�งิจากช้นก�นแล#วิเก�ด เห้ติ�การณ์1“ ”• เห้ติ�การณ์1ท-าให้#สุถานะขีองิวิ�ติถ�เปล��ยนไป
• การเคล��อนท��ขีองิวิ�ติถ�ไม�จ-าเป7นติ#องิเป7นไปติามกฎทางิฟี5สุ�กสุ1จร�งิๆ
ติ�วิอย�างิเกมท��ม�ระบบฟี5สุ�กสุ1• The Incredible Machine• Crayon Physics
• Pong• Breakout• Super Mario Brothers• First Person Shooter ติ�างิๆ• เกมติ�อสุ(#ติ�างิๆ
The Incredible Machine
• http://www.youtube.com/watch?v=EJbEDlDDVVc
Crayon Physics
• http://www.youtube.com/watch?v=sZaxO6wbxi8
Super Mario World’s Physics System
• http://www.youtube.com/watch?v=LmR2bt3-mXY
สุ�วินประกอบขีองิเกมท��ม�ระบบฟี5สุ�กสุ1• วิ�ติถ�– ล(กบอล ติ�วิละคร บล'อก พ��น แป*น ฯลฯ– สุ��งิท��เป7นเอกเทศในติ�วิม�นเองิ สุามารถม�ปฏิ�สุ�มพ�นธ์1ก�บ
สุ��งิอ��นได#– สุถานะขีองิวิ�ติถ�ค�อสุถานะขีองิเกม
• กฎทางิฟี5สุ�กสุ1– บรรยายวิ�าวิ�ติถ�เคล��อนท��อย�างิไร– บรรยายวิ�าวิ�ติถ�ติ�างิๆ สุามารถม�ปฏิ�สุ�มพ�นธ์1ก�นได#
อย�างิไรบ#างิ
สุ�วินประกอบขีองิเกมท��ม�ระบบฟี5สุ�กสุ1• เห้ติ�การณ์1– สุ�ญญาณ์วิ�าเก�ดอะไรขี.�นบ#างิในระบบ– เป7นกลไกสุ-าค�ญขีองิระบบฟี5สุ�กสุ1ท��ร #อยสุ�วินติ�างิๆ เขี#า
ด#วิยก�น
ล(ปห้ล�กขีองิเกมท��ม�ระบบฟี5สุ�กสุ1while (true){
ให้#วิ�ติถ�ติ�างิๆ ปร�บปร�งิสุถานะขีองิติ�วิเองิ (= ให้#ม�นเคล��อนท��)เช้'ควิ�าวิ�ติถ�ค(�ใดช้นก�นบ#างิจ�ดการก�บเห้ติ�การณ์1ท��งิห้มดท��เก�ดขี.�น
}
เห้ติ�การณ์1มาจากไห้น?
• เวิลาวิ�ติถ�ปร�บสุถานะขีองิติ�วิเองิ อาจเก�ดเห้ติ�การณ์1ขี.�น– เวิลาขีองิไอเทมเพ��มพล�งิห้มด– เวิลาขีองิด�านท��งิด�านห้มด– ติ�วิละครท-าท�าทางิอะไรติ�างิๆ เสุร'จ
• การช้นก�นขีองิวิ�ติถ�เป7นเห้ติ�การณ์1อย�างิห้น.�งิ• เม��อจ�ดการเห้ติ�การณ์1แล#วิอาจท-าให้#เก�ดเห้ติ�การณ์1อ��น– ติ�วิละครช้นก�บล(กป>น ท-าให้#ติ�วิละครติาย– ติ�วิละครช้นก�บไอเทม ท-าให้#พล�งิเพ��ม– ฯลฯ
ห้ล�กการสุ-าค�ญ• ม�ล�สุติ1ขีองิเห้ติ�การณ์1• เม��อเก�ดเห้ติ�การณ์1ให้#เพ��มม�นเขี#าในล�สุติ1• ติอนท#ายล(ป อ�านล�สุติ1น��นแล#วิจ�ดการม�นท�ละ
เห้ติ�การณ์1
CLASS ต่�างๆ ในเกม BREAKOUT
Class ท��เก��ยวิขี#องิ• public abstract class GameObject– บรรพบ�ร�ษัขีองิวิ�ติถ�ท��งิห้มดในเกม
• public interface Event– บรรพบ�ร�ษัขีองิเห้ติ�การณ์1ท��งิห้มดในเกม
Class ท��เก��ยวิขี#องิ• public class GameState– คลาสุน��เป7นท��รวิม• GameObject ท��งิห้มดในเกม• Event ท��งิห้มดท��เก�ดขี.�นในเฟีรมเฟีรมห้น.�งิ
– Field ท��สุ-าค�ญ• List<GameObject> objects;• List<Event> events;• List<GameObject> objectsToAdd;• List<GameObject> objectsToRemove;
objectsToAdd และ objectsToRemove
• เม��อเกมด-าเน�นไป อาจม�วิ�ติถ�ถ(กเพ��มเขี#ามาห้ร�อถ(กลบออกไป
• แติ�เราไม�ควิรจะลบห้ร�อเพ��มวิ�ติถ�เขี#าในเกมท�นท�เม��อร( #วิ�าติ#องิท-า– ถ#าท-าเช้�นน��การจ�ดการเห้ติ�การณ์1ติ�างิๆ อาจม�ควิามผ�ด
พลาด– ติ�วิอย�างิ
• ถ#าลบวิ�ติถ�ออกเลย • อาจม�เห้ติ�การณ์1ท��เก��ยวิก�บวิ�ติถ�ค#างิอย(�ในล�สุติ1ขีองิเห้ติ�การณ์1• เม��อไปจ�ดการเห้ติ�การณ์1น��นกลายเป7นวิ�าวิ�ติถ�ห้ายไปแล#วิ
objectsToAdd และ objectsToRemove
• ด�งิน��น– เวิลาจะเพ��มวิ�ติถ�ใด เราจะเพ��มวิ�ติถ�น��นใสุ� objectsToAdd– เวิลาจะลบวิ�ติถ�ใด เราจะเพ��มวิ�ติถ�น��นใสุ�
objectsToRemove– ห้ล�งิจากจ�ดการเห้ติ�การณ์1ท�กอย�างิเสุร'จแล#วิ• เราจ.งิเอาวิ�ติถ�ใน objectsToAdd ไปใสุ�ใน objects• เราจ.งิลบวิ�ติถ�ท��อย(�ใน objectsToRemove ท�กติ�วิออกจาก
objects
GameObject ใน Breakout
• Bar = แป*นท��ผ(#ใช้#บ�งิค�บ• Ball = ล(กบอล• Brick = บล'อกท��ลอยอย(�ในฉาก• Item = ไอเทมท��ผ(#ใช้#สุามารถเก'บได#• Border = ขีอบทางิด#านซ้#ายขีวิาและด#านบนขีองิฉาก• DeathZone = บร�เวิณ์ด#านล�างิขีองิห้น#าจอท��เม��อ
ล(กบอลเขี#าไปอย(�ในน��นแล#วิจะถ�อวิ�าออกนอกฉาก
• ท�กคลาสุขี#างิบนเป7น subclass ขีองิ GameObject
GameObject ใน Breakout
• สุ�งิเกติ– เราไม�ม�การเช้'ควิ�าล(กบอลห้ร�อไอเทมออกนอกห้น#าจอ
ห้ร�อไม�– แติ�เราเช้'ควิ�าม�นช้นก�บ DeathZone ห้ร�อไม�แทน
การปร�บสิถานะต่นเองขีองวั�ต่ถ�
คลาสุ GameState
• private void UpdateObjects()– ปร�บสุถานะขีองิ GameObject ท�กติ�วิท��อย(�ใน
GameState
• private void UpdateObject(GameObject obj)– ปร�บสุถานะขีองิ obj เพ�ยงิแติ�ติ�วิเองิ– การท-างิานจะแติกติ�างิก�นติามช้น�ดขีองิ obj– กล�าวิค�อท-า single dispatch ติาม type ขีองิ obj
การให้# GameObject ปร�บสุถานะขีองิตินเองิ
public GameState { : : void UpdateObjects() { foreach (GameObject obj in objects) UpdateObject(obj); }
void UpdateObject(GameObject obj) { UpdateSpecificObject((dynamic)obj); }
: :}
การให้# GameObject ปร�บสุถานะขีองิตินเองิ
• สุ�งิเกติ– UpdateObject จะเร�ยก UpdateSpecificObject
โดยแปลงิ obj ท��ให้#มาเป7น dynamic– ห้ล�งิจากน��เราจะติ#องิเขี�ยน UpdateSpecificObject
สุ-าห้ร�บ GameObject แบบติ�างิๆ ขี.�นมา
การให้# GameObject ปร�บสุถานะขีองิตินเองิ
void UpdateSpecificObject(GameObject obj) { // NOP
}
void UpdateSpecificObject(Ball ball) { ball.UpdatePosition(gameTime); }
void UpdateSpecificObject(Item item) { item.UpdatePosition(gameTime); }
void UpdateSpecificObject(Bar bar) { bar.Update(gameTime); }
การให้# GameObject ปร�บสุถานะขีองิตินเองิ
• ม� GameObject เพ�ยงิสุามแบบท��ติ#องิปร�บสุถานะขีองิตินเองิ– Ball
• ติ#องิเคล��อนท��ไปติามเวิลา– Item
• ติ#องิติกลงิมาขี#างิล�างิขีองิห้น#าจอติามเวิลา– Bar
• เม��อไอเทมเพ��มพล�งิห้มดติ#องิเปล��ยนสุถานเป7นแป*นปกติ�สุ�งิเกติวิ�าเราจะเขี�ยน UpdateSpecificObject สุ-าห้ร�บ
GameObject พวิกน��ไวิ#
การให้# GameObject ปร�บสุถานะขีองิตินเองิ
• สุ�งิเกติวิ�าเราไม�ได#เขี�ยนโค#ดสุ-าห้ร�บ GameObject อ��นๆ นอกจากสุามแบบขี#างิติ#น
• น��เป7นเพราะเราเขี�ยน void UpdateSpecificObject(GameObject obj) {
// NOP
}
• เม��อ UpdateSpecificObject ถ(กเร�ยกก�บ GameObject ประเภทอ��นๆ ท��ไม�ใช้�สุามแบบขี#างิติ#น เมธ์อดขี#างิบนจะถ(กเร�ยกแทน
การเช็ ควั�าวั�ต่ถ�ช็นก�นหร#อไม�
GameState
• void CheckCollisions()– ติรวิจวิ�ติถ�ท�กค(�ใน objects วิ�าม�ติ�วิไห้นช้นก�นบ#างิ– สุ-าห้ร�บท�กค(�ท��ช้นก�น ให้#สุร#างิ CollisionEvent แล#วิใสุ�ลงิ
ใน events
void CheckCollisions() { int count = objects.Count; for (int i = 0; i < count; i++) for (int j = i + 1; j < count; j++) if (GameObject.CheckColision(objects[i], objects[j])) events.Add(new CollisionEvent(objects[i], objects[j]));}
CollisionEventpublic class CollisionEvent : Event{ private GameObject first; public GameObject First { get { return first; } }
private GameObject second; public GameObject Second { get { return second; } }
public CollisionEvent(GameObject first, GameObject second) { this.first = first; this.second = second; }}
GameObject
• public static bool CheckCollision(GameObject first, GameObject second)
– เช้'ควิ�า first ช้นก�บ second ห้ร�อไม�– ถ#าช้นค�อ true ม�เช้�นน��นค�อ false– การท-างิานขี.�นอย(�ก�บช้น�ดขีองิ first และ second– กล�าวิค�อท-า double dispatch– เราใช้# dynamic เพ��อท-า double dispatch
โค#ดpublic static bool CheckColision( GameObject first, GameObject second){ return CheckCollisionSpecific( (dynamic)first, (dynamic)second);}
private static bool CheckCollisionSpecific( GameObject first, GameObject second){ return false;}
โค#ด• ติ�วิอย�างิ CheckCollisionSpecific ขีองิค(�วิ�ติถ�ติ�างิๆ
private static bool CheckCollisionSpecific(Ball ball, Brick brick)
{ return brick.CollisionRect.Intersects(
ball.CollisionRect);}private static bool CheckCollisionSpecific(
Brick brick, Ball ball){ return CheckCollisionSpecific(ball, brick);}
โค#ดprivate static bool CheckCollisionSpecific(
Ball ball, Border border){ return !ball.StickingToBar && ( ball.Position.X - ball.Radius < border.Left || ball.Position.X + ball.Radius > border.Right || ball.Position.Y - ball.Radius < border.Top);}private static bool CheckCollisionSpecific(
Border border, Ball ball){ return CheckCollisionSpecific(ball, border);}
การจั�ดการก�บเหต่�การณ์�
GameState• void HandleEvents()
– ด.งิเห้ติ�การณ์1จาก events มาด(ท�ละติ�วิ แล#วิจ�ดการก�บม�นไปท�ละติ�วิ
void HandleEvents(){ int eventIndex = 0; while (eventIndex < events.Count) { Event ev = events[eventIndex]; HandleEvent(ev); eventIndex++; }}
• ท-าไมเราไม�ใช้# for ห้ร�อ foreach แทน while?– ระห้วิ�างิจ�ดการก�บเห้ติ�การณ์1อย(�อาจจะม�เห้ติ�การณ์1เก�ดเพ��มขี.�นมาก'ได#!
GameState
• void HandleEvent(Event ev)– จ�ดการก�บเห้ติ�การณ์1 ev– เมธ์อดม�พฤติ�กรรมแติกติ�างิก�นติาม ev– กล�าวิค�อติ#องิท-า single dispatch ด#วิย ev– เราใช้# dynamic เพ��อท-า single dispatch อ�กเช้�นเคย
HandleEvent และ HandleSpecificEvent
void HandleEvent(Event ev){ HandleSpecificEvent((dynamic)ev);}
void HandleSpecificEvent(Event ev){ // NOP
}
void HandleSpecificEvent(CollisionEvent ev){ HandleSpecificCollision((dynamic)ev.First, (dynamic)ev.Second);}
HandleSpecificEvent(CollisionEvent ev)
• การจ�ดการเห้ติ�การณ์1ท��วิ�ติถ�ช้นก�นขี.�นอย(�ก�บช้น�ดขีองิวิ�ติถ�ท��ช้นก�น
• กลาวิค�อเราติ#องิท-า double dispatch ติาม ev.first และ ev.second
• เราใช้# dynamic เพ��อท-า double dispatch เช้�นเคย• เราท-าให้#โดยปกติ�ค(�ขีองิขีองิท��ช้นก�นไม�ท-าให้#เก�ดอะไรขี.�นก�อน
void HandleSpecificCollision( GameObject first, GameObject second) { // NOP}
โค#ด• ติ�วิอย�างิการจ�ดการการช้นก�นขีองิวิ�ติถ�ค(�ติ�างิๆvoid HandleSpecificCollision(Ball ball, Border border){ if (ball.Position.X < border.Left + ball.Radius) { ball.Position = new Vector2(border.Left + ball.Radius, ball.Position.Y); ball.Velocity = new Vector2(Math.Abs(ball.Velocity.X), ball.Velocity.Y); } if (ball.Position.X > border.Right - ball.Radius) { ball.Position = new Vector2(border.Right - ball.Radius, ball.Position.Y); ball.Velocity = new Vector2(-Math.Abs(ball.Velocity.X), ball.Velocity.Y); } if (ball.Position.Y < border.Top + ball.Radius) { ball.Position = new Vector2(ball.Position.X, border.Top + ball.Radius); ball.Velocity = new Vector2(ball.Velocity.X, Math.Abs(ball.Velocity.Y)); }}void HandleSpecificCollision(Border border, Ball ball){ HandleSpecificCollision(ball, border);}
โค#ดvoid HandleSpecificCollision(Bar bar, Border border){ if (bar.Left < border.Left) bar.Left = border.Left; if (bar.Right > border.Right) bar.Right = border.Right; foreach (Ball ball in bar.GetStickingBalls()) { if (ball.Left < border.Left) ball.Left = border.Left; if (ball.Right > border.Right) ball.Right = border.Right; ball.UpdatePosition(gameTime); }} void HandleSpecificCollision(Border border, Bar bar){ HandleSpecificCollision(bar, border);}
เอาที่�กอย�างมารวัมก�น
โค#ดการ update สุถานะขีองิ GameState
public void Update(KeySensor keySensor, GameTime gameTime)
{ objectsToAdd.Clear(); objectsToRemove.Clear(); events.Clear();
this.gameTime = gameTime; ControlBar(keySensor); UpdateObjects(); CheckCollisions(); HandleEvents(); RemovePendingObjects(); AddPendingObjects();}
โค#ดการ update สุถานะขีองิ GameState
void ControlBar(KeySensor keySensor){ if (keySensor.IsKeyDown(KeyMapping.leftKey)) bar.MoveLeft((float)ElapsedGameTimeInMilliseconds); if (keySensor.IsKeyDown(KeyMapping.rightKey)) bar.MoveRight((float)ElapsedGameTimeInMilliseconds);
if (bar.HasStickingBalls && keySensor.IsKeyTyped(KeyMapping.launchKey))
{ Ball ball = bar.LaunchOneStickingBall(); AddEvent(new LaunchBallEvent(ball)); }}
โค#ดการ update สุถานะขีองิ GameState
void AddPendingObjects(){ foreach (GameObject obj in objectsToAdd) objects.Add(obj);}
void RemovePendingObjects(){ foreach (GameObject obj in objectsToRemove) objects.Remove(obj);}