基于C#制作一個(gè)飛機(jī)大戰(zhàn)小游戲的全過(guò)程
前言
此文主要基于C#制作一個(gè)飛機(jī)大戰(zhàn)游戲,重溫經(jīng)典的同時(shí)亦可學(xué)習(xí)。

實(shí)現(xiàn)流程
1、創(chuàng)建項(xiàng)目
打開Visual Studio,右側(cè)選擇創(chuàng)建新項(xiàng)目。

搜索框輸入winform,選擇windows窗體應(yīng)用,填寫對(duì)應(yīng)的保存路徑點(diǎn)擊下一步,創(chuàng)建成功后如下圖,會(huì)有一個(gè)默認(rèn)打開的Form窗體。


2、界面繪制
準(zhǔn)備對(duì)應(yīng)的素材(飛機(jī)、子彈、音效等),通過(guò)Icon以及窗體Text屬性修改窗體圖標(biāo)以及標(biāo)題顯示;同時(shí)配置StartPosition屬性值為CenterScreen,讓窗體默認(rèn)居中顯示。

雙擊窗體生成窗體加載事件并定義函數(shù)對(duì)背景進(jìn)行初始化。





使用Random產(chǎn)生隨機(jī)數(shù),從資源中獲取圖片設(shè)置為窗體背景。

private const int PLANE_OFFSET = 2; //設(shè)置每次定時(shí)器觸發(fā)時(shí)圖片發(fā)生偏移的速度
private int pix_x = 0;
private int pix_y = 0; //背景圖片移動(dòng)起始的坐標(biāo)
int shot_y = 10;
int blood_y = 50;
private Image[] bgrounds; //設(shè)置多張背景圖片,每次運(yùn)行程序隨機(jī)產(chǎn)生背景圖片
int index = 0; //背景圖片索引
Image avatar = Resource.imgHeadSheep; //角色頭像圖片
Image boomImg = Resource.bomb4; //爆炸效果圖片
Image shotImg = Resource.shotgun;
Image bloodImg = Resource.bloodbox;
bool isDropGun = false; //是否產(chǎn)生shotgun的標(biāo)志
bool isDropBox = false; //是否產(chǎn)生bloodbox的標(biāo)志
private void Form1_Load(object sender, EventArgs e)//窗體加載事件
{
InitBackground(); //初始化背景
}
public GameForm()
{
InitializeComponent();
this.Size = new Size(420, 630);//讓窗體與圖片一樣大
//this.DoubleBuffered = true; //雙緩沖區(qū)
}
///<summary>
/// 初始化背景,隨機(jī)生成背景圖片
/// </summary>
public void InitBackground()
{
bgrounds = new Image[4];
Random rd = new Random();
index = rd.Next(0, 4);//產(chǎn)生0-3的隨機(jī)數(shù),表示不同背景
bgrounds[0] = Resource.background1;//從資源獲取圖片
bgrounds[1] = Resource.background2;
bgrounds[2] = Resource.background3;
bgrounds[3] = Resource.background4;
}
新建一個(gè)背景移動(dòng)函數(shù),通過(guò)定時(shí)位置讓圖片發(fā)生偏移,防止有空白。

/// <summary>
/// 背景移動(dòng)函數(shù)
/// </summary>
/// <param name="e">圖形對(duì)象</param>
public void BackMove(Graphics e)//通過(guò)定時(shí)位置讓圖片發(fā)生偏移,防止有空白
{
e = this.CreateGraphics();
pix_y += PLANE_OFFSET;
if (pix_y > 630)
{
pix_y = 0;
}
}
通過(guò)工具箱拖拽兩個(gè)定時(shí)器到窗體上并設(shè)置定時(shí)器事件,用于定時(shí)生成血包以及強(qiáng)化彈藥。


/// <summary>
/// 設(shè)置定時(shí)器1事件
/// </summary>
private void timer1_Tick(object sender, EventArgs e)
{
this.Invalidate(); //使當(dāng)前窗口無(wú)效,系統(tǒng)自動(dòng)調(diào)用OnPaint()函數(shù)重繪
}
/// <summary>
/// 設(shè)置定時(shí)器2事件
/// </summary>
private void timer2_Tick(object sender, EventArgs e)
{
Graphics g = this.CreateGraphics();
for (int j = 0; j < Fighter.fighters.Count; j++)
{
if (Fighter.fighters[j].flag)
{
g.DrawImage(boomImg, Fighter.fighters[j].GetLoc());
SoundPlayer music = new SoundPlayer(Resource.BOMB21);
music.Play();
Fighter.fighters.Remove(Fighter.fighters[j]);
}
}
}
通過(guò)Graphics類繪制游戲界面,Graphics類作用為封裝一個(gè)GDI+繪圖圖畫,效果如下。
| 函數(shù) | 作用 |
|---|---|
| DrawImage | 在指定位置并且按指定大小繪制指定的Image |
| DrawRectangle | 繪制由Rectangle結(jié)構(gòu)指定的矩形 |
| FillRectangle | 填充由一對(duì)坐標(biāo)、一個(gè)寬度和一個(gè)高度指定的矩形的內(nèi)部 |
| DrawString | 在指定位置并目用指定的 Brush 和 Font 對(duì)象繪制指定的文本字符串 |
| DrawImage | 在指定位置并且按指定大小繪制指定的Image |


