Home > C# .NET, game development, XNA > XNA first steps [ เริ่มต้นกับ XNA ]

XNA first steps [ เริ่มต้นกับ XNA ]

ช่วงนี้เป็นช่วงที่ผม เรียนเรื่องการเขียนเกมส์ อืม ไม่ได้ความว่าผมจะไปทำเกมส์ขายที่ไหน หรอก ผมทำงานอยู่ในทีมพัฒนาระบบจำลองยุทธ์ และเราก็เล็งเห็นแล้วว่าอีกไม่นาน เทคโนโลยีที่เราใช้กับ ระบบของเรามันกำลังล้าแล้ว เราก็มองหาเทคโนโลยีที่ดีกว่า ใหม่กว่า และยืนยาวกว่า นั่นเอง

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

ครับเอาเป็นว่า ตัวที่ผมสนใจก็คือ XNA
กล่าวโดยย่อนะครับ XNA เป็น Framework ในการพัฒนาเกมส์ที่ผลิตโดย บริษัท Microsoft แน่นอนครับ มันเป็น เทคโนโลยีที่ทำงานบน .NET Framework ซึ่งตัว XNA นั่นประกอบด้วย library และเครื่งมือ ที่จะช่วยให้ผู้พัฒนาเกมส์สามารถทำงานได้รวดเร็ว และสามารถพัฒนาเกมส์ให้สามารถใช้งานได้ บน Windows Xbox 360 รวมถึง Windows Phone 7 ส่วนในรายละเอียดของ XNA ก็ไปอ่านเพิ่มเติมต่อที่นี่ก็แล้วกันนะครับ  http://en.wikipedia.org/wiki/Microsoft_XNA  [ อะไร อะไรก็ wiki นะครับเดี๊ยวนี้ ]

เอาเป็นว่าผมมองตัวนี้ก็เพราะว่า ผมไม่ต้องไปกังวลในเรื่องของการแสดงภาพ เสียง รวมถึงการจัดการกับ input device ต่าง ๆเลย และที่สำคัญ มันทำงานบน development platform ที่ผมใช้งานอยู่แล้ว แค่นี้เป็นอันว่า จบประเด็นข้อเหตุผลในการเลือก

ลองมามองในด้านของ การพัฒนาเกมส์กันบ้างครับ
– เกมส์ programming นั้นจะแตกต่างจากการเขียนโปรแกรม windows  ตามที่เราทราบกันนะครับ การเขียน โปรแกรมบนwindows นั้นเป็นเขียนแบบ event driven หมายถึง code ที่เราเขียน จะอยู่ใน event handler ซึ่งจะถูกเรียกใช้ก็ต่อเมื่อ มีเหตุการณ์อะไรสักอย่างเกิดขึ้น กับ control เช่นการ click mouse เป็นต้น ขี้เกียจ
– เกมส์ programming นั้นเป็น real-time ครับ และก็ไม่มีการรอ event เพื่อการทำงาน  การทำงานจะเป็น loop ที่เราเรียกว่า game loop มีการคำนวณ game status มีการตรวจสอบ input ภายใน loop ในแต่ระรอบของการทำงาน เพราะฉะนั้นการทำงาน programmer จะต้องเขียนโปรแกรมเพื่อ ตรวจสอบ และสั่งการภายใน loop นั่นเองครับ
ดังนั้น typical game loop ประกอบด้วย ขั้นตอน ตามที่แสดงรูปด้านล่าง
จะเห็นว่า loop นั้นเกิดขึ้นใน ส่วนสีเหลือง ซึ่งก็จะมีการรับค่า input คำนวณสถานะต่าง ๆ ของเกมส์ ตรวจสอบ กฎเกณฑ์ต่าง ที่เกมส์กำหนด update ค่า แล้วจึง ให้ feedback กลับไป เช่นการแสดงหล หรือเสียงเป็นต้น ทำอย่างนี้ไปเรื่อย ๆ จนกว่าจะออกจากเกมส์
ส่วน วงรอบเกมส์ของ XNA จะเห็นดังภาพ ครับ


อันที่จริง มันก็ไม่ได้ต่างกัน นัก เพียงแต่ XNA ทำ loop ไว้ให้ โดยทีกำหนดว่า ภายใน update() function ผู้พัฒนาต้อง ตรวจสอบ input คำนวณสถานะ และตรวจสอบ กฎ ต่าง ๆ  และการ feedback ต่าง ๆ ให้ทำใน Draw() function
คงพอได้ concept นะครับ

ลองมาดูการ implement กันบ้างนะครับ

