小白的学习记录
一、前置知识
注:博主是小白,所以记录的可能是一些无意中看到,但是不清楚的东西,所以大家择需了解。
JSON
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人类阅读和编写,同时也易于机器解析和生成。JSON 使用文本格式来表示结构化数据,通常用于在服务器和客户端之间传输数据,尤其是在 web 应用程序中。
JSON 的基本特性
-
简单易读:
- JSON 的语法结构简单,易于理解和使用。它的格式类似于 JavaScript 对象,因此对于熟悉 JavaScript 的开发者来说特别直观。
-
数据结构:
- JSON 支持两种数据结构:
- 对象:由键值对组成,使用花括号
{}
包围,键是字符串,值可以是字符串、数字、布尔值、数组、对象或null
。json
{ "name": "Alice", "age": 30, "isStudent": false }
- 数组:有序值的集合,使用方括号
[]
包围,可以包含多个值(对象或其他类型)。json
{ "students": [ {"name": "Alice", "age": 30}, {"name": "Bob", "age": 25} ] }
- 对象:由键值对组成,使用花括号
- JSON 支持两种数据结构:
-
数据类型:
- JSON 支持以下基本数据类型:
- 字符串(String):用双引号括起来的文本。
- 数字(Number):整数或浮点数。
- 布尔值(Boolean):
true
或false
。 - 数组(Array):值的有序集合。
- 对象(Object):键值对的无序集合。
- 空值(null):表示空值。
- JSON 支持以下基本数据类型:
-
语言无关性:
- JSON 是一种语言无关的数据格式,几乎所有编程语言都支持 JSON 的解析和生成,包括 JavaScript、Python、Java、C#、PHP 等。
JSON 的应用场景
-
Web API:
- JSON 是现代 Web 应用程序中常用的数据交换格式,尤其是在 RESTful API 中。客户端和服务器之间常使用 JSON 格式传输数据。
-
配置文件:
- JSON 常用于存储配置信息,比如应用程序的配置文件,因其易于阅读和编辑。
-
数据存储:
- 一些 NoSQL 数据库(如 MongoDB)使用 JSON(或类似格式)来表示和存储数据。
-
序列化:
- JSON 可以用于将数据结构序列化为字符串,以便于存储或通过网络传输。
JSON 示例
以下是一个完整的 JSON 示例,表示一个学生的信息:
json
{
"student": {
"name": "Alice",
"age": 20,
"isGraduate": false,
"courses": [
{"courseName": "Mathematics", "credits": 3},
{"courseName": "Physics", "credits": 4}
],
"address": {
"street": "123 Main St",
"city": "Anytown",
"zip": "12345"
}
}
}
JSON 的优缺点
优点:
- 轻量级:相比 XML 等其他格式,JSON 的数据结构更简洁,传输的数据量较小。
- 易读性:人类友好,易于理解和维护。
- 广泛支持:几乎所有编程语言都支持 JSON 数据的解析和生成。
缺点:
- 功能有限:JSON 不支持复杂的数据类型(如日期、时间)和注释。
- 不支持函数:JSON 仅用于数据表示,不能包含可执行的代码。
结论
JSON 已成为现代 Web 开发中广泛使用的数据交换格式,因其简单、灵活和易于使用而受到开发者的青睐。无论是在 API 开发、数据存储还是配置管理中,JSON 都发挥着重要作用。
SDL_Event event
SDL_Event event;
是 SDL (Simple DirectMedia Layer) 库中用于处理各种事件的一个声明。SDL_Event
是一个结构体,用于封装来自用户的输入事件以及系统产生的事件,如键盘按键、鼠标移动、窗口大小变化等。
SDL_Event
结构体
SDL_Event
结构体定义在SDL_events.h
头文件中,它包含了一个联合体(union)和一个事件类型字段,用于处理不同类型的事件。下面是SDL_Event
结构体的基本定义:1typedef struct _SDL_Event { 2 union { 3 // 用于存储不同类型的事件数据 4 // ... 5 } u; 6 7 Uint32 type; // 事件类型 8} SDL_Event;
事件类型
type
字段是一个Uint32
类型的成员,用于标识事件的类型。常见的事件类型包括但不限于:
SDL_QUIT
: 窗口关闭请求。SDL_KEYDOWN
和SDL_KEYUP
: 键盘按键按下和释放。SDL_MOUSEBUTTONDOWN
和SDL_MOUSEBUTTONUP
: 鼠标按钮按下和释放。SDL_MOUSEMOTION
: 鼠标移动。SDL_WINDOWEVENT
: 窗口事件,如大小变化、最小化等。SDL_JOYAXISMOTION
: 手柄轴运动。SDL_CONTROLLERBUTTONDOWN
和SDL_CONTROLLERBUTTONUP
: 控制器按钮按下和释放。SDL_AUDIODEVICEADDED
和SDL_AUDIODEVICEREMOVED
: 音频设备添加和移除。示例代码
下面是一个简单的示例,展示了如何使用
SDL_Event
结构体来处理事件:1#include <SDL.h> 2 3int main(int argc, char* argv[]) 4{ 5 if (SDL_Init(SDL_INIT_VIDEO) < 0) { 6 fprintf(stderr, "SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); 7 return 1; 8 } 9 10 SDL_Window* window = SDL_CreateWindow("Event Handling Example", 11 SDL_WINDOWPOS_CENTERED, 12 SDL_WINDOWPOS_CENTERED, 13 800, 600, 14 SDL_WINDOW_SHOWN); 15 if (window == NULL) { 16 fprintf(stderr, "Window could not be created! SDL_Error: %s\n", SDL_GetError()); 17 return 1; 18 } 19 20 SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 21 if (renderer == NULL) { 22 fprintf(stderr, "Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); 23 return 1; 24 } 25 26 bool quit = false; 27 SDL_Event event; 28 29 while (!quit) { 30 while (SDL_PollEvent(&event)) { 31 switch (event.type) { 32 case SDL_QUIT: 33 quit = true; 34 break; 35 case SDL_KEYDOWN: 36 printf("Key pressed: %d\n", event.key.keysym.sym); 37 break; 38 case SDL_MOUSEBUTTONDOWN: 39 printf("Mouse button pressed: %d\n", event.button.button); 40 break; 41 default: 42 break; 43 } 44 } 45 46 // 清除渲染器 47 SDL_RenderClear(renderer); 48 49 // 更新屏幕 50 SDL_RenderPresent(renderer); 51 } 52 53 // 清理 54 SDL_DestroyRenderer(renderer); 55 SDL_DestroyWindow(window); 56 SDL_Quit(); 57 58 return 0; 59}
总结
SDL_Event event;
是一个用于处理 SDL 事件的结构体声明。通过轮询事件队列 (SDL_PollEvent
) 或等待事件发生 (SDL_WaitEvent
),你可以检查事件的类型,并根据不同的事件类型采取相应的行动。SDL_Event
结构体是 SDL 中处理用户输入和系统事件的核心部分。
二、SDL2环境安装
可能遇到的问题
kerror LNK2019: 无法解析的外部符号 __imp___iob_func,该符号在函数 amqp_abort 中被引用,无法解析的外部符号 __imp_vfprintf-CSDN博客
SDL2链接报错:无法解析的外部符号 SDL_main,函数 main_getcmdline 中引用了该符号/main 已经在 main.obj 中定义_sdl库无法解析-CSDN博客
VS使用SDL2时LNK2019无法解析的外部符号_main_sdl2 无法解析的外部符号 main-CSDN博客
注意:如果已经感觉完全没问题了,但依旧报如下的错误{warning C4013: “SDL_Iint”未定义;假设外部返回 int} ,看看是不是写错了代码,比如字母写反了
SDL相关函数:
1.int SDL_Init(Uint32 flags) 作用:用来初始化SDL2.0
参数: flags:可以取下列值: SDL_INIT_TIMER:定时器 SDL_INIT_AUDIO:音频 SDL_INIT_VIDEO:视频 SDL_INIT_JOYSTICK:摇杆 SDL_INIT_HAPTIC:触摸屏 SDL_INIT_GAMECONTROLLER:游戏控制器 SDL_INIT_EVENTS:事件 SDL_INIT_NOPARACHUTE:不捕获关键信号(这个不理解) SDL_INIT_EVERYTHING:包含上述所有选项返回:0初始化成功,其他失败
2.const char* SDL_Geterror(void) 作用:当有错误信息发生时用来获取错误信息
返回值:错误信息
3.SDL_Window* SDL_CreateWindow(const char* title,
int x,
int y,
int w,
int h,
Uint32 flags
)
作用:用来创建一个窗口
参数: title 窗口标题x :窗口位置 x 坐标。可以设置为 SDL_WINDOWPOS_CENTERED 表示窗口居中显示y:窗口位置 y 坐标。可以设置为 SDL_WINDOWPOS_CENTERED 表示窗口居中显示w :窗口宽度h :窗口高度flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性 SDL_WINDOW_FULLSCREEN 窗口全屏 SDL_WINDOW_FULLSCREEN_DESKTOP 在当前分辨率的桌面上全屏 SDL_WINDOW_OPENGL OPENGL 下可用的窗口 SDL_WINDOW_HIDDEN 窗口不可见(隐藏窗口) SDL_WINDOW_BORDERLESS 没有窗口修饰(没有边框) SDL_WINDOW_RESIZABLE 窗口可以调整大小 SDL_WINDOW_MINIMIZED 窗口最小化 SDL_WINDOW_MAXIMIZED 窗口最大化 SDL_WINDOW_INPUT_GRABBED 窗口有输入焦点 (鼠标只能在窗口内) SDL_WINDOW_ALLOW_HIGHDPI 窗口应该创建在 high-DPI 模式下如果大于 SDL2.0.1 版本 SDL_WINDOW_SHOWN 窗口是可见的返回:成功返回指向 SDL_Window 的指针,失败返回 NULL
4、SDL_Renderer* SDL_CreateRenderer( SDL_Window* window,
int index,
Unit32 flags
)
作用:基于窗口创建渲染器参数: window : 渲染的目标窗口。index :打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备。flags:支持以下值(位于 SDL_RendererFlags 定义中) SDL_RENDERER_SOFTWARE :使用软件渲染 SDL_RENDERER_ACCELERATED :使用硬件加速(PS:用这个的时候要使用 SDL_CreateTextureFromSurface(SDL_Renderer* ,SDL_Surface* )建立一个硬件加速纹理,或者直接用 IMG_LoadTexture(SDL_Renderer* ,std::string filename) SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步 SDL_RENDERER_TARGETTEXTURE :渲染器支持渲染纹理返回:返回值为一个 SDL_Renderer 渲染器指针,成功返回一个指针,失败返回 NULL
5、 SDL_Texture* IMG_LoadTexture(
SDL_Renderer* render,
const char* file
)
作用:加载指定纹理参数: renderer :渲染器 file : 图片路径返回:返回值为一个 SDL_Texture 纹理指针,成功返回一个指针,失败返回 NULL
6、int SDL_RenderCopy(SDL_Renderer* renderer,SDL_Texture* texture,const SDL_Rect* srcrect,const SDL_Rect* dstrect)作用:将纹理数据复制给渲染目标参数: renderer:渲染目标texture:输入纹理srcrect:选择输入纹理的一块矩形区域作为输入。设置为 NULL 的时候整个纹理作为输入dstrect:选择渲染目标的一块矩形区域作为输出。设置为 NULL 的时候整个渲染目标作为输出返回:成功的话返回 0,失败的话返回-1
7. void SDL_RenderPresent(SDL_Renderer* renderer)
作用:更新显示画面
参数:renderer:用于指定渲染器
8. void SDL_Delay(Uint32 ms)
作用:执行到这个函数时会延迟你所设定的毫秒数
参数:ms:为32为(4个字节)无符号整型数,为延迟的毫秒数
9.void SDL_DestroyTexture(SDL_Texture* texture)
作用:使用这个函数摧毁指定的纹理,释放缓存
参数:textrue:创建过的需要释放的SDL_Texture的指针
10.void SDL_DestroyRenderer(SDL_Renderer* renderer)
作用:使用这个函数摧毁的渲染上下文窗口和自由相关的纹理,释放缓存
参数:render:创建过需要释放的SDL_Renderer指针
11.void SDL_DestroyWindow(SDL_Window* window)
作用:使用这个函数摧毁一个窗口,释放内存
参数 :window:创建过的需要释放的SDL_Window指针
void SDL_Quit(void)
作用:使用此函数来清理所有已初始化的子系统。在退出前调用它
相关结构体
SDL_Rect 说明:SDL_Rect定义了屏幕上的一个矩形区域
typedef struct SDL_Rect{
int x; //所表示区域的左上角位置,x坐标
int y //所表示区域的左上角位置,y坐标
int w; //区域宽度
int h; //区域高度
}
三、头文件(学习记录)
#pragma once
是一种预处理器指令,用于防止同一个头文件被多次包含。当一个头文件被包含时,编译器会检查该头文件是否已经被包含过,如果是,则不会再次包含该文件。这种方法在很多现代编译器中被广泛使用,尤其是在编写头文件时。
作用
#pragma once
的主要目的是避免头文件被重复包含,从而导致编译错误或效率降低。当一个头文件被多个源文件包含时,如果没有适当的保护措施,可能会导致以下问题:
-
多重定义错误:
- 如果一个头文件被多次包含,其中声明的变量或类型可能会被多次定义,导致编译错误。
-
编译效率降低:
- 重复包含相同的头文件会导致编译时间增加,因为编译器需要多次解析相同的内容。
使用示例
下面是一个使用 #pragma once
的简单示例:
1// my_header.h
2#pragma once
3
4void my_function();
与传统头文件保护的区别
传统的头文件保护通常使用预处理器宏来实现,例如:
1// my_header.h
2#ifndef MY_HEADER_H
3#define MY_HEADER_H
4
5void my_function();
6
7#endif // MY_HEADER_H
优缺点
优点:
- 简洁性:
#pragma once
更加简洁,不需要手动定义宏。 - 易于理解: 对于初学者来说,
#pragma once
更容易理解。
缺点:
- 兼容性: 不是所有编译器都支持
#pragma once
。尽管大多数现代编译器如 GCC, Clang 和 MSVC 都支持它,但在一些老旧的编译器中可能不被支持。 - 非标准:
#pragma
是编译器特定的预处理器指令,不是 C 或 C++ 标准的一部分。
关于<>和""
在C和C++编程语言中,
#include
指令用于引入其他文件,通常用来引入头文件。#include
指令有两种不同的形式来引用头文件:
角括号
<...>
:
- 用途:通常用于包含标准库头文件或系统头文件。
- 搜索路径:编译器会从标准系统路径开始搜索,这些路径通常指向系统安装的库文件所在的目录。
- 例子:
1#include <stdio.h>
双引号
"..."
:
- 用途:通常用于包含用户自定义的头文件或项目内的头文件。
- 搜索路径:编译器首先会从当前源文件所在的目录开始搜索,如果在当前目录找不到,才会去系统路径搜索。
- 例子:
1#include "myheader.h"
总结:
- 标准库/系统头文件:使用
<...>
,因为这些文件通常位于系统路径下,而且不随项目的不同而改变。- 用户自定义头文件:使用
"..."
,因为这些文件通常与你的源代码文件放在一起,编译器需要先在当前目录查找。这种区分有助于编译器更快地找到所需的文件,并且可以避免在项目结构发生变化时引发问题。例如,如果你更改了一个用户自定义头文件的位置,使用双引号形式的
#include
指令可能会导致编译失败,因为你可能需要更新包含路径。而对于标准库头文件,由于它们的位置是固定的,所以使用角括号形式的#include
指令可以避免此类问题。
四、过程记录
一、初步实现背景
注:尝试使用SDL,并非具体实现,具体实现应该分块,很好的利用头文件。
#define _CRT_SECURE_NO_WARNINGS
#define SDL_MAIN_HANDLED //把哪些奇奇怪怪的报错搞了
#include<SDL.h>
#include<stdio.h>
#include<SDL_image.h> //SDL2.0_image库头文件
int main(int argc, char* argv[]) {
SDL_Window* pWindow = NULL; //窗口
SDL_Renderer* pRenderer = NULL; //渲染器
SDL_Texture* pTexture = NULL; //纹理
SDL_Rect dstRect; //图片要显示的区域
/*初始化SDL库,参数:初始化所有子系统*/
if (0 != SDL_Init(SDL_INIT_EVERYTHING))
{
printf("SDL初始化失败: %s\n", SDL_GetError());
return -1;
}
printf("SDL初始化成功!\n");/*创建窗口*/
pWindow = SDL_CreateWindow("Snake", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 756/2, 899/2, SDL_WINDOW_SHOWN);
if (NULL == pWindow)
{
printf("创建窗口失败: %s\n", SDL_GetError());
return -2;
}
printf("创建窗口成功!\n");/*创建渲染器*/
pRenderer = SDL_CreateRenderer(pWindow, -1, SDL_RENDERER_ACCELERATED);
if (NULL == pRenderer)
{
printf("创建渲染器失败:%s\n", SDL_GetError());
return -3;
}
printf("创建渲染器成功!\n");/*加载图片并创建纹理*/
pTexture = IMG_LoadTexture(pRenderer, "..\\image\\background.png");
if (pTexture == NULL)
{
printf("加载图片失败:%s\n", IMG_GetError());
return -4;;
}
printf("加载图片成功!\n");/*设置图片要显示在窗口中的位置及大小*/
dstRect.x = 0;
dstRect.y = 0;
dstRect.w = 756/2;
dstRect.h = 899/2;/*将图片拷贝到渲染器中*/
SDL_RenderCopy(pRenderer, pTexture, NULL, &dstRect);
/*刷新渲染器,实现往窗口中绘制图片*/
SDL_RenderPresent(pRenderer);
/*延迟5秒*/
SDL_Delay(5000);
/*释放纹理*/
SDL_DestroyTexture(pTexture);
/*释放渲染器*/
SDL_DestroyRenderer(pRenderer);
/*释放窗口*/
SDL_DestroyWindow(pWindow);/*退出SDL库*/
SDL_Quit();
return 0;
}//main.c
二、一次集成初始化
#include "interface.h"
int main()
{
Picture pic;
//初始化SDL库、创建窗口、渲染器
if (0 != initSDL())
{
return -1;
}
//加载图片资源
if (0 != loadPicResources())
{
return -2;
}//绘制窗口
paint();
//延迟5秒
SDL_Delay(5000);//释放图片
freePicResources();//退出SDL库,释放窗口、渲染器
freeSDL();
return 0;
}//main.c
三、再次集成
#include "interface.h"
#include"logic.h"int main()
{
//初始化
init();
//运行
start();
//释放
gameFree();
return 0;
}
//main
思路整理
一、
int initSDL(void) //初始化SDL库、窗口、渲染器
{
//初始化SDL2.0开发库 SDL_Init(SDL_INIT_EVERYTHING)
//创建窗口
g_pWindow = SDL_CreateWindow("Snake", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,WINDOW_WIDTH,WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
//创建渲染器
g_pRenderer = SDL_CreateRenderer(g_pWindow, -1,SDL_RENDERER_ACCELERATED);
}
二、void freeSDL(void) //释放SDL库、窗口、渲染器
void freeSDL(void)
{
//释放渲染器
SDL_DestroyRenderer(g_pRenderer);
printf("渲染器释放!\n");//释放窗口
SDL_DestroyWindow(g_pWindow);
printf("窗口释放!\n");
//退出SDL2.0
SDL_Quit();
printf("SDL库已退出!\n");}
三、
// 名称:Picture loadPic(const char* path)
// 说明:加载一副图片
// 参数:path本地图片路径
// 返回:图片结构体Picture loadPic(const char* path)
{
Picture pic;
//加载一副图片
pic.pPic = IMG_LoadTexture(g_pRenderer, path);}
四、void setPicDstRect(Picture* picture, int x, int y, int w, int h)
//目标区域的映射大小,xy是对左上角的坐标,wh分别是宽高
五、void setPicSrcRect(Picture* picture, int x, int y, int w, int h)
//源图像的映大小,同上
{
picture->srcRect.x = x;
picture->srcRect.y = y;
if (0 == w || 0 == h)
{
SDL_QueryTexture(picture->pPic, NULL, NULL, &(picture->srcRect.w), &(picture->srcRect.h));
}
else
{
picture->srcRect.w = w;
picture->srcRect.h = h;
}
六、具体加载
int loadPicResources(void)
{
//加载背景图片
g_picBackGround = loadPic(PATH_BACKGROUND);
if (g_picBackGround.pPic == NULL)
{
return -1;
}
setPicSrcRect(&g_picBackGround, 0, 0, 0, 0);
setPicDstRect(&g_picBackGround, BACKGROUND_PICI_X, BACKGROUND_PICI_Y, BACKGROUND_PICI_W, BACKGROUND_PICI_H);
........//其他图片加载类似
}
void freePicResources(void) //释放图片加载资源
{
SDL_DestroyTexture(g_picBackGround.pPic);
.......//其他释放类似
}
//往渲染器加载图片
void paintPic(Picture* picture)
{
SDL_RenderCopy(g_pRenderer, picture->pPic, &(picture->srcRect), &(picture->dstRect));}
void paint(void)
{
paintPic(&g_picBackGround);
//更新渲染器图像
SDL_RenderPresent(g_pRenderer);
}
//绘制窗口
接下来就是具体实现了