บทความในนิตยสาร Pantip Guide ฉบับที่ 44 : การพัฒนาเกมง่ายๆ ด้วย XNA Game Studio ตอนที่ 4 การควบคุมอุปกรณ์อินพุต...
สวัสดีผู้อ่านทุกท่านครับ ก่อนที่จะเข้าสู่เนื้อหาของบทความในตอนนี้ ผมขอคั่นเรื่องสั้นๆ ก่อน เนื่องจากว่าในวันที่ 11 มิถุนายน 2552 ทางไมโครซอฟต์ได้ประกาศให้ดาวน์โหลด XNA Game Studio 3.1 ซึ่งเป็นเวอร์ชันใหม่ล่าสุดในตอนนี้ โดยผู้ผ่านสามารถดาวน์โหลดได้จาก http://www.microsoft.com/downloads/details.aspx?FamilyID=80782277-d584-42d2-8024-893fcd9d3e82&displaylang=en โดยขั้นตอนการลงโปรแกรมก็ไม่ยาก ซึ่งจะต้อง Uninstall XNA Game Studio 3.0 ออกก่อน แล้วลงตัวใหม่ซึ่งจะมีทั้ง XNA Game Studio 3.0 และ 3.1 ด้วย สำหรับในเวอร์ชันนี้มีการเพิ่มความสามารถเพิ่มเติมที่เด่นๆ ก็คือ มีการสร้างเกมที่มีระบบ Avatar ได้โดยผู้เล่นสามารถปรับแต่งตัวละครของตัวเองได้ และความสามารถที่เพิ่มขึ้นอีกอย่างหนึ่งคือสามารถเล่นไฟล์วิดีโอได้ ซึ่งหลายคนที่เคยใช้ XNA ได้รอคอยกันมานาน ทั้งนี้สามารถดูรายละเอียดเพิ่มเติมได้จาก http://creators.xna.com/en-US/news/xnagamestudio3.1 ดังนั้น บทความต่อจากนี้ไปผมข้อใช้ XNA Game Studio 3.1 ในการสร้างงานประกอบบทความละกันนะครับ
สำหรับในตอนนี้เราจะมาพูดถึงการเขียนโปรแกรมเพื่อควบคุมอุปกรณ์อินพุต โดยจะกล่าวถึงอุปกรณ์หลักที่นิยมใช้สำหรับเล่นเกมคือ คีย์บอร์ด เมาส์ และ Xbox 360 Controller โดยเฟรมเวิร์คที่ใช้สำหรับรับอุปกรณ์และใช้จัดการกับอุปกรณ์อินพุตนี้คือ Microsoft.XNA.Framework.Input ซึ่งเป็นหนึ่งในเฟรมเวิร์คพื้นฐานที่มีมากับ XNA
ก่อนอื่นมาทำความรู้จักกับอุปกรณ์อินพุตทั้งสามนี้ก่อน ซึ่งโดยทั่วไปแล้วอุปกรณ์อินพุตแบ่งออกเป็น 2 แบบด้วยกัน คือ แบบดิจิตอล (Digital) และแบบแอนาลอก (Analog) ซึ่งถ้าเป็นแบบดิจิตอลอุปกรณ์อินพุตจะส่งค่าออกมาเป็น On หรือ Off เท่านั้น ตัวอย่างของอินพุตแบบดิจิตอลคือ ปุ่มบนคีย์บอร์ด หรือปุ่มบน Xbox 360 Controller เป็นต้น สำหรับอินพุตแบบแอนาลอกอุปกรณ์อินพุตจะส่งค่าออกมาเป็นช่วงของตัวเลข ยกตัวอย่างเช่น Stick ใน Xbox 360 Controller เป็นต้น โดยใน XNA Game Studio 3.1 นี้ค่าที่รับมาจาก Stick จะมีค่าอยู่ในช่วง -1.0 ถึง 1.0 และ Trigger จะมีค่าอยู่ในช่วง 0.0 ถึง 1.0 ทั้งสองค่าเป็นตัวแปรชนิด float แต่ถ้าเป็นเมาส์ค่าที่ได้เพิ่มเติมคือตำแหน่งของเคอเซอร์โดยให้ค่าออกมาในระดับพิกเซล