ก่อนที่เราจะทำงาน กับ XNA ได้ก็ต้องมีการ ติดตั้งกันเสียก่อนนะครับ สามารถไป download ได้ที่  http://creators.xna.com/en-US/downloads ตรวจสอบให้ดีนะครับ ท่านควรจะ download version ไหน ขึ้นอยู่กับว่าท่านใช้ visual studio version ไหน ครับซึ่งมันก็หมายถึงท่านใช้ framework version ไหนนั่นเอง 2.0 3.5 หรือ 4.0 ท่าก็เลือก XNA ให้เหมาะสมกับท่าน ได้เลย หลังจากติดตั้งแล้ว ก็มาเริ่มกันเลยนะครับ ( ของผมเองใช้  version 4.0 เพราะผมใช้ visual studio 2010 .Net framework version 4.0  code เป็น C# .net นะครับ)

เริ่มด้วยการ Project ใหม่นะครับ แล้วเลือก Windows Game เราจะพบว่า template ได้มีการสร้าง ไฟล์ให้เรา หลายไฟล์ที่สำคัญก็คือ Game1.cs ซึ่งเป็น file ที่มี game loop และก็ยังมี Content folder ซึ่งมีไว้สำหรับเก็บ พวก resources ต่าง ๆเช่น รูป sprite ที่เราจะใช้งานเป็นต้น

การเพิ่ม หรือ เอารูปที่เตรียมไว้มาใส่ ใน content ก็เพียงแต่ click mouse ขวาที่ content แล้วเลือก “Add/Existing item…” แล้วก็วิ่งไปหา Image file ที่ต้องการ (ship3 คลิก ขวาแล้วเลือก save linke as…)  อ่านตรงนี้ให้ เข้าใจนะครับ เมื่อ เพิ่ม image เข้ามาใน content แล้วลอง click รูปแล้วดู properties จะเห็นว่า มี Asset Name ที่เป็นชื่อของ รูปภาพ ชื่อเหล่านี้จะเก็บไว้สำหรับอ้างอิงในการ เรียกใช้ต่อไป ครับ

สิ่งที่เราต้องการทำใน Project นี้ก็คือการ load ภาพแล้วแสดงในหน้าจอนั่นเอง ครับ ดังนั้นเราก็แค่ ใส่ code ลงใน LoadContent() method เพื่อ load รูป และใส่ code ใน UnloadContent() เพื่อ เอาสิ่งที่ load ออกไป เรายังไม่มีการ ทำอะไรใน Update() เพราะว่าเราแค่แสดงรูปเท่านั้น   และเพื่อแสดงภาพเราก็จะใส code ลงใน Draw() method ตามนี้เลย ครับ

เพื่อให้มันสั้น ผม ลบ comment ออกนะครับ code ที่เราเพิ่มจะอยู่ระหว่าง // — our code #1,2,3,4,5 นะครับ


public class Game1 : Microsoft.Xna.Framework.Game
{
 GraphicsDeviceManager graphics;
 SpriteBatch spriteBatch;
 // -- our code #1 --------------------------
 private Texture2D ship;
 private Vector2 ship_pos;
 // -----------------------------------------
 public Game1()
 {
   graphics = new GraphicsDeviceManager(this);
   // -- our code #2------------------------------
   graphics.PreferredBackBufferHeight = 600;
   graphics.PreferredBackBufferWidth = 800;
   Window.Title = "Game Test ver. 1.0 - Teerapong S.";
   // ------------------------------------------
   Content.RootDirectory = "Content";
 }

 protected override void Initialize()
 {
   // TODO: Add your initialization logic here
   base.Initialize();
 }

 protected override void LoadContent()
 {
   // Create a new SpriteBatch, which can be used to draw textures.
   spriteBatch = new SpriteBatch(GraphicsDevice);

   // TODO: use this.Content to load your game content here
   // --- our code #3 ------------------------------------
    ship = Content.Load<Texture2D>("ship3");  // using asset name นะครับ
  // -----------------------------------------------------

 }

 protected override void UnloadContent()
 {
  // TODO: Unload any non ContentManager content here
  // --- our code #4 ------------
   ship.Dispose();     // free resources, and release memory ไม่มีก็ยังได้
  // -----------------------------
 }

 protected override void Update(GameTime gameTime)
 {
  // Allows the game to exit
  if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
  this.Exit();

  // TODO: Add your update logic here
  base.Update(gameTime);
 }

 protected override void Draw(GameTime gameTime)
 {
   GraphicsDevice.Clear(Color.CornflowerBlue);

   // TODO: Add your drawing code here
   // --- our code # 5 ------------------------
   spriteBatch.Begin();
   spriteBatch.Draw(ship, ship_pos, Color.White);
   spriteBatch.End();
   // -----------------------------------------
   base.Draw(gameTime);
 }
}

