[ESC] EnTT 学习记录 1

1.1 Pacman

1.1.1 准备

源码:

Kerndog73/EnTT-Pacman: An example of how to use … - GitHub
https://github.com/Kerndog73/EnTT-Pacman

安装 vcpkg:

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat

参考:

vcpkg安装遇到的问题记录 - JK-Z - 博客园
https://www.cnblogs.com/JK-Z/p/12264762.html

安装工程:

git clone https://github.com/Kerndog73/EnTT-Pacman.git
cd EnTT-Pacman/build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
./pacman

使用命令行是不太靠谱,很容易克隆不到……
最好还是先下载 zip,然后 cd 到解压出来的库里面

PS D:\Tools\vcpkg-master> .\vcpkg install sdl2
Computing installation plan...
The following packages will be built and installed:
    sdl2[core]:x86-windows -> 2.0.20
  * vcpkg-cmake[core]:x64-windows -> 2022-01-19
  * vcpkg-cmake-config[core]:x64-windows -> 2022-02-06
Additional packages (*) will be modified to complete this operation.
Detecting compiler hash for triplet x64-windows...
A suitable version of git was not found (required v2.35.1). Downloading portable git v2.35.1...
Extracting git...
A suitable version of 7zip was not found (required v19.0.0). Downloading portable 7zip v19.0.0...
Downloading 7zip...
  https://www.7-zip.org/a/7z1900-x64.msi -> D:\Tools\vcpkg-master\downloads\7z1900-x64.msi
Extracting 7zip...
-- Automatically setting HTTP(S)_PROXY environment variables to 127.0.0.1:7890
A suitable version of powershell-core was not found (required v7.2.1). Downloading portable powershell-core v7.2.1...
Downloading powershell-core...
  https://github.com/PowerShell/PowerShell/releases/download/v7.2.1/PowerShell-7.2.1-win-x86.zip -> D:\Tools\vcpkg-master\downloads\PowerShell-7.2.1-win-x86.zip
Extracting powershell-core...
Detecting compiler hash for triplet x86-windows...
Restored 0 packages from C:\Users\18221\AppData\Local\vcpkg\archives in 654.9 us. Use --debug to see more details.
Starting package 1/3: vcpkg-cmake:x64-windows
Building package vcpkg-cmake[core]:x64-windows...
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/vcpkg_cmake_configure.cmake
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/vcpkg_cmake_build.cmake
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/vcpkg_cmake_install.cmake
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/vcpkg_cmake_get_vars.cmake
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/cmake_get_vars
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/cmake_get_vars/CMakeLists.txt
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/vcpkg-port-config.cmake
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake_x64-windows/share/vcpkg-cmake/copyright
-- Performing post-build validation
-- Performing post-build validation done
Stored binary cache: C:\Users\18221\AppData\Local\vcpkg\archives\46\462fcc355ce001e0fc8be8a27307ded83503a18a3c16d1a10473fc3036040c32.zip
Installing package vcpkg-cmake[core]:x64-windows...
Elapsed time for package vcpkg-cmake:x64-windows: 633.7 ms
Starting package 2/3: vcpkg-cmake-config:x64-windows
Building package vcpkg-cmake-config[core]:x64-windows...
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake-config_x64-windows/share/vcpkg-cmake-config/vcpkg_cmake_config_fixup.cmake
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake-config_x64-windows/share/vcpkg-cmake-config/vcpkg-port-config.cmake
-- Installing: D:/Tools/vcpkg-master/packages/vcpkg-cmake-config_x64-windows/share/vcpkg-cmake-config/copyright
-- Performing post-build validation
-- Performing post-build validation done
Stored binary cache: C:\Users\18221\AppData\Local\vcpkg\archives\38\38dcd680d8ba565bcf7347013545e6e6695b723804eea0dba27afb4323071cd9.zip
Installing package vcpkg-cmake-config[core]:x64-windows...
Elapsed time for package vcpkg-cmake-config:x64-windows: 636.3 ms
Starting package 3/3: sdl2:x86-windows
Building package sdl2[core]:x86-windows...
-- Downloading https://github.com/libsdl-org/SDL/archive/release-2.0.20.tar.gz -> libsdl-org-SDL-release-2.0.20.tar.gz...
-- Extracting source D:/Tools/vcpkg-master/downloads/libsdl-org-SDL-release-2.0.20.tar.gz
-- Applying patch 0001-sdl2-Enable-creation-of-pkg-cfg-file-on-windows.patch
-- Applying patch 0002-sdl2-skip-ibus-on-linux.patch
-- Applying patch 0003-sdl2-disable-sdlmain-target-search-on-uwp.patch
-- Applying patch 0004-Define-crt-macros.patch
-- Applying patch 0005-Fix-uwp-joystick.patch
-- Applying patch 0006-Update-SDL_sysurl.cpp.patch
-- Using source at D:/Tools/vcpkg-master/buildtrees/sdl2/src/ase-2.0.20-4d7f28b96f.clean
-- Configuring x86-windows
-- Building x86-windows-dbg
-- Building x86-windows-rel
CMake Warning at scripts/cmake/vcpkg_copy_pdbs.cmake:70 (message):
  Could not find a matching pdb file for:

      D:/Tools/vcpkg-master/packages/sdl2_x86-windows/bin/SDL2.dll
      D:/Tools/vcpkg-master/packages/sdl2_x86-windows/debug/bin/SDL2d.dll