/// <summary>
/// 繪制游戲界面
/// </summary>
/// <param name="g"></param>
private void DrawGame(Graphics g)
{
this.BackMove(g);
g.DrawImage(bgrounds[index], pix_x, pix_y, 420, 630);
g.DrawImage(bgrounds[index], pix_x, pix_y - 630, 420, 630); //繪制背景
g.DrawImage(avatar, 10, 10); //繪制角色頭像
g.DrawRectangle(new Pen(Color.Black), new Rectangle(10, 100, 100, 10)); //繪制血條矩形
g.FillRectangle(Brushes.Red, 10, 101, MyPlane.health, 9); //填充血條矩形
g.DrawRectangle(new Pen(Color.Blue), new Rectangle(10, 120, 100, 10));
g.FillRectangle(Brushes.Green, 11, 121, MyPlane.score, 9);
g.DrawString("Player:摔跤貓子", new Font("宋體", 9, FontStyle.Bold), Brushes.Yellow, new Point(10, 140)); //顯示玩家
g.DrawString("Score:" + MyPlane.score, new Font("宋體", 9, FontStyle.Bold), Brushes.Yellow, new Point(10, 160)); //顯示分?jǐn)?shù)
}
3、我方飛機(jī)

創(chuàng)建一個(gè)MyPlane實(shí)體類,定義字段如下。

public static int x = 180;
public static int y = 530;//坐標(biāo)
public static int health = 100; //血量
private const int PLANE_OFFSET = 12;//移動(dòng)速度
public static Image myPlaneImg=Resource.plane;//我方飛機(jī)圖片
static List<Keys> keys = new List<Keys>();//鍵盤鍵列表,用于控制飛機(jī)移動(dòng)
static Image gameOver = Resource.gameover;
public static bool isGetGun = false;//是否得到shotgun的標(biāo)志
public static bool isGetBlood = false;//是否得到bloodbox的標(biāo)志
public static bool isGameOver = false; //游戲是否結(jié)束的標(biāo)志
public static int score = 0; //得分
調(diào)用Graphics類的DrawImage函數(shù)顯示我方飛機(jī)。

/// <summary>
/// 顯示我方飛機(jī)
/// </summary>
/// <param name="g"></param>
public static void MyPlaneShow(Graphics g)
{
if (health > 0)
{
g.DrawImage(myPlaneImg, x, y);
}
else if (health <= 0 || score <= 0)
{
isGameOver = true;
g.DrawImage(myPlaneImg, 0, -300);
g.DrawImage(gameOver, 10, 260);
}
else if (isGetBlood && health <= 90)
{
health += 10;
}
}
通過(guò)Keys類定義鍵盤事件函數(shù)。

| 成員名稱 | 說(shuō)明 |
|---|---|
| KeyCode | 從鍵值提取鍵代碼的位屏蔽 |
| Modifiers | 從鍵值提取修飾符的位屏蔽 |
| None | 沒(méi)有按任何鍵 |
| LButton | 鼠標(biāo)左按鈕 |
| RButton | 鼠標(biāo)石按鈕 |
| Cancel | Cancel 鍵 |
| MButton | 鼠標(biāo)中按鈕(三個(gè)按鈕的鼠標(biāo)) |
| XButton1 | 第一個(gè)X鼠標(biāo)按鈕(五個(gè)按鈕的鼠標(biāo)) |
| XButton2 | 第二個(gè) X 鼠標(biāo)按鈕(五個(gè)按鈕的鼠標(biāo)) |
| Back | Backspace 鍵 |
這里先用熟悉的WASD鍵來(lái)控制我方飛機(jī)移動(dòng)。

