首先,了解下WinForm做游戲的基本思路:
做游戲需要的最基本的兩個元素,一個是屏幕,另一個就是在屏幕的移動的對象了。
然后,了解下parint事件,WinForm的對象都是繼承至Control類的,而Control類中包含一個事件PaintEventHandler Paint,paint翻譯過來就是噴繪,類似於繪畫,當容器刷新時,就等於重新噴繪一次圖像,就會觸發此事件。
有了這些,就可以開始做游戲了。
先是定義一個元素(本文是小雞),這個元素包含一張圖片,和X坐標和Y坐標,然后將元素按其坐標,添加進屏幕(WinForm窗口或者其他容器,本文使用PictureBox)中,這樣就屏幕就會在剛才定義的X坐標和Y坐標處,出現一個元素的圖像。
然后,定義一個定時器timer,每30毫秒運行一次,每次運行都要刷新屏幕。自然屏幕刷新就會觸發paint事件啦,本文中的paint事件為GamepictureBox_Paint
那么怎么移動小雞呢?很簡單,在定時器timer的事件里(本文為timer1_Tick)將元素的X坐標改變一下就可以了,然后timer里會進行容器刷新,容器刷新就會觸發
paint事件,然后在paint事件里,重新定位下小雞的X坐標就行了。
不多說了,上代碼。
Form頁面代碼如下:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using Chicken.Properties; namespace Chicken { public partial class MyG : Form { Element Chicken;//小雞類 Road GameRoad;//陸塊類 public int RoadCount;//陸塊數 public int Length;//陸塊長度 int EndX;//設置終點X EventHandler TimerHandler;//時間控制手柄 bool TimerHandlerbool;//是否已傳遞時間手柄 EventHandler AgainGame;//時間控制手柄 int GamePicX; int GamePicY; public MyG() { InitializeComponent(); Initial(20, Resources.Bird.Width + 10);//陸塊長度為小雞長度加10 50個陸塊 } private void Initial(int Rcount, int Len) { AgainGame += new EventHandler(AgainGame_Start);//實例化重新開始手柄 TimerHandler += new EventHandler(Timer_Enabled);//實例化時間手柄 RoadCount = Rcount;//陸塊數 Length = Len;//陸塊長度 TimerHandlerbool = false;//未已傳遞時間手柄 Chicken = new Element(0, 100-Resources.Bird.Height); GameRoad = new Road(RoadCount, Len); GamePicX = 0; GamePicY = 0; Point p = new Point(); p.Offset(GamePicX, GamePicY); GamepictureBox.Location = p; } private void InitialLand(Graphics g) { //Pen pen = new Pen(Color.Green); for (int i = 0; i < GameRoad.ListRoad.Count; i++) { RoadItem Item = GameRoad.ListRoad[i]; if (Item.type == 1)//如果類型為1 是陸塊是陸地 { Image img = GameRoad.LandImgList[Item.imageIndex]; g.DrawImage(img, new Rectangle( Item.start.X, Item.end.Y, Item.end.X - Item.start.X, img.Height ) );//畫陸塊 } } EndX = GameRoad.ListRoad.ElementAt(RoadCount - 1).end.X;//設置終點X this.GamepictureBox.Width = EndX; } /// <summary> /// 時間控制函數 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Timer_Enabled(object sender, EventArgs e) { TimerHandler -= new EventHandler(Timer_Enabled); timer1.Enabled = false; Dead D = new Dead(AgainGame); D.Show(); } /// <summary> /// 游戲開始控制函數 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void AgainGame_Start(object sender, EventArgs e) { AgainGame -= new EventHandler(AgainGame_Start); Initial(RoadCount, Length); timer1.Enabled = true; } private void timer1_Tick(object sender, EventArgs e) { //設置屏幕移動 if ((Chicken.x + this.GamepictureBox.Location.X) > this.Width / 2 && (this.GamepictureBox.Width + this.GamepictureBox.Location.X) > this.Width) { int OffX = 1; if (Chicken.IsSpeedUp) { OffX = 2; } GamePicX = GamePicX - OffX; Point p = GamepictureBox.Location; p.Offset(GamePicX, GamePicY); GamepictureBox.Location = p; } if (Chicken.x + Chicken.bmp.Width / 2 >= EndX) { timer1.Enabled = false; Replay R = new Replay(AgainGame); R.Show(); } int CurrentRoadsIndex = Chicken.x / Length;//獲取當前為第幾個陸塊 if (CurrentRoadsIndex >= RoadCount) { CurrentRoadsIndex = RoadCount - 1; }//如果大於定義總陸塊數 設置為最大數 if (CurrentRoadsIndex < 0) { CurrentRoadsIndex = 0; } if (GameRoad.ListRoad.ElementAt(CurrentRoadsIndex).type == 0)//如果當前陸塊為空 { // Y坐標等於空陸塊Y坐標 if ((Chicken.y + Chicken.bmp.Height) == GameRoad.ListRoad.ElementAt(CurrentRoadsIndex).start.Y) { int DepthEndX = GameRoad.ListRoad.ElementAt(CurrentRoadsIndex).end.X;//X下落點為當前陸塊的X if (CurrentRoadsIndex + 1 <= RoadCount - 1)//如果下一個陸塊存在 { if (GameRoad.ListRoad.ElementAt(CurrentRoadsIndex + 1).type == 0)//如果下一個陸塊也是空 { DepthEndX = GameRoad.ListRoad.ElementAt(CurrentRoadsIndex + 1).end.X;//X下落點為下一個陸塊的X } } if (Chicken.x + Chicken.bmp.Width < DepthEndX)//對象的坐標加對象的寬度 小於空陸塊的尾坐標 { Chicken.IsFalling = true;//下降 if (!TimerHandlerbool) { Chicken.GetHandler(TimerHandler);//傳遞時間控制手柄 TimerHandlerbool = true; } } } } GamepictureBox.Refresh(); } private void GamepictureBox_Paint(object sender, PaintEventArgs e) { Chicken.Draw(e.Graphics); InitialLand(e.Graphics); } private void MyG_KeyDown(object sender, KeyEventArgs e) { if (e.KeyData == Keys.Right) { Chicken.IsRuning = true; } if (e.KeyData == Keys.Space && Chicken.IsRuning) { Chicken.IsSpeedUp = true; } if (e.KeyData == Keys.Up) { Chicken.IsJumping = true; } int CurrentRoadsIndex = Chicken.point.X / Length;//當前陸塊 if (e.KeyData == Keys.Left) { Chicken.Back = true; } } private void MyG_KeyUp(object sender, KeyEventArgs e) { Chicken.IsRuning = false; Chicken.IsSpeedUp = false; Chicken.Back = false; } private void GamepictureBox_MouseDown(object sender, MouseEventArgs e) { Chicken.x = e.X; Chicken.y = e.Y; } } }
元素類,定義幾個變量來控制對象,注釋還算比較多,就不一一解釋了,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; using Chicken.Properties; namespace Chicken { class Element { public int x; public int y; public int JumpHeight = 0;//跳躍高度 private bool JumpTop = false;//是否跳到最高點 public int FallHeight = 0;//跳躍高度 public bool FallDepth = false;//是否跳到最高點 public int BasicSpeed = 1;//基本速度 public bool IsRuning = false;//是否移動 public bool Back = false;//是否后退 public bool IsJumping = false;//是否跳躍 public bool IsSpeedUp = false;//是否加速 public bool IsFalling = false;//是否降落 public Image bmp;//對象圖形 public Image img;//對象圖形 暫不使用 public Point point;//坐標 暫不使用 public EventHandler TimerHandler; public Element(int x, int y) { bmp = Resources.Bird; this.x = x; this.y = y; } public Element(int x,int y,Image img) { bmp = Resources.Bird; this.x = x; this.y = y; this.img = img; } public void Draw(Graphics G) { G.DrawImage(bmp, x, y); Move(); } public void Move() { if (IsFalling) { IsSpeedUp = false; IsJumping = false; IsRuning = false; if (!FallDepth) { this.y += BasicSpeed * 2; FallHeight += BasicSpeed * 2; } if (FallHeight == 50) { FallDepth = true; IsFalling = false; TimerHandler(null, null); }//如果下降了50 則下降完成 不在下降 } if (Back) { bmp = Resources.BirdBack; this.x -= BasicSpeed; } if (IsSpeedUp) { bmp = Resources.Bird; this.x += BasicSpeed*3; } else if (IsRuning) { bmp = Resources.Bird; this.x += BasicSpeed; } if (IsJumping) { if (!JumpTop) { this.y += BasicSpeed * (-2); JumpHeight += BasicSpeed * (2); } else { this.y += BasicSpeed * 2; JumpHeight += BasicSpeed * (-2); } if (JumpHeight == 30) { JumpTop = true; }//如果跳躍了30 則跳躍到頂部 不在上升 if (JumpHeight == 0) { JumpTop = false; IsJumping = false; }//如果回到地面 不在下降 跳躍結束 } } public void GetHandler(EventHandler TimerHandler) { this.TimerHandler = TimerHandler; } } }
然后,創建陸塊類,如下:
using System; using System.Collections.Generic; using System.Text; using System.Drawing; using Chicken.Properties; namespace Chicken { class Road { private Random rand = new Random(); public List<Image> LandImgList = new List<Image>(); public List<RoadItem> ListRoad = new List<RoadItem>(); public int RoadY = 100;//陸地的Y坐標 /// <summary> /// 構建陸地 /// </summary> /// <param name="Number">陸塊數量 必須大於2</param> /// <param name="Length">陸塊長度</param> public Road(int Number, int Length) { if (Length < 2) return; RoadItem StartItem = new RoadItem(0, Length); StartItem.imageIndex = 0;//選擇陸塊的圖像 0為第一個平地 1為第二個左傾斜 2為第三個右傾斜 StartItem.type = 1; ListRoad.Add(StartItem);//先添加一個陸塊 第一個路塊必須是陸地 for (int i = 0; i < Number - 2; i++) { int Temp = rand.Next(0, 3); int Index = 0;//選擇陸塊的圖像 0為第一個平地 1為第二個左傾斜 2為第三個右傾斜 這里暫時不使用 int Ang = 0; if (Temp == 0) { Ang = -20; Index = 2; } else if (Temp == 1) { Ang = 0; Index = 0; } else { Ang = 20; Index = 1; } RoadItem CItem = new RoadItem(Ang, Length); //CItem.imageIndex = Index;獲取隨機陸塊的圖片 這樣獲取Y值需要寫一個一元一次方程獲取 CItem.imageIndex = 0;//這里設置全為第一個圖像 這樣獲取Y值比較方便 if (rand.Next(0, 4) == 1)//4分之1的可能性為空陸塊 CItem.type = 0; else CItem.type = 1; ListRoad.Add(CItem);//添加中間的陸塊 添加進陸塊列表 } RoadItem EndItem = new RoadItem(0, Length); EndItem.imageIndex = 0;//選擇陸塊的圖像 0為第一個平地 1為第二個左傾斜 2為第三個右傾斜 EndItem.type = 1; ListRoad.Add(EndItem);//添加最后一個陸塊 for (int i = 0; i < ListRoad.Count; i++) { RoadItem DrawItem = ListRoad[i]; if (i == 0) { DrawItem.start = new Point(0, RoadY); } else { DrawItem.start = ListRoad[i - 1].end; } DrawItem.end = new Point(DrawItem.start.X + DrawItem.length, RoadY); } //為每一個陸塊 定義 起始和終止向量坐標 LandImgList.Add(Resources.land); //為陸塊使用的圖片列表 賦值 } } public class RoadItem { public int angle; public int length;//陸塊長度 public int type;//0為空,1為陸地 public int imageIndex = 0;//使用的圖片 /// <summary> /// 構建路塊 /// </summary> /// <param name="angle"></param> /// <param name="length">陸塊長度</param> public RoadItem(int angle, int length) { this.angle = angle; this.length = length; } public Point start;//陸塊起始坐標 public Point end;//陸塊終止坐標 } }
輔助類,這兩個類是兩個窗口,我閑MessageBox不太好看,就換了個窗口,但貌似也沒好看到那里去。。。哈哈
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace Chicken { public partial class Replay : Form { EventHandler Again; public Replay(EventHandler Again) { this.Again = Again; InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { this.Close(); Again(null, null); } } }
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace Chicken { public partial class Dead : Form { EventHandler Again; public Dead(EventHandler Again) { this.Again = Again; InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { this.Close(); Again(null, null); } } }
這兩個類是死亡窗口和重新開始窗口。
源代碼下載地址http://download.csdn.net/detail/kiba518/4355712
源代碼中,和文中的代碼稍微有點不一樣,如果我記得沒錯是這里,如下:
if (Chicken.x + Chicken.bmp.Width / 2 >= EndX)
是修改,如果雞身的一半以上超過終點,到達終點,游戲結束。這個源碼上傳時沒修改這里。
不過不影響運行啦,但是還有一些小BUG。。
如果想升級這個游戲也很簡答,比如,定義一個炮彈類,隨機發一個。
當炮彈的矩形和小雞的矩形相碰撞了,就死亡啦,矩形相撞有函數的,有興趣的朋友可以自己擴展。
補充:上是跳躍,左右可以移動,空格是加速,鼠標全屏飛。。。。
開發環境:VS2008。
代碼很簡單,可以復制到別的環境中運行。
----------------------------------------------------------------------------------------------------
注:此文章為原創,任何形式的轉載都請聯系作者獲得授權並注明出處!
若您覺得這篇文章還不錯,請點擊下方的【推薦】,非常感謝!