【C++】做一个飞机空战小游戏(四)——给游戏添加背景音乐(多线程技巧应用)

news2024/11/28 8:51:29

  [导读]本系列博文内容链接如下:

【C++】做一个飞机空战小游戏(一)——使用getch()函数获得键盘码值

【C++】做一个飞机空战小游戏(二)——利用getch()函数实现键盘控制单个字符移动
【C++】做一个飞机空战小游戏(三)——getch()函数控制任意造型飞机图标移动

【C++】做一个飞机空战小游戏(四)——给游戏添加背景音乐(多线程技巧应用)

【C++】做一个飞机空战小游戏(五)——getch()控制两个飞机图标移动(控制光标位置)

【C++】做一个飞机空战小游戏(六)——给两架飞机设置不同颜色(结构体的使用技巧)

目录

一、线程

(一)什么是线程

(二)为什么本游戏要用到多线程

(三)创建线程的步骤

1、引入头文件

2、编写线程函数

3、声明线程号

4、创建线程

5、创建线程的完整代码

二、播放音乐

(一)播放音乐的相关头文件和函数

1、引入头文件

2、播放音乐相关函数

(1)打开音频文件

(2)播放音频文件

(3)关闭音频文件 

(二)播放音频文件的设置

1、打开项目选项对话框

 2、设置链接

三、将循环播放音频线程集成到主程序中

(一)自定义函数

(二)将循环播放音频进程相关函数声明和所引入的头文件集成control_plane.h中

(三)将循环播放音频进程相关函数定义集成到control_plane.cpp文件中

(四)将循环播放音频进程集成到主函数中


本系列文章前三节介绍了如何用getch()函数控制飞机的图标移动,并使用了模块化的设计,为后边的设计打下了一个比较好的基础。

今天介绍如何给游戏添加背景音乐。

一、线程

(一)什么是线程

关于线程的概念,以及进程和线程的关系,网上有好多资料,本文不再赘述.本文只介绍一下小编对两者的理解。

一个具有独立完整功能的程序或软件就是一个进程,这个程序里可以有多个线程并行运行,互不干扰,但是各个线程之间可以传递信息。线程,就是线性的过程,各个程序语句只能依次执行,执行完了前边的才能执行后边的,这个线程中的每条语句都会顺序执行一次。

如果把火车站售票看成是一个进程,那么售票厅里开设的多个售票窗口就是多个线程,在每个窗口购票的人需要排队依次购票,排在前边的购完票了,后边的才能购票。而两个窗口,也就是两个线程之间相互没有影响,这个队伍中的每名购票者速度的快慢,只会对这个窗口排队购票的进度有影响,而不会对另外窗口的购票产生影响。

(二)为什么本游戏要用到多线程

本游戏目前设计了三个模块:检测键盘指令并控制飞机移动模块,背景音乐循环播放模块,敌方炮弹循环掉落模块。

这三个模块都需要循环进行,在程序里都需要用到while(1)循环。如果三个模块放到同一个while(1)循环里,那么就会相互影响进程,游戏的流畅度大大降低。如果在同一个线程里放3个不同的while(1)循环,那么后边连个while(1)循环就根本没有执行的就会了。

所以把三个模块放到3个不同的线程里,才会保证程序的正常运行。

(三)创建线程的步骤

1、引入头文件

#include <pthread.h>

2、编写线程函数

void* thread_name(void* arg) 
{ 
  //此处为函数的具体内容
} 

3、声明线程号

pthread_t tid;//pthread_t为线程类型,tid为线程id变量名

4、创建线程

pthread_create(&tid, NULL, thread_name, NULL);//创建线程

5、创建线程的完整代码

#include <pthread.h>//导入线程头文件库

using namespace std;


//定义线程函数 
void* thread_name(void* arg) //
{ 
  //此处为函数的具体内容,本例要填入播放音频的代码
} 

