[导读]本系列博文内容链接如下:
【C++】做一个飞机空战小游戏(一)------使用getch()函数获得键盘码值
【C++】做一个飞机空战小游戏(二)------利用getch()函数实现键盘控制单个字符移动
【C++】做一个飞机空战小游戏(三)------getch()函数控制任意造型飞机图标移动【C++】做一个飞机空战小游戏(四)------给游戏添加背景音乐(多线程技巧应用)
目录
(二)将循环播放音频进程相关函数声明和所引入的头文件集成control_plane.h中
(三)将循环播放音频进程相关函数定义集成到control_plane.cpp文件中
本系列文章前三节介绍了如何用getch()函数控制飞机的图标移动,并使用了模块化的设计,为后边的设计打下了一个比较好的基础。
今天介绍如何给游戏添加背景音乐。
一、线程
(一)什么是线程
关于线程的概念,以及进程和线程的关系,网上有好多资料,本文不再赘述.本文只介绍一下小编对两者的理解。
一个具有独立完整功能的程序或软件就是一个进程,这个程序里可以有多个线程并行运行,互不干扰,但是各个线程之间可以传递信息。线程,就是线性的过程,各个程序语句只能依次执行,执行完了前边的才能执行后边的,这个线程中的每条语句都会顺序执行一次。
如果把火车站售票看成是一个进程,那么售票厅里开设的多个售票窗口就是多个线程,在每个窗口购票的人需要排队依次购票,排在前边的购完票了,后边的才能购票。而两个窗口,也就是两个线程之间相互没有影响,这个队伍中的每名购票者速度的快慢,只会对这个窗口排队购票的进度有影响,而不会对另外窗口的购票产生影响。
(二)为什么本游戏要用到多线程
本游戏目前设计了三个模块:检测键盘指令并控制飞机移动模块,背景音乐循环播放模块,敌方炮弹循环掉落模块。
这三个模块都需要循环进行,在程序里都需要用到while(1)循环。如果三个模块放到同一个while(1)循环里,那么就会相互影响进程,游戏的流畅度大大降低。如果在同一个线程里放3个不同的while(1)循环,那么后边连个while(1)循环就根本没有执行的就会了。
所以把三个模块放到3个不同的线程里,才会保证程序的正常运行。
(三)创建线程的步骤
1、引入头文件
cpp
#include <pthread.h>
2、编写线程函数
cpp
void* thread_name(void* arg)
{
//此处为函数的具体内容
}
3、声明线程号
cpp
pthread_t tid;//pthread_t为线程类型,tid为线程id变量名
4、创建线程
cpp
pthread_create(&tid, NULL, thread_name, NULL);//创建线程
5、创建线程的完整代码
cpp
#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、引入头文件
cpp
#include <mmsystem.h> //导入声音头文件库
#pragma comment(lib,"winmm.lib")//导入声音的链接库
#define _CRT_SECURE_NO_WARNINGS
2、播放音乐相关函数
(1)打开音频文件
cpp
mciSendString(TEXT("open hero.mp3 alias s1"),NULL,0,NULL);
代码中hero.mp3为音频名称,s1为导入程序后的变量名。
(2)播放音频文件
cpp
mciSendString(TEXT("play s1"),NULL,0,NULL);
代码中s1为打开音频文件时,导入程序后的变量名。
(3)关闭音频文件
cpp
mciSendString(TEXT("close S1"),NULL,0,NULL);
代码中s1为打开音频文件时,导入程序后的变量名。
(二)播放音频文件的设置
本文是以Dev-C编程环境为例,需要进行如下配置:
1、打开项目选项对话框
操作:菜单"项目"-->"项目属性"子菜单
2、设置链接
操作:单击"参数"标签-->"链接"栏目中填入"-lwinmm",然后点"确定"。
三、将循环播放音频线程集成到主程序中
(一)自定义函数
下图为自定义的与播放音频相关的3个函数:
play_bgmusic()的作用是播放项目所在路径内名为"hero.mp3"的一首音频,播放完毕后关闭音频;
thread_bgmusic()是实现循环播放音频的线程函数,其返回值为指针类型;
bgmusic()是最终的创建循环播放音频进程的函数,在主函数中只需调用这个函数即可。
cpp
//********************************************************************************
//以下三个函数为播放背景音乐功能
//********************************************************************************
//播放一遍背景音乐
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中
cpp
#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文件中
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;
}
(四)将循环播放音频进程集成到主函数中
在游戏中插入背景音乐,代码如下:
cpp
#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博客
(未完待续)