init
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
#本节所有设置在https://clang.llvm.org/docs/ClangFormatStyleOptions.html查询具体定义
|
||||
#全局选项
|
||||
Language: Cpp
|
||||
Standard: c++20
|
||||
BasedOnStyle: Google
|
||||
BreakBeforeBraces: Attach #使用attach的括号换行风格
|
||||
|
||||
#缩进、空格相关
|
||||
AccessModifierOffset: -2 #访问限制词(如public)后面的缩进
|
||||
ContinuationIndentWidth: 2 #长表达式换行缩进
|
||||
IndentWidth: 4 #行缩进为4个空格
|
||||
TabWidth: 4 #tab替换为为4个空格
|
||||
IndentCaseLabels: true #case标签需要缩进
|
||||
IndentPPDirectives: AfterHash #预编译指令采用#前置的缩进
|
||||
NamespaceIndentation: All #namespace括号里的均需要缩进(内部有namespace则继续缩进)
|
||||
|
||||
SpaceBeforeCaseColon: false #case标识符的冒号前不加空格
|
||||
SpaceBeforeCpp11BracedList: false #新建类初始化的参数列表前不加空格
|
||||
SpaceBeforeCtorInitializerColon: false #类初始化的参数列表前不加空格
|
||||
SpaceBeforeParens: Never #小括号前不加空格
|
||||
SpaceBeforeRangeBasedForLoopColon: false #遍历列表形式的for循环小括号前不加空格
|
||||
SpacesInLineCommentPrefix:
|
||||
Maximum: 1
|
||||
Minimum: 1 # //型注释前强制限定为至少添加一个空格 //comment -> // comment
|
||||
|
||||
#对齐、换行相关
|
||||
AlignArrayOfStructures: Left #设置初始化数组的方阵为左对齐
|
||||
AlignTrailingComments: true #对齐所有尾注释
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly #只允许内联函数写在一行中
|
||||
AlwaysBreakTemplateDeclarations: 'No' # 不强制模板函数换行
|
||||
|
||||
#代码风格相关
|
||||
BinPackArguments: true #允许在不同行写任意数目的函数参数
|
||||
PackConstructorInitializers: BinPack #允许在不同行写任意数目的类默认参数
|
||||
#start using after the LLVM15.0 is open. InsertBraces: true #设置每个控制语句后必须用{}包裹,即使只有一行。
|
||||
EmptyLineAfterAccessModifier: Never #访问限制符后面不加空行
|
||||
EmptyLineBeforeAccessModifier: Always #访问限制符前强制加空行
|
||||
IncludeBlocks: Regroup #对include文件进行重新分组
|
||||
DerivePointerAlignment: false #关闭根据int* 和int *的数量自动推导
|
||||
PointerAlignment: Left #强制使用int*类型的指针声明
|
||||
ReferenceAlignment: Pointer #强制使用int&类型的引用声明
|
||||
ColumnLimit: 256 #每行长度限制
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# settings
|
||||
.vs
|
||||
.vscode
|
||||
build/
|
||||
out/
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(Aircraft-Battle)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_definitions(-DTEST_DATA_PATH="${CMAKE_SOURCE_DIR}/data")
|
||||
|
||||
file(GLOB_RECURSE SOURCES *.cpp *.h)
|
||||
|
||||
add_executable(main ${SOURCES})
|
||||
|
||||
set_target_properties(main PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
|
||||
target_include_directories(main PUBLIC include)
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
|
||||
#include "header.h"
|
||||
|
||||
// 计时器
|
||||
class Timer {
|
||||
public:
|
||||
Timer() {
|
||||
interval = 0;
|
||||
t_start = 0;
|
||||
open_timer = false;
|
||||
}
|
||||
Timer(DWORD _interval): interval(_interval) {
|
||||
t_start = 0;
|
||||
open_timer = false;
|
||||
}
|
||||
|
||||
// 设置间隔
|
||||
void set(DWORD _interval) { interval = _interval; }
|
||||
// 开启和关闭计时器
|
||||
void open() {
|
||||
open_timer = true;
|
||||
t_start = clock();
|
||||
}
|
||||
void close() { open_timer = false; }
|
||||
// 获得状态
|
||||
bool status() { return open_timer; }
|
||||
// 确认是否经过了间隔时间
|
||||
bool t_time() {
|
||||
if(open_timer && clock() - t_start > interval) {
|
||||
t_start = clock();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD t_start;
|
||||
DWORD interval;
|
||||
bool open_timer;
|
||||
};
|
||||
|
||||
// 音乐封装类
|
||||
class Music {
|
||||
public:
|
||||
// 获取音乐路径
|
||||
Music(const char _arr[]): command("") { strcat_s(command, _arr); }
|
||||
|
||||
void init(); // 初始化
|
||||
void open(); // 打开
|
||||
void play(); // 播放
|
||||
void pause(); // 暂停
|
||||
void resume(); // 继续
|
||||
void stop(); // 停止
|
||||
void close(); // 关闭
|
||||
|
||||
void length(char[]); // 音乐长度
|
||||
void status(char[]); // 音乐播放状态
|
||||
|
||||
private:
|
||||
char command[BUF_SIZE];
|
||||
};
|
||||
|
||||
// 矩形类,用于碰撞检测
|
||||
class Rect {
|
||||
public:
|
||||
Rect() {}
|
||||
Rect(int _left, int _top, int _width, int _height): left(_left), top(_top), width(_width), height(_height) {
|
||||
c_x = left + width / 2;
|
||||
c_y = top + height / 2;
|
||||
|
||||
right = left + width;
|
||||
bottom = top + height;
|
||||
|
||||
radius = (int)sqrt(width * width + height * height) / 2;
|
||||
}
|
||||
|
||||
// 返回碰撞的index,否则返回-1
|
||||
int collision(int, const Rect[]);
|
||||
bool collision(const Rect&);
|
||||
|
||||
// 返回点是否在矩形中
|
||||
bool point_in(int, int);
|
||||
|
||||
// 用于修改矩形大小
|
||||
void operator*=(double times) {
|
||||
radius *= times;
|
||||
width *= times, height *= times;
|
||||
|
||||
left = c_x - width / 2;
|
||||
right = c_x + width / 2;
|
||||
top = c_y - height / 2;
|
||||
bottom = c_y + height / 2;
|
||||
}
|
||||
|
||||
// 输出矩形
|
||||
friend ostream& operator<<(ostream& out, const Rect& rect) {
|
||||
out << rect.left << " " << rect.right << endl;
|
||||
out << rect.top << " " << rect.bottom << endl;
|
||||
return out;
|
||||
}
|
||||
|
||||
int left, right, top, bottom, width, height;
|
||||
int c_x, c_y;
|
||||
int radius;
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// header.h: 标准系统包含文件的包含文件,
|
||||
// 或特定于项目的包含文件
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
// Windows 头文件
|
||||
#include <windows.h>
|
||||
// C 运行时头文件
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include <stdlib.h>
|
||||
#include <tchar.h>
|
||||
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
#include <conio.h>
|
||||
#include <graphics.h>
|
||||
#include <mmsystem.h> //包含多媒体设备接口头文件
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
|
||||
#pragma comment(lib, "winmm.lib") // 加载静态库
|
||||
|
||||
using namespace std;
|
||||
|
||||
// 窗口属性
|
||||
enum WIN_DOW { WIDTH = 480, HEIGHT = 700, OPTION_WIDTH = 189, OPTION_HEIGHT = 43 };
|
||||
|
||||
// 我的飞机属性
|
||||
enum M_PLANE {
|
||||
m_width = 52,
|
||||
m_height = 60,
|
||||
|
||||
m_speed = 8,
|
||||
m_speed_down = 3,
|
||||
|
||||
m_hp = 3,
|
||||
m_boom = 3,
|
||||
|
||||
m_life_width = 30,
|
||||
m_life_height = 30,
|
||||
|
||||
m_boom_width = 63,
|
||||
m_boom_height = 56,
|
||||
};
|
||||
|
||||
// 敌机属性
|
||||
enum ENEMY {
|
||||
// 分类
|
||||
|
||||
// 小型机
|
||||
e_small,
|
||||
e_small_width = 30,
|
||||
e_small_height = 20,
|
||||
e_small_speed = 4,
|
||||
e_small_hp = 1,
|
||||
|
||||
// 中型机
|
||||
e_mid,
|
||||
e_mid_width = 49,
|
||||
e_mid_height = 58,
|
||||
e_mid_speed = 2,
|
||||
e_mid_hp = 10,
|
||||
|
||||
// 大型机
|
||||
e_big,
|
||||
e_big_width = 174,
|
||||
e_big_height = 254,
|
||||
e_big_speed = 1,
|
||||
e_big_hp = 25
|
||||
};
|
||||
|
||||
// 弹药属性
|
||||
enum BULLET {
|
||||
bullet_width = 4,
|
||||
bullet_height = 10,
|
||||
bullet_speed = 10,
|
||||
supply_width = 39,
|
||||
supply_height = 47,
|
||||
supply_speed = 3,
|
||||
// 弹药类型
|
||||
normal_bullet,
|
||||
super_bullet,
|
||||
recover_life,
|
||||
boom_boom
|
||||
};
|
||||
|
||||
// 敌机/子弹最大数量
|
||||
#define MAX_NUM 100
|
||||
// 缓冲区
|
||||
#define BUF_SIZE 255
|
||||
// 移动刷新间隔
|
||||
#define MOVE_SEP 20
|
||||
// 子弹发射间隔
|
||||
#define FIRE_SEP 150
|
||||
// 敌人创建间隔
|
||||
#define ENEMY_SEP 500
|
||||
// 碰撞检测间隔
|
||||
#define COLLISION_SEP 1
|
||||
// 补给间隔
|
||||
#define SUPPLY_SEP 25000
|
||||
// 超级子弹持续时间
|
||||
#define SUPER_TIME 10000
|
||||
// 无敌时间
|
||||
#define INVINCIBLE_TIME 3000
|
||||
// 按键保护时间(防止重复按键)
|
||||
#define PROTECT_TIME 2000
|
||||
|
||||
// 定义类型
|
||||
typedef int Type;
|
||||
|
||||
void play_music(const char[]); // 音乐播放
|
||||
void blood_line(int, int, int, double); // 画血条
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
#pragma once
|
||||
|
||||
#include "class.h"
|
||||
#include "header.h"
|
||||
|
||||
|
||||
// 父类,移动类
|
||||
class Plane {
|
||||
public:
|
||||
Plane(int _width, int _height, int _speed): width(_width), height(_height), speed(_speed), move_timer(MOVE_SEP) {
|
||||
radius = (int)sqrt(width * width + height * height) / 2;
|
||||
// 开启移动计时器
|
||||
move_timer.open();
|
||||
}
|
||||
|
||||
// 返回矩形区域
|
||||
Rect get_rect() {
|
||||
Rect rect(x, y, width, height);
|
||||
return rect;
|
||||
}
|
||||
|
||||
protected:
|
||||
int radius;
|
||||
int width, height;
|
||||
int speed;
|
||||
int x, y;
|
||||
IMAGE img[2]; // 存放图片
|
||||
Timer move_timer; // 移动计时器
|
||||
};
|
||||
|
||||
// 弹药类
|
||||
class Bullet : public Plane {
|
||||
public:
|
||||
Bullet(int _x, int _y, Type _type = normal_bullet): Plane(bullet_width, bullet_height, bullet_speed), type(_type) {
|
||||
x = _x, y = _y;
|
||||
|
||||
if(type == normal_bullet) {
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/bullet0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/bullet1.png");
|
||||
} else {
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/bullet2.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/bullet3.png");
|
||||
}
|
||||
}
|
||||
|
||||
static void init(); // 初始化类
|
||||
static void create(int, int, Type); // 创建子弹
|
||||
static void test_live(); // 检测是否应该存活
|
||||
static void distory(int); // 删除指定子弹
|
||||
static void move(); // 移动全体子弹
|
||||
static void show(); // 显示全体子弹
|
||||
static void delete_bullet(); // 清空子弹
|
||||
static Rect* get_rects(); // 获得所有子弹的矩形的数组
|
||||
static int get_count(); // 获得子弹数
|
||||
static void distory(); // 销毁全部
|
||||
|
||||
private:
|
||||
Type type; // 子弹类型
|
||||
|
||||
// 记录子弹数和子弹
|
||||
static int count;
|
||||
static Bullet** bullet;
|
||||
};
|
||||
|
||||
// 单例模式
|
||||
class MyPlane : public Plane {
|
||||
public:
|
||||
void init_plane();
|
||||
void init_pos(); // 初始化飞机位置
|
||||
void move(); // 移动
|
||||
void fire(); // 开火
|
||||
void show(); // 显示
|
||||
bool test_live(); // 检查是否存活
|
||||
|
||||
void change_bullet(); // 改变弹药
|
||||
void get_life(); // 获得生命
|
||||
void get_boom(); // 获得炸弹
|
||||
|
||||
void reduce_life();
|
||||
void use_boom();
|
||||
|
||||
void get_score(int);
|
||||
|
||||
// 获取/创建唯一本地变量
|
||||
static MyPlane* share_plane() {
|
||||
if(my_plane == nullptr) {
|
||||
my_plane = new MyPlane;
|
||||
}
|
||||
return my_plane;
|
||||
}
|
||||
|
||||
// 删除飞机
|
||||
static void delete_plane() {
|
||||
if(my_plane != nullptr) {
|
||||
// 删除指针,然后指向空指针
|
||||
delete my_plane;
|
||||
my_plane = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
IMAGE life_img[2]; // 生命图像
|
||||
IMAGE boom_img[2]; // 炸弹图像
|
||||
|
||||
Timer fire_timer; // 开火计时器
|
||||
Timer super_timer; // 超级子弹时间
|
||||
Timer invincible_timer; // 无敌时间
|
||||
Timer protect_timer; // 按键保护
|
||||
|
||||
int hp;
|
||||
int my_score; // 积分
|
||||
int boom_num; // 炸弹数量
|
||||
bool my_invincible; // 是否无敌
|
||||
Type bullet_type; // 子弹类型
|
||||
|
||||
static MyPlane* my_plane; // 我的飞机
|
||||
|
||||
// 私有化构造/析构函数
|
||||
MyPlane(): Plane(m_width, m_height, m_speed) {
|
||||
init_pos();
|
||||
init_plane();
|
||||
|
||||
// 初始化计时器
|
||||
fire_timer.set(FIRE_SEP);
|
||||
super_timer.set(SUPER_TIME);
|
||||
protect_timer.set(PROTECT_TIME);
|
||||
invincible_timer.set(INVINCIBLE_TIME);
|
||||
|
||||
// 开启计时器
|
||||
fire_timer.open();
|
||||
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/me0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/me1.png");
|
||||
|
||||
loadimage(&life_img[0], TEST_DATA_PATH "/images/life0.png");
|
||||
loadimage(&life_img[1], TEST_DATA_PATH "/images/life1.png");
|
||||
|
||||
loadimage(&boom_img[0], TEST_DATA_PATH "/images/bomb0.png");
|
||||
loadimage(&boom_img[1], TEST_DATA_PATH "/images/bomb1.png");
|
||||
}
|
||||
~MyPlane() {}
|
||||
|
||||
// 私有化赋值
|
||||
void operator=(const MyPlane* plane) {}
|
||||
};
|
||||
|
||||
// 敌人类
|
||||
class Enemy : public Plane {
|
||||
public:
|
||||
Enemy(Type _type): Plane(0, 0, 0), type(_type) {
|
||||
// 根据不同类型获取不同属性
|
||||
switch(type) {
|
||||
case e_small:
|
||||
width = e_small_width;
|
||||
height = e_small_height;
|
||||
speed = e_small_speed;
|
||||
hp = e_small_hp;
|
||||
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/smallenemy0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/smallenemy1.png");
|
||||
break;
|
||||
case e_mid:
|
||||
width = e_mid_width;
|
||||
height = e_mid_height;
|
||||
speed = e_mid_speed;
|
||||
hp = e_mid_hp;
|
||||
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/midenemy0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/midenemy1.png");
|
||||
break;
|
||||
case e_big:
|
||||
width = e_big_width;
|
||||
height = e_big_height;
|
||||
speed = e_big_speed;
|
||||
hp = e_big_hp;
|
||||
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/bigenemy0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/bigenemy1.png");
|
||||
break;
|
||||
}
|
||||
|
||||
// 随机生成
|
||||
x = rand() % (WIDTH - width);
|
||||
y = -1 * height;
|
||||
}
|
||||
|
||||
static void init(); // 初始化
|
||||
static void create(); // 创建敌人
|
||||
static void distory(int); // 销毁敌人
|
||||
static void test_live(); // 检测是否存活
|
||||
static void move(); // 移动
|
||||
static void show(); // 显示
|
||||
static void delete_enemy(); // 清空所有敌人
|
||||
static void collision(); // 碰撞检测
|
||||
static void distory(); // 销毁全部
|
||||
|
||||
void attacked(); // 被攻击
|
||||
int get_score(); // 获取该敌人的分数
|
||||
|
||||
private:
|
||||
int hp;
|
||||
Type type;
|
||||
|
||||
static Timer create_timer; // 创建计时器
|
||||
static Timer collision_timer; // 碰撞计时器
|
||||
static int count; // 计数
|
||||
static Enemy** enemy; // 存放敌人
|
||||
};
|
||||
|
||||
// 补给(单例模式)
|
||||
class Supply : public Plane {
|
||||
public:
|
||||
void move(); // 移动
|
||||
void show(); // 显示
|
||||
void test_live(); // 销毁
|
||||
|
||||
static void create(); // 创建supply
|
||||
static void action(); // supply动作
|
||||
|
||||
// 删除补给
|
||||
static void delete_supply() {
|
||||
if(my_supply != nullptr) {
|
||||
// 删除指针,然后指向空指针
|
||||
delete my_supply;
|
||||
my_supply = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Type type;
|
||||
|
||||
static Timer supply_timer; // 补给计时器
|
||||
static Supply* my_supply; // 我的补给
|
||||
|
||||
// 私有化构造/析构函数
|
||||
Supply(): Plane(supply_width, supply_height, supply_speed) {
|
||||
int id = rand() % 3;
|
||||
switch(id) {
|
||||
case 0:
|
||||
type = super_bullet;
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/bullet_supply0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/bullet_supply1.png");
|
||||
break;
|
||||
case 1:
|
||||
type = recover_life;
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/life_supply0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/life_supply1.png");
|
||||
break;
|
||||
case 2:
|
||||
type = boom_boom;
|
||||
loadimage(&img[0], TEST_DATA_PATH "/images/bomb_supply0.png");
|
||||
loadimage(&img[1], TEST_DATA_PATH "/images/bomb_supply1.png");
|
||||
break;
|
||||
}
|
||||
|
||||
supply_timer.set(SUPPLY_SEP);
|
||||
|
||||
// 随机生成
|
||||
x = rand() % (WIDTH - width);
|
||||
y = -1 * height;
|
||||
}
|
||||
~Supply() {}
|
||||
|
||||
// 私有化赋值
|
||||
void operator=(const Supply* supply) {}
|
||||
};
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
|
||||
#include "class.h"
|
||||
|
||||
// 回到开头
|
||||
void Music::init() {
|
||||
char command_name[BUF_SIZE] = "seek ";
|
||||
strcat_s(command_name, command);
|
||||
strcat_s(command_name, " to start");
|
||||
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 打开音乐(支持多种音乐格式)
|
||||
void Music::open() {
|
||||
char command_name[BUF_SIZE] = "open ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 播放
|
||||
void Music::play() {
|
||||
char command_name[BUF_SIZE] = "play ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 暂停
|
||||
void Music::pause() {
|
||||
char command_name[BUF_SIZE] = "pause ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 继续
|
||||
void Music::resume() {
|
||||
char command_name[BUF_SIZE] = "resume ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 结束
|
||||
void Music::stop() {
|
||||
char command_name[BUF_SIZE] = "stop ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 关闭
|
||||
void Music::close() {
|
||||
char command_name[BUF_SIZE] = "close ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 获取设备状态
|
||||
void Music::status(char buf[]) {
|
||||
char command_name[BUF_SIZE] = "status ";
|
||||
strcat_s(command_name, command);
|
||||
strcat_s(command_name, " mode");
|
||||
|
||||
// 记录状态
|
||||
mciSendString(command_name, buf, BUF_SIZE, 0);
|
||||
}
|
||||
|
||||
// 获取文件长度
|
||||
void Music::length(char buf[]) {
|
||||
char command_name[BUF_SIZE] = "status ";
|
||||
strcat_s(command_name, command);
|
||||
strcat_s(command_name, " length");
|
||||
|
||||
// 记录状态
|
||||
mciSendString(command_name, buf, BUF_SIZE, 0);
|
||||
}
|
||||
|
||||
// 矩形方法
|
||||
bool Rect::collision(const Rect& rect) {
|
||||
if((right >= rect.left && right <= rect.right) || (left >= rect.left && left <= rect.right)) {
|
||||
if((top >= rect.top && top <= rect.bottom) || (bottom >= rect.top && bottom <= rect.bottom)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if((rect.right >= left && rect.right <= right) || (rect.left >= left && rect.left <= right)) {
|
||||
if((rect.top >= top && rect.top <= bottom) || (rect.bottom >= top && rect.bottom <= bottom)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Rect::collision(int len, const Rect rect[]) {
|
||||
for(int i = 0; i < len; i++) {
|
||||
if(collision(rect[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Rect::point_in(int point_x, int point_y) {
|
||||
if(point_x >= left && point_x <= right) {
|
||||
if(point_y >= top && point_y <= bottom) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
// Aircraft battle.cpp : 定义应用程序的入口点。
|
||||
//
|
||||
|
||||
#include "class.h"
|
||||
#include "framework.h"
|
||||
#include "header.h"
|
||||
#include "plane.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
// 播放音乐的方法
|
||||
void play_music(const char _arr[]) {
|
||||
Music music(_arr);
|
||||
music.open();
|
||||
|
||||
char buf[BUF_SIZE];
|
||||
music.status(buf);
|
||||
|
||||
if(buf[0] == 's') {
|
||||
music.init();
|
||||
music.play();
|
||||
}
|
||||
}
|
||||
|
||||
// 画血条
|
||||
void blood_line(int x, int y, int len, double ratio) {
|
||||
setlinestyle(PS_SOLID, 3);
|
||||
setlinecolor(BLACK);
|
||||
line(x, y, x + len, y);
|
||||
if(ratio > 0.4) {
|
||||
setlinecolor(GREEN);
|
||||
} else {
|
||||
setlinecolor(RED);
|
||||
}
|
||||
line(x, y, (int)(x + len * ratio), y);
|
||||
}
|
||||
|
||||
// 主程序入口
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
|
||||
srand((int)time(0));
|
||||
initgraph(WIDTH, HEIGHT);
|
||||
|
||||
// 加载背景图片
|
||||
IMAGE bk_img;
|
||||
loadimage(&bk_img, TEST_DATA_PATH "/images/background.png");
|
||||
|
||||
// 游戏选项图片
|
||||
IMAGE option_img[7];
|
||||
loadimage(&option_img[0], TEST_DATA_PATH "/images/continue_nor.png");
|
||||
loadimage(&option_img[1], TEST_DATA_PATH "/images/again_nor.png");
|
||||
loadimage(&option_img[2], TEST_DATA_PATH "/images/gameover_nor.png");
|
||||
|
||||
loadimage(&option_img[3], TEST_DATA_PATH "/images/continue_nor1.png");
|
||||
loadimage(&option_img[4], TEST_DATA_PATH "/images/again_nor1.png");
|
||||
loadimage(&option_img[5], TEST_DATA_PATH "/images/gameover_nor1.png");
|
||||
|
||||
loadimage(&option_img[6], TEST_DATA_PATH "/images/nor.png"); // 掩码
|
||||
|
||||
// 输出索引的数组
|
||||
int put_index[3] = {0, 1, 2};
|
||||
int chosen_id = -1; // 选中的图片
|
||||
|
||||
// 中间选项的位置
|
||||
int option_x = (WIDTH - OPTION_WIDTH) / 2;
|
||||
int option_y = (HEIGHT - OPTION_HEIGHT) / 2;
|
||||
|
||||
// 选项的矩形
|
||||
Rect continue_rect(option_x, option_y - OPTION_HEIGHT - 25, OPTION_WIDTH, OPTION_HEIGHT);
|
||||
Rect again_rect(option_x, option_y, OPTION_WIDTH, OPTION_HEIGHT);
|
||||
Rect gameover_rect(option_x, option_y + OPTION_HEIGHT + 25, OPTION_WIDTH, OPTION_HEIGHT);
|
||||
|
||||
// 按键保护计时器
|
||||
Timer protect_timer(PROTECT_TIME);
|
||||
|
||||
MyPlane* my_plane = MyPlane::share_plane(); // 我的飞机
|
||||
|
||||
// 初始化类
|
||||
Bullet::init();
|
||||
Enemy::init();
|
||||
|
||||
// 游戏进程
|
||||
bool game_process = true;
|
||||
bool game = true;
|
||||
bool my_alive = true;
|
||||
|
||||
BeginBatchDraw();
|
||||
while(game) {
|
||||
// 加载背景音乐
|
||||
play_music(TEST_DATA_PATH "/music/KOTOKO.mp3");
|
||||
|
||||
putimage(0, 0, &bk_img);
|
||||
// 死亡则停止
|
||||
if(!my_alive) {
|
||||
game_process = false;
|
||||
}
|
||||
|
||||
// 保护器到时间就关闭
|
||||
if(protect_timer.t_time()) {
|
||||
protect_timer.close();
|
||||
}
|
||||
|
||||
if(!protect_timer.status() && GetAsyncKeyState('P')) {
|
||||
protect_timer.open();
|
||||
game_process = !game_process;
|
||||
}
|
||||
|
||||
if(game_process) {
|
||||
// 敌机操作
|
||||
Enemy::test_live();
|
||||
Enemy::create();
|
||||
Enemy::move();
|
||||
Enemy::collision();
|
||||
Enemy::show();
|
||||
|
||||
// 补给
|
||||
Supply::create();
|
||||
Supply::action();
|
||||
|
||||
// 我的飞机操作
|
||||
my_plane->move();
|
||||
my_plane->show();
|
||||
my_plane->fire();
|
||||
my_plane->use_boom();
|
||||
my_alive = my_plane->test_live();
|
||||
|
||||
// 子弹操作
|
||||
Bullet::move();
|
||||
Bullet::test_live();
|
||||
Bullet::show();
|
||||
} else {
|
||||
Enemy::show();
|
||||
if(my_alive) {
|
||||
my_plane->show();
|
||||
Bullet::show();
|
||||
}
|
||||
|
||||
int x = 0, y = 0;
|
||||
int record_id = chosen_id;
|
||||
|
||||
ExMessage msg;
|
||||
peekmessage(&msg, EM_MOUSE);
|
||||
switch(msg.message) {
|
||||
case WM_MOUSEMOVE:
|
||||
x = msg.x, y = msg.y;
|
||||
chosen_id = -1;
|
||||
put_index[0] = 0;
|
||||
put_index[1] = 1;
|
||||
put_index[2] = 2;
|
||||
|
||||
if(continue_rect.point_in(x, y)) {
|
||||
put_index[0] = 3;
|
||||
chosen_id = 0;
|
||||
} else if(again_rect.point_in(x, y)) {
|
||||
put_index[1] = 4;
|
||||
chosen_id = 1;
|
||||
} else if(gameover_rect.point_in(x, y)) {
|
||||
put_index[2] = 5;
|
||||
chosen_id = 2;
|
||||
}
|
||||
// 当前后id不同且不为-1才播放
|
||||
if(chosen_id != record_id && chosen_id != -1) {
|
||||
// 死亡不播放第一个音乐
|
||||
if(my_alive || chosen_id != 0) {
|
||||
play_music(TEST_DATA_PATH "/music/button.wav");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_LBUTTONDOWN: {
|
||||
switch(chosen_id) {
|
||||
case 0:
|
||||
game_process = true;
|
||||
protect_timer.set(0);
|
||||
break;
|
||||
case 1:
|
||||
my_alive = true;
|
||||
game_process = true;
|
||||
protect_timer.set(0);
|
||||
// 清除所有子弹和敌人
|
||||
Bullet::distory();
|
||||
Enemy::distory();
|
||||
Supply::delete_supply();
|
||||
|
||||
MyPlane::share_plane()->init_plane(); // 我的飞机初始化
|
||||
break;
|
||||
case 2:
|
||||
// 清除类
|
||||
MyPlane::delete_plane();
|
||||
Bullet::delete_bullet();
|
||||
Enemy::delete_enemy();
|
||||
Supply::delete_supply();
|
||||
|
||||
// 退出窗口
|
||||
HWND hWnd = GetHWnd();
|
||||
SendMessage(hWnd, WM_CLOSE, NULL, NULL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 显示游戏选项
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(i == 0 && !my_alive) {
|
||||
continue;
|
||||
}
|
||||
putimage(option_x, option_y + (OPTION_HEIGHT + 25) * (i - 1), &option_img[6], NOTSRCERASE);
|
||||
putimage(option_x, option_y + (OPTION_HEIGHT + 25) * (i - 1), &option_img[put_index[i]], SRCINVERT);
|
||||
}
|
||||
|
||||
if(!my_alive) {
|
||||
// 设置字体
|
||||
LOGFONT f;
|
||||
gettextstyle(&f); // 获取文字格式
|
||||
f.lfQuality = ANTIALIASED_QUALITY; // 抗锯齿
|
||||
f.lfHeight = 50; // 字高
|
||||
f.lfWeight = 10; // 字重
|
||||
_tcscpy_s(f.lfFaceName, "Consolas"); // 字体
|
||||
settextstyle(&f);
|
||||
|
||||
char game_over_text[15] = "游戏结束";
|
||||
int text_w = textwidth(game_over_text);
|
||||
int text_h = textheight(game_over_text);
|
||||
outtextxy((WIDTH - text_w) / 2, HEIGHT / 4, game_over_text);
|
||||
}
|
||||
}
|
||||
FlushBatchDraw();
|
||||
}
|
||||
|
||||
EndBatchDraw();
|
||||
closegraph();
|
||||
|
||||
// 清除类
|
||||
MyPlane::delete_plane();
|
||||
Bullet::delete_bullet();
|
||||
Enemy::delete_enemy();
|
||||
Supply::delete_supply();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,480 @@
|
|||
#include "plane.h"
|
||||
|
||||
// 子弹类
|
||||
int Bullet::count = 0;
|
||||
Bullet** Bullet::bullet = nullptr;
|
||||
|
||||
void Bullet::init() {
|
||||
if(bullet == nullptr) {
|
||||
bullet = new Bullet*[MAX_NUM];
|
||||
for(int i = 0; i < MAX_NUM; i++) {
|
||||
bullet[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bullet::create(int x, int y, Type _type) {
|
||||
if(count < MAX_NUM) {
|
||||
bullet[count] = new Bullet(x, y, _type);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
void Bullet::show() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
putimage(bullet[i]->x, bullet[i]->y, &(bullet[i]->img[0]), NOTSRCERASE);
|
||||
putimage(bullet[i]->x, bullet[i]->y, &(bullet[i]->img[1]), SRCINVERT);
|
||||
}
|
||||
}
|
||||
|
||||
void Bullet::move() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
if(bullet[i]->move_timer.t_time()) {
|
||||
bullet[i]->y -= bullet[i]->speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bullet::test_live() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
if(bullet[i]->y < -1 * bullet[i]->height) {
|
||||
distory(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect* Bullet::get_rects() {
|
||||
Rect* rects = new Rect[count];
|
||||
for(int i = 0; i < count; i++) {
|
||||
rects[i] = bullet[i]->get_rect();
|
||||
}
|
||||
return rects;
|
||||
}
|
||||
|
||||
int Bullet::get_count() {
|
||||
return count;
|
||||
}
|
||||
|
||||
void Bullet::distory(int i) {
|
||||
delete bullet[i];
|
||||
for(int j = i; j < count - 1; j++) {
|
||||
bullet[j] = bullet[j + 1];
|
||||
}
|
||||
bullet[count - 1] = nullptr;
|
||||
count--;
|
||||
}
|
||||
|
||||
void Bullet::distory() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
delete bullet[i];
|
||||
bullet[i] = nullptr;
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
|
||||
void Bullet::delete_bullet() {
|
||||
distory();
|
||||
delete bullet;
|
||||
bullet = nullptr;
|
||||
}
|
||||
|
||||
/*我的飞机函数*/
|
||||
MyPlane* MyPlane::my_plane = nullptr;
|
||||
|
||||
void MyPlane::init_plane() {
|
||||
init_pos();
|
||||
|
||||
hp = m_hp;
|
||||
my_score = 0;
|
||||
boom_num = m_boom;
|
||||
bullet_type = normal_bullet;
|
||||
my_invincible = false;
|
||||
}
|
||||
|
||||
void MyPlane::init_pos() {
|
||||
x = (WIDTH - width) / 2;
|
||||
y = HEIGHT - height * 2;
|
||||
}
|
||||
|
||||
void MyPlane::show() {
|
||||
putimage(x, y, &img[0], NOTSRCERASE);
|
||||
putimage(x, y, &img[1], SRCINVERT);
|
||||
|
||||
if(my_invincible) {
|
||||
setlinecolor(GREEN);
|
||||
setlinestyle(PS_DOT, 2);
|
||||
circle(x + width / 2, y + height / 2, radius);
|
||||
}
|
||||
|
||||
// 显示生命
|
||||
int life_x = WIDTH - m_life_width - 10, life_y = HEIGHT - m_life_height - 10;
|
||||
for(int i = 0; i < hp; i++) {
|
||||
putimage(life_x, life_y, &life_img[0], NOTSRCERASE);
|
||||
putimage(life_x, life_y, &life_img[1], SRCINVERT);
|
||||
|
||||
life_x -= m_life_width + 5;
|
||||
}
|
||||
|
||||
// 显示炸弹数
|
||||
int boom_x = 5, boom_y = HEIGHT - m_boom_height - 5;
|
||||
putimage(boom_x, boom_y, &boom_img[0], NOTSRCERASE);
|
||||
putimage(boom_x, boom_y, &boom_img[1], SRCINVERT);
|
||||
|
||||
// 设置字体样式
|
||||
settextcolor(WHITE);
|
||||
setbkmode(TRANSPARENT); // 去掉文字背景
|
||||
|
||||
LOGFONT f;
|
||||
gettextstyle(&f); // 获取文字格式
|
||||
f.lfQuality = ANTIALIASED_QUALITY; // 抗锯齿
|
||||
f.lfHeight = 30; // 字高
|
||||
_tcscpy_s(f.lfFaceName, "Consolas"); // 字体
|
||||
settextstyle(&f);
|
||||
|
||||
// 显示文字
|
||||
char str[15];
|
||||
sprintf_s(str, " X %d", boom_num);
|
||||
int str_h = textheight(str);
|
||||
outtextxy(boom_x + m_boom_width + 5, boom_y + (m_boom_height - str_h) / 2, str);
|
||||
|
||||
// 显示分数
|
||||
int score_x = 5, score_y = 5;
|
||||
sprintf_s(str, "Score: %d", my_score);
|
||||
outtextxy(score_x, score_y, str);
|
||||
}
|
||||
|
||||
bool MyPlane::test_live() {
|
||||
if(hp < 0) {
|
||||
play_music(TEST_DATA_PATH "/music/me_down.wav");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MyPlane::fire() {
|
||||
if(fire_timer.t_time()) {
|
||||
if(bullet_type == super_bullet) {
|
||||
// 超级子弹会有两发
|
||||
Bullet::create(x + (width - bullet_width) / 6, y - bullet_height, bullet_type);
|
||||
Bullet::create(x + 5 * (width - bullet_width) / 6, y - bullet_height, bullet_type);
|
||||
} else {
|
||||
Bullet::create(x + (width - bullet_width) / 2, y - bullet_height, bullet_type);
|
||||
}
|
||||
}
|
||||
// 超级子弹持续完毕
|
||||
if(super_timer.t_time()) {
|
||||
super_timer.close();
|
||||
change_bullet();
|
||||
}
|
||||
}
|
||||
|
||||
void MyPlane::move() {
|
||||
if(move_timer.t_time()) {
|
||||
// 直接获取键是否按下,可以得到多个按键操作
|
||||
if(GetAsyncKeyState(VK_UP) || GetAsyncKeyState('W')) {
|
||||
y -= speed;
|
||||
if(y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
if(GetAsyncKeyState(VK_DOWN) || GetAsyncKeyState('S')) {
|
||||
y += speed;
|
||||
if(y + height > HEIGHT) {
|
||||
y = HEIGHT - height;
|
||||
}
|
||||
}
|
||||
if(GetAsyncKeyState(VK_LEFT) || GetAsyncKeyState('A')) {
|
||||
x -= speed;
|
||||
if(x < -1 * width / 2) {
|
||||
x = -1 * width / 2;
|
||||
}
|
||||
}
|
||||
if(GetAsyncKeyState(VK_RIGHT) || GetAsyncKeyState('D')) {
|
||||
x += speed;
|
||||
if(x + width / 2 > WIDTH) {
|
||||
x = WIDTH - width / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 无敌时间结束,取消计时器
|
||||
if(invincible_timer.t_time()) {
|
||||
// 速度回复
|
||||
speed = m_speed;
|
||||
my_invincible = false;
|
||||
invincible_timer.close();
|
||||
}
|
||||
}
|
||||
|
||||
void MyPlane::change_bullet() {
|
||||
if(bullet_type == normal_bullet) {
|
||||
super_timer.open();
|
||||
bullet_type = super_bullet;
|
||||
} else {
|
||||
bullet_type = normal_bullet;
|
||||
}
|
||||
}
|
||||
|
||||
void MyPlane::get_life() {
|
||||
hp++;
|
||||
hp %= 6;
|
||||
}
|
||||
|
||||
void MyPlane::get_boom() {
|
||||
boom_num++;
|
||||
boom_num %= 6;
|
||||
}
|
||||
|
||||
void MyPlane::reduce_life() {
|
||||
if(!my_invincible) {
|
||||
hp--;
|
||||
// 回到初始位置
|
||||
init_pos();
|
||||
// 掉血无敌,减速
|
||||
speed = m_speed_down;
|
||||
my_invincible = true;
|
||||
// 开启计时器
|
||||
invincible_timer.open();
|
||||
}
|
||||
}
|
||||
|
||||
void MyPlane::use_boom() {
|
||||
// 保护器到时间就关闭
|
||||
if(protect_timer.t_time()) {
|
||||
protect_timer.close();
|
||||
}
|
||||
|
||||
// 如果保护器关闭则进入
|
||||
if(boom_num > 0 && GetAsyncKeyState(VK_SPACE) && !protect_timer.status()) {
|
||||
// 开启
|
||||
protect_timer.open();
|
||||
|
||||
play_music(TEST_DATA_PATH "/music/bomb.wav");
|
||||
Enemy::init();
|
||||
boom_num--;
|
||||
}
|
||||
}
|
||||
|
||||
void MyPlane::get_score(int score) {
|
||||
my_score += score;
|
||||
}
|
||||
|
||||
// 敌机
|
||||
int Enemy::count = 0;
|
||||
Enemy** Enemy::enemy = nullptr;
|
||||
Timer Enemy::create_timer(ENEMY_SEP);
|
||||
Timer Enemy::collision_timer(COLLISION_SEP);
|
||||
|
||||
void Enemy::init() {
|
||||
if(enemy == nullptr) {
|
||||
enemy = new Enemy*[MAX_NUM];
|
||||
for(int i = 0; i < MAX_NUM; i++) {
|
||||
enemy[i] = nullptr;
|
||||
}
|
||||
// 开启计时器
|
||||
create_timer.open();
|
||||
collision_timer.open();
|
||||
} else {
|
||||
for(int i = 0; i < count; i++) {
|
||||
// 得分
|
||||
MyPlane::share_plane()->get_score(enemy[i]->get_score());
|
||||
delete enemy[i];
|
||||
enemy[i] = nullptr;
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Enemy::show() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
putimage(enemy[i]->x, enemy[i]->y, &(enemy[i]->img[0]), NOTSRCERASE);
|
||||
putimage(enemy[i]->x, enemy[i]->y, &(enemy[i]->img[1]), SRCINVERT);
|
||||
|
||||
if(enemy[i]->type == e_mid) {
|
||||
blood_line(enemy[i]->x, enemy[i]->y - 5, enemy[i]->width, 1.0 * enemy[i]->hp / e_mid_hp);
|
||||
}
|
||||
if(enemy[i]->type == e_big) {
|
||||
blood_line(enemy[i]->x, enemy[i]->y - 5, enemy[i]->width, 1.0 * enemy[i]->hp / e_big_hp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Enemy::move() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
if(enemy[i]->move_timer.t_time()) {
|
||||
enemy[i]->y += enemy[i]->speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Enemy::attacked() {
|
||||
hp--;
|
||||
}
|
||||
|
||||
void Enemy::create() {
|
||||
if(count < MAX_NUM && create_timer.t_time()) {
|
||||
double id = rand() / double(RAND_MAX);
|
||||
if(id >= 0.95) {
|
||||
enemy[count] = new Enemy(e_big);
|
||||
} else if(id >= 0.85) {
|
||||
enemy[count] = new Enemy(e_mid);
|
||||
} else {
|
||||
enemy[count] = new Enemy(e_small);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
void Enemy::test_live() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
if(enemy[i]->hp <= 0) {
|
||||
// 得分
|
||||
MyPlane::share_plane()->get_score(enemy[i]->get_score());
|
||||
distory(i);
|
||||
} else if(enemy[i]->y > HEIGHT) {
|
||||
distory(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Enemy::collision() {
|
||||
if(collision_timer.t_time()) {
|
||||
// 得到所有需要检测碰撞的矩形
|
||||
int bullet_num = Bullet::get_count();
|
||||
Rect* bullet_rects = Bullet::get_rects();
|
||||
Rect plane_rect = MyPlane::share_plane()->get_rect();
|
||||
|
||||
for(int i = 0; i < count; i++) {
|
||||
Rect rect = enemy[i]->get_rect();
|
||||
int id = rect.collision(bullet_num, bullet_rects);
|
||||
if(id != -1) {
|
||||
// 删除碰撞的子弹
|
||||
enemy[i]->attacked();
|
||||
Bullet::distory(id);
|
||||
} else if(rect.collision(plane_rect)) {
|
||||
// 删除碰撞的敌人,自己掉血,进入无敌
|
||||
MyPlane::share_plane()->reduce_life();
|
||||
// 得分
|
||||
MyPlane::share_plane()->get_score(enemy[i]->get_score());
|
||||
Enemy::distory(i);
|
||||
}
|
||||
}
|
||||
delete[] bullet_rects;
|
||||
bullet_rects = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int Enemy::get_score() {
|
||||
switch(type) {
|
||||
case e_small:
|
||||
return 100;
|
||||
case e_mid:
|
||||
return 250;
|
||||
case e_big:
|
||||
return 400;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Enemy::distory(int i) {
|
||||
switch(enemy[i]->type) {
|
||||
case e_small:
|
||||
play_music(TEST_DATA_PATH "/music/smallenemy_down.wav");
|
||||
break;
|
||||
case e_mid:
|
||||
play_music(TEST_DATA_PATH "/music/midenemy_down.wav");
|
||||
break;
|
||||
case e_big:
|
||||
play_music(TEST_DATA_PATH "/music/bigenemy_down.wav");
|
||||
break;
|
||||
}
|
||||
|
||||
delete enemy[i];
|
||||
for(int j = i; j < count - 1; j++) {
|
||||
enemy[j] = enemy[j + 1];
|
||||
}
|
||||
enemy[count - 1] = nullptr;
|
||||
count--;
|
||||
}
|
||||
|
||||
void Enemy::distory() {
|
||||
for(int i = 0; i < count; i++) {
|
||||
delete enemy[i];
|
||||
enemy[i] = nullptr;
|
||||
}
|
||||
count = 0;
|
||||
}
|
||||
|
||||
void Enemy::delete_enemy() {
|
||||
distory();
|
||||
delete enemy;
|
||||
enemy = nullptr;
|
||||
}
|
||||
|
||||
/*补给*/
|
||||
Timer Supply::supply_timer(SUPPLY_SEP);
|
||||
Supply* Supply::my_supply = nullptr;
|
||||
|
||||
void Supply::show() {
|
||||
putimage(x, y, &img[0], NOTSRCERASE);
|
||||
putimage(x, y, &img[1], SRCINVERT);
|
||||
}
|
||||
|
||||
void Supply::move() {
|
||||
if(move_timer.t_time()) {
|
||||
y += speed;
|
||||
}
|
||||
}
|
||||
|
||||
void Supply::test_live() {
|
||||
if(y > HEIGHT) {
|
||||
delete_supply();
|
||||
return;
|
||||
}
|
||||
Rect rect = MyPlane::share_plane()->get_rect();
|
||||
Rect supply_rect = get_rect();
|
||||
|
||||
if(rect.collision(supply_rect)) {
|
||||
switch(type) {
|
||||
case super_bullet:
|
||||
// 碰撞则播放音乐,改变弹药,删除补给
|
||||
play_music(TEST_DATA_PATH "/music/get_bullet.wav");
|
||||
MyPlane::share_plane()->change_bullet();
|
||||
break;
|
||||
case recover_life:
|
||||
// 碰撞则播放音乐,增加生命,删除补给
|
||||
play_music(TEST_DATA_PATH "/music/life.wav");
|
||||
MyPlane::share_plane()->get_life();
|
||||
break;
|
||||
case boom_boom:
|
||||
// 碰撞则播放音乐,增加炸药,删除补给
|
||||
play_music(TEST_DATA_PATH "/music/get_bomb.wav");
|
||||
MyPlane::share_plane()->get_boom();
|
||||
break;
|
||||
}
|
||||
|
||||
delete_supply();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Supply::create() {
|
||||
// 到时间则关闭
|
||||
if(supply_timer.t_time()) {
|
||||
supply_timer.close();
|
||||
}
|
||||
|
||||
// 当保护器关闭则产生
|
||||
if(my_supply == nullptr && !supply_timer.status()) {
|
||||
// 开启计时器
|
||||
supply_timer.open();
|
||||
my_supply = new Supply;
|
||||
}
|
||||
}
|
||||
|
||||
void Supply::action() {
|
||||
if(my_supply != nullptr) {
|
||||
my_supply->move();
|
||||
my_supply->show();
|
||||
my_supply->test_live();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
---
|
||||
#本节所有设置在https://clang.llvm.org/docs/ClangFormatStyleOptions.html查询具体定义
|
||||
#全局选项
|
||||
Language: Cpp
|
||||
Standard: c++20
|
||||
BasedOnStyle: Google
|
||||
BreakBeforeBraces: Attach #使用attach的括号换行风格
|
||||
|
||||
#缩进、空格相关
|
||||
AccessModifierOffset: -2 #访问限制词(如public)后面的缩进
|
||||
ContinuationIndentWidth: 2 #长表达式换行缩进
|
||||
IndentWidth: 4 #行缩进为4个空格
|
||||
TabWidth: 4 #tab替换为为4个空格
|
||||
IndentCaseLabels: true #case标签需要缩进
|
||||
IndentPPDirectives: AfterHash #预编译指令采用#前置的缩进
|
||||
NamespaceIndentation: All #namespace括号里的均需要缩进(内部有namespace则继续缩进)
|
||||
|
||||
SpaceBeforeCaseColon: false #case标识符的冒号前不加空格
|
||||
SpaceBeforeCpp11BracedList: false #新建类初始化的参数列表前不加空格
|
||||
SpaceBeforeCtorInitializerColon: false #类初始化的参数列表前不加空格
|
||||
SpaceBeforeParens: Never #小括号前不加空格
|
||||
SpaceBeforeRangeBasedForLoopColon: false #遍历列表形式的for循环小括号前不加空格
|
||||
SpacesInLineCommentPrefix:
|
||||
Maximum: 1
|
||||
Minimum: 1 # //型注释前强制限定为至少添加一个空格 //comment -> // comment
|
||||
|
||||
#对齐、换行相关
|
||||
AlignArrayOfStructures: Left #设置初始化数组的方阵为左对齐
|
||||
AlignTrailingComments: true #对齐所有尾注释
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly #只允许内联函数写在一行中
|
||||
AlwaysBreakTemplateDeclarations: 'No' # 不强制模板函数换行
|
||||
|
||||
#代码风格相关
|
||||
BinPackArguments: true #允许在不同行写任意数目的函数参数
|
||||
PackConstructorInitializers: BinPack #允许在不同行写任意数目的类默认参数
|
||||
#start using after the LLVM15.0 is open. InsertBraces: true #设置每个控制语句后必须用{}包裹,即使只有一行。
|
||||
EmptyLineAfterAccessModifier: Never #访问限制符后面不加空行
|
||||
EmptyLineBeforeAccessModifier: Always #访问限制符前强制加空行
|
||||
IncludeBlocks: Regroup #对include文件进行重新分组
|
||||
DerivePointerAlignment: false #关闭根据int* 和int *的数量自动推导
|
||||
PointerAlignment: Left #强制使用int*类型的指针声明
|
||||
ReferenceAlignment: Pointer #强制使用int&类型的引用声明
|
||||
ColumnLimit: 256 #每行长度限制
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# settings
|
||||
.vs
|
||||
.vscode
|
||||
build/
|
||||
out/
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(Game2D)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_definitions(-DTEST_DATA_PATH="${CMAKE_SOURCE_DIR}/data")
|
||||
|
||||
file(GLOB_RECURSE SOURCES *.cpp *.h)
|
||||
|
||||
add_executable(main ${SOURCES})
|
||||
|
||||
set_target_properties(main PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
|
||||
target_include_directories(main PUBLIC include)
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
这篇文档内容主要是关于游戏设计框架的,将系统地说明整个代码设计的结构,以及数据编写的格式问题。
|
||||
|
||||
|
||||
事件
|
||||
游戏中存在一些事件,。它们有以下几类:
|
||||
被 [] 包围的语句是注释
|
||||
|
||||
None
|
||||
纯命令:
|
||||
|
||||
pause
|
||||
暂停事件,然后退出事件,有格式:
|
||||
pause
|
||||
|
||||
One
|
||||
一对一式:一个命令配合一个描述
|
||||
|
||||
delay
|
||||
指定延时,用于增加命令之间的时间间隔
|
||||
delay time
|
||||
|
||||
map
|
||||
此事件将指定事件在哪张地图上执行,按照格式:
|
||||
map name
|
||||
|
||||
talk
|
||||
此事件将会开启一段对话,按照格式:
|
||||
talk 对话内容
|
||||
需要注意,对话内容必须是连续的,不能有空格或换行
|
||||
|
||||
tip
|
||||
此事件发起提示,按照格式:
|
||||
tip 提示内容
|
||||
|
||||
goto
|
||||
此事件跳过行,直到遇见指定标记为止,按照格式:
|
||||
goto done // 则会一直跳过,直到遇见 done
|
||||
|
||||
back
|
||||
此事件将会向前回溯,直到遇见指定标记为止,按照格式:
|
||||
back command
|
||||
一般会选择返回到之前执行的命令(此命令较为复杂,因为返回难度比较大,暂不考虑)
|
||||
|
||||
question
|
||||
此事件发起询问,如果回答 yes 执行下一行,否则跳过下一行,一般配合 goto 使用;按照格式:
|
||||
// 如果不想继续,可以退出询问,从而可以反复进入
|
||||
question 提问内容
|
||||
goto yes // yes
|
||||
goto no // no
|
||||
|
||||
...
|
||||
|
||||
// 跳过上面,直到遇见 yes
|
||||
yes
|
||||
...
|
||||
goto done // 为了不执行 no ,再次跳过
|
||||
|
||||
no
|
||||
...
|
||||
|
||||
done
|
||||
如此便得到一个分支结构
|
||||
|
||||
delete
|
||||
此命令将销毁一个物体,按照 id 识别,有格式:
|
||||
delete id
|
||||
|
||||
open
|
||||
此命令打开指定的门,按照 id 识别,有格式:
|
||||
open id
|
||||
|
||||
test
|
||||
此命令检测某个物体是否存在,按照 id 识别,其使用方法和 question 类似:
|
||||
test id
|
||||
goto yes // yes
|
||||
goto no // no
|
||||
|
||||
...
|
||||
|
||||
// 跳过上面,直到遇见 yes
|
||||
yes
|
||||
...
|
||||
goto done // 为了不执行 no ,再次跳过
|
||||
|
||||
no
|
||||
...
|
||||
|
||||
done
|
||||
|
||||
background
|
||||
此命令更换背景,按照背景名,有格式:
|
||||
background name
|
||||
|
||||
music
|
||||
此命令播放音乐,按照音乐名,有格式:
|
||||
music name
|
||||
|
||||
link
|
||||
按照 id 链接其它事件,然后退出此事件,进入该事件,有格式:
|
||||
link id
|
||||
|
||||
cancel
|
||||
取消指定物体的事件,有格式:
|
||||
cancel id
|
||||
|
||||
lock
|
||||
此命令锁定指定物体的运动状态,如果 id 为 0 则表示锁定绘图原点
|
||||
lock id
|
||||
|
||||
unlock
|
||||
此命令取消对物体的锁定,如果 id 为 0 则表示取消锁定绘图原点
|
||||
unlock id
|
||||
|
||||
origin
|
||||
此命令移动绘图原点,到指定位置,用于改变视野
|
||||
origin x y
|
||||
|
||||
Two
|
||||
一对二:一个命令需要两个描述
|
||||
|
||||
bind
|
||||
为指定物体绑定事件,有格式:
|
||||
bind id eId
|
||||
|
||||
Three
|
||||
一对三:一个命令需要三个描述
|
||||
|
||||
move
|
||||
此命令移动一个物体到指定位置,按照 id 识别,有格式:
|
||||
move id x y
|
||||
|
||||
create
|
||||
此命令在指定位置创建一个物体,按照 name 识别,有格式:
|
||||
create name x y
|
||||
|
||||
buff
|
||||
此命令给予指定物体加成,有格式:
|
||||
buff id 属性 加成
|
||||
|
||||
debuff
|
||||
此命令给予减损,有格式:
|
||||
debuff id 属性 减损
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
[ 此处存放资源文件的路径 ]
|
||||
|
||||
object
|
||||
{
|
||||
object ../../data/config/object.txt
|
||||
}
|
||||
|
||||
map
|
||||
{
|
||||
map ../../data/config/map.txt
|
||||
}
|
||||
|
||||
UI
|
||||
{
|
||||
head ../../data/config/head.txt
|
||||
}
|
||||
|
||||
image
|
||||
{
|
||||
Background ../../data/image/background.png
|
||||
Man ../../data/image/man.png
|
||||
GStatic ../../data/image/ground.png
|
||||
GWindow ../../data/image/window.png
|
||||
GButton ../../data/image/cbutton.png
|
||||
Enemy ../../data/image/enemy.png
|
||||
}
|
||||
|
||||
music
|
||||
{
|
||||
head ../../data/music/ColdWarEcho.mp3
|
||||
game ../../data/music/Cosmos.mp3
|
||||
}
|
||||
|
||||
[ sword ../../data/music/sword.mp3 ]
|
||||
[ hurt ../../data/music/hurt.mp3 ]
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
[ 事件类型存放在这里 ]
|
||||
|
||||
{
|
||||
create ground 0 0
|
||||
}
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
[ 首页的样式 ]
|
||||
|
||||
{
|
||||
Detail name cbutton
|
||||
|
||||
[ 附加消息 ]
|
||||
Detail text 开始
|
||||
Detail result start
|
||||
|
||||
[ 位置信息 ]
|
||||
Point pos 290 200
|
||||
}
|
||||
|
||||
{
|
||||
Detail name cbutton
|
||||
|
||||
Detail text 加载
|
||||
Detail result load
|
||||
|
||||
Point pos 290 250
|
||||
}
|
||||
|
||||
{
|
||||
Detail name cbutton
|
||||
|
||||
Detail text 设置
|
||||
Detail result setting
|
||||
|
||||
Point pos 290 300
|
||||
}
|
||||
|
||||
{
|
||||
Detail name cbutton
|
||||
|
||||
Detail text 关于
|
||||
Detail result about
|
||||
|
||||
Point pos 290 350
|
||||
}
|
||||
|
||||
[ 控件之间相互分离 ]
|
||||
[ 首页设置的窗口 ]
|
||||
{
|
||||
Detail name window
|
||||
|
||||
[ 附加消息 ]
|
||||
Detail show false
|
||||
|
||||
Point pos 150 100
|
||||
}
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
[ 此文档中存放地图相关的数据 ]
|
||||
|
||||
[ 第一部分 ]
|
||||
|
||||
[ 地面 ]
|
||||
[ 地图尺寸 100 * 50 ]
|
||||
[ 窗口尺寸 720 * 480 宽度不是标准的 ]
|
||||
|
||||
[ 注意: framework 中不能有注释 ]
|
||||
[ row 先写出 y 方向的坐标,然后写 x 方向的 ]
|
||||
[ col 先写出 x 方向的坐标,然后写 y 方向的 ]
|
||||
|
||||
[ 注意应当保证所有物体的坐标都非负 ]
|
||||
|
||||
[ link 连接模式 应当成对出现坐标,分别作为连接的范围 ]
|
||||
[ point 点模式 逐点设置位置 ]
|
||||
|
||||
[ 另外,相同的“行”可以重复出现 ]
|
||||
|
||||
[ 搭建地图 ]
|
||||
framework ground
|
||||
12 row link 2 10 -1
|
||||
12 row link 20 30 -1
|
||||
12 row link 12 18 -1
|
||||
10 row link 30 50 -1
|
||||
6 row link 2 50 -1
|
||||
1 col link 6 12 -1
|
||||
-1
|
||||
|
||||
[ 人物位置 ]
|
||||
{
|
||||
Detail name man
|
||||
|
||||
[ 人物属性 ]
|
||||
Single HP 3
|
||||
Single attack 1
|
||||
Single jump 20
|
||||
Point speed 5 0
|
||||
|
||||
Point pos 5 10
|
||||
}
|
||||
|
||||
[ 布置怪物 ]
|
||||
|
||||
{
|
||||
Detail name defender
|
||||
|
||||
[ 位置信息 ]
|
||||
Point pos 5 10
|
||||
[ 可移动范围 left right ]
|
||||
Point range 5 10
|
||||
Detail dir left
|
||||
}
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
[ 此处存放所有全局定义 ]
|
||||
|
||||
{
|
||||
Detail class Man
|
||||
Detail name man
|
||||
|
||||
Point size 32 32
|
||||
|
||||
[ 剑 ]
|
||||
[ 剑刺的步数,以及停止步数(需要有静止帧体现惯性) ]
|
||||
Single swordStep 12
|
||||
Single swordStay 9
|
||||
Point swordPos1 0 128
|
||||
Point swordPos2 32 128
|
||||
|
||||
[ 盾 id 用于标识计时器 ]
|
||||
Single shieldID 1
|
||||
Point shieldSize 45 45
|
||||
Point shieldPos 64 128
|
||||
[ 分别是护盾持续时间,和发起护盾需要的时间(从开启护盾算起) ]
|
||||
Single shieldTime 3000
|
||||
Single shieldCool 6000
|
||||
|
||||
|
||||
[ 矩形修正 ]
|
||||
Single wFix 0.70
|
||||
Single hFix 1
|
||||
}
|
||||
|
||||
[ 物体应当标注类名 ]
|
||||
{
|
||||
[ 地面 ]
|
||||
Detail class GStatic
|
||||
Detail name ground
|
||||
|
||||
Point size 32 32
|
||||
|
||||
Single wFix 1
|
||||
Single hFix 1
|
||||
|
||||
Point imagePos 0 0
|
||||
}
|
||||
|
||||
{
|
||||
[ 点击选择按钮 ]
|
||||
Detail class GButton
|
||||
Detail name cbutton
|
||||
|
||||
Point size 140 40
|
||||
|
||||
Single wFix 0.9
|
||||
Single hFix 0.9
|
||||
|
||||
Point imagePos1 0 0
|
||||
Point imagePos2 140 0
|
||||
}
|
||||
|
||||
{
|
||||
[ 游戏中的选项窗口 ]
|
||||
Detail class GStatic
|
||||
Detail name window
|
||||
|
||||
Point size 420 280
|
||||
|
||||
Point imagePos 0 480
|
||||
}
|
||||
|
||||
[ 一共有三种怪物 ]
|
||||
|
||||
[ 使用镰刀挥砍的怪物 ]
|
||||
|
||||
{
|
||||
Detail class Enemy
|
||||
Detail name defender
|
||||
|
||||
Single HP 10
|
||||
|
||||
[ 注意:这里只表示速度大小,还需要确定方向 ]
|
||||
Point speed 2 0
|
||||
|
||||
Point size 32 32
|
||||
Point imagePos 0 0
|
||||
|
||||
Single wFix 1
|
||||
Single hFix 1
|
||||
|
||||
[ 长枪 ]
|
||||
[ 攻击范围 ]
|
||||
Single range 32
|
||||
Single speedBuf 8
|
||||
Single bufTime 1500
|
||||
|
||||
[ 位置修正 ]
|
||||
Single dx 16
|
||||
|
||||
Point actSize 50 32
|
||||
Point actPos1 0 128
|
||||
Point actPos2 50 128
|
||||
}
|
||||
|
||||
end
|
||||
|
After Width: | Height: | Size: 309 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 319 B |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 331 B |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -0,0 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
#define MAX_LOADSTRING 100
|
||||
#define BUF_SIZE 255
|
||||
|
||||
#define MOVESEP 50
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define SIGN(x) ((x) > 0 ? 1 : (((x) == 0) ? 0 : -1))
|
||||
|
||||
enum { DGravity = 4, DUp = 0, DDown = 3, DLeft = 1, DRight = 2, DStay = 5, Gravity = 3 };
|
||||
|
||||
enum { StdWidth = 32, StdHeight = 32, WindowWidth = 720, WindowHeight = 480, GAME_HEAD, GAME_START, GAME_PAUSE, GAME_LOAD, GAME_SET, GAME_ABOUT };
|
||||
|
||||
enum { ById, ByName, ByFree };
|
||||
|
||||
typedef int Direction;
|
||||
typedef int Mode;
|
||||
|
||||
// #define OPEN_DEBUG
|
||||
|
||||
// 调试输出
|
||||
namespace DebugOutPut {
|
||||
inline void openConsole() {
|
||||
AllocConsole();
|
||||
}
|
||||
inline void closeConsole() {
|
||||
FreeConsole();
|
||||
}
|
||||
inline void output(const char buf[]) {
|
||||
HANDLE hd = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
WriteConsole(hd, buf, strlen(buf), NULL, NULL);
|
||||
CloseHandle(hd);
|
||||
}
|
||||
}; // namespace DebugOutPut
|
||||
|
||||
// 设置文字
|
||||
inline void setText(LPCSTR fName, COLORREF color, LONG fHeight, LONG fWeight) {
|
||||
settextcolor(color);
|
||||
LOGFONT f;
|
||||
gettextstyle(&f);
|
||||
|
||||
f.lfHeight = fHeight;
|
||||
f.lfWeight = fWeight;
|
||||
|
||||
_tcscpy_s(f.lfFaceName, fName);
|
||||
settextstyle(&f);
|
||||
}
|
||||
|
||||
// 绘图函数,补充透明度
|
||||
inline void drawAlpha(IMAGE* image, int x, int y, int width, int height, int pic_x, int pic_y, double AA = 1) {
|
||||
// 变量初始化
|
||||
DWORD* dst = GetImageBuffer(); // GetImageBuffer()函数,用于获取绘图设备的显存指针,EASYX自带
|
||||
DWORD* draw = GetImageBuffer();
|
||||
DWORD* src = GetImageBuffer(image); // 获取picture的显存指针
|
||||
int imageWidth = image->getwidth(); // 获取图片宽度
|
||||
int imageHeight = image->getheight(); // 获取图片宽度
|
||||
int dstX = 0; // 在 绘图区域 显存里像素的角标
|
||||
int srcX = 0; // 在 image 显存里像素的角标
|
||||
|
||||
// 实现透明贴图 公式: Cp=αp*FP+(1-αp)*BP , 贝叶斯定理来进行点颜色的概率计算
|
||||
for(int iy = 0; iy < height; iy++) {
|
||||
for(int ix = 0; ix < width; ix++) {
|
||||
// 防止越界
|
||||
if(ix + pic_x >= 0 && ix + pic_x < imageWidth && iy + pic_y >= 0 && iy + pic_y < imageHeight && ix + x >= 0 && ix + x < WindowWidth && iy + y >= 0 && iy + y < WindowHeight) {
|
||||
// 获取像素角标
|
||||
int srcX = (ix + pic_x) + (iy + pic_y) * imageWidth;
|
||||
dstX = (ix + x) + (iy + y) * WindowWidth;
|
||||
|
||||
int sa = ((src[srcX] & 0xff000000) >> 24) * AA; // 0xAArrggbb;AA是透明度
|
||||
int sr = ((src[srcX] & 0xff0000) >> 16); // 获取 RGB 里的 R
|
||||
int sg = ((src[srcX] & 0xff00) >> 8); // G
|
||||
int sb = src[srcX] & 0xff; // B
|
||||
|
||||
// 设置对应的绘图区域像素信息
|
||||
int dr = ((dst[dstX] & 0xff0000) >> 16);
|
||||
int dg = ((dst[dstX] & 0xff00) >> 8);
|
||||
int db = dst[dstX] & 0xff;
|
||||
draw[dstX] = ((sr * sa / 255 + dr * (255 - sa) / 255) << 16) // 公式: Cp=αp*FP+(1-αp)*BP ; αp=sa/255 , FP=sr , BP=dr
|
||||
| ((sg * sa / 255 + dg * (255 - sa) / 255) << 8) // αp=sa/255 , FP=sg , BP=dg
|
||||
| (sb * sa / 255 + db * (255 - sa) / 255); // αp=sa/255 , FP=sb , BP=db
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
|
||||
#include "object.h"
|
||||
|
||||
class GDynamic : public Object {
|
||||
public:
|
||||
// 移动方向
|
||||
void move() {
|
||||
if(!stay) {
|
||||
coord.x += speed.x;
|
||||
}
|
||||
int x = status % 10, y = status / 10;
|
||||
x = (x + 1) % 9;
|
||||
status = x + y * 10;
|
||||
}
|
||||
|
||||
// 原地不动
|
||||
void lock(bool ifLock) { stay = ifLock; }
|
||||
|
||||
void gravity() {
|
||||
coord.y += speed.y;
|
||||
speed.y += Gravity;
|
||||
}
|
||||
|
||||
bool ifAlive() { return alive; }
|
||||
|
||||
void setDir(Direction d) {
|
||||
int x = status % 10, y = status / 10;
|
||||
switch(d) {
|
||||
case DLeft:
|
||||
speed.x = -std::abs(speed.x);
|
||||
y = DLeft;
|
||||
break;
|
||||
case DRight:
|
||||
speed.x = std::abs(speed.x);
|
||||
y = DRight;
|
||||
break;
|
||||
case DUp:
|
||||
break;
|
||||
case DDown:
|
||||
break;
|
||||
}
|
||||
status = x + y * 10;
|
||||
}
|
||||
int getDir() {
|
||||
if(speed.x > 0) {
|
||||
return DRight;
|
||||
} else if(speed.x < 0) {
|
||||
return DLeft;
|
||||
} else {
|
||||
return DStay;
|
||||
}
|
||||
}
|
||||
void setSpeed(POINT v) {
|
||||
speed = v;
|
||||
if(v.x > 0) {
|
||||
setDir(DRight);
|
||||
} else {
|
||||
setDir(DLeft);
|
||||
}
|
||||
}
|
||||
|
||||
POINT getSpeed() const { return speed; }
|
||||
void setPos(POINT pos) { coord = pos; }
|
||||
|
||||
POINT speed; // 速度
|
||||
bool alive; // 是否存活
|
||||
bool stay; // 运动锁
|
||||
int status; // 记录运动状态 十位表示上下左右,个位表示状态
|
||||
|
||||
// 这里涉及到一个关键 bug :不要偷懒在构造函数执行前就直接调用封装在类中的 getPoint 等函数
|
||||
GDynamic(std::string name, POINT pos, POINT speed, Data* data = nullptr): Object(name, pos, data), speed(speed), alive(true), stay(false), status(0) {}
|
||||
};
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "object.h"
|
||||
|
||||
// 静态物体不会移动,只是装饰物
|
||||
class GStatic : public Object {
|
||||
public:
|
||||
// 默认情况下是会显示的
|
||||
GStatic(std::string name, POINT pos, Data* data = nullptr): Object(name, pos, data) {}
|
||||
|
||||
void draw(POINT origin) {
|
||||
POINT size = getPoint("size");
|
||||
// 如果离开画面范围,就不用绘制
|
||||
if(coord.x - origin.x < WindowWidth && origin.x - coord.x < size.x && coord.y - origin.y < WindowHeight && origin.y - coord.y < size.y) {
|
||||
POINT picPos = getPoint("imagePos");
|
||||
drawAlpha(MImage::imageMap["GStatic"], coord.x - origin.x, coord.y - origin.y, (int)size.x, (int)size.y, (int)picPos.x, (int)picPos.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 按钮是特殊的静态物体
|
||||
class GButton : public GStatic {
|
||||
public:
|
||||
GButton(std::string name, POINT pos, Data* data = nullptr): GStatic(name, pos, data), clicked(false) {}
|
||||
|
||||
void draw(POINT origin) {
|
||||
POINT picPos = (clicked ? getPoint("imagePos1") : getPoint("imagePos2"));
|
||||
POINT size = getPoint("size");
|
||||
drawAlpha(MImage::imageMap["GButton"], coord.x - origin.x, coord.y - origin.y, (int)size.x, (int)size.y, (int)picPos.x, (int)picPos.y);
|
||||
|
||||
// 从附加信息中获取文本
|
||||
std::string text = data->details["text"];
|
||||
if(text != "") {
|
||||
// 显示文本
|
||||
setText("Consolas", WHITE, 15, 5);
|
||||
LPCSTR p = text.c_str();
|
||||
RECT r = getRect();
|
||||
drawtext(p, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||
}
|
||||
}
|
||||
bool getClicked() const { return clicked; }
|
||||
void setClicked(bool c) { clicked = c; }
|
||||
|
||||
protected:
|
||||
bool clicked;
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include "map.h"
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "GDynamic.h"
|
||||
|
||||
// 关于主角的相关类
|
||||
|
||||
class Man : public GDynamic {
|
||||
public:
|
||||
// 创建和删除
|
||||
static Man* createMan(Data* data);
|
||||
static bool deleteMan();
|
||||
|
||||
// 移动方向
|
||||
void jump();
|
||||
void draw(POINT origin);
|
||||
|
||||
// 能力
|
||||
void sword(); // 剑刺
|
||||
void shield(); // 护盾
|
||||
RECT swordRange(Direction& d); // 返回剑刺的范围和方向
|
||||
|
||||
void dead();
|
||||
void thinking(POINT&, POINT&);
|
||||
|
||||
int HP, attack;
|
||||
|
||||
private:
|
||||
static Man* man;
|
||||
|
||||
int jumpHeight;
|
||||
bool shielding; // 是否有护盾
|
||||
POINT alivePos; // 复活位置
|
||||
int swordStep;
|
||||
Timer* timer; // 用于一些技能的计时(护盾)
|
||||
|
||||
// 这里涉及到一个关键 bug :不要偷懒在构造函数执行前就直接调用封装在类中的 getPoint 等函数
|
||||
Man(): GDynamic("man", dataMap["man"]->points["pos"], {0, 0}), alivePos({0, 0}), HP(0), attack(0), jumpHeight(0), shielding(false), swordStep(0), timer(nullptr) {}
|
||||
~Man() {}
|
||||
};
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "GDynamic.h"
|
||||
|
||||
// 怪物相关
|
||||
|
||||
class Enemy : public GDynamic {
|
||||
public:
|
||||
// 创建怪物,删除操作在 Map 中进行
|
||||
static Enemy* createEnemy(std::string name, POINT pos, POINT moveRange);
|
||||
|
||||
// 移动方向
|
||||
void draw(POINT origin);
|
||||
void hurt(int a) {
|
||||
HP -= a;
|
||||
if(HP <= 0) {
|
||||
alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 砍击
|
||||
void cut(int ID);
|
||||
bool ifSkill() { return skilling; }
|
||||
|
||||
// 技能步数,攻击范围
|
||||
int HP, skillRange;
|
||||
bool skilling;
|
||||
POINT moveRange;
|
||||
Timer* timer; // 技能计时
|
||||
|
||||
Enemy(std::string name, POINT pos, POINT moveRange, Data* data = nullptr): GDynamic(name, pos, {0, 0}, data), HP(0), skillRange(0), skilling(false), timer(nullptr), moveRange(moveRange) {}
|
||||
~Enemy() {
|
||||
if(timer != nullptr) {
|
||||
delete timer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// header.h: 标准系统包含文件的包含文件,
|
||||
// 或特定于项目的包含文件
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
// Windows 头文件
|
||||
#include <conio.h>
|
||||
#include <graphics.h> // 引用图形库头文件
|
||||
|
||||
// C 运行时头文件
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#include <mmsystem.h> //包含多媒体设备接口头文件
|
||||
#include <stdlib.h>
|
||||
#include <tchar.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#pragma comment(lib, "winmm.lib") // 加载静态库
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "GStatic.h"
|
||||
#include "Man.h"
|
||||
#include "enemy.h"
|
||||
|
||||
|
||||
class Map {
|
||||
public:
|
||||
static Map* createMap(LPCSTR name);
|
||||
|
||||
// id 对应每一个物体,每个物体的 id 就是它的下标。当然,为了方便创建物体,我们保存最大 id
|
||||
std::map<int, Object*> staticMap; // 当前地图的静态物体
|
||||
std::map<int, Enemy*> enemyMap; // 敌人
|
||||
|
||||
int MAXID; // 最大 id ,是当前地图中物体的最大 id
|
||||
Data* mData; // 人物数据
|
||||
|
||||
Map(LPCSTR name);
|
||||
~Map();
|
||||
|
||||
void draw(POINT origin);
|
||||
bool collision(GDynamic* gd, Direction d);
|
||||
void move(Man* man); // 移动地图中的物体,同时进行碰撞检测,它应当在 man 移动后进行
|
||||
std::string buttonClicked(POINT mp); // 返回按下的结果
|
||||
};
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include "struct.h"
|
||||
|
||||
class Object {
|
||||
public:
|
||||
static std::map<std::string, Data*> dataMap; // 存放所有物体对应的信息
|
||||
|
||||
Object(std::string name, POINT pos, Data* data = nullptr): name(name), coord(pos), data(data), show(true), eId(0), AA(1) {}
|
||||
// 清除缓存数据和附加信息
|
||||
~Object();
|
||||
|
||||
virtual void draw(POINT origin) = 0; // 绘图
|
||||
|
||||
// 从 dataMap 返回数据
|
||||
double getSingle(std::string str) { return dataMap[name]->singles[str]; }
|
||||
POINT getPoint(std::string str) { return dataMap[name]->points[str]; }
|
||||
std::string getDetail(std::string str) { return dataMap[name]->details[str]; }
|
||||
|
||||
// 绑定事件
|
||||
void bindEvent(int eventId) { eId = eventId; }
|
||||
|
||||
POINT getPos() const { return coord; } // 位置
|
||||
RECT getRect(); // 所在矩形,考虑修正 fix
|
||||
std::string getName() const { return name; }
|
||||
Data* getData() const { return data; }
|
||||
bool ifShow() const { return show; }
|
||||
|
||||
void setShow(bool s) { show = s; }
|
||||
double getAA() { return AA; }
|
||||
|
||||
static void readData(LPCSTR name); // 获取初始化信息
|
||||
static void deleteData(); // 清除相关信息
|
||||
|
||||
protected:
|
||||
// 物体不需要 id , id 通过 Map 中的映射隐式地使用
|
||||
std::string name; // 用于标记物体类型
|
||||
POINT coord;
|
||||
Data* data; // 可能携带的附加信息,例如返回值,文本等
|
||||
bool show; // 是否显示
|
||||
int eId; // 绑定的事件 id ,每次只能绑定一个
|
||||
double AA; // 透明度
|
||||
};
|
||||
|
||||
// 碰撞检测
|
||||
namespace Collision {
|
||||
inline bool checkIn(POINT p, RECT r) {
|
||||
if((p.x > r.left) && (p.x < r.right) && (p.y > r.top) && (p.y < r.bottom)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool checkCollision(RECT r1, RECT r2) {
|
||||
// r1 中心位置
|
||||
POINT size = {r1.right - r1.left, r1.bottom - r1.top};
|
||||
POINT c = {(r1.right + r1.left) / 2, (r1.top + r1.bottom) / 2};
|
||||
RECT r = {r2.left - size.x / 2, r2.top - size.y / 2, r2.right + size.x / 2, r2.bottom + size.y / 2};
|
||||
if(checkIn(c, r)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool checkCollision(Object* obj1, Object* obj2) {
|
||||
RECT r1 = obj1->getRect(), r2 = obj2->getRect();
|
||||
return checkCollision(r1, r2);
|
||||
}
|
||||
|
||||
inline std::vector<int> checkCollision(Object* obj, std::vector<Object*>& l) {
|
||||
std::vector<int> v;
|
||||
int index = 0;
|
||||
for(auto it = l.begin(); it != l.end(); ++it) {
|
||||
if(checkCollision(obj, *it)) {
|
||||
v.push_back(index);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
}; // namespace Collision
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
// 封装图片结构
|
||||
struct MImage {
|
||||
static std::map<std::string, IMAGE*> imageMap; // 存放所有图片
|
||||
|
||||
static IMAGE* createImage(LPCSTR name) {
|
||||
IMAGE* image = new IMAGE;
|
||||
loadimage(image, name);
|
||||
return image;
|
||||
}
|
||||
static void deleteImage() {
|
||||
for(auto it = imageMap.begin(); it != imageMap.end(); ++it) {
|
||||
if(it->second != nullptr) {
|
||||
delete it->second;
|
||||
imageMap[it->first] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 音乐封装类
|
||||
struct Music {
|
||||
// 使用 LPCSTR 作为键值有重大隐患,可能会有离开作用域导致键值丢失的情况出现
|
||||
static std::map<std::string, Music*> musicMap; // 存放所有音乐
|
||||
|
||||
// 获取音乐路径
|
||||
Music(LPCSTR name): command("") { strcat_s(command, name); }
|
||||
static Music* createMusic(std::string name) { return new Music(name.c_str()); }
|
||||
static void deleteMusic() {
|
||||
for(auto it = musicMap.begin(); it != musicMap.end(); ++it) {
|
||||
delete it->second;
|
||||
musicMap[it->first] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回开头
|
||||
void toStart() {
|
||||
char command_name[BUF_SIZE] = "seek ";
|
||||
strcat_s(command_name, command);
|
||||
strcat_s(command_name, " to start");
|
||||
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
void open() {
|
||||
char command_name[BUF_SIZE] = "open ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
void play(bool repeat = false) {
|
||||
char buf[BUF_SIZE];
|
||||
status(buf);
|
||||
// 如果音乐没有在播放,就播放音乐
|
||||
if(buf[0] != 'p') {
|
||||
char command_name[BUF_SIZE] = "play ";
|
||||
strcat_s(command_name, command);
|
||||
if(repeat) {
|
||||
strcat_s(command_name, " repeat");
|
||||
}
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
void pause() {
|
||||
char command_name[BUF_SIZE] = "pause ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
void resume() {
|
||||
char command_name[BUF_SIZE] = "resume ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
void stop() {
|
||||
char command_name[BUF_SIZE] = "stop ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
void close() {
|
||||
char command_name[BUF_SIZE] = "close ";
|
||||
strcat_s(command_name, command);
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
// 设置音量
|
||||
void setVolume(int volume) {
|
||||
char command_name[BUF_SIZE] = "setaudio ";
|
||||
strcat_s(command_name, command);
|
||||
strcat_s(command_name, " volume to");
|
||||
char volume_buf[BUF_SIZE];
|
||||
sprintf_s(volume_buf, " %d", volume);
|
||||
strcat_s(command_name, volume_buf);
|
||||
// 设置音量
|
||||
mciSendString(command_name, 0, 0, 0);
|
||||
}
|
||||
// 音乐长度
|
||||
void length(char buf[]) {
|
||||
char command_name[BUF_SIZE] = "status ";
|
||||
strcat_s(command_name, command);
|
||||
strcat_s(command_name, " length");
|
||||
|
||||
// 记录状态
|
||||
mciSendString(command_name, buf, BUF_SIZE, 0);
|
||||
}
|
||||
// 获取播放状态
|
||||
void status(char buf[]) {
|
||||
char command_name[BUF_SIZE] = "status ";
|
||||
strcat_s(command_name, command);
|
||||
strcat_s(command_name, " mode");
|
||||
|
||||
// 记录状态
|
||||
mciSendString(command_name, buf, BUF_SIZE, 0);
|
||||
}
|
||||
|
||||
char command[BUF_SIZE];
|
||||
};
|
||||
|
||||
// 定时器封装
|
||||
struct Timer {
|
||||
static Timer* createTimer(const int ID, const DWORD interval) {
|
||||
Timer* timer = new Timer(ID, interval);
|
||||
timerMap[ID] = timer;
|
||||
return timer;
|
||||
}
|
||||
static void deleteTimer() {
|
||||
for(auto it = timerMap.begin(); it != timerMap.end(); ++it) {
|
||||
delete it->second;
|
||||
timerMap[it->first] = nullptr;
|
||||
timerMap.erase(it);
|
||||
}
|
||||
}
|
||||
static void deleteTimer(const int ID) {
|
||||
if(timerMap[ID] != nullptr) {
|
||||
delete timerMap[ID];
|
||||
timerMap[ID] = nullptr;
|
||||
timerMap.erase(ID);
|
||||
}
|
||||
}
|
||||
// 开启定时器,同时返回指针
|
||||
static Timer* start(const int ID) {
|
||||
Timer* timer = timerMap[ID];
|
||||
if(!timer->on) {
|
||||
timer->t = clock();
|
||||
timer->on = true;
|
||||
}
|
||||
return timer;
|
||||
}
|
||||
|
||||
void start() {
|
||||
t = clock();
|
||||
on = true;
|
||||
}
|
||||
void stop() { on = false; }
|
||||
bool onTime() {
|
||||
if(on) {
|
||||
DWORD t2 = clock();
|
||||
if(t2 - t > interval) {
|
||||
t = t2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
DWORD now() {
|
||||
// 返回持续时间
|
||||
if(on) {
|
||||
return clock() - t;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 保存所有的定时器
|
||||
static std::map<int, Timer*> timerMap;
|
||||
|
||||
Timer(const int ID, const DWORD interval): t(0), ID(ID), interval(interval), on(false) {}
|
||||
|
||||
DWORD t;
|
||||
const DWORD interval;
|
||||
const int ID;
|
||||
bool on;
|
||||
};
|
||||
|
||||
// 数据
|
||||
struct Data {
|
||||
static Data* readSrc(std::fstream& fp); // 特殊资源映射
|
||||
static Data* readObject(std::fstream& fp); // 读取物体,返回数据指针
|
||||
static void readFramework(std::fstream& fp, std::vector<Data*>& dataList);
|
||||
static void readNote(std::fstream& fp); // 读取注释
|
||||
|
||||
// 读取数据,返回数据指针
|
||||
static void read(LPCSTR name, std::map<std::string, Data*>& dataMap);
|
||||
static void read(LPCSTR name, std::map<std::string, Data*>& dataMap, Mode mode);
|
||||
static std::string freeStr(); // 产生随机字符串
|
||||
|
||||
std::map<std::string, double> singles; // 单变量
|
||||
std::map<std::string, POINT> points; // 点变量
|
||||
std::map<std::string, std::string> details; // 描述
|
||||
};
|
||||
|
||||
struct Event {
|
||||
static std::map<int, Event*> eventMap; // 所有事件映射,通过 id 访问
|
||||
|
||||
static void read(LPCSTR name); // 读取所有事件
|
||||
static Event* readEvent(std::fstream& fp); // 读取 {} 中的事件
|
||||
static void deleteEvent(); // 删除所有事件
|
||||
|
||||
// 事件同样不需要保存 id,其 id 通过 eventMap 的键值隐式地使用
|
||||
std::vector<std::string>::iterator eIt; // 保存指令迭代器,当为 commandList.end 则结束
|
||||
std::vector<std::string> commandList; // 指令列表
|
||||
std::vector<int> intList; // 数值表
|
||||
};
|
||||
|
|
@ -0,0 +1,307 @@
|
|||
// Game2D.cpp : 定义应用程序的入口点。
|
||||
//
|
||||
|
||||
#include "Game2D.h"
|
||||
|
||||
#include "framework.h"
|
||||
|
||||
|
||||
// 全局变量:
|
||||
CHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
|
||||
POINT WindowSize; // 窗口信息
|
||||
|
||||
// 定时器
|
||||
int moveTimeID = 1000;
|
||||
|
||||
// 声明函数
|
||||
void WndCreate(); // 初始化
|
||||
void WndDelete(); // 销毁内容
|
||||
Map* loadMap(LPCSTR name); // 加载地图
|
||||
Map* loadUI(LPCSTR name); // 加载 UI
|
||||
|
||||
// 不同界面的消息处理
|
||||
int WndProc(HINSTANCE hInstance, HWND hWnd, int status);
|
||||
int WndProcHead(HINSTANCE hInstance, HWND hWnd); // 首页
|
||||
int WndProcGame(HINSTANCE hInstance, HWND hWnd); // 游戏
|
||||
|
||||
// 创建主角
|
||||
Man* man = nullptr;
|
||||
Map* map = nullptr;
|
||||
Map* ui = nullptr;
|
||||
// 外部数据
|
||||
std::map<std::string, Data*> srcMap;
|
||||
|
||||
// 绘图偏移坐标系:随人物移动绘图原点发生变化
|
||||
POINT origin = {0, 0};
|
||||
// 移动偏移值
|
||||
POINT moveDist = {0, 0};
|
||||
|
||||
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) {
|
||||
// 调试选项
|
||||
#ifdef OPEN_DEBUG
|
||||
DebugOutPut::openConsole();
|
||||
#endif // OPEN_DEBUG
|
||||
|
||||
// TODO: 初始化数据
|
||||
WndCreate();
|
||||
|
||||
// 初始化全局字符串
|
||||
// LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
|
||||
// 创建绘图窗口
|
||||
initgraph(WindowWidth, WindowHeight);
|
||||
|
||||
HWND hWnd = GetHWnd();
|
||||
SetWindowText(hWnd, szTitle);
|
||||
|
||||
// 开始绘图
|
||||
BeginBatchDraw();
|
||||
setbkcolor(RGB(0, 0, 0));
|
||||
setbkmode(TRANSPARENT);
|
||||
|
||||
// 页面状态初始化为首页
|
||||
int status = GAME_HEAD;
|
||||
|
||||
while(TRUE) {
|
||||
// 使用当前背景色刷新窗口
|
||||
// 由于使用了定时器,这一刷新会覆盖绘图
|
||||
status = WndProc(hInstance, hWnd, status);
|
||||
// 绘制到窗口
|
||||
FlushBatchDraw();
|
||||
// 睡眠可能导致无法按时响应鼠标消息
|
||||
// Sleep(ROUNDSEP);
|
||||
}
|
||||
|
||||
EndBatchDraw(); // 结束绘图
|
||||
closegraph(); // 关闭绘图窗口
|
||||
WndDelete(); // 销毁数据
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WndCreate() {
|
||||
// 随机数种子
|
||||
srand((int)time(0));
|
||||
|
||||
// 外部数据初始化,读取配置文件
|
||||
LPCSTR STRData;
|
||||
Data::read(TEST_DATA_PATH "/config/config.txt", srcMap);
|
||||
|
||||
// 物体数据
|
||||
STRData = srcMap["object"]->details["object"].c_str();
|
||||
Object::readData(STRData);
|
||||
|
||||
// 加载图片,它们分别和类名对应
|
||||
std::map<std::string, std::string> mImage = srcMap["image"]->details;
|
||||
for(auto it = mImage.begin(); it != mImage.end(); ++it) {
|
||||
STRData = it->second.c_str();
|
||||
MImage::imageMap[it->first] = MImage::createImage(STRData);
|
||||
}
|
||||
|
||||
// 加载音乐,它们分别和标签对应
|
||||
std::map<std::string, std::string> mMusic = srcMap["music"]->details;
|
||||
for(auto it = mMusic.begin(); it != mMusic.end(); ++it) {
|
||||
Music::musicMap[it->first] = Music::createMusic(it->second);
|
||||
}
|
||||
|
||||
// 创建定时器
|
||||
Timer::createTimer(moveTimeID, MOVESEP);
|
||||
}
|
||||
|
||||
void WndDelete() {
|
||||
Man::deleteMan();
|
||||
Event::deleteEvent();
|
||||
Object::deleteData();
|
||||
Timer::deleteTimer();
|
||||
Music::deleteMusic();
|
||||
MImage::deleteImage();
|
||||
|
||||
// 清除 ui 和 map
|
||||
if(ui != nullptr) {
|
||||
delete ui;
|
||||
}
|
||||
if(map != nullptr) {
|
||||
delete map;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 函数: void WndProc(HINSTANCE hInstance, HWND hWnd);
|
||||
//
|
||||
// 目标: 处理主窗口的消息。
|
||||
|
||||
int WndProc(HINSTANCE hInstance, HWND hWnd, int status) {
|
||||
switch(status) {
|
||||
// 设置框,加载框放在首页
|
||||
case GAME_ABOUT: // 弹出一个关于框
|
||||
MessageBox(hWnd, "version 0.0.1", "About", MB_OK);
|
||||
status = GAME_HEAD;
|
||||
case GAME_SET:
|
||||
case GAME_LOAD:
|
||||
case GAME_HEAD:
|
||||
status = WndProcHead(hInstance, hWnd);
|
||||
break;
|
||||
// 游戏内容相关
|
||||
case GAME_PAUSE:
|
||||
case GAME_START:
|
||||
// 游戏进程,开启计时器
|
||||
status = WndProcGame(hInstance, hWnd);
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Map* loadMap(LPCSTR name) {
|
||||
// 加载地图
|
||||
std::map<std::string, std::string> mMap = srcMap["map"]->details;
|
||||
map = Map::createMap(mMap[name].c_str());
|
||||
|
||||
// 创建人物,先执行这一步,因为要修正位置
|
||||
man = Man::createMan(map->mData);
|
||||
// 记录最初位置
|
||||
moveDist = man->getPos();
|
||||
// 调整原点位置
|
||||
origin = {moveDist.x - WindowWidth / 2, moveDist.y - WindowHeight / 2};
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
Map* loadUI(LPCSTR name) {
|
||||
// 加载界面
|
||||
std::map<std::string, std::string> mMap = srcMap["UI"]->details;
|
||||
ui = Map::createMap(mMap[name].c_str());
|
||||
return ui;
|
||||
}
|
||||
|
||||
int WndProcHead(HINSTANCE hInstance, HWND hWnd) {
|
||||
static Map* ui = loadUI("head");
|
||||
int status = GAME_HEAD;
|
||||
|
||||
// 播放音乐
|
||||
Music::musicMap["head"]->play(true);
|
||||
|
||||
// 首页,处理鼠标消息
|
||||
ExMessage msg;
|
||||
if(peekmessage(&msg, EM_MOUSE)) {
|
||||
std::string str = "";
|
||||
switch(msg.message) {
|
||||
case WM_LBUTTONDOWN:
|
||||
ui->buttonClicked({msg.x, msg.y});
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
str = ui->buttonClicked({msg.x, msg.y});
|
||||
if(str == "start") {
|
||||
// 进入游戏,音乐停止
|
||||
Music::musicMap["head"]->stop();
|
||||
status = GAME_START;
|
||||
} else if(str == "load") {
|
||||
status = GAME_LOAD;
|
||||
} else if(str == "setting") {
|
||||
// 显示设置窗口,按照 id 进行设置
|
||||
ui->staticMap[5]->setShow(true);
|
||||
status = GAME_SET;
|
||||
} else if(str == "about") {
|
||||
status = GAME_ABOUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 背景刷新
|
||||
cleardevice();
|
||||
|
||||
// 绘制背景
|
||||
drawAlpha(MImage::imageMap["Background"], 0, 0, WindowWidth, WindowHeight, 0, 0);
|
||||
// 绘制标题
|
||||
RECT r = {0, 0, WindowWidth, 200};
|
||||
setText("Acquaintance", WHITE, 100, 20);
|
||||
drawtext("Game2D", &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
||||
// 绘制按钮
|
||||
ui->draw(origin);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int WndProcGame(HINSTANCE hInstance, HWND hWnd) {
|
||||
static Map* map = loadMap("map");
|
||||
|
||||
// 游戏进程,开启计时器
|
||||
Timer* moveTimer = Timer::start(moveTimeID);
|
||||
|
||||
// 播放音乐
|
||||
Music::musicMap["game"]->play(true);
|
||||
|
||||
// 游戏内操作制造 50 毫秒延时
|
||||
if(moveTimer->onTime()) {
|
||||
// 记录移动前的位置
|
||||
POINT oPos = man->getPos();
|
||||
|
||||
// 跳跃
|
||||
if(GetAsyncKeyState(VK_SPACE)) {
|
||||
man->jump();
|
||||
}
|
||||
// 剑刺
|
||||
if(GetAsyncKeyState('J')) {
|
||||
man->sword();
|
||||
}
|
||||
// 暂时禁用护盾
|
||||
/*
|
||||
// 护盾
|
||||
if (GetAsyncKeyState('K'))
|
||||
{
|
||||
man->shield();
|
||||
}*/
|
||||
// 移动过程
|
||||
if(GetAsyncKeyState('A')) {
|
||||
// 碰撞检测
|
||||
man->setDir(DLeft);
|
||||
man->move();
|
||||
map->collision(man, DLeft);
|
||||
}
|
||||
if(GetAsyncKeyState('D')) {
|
||||
// 碰撞检测
|
||||
man->setDir(DRight);
|
||||
man->move();
|
||||
map->collision(man, DRight);
|
||||
}
|
||||
// 重力作用
|
||||
man->gravity();
|
||||
map->collision(man, DGravity);
|
||||
|
||||
// 地图本身的移动
|
||||
map->move(man);
|
||||
|
||||
// 调整窗口位置
|
||||
// 记录移动到的位置
|
||||
POINT nPos = man->getPos();
|
||||
moveDist.x += nPos.x - oPos.x;
|
||||
moveDist.y += nPos.y - oPos.y;
|
||||
|
||||
RECT mr = man->getRect();
|
||||
POINT mSize = {mr.right - mr.left, mr.bottom - mr.top};
|
||||
|
||||
// 横向移动
|
||||
// 向右
|
||||
if(nPos.x > oPos.x && (nPos.x - origin.x) + mSize.x > WindowWidth * 0.85) {
|
||||
origin.x = moveDist.x + mSize.x - WindowWidth * 0.85;
|
||||
} else if(nPos.x < oPos.x && (nPos.x - origin.x) < WindowWidth * 0.15) {
|
||||
origin.x = moveDist.x - WindowWidth * 0.15;
|
||||
}
|
||||
// 纵向移动
|
||||
if(nPos.y > oPos.y && (nPos.y - origin.y) + mSize.y > WindowHeight * 0.85) {
|
||||
origin.y = moveDist.y + mSize.y - WindowHeight * 0.85;
|
||||
} else if(nPos.y < oPos.y && (nPos.y - origin.y) < WindowHeight * 0.15) {
|
||||
origin.y = moveDist.y - WindowHeight * 0.15;
|
||||
}
|
||||
|
||||
// 绘图部分
|
||||
// 背景刷新
|
||||
cleardevice();
|
||||
|
||||
map->draw(origin); // 画一个简单的地图
|
||||
man->draw(origin); // 绘制人物
|
||||
// 调整原点和移动距离
|
||||
man->thinking(origin, moveDist);
|
||||
}
|
||||
|
||||
return GAME_START;
|
||||
}
|
||||