Call Stack (most recent call first):
  ports/sdl2/portfile.cmake:87 (vcpkg_copy_pdbs)
  scripts/ports.cmake:145 (include)


-- Fixing pkgconfig file: D:/Tools/vcpkg-master/packages/sdl2_x86-windows/lib/pkgconfig/sdl2.pc
-- Downloading https://repo.msys2.org/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst;https://www2.futureware.at/~nickoe/msys2-mirror/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst;https://mirror.yandex.ru/mirrors/msys2/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst;https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst;https://mirrors.ustc.edu.cn/msys2/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst;https://mirror.bit.edu.cn/msys2/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst;https://mirror.selfnet.de/msys2/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst;https://mirrors.sjtug.sjtu.edu.cn/msys2/mingw/i686/mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst -> msys-mingw-w64-i686-pkg-config-0.29.2-3-any.pkg.tar.zst...
-- Downloading https://repo.msys2.org/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst;https://www2.futureware.at/~nickoe/msys2-mirror/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst;https://mirror.yandex.ru/mirrors/msys2/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst;https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst;https://mirrors.ustc.edu.cn/msys2/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst;https://mirror.bit.edu.cn/msys2/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst;https://mirror.selfnet.de/msys2/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst;https://mirrors.sjtug.sjtu.edu.cn/msys2/mingw/i686/mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst -> msys-mingw-w64-i686-libwinpthread-git-9.0.0.6373.5be8fcd83-1-any.pkg.tar.zst...
-- Using msys root at D:/Tools/vcpkg-master/downloads/tools/msys2/9a1ec3f33446b195
-- Fixing pkgconfig file: D:/Tools/vcpkg-master/packages/sdl2_x86-windows/debug/lib/pkgconfig/sdl2.pc
-- Performing post-build validation
-- Performing post-build validation done
Stored binary cache: C:\Users\18221\AppData\Local\vcpkg\archives\a5\a57c81eb0679aa96746a5c5f6b56b2582c124e3be3b136032ab41b3bb702c2f1.zip
Installing package sdl2[core]:x86-windows...
Elapsed time for package sdl2:x86-windows: 2.912 min

Total elapsed time: 7.466 min

The package sdl2 provides CMake targets:

    find_package(SDL2 CONFIG REQUIRED)
    target_link_libraries(main PRIVATE SDL2::SDL2 SDL2::SDL2main)

我还以为我没下载成功,毕竟看到了一个 warning
但是之后我再次下载的时候又提示我已经下好了

Computing installation plan...
The following packages are already installed:
    sdl2[core]:x86-windows -> 2.0.20
Package sdl2:x86-windows is already installed
Restored 0 packages from C:\Users\18221\AppData\Local\vcpkg\archives in 282.8 us. Use --debug to see more details.

Total elapsed time: 50.33 ms

The package sdl2 provides CMake targets:

    find_package(SDL2 CONFIG REQUIRED)
    target_link_libraries(main PRIVATE SDL2::SDL2 SDL2::SDL2main)

在这里插入图片描述

但是真正去编译的时候又跟我说没有

PS D:\Tools\vcpkg-master> cd D:\Documents\ECSProject\EnTT-Pacman-master\build
PS D:\Documents\ECSProject\EnTT-Pacman-master\build> cmake -DCMAKE_BUILD_TYPE=Release ..
-- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.22000.
Finding SDL2
CMake Error at D:/Tools/CMake/share/cmake-3.23/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  Could NOT find SDL2 (missing: SDL2_LIBRARY SDL2_INCLUDE_DIR)
Call Stack (most recent call first):
  D:/Tools/CMake/share/cmake-3.23/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)
  cmake/modules/FindSDL2.cmake:173 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
  CMakeLists.txt:73 (find_package)


-- Configuring incomplete, errors occurred!
See also "D:/Documents/ECSProject/EnTT-Pacman-master/build/CMakeFiles/CMakeOutput.log".
See also "D:/Documents/ECSProject/EnTT-Pacman-master/build/CMakeFiles/CMakeError.log".

我也加好了 vcpkg 的环境变量
sdl2 的环境变量我也试过了 ...\vcpkg-master\packages\sdl2_x86-windows 或者 ...\vcpkg-master\packages\sdl2_x86-windows\bin

之后我看那个 FindSDL2.cmake 中好像需要 SDL2_PATH,我就建了个,也还是不行
我再看 missing: SDL2_LIBRARY SDL2_INCLUDE_DIR,感觉又可能是需要建这两个

在这里插入图片描述

结果还是不行

指令果然又靠不住啊……还是用 GUI 吧

在这里插入图片描述

构建了之后在本地调试,报错说没找到 sdl2 的头文件
似乎头文件的文件夹还要再深一层