รูปที่ 1 Xbox 360 Controller
รูปที่ 2 Keyboards
รูปที่ 3 Mouse
ที่สำคัญอีกอย่างหนึ่งสำหรับเรื่องอุปกรณ์อินพุตคือถ้าเป็นเมาส์จะรองรับเฉพาะเครื่องพีซี สำหรับเครื่อง Xbox 360 จะรองรับเฉพาะคีย์บอร์ดและ Xbox 360 Controller นะครับ และสำหรับจำนวนปุ่มและจำนวนอุปกรณ์ที่รองรับได้ สรุปดังตาราง
|
อุปกรณ์ |
ปุ่มดิจิตอล |
แอนาลอก |
ความสามารถในการสั่น |
รองรับในเครื่องพีซี |
รองรับในเครื่อง Xbox 360 |
จำนวนอุปกรณ์ ที่รองรับ |
|
Xbox 360 Controller |
14 |
4 |
มี |
ใช่ |
ใช่ |
4 |
|
Keyboard |
>100 |
0 |
ไม่มี |
ใช่ |
ใช่ |
1 |
|
Mouse |
5 |
3 |
ไม่มี |
ใช่ |
ไม่ใช่ |
1 |
เมื่อเราได้ทำความเข้าใจเกี่ยวกับอุปกรณ์อินพุตทั้งสามพอสังเขปแล้ว ก็มาเริ่มเขียนโปรแกรมควบคุมอุปกรณ์เหล่านี้โดยเริ่มจากสร้างโปรเจคใหม่ ผมขอใช้ชื่อโปรเจคว่า Pantip_Article_04 แล้วกันนะครับ

รูปที่ 4 สร้างโปรเจคใหม่ชื่อ Pantip_Article_04
เมื่อสร้างโปรเจคใหม่แล้วเราจะได้โค้ดเริ่มต้นที่ให้เข้ามาซึ่งในส่วนของ Using Statement จะมี Framework Input มาด้วยคือ using Microsoft.Xna.Framework.Input ซึ่งเป็นส่วนสำคัญของบทความตอนนี้
ในโฟว์เดอร์ Contents ให้เพิ่ม SubFolder: Textures พร้อมทั้งเพิ่มรูป sprite.png ซึ่งเป็นรูปของตัวละครโดยแบ่งออกเป็น 4 ส่วน ขนาด 100X400 ดังรูปที่ 5 (หากไม่เข้าใจเรื่อง sprite ลองไปอ่านตอนที่ 3 อีกครั้ง)

