This commit is contained in:
linianlin 2026-04-01 15:26:45 +08:00
commit c53e03ebee
194 changed files with 52563 additions and 0 deletions

View File

@ -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 #每行长度限制

5
Aircraft-Battle/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# settings
.vs
.vscode
build/
out/

View File

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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;
};

View File

@ -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>

View File

@ -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); // 画血条

View File

@ -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) {}
};

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}
}

43
Game2D/.clang-format Normal file
View File

@ -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 #每行长度限制

5
Game2D/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# settings
.vs
.vscode
build/
out/

17
Game2D/CMakelists.txt Normal file
View File

@ -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)

142
Game2D/data/README.txt Normal file
View File

@ -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 属性 减损

View File

@ -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

View File

@ -0,0 +1,7 @@
[ 事件类型存放在这里 ]
{
create ground 0 0
}
end

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
Game2D/data/image/enemy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

BIN
Game2D/data/image/man.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
Game2D/data/man.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Binary file not shown.

87
Game2D/include/Config.h Normal file
View File

@ -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
}
}
}
}

73
Game2D/include/GDynamic.h Normal file
View File

@ -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) {}
};

46
Game2D/include/GStatic.h Normal file
View File

@ -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;
};

3
Game2D/include/Game2D.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
#include "map.h"

39
Game2D/include/Man.h Normal file
View File

@ -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() {}
};

37
Game2D/include/enemy.h Normal file
View File

@ -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;
}
}
};

View File

@ -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") // 加载静态库

26
Game2D/include/map.h Normal file
View File

@ -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); // 返回按下的结果
};

81
Game2D/include/object.h Normal file
View File

@ -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

211
Game2D/include/struct.h Normal file
View File

@ -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; // 数值表
};

307
Game2D/src/Game2D.cpp Normal file
View File

@ -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;
}

Some files were not shown because too many files have changed in this diff Show More