//主函数 
int main()
{
	pthread_t tid; //声明线程id
  	pthread_create(&tid, NULL, thread_name, NULL);//创建线程
    return 0;
}

二、播放音乐

(一)播放音乐的相关头文件和函数

1、引入头文件

#include <mmsystem.h> //导入声音头文件库
#pragma comment(lib,"winmm.lib")//导入声音的链接库
#define _CRT_SECURE_NO_WARNINGS

2、播放音乐相关函数

(1)打开音频文件

mciSendString(TEXT("open hero.mp3 alias s1"),NULL,0,NULL);

代码中hero.mp3为音频名称,s1为导入程序后的变量名。

(2)播放音频文件

mciSendString(TEXT("play s1"),NULL,0,NULL);

代码中s1为打开音频文件时,导入程序后的变量名。

(3)关闭音频文件 

mciSendString(TEXT("close S1"),NULL,0,NULL); 

代码中s1为打开音频文件时,导入程序后的变量名。

(二)播放音频文件的设置

本文是以Dev-C编程环境为例,需要进行如下配置:

1、打开项目选项对话框

操作:菜单"项目"-->"项目属性"子菜单

 2、设置链接

操作:单击"参数"标签-->"链接"栏目中填入"-lwinmm",然后点"确定"。

三、将循环播放音频线程集成到主程序中

(一)自定义函数

下图为自定义的与播放音频相关的3个函数:

play_bgmusic()的作用是播放项目所在路径内名为"hero.mp3"的一首音频,播放完毕后关闭音频;

thread_bgmusic()是实现循环播放音频的线程函数,其返回值为指针类型;

bgmusic()是最终的创建循环播放音频进程的函数,在主函数中只需调用这个函数即可。

//********************************************************************************
 //以下三个函数为播放背景音乐功能 
//********************************************************************************
 
 //播放一遍背景音乐 
 void play_bgmusic() {  

	mciSendString(TEXT("open hero.mp3 alias s1"),NULL,0,NULL);
	mciSendString(TEXT("play s1"),NULL,0,NULL);
 
	Sleep(153*1000);//153*1000意思是153秒,是整首音乐的时长 
	mciSendString(TEXT("close S1"),NULL,0,NULL); 
  
}

//循环播放音乐线程函数 
void* thread_bgmusic(void* arg) //
{ 
  while(1)
  {  	
  	play_bgmusic();
  }
} 

//创建音乐播放线程,开始播放音乐 
void bgmusic()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_bgmusic, NULL);
}

(二)将循环播放音频进程相关函数声明和所引入的头文件集成control_plane.h中

#ifndef CONTROL_PLANE_H
#define CONTROL_PLANE
#include <iostream>
#include <string>
#include<stdlib.h>
#include<windows.h>
#include <pthread.h>//导入线程头文件库
#include <mmsystem.h> //导入声音头文件库
#pragma comment(lib,"winmm.lib")//导入声音的链接库
#define _CRT_SECURE_NO_WARNINGS 
using namespace std;

#define t_b 0  	//图形显示区域上侧边界 
#define l_b 0	//图形显示区域左侧边界
#define r_b 100	//图形显示区域右侧边界
#define b_b 20	//图形显示区域下侧边界


//定义飞机造型 
const string icon_plane[]={"    ■   ","■  ■  ■","■■■■■","■  ■  ■","    ■   ","  ■■■ "};

//定义图标坐标结构体 
typedef struct{
	int x;
	int y;
} location;

//定义移动方向命令枚举类型 
typedef  enum {up_cmd,down_cmd,left_cmd,right_cmd} direction_cmd;


extern location plocation;			//声明飞机坐标 
static direction_cmd dir_cmd; 		//声明存放按键码值的两个变量

//声明刷新飞机位置函数 
void show_plane(location plct);

//获取键盘指令 
direction_cmd key(void);

//计算出接收指令后的飞机坐标 
location plane_locate(location plct,direction_cmd dircmd);