/// <summary>
/// 顯示我方飛機(jī)
/// </summary>
/// <param name="g"></param>
public static void MyPlaneShow(Graphics g)
{
if (health > 0)
{
g.DrawImage(myPlaneImg, x, y);
}
else if (health <= 0 || score <= 0)
{
isGameOver = true;
g.DrawImage(myPlaneImg, 0, -300);
g.DrawImage(gameOver, 10, 260);
}
else if (isGetBlood && health <= 90)
{
health += 10;
}
}
/// <summary>
/// 松開鍵盤鍵
/// </summary>
/// <param name="key"></param>
public static void Keyup(Keys key)
{
keys.Remove(key);
}
/// <summary>
/// 按下鍵盤鍵
/// </summary>
/// <param name="key"></param>
public static void Keydown(Keys key)
{
if (!keys.Contains(key))
{
keys.Add(key);
}
}
/// <summary>
/// 判斷按鍵是否被按下
/// </summary>
/// <param name="key"></param>
/// <returns>是則返回true 不是則返回false</returns>
public static bool IsKeyDown(Keys key)
{
return keys.Contains(key);
}
/// <summary>
/// 用鍵盤控制我方飛機(jī)移動(dòng)
/// </summary>
public static void MyPlaneMove()
{
if(isGameOver)
{
return;
}
if (IsKeyDown(Keys.A))
{
myPlaneImg = Resource.planeLeft;
if (x < 5)
x = 5;
x -= PLANE_OFFSET;
}
if (IsKeyDown(Keys.D))
{
myPlaneImg = Resource.planeRight;
if (x > 370)
x = 370;
x += PLANE_OFFSET;
}
if (IsKeyDown(Keys.W))
{
if (y < 5)
y = 5;
y -= PLANE_OFFSET;
}
if (IsKeyDown(Keys.S))
{
if (y > 530)
y = 530;
y += PLANE_OFFSET;
}
}
4、敵方飛機(jī)

創(chuàng)建一個(gè)Fighter實(shí)體類,定義字段如下。

Image redImg; Image greenImg; Image yellowImg; public Image fighterImg;//敵機(jī)圖片 private const int FIGHTER_OFFSET = 4;//敵機(jī)圖片移動(dòng)速度 public int _x = 0; public int _y = 0;//敵機(jī)圖片移動(dòng)起始的坐標(biāo) public static List<Fighter> fighters = new List<Fighter>();//敵機(jī)對(duì)象列表 private int fi;//敵機(jī)圖片索引 List<Image> imgList = new List<Image>();//敵機(jī)圖片列表 public bool flag = false;//碰撞的標(biāo)志
通過(guò)Random產(chǎn)生隨機(jī)數(shù),用于定義隨機(jī)出現(xiàn)的敵機(jī)x以及y點(diǎn)坐標(biāo)。


/// <summary>
/// 隨機(jī)產(chǎn)生敵機(jī)
/// </summary>
public static void ProduceFighter()
{
Random rad = new Random();
if (rad.Next(18) == 0)
{
Fighter f = new Fighter(rad.Next(0, 350), rad.Next(0, 3));
fighters.Add(f);
}
}
public Fighter(int x,int i)
{
_x = x;//橫坐標(biāo)
fi = i;
redImg = Resource.fighterRed;
greenImg = Resource.fighterGreen;
yellowImg = Resource.fighterYellow;
switch (fi)
{
case 0:
fighterImg = redImg;break;
case 1:
fighterImg = greenImg;break;
case 2:
fighterImg = yellowImg;break;
default:
break;
}
imgList.Add(redImg);
imgList.Add(greenImg);
imgList.Add(yellowImg);
}
通過(guò)Graphics繪制敵機(jī)圖片。

/// <summary>
/// 出現(xiàn)敵機(jī)
/// </summary>
public void FighterShow(Graphics g)
{
g.DrawImage(fighterImg,_x,_y);
}
public void fMove()
{
_y += FIGHTER_OFFSET;
}
/// <summary>
/// 敵機(jī)移動(dòng)函數(shù)
/// </summary>
public static void FighterMove(Graphics g)//通過(guò)定時(shí)位置讓圖片發(fā)生偏移
{
for (int i = 0; i < fighters.Count; i++)
{
fighters[i].FighterShow(g);
fighters[i].fMove();
if (fighters[i]._y > 650)
{
fighters.Remove(fighters[i]);
}
}
}
5、子彈及碰撞檢測(cè)

創(chuàng)建一個(gè)MyBullet實(shí)體類,定義字段如下。

