基于C#制作一个飞机大战小游戏
此文主要基于C#制作一个飞机大战游戏,重温经典的同时亦可学习。
- 实现流程
- 1、创建项目
- 2、界面绘制
- 3、我方飞机
- 4、敌方飞机
- 5、子弹及碰撞检测
实现流程
1、创建项目
- 打开Visual Studio,右侧选择创建新项目。

- 搜索框输入winform,选择windows窗体应用,填写对应的保存路径点击下一步,创建成功后如下图,会有一个默认打开的Form窗体。


2、界面绘制
- 准备对应的素材(飞机、子弹、音效等),通过Icon以及窗体Text属性修改窗体图标以及标题显示;同时配置StartPosition属性值为CenterScreen,让窗体默认居中显示。

- 双击窗体生成窗体加载事件并定义函数对背景进行初始化。

使用Random产生随机数,从资源中获取图片设置为窗体背景。

private const int PLANE_OFFSET = 2; //设置每次定时器触发时图片发生偏移的速度private int pix_x = 0;private int pix_y = 0; //背景图片移动起始的坐标int shot_y = 10;int blood_y = 50;private Image[] bgrounds; //设置多张背景图片,每次运行程序随机产生背景图片int index = 0; //背景图片索引Image avatar = Resource.imgHeadSheep; //角色头像图片Image boomImg = Resource.bomb4; //爆炸效果图片Image shotImg = Resource.shotgun;Image bloodImg = Resource.bloodbox;bool isDropGun = false; //是否产生shotgun的标志bool isDropBox = false; //是否产生bloodbox的标志private void Form1_Load(object sender, EventArgs e)//窗体加载事件{InitBackground(); //初始化背景}public GameForm(){InitializeComponent();this.Size = new Size(420, 630);//让窗体与图片一样大//this.DoubleBuffered = true; //双缓冲区}///<summary>/// 初始化背景,随机生成背景图片/// </summary>public void InitBackground(){bgrounds = new Image[4];Random rd = new Random();index = rd.Next(0, 4);//产生0-3的随机数,表示不同背景bgrounds[0] = Resource.background1;//从资源获取图片bgrounds[1] = Resource.background2;bgrounds[2] = Resource.background3;bgrounds[3] = Resource.background4;}
- 新建一个背景移动函数,通过定时位置让图片发生偏移,防止有空白。

/// <summary>
/// 背景移动函数
/// </summary>
/// <param name="e">图形对象</param>
public void BackMove(Graphics e)//通过定时位置让图片发生偏移,防止有空白
{e = this.CreateGraphics();pix_y += PLANE_OFFSET;if (pix_y > 630){pix_y = 0;}
}
- 通过工具箱拖拽两个定时器到窗体上并设置定时器事件,用于定时生成血包以及强化弹药。


/// <summary>
/// 设置定时器1事件
/// </summary>
private void timer1_Tick(object sender, EventArgs e)
{this.Invalidate(); //使当前窗口无效,系统自动调用OnPaint()函数重绘
}/// <summary>
/// 设置定时器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]);}}
}
- 通过Graphics类绘制游戏界面,Graphics类作用为封装一个GDI+绘图图画,效果如下。
| 函数 | 作用 |
|---|---|
| DrawImage | 在指定位置并且按指定大小绘制指定的Image |
| DrawRectangle | 绘制由Rectangle结构指定的矩形 |
| FillRectangle | 填充由一对坐标、一个宽度和一个高度指定的矩形的内部 |
| DrawString | 在指定位置并目用指定的 Brush 和 Font 对象绘制指定的文本字符串 |
| 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)); //显示分数
}
3、我方飞机

- 创建一个MyPlane实体类,定义字段如下。

public static int x = 180;public static int y = 530;//坐标public static int health = 100; //血量private const int PLANE_OFFSET = 12;//移动速度public static Image myPlaneImg=Resource.plane;//我方飞机图片static List<Keys> keys = new List<Keys>();//键盘键列表,用于控制飞机移动static Image gameOver = Resource.gameover;public static bool isGetGun = false;//是否得到shotgun的标志public static bool isGetBlood = false;//是否得到bloodbox的标志public static bool isGameOver = false; //游戏是否结束的标志public static int score = 0; //得分
- 调用Graphics类的DrawImage函数显示我方飞机。

/// <summary>
/// 显示我方飞机
/// </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;}
}
- 通过Keys类定义键盘事件函数。

| 成员名称 | 说明 |
|---|---|
| KeyCode | 从键值提取键代码的位屏蔽 |
| Modifiers | 从键值提取修饰符的位屏蔽 |
| None | 没有按任何键 |
| LButton | 鼠标左按钮 |
| RButton | 鼠标石按钮 |
| Cancel | Cancel 键 |
| MButton | 鼠标中按钮(三个按钮的鼠标) |
| XButton1 | 第一个X鼠标按钮(五个按钮的鼠标) |
| XButton2 | 第二个 X 鼠标按钮(五个按钮的鼠标) |
| Back | Backspace 键 |
这里先用熟悉的WASD键来控制我方飞机移动。