void init(void);

 
void* thread_bgmusic(void* arg);
void play_bgmusic();
void bgmusic();
 
#endif

(三)将循环播放音频进程相关函数定义集成到control_plane.cpp文件中

#include <iostream>
#include "conio.h"
#include <string>
#include "control_plane.h"
using namespace std;

//初始化函数 
void init(void)
{
	plocation.x=r_b/2;				//初始化飞机图标的x坐标为屏幕横轴最大值的一半 
	plocation.y=b_b;					//初始化飞机图标的y坐标为屏幕纵轴最大值
}

//********************************************************************************
 //以下三个函数为播放背景音乐功能 
//********************************************************************************
 
 //播放一遍背景音乐 
 void play_bgmusic() {  

	mciSendString(TEXT("open hero.mp3 alias s1"),NULL,0,NULL);
	mciSendString(TEXT("play s1"),NULL,0,NULL);
 
	Sleep(153*1000);//153*1000意思是153秒,是整首音乐的时长 
	mciSendString(TEXT("close S1"),NULL,0,NULL); 
  
}

//循环播放音乐线程函数 
void* thread_bgmusic(void* arg) //
{ 
  while(1)
  {  	
  	play_bgmusic();
  }
} 

//创建音乐播放线程,开始循环播放音乐 
void bgmusic()
{
	pthread_t tid; 
  	pthread_create(&tid, NULL, thread_bgmusic, NULL);
}

//飞机图标刷新函数 
void show_plane(location plct)		//预先定义字符定位显示函数,x是列坐标,y是行坐标,原点(x=0,y=0)位于屏幕左上角 
{
	int x,y;
	int i,j;	
	int rows=sizeof(icon_plane)/sizeof(icon_plane[0]);
	x=plct.x;
	y=plct.y;	
	for(j=0;j<y;j++)				//图标上侧输出y个换行符
	{
		cout<<endl;
	}
	
	for(i=0;i<rows;i++)				//图标每行前输出x个空格 
	{
		for(j=0;j<x;j++)
		{
			cout<<" ";
		}
		cout<<icon_plane[i]<<endl;
	}
}


//获取键盘指令函数 
direction_cmd key(void)
{
	int key_value1,key_value2;			//声明两个变量,存放键值 
	key_value1=getch();					//先获取第一个码值 
	if(key_value1==224)					//如果第一个码值为224,则进行第二个码值的判断 
		{
			key_value2=getch();			//先获取第二个码值
			
			switch(key_value2)
			{
				case 72:				//向上方向键 
					return up_cmd;
				case 80:				//向下方向键
					return down_cmd;
				case 75:				//向左方向键
					return left_cmd;
				case 77:				//向右方向键
					return right_cmd;
			}
			
		}		
}


//计算获得移动指令后飞机的坐标 
location plane_locate(location plct,direction_cmd dircmd)
{
 	int x,y;
 	x=plct.x;
 	y=plct.y;
	 switch(dircmd)
		{
			case up_cmd:
				y--;				//字符上移一行,行值y减1
				if(y<t_b)			//限定y值最小值为0
				{
					y=t_b;
				}
				break;
			case down_cmd:
				y++;				//字符下移一行,行值y加1
				if(y>b_b)			//限定y高度 
				{
					y=b_b;
				}
				break;
			case left_cmd:
				x--;				//字符左移一列,列值x减1
				if(x<l_b)
				{
					x=l_b;			//限定x最小值为0; 
				}
				break;
			case right_cmd:
				x++;				//字符右移一列,列值x加1
				if(x>r_b)
				{
					x=r_b;			//限定x宽度
				}
				break;
				
		}
		plct.x=x;
		plct.y=y;
		return plct;
 }
 
 
 

(四)将循环播放音频进程集成到主函数中

在游戏中插入背景音乐,代码如下:

#include "control_plane.h"
using namespace std;

location plocation;