รูปที่ 5 Sprite ของตัวละคร
หลังจากนั้นประกาศตัวแปรที่เป็น Global ที่สำคัญคือ sprite เอาไว้เก็บรูป และตัวแปรที่เก็บคุณสมบัติที่เตรียมไว้วาดรูปคือตัวแปร position (ตำแหน่งที่ใช้วาดรูป), scale (ขนาดของรูป), rotation (การหมุนของรูป), และ origin (จุดศูนย์กลางของรูปเพื่ออ้างอิงตอนหมุน) ซึ่งเขียนโค้ดได้ดังนี้
private Texture2D sprite;
private int spriteIndex=0;
private Vector2 position = new Vector2(0.0f, 0.0f);
private Vector2 scale = new Vector2(1.0f, 1.0f);
private float rotation = 0.0f;
private Vector2 origin = new Vector2(50.0f, 50.0f);
จากนั้นในเมธอด LoadContent() ให้เพิ่มคำสั่งโหลดรูปและปรับตำแหน่งที่วาดรูปให้เป็นกึ่งกลางของ Windows form โดย Viewport คือขนาดของฟอร์มที่ใช้งานจริงไม่รวม Title ของวินโดว์
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
sprite = Content.Load<Texture2D>("Textures/sprite");
position.X = graphics.GraphicsDevice.Viewport.Width / 2;
position.Y = graphics.GraphicsDevice.Viewport.Height / 2;
}
สำหรับในเมธอด Update() ให้เพิ่มโค้ดเพื่อเรียกเมธอดในการเรียกใช้งาน Xbox 360 Controller, Keyboard และ Mouse ดังนี้
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
UpdateInputXbox360();
UpdateInputKey();
UpdateInputMouse();
base.Update(gameTime);
}
สร้างเมธอด UpdateInputXbox360() เพื่อควบคุม Xbox 360 Controller ทุกปุ่มดังนี้
private void UpdateInputXbox360()
{
GamePadState s = GamePad.GetState(PlayerIndex.One); //รับ state จาก Xbox 360 Controller
ButtonState bp = ButtonState.Pressed; //กำหนดปุ่มเพื่อเปรียบเทียบว่ามีการกดปุ่มหรือเปล่า
//ตรวจสอบว่ามีการกดปุ่ม A, B, X, Y หรือเปล่าหากมีการกดปุ่มจะปรับขนาดของรูปให้ย่อหรือขยาย
if (s.Buttons.A == bp)
scale.Y *= 1.01f;
if (s.Buttons.B == bp)
scale.X *= 1.01f;
if (s.Buttons.X == bp)
scale.X *= 0.99f;
if (s.Buttons.Y == bp)
scale.Y *= 0.99f;
//ตรวจสอบว่ามีการกดปุ่ม Start หรือเปล่า หากมีการกดจะกำหนดค่าให้เป็นเหมือนตอนเริ่มต้น
if (s.Buttons.Start == bp)
{
position.X = graphics.GraphicsDevice.Viewport.Width / 2;
position.Y = graphics.GraphicsDevice.Viewport.Height / 2;
rotation = 0.0f;
scale = new Vector2(1.0f, 1.0f);
spriteIndex = 0;
}
//ถ้ากดปุ่ม Back จะออกจากโปรแกรมทันที
if (s.Buttons.Back == bp)
Exit();
//ตรวจสอบว่ามีการกดปุ่ม D-Pad หรือเปล่าซึ่งมีอยู่ 4 ทิศทาง โดยหากมีการกดปุ่มจะเลื่อนรูปซ้าย ขวา
//ขึ้นหรือลง พร้อมทั้งปรับหน้าตัวละครให้หันหน้าตามปุ่มที่กดโดยควบคุมจาก spriteIndex
if (s.DPad.Left == bp)
{
spriteIndex = 3;
position.X -= 10.00f;
}
if (s.DPad.Right == bp)
{
spriteIndex = 1;
position.X += 10.00f;
}
if (s.DPad.Up == bp)
{
spriteIndex = 0;
position.Y -= 10.00f;
}
if (s.DPad.Down == bp)
{
spriteIndex = 2;
position.Y += 10.00f;
}
//ตรวจสอบว่ามีการกดปุ่ม Left-Right Shoulder หรือ Left-Right Bumper
//ซึ่งถ้ามีการกดปุ่ม LB จะให้รูปไปอยู่ติดขอบบน และถ้ากดปุ่ม RB จะให้รูปอยู่ติดขอบล่าง
if (s.Buttons.LeftShoulder == bp)
position.Y = origin.Y;
if (s.Buttons.RightShoulder == bp)
position.Y = graphics.GraphicsDevice.Viewport.Height - origin.Y;
//ตรวจสอบว่ามีการกดปุ่ม Left Thumbstick หรือ Right Thumbstick (กดปุ่มตรงกลางของคันโยก)
//ซึ่งถ้ามีการกดปุ่ม Left Thumbstick จะให้รูปไปอยู่ติดขอบซ้าย
//และถ้ากดปุ่ม Right Thumbstick จะให้รูปอยู่ติดขอบขวา
if (s.Buttons.LeftStick == bp)
position.X = origin.X;
if (s.Buttons.RightStick == bp)
position.X = graphics.GraphicsDevice.Viewport.Width-origin.X;
//ตรวจสอบว่ามีการโยก Left Thumbstick โดยหากโยกไปด้านใดรูปก็จะเลื่อนตำแหน่งไปด้านนั้น
Vector2 ts = s.ThumbSticks.Left;
position.X += 9.0f * ts.X;
position.Y -= 9.0f * ts.Y;
//ตรวจสอบว่ามีการโยก Right Thumbstick โดยหากโยกไปแถบซ้าย รูปจะหมุนทวนเข็มนาฬิกา
//แต่ถ้าโยกไปฝั่งขวา รูปจะหมุนตามเข็มนาฬิกา
ts = s.ThumbSticks.Right;
rotation += 0.1f * ts.X;
rotation += 0.1f * ts.Y;
//ตรวจสอบว่ามีการกดปุ่ม Left Trigger หรือ Right Trigger โดย
//ถ้ากด Left Trigger จะเกิดการสั่นที่จอย ด้านซ้าย และถ้ากด Right Trigger จะสั่นที่จอยด้านขวา
float tr = s.Triggers.Left;
GamePad.SetVibration(PlayerIndex.One, tr, 0.0f);
tr = s.Triggers.Right;
GamePad.SetVibration(PlayerIndex.One, 0.0f, tr);
}
ถัดมาเพิ่มเมธอด UpdateInputKey() เพื่อเขียนคำสั่งสำหรับควบคุม Keyboard โดยหลักสำคัญคือสร้างตัวแปร KeyboardState เพื่อเก็บสถานะของคีย์ แล้วตรวจเช็คว่ามีการกดหรือเปล่าโดยใช้คำสั่ง IsKeyDown ซึ่งในตัวอย่างนี้เช็คว่ามีการกดปุ่ม A, W, S, D หรือเปล่าหากมีการกดก็จะเลื่อนรูปไปทางซ้าย ขึ้น ลง ขวา ตามลำดับดังนี้
private void UpdateInputKey()
{
KeyboardState keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(Keys.Escape))
{
Exit();
}
if (keyboardState.IsKeyDown(Keys.A))
{
spriteIndex = 3;
position.X -= 10.11f;
}
if (keyboardState.IsKeyDown(Keys.W))
{
spriteIndex = 0;
position.Y -= 10.11f;
}
if (keyboardState.IsKeyDown(Keys.S))
{
spriteIndex = 2;
position.Y += 10.11f;
}
if (keyboardState.IsKeyDown(Keys.D))
{
spriteIndex = 1;
position.X += 10.11f;
}
}
หลังจากนั้นเพิ่มเมธอด UpdateInputMouse() เพื่อเขียนคำสั่งควบคุมเมาส์ โดยหลักสำคัญอยู่ที่สร้าง MouseState เพื่อเก็บ State ของเมาส์ หากต้องการตรวจสอบการคลิ๊กซ้ายหรือขวาสามารถใช้คำสั่ง current_mouse.LeftButton == ButtonState.Pressed และสามารถเก็บค่าตำแหน่งของเมาส์จาก current_mouse.X หรือ current_mouse.Y ดังนี้
private void UpdateInputMouse()
{
MouseState current_mouse = Mouse.GetState();
if (current_mouse.LeftButton == ButtonState.Pressed)
{
position.X = current_mouse.X;
position.Y = current_mouse.Y;
}
}
ขั้นตอนสุดท้ายคือนำรูป Sprite มาวาดตามตำแหน่ง การหมุน และขนาด ตามที่เราได้กำหนดจากอุปกรณ์นำเข้า โดยเพิ่มโค้ดในเมธอด Draw() ดังนี้
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
/*spriteBatch.Draw(รูป sprite, ตำแหน่ง, ตัดส่วนของรูป sprite มาแสดง ซึ่งมีขนาด 100x100, โทนสีของรูป ถ้าเป็นสีขาวจะเป็นรูปปกติต้นแบบ แต่ถ้าต้องการเปลี่ยนโทนสีก็สามารถทำได้, มุมที่รูปหมุนหน่วยเป็น radian , จุดศูนย์กลางของรูป, ปรับขนาดของรูป, effect ที่ใส่ให้กับรูป, ลำดับ layer ของ sprite มีค่าระหว่าง
0-1);*/
spriteBatch.Draw(sprite, position,new Rectangle(100*spriteIndex,0,100,100), Color.White, rotation, origin, scale, SpriteEffects.None, 0);
spriteBatch.End();
base.Draw(gameTime);
}
แล้วขั้นตอนที่สำคัญอีกอย่างหนึ่งคือ การแสดงรูป Cursor ของเมาส์ ซึ่งโดยปกติแล้วจะไม่แสดง คำสั่งที่ให้แสดงผล เราต้องใส่คำสั่งเพิ่มในคอนสตรัคเตอร์ Game1() ดังนี้
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
this.IsMouseVisible = true;
}
เป็นอันว่าเสร็จเรียบร้อยเมื่อตรวจทานครบถ้วนแล้วถ้าไม่มีสีแดงขีดเส้นใต้ (error) ก็รันโปรแกรมด้วยการกดปุ่ม F5 ได้เลย ซึ่งจะได้ผลลัพธ์ดังรูปที่ 6

รูปที่ 6 ผลจากการรันโปรแกรม
จากที่กล่าวมาข้างต้นตอนนี้เราก็สามารถควบคุมตัวละคร หรือวัตถุอื่นๆ ในเกมของเราได้แล้ว หรือถ้าหากใครที่ต้องการสร้างเป็นคลาสใหม่เพื่อใช้ในการควบคุมตัวละครหลายๆ ตัวพร้อมกันก็สามารถทำได้นะครับ สำหรับในตอนหน้าจะเขียนถึงเรื่องการทำ Splash screen ของเกมเพื่อแสดงโลโก้ของตัวเอง และวิธีการสร้างเมนูในเกม อย่าลืมฝึกเขียนกันเรื่อยๆ นะครับ
| < ย้อนกลับ | Next > |
|---|


Comments