/// <summary>
/// 显示我方飞机
/// </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>
/// 用键盘控制我方飞机移动
/// </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、敌方飞机

- 创建一个Fighter实体类,定义字段如下。

Image redImg;
Image greenImg;
Image yellowImg;
public Image fighterImg;//敌机图片
private const int FIGHTER_OFFSET = 4;//敌机图片移动速度
public int _x = 0;
public int _y = 0;//敌机图片移动起始的坐标
public static List<Fighter> fighters = new List<Fighter>();//敌机对象列表
private int fi;//敌机图片索引
List<Image> imgList = new List<Image>();//敌机图片列表
public bool flag = false;//碰撞的标志
- 通过Random产生随机数,用于定义随机出现的敌机x以及y点坐标。


/// <summary>
/// 随机产生敌机
/// </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;//横坐标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);
}
- 通过Graphics绘制敌机图片。

/// <summary>
/// 出现敌机
/// </summary>
public void FighterShow(Graphics g)
{g.DrawImage(fighterImg,_x,_y);
}public void fMove()
{_y += FIGHTER_OFFSET;
}/// <summary>
/// 敌机移动函数
/// </summary>
public static void FighterMove(Graphics g)//通过定时位置让图片发生偏移
{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、子弹及碰撞检测

- 创建一个MyBullet实体类,定义字段如下。

private int x;//子弹横坐标
private int y;//子弹纵坐标
private const int BULLET_OFFSET = 18;//移动速度
public int Angle;//子弹角度
private Image bulImg;//定义子弹图片
private const double PI = Math.PI;
public static List<MyBullet> mybulList = new List<MyBullet>();//子弹对象集合static Bitmap bm = new Bitmap(Resource.bomb4);//爆炸图片
public bool isHit = false;//碰撞的标志
- 通过按键盘J键来产生我方子弹

/// <summary>/// 通过按键盘J键来产生我方子弹/// </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));}}}
- 定义敌机碰撞检测方法

/// <summary>
/// 敌机碰撞检测方法
/// </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)) //我方子弹击中敌机,敌机爆炸{mybulList.Remove(mybulList[i]);Fighter.fighters[j].flag = true;if (MyPlane.score < 100){MyPlane.score += 1;}}else if (myPlaneRect.IntersectsWith(fighterRect)) //我方飞机撞上敌机,敌机爆炸{Fighter.fighters[j].flag = true;if (MyPlane.score < 100){MyPlane.score += 1;}}}
}
相关文章:
基于C#制作一个飞机大战小游戏
此文主要基于C#制作一个飞机大战游戏,重温经典的同时亦可学习。 实现流程1、创建项目2、界面绘制3、我方飞机4、敌方飞机5、子弹及碰撞检测实现流程 1、创建项目 打开Visual Studio,右侧选择创建新项目。 搜索框输入winform,选择windows窗体…...
git修改历史提交(commit)信息
我们在开发中使用git经常会遇到想要修改之前commit的提交信息,这里记录下怎么使用git修改之前已经提交的信息。一、修改最近一次commit的信息 首先通过git log查看commit信息。 我这里一共有6次commit记录。 最新的commit信息为“Merge branch ‘master’ of https:…...
代码解析工具cpg
cpg 是一个跨语言代码属性图解析工具,它目前支持C/C (C17), Java (Java 13)并且对Go, LLVM, python, TypeScript也有支持,在这个项目的根目录下: cpg-core为cpg解析模块的核心功能,主要包括将代码解析为图,core模块只包括对C/C/Ja…...
Linux虚拟机部署Java环境-Jdk-Mysql
Linux虚拟机部署 author hf 1.安装 电脑安装x-shell工具,然后使用堡垒机基础控件windows版进行安装扫描,最后点击自动检测,保证能扫描到X-shell工具的安装路径 使用堡垒机登录快照夏选择工具点击Xshell进行连接 查看linux版本 root:~# ca…...
每日学术速递2.9
CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV、cs.AI、cs.LG、cs.IR 1.Graph Signal Sampling for Inductive One-Bit Matrix Completion: a Closed-form Solution(ICLR 2023) 标题:归纳单比特矩阵完成的图信号采样&am…...
【Linux】进程优先级 | 进程的切换 | 环境变量详解
🤣 爆笑教程 👉 《看表情包学Linux》👈 猛戳订阅 🔥 💭 写在前面:我们先讲解进程的优先级,探讨为什么会存在优先级,以及如何查看系统进程、进程优先级的修改。然后讲解进程的切…...
leaflet 实现左卷帘效果 (代码示例045)
第045个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+leaflet中实现左卷帘效果,这里主要引用了leaflet-side-by-side这个插件,直接调用的话,CSS方面有些问题,需要自行调整一下。 直接复制下面的 vue+leaflet源代码,操作2分钟即可运行实现效果 文章目录 示例效果配…...
程序的翻译环境和执行环境
程序环境和预处理🦖程序的翻译环境和执行环境🦖详解编译链接🐳 翻译环境🐳 详解编译过程🐳 运行环境🦖预处理详解🐳 预定义符号🐳 #define🦀 #define 定义标识符…...
2023最新量化优选股票参考(2.9)
还是周一发的那些股票(可以看我周一的文章),安心持仓就好,跑赢指数是大概率的事情,也大概率获得正收益。 其实我知道大家都没法全天一直看盘操作,毕竟要工作,我也是一样,没法一直看盘…...
深眸科技以科技赋能智慧物流搭建,实现周转箱拆垛作业智能化
数字化时代下市场竞争的核心要素转化为科技的竞争,智能化技术的投入是企业占据市场竞争绝对优势的重要支撑。深眸科技凭借轻辙视觉引擎实现周转箱拆垛作业的智能化突破。人力成本增加,企业积极转变特别是在后疫情时代,人力成本迅猛增加&#…...
R数据分析:孟德尔随机化中介的原理和实操二
delta方法 上面的流程跑通之后,对于中介分析,我们需要报告间接效应的估计值和置信区间,还有中介比例的估计值和置信区间,类似下面的这样: 但是其实我们是光跑孟德尔是得不到上面的需要的值的(比如间接效应…...
【SQL开发实战技巧】系列(十二):三问(如何对字符串字母去重后按字母顺序排列字符串?如何识别哪些字符串中包含数字?如何将分隔数据转换为多值IN列表?)
系列文章目录 【SQL开发实战技巧】系列(一):关于SQL不得不说的那些事 【SQL开发实战技巧】系列(二):简单单表查询 【SQL开发实战技巧】系列(三):SQL排序的那些事 【SQL开发实战技巧…...
数据库模式(schema)是什么?
在数据库的术语中,模式(schema)是一个逻辑概念,用于组织数据库中的对象。模式中的对象通常包括表、索引、数据类型、序列、视图、存储过程、主键、外键等等。 模式可以为数据库对象提供逻辑隔离功能,不用应用程序可以…...
出现failed to load steamui.dll如何解决?好的修复方法推荐
当你电脑突然出现failed to load steamui.dll的时候,你是否一脸懵逼?根本不知道发生啥时候,突然就会这样报错,其实造成这个原因,主要是因为问题出在steam上,我们还是有很多种方法可以解决的,今天…...
js 原生事件触发
var event nullevent new Event(input);document.querySelectorAll("input[placeholder点击网址 选择远端数据字典网址]")[0].dispatchEvent(event)...
Nacos安装配置(二)
目录 一、概述 二、Nacos 安装 A)Debian11 1)软件环境 2)下载源码或者安装包 3)mysql配置 4)启动服务器 B) Debian11 1) 安装JDK 2) 安装Maven 3) 安装Nacos2 4) 修改访问参数(/conf/applicati…...
【Linux基础知识】
Linux基础知识 Linux基础知识 系统目录结构 /bin: 命令和应用程序。 /boot: 这里存放的是启动 Linux 时使用的一些核心文件,包括一些连接文件以及镜像文件。 /dev : dev 是 Device(设备) 的缩写, 该目录下存放的是 Linux 的外…...
【王道数据结构】第七章| 查找 | 树
目录 一、查找 1、查找概念 2、顺序查找 3、折半查找 4、分块查找 二、树 1、B树 2、B树的基本操作 3、B树 4、散列查找及其性能分析 5、散列查找及性能分析 一、查找 1、查找概念 查找:在数据集合中寻找满足某种条件的数据元素的过程称为查找。查找…...
VBA提高篇_19 可选参数Optional_ IsMissing _MSgbox
文章目录1. 可选参数Optional2.IsMissing判断参数是否提供,只能判断变体类型3. 使用 : 可以按参数名传递参数 a:1,c:34.Msgbox 常用参数5.VBA颜色常量表1. 可选参数Optional Optional 代表本参数是可选项 False ; 代表参数若不指定,则默认为False Function mySumProduct(r As R…...
【子网划分】求子网网络前缀、子网地址、每个子网可以分配给主机使用的最小地址和最大地址
1、某单位分配到一个地址块152.7.77.0/24,现在需要进一步划分为4个一样大的子网。(10分) 问题: (1) 每个子网的网络前缀有多长? (2) 每一个子网中有多少个地址? (3) 每一个子网的网络地址是什么?…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
命令行关闭Windows防火墙
命令行关闭Windows防火墙 引言一、防火墙:被低估的"智能安检员"二、优先尝试!90%问题无需关闭防火墙方案1:程序白名单(解决软件误拦截)方案2:开放特定端口(解决网游/开发端口不通)三、命令行极速关闭方案方法一:PowerShell(推荐Win10/11)方法二:CMD命令…...
Python第七周作业
Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt,并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径,并创建logs目录(若不存在) 3.递归遍历目录data,输出所有.csv文件的路径…...
智能体革命:企业如何构建自主决策的AI代理?
OpenAI智能代理构建实用指南详解 随着大型语言模型(LLM)在推理、多模态理解和工具调用能力上的进步,智能代理(Agents)成为自动化领域的新突破。与传统软件仅帮助用户自动化流程不同,智能代理能够自主执行工…...