int main(int argc, char** argv) {	
	init();	//初始化					 
	
	bgmusic();//播放背景音乐 
	
	while(1)					//循环等待键盘指令 
	{
		system("cls");			//清屏
		show_plane(plocation);	//刷新飞机图标 		
		dir_cmd=key();			//获取按键指令 
		
		//计算收到键盘指令后的飞机坐标 
		plocation=plane_locate(plocation,dir_cmd);		
	}
	return 0; 	
}

参考博文:

dev c++播放音乐MP3_c++播放mp3_轻览月的博客-CSDN博客

(未完待续)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/858267.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【项目实战】Kafka 生产者写入分区的策略

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO TOP红人 Java知识图谱点击链接&…

苏州OV泛域名RSA加密算法https

RSA加密算法是一种非对称加密算法&#xff0c;它被广泛应用于信息安全领域。与对称加密算法不同&#xff0c;RSA加密算法使用了两个密钥&#xff0c;一个公钥和一个私钥。公钥可以公开&#xff0c;任何人都可以使用它加密信息&#xff0c;但只有私钥的持有者才能解密信息。RSA加…

NGINX负载均衡及LVS-DR负载均衡集群

目录 LVS-DR原理搭建过程nginx 负载均衡 LVS-DR原理 原理&#xff1a; 1. 当用户向负载均衡调度器&#xff08;Director Server&#xff09;发起请求&#xff0c;调度器将请求发往至内核空间 2. PREROUTING链首先会接收到用户请求&#xff0c;判断目标IP确定是本机IP&#xff…

8路AD采集FMC子卡【产品资料】

FMC148是一款基于VITA57.4标准的JESD204B接口FMC子卡模块&#xff0c;该模块可以实现8路14-bit、500MSPS/1GSPS/1.25GSPS ADC采集功能。该板卡ADC器件采用ADI公司的AD9680芯片,全功率-3dB模拟输入带宽可达2GHz。该ADC与FPGA的主机接口通过16通道的高速串行GTX收发器进行互联。 …

《Java-SE-第三十三章》之函数式编程

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水平很有限&…

消息中间件 Asio (C++)

折腾了一上午&#xff0c;看到这个结果的时候泪目了兄弟闷&#xff0c;讲真。我的asio客户端成功收到服务端发来的消息了。虽然这确实是极其智障又简单的入门哈哈 下载独立版本 asio网络通信库新建cmake工程&#xff0c;CMakeLists.txt加载asioasio最简单的服务端和客户端代码…

iview+treeSelect组件,我是如何一步步手动实现全选功能的

如果我掏出下图&#xff0c;阁下除了私信我加入学习群&#xff0c;还能如何应对&#xff1f; 正文开始 前言一、历史问题二、通过监听select事件实现全选不靠谱&#xff01;&#xff01;&#xff01;三、 通过外部事件控制树选择组件四、render函数创建组件4.1 不得不说的h函数…

STM32 低功耗-待机模式

STM32 待机模式 文章目录 STM32 待机模式第1章 低功耗模式简介第2章 待机模式简介2.1 进入待机模式2.1 退出待机模式 第3章 待机模式代码部分总结 第1章 低功耗模式简介 在 STM32 的正常工作中&#xff0c;具有四种工作模式&#xff1a;运行、睡眠、停止和待机模式。 在系统或…

nuxt.js框架使用

1、这种框架只要页面有一个地方错&#xff0c;都会出现404或者吓人的报错界面。 如表单的prop属性&#xff0c;在data函数return对象里面该字段找不到或者不一致&#xff0c;就会报404。 2、使用字典&#xff0c;对字典进行翻译。 在plugins/methods.js文件里面&#xff0c;加…

APP专项测试知识点

APP的专项测试 测试要点&#xff1a; 功能测试、兼容性测试、安装、卸载、升级测试、交叉事件测试、PUSH测试、性能测试-使用solopi监控-仅适用于安卓手机&#xff08;CPU、内存、流量测试、电量测试、流畅度测试、启动测试&#xff09;、用户体验测试、稳定性测试 &#xf…

