標籤:style blog http io ar 使用 sp for on
這些天開始在深圳找工作,想著把從前有些淡忘的技術再溫故下。看到尊敬的《傳智播客》有一期公開課,講的是用c#編寫flappybird小遊戲,也就自己搜了下遊戲資源,也來試試看。
其實用到的技術就是傳智播客講的。只不過項目結構和一些邏輯稍微修改了下,更加符合原遊戲的特點。
————————再次聲明:非本人獨創方案————————————
匯入資源
表單背景設定為background圖片382*681,然後在表單上加個picturebox,放入road圖片,大小展開.
而後加三個timer,依次設定100ms,10ms,10ms
如此,遊戲介面搭建完成。
下面是代碼結構,所有遊戲元素的父類,抽象類別:public abstract class GameObj
小鳥類:public class Bird : GameObj
管道類:public class Pipe : GameObj
重力類:public static class Gravity
GameObj類:添加兩個抽象方法void Move() 和 void Draw(Graphics g)
下面是整個類的代碼:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Drawing;namespace flappybird{ /// <summary> /// 遊戲對象的父類 /// </summary> public abstract class GameObj { public int X { get; set; } public int Y { get; set; } public int Height { get; set; } public int Width { get; set; } public GameObj(int x,int y,int height,int width) { this.X = x; this.Y = y; this.Height = height; this.Width = width; } public abstract void Move(); public abstract void Draw(Graphics g); }}
下面是Bird類源碼:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Drawing;using flappybird.Properties;namespace flappybird{ public class Bird : GameObj { /// <summary> /// 各種小鳥形態數組 /// </summary> private static Image[] imgs ={ Properties.Resources.bird_blue_0, Properties.Resources.bird_blue_1, Properties.Resources.bird_blue_2 }; /// <summary> /// 當前繪製的小鳥形態的索引 /// </summary> public int BirdIndex { get; set; } /// <summary> /// 儲存當前下落速度 /// </summary> public float Speed { get; set; } /// <summary> /// 儲存已下落期間 /// </summary> public float Time { get; set; } private static Bird bird = null; /// <summary> /// 單例模式,確保只執行個體化一個Bird對象 /// </summary> /// <returns></returns> public static Bird GetSingleBird() { if (bird == null) { bird = new Bird(100, 200, 0); } return bird; } /// <summary> /// 調用父類的建構函式 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="birdIndex"></param> private Bird(int x, int y, int birdIndex) : base(x, y, imgs[0].Height, imgs[0].Width) { this.BirdIndex = birdIndex; this.Time = 10f; this.Speed = 0f; } /// <summary> /// 重寫父類的抽象Draw方法 /// </summary> public override void Draw(Graphics g) { switch (this.BirdIndex) { case 0: g.DrawImage(imgs[0], this.X, this.Y); break; case 1: g.DrawImage(imgs[1], this.X, this.Y); break; case 2: g.DrawImage(imgs[2], this.X, this.Y); break; default: break; } this.BirdIndex++; this.BirdIndex %= 3; } /// <summary> /// 重寫父類的抽象Move方法 /// </summary> public override void Move() { this.Y = this.Y <= 60 ? 0 : this.Y - 60; //this.Y -= 30; } /// <summary> /// 擷取繪圖區域,用於碰撞檢測 /// </summary> /// <returns></returns> public static Rectangle GetRectangle() { return new Rectangle(bird.X, bird.Y, Resources.bird_blue_0.Width, Resources.bird_blue_0.Height); } }}
結合注釋,相信大家都能搞懂。。。
Pipe類:
此類中,我定義了一個pipes數組,用來向畫面中呈現3列水管(每列包含上下兩個水管)
然後用InitPipes()函數初始化。此函數中,用了一種比較討巧的方法,就是先判斷數組中最後一個元素是否為null,類似Bird類中使用的單例模式。
具體代碼:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Drawing;using flappybird.Properties;namespace flappybird{ public class Pipe : GameObj { public override void Move() { for (int i = 0; i < 3; i++) { pipes[i, 0].X -= 1; pipes[i, 1].X -= 1; } // this.X -= 1; } public override void Draw(System.Drawing.Graphics g) { for (int i = 0; i < 3; i++) { //畫出上水管 g.DrawImage(Resources.bound_up, pipes[i, 0].X, pipes[i, 0].Y); //畫出下水管 g.DrawImage(Resources.bound_down, pipes[i, 1].X, pipes[i, 1].Y); } } private Pipe(int x, int y) : base(x, y, Resources.bound_down.Height, Resources.bound_down.Width) { } private static Pipe[,] pipes = new Pipe[3, 2]; private static void InitPipes() { if (pipes[2, 1] == null) { Random rand = new Random(DateTime.Now.Millisecond); for (int i = 0; i < 3; i++) { int y = rand.Next(80, 380); //上水管 pipes[i, 0] = new Pipe(300 + i * 200, -y); //下水管 pipes[i, 1] = new Pipe(300 + i * 200, -y + Resources.bound_up.Height + 170); } } } public static Pipe GetPipes(int row, int col) { if (row > 1 || col > 2) { return null; } InitPipes(); return pipes[col, row]; } public static Rectangle GetRectangle(int row,int col) { return new Rectangle(pipes[col, row].X, pipes[col, row].Y, Resources.bound_up.Width, Resources.bound_up.Height); } }}
然後Gravity類:
using System;using System.Collections.Generic;using System.Linq;using System.Text;namespace flappybird{ public static class Gravity { private const float g=9.8f; private const float a = 40f; /// <summary> /// 擷取下落的距離 /// </summary> /// <param name="time"></param> /// <param name="speed"></param> /// <returns></returns> public static int GetDropHeight(float time,float speed) { if (speed<0) { return (int)(0.5f * a * time * time + speed * time); } return (int)(0.5f * g * time * time + speed * time); } /// <summary> /// 擷取下落後的速度 /// </summary> /// <param name="time"></param> /// <param name="speed"></param> /// <returns></returns> public static float GetDropSpeed(float time,float speed) { if (speed<0) { return speed + a * time; } return speed + g * time; } }}
定義一個 float a;這個表示小鳥向上飛的時候受到的向下的加速度。之所以這樣設計,是為了小鳥向上飛的時候,不是一下就跳到某個高度,讓它連續上升,但是減速特快的那種效果。
我看傳智播客裡趙老師是直接設定個上升高度,給人的感覺是小鳥一下就”飛“上去了,這樣設計會簡單些,缺點是沒有連續效果。
大餐來了,Form1代碼:
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 flappybird.Properties;namespace flappybird{ public partial class Form1 : Form { public Form1() { InitializeComponent(); } private bool IsGameOver = false; private void Form1_Load(object sender, EventArgs e) { timer1.Enabled = true; timer2.Enabled = true; timer3.Enabled = true; } private void Form1_Paint(object sender, PaintEventArgs e) { if (IsGameOver) { return; } Bird.GetSingleBird().Draw(e.Graphics); Pipe.GetPipes(0, 0).Draw(e.Graphics); } private void timer1_Tick(object sender, EventArgs e) { if (IsGameOver) { return; } this.Invalidate(); } private void Form1_MouseClick(object sender, MouseEventArgs e) { if (IsGameOver) { return; } //Bird.GetSingleBird().Move(); Bird.GetSingleBird().Speed = -80f; } private void Form1_KeyPress(object sender, KeyPressEventArgs e) { } private void Form1_KeyDown(object sender, KeyEventArgs e) { if (IsGameOver) { return; } if (e.KeyCode == Keys.Space) { //Bird.GetSingleBird().Move(); Bird.GetSingleBird().Speed = -80f; } } /// <summary> /// 計算重力影響 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer2_Tick(object sender, EventArgs e) { if (IsGameOver) { return; } int dropHeight = Gravity.GetDropHeight(Bird.GetSingleBird().Time * 0.01f, Bird.GetSingleBird().Speed); int currentY = Bird.GetSingleBird().Y + dropHeight; int maxY = ClientRectangle.Height - pictureBox1.Height - Bird.GetSingleBird().Height; if (currentY >= maxY) { Bird.GetSingleBird().Y = maxY; } else if (currentY < 0) { Bird.GetSingleBird().Y = 0; } else { Bird.GetSingleBird().Y = currentY; } Bird.GetSingleBird().Speed = Gravity.GetDropSpeed(Bird.GetSingleBird().Time * 0.01f, Bird.GetSingleBird().Speed); } Random rand = new Random(DateTime.Now.Millisecond); private void timer3_Tick(object sender, EventArgs e) { if (IsGameOver) { return; } Pipe.GetPipes(0, 0).Move(); //最靠近左邊的管道列,為了下面的碰撞檢測做準備。 //因為只有最左邊的管道才需要碰撞檢測。避免不必要的操作。 int left = 0; for (int i = 0; i < 3; i++) { //若某一列管道左移出了螢幕範圍,則把其放到螢幕右端外 //然後再進行左移 if (Pipe.GetPipes(0, i).X < -90) { //若i列移出了螢幕,則現在是(i + 1) % 3列在最左側 left = (i + 1) % 3; Console.WriteLine(left); int y = rand.Next(80, 380); //上面的管道 Pipe.GetPipes(0, i).X = 500; Pipe.GetPipes(0, i).Y = -y; //下面的管道 Pipe.GetPipes(1, i).X = 500; Pipe.GetPipes(1, i).Y = -y + Resources.bound_up.Height + 170; } } bool isInterUp = Bird.GetRectangle().IntersectsWith(Pipe.GetRectangle(0, left)); bool isInterDown = Bird.GetRectangle().IntersectsWith(Pipe.GetRectangle(1, left)); bool isInterGround = Bird.GetRectangle().IntersectsWith(new Rectangle(pictureBox1.Location, pictureBox1.Size)); if (isInterUp||isInterDown||isInterGround) { timer1.Enabled = false; timer2.Enabled = false; timer3.Enabled = false; IsGameOver = true; } } }}
結合前面的幾個類,這裡就留給大家自己體會吧。(絕不是我懶得寫說明。哈哈哈)
傳送門:http://download.csdn.net/detail/geeking/8255401
c#版flappybird 未完全實現