จะเห็นว่าเราประกาศ ตัวแปร 2 ตัว  ship และ ship_pos นะครับ เป็น Texture2D กับ Vector2 จะยังไม่ขอ พูดอะไรเกี่ยว variable type ครับแล้วเรามีการกำหนด ค่าเริ่มต้น ใน constructor นิดหน่อย เป็นการกำหนด ขนาด windows สำหรับแสดงภาพ  our code #2

ต่อมาใน loadContent เราทำการ load resource ชื่อว่า ship3 ตรวจสอบ asset name ของresources ให้ดีนะครับ ไม่จะเป็นต้องตรงกับชื่อไฟล์ก็ได้ ถ้าหากเราไม่ได้มีการแกไขส่วนใหญ่จะต้องกลับชื่อไฟล์

ใน UnloadContent ใส่ไว้เพื่อ unload resource นั่น ๆ นะครับ ผมเข้าใจว่าอาจไม่ต้อง ก็ได้ ปล่อยให้ framework มันจัดการเอง [ ปกติถ้าเรา เขียน c++ หรือ c ต้องกำจัดให้หมด ]

สุดท้ายเรา draw เพื่อแสดงรูป นะครับ

สังเกตว่าการแสดงผลใด ๆ ก็ตามต้อง อยู่ระหว่าง spriteBatch.Begin() และ spriteBatch.End()

ลอง run ดู จะได้รูปเรือ ขนาดเท่าต้นฉบับบ แสดงอยู่ที่ตำแหน่ง x = 400 , y = 300  ( นับจากมุม บนซ้าย x เป็นแนวนอน y เป็น แนวตั้งนะครับ ,มุมบนซ้าย เป็นตำแหน่ง x = 0 , y = 0 )

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

หากต้องการให้มีการเคลื่อนที่จะต้องทำอย่างไร ลองคิดดูครับ …………………………….. ใช้แล้วเปลี่ยน ค่า ship_pos

Ship_pos instance ของ  class Vector2 เป็นซึ่ง coordinate ในระนาบ 2 มิติ มีแค่ค่า x,y เท่านั้น เพราะฉะนั้นการเคลือน ภาพก็ต้องทำการ บวกหรือ ลบ ค่า X หรือ Y นี่แหละครับ และจะต้องทำภายใน Update() mehtod ตาม code ที่แสดงด้านล่าง นะครับ


protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
// ourcode #6 ----------------------------
KeyboardState keystate = Keyboard.GetState();
//ขึ้นบน
if (keystate.IsKeyDown(Keys.Up))
ship_pos = new Vector2(ship_pos.X, ship_pos.Y - 0.5f);
//ลงล่าง
if (keystate.IsKeyDown(Keys.Down))
ship_pos = new Vector2(ship_pos.X, ship_pos.Y + 0.5f);

// ไปซ้าย
if (keystate.IsKeyDown(Keys.Left))
ship_pos = new Vector2(ship_pos.X - 0.5f , ship_pos.Y );
// ไปขวา
if (keystate.IsKeyDown(Keys.Right))
ship_pos = new Vector2(ship_pos.X + 0.5f, ship_pos.Y );
// ---------------------------------------
base.Update(gameTime);
}

Code มีความหมาย ตาม remark ไว้ไห้

เป็นการตรวจจับ keyboard ว่ามีการ กดปุ่ม หัวลูกศรหรือป่าว และเป็น หัวลูกศรทิศไหน ก็ ดำเนินการกับ ดำแปร ship_pos ในด้านที่ ควรจะเป็น ถ้าไม่เข้าใจ ลอง ร่างหน้าจอใน กระดาษดูนะครับ แล้วจะเข้าใจเอง ขี้เกียจอธิบาย lazy  อะ
เอาดูจากรูป ก็แล้วกัน

ส่วนใคร ไม่เข้าใจการเพิ่มค่า shiop_pos.X และ ship_pos.Y ลองทำแบบนี้แทนก็ได้

Ship_pos.Y -= 0.5f ;

หรือ

Ship_pos.Y = Ship_pos.Y – 0.5f ;

ใครไม่เข้าใจอะไร ก็ลอง mail มาคุยกันก็ได้ หรือ คุยกันผ่านทาง blog ก็ได้

ขอให้สนุกสนานกับการเริ่มต้น กับ XNA  นะครับ

ธีระพงษ สนธยามาลย์

Teerapong Sontayaman

s_teerapong2000@yahoo.com


Categories: C# .NET, game development, XNA Tags:
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: