การเขียนโปรแกรมด้วย PocketC# ตอนที่ 11-Game:IV

ครับ มาถึงตอนที่ 11 แล้วครับ ใกล้จะจบบทความชุดการเขียนโปรแกรมด้วย PocketC# ของงูดินแล้วนะครับ ผมตั้งใจจะเขียนทั้งหมด 12 ตอนครับ ในตอนสุดท้ายจะเป็นบทสรุปและเอา source code ของโปรแกรม game นี้มาให้เพื่อน ๆ ได้ลองดาวน์โหลดไปศึกษาหรือปรับปรุงเพิ่มเติมได้เองครับ

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

I will write a method to detect collision. I use Rectangle.Intersect to detect the intersection of two sprites,my plane and enemy plane.

แนวคิดการตรวจจับการชนกันหรือซ้อนกันของวัตถุหรือ sprite แต่ละอัน ในที่นี้คือเครื่องบินสองลำ มีแนวทางง่ายมากครับ เพราะ sprite นั้น แท้จริงคือ สี่เหลี่ยมเล็ก ๆ นั่นเอง หากสี่เหลี่ยมมาซ้อนกันเมื่อไหร่ก็แสดงว่าเกิดการชนกันแล้ว ใน PocketC# มีคำสั่งตรวจสอบการซ้อนกันของสี่เหลี่ยมอยู่แล้วครับ คือคำสั่ง Intersect นั่นเองครับ ก่อนอื่นเราจึงต้องประกาศกรอบสี่เหลี่ยมเป็นตัวแทนของ sprite แต่ละอันก่อนครับโดยเพิ่มคำสั่งลงใน class grx : Form ดังนี้

…

class grx : Form