Java 11 新特性解读(1)

目录 前言 新增了一系列字符串处理方法 Optional 加强 局部变量类型推断升级 前言 北京时间2018年9月26日&#xff0c;Oracle官方宣布Java 11正式发布。这是Java大版本周期变化后的第一个长期支持版本&#xff0c;非常值得关注。从官网即可下载,最新发布的Java11将带来ZGC、…

[C++] 自定义的类如何使用“cout“和“cin“?(含日期类实现)

一、引言 在C中&#xff0c;“cin”和"cout"可以说是区别于C语言的一大亮点。 但是&#xff0c;它的自动识别类型&#xff0c;其本质不过是运算符重载。若真到了能够“自动识别”的那一天&#xff0c;人类大概也能进入新的纪元了罢。 对于我们自己写的类&#xff…

uni-app之app上传pdf类型文件

通过阅读官方文档发现&#xff0c;uni.chooseFile在app端不支持非媒体文件上传&#xff1b; 可以使用这个插件&#xff0c;验证过可以上传pdf&#xff1b;具体使用可以去看文档 插件地址 就是还是会出现相机&#xff0c;这个可能需要自己解决下 实现功能&#xff1a;上传只能上…

vscode ssh远程的config/配置文件无法保存解决

问题 之前已经有了一个config&#xff0c;我想更改连接的地址和用户名&#xff0c;但是无法保存&#xff0c;显示需要管理员权限&#xff0c;但以管理员启动vscode或者以管理员权限保存都不行 未能保存“config”: Command failed: “D:\vscode\Microsoft VS Code\bin\code.c…

ssm+vue基于java的少儿编程网上报名系统源码和论文PPT

ssmvue基于java的少儿编程网上报名系统源码和论文PPT006 开发工具&#xff1a;idea 数据库mysql5.7(mysql5.7最佳) 数据库链接工具&#xff1a;navcat,小海豚等 开发技术&#xff1a;java ssm tomcat8.5 摘 要 在国家重视教育影响下&#xff0c;教育部门的密确配合下&#…

如何将Linux上的cpolar内网穿透设置成 - > 开机自启动

如何将Linux上的cpolar内网穿透设置成 - > 开机自启动 文章目录 如何将Linux上的cpolar内网穿透设置成 - > 开机自启动前言一、进入命令行模式二、输入token码三、输入内网穿透命令 前言 我们将cpolar安装到了Ubuntu系统上&#xff0c;并通过web-UI界面对cpolar的功能有…

[YAPI]导出API文档

1.登录点击进去,点击项目2.点击接口,点击编辑,划到最下面,开启开放接口3.点击数据管理, 选择你要的数据导出格式,点击公开接口, 导出完别忘记关闭,防止别人导的时候将你开启的 也一并下载下来

API 测试 | 了解 API 接口概念|电商平台 API 接口测试指南

什么是 API&#xff1f; API 是一个缩写&#xff0c;它代表了一个 pplication P AGC 软件覆盖整个房间。API 是用于构建软件应用程序的一组例程&#xff0c;协议和工具。API 指定一个软件程序应如何与其他软件程序进行交互。 例行程序&#xff1a;执行特定任务的程序。例程也称…

springboot教务综合管理系统java学生教师班级课题jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 springboot教务综合管理系统 系统有1权限&#xff1a…

全球外贸b2b2c跨境电商购物网站开源搭建

要搭建一个全球外贸B2B2C跨境电商购物网站&#xff0c;需要采取以下步骤&#xff08;以下步骤不分先后&#xff09;&#xff1a; 设计系统架构首先需要设计系统的整体架构&#xff0c;确定系统的技术选型、功能模块和业务流程等。可以考虑使用分布式架构&#xff0c;将系统划分…