private int x;//子彈橫坐標(biāo) private int y;//子彈縱坐標(biāo) private const int BULLET_OFFSET = 18;//移動(dòng)速度 public int Angle;//子彈角度 private Image bulImg;//定義子彈圖片 private const double PI = Math.PI; public static List<MyBullet> mybulList = new List<MyBullet>();//子彈對(duì)象集合 static Bitmap bm = new Bitmap(Resource.bomb4);//爆炸圖片 public bool isHit = false;//碰撞的標(biāo)志
通過(guò)按鍵盤J鍵來(lái)產(chǎn)生我方子彈

/// <summary>
/// 通過(guò)按鍵盤J鍵來(lái)產(chǎn)生我方子彈
/// </summary>
public static void ProduceMybul()
{
if (!MyPlane.isGameOver && MyPlane.IsKeyDown(Keys.J))
{
mybulList.Add(new MyBullet(MyPlane.x + 13, MyPlane.y - 10, 0));
if (MyPlane.isGetGun)
{
mybulList.Add(new MyBullet(MyPlane.x + 13, MyPlane.y - 8, 60));
mybulList.Add(new MyBullet(MyPlane.x + 7, MyPlane.y - 8, 30));
mybulList.Add(new MyBullet(MyPlane.x + 30, MyPlane.y - 12, 120));
mybulList.Add(new MyBullet(MyPlane.x, MyPlane.y - 7, 150));
}
}
}
定義敵機(jī)碰撞檢測(cè)方法

/// <summary>
/// 敵機(jī)碰撞檢測(cè)方法
/// </summary>
public static void IsHitEnemy(Graphics g)
{
Rectangle myPlaneRect = new Rectangle(MyPlane.x, MyPlane.y, MyPlane.myPlaneImg.Width, MyPlane.myPlaneImg.Height); //包住myplane的Rectangle
//g.DrawRectangle(new Pen(Color.Red), myPlaneRect);
for(int i = 0; i < mybulList.Count; i++)
for (int j = 0; j < Fighter.fighters.Count; j++)
{
Rectangle mybulRect = new Rectangle(mybulList[i].x, mybulList[i].y, 8, 10);
Rectangle fighterRect = new Rectangle(Fighter.fighters[j]._x, Fighter.fighters[j]._y, 65, 45);
//g.DrawRectangle(new Pen(Color.Black), fighterRect);
if (mybulRect.IntersectsWith(fighterRect)) //我方子彈擊中敵機(jī),敵機(jī)爆炸
{
mybulList.Remove(mybulList[i]);
Fighter.fighters[j].flag = true;
if (MyPlane.score < 100)
{
MyPlane.score += 1;
}
}
else if (myPlaneRect.IntersectsWith(fighterRect)) //我方飛機(jī)撞上敵機(jī),敵機(jī)爆炸
{
Fighter.fighters[j].flag = true;
if (MyPlane.score < 100)
{
MyPlane.score += 1;
}
}
}
}
總結(jié)
到此這篇關(guān)于基于C#制作一個(gè)飛機(jī)大戰(zhàn)小游戲的文章就介紹到這了,更多相關(guān)C#制作飛機(jī)大戰(zhàn)小游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MVC設(shè)定默認(rèn)路由為指定的Area下的某個(gè)action
今天小編就為大家分享一篇關(guān)于MVC設(shè)定默認(rèn)路由為指定的Area下的某個(gè)action,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01
詳解C#如何利用爬蟲技術(shù)實(shí)現(xiàn)快捷租房
做為一個(gè)碼農(nóng),大部分都集中在一二線城市,所以租房也就無(wú)可避免,面對(duì)如今五花八門的租房信息,往往很難找到合適的房子。本文教你如何利用爬蟲技術(shù)實(shí)現(xiàn)快捷租房,感興趣的可以了解一下2022-09-09
C#簡(jiǎn)單實(shí)現(xiàn)顯示中文格式星期幾的方法
這篇文章主要介紹了C#簡(jiǎn)單實(shí)現(xiàn)顯示中文格式星期幾的方法,涉及C#常見的日期與時(shí)間以及字符串轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2016-07-07
C#基于DBContext(EF)實(shí)現(xiàn)通用增刪改查的REST方法實(shí)例
這篇文章主要介紹了C#基于DBContext(EF)實(shí)現(xiàn)通用增刪改查的REST方法實(shí)例,是C#程序設(shè)計(jì)中非常實(shí)用的技巧,需要的朋友可以參考下2014-10-10
C#基于SQLiteHelper類似SqlHelper類實(shí)現(xiàn)存取Sqlite數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了C#基于SQLiteHelper類似SqlHelper類實(shí)現(xiàn)存取Sqlite數(shù)據(jù)庫(kù)的方法,涉及C#操作SQLite數(shù)據(jù)庫(kù)的相關(guān)技巧,需要的朋友可以參考下2015-06-06