{

…

//myplane & enemyplane rectangle

Rectangle myPlaneRec=new Rectangle(0,0,30,30);

Rectangle enemyPlaneRec=new Rectangle(0,0,30,30);

…

เพื่อน ๆ จะสังเกตว่าเราประกาศกรอบสี่เหลี่ยมเพียงขนาด 30,30 ซึ่งมีขนาดเล็กกว่าขนาด sprite ซึ่งมีขนาด 60,60 ทั้งนี้เพราะต้องการให้ถือว่าเป็นการชนเมื่อถูกลำตัวเครื่องบินจริง ๆ ซึ่งจะอยู่บริเวณกลางของ sprite ครับ

จากนั้นเราก็มาสร้าง method เพื่อตรวจสอบการชนของเครื่องบินทั้งสองลำของเรา โดยอ้างอิงจาก spriteX,Y และ enemyX,Y แล้วใช้ คำสั่ง Intersect เพื่อตรวจสอบครับ จากนั้นให้แสดงผลการชนกันด้วยเสียง notify อย่าลืมประกาศการใช้ DLLImport ในเรื่องเสียงด้วยครับ

I use PlaySound() to show that the collision occurs. Don’t forget to declare P/Invoke DllImport in class.

private void checkCollision()

{

myPlaneRec.X=spriteX;

myPlaneRec.Y=spriteY;

enemyPlaneRec.X=enemyX;

enemyPlaneRec.Y=enemyY;

if(myPlaneRec.IntersectsWith(enemyPlaneRec))

{

PlaySound(“notify”,0,1);

}

}

เมื่อเพื่อน ๆ ลอง build และ run จะเห็นว่าเมื่อเครื่องบินทั้งสองลำบินมาชนกันก็จะมีเสียง notify ต่อเนื่องกันไปจนกว่าจะแยกออกจากกันครับ

เพื่อความสนุกสนานขึ้น เราสามารถเติม sprite ของการระเบิดเป็นภาพเคลื่อนไหวง่าย ๆ ในช่วงที่เครื่องบินชนกันครับ โดยการสร้าง sprite อีก 2 ภาพวาดรูปการระเบิดที่แตกต่างกัน แล้วใช้ Random() สุ่มว่าจะเอาภาพไหนมาแสดงสลับกันไปในช่วงเวลาและตำแหน่งที่เครื่องบินบินชนกัน แค่นี้ก็จะได้ภาพการระเบิดแล้วครับ

I make this game be more interesting by adding animated explosion. Just create 2 more sprites and use Random() to randomly show these sprites.

ขอให้เพื่อน ๆ เพิ่มคำสั่งลงใน class grx : Form ดังนี้ครับ

class grx : Form

{

…

//Graphics declaration

…

Bitmap spriteExpl1=new Bitmap(60,60);

Bitmap sprite Expl2=new Bitmap(60,60);

…

และเพิ่มเติมในส่วนของ initSprite() ดังนี้ครับ

private void initSprite()

{

//my plane

..

//enemy plane

..

//explosion1

gr=Graphics.FromImage(spriteExpl1);

gr.FillEllipse(new SolidBrush(Color.White),10,10,30,30);

gr.FillEllipse(new SolidBrush(Color.Red),30,4,30,25);

//explosion2

gr=Graphics.FromImage(spriteExpl2);

gr.FillEllipse(new SolidBrush(Color.White),25,25,15,20);

gr.FillEllipse(new SolidBrush(Color.Orange),15,7,25,20);

gr.Dispose();

}

จากนั้นเพิ่ม method drawExplosion() ดังนี้

private void drawExplosion()

{

//draw random explosion

imgattr.SetColorKey(spriteExpl1.GetPixel(0,0),spriteExpl1.GetPixel(0,0));

rndX=rand.Next(3);

if(rndX==2)

grBuffer.DrawImage(spriteExpl1,new Rectangle(spriteX,spriteY,60,60),0,0,60,60,GraphicsUnit.Pixel,imgattr);

else

grBuffer.DrawImage(spriteExpl2,new Rectangle(spriteX,spriteY,60,60),0,0,60,60,GraphicsUnit.Pixel,imgattr);

}

และเพิ่มเติมให้วาดภาพการระเบิดเมื่อตรวจพบการชนกันใน method checkCollision() ดังนี้ครับ

private void checkCollision()

{

…

if(myPlaneRec.IntersectsWith(enemyPlaneRec))

{

drawExplosion();

PlaySound(“notify”,0,1);

}

}

เพียงเท่านี้ เพื่อน ๆ ก็จะได้เห็นการระเบิดอย่างสวยงามเวลาเครื่องบินชนกันครับ

To communicate with the player. I use Graphics.DrawString() to show the amount of shield left. If shield left=0 then the game ends. You may add scores or time or number of enemy planes to show on screen. It is up to you to decide what condition in game to make the player win or lose.

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

สำหรับเกมขับเครื่องบินของเรา ศัตรูมีเป้าหมายคือพุ่งเข้าชนเรา ส่วนเราก็ต้องหลบหลีกให้พ้นใช่ไหมครับ ถ้าหลบไม่พ้นก็ถูกชน ดังนั้น ผมจึงกำหนดเป้าหมายว่า หากถูกชนหลาย ๆ ครั้ง เราก็ต้องพ่ายแพ้ ผมจึงกำหนดการสื่อสารกับผู้เล่นในรูปของเกราะป้องกันหรือ shield ของเครื่องบินของเรา เริ่มแรกให้มี shield=20 หากถูกชน shield ก็จะลดลงเรื่อย ๆ จนถึง 0 ก็ถือว่าเราเป็นฝ่ายพ่ายแพ้ เกมยุติครับ ดังนั้นเราจึงต้องกำหนดตัวแปร shield และแสดงผลเป็นตัวอักษรและตัวเลขที่หน้าจอเพื่อสื่อสารกับผู้เล่นโดยใช้คำสั่ง DrawString() ครับ

เนื่องจากคำสั่ง DrawString() มีตัวแปรที่เกี่ยวข้องหลายตัว เช่น ชนิดของอักษร ขนาด สี ตำแหน่ง ฯลฯ จึงต้องกำหนดไว้ตั้งแต่ตอนประกาศตัวแปรใน class grx : Form ดังนี้

class grx : Form

{

…

//shield score

Font messageFont=new Font(FontFamily.GenericSansSerif,10,FontStyle.Regular);

Rectangle messageRectangle=new Rectangle(150,0,100,30);

SolidBrush messageBrush=new SolidBrush(Color.Yellow);

string messageString;

int shield=20;

…

จากนั้นเราก็เพิ่ม method drawScore() ดังนี้ครับ

private void drawScore()

{

messageString=”Shield=”+shield.ToString();

grBuffer.DrawString(messageString,messageFont,messageBrush,messageRectangle);

}

และในส่วนที่เครื่องบินชนกัน ก็กำหนดให้ shield ลดลงไปเรื่อย ๆ ดังนี้

private void checkCollision()

{

…

if(myPlaneRec.IntersectsWith(enemyPlaneRec))

{

drawExplosion();

PlaySound(“notify”,0,1);

shield--;

}

}

และสุดท้ายแล้วครับ เราต้องมี method เพื่อตรวจสอบว่าเมื่อ shield <0 ให้จบเกมทันทีโดยคืนค่าทาง graphics ต่าง ๆ ให้แก่ระบบด้วยครับ โดยการเพิ่ม method checkEnd()

In checkEnd() method, I use Dispose() to return graphics memories to the system and program ends.

private void checkEnd()

{

if(shield<0)

{

grBuffer.Dispose();

grBackground.Dispose();

buffer.Dispose();

spritePlane.Dispose();

spriteEnemy.Dispose();

background.Dispose();

spriteExpl1.Dispose();

spriteExpl2.Dispose();

Application.Exit();

}

}

สรุปว่าใน method OnTimerTick() ต้องมีการตรวจสอบเกมตามลำดับดังนี้

private void OnTimerTick(object sender,EventArgs evArgs)

{

myPlaneMove();

enemyMove();

enemyAttack();

updateBackground();

drawAllPlanes();

checkCollision();

drawScore();

Invalidate();

grBuffer.Dispose();

CheckEnd();

}

อ่าฮ่า เสร็จแล้วครับ เมื่อเพื่อน ๆ build และ run ก็จะมีเครื่องบินศัตรูบินเข้าหาเครื่องบินสีฟ้าของเรา เราต้องคอยหลบหลีกอย่าให้ถูกชน หากถูกชนมาก เกราะป้องกันก็จะลดลงไปเรื่อยๆ หากเกราะหมดเมื่อใด เกมจะยุติ ออกจากโปรแกรมทันทีเลยครับ คอยพบกับบทสรุปในตอนหน้าซึ่งจะเป็นตอนสุดท้ายแล้วครับ

Next time is the last part. I will upload the source code of this graphic game program and EXE file in this website.

ขอให้สนุกครับ

[email protected]

22 สิงหาคม 2550

 

 

 

 

Hosted by www.Geocities.ws

1