550 likes | 633 Views
Game State และ Game Component. Suphot Sawattiwong tohpus@hotmail.com. GameComponent ใน XNA.
E N D
Game State และ Game Component SuphotSawattiwongtohpus@hotmail.com
GameComponentใน XNA • ใน XNA มีการสร้าง Component เพื่อเรียกใช้ในเกม ใน Class Game Component โดยใน Class ดังกล่าวมีโครงสร้างในการแยก logic ในการใช้งานออกจากตัว Class Game หลักได้ และสามารถเพิ่ม class GameComponentใส่ไว้ใน project อื่นๆ ได้ด้วยเช่นกัน • แต่ GameComponent มีแค่ Method Update เท่านั้น หากต้องการให้มีการควบคุมการ Draw ด้วยให้ใช้ DrawableGameComponent
GameComponentใน XNA • ใน XNA มีการสร้าง Component เพื่อเรียกใช้ในเกม ใน Class Game Component โดยใน Class ดังกล่าวมีโครงสร้างในการแยก logic ในการใช้งานออกจากตัว Class Game หลักได้ และสามารถเพิ่ม class GameComponentใส่ไว้ใน project อื่นๆ ได้ด้วยเช่นกัน • แต่ GameComponent มีแค่ Method Update เท่านั้น
การใช้งาน Game Component • Game Component เป็น Class พิเศษของ XNA ที่ทำงานโดยต้องทำการ Add เข้า Component Collection ซึ่งทำได้ดังนี้ • ประกาศตัวแปร GameComponent • ทำการประกาศ Instance ใน Method Initialize • ทำการ Add เข้า Component Collection ใน Method Initialize GameComponent1gameComp; gameComp = newGameComponent1(this); Components.Add(gameComp);
การทำงานของ Game Component • ดูใน Project Ex10_GameComponentGameLoop1
การทำงานของ Game Component Main Before Game1 game = new Game1() ===================Game1 Constructor Before game.Run(); ===================Initialize ===================Before new GameComponent1(this) -------------------------------------------GameComponent1 Constructor ===================After new GameComponent1(this) ===================Before Components.Add(gameComp) ===================After Components.Add(gameComp) ===================Initialize Before Base.Initialize -------------------------------------------GameComponent1 Initialize() Before base.Initialize() -------------------------------------------GameComponent1 Initialize() After base.Initialize() ===================LoadContent ===================Initialize AferBase.Initialize ===================Update Before Base.Update -------------------------------------------GameComponent1 Update() Before base.Update() -------------------------------------------GameComponent1 Update() After base.Update() ===================Update After Base.Update ===================Draw Before Base.Draw ===================Draw After Base.Draw
การทำงานของ Game Component ===================Update Before Base.Update -------------------------------------------GameComponent1 Update() Before base.Update() -------------------------------------------GameComponent1 Update() After base.Update() ===================Update After Base.Update ===================Draw Before Base.Draw ===================Draw After Base.Draw After game.Run(); ===================UnloadContent
การทำงานของ Game Component 2 อัน Main Before Game1 game = new Game1() ===================Game1 Constructor Before game.Run(); ===================Initialize ===================Before new GameComponent1(this) -------------------------------------------GameComponent1 Constructor ===================After new GameComponent1(this) ===================Before Components.Add(gameComp) ===================After Components.Add(gameComp) ===================Before new GameComponent2(this) ______________________GameComponent2 Constructor ===================After new GameComponent2(this) ===================Before Components.Add(gameComp2) ===================After Components.Add(gameComp2) ดูใน Ex11_GameComponentGameLoop2
การทำงานของ Game Component 2 อัน ===================Initialize Before Base.Initialize -------------------------------------------GameComponent1 Initialize() Before base.Initialize() -------------------------------------------GameComponent1 Initialize() After base.Initialize() ______________________GameComponent2 Initialize() Before base.Initialize() ______________________GameComponent2 Initialize() After base.Initialize() ===================LoadContent ===================Initialize AferBase.Initialize ===================Update Before Base.Update -------------------------------------------GameComponent1 Update() Before base.Update() -------------------------------------------GameComponent1 Update() After base.Update() ______________________GameComponent2 Update() Before base.Update() ______________________GameComponent2 Update() After base.Update() ===================Update After Base.Update
การทำงานของ Game Component 2 อัน ===================Update Before Base.Update -------------------------------------------GameComponent1 Update() Before base.Update() -------------------------------------------GameComponent1 Update() After base.Update() ______________________GameComponent2 Update() Before base.Update() ______________________GameComponent2 Update() After base.Update() ===================Update After Base.Update ===================Draw Before Base.Draw ===================Draw After Base.Draw ===================Update Before Base.Update -------------------------------------------GameComponent1 Update() Before base.Update() -------------------------------------------GameComponent1 Update() After base.Update() ______________________GameComponent2 Update() Before base.Update() ______________________GameComponent2 Update() After base.Update() ===================Update After Base.Update
การทำงานของ Game Component 2 อัน ===================Update Before Base.Update -------------------------------------------GameComponent1 Update() Before base.Update() -------------------------------------------GameComponent1 Update() After base.Update() ______________________GameComponent2 Update() Before base.Update() ______________________GameComponent2 Update() After base.Update() ===================Update After Base.Update ===================Draw Before Base.Draw ===================Draw After Base.Draw After game.Run(); ===================UnloadContent
คำสั่งที่ใช้ในการลบ Game Component • หากมี Game Component อันไหน ที่ไม่ได้ใช้ หากต้องการทำการลบ ให้ใช้คำสั่ง • เช่น Components.Remove(<ชื่อ Component>); Components.Remove(gameComp2);
DrawableGameComponent • GameComponentมีแต่ Method Update ดังนั้น • หากต้องการให้มีการควบคุมการ Draw ด้วยทางทีมพัฒนาDrawableGameComponent
DrawableGameComponent • จากก่อนหน้านี้ ได้มีการใช้วิธีคำนวณ fps ตอนนี้มาทำ fps DrawableGameComponentขึ้นมา • เริ่มแรกสร้าง Project ที่ชื่อ Ex12_FPSDrawable • กด Add New Item โดยการ click ขวา ใน Solution Explorer • เลือก GameComponentแล้วตั้งชื่อว่า FPS
ตัวอย่างการเปลี่ยน GameComponentเป็น DrawableGameComponent จาก public class FPS :Microsoft.Xna.Framework.GameComponent เป็น public class FPS :Microsoft.Xna.Framework.DrawableGameComponent ให้ดูใน Project ที่ชื่อว่า FPSDrawable
ตัวอย่างการ add GameComponentลงใน Game1.cs • ประกาศตัวแปร ที่เป็นชื่อของ GameComponent นั้น เช่นในที่นี้ private FPS fps; เพิ่ม code ในส่วนของ Initialize หรือ Method ที่เหมาะสมแล้วแต่กรณีให้มีการเรียก instance แล้วทำการ add เข้าไป เช่น fps = new FPS(this, true, true,this.TargetElapsedTime); Components.Add(fps);
การเพิ่ม Library เข้า Project • ให้ click ขวา ตามรูป • แล้วเลือก add ไปที่ Existing project ดังภาพ • เลือก Folder Project Library ที่ต้องการแล้วกด ok
การเพิ่ม Library เข้า Project • ให้ click ขวา ตามรูปแล้วเลือก add Reference…
การเพิ่ม Library เข้า Project • เลือก Library ที่ต้องการ ตามรูป แล้วกด ok
การเพิ่ม Library เข้า Project • จากนั้นให้ทำการพิมพ์ข้อความเพื่อขอใช้ Library โดยการพิมพ์ using ตามด้วยชื่อ NameSpaceเช่น using PhotXNALibrary; • จากนั้นสามารถใช้งานได้ตามปกติต่อไป ลองดูตามตัวอย่างต่อไปในเรื่องของ SpriteAnimation
Parallax Scrolling SuphotSawattiwong tohpus@hotmail.com
Parallax Scrolling • การทำ Parallax Background คือการวาด Background ต่อเนื่องกันไป ให้ดูเหมือนกับว่า มี Background ที่ยาวมากแบบไร้ชอบเขต โดยหลักการจะมีการDraw Background 2 รอบเพื่อทำให้ต่อเนื่องกันไป
วิธีการใช้งาน Parallax Scrolling • ก่อนอื่นให้ทำการ ประกาศตัวแปรดังต่อไปนี้ ScrollingBackgroundตัวแปร BG; เช่นScrollingBackgroundscBg; • ดูใน Ex13_ParallaxBackground • ทำการประกาศ Constructor ดังต่อไปนี้ scBg = newScrollingBackground(this); • ทำการ Add Component ดังต่อไปนี้ Components.Add(scBg);
การ LoadBG • ตัวแปร ParallaxBG.Load(ชื่อไฟล์, ความกว้าง, ความยาว, ความเร็ว, FPS); โดย Load ที่ LoadContent • เช่น scBg.Load("SlideBG_01_1", graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, 1, 45);
การวาด Parallax Background • การวาด BG ทำได้ใน Method Draw ดังนี้ ตัวแปร.Draw(เวลาในเกม หรือGameTime, ตัวแปร SpriteBatch); เช่น scBg.Draw(gameTime, spriteBatch);
Game State • เกมมีการทำงานหลายส่วน และหลายหน้าจอ อย่างเช่นดังตัวอย่าง • การจัดการหน้าจอ เหล่านี้จำเป็นต้องแยก state ออกจากกันเพื่อให้สะดวกในการเขียน และ debug
Game State • วิธีการแบ่งGameState มีหลายวิธี • การทำGameStateแบบที่ง่ายที่สุดคือการใช้ if else หรือ switch case ในการแยก state โดยจะทำทั้งในส่วน Method Draw และ Update • ทดลองทำGameStateแบบง่ายๆ โดยเปิด Ex14_GameStateAndMenu
การทำGameState • ก่อนอื่น สร้าง enumGameStateใน game1.cs เหนือ Class Game1 ดังนี้ enumGameState { MENU=0, PLAY, GAMEOVER, OPTION }
การทำ GameState • ประกาศตัวแปร ดังต่อไปนี้ ใน Class Game1 • ใน Method Initialize() ให้ทำการกำหนดดังต่อไปนี้ เพื่อกำหนดให้เกมเริ่มต้นที่หน้า Menu GameStatecurrentGameState; currentGameState = GameState.MENU;
การทำGameState • ในส่วน Method Update ให้เพิ่มดังต่อไปนี้ ในส่วนนี้บอกว่าหาก currentGameStateเป็น Menu ก็จะเข้าไปทำการ updateในส่วนUpdateMenuเป็นต้น switch (currentGameState) { caseGameState.MENU: UpdateMenu(gameTime); break; caseGameState.PLAY: UpdatePlay(gameTime); break; }
การทำGameState • สร้าง Method UpdateMenuและ UpdatePlayดังนี้ privatevoidUpdateMenu(GameTimegameTime) { // TODO: เพิ่มส่วนการ Update หน้า Menu } privatevoidUpdatePlay(GameTimegameTime) { // TODO: เพิ่มส่วนการ Update หน้า Play }
การทำGameState • เช่นกันให้ทำแบบเดียวกันกับ Draw Method switch (currentGameState) { caseGameState.MENU: DrawMenu(gameTime); break; caseGameState.PLAY: DrawPlay(gameTime); break; }
การทำGameState • สร้าง Method DrawMenuและ DrawPlayดังนี้ privatevoidDrawMenu(GameTimegameTime) { // TODO: เพิ่มส่วนการ Draw หน้า Menu } privatevoidDrawPlay(GameTimegameTime) { // TODO: เพิ่มส่วนการ Draw หน้า Play }
Game State • จากวิธีข้างต้น เห็นได้ชัดว่าค่อนข้างยุ่งยากและทำให้ Code ค่อนข้างซับซ้อน และในทุก Loop Update กับ Draw ต้องเช็คค่าcurrentState เพื่อเลือกทำ Method ที่ถูกต้อง มันทำให้เสียเวลา • วิธีการทำGameStateที่น่าสนใจที่ควรลองไปศึกษาและทำความเข้าใจ หากมีพื้นฐาน Programming ที่ดี http://creators.xna.com/en-US/samples/gamestatemanagement
Lab7:เกมจับคู่ Card ต่างๆ ต่อไปนี้ • Card ในที่นี้ ผมให้ใช้ Card จำนวนทั้งหมด 10 ใบ โดยจะเหมือนกัน 5 คู่ • Random ตำแหน่งของ Card แต่ละใบ โดยเริ่มแรกให้คว่ำไพ่ไว้ก่อน • ให้ใช้ Mouse เลือกจับคู่ Card โดยหากจับได้การ์ดที่ไม่ตรงกัน ให้จัดการคว่ำหน้า Card เหมือนเดิม และทำการลบคะแนนไป 100 หากจับคู่ได้ ไม่ต้องวาดการ์ดคู่นั้นอีก และทำการบวกคะแนนให้ 200
Matching Game • สถานะของ Card ควรจะมีดังต่อไปนี้ • None กรณี ไม่วาด Card • Close กรณีคว่ำ Card • Open กรณีเปิด Card enumCardStatus { NONE, CLOSE, OPEN }
แนวคิดการทำเกมไพ่ SuphotSawattiwong
จากโจทย์การทำเกมไพ่จับคู่จากโจทย์การทำเกมไพ่จับคู่ • สิ่งที่เรารู้แน่ๆ ว่าต้องทำคือ • ต้องวาดไพ่ 10 ใบ บนหน้าจอ • ไพ่ มี 5 แบบ • แบบละ 2 ใบ ถ้าเกินจะจับคู่ไม่ได้
การวาดภาพไพ่ • เนื่องจากเกมนี้เป็นเกมไพ่ ควรที่จะเริ่มจากการวาดไพ่ก่อนสิ่งที่จำเป็นกับการวาดไพ่มีอะไรบ้าง • ตำแหน่งของไพ่ทั้ง 10 ใบ เมื่อลองได้แค่นี้ ให้ทดลองประกาศ Class Card ออกมาดังนี้ class Card { public Vector2 pos; public Card(Vector2 pos) { this.pos = pos; } }
การวาดภาพไพ่ • เมื่อได้ Class แล้วกลับมาที่ Game1.cs เพื่อประกาศการใช้ Array ดังนี้ • ทำการประกาศตัวแปร Texture2D สำหรับการทำหลังไพ่ดังนี้ • จัดการLoad ภาพในLoadContentดังนี้ Card [] card=new Card[10]; Texture2D backCardTexture; backCardTexture = Content.Load<Texture2D>(@"backcover");
การวาดไพ่ • การวาดภาพไพ่ที่เป็น array ทำได้โดยดังนี้ • กำหนดตำแหน่งให้ Card แต่ละใบก่อน • ทำการวาด Draw Method
กำหนดตำแหน่งให้ Card แต่ละใบก่อน • เนื่องจาก Card ที่กำหนดขึ้นเป็นเพียงแค่จองสมาชิก Array เท่านั้น มันไม่มีค่า หรือเป็น null อยู่นั่นเองจำเป็นต้องประกาศ instance เพื่อให้มีค่าในLoadContentดังต่อไปนี้ card[0] = new Card(new Vector2(130, 30)); card[1] = new Card(new Vector2(260, 30)); card[2] = new Card(new Vector2(390, 30)); card[3] = new Card(new Vector2(520, 30)); card[4] = new Card(new Vector2(650, 30)); card[5] = new Card(new Vector2(130, 230)); card[6] = new Card(new Vector2(260, 230)); card[7] = new Card(new Vector2(390, 230)); card[8] = new Card(new Vector2(520, 230)); card[9] = new Card(new Vector2(650, 230));
ทำการวาด Draw Method • ให้ทำการวาดภาพที่ Draw Method ดังนี้ spriteBatch.Begin(); for(inti=0;i<10;i++) { spriteBatch.Draw(backCardTexture, card[i].pos, Color.White); } spriteBatch.End();
ชนิดของไพ่ • ในโจทย์เรามีไพ่ทั้งหมด 5 แบบ ซึ่งต้อง สุ่มเข้ามาในไพ่ 10 ใบ โดยให้ซ้ำกันแค่อย่างละ 1 คู่ • ก่อนอื่นให้ทำการกำหนดชนิดไพ่ก่อนประกาศ Class Cardดังนี้ public enumCardType { TYPE1 = 0, TYPE2, TYPE3, TYPE4, TYPE5 };
ชนิดของไพ่ • แล้วให้ทำการเพิ่มและทับบรรทัดต่อไปนี้ในส่วน Constructor public Vector2 pos; public CardTypecardType; public Card(Vector2 pos, CardTypecardType) { this.pos = pos; this.cardType = cardType; }
ทำการ Load หน้าไพ่ทั้ง 5แบบในLoadContent • ก่อนอื่นให้ทำการประกาศตัวแปรใน Class Game1 เป็น Texture2D[5] ดังนี้ • ทำการLoadContentดังนี้ Texture2D[] cardTexture=new Texture2D[5]; for (int i = 0; i < 5; i++) { cardTexture[i] = Content.Load<Texture2D>(@"card0" + (i + 1)); }
ทำการแก้ไขการกำหนดค่าในLoadContentดังนี้ทำการแก้ไขการกำหนดค่าในLoadContentดังนี้ • เริ่มแก้ไขการประกาศ Instance ก่อนหน้า card[0] = new Card(new Vector2(130, 30), CardType.TYPE1); card[1] = new Card(new Vector2(260, 30), CardType.TYPE1); card[2] = new Card(new Vector2(390, 30), CardType.TYPE2); card[3] = new Card(new Vector2(520, 30), CardType.TYPE2); card[4] = new Card(new Vector2(650, 30), CardType.TYPE3); card[5] = new Card(new Vector2(130, 230), CardType.TYPE3); card[6] =new Card(new Vector2(260, 230), CardType.TYPE4); card[7] = new Card(new Vector2(390, 230), CardType.TYPE4); card[8] = new Card(new Vector2(520, 230), CardType.TYPE5); card[9] = new Card(new Vector2(650, 230), CardType.TYPE5);
ปรับเปลี่ยนใน Draw Method • ให้ทำการปรับเปลี่ยนดังนี้ spriteBatch.Begin(); for(inti=0;i<10;i++) { spriteBatch.Draw(cardTexture[(int)card[i].cardType], card[i].pos,Color.White); } spriteBatch.End();