在这里插入图片描述

再本地调试,没头文件问题了,但是还是有别的问题

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK1107	文件无效或损坏: 无法在 0x2D8 处读取	pacman	D:\Tools\vcpkg-master\packages\sdl2_x86-windows\bin\SDL2.dll	1	

看上去是 sdl2 本身的问题啊
哦,不对,这个是我的 SDL2MAIN_LIBRARY 选的,说明我其实不应该选它
思考……

在这里插入图片描述

Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.22000.
Finding SDL2
Configuring done
CMake Warning at CMakeLists.txt:79 (target_link_libraries):
  Target "pacman" requests linking to directory
  "D:/Tools/vcpkg-master/packages/sdl2_x86-windows/lib".  Targets may link
  only to libraries.  CMake is dropping the item.


CMake Warning at CMakeLists.txt:79 (target_link_libraries):
  Target "pacman" requests linking to directory
  "D:/Tools/vcpkg-master/packages/sdl2_x86-windows/lib".  Targets may link
  only to libraries.  CMake is dropping the item.


CMake Warning at CMakeLists.txt:79 (target_link_libraries):
  Target "pacman" requests linking to directory
  "D:/Tools/vcpkg-master/packages/sdl2_x86-windows/lib".  Targets may link
  only to libraries.  CMake is dropping the item.


CMake Warning at CMakeLists.txt:79 (target_link_libraries):
  Target "pacman" requests linking to directory
  "D:/Tools/vcpkg-master/packages/sdl2_x86-windows/lib".  Targets may link
  only to libraries.  CMake is dropping the item.


Generating done

这样连会报错,难道 SDL2_LIBRARY 指的不是 lib 文件夹而是整个 sdl2?
一个个试:
.../vcpkg-master/packages/sdl2_x86-windows 不行
.../vcpkg-master/packages/sdl2_x86-windows/lib 不行
.../vcpkg-master/packages/sdl2_x86-windows/lib/SDL2.lib 不行
.../vcpkg-master/packages/sdl2_x86-windows/bin/SDL2.dll 不行

啊……那就真不知道他到底要什么了

我感觉果然还是取 .lib 的路径最靠谱
但是他的报错是这样子的

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK2019	无法解析的外部符号 SDL_UpdateTexture,函数 "class std::unique_ptr<struct SDL_Texture,struct SDL::DeleteTexture> __cdecl SDL::loadTexture(struct SDL_Renderer *,unsigned char const *,unsigned __int64)" (?loadTexture@SDL@@YA?AV?$unique_ptr@USDL_Texture@@UDeleteTexture@SDL@@@std@@PEAUSDL_Renderer@@PEBE_K@Z) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\sdl_load_texture.obj	1	
错误	LNK2019	无法解析的外部符号 main,函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\MSVCRTD.lib(exe_main.obj)	1	
错误	LNK2019	无法解析的外部符号 SDL_CreateRenderer,函数 "public: void __cdecl Application::run(void)" (?run@Application@@QEAAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_CreateTexture,函数 "class std::unique_ptr<struct SDL_Texture,struct SDL::DeleteTexture> __cdecl SDL::loadTexture(struct SDL_Renderer *,unsigned char const *,unsigned __int64)" (?loadTexture@SDL@@YA?AV?$unique_ptr@USDL_Texture@@UDeleteTexture@SDL@@@std@@PEAUSDL_Renderer@@PEBE_K@Z) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\sdl_load_texture.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_CreateWindow,函数 "public: void __cdecl Application::run(void)" (?run@Application@@QEAAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_DestroyRenderer,函数 "public: void __cdecl SDL::DeleteRenderer::operator()(struct SDL_Renderer *)const " (??RDeleteRenderer@SDL@@QEBAXPEAUSDL_Renderer@@@Z) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2001	无法解析的外部符号 SDL_DestroyTexture	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\sdl_load_texture.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_DestroyTexture,函数 "public: void __cdecl SDL::DeleteTexture::operator()(struct SDL_Texture *)const " (??RDeleteTexture@SDL@@QEBAXPEAUSDL_Texture@@@Z) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_DestroyWindow,函数 "public: void __cdecl SDL::DeleteWindow::operator()(struct SDL_Window *)const " (??RDeleteWindow@SDL@@QEBAXPEAUSDL_Window@@@Z) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_GetDisplayUsableBounds,函数 "int __cdecl `anonymous namespace'::getScaleFactor(void)" (?getScaleFactor@?A0x04216aae@@YAHXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2001	无法解析的外部符号 SDL_GetError	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\sdl_load_texture.obj	1	
错误	LNK2001	无法解析的外部符号 SDL_GetError	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\sdl_quad_writer.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_GetError,函数 "void __cdecl SDL::raise(void)" (?raise@SDL@@YAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_Init,函数 "public: __cdecl Application::Application(void)" (??0Application@@QEAA@XZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_PollEvent,函数 "public: void __cdecl Application::run(void)" (?run@Application@@QEAAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_Quit,函数 "public: __cdecl Application::~Application(void)" (??1Application@@QEAA@XZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_RenderClear,函数 "public: void __cdecl Application::run(void)" (?run@Application@@QEAAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_RenderCopyEx,函数 "public: void __cdecl SDL::QuadWriter::render(void)const " (?render@QuadWriter@SDL@@QEBAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\sdl_quad_writer.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_RenderPresent,函数 "public: void __cdecl Application::run(void)" (?run@Application@@QEAAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_RenderSetLogicalSize,函数 "public: void __cdecl Application::run(void)" (?run@Application@@QEAAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_SetRenderDrawColor,函数 "public: void __cdecl Application::run(void)" (?run@Application@@QEAAXXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\app.obj	1	
错误	LNK2019	无法解析的外部符号 SDL_SetTextureBlendMode,函数 "class std::unique_ptr<struct SDL_Texture,struct SDL::DeleteTexture> __cdecl SDL::loadTexture(struct SDL_Renderer *,unsigned char const *,unsigned __int64)" (?loadTexture@SDL@@YA?AV?$unique_ptr@USDL_Texture@@UDeleteTexture@SDL@@@std@@PEAUSDL_Renderer@@PEBE_K@Z) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\sdl_load_texture.obj	1	
错误	LNK1120	19 个无法解析的外部命令	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\Debug\pacman.exe	1	
警告	LNK4272	库计算机类型“x86”与目标计算机类型“x64”冲突	pacman	D:\Tools\vcpkg-master\packages\sdl2_x86-windows\lib\SDL2.lib	1	

我试试换成我自己下载的 sdl2

https://www.libsdl.org/download-2.0.php

在这里插入图片描述

在这里插入图片描述

这回好了……我才发现自己下载的 sdl2 的 lib 文件有区分 main,这才是环境变量区分 MAIN 的意义啊……vcpkg 你怎么回事

但是之后还有问题……

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK2019	无法解析的外部符号 main,函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\MSVCRTD.lib(exe_main.obj)	1	
错误	LNK1120	1 个无法解析的外部命令	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\Debug\pacman.exe	1	
警告	C4267	“参数”: 从“size_t”转换到“int”,可能丢失数据	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\src\util\sdl_load_texture.cpp	36	

挺无敌的,main 函数都识别不了

根据这个改了

https://blog.csdn.net/weixin_45189666/article/details/116563083

在这里插入图片描述

然后居然又出来了 sdl2 的问题……我无语了,还以为我搞定了

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK1107	文件无效或损坏: 无法在 0x2D8 处读取	pacman	D:\Tools\vcpkg-master\packages\sdl2_x86-windows\bin\SDL2.dll	1	
警告	C4267	“参数”: 从“size_t”转换到“int”,可能丢失数据	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\src\util\sdl_load_texture.cpp	36	

但是首先确定我这个 sdl2 库是没问题的

cmake 用的 sdl2.dll 是 vcpkg 的,gui 里面没地方给我填 dll 的路径,但是我 lib 的路径全都填的自己的
如果真的是 vcpkg 的问题的话,那我应该 dll 也应该用自己的,可是我改不了
怎么会有这种操作啊,简直无敌

在这里插入图片描述

CMake 里面都没给出来的变量,它自己就配好了,我觉得这就是有东西干扰了
刚好我看了工程里面的 cmake 文件夹里面有它自己的寻找 SDL2 的文件,我不知道我在用 gui 的时候这个文件会不会起什么作用

在这里插入图片描述

这个文件的内容在谷歌上面很常见

在这里插入图片描述

我在想,如果我 CMake 里面指定了 dll 的位置,那么我会不会覆盖掉其他所有设置……

于是我新建了一个 SDL2_PATH

在这里插入图片描述

然后报错

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	LNK2019	无法解析的外部符号 main,函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中引用了该符号	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\MSVCRTD.lib(exe_main.obj)	1	
错误	LNK1120	1 个无法解析的外部命令	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\build\Debug\pacman.exe	1	
警告	C4267	“参数”: 从“size_t”转换到“int”,可能丢失数据	pacman	D:\Documents\ECSProject\EnTT-Pacman-master\src\util\sdl_load_texture.cpp	36	

那就把之前改过的东西改回来

哦……看了一下,重新生成之后,链接器都会变回控制台
嗯……
之后改了好多的链接器都不行……难道这个游戏工程的入口很特别?

在这里插入图片描述

我感觉好像也没问题啊……

然后我按照这个方法 4 来

https://www.cnblogs.com/eve612/p/13823073.html

原先是这样

在这里插入图片描述

然后改成这样

在这里插入图片描述

没用捏……

哦,有没有一种可能,我不能用 visualstudio
毕竟游戏项目的首界面在 cmake 构建完之后直接输入指令开启游戏的
让我试试

在这里插入图片描述

好吧,我放弃了

1.1.2 脑运行

一开始是 app,然后 run(),那就看 Application

然后可以发现有挺多 sdl 的 api 的……我觉得我应该尽量不去深究这些 api,知道作用就好了……

这个 run() 的主循环好厉害
使用了一个计数器实现逻辑帧和渲染帧的分离

  while (!quit) {
    FrameCap sync{fps};

    SDL_Event e;
    while (SDL_PollEvent(&e)) {
      if (e.type == SDL_QUIT) {
        quit = true;
        break;
      } else if (e.type == SDL_KEYDOWN) {
        game.input(e.key.keysym.scancode);
      }
    }

    // Game::logic is called once for each tile
    // Game::render is called for each pixel between tiles
    if (frame % tileSize == 0) {
      if (!game.logic()) {
        quit = true;
      }
    }

    SDL_CHECK(SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, 255));
    SDL_CHECK(SDL_RenderClear(renderer.get()));
    game.render(writer, frame % tileSize);
    ++frame;
    SDL_RenderPresent(renderer.get());
  }

然后 sdl 也有内置的事件机制哦

逻辑还在 game 里面,那就看 Game

const entt::entity player = makePlayer(reg);

首先是,实体类就是 entt::entity

entt::entity makePlayer(entt::registry &reg) {
  const entt::entity e = reg.create();
  reg.emplace<Player>(e);
  reg.emplace<DesiredDir>(e, playerSpawnDir);
  reg.emplace<ActualDir>(e, playerSpawnDir);
  reg.emplace<Position>(e, playerSpawnPos);
  reg.emplace<PlayerSprite>(e, animera::SpriteID::pacman_beg_);
  return e;
}

然后组件是用 entt::registry 存放的
组件本身就是一个结构体,声明放在头文件中,不需要定义

const entt::entity blinky = makeBlinky(reg, player);

创建实体就在一个仓库中 .create(),返回一个 entt::entity

可以使用实体作为另一个实体的组件

bool Game::logic() {
  // The order of systems is very important in an ECS. Each system reads some
  // state and modifies some state. If the state isn't read and modified in the
  // right order, subtle bugs can occur. Make sure that the order of systems is
  // easy to see (i.e. not hidden away by some abstraction that sets the order
  // for you). Always think carefully about the order that systems should be in.

  // It's OK to keep some game state outside of the ECS (e.g. maze, dots,
  // dotSprite) but try to keep as much state within the ECS as you can.
  // Keeping too much state outside of the ECS can lead to problems.
  // For example: `dots` is the amount of dots eaten by the player. If there
  // were more than one player, then each player might want to keep track of how
  // many dots they've eaten. So `dots` would have to be moved into a component

  if (state != State::playing) {
    return true;
  }

  if (scattering) {
    if (ticks >= scatterTicks) {
      ghostChase(reg);
      ticks = 0;
      scattering = false;
    }
  } else {
    if (ticks >= chaseTicks) {
      ghostScatter(reg);
      ticks = 0;
      scattering = true;
    }
  }
  ++ticks;

  movement(reg);
  wallCollide(reg, maze);
  dots += eatDots(reg, maze);
  if (eatEnergizer(reg, maze)) {
    ghostScared(reg);
  }
  ghostScaredTimeout(reg);
  enterHouse(reg);
  setBlinkyChaseTarget(reg);
  setPinkyChaseTarget(reg);
  setInkyChaseTarget(reg);
  setClydeChaseTarget(reg);
  setScaredTarget(reg, maze, rand);
  setScatterTarget(reg);
  setEatenTarget(reg);
  leaveHouse(reg);
  pursueTarget(reg, maze);

  const GhostCollision collision = playerGhostCollide(reg);
  if (collision.type == GhostCollision::Type::eat) {
    ghostEaten(reg, collision.ghost);
  }
  if (collision.type == GhostCollision::Type::lose) {
    state = State::lost;
  } else if (dots == dotsInMaze) {
    state = State::won;
  }
  return true;
}

主逻辑就是所有系统排列下来

值得注意的是,接受输入的系统和逻辑中顺序排列的系统是分开的

void Game::input(const SDL_Scancode key) {
  if (state == State::playing) {
    playerInput(reg, key);
  }
}

挨个挨个看吧

运动系统

void movement(entt::registry &reg) {
  auto view = reg.view<Position, ActualDir>();
  for (const entt::entity e : view) {
    Pos &pos = view.get<Position>(e).p;
    const Dir dir = view.get<ActualDir>(e).d;
    pos += toPos(dir);

    // The tunnel.
    // This assumes the exact position of the tunnel.
    // It's good enough for this simple game but a more robust solution might
    // involve making the tunnel into an entity
    if (pos.y == 10) {
      if (pos.x <= -1 && dir == Dir::left) {
        pos.x = 19;
      } else if (pos.x >= 19 && dir == Dir::right) {
        pos.x = -1;
      }
    }
  }
}

取到 gamereg,这个 reg 应该是存着场景中所有实体引用
刚开始看,也没看到内部修改 gamereg 的逻辑,全是引用传下去各个系统里面改hhh,不管了

.view<T>() 用来取 registry 中包含这些组件的实体

已知实体,使用 view 类的方法 .get<T>() 获得组件
为啥不能直接访问实体的 reg 呢……估计 view 类中写好的方法更好吧

撞墙检测

void wallCollide(entt::registry &reg, const MazeState &maze) {
  auto view = reg.view<Position, ActualDir, DesiredDir>();
  for (const entt::entity e : view) {
    const Pos pos = view.get<Position>(e).p;
    const Dir desiredDir = view.get<DesiredDir>(e).d;
    if (canMove(reg, maze, e, pos, desiredDir)) {
      view.get<ActualDir>(e).d = desiredDir;
      continue;
    }

    const Dir prevDir = view.get<ActualDir>(e).d;
    if (canMove(reg, maze, e, pos, prevDir)) {
      continue;
    }

    view.get<ActualDir>(e).d = Dir::none;
  }
}

如果向期望运动方向上运动不会发生碰撞,那么实际运动方向就是期望运动方向
如果向期望运动方向上运动会发生碰撞,就抛弃期望运动方向,检查上一次的实际运动方向,如果向上一次的实际运动方向上运动不会发生碰撞,那么实际运动方向就是上一次的实际运动方向;否则实际运动方向为零

能变速就变速,不能变速就原速,不能原速就停

吃豆

一开始看到还以为是,maze 是一个 PosTileMap
然后去看 maze,发现是个 Grid 类重载了 [] 而已

public:
  Grid()
    : size{0, 0}, storage{} {}
private:
  Pos size;
  std::vector<Elem> storage;

一开始看到这个还以为是使用了两个 {}storage 初始化
我还以为是什么神奇的新特性,原来后面那个花括号时空的函数体hhhh

他这个运算符重载还挺厉害的

  Elem &operator[](const std::size_t i) {
    return const_cast<Elem &>(std::as_const(*this)[i]);
  }

  Elem &operator[](const Pos pos) {
    return const_cast<Elem &>(std::as_const(*this)[pos]);
  }

  const Elem &operator[](const std::size_t i) const {
    assert(!outOfRange(i));
    return storage[i];
  }

  const Elem &operator[](const Pos pos) const {
    assert(!outOfRange(pos));
    return storage[pos.y * size.x + pos.x];
  }

先传给操作常量的函数,然后再用 const_cast 把常量返回值的 const 去掉
const 函数中,因为 *thisstd::as_const 视为常量,所以接下来会跳转到 const 的重载函数
这样就完成了 const 函数的封装

一个地图格有不同类型,比如食物格、能量格或者空格
小游戏嘛,就直接把物品绑定到格子上了

如果吃到了豆子,那么所有鬼都会进入受惊模式,已经处于受惊模式的鬼的受惊计时器会延长剩余时间
碰到了处于受惊模式的鬼就可以把这个鬼吃掉

//
//  change_ghost_mode.hpp
//  EnTT Pacman
//
//  Created by Indiana Kernick on 29/9/18.
//  Copyright © 2018 Indiana Kernick. All rights reserved.
//

#ifndef SYS_CHANGE_GHOST_MODE_HPP
#define SYS_CHANGE_GHOST_MODE_HPP

#include <entt/entity/fwd.hpp>

// This is called when pacman eats an energizer. All ghosts in chase or scatter
// mode will be put into scared mode. Ghosts that are already in scared mode
// will have there scared timer extended
void ghostScared(entt::registry &);

// Ghosts in scared mode have a timer. When the timer runs out, they will enter
// chase mode.
void ghostScaredTimeout(entt::registry &);

// This is called when pacman collides with a ghost that is in scared mode.
// This system puts the ghost into eaten mode
void ghostEaten(entt::registry &, entt::entity);

// Puts all of the ghosts into scatter mode
void ghostScatter(entt::registry &);

// Puts all of the ghost into chase mode
void ghostChase(entt::registry &);

#endif

所以我还挺好奇计时器在 ecs 中是什么样的
毕竟应该是需要反射的而不是 update 的

void ghostScaredTimeout(entt::registry &reg) {
  auto view = reg.view<Ghost, ScaredMode>();
  for (const entt::entity e : view) {
    ScaredMode &scared = view.get<ScaredMode>(e);
    --scared.timer;
    if (scared.timer <= 0) {
      // Adding and removing components from the entity that is currently
      // returned by the view is OK
      reg.remove<ScaredMode>(e);
      reg.emplace<ChaseMode>(e);
    }
  }
}

现在知道了,他这个计时器跟逻辑有关的话,就是经过的逻辑帧的数量代表经过的时间

void ghostScatter(entt::registry &reg) {
  const auto view = reg.view<Ghost, ChaseMode>();
  for (const entt::entity e : view) {
    reg.remove<ChaseMode>(e);
    reg.emplace<ScatterMode>(e);
  }
}

然后他这个鬼的行动模式也是做成了一个组件,而且是每一个模式是一个组件,比如这个切换模式就是加一个组件减一个组件
这样的话,到后期组件会很多吧……我看那个守望先锋的讲座上面展示的组件也不过是二十几个大概吧
不过现在小游戏也什么

啊……之后好像差不多了hhh

1.2 CrashCourse

这个看上去像是百科全书……但是看过一遍简单的项目之后应该大概似乎能看懂……吧

https://entt.docsforge.com/master/configuration/

不行,我还是看不懂
我觉得这名义上说是速成课,实际上还是需要真正理解到项目需求才能知道他在做什么我觉得我还是要找些别的来看

1.3 godot_entt_example

Github 地址:
https://github.com/portaloffreedom/godot_entt_example

用 3.4 的版本打开这个项目好像用不了,在 godot 里面看不到脚本
可能也是因为我没用过 gdnative 的原因

直接看代码吧

view.each 中使用 callback 的用法

registry.view<position, velocity, Spatial*>().each([delta](position &pos, velocity &vel, Spatial* s_entity) {
        pos.x += vel.dx * delta;
        pos.y += vel.dy * delta;
        pos.z += vel.dz * delta;
        s_entity->set_translation(Vector3(pos.x, pos.y, pos.z));
    });

view 写到了条件语句里面,紧凑的写法

for(auto &entity: registry.view<position, Spatial*>())
    {
        const float arena_size = 36.0f;
        auto &pos = registry.get<position>(entity);
        if (fabs(pos.x) > arena_size or fabs(pos.y) > arena_size)
        {
            registry.get<Spatial*>(entity)->queue_free();
            registry.destroy(entity);
        }
    }

queue_free()godot 的在帧末尾安全销毁节点并将其从树中删除的快捷方式
registry.destroy()entt 的仓库对实体的销毁

registry.assign<position>(entity, 0.0f, 0.0f, 0.0f);

之前的吃豆人是 registry.emplace<struct>(entity,structIns)
现在是 registry.assign<struct>(entity,structData)

但是在官网没搜到 assign 的 api……

在这里插入图片描述

1.4 EnttPong

Github 地址:
https://github.com/DomRe/EnttPong

1.4.1 准备

一开始我是不会用 CMake GUI 来构建这个项目的,不知道为啥,选文件没选对?不懂
后来我试了一下吃豆人的指令,居然可以hhh

cd EnttPong-master\cmake
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .

在 VisualStudio 中将 entt_pong 设为启动项目,然后解决方案配置设为 Release,就可以运行了

在这里插入图片描述

在这里插入图片描述

1.4.2 调试

in Game.cpp

哦……我知道之前为什么没有查到 assign 的 api 了
应该是因为那个是旧版本的 entt
现在都是用 emplace

在这里插入图片描述
在这里插入图片描述

之后就有一个事件分发机制的应用,爽

in Game.hpp

namespace ep
{
	class Game final
	{
	private:
		///
		/// Default event dispatcher.
		///
		entt::dispatcher m_dispatcher;
		
		///
		/// The movement system.
		///
		MoveSystem m_move_system;
	}
}

声明了事件分发器、运动系统

in KeyDown.hpp

namespace ep
{
	///
	/// Simple keydown event.
	///
	struct KeyDown final
	{
		///
		/// Argument constructor.
		///
		inline KeyDown(int key_code)
		    : m_keycode(key_code)
		{
		}

		///
		/// \brief SDL2 Key code.
		///
		/// Defaults to invalid -1.
		///
		int m_keycode = -1;
	};
} // namespace ep

按键事件结构体

in KeyUp.hpp

namespace ep
{
	///
	/// Simple keydown event.
	///
	struct KeyUp final
	{
		///
		/// Argument constructor.
		///
		inline KeyUp(int key_code)
		    : m_keycode(key_code)
		{
		}

		///
		/// \brief SDL2 Key code.
		///
		/// Defaults to invalid -1.
		///
		int m_keycode = -1;
	};
} // namespace ep

按键事件结构体

in MoveSystem.hpp

namespace ep
{
	///
	/// This class will take data from the appropriate components
	/// and update the position based on input.
	///
	class MoveSystem final
	{
	public:
		///
		/// Default constructor.
		///
		MoveSystem() = default;

		///
		/// Default destructor.
		///
		~MoveSystem() = default;

		///
		/// Called when a key is pressed.
		///
		/// \param key_down Key Down Event.
		///
		void on_key_down(const KeyDown& key_down) noexcept;

		///
		/// Called when a key is released.
		///
		/// \param key_up Key Up Event.
		///
		void on_key_up(const KeyUp& key_up) noexcept;

		///
		/// Process events and update entities accordingly.
		///
		/// \param time DeltaTime or something similar from fixed-timestep gameloop.
		/// \param registry The registry to retrieve entities from.
		///
		void update(const double time, entt::registry& registry);

		///
		/// Current movement of player.
		///
		Player::MoveDirection m_player_movement;
	};
} // namespace ep

按键回调函数

in Game.cpp

namespace ep
{
	Game::Game(std::string_view title, const int w, const int h, std::uint32_t flags)
	    : m_window {}
	{
		// Assign events to systems.
		m_dispatcher.sink<KeyDown>().connect<&MoveSystem::on_key_down>(m_move_system);
		m_dispatcher.sink<KeyUp>().connect<&MoveSystem::on_key_up>(m_move_system);
	}
}

综上,订阅事件的方法为
entt::dispatcher.sink<EventStruct>().connect<CallBack>(Listener)

之后触发事件是在

namespace ep
{
	void Game::events()
	{
		// Process all user and system events.
		while (SDL_PollEvent(&m_window.m_event) != 0)
		{
			switch (m_window.m_event.type)
			{
				case SDL_QUIT:
					m_window.close();
					break;

				case SDL_KEYDOWN:
					m_dispatcher.trigger<KeyDown>(m_window.m_event.key.keysym.sym);
					break;

				case SDL_KEYUP:
					m_dispatcher.trigger<KeyUp>(m_window.m_event.key.keysym.sym);
					break;
			}
		}
	}
}

trigger 的泛型就是事件,圆括号里面是事件参数

namespace ep
{
	const int Game::run()
	{
		// 60 updates per second. We divide 1000 by 60 instead of 1 because sdl operates on milliseconds
		// not nanoseconds.
		const constexpr double dt = 1000.0 / 60.0;

		// This is a fixed-step gameloop.
		// See https://gafferongames.com/post/fix_your_timestep/
		// For an explanation.
		double time         = 0.0;
		double accumulator  = 0.0;
		double current_time = SDL_GetTicks();
		double new_time     = 0.0;
		double frame_time   = 0.0;

		while (m_window.is_open())
		{
			new_time     = SDL_GetTicks();
			frame_time   = new_time - current_time;
			current_time = new_time;

			accumulator += frame_time;

			events();

			while (accumulator >= dt)
			{
				update(accumulator);

				accumulator -= dt;
				time += dt;
			}

			render();
		}

		return EXIT_SUCCESS;
	}
}

这个是固定帧率内执行逻辑 update,实际帧率内渲染

in AISystem.cpp

namespace ep
{
	void AISystem::update(const double time, entt::registry& registry)
	{
		// Center the ai to the ball so it is always in the right position.
		// we want to increase or decrese ai position based on if the ball is above or below it.
		// so something like:
		/*
			if ball position is above ai:
				move ai smoothly up towards ball so middle of ai paddle is at ball pos.
		*/
		
		// Takes advantage of the fact that there is only 1 AI and 1 Ball.
		auto ai_view   = registry.view<AI, Position>();
		auto ball_view = registry.view<Ball, Position>();

		ai_view.each([&](auto& ai, auto& ai_pos) {
			ai.m_x = ai_pos.m_x;
			ai.m_y = ai_pos.m_y;

			ball_view.each([&](auto& ball, auto& ball_pos) {
				if (ball_pos.m_y > ai_pos.m_y)
				{
					ai_pos.m_y += 2.5;
				}
				else if (ball_pos.m_y < ai_pos.m_y)
				{
					ai_pos.m_y -= 2.5;
				}
			});
		});
	}
}

原来 each 输入的函数参数就是组件

在这里插入图片描述

之前在 api 里面看过,但是没看到他这个 component&

在这里插入图片描述

此外就没有什么了

1.5 entt-breakout

Github 地址:
https://github.com/vblanco20-1/entt-breakout

没编译成……好像要下个 SDL2_Image。我懒

他是把所有组件的结构体声明放在一个头文件 component.hpp 里面
这样挺好诶,一眼看到所有

这个是个把主逻辑放在 main 中的
我一点开就看到有用 entt::observer entt::collector,就感觉很厉害

//holds the observers for the reactive systems of this program
struct RegistryObservers {
	entt::observer sprite_transform_observer;

	void initialize(entt::registry& registry) {

		auto sprite_collector = entt::collector.replace<SpriteLocation>().when<SDL_RenderSprite>().group<SpriteLocation,SDL_RenderSprite>();
		sprite_transform_observer.connect( registry, sprite_collector );

		printf( "initializing observers");
	}
};

看了一会才知道这个 entt::observer 的作用是找到给定 registry 中满足条件的实体,这个条件就是 entt::collector
但是在哪里都找不到 entt::collectorreplace 方法,太奇怪了

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

还有 when 也是

可能还是因为这是三年前的项目,所以用的 api 都被改动过吧

sprite_transform_observer.connect( registry, sprite_collector );

但是至少这个句子的意思就是,使用 sprite_collector 来筛选 registry,得到的结果在 sprite_transform_observer

void transform_sprites(entt::registry &registry)
{		
	int nexecutions = 0;
	registry.ctx<RegistryObservers>().sprite_transform_observer.each([&registry,&nexecutions](auto et) {

		SDL_RenderSprite& sprite = registry.get<SDL_RenderSprite>(et);
		SpriteLocation& location = registry.get<SpriteLocation>(et);
		Vec2i screenspace = game_space_to_screen_space(location.location);
		sprite.location = screenspace; 	
		nexecutions++;
	});
	//printf("transform executions %i, \n",nexecutions);
}

ctx 的作用就好像一个……一种类型只有一种值的黑板
好奇怪,为什幺做成一个类型只有一个值

https://entt.docsforge.com/master/entity-component-system/#context-variables

此外就没有什么了

唉……看了这么多,估计大家的示例程序都差不多把……或许我也要开始自己写了


版权声明:本文为PriceCheap原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。