Hazel 2024
-
不喜欢游戏的人也可以做引擎,比如 cherno
-
引擎的作用主要是有两点:
- 将数据可视化
- 交互
-
当然有些引擎的功能也包含有制作数据文件,称之为资产 assets
-
不做窗口类的应用栈,可能要花一年才能做一个能实际使用的应用,只需要在游戏引擎中创建一个像 ImGUI 之类的关卡编辑器
类似于着色编译器
目录
- 设计
- 开发
- 属性配置
- EntryPoint 入口
- Logging Library 日志记录
- premake 构建系统
- 事件系统
- 事件系统(下)_实现
- KeyEvent: 重点
- 预编译头
- 技巧
- Git
- VS
- 其他
- 报错
- 找不到头文件
- Sandbox.exe 无法找到入口
- inconsistent dll linkage 不一致的 dll 链接
- error LNK2019: 无法解析的外部符号 & fatal error LNK1120: 1 个无法解析的外部命令
- error LNK2019: 无法解析的外部符号,函数 main 中引用了该符号
- git push origin main报错 连接不到 443
- git 拉取超时
- error C2597: 对非静态成员的非法引用
- error C2039: "stdout_color_mt": 不是 "spdlog" 的成员
- error C2040: 间接寻址级别不同
- 弹窗报错:Sandbox.exe 无法找到入口
- No Premake script (premake5.lua) found!
- Error: [string "src/base/api.lua"]:606: bad argument #2 to 'deferredjoin' (string expected, got table)
- error MSB3191: 无法创建目录,因为同名文件或目录已存在。
- error MSB3073
设计
-
EntryPorint:控制了什么,
-
Apllication Layer
整个窗口
处理应用的生命周期和事件,比如运行循环,什么能保持应用运行并渲染帧,什么能够推动时间发展,什么能够执行游戏想要执行的所有代码,什么是事件,窗口调整大小或者关闭,输入事件,鼠标和键盘。需要一种方法来运行游戏或引擎,作为一个实际的应用运行在任意平台 -
Window Layer
渲染的目标地,输入事件将来自哪里,整个应用就是这个窗口,和平台支持和渲染接口支持有关- 输入
- 事件:事件管理器
- 处理输入
- 广播功能:订阅事件并获得通知
-
Render
引擎最重要占比最大的一部分,很多引擎教程都是从渲染器开始。但是自己做一个引擎会从调试器开始,其实当其他部分都完成的时候,渲染器的构建将会变得非常简单
通常是每帧一次
-
Render API (OpenGL 是最简单的接口,跨平台)
最后 Hazel 将会支持多种渲染接口,如 Vulcan(在实现一些东西的时候会比 OpenGL 更高效)
-
Debugger Support
-
Scripting Language
不用一直写 c++
-
Memory Systems
内存对于性能而言很重要,比如分配内存的时候CPU的耗时
自定义分配器和内存跟踪
-
Entity-Compoennt-System(ECS)
实体-组件系统,模块化游戏制作过程,能够包含特定的组件 -
Physics
-
File I/O, VFS(virtual file system)
-
Build System
能够把现有的 3D 模型或者资产自动导入引擎变成优化后的资产格式
能够在 photoshop 更改后事实热更新到引擎内
开发
-
视频是 VS2017,视频中讲到过,但是 github 文档说 VS2019 也可以
-
git clone https://github.com/xieyouchen/Hazel Git
这一句指令是把 Hazel 项目拷贝到 Git 空文件夹(如果没有创建 Git 空文件夹会自动创建),把里面的所有文件(包括隐藏文件)拷贝到本地的 Hazel 文件夹。如果只是 git clone … 那么会在指定位置先创建一个 Hazel 文件夹,再 clone 下 github 内所有资源
为什么不先 clone 到空文件夹,然后再创建 VS 解决方案?其实是一样的,都需要手动迁移一下文件内容,因为 git clone 克隆下来的文件夹和 vs 创建解决方案时创建的文件夹应该处于同一文件层级 -
使用动态链接,将引擎和制作的可执行文件 .exe 动态链接,可以自定义加载或者卸载某些部分,会存在很多依赖项链接到引擎的 engine.dll,再将 engine.dll 链接到游戏
c++系列有个视频介绍过动态链接和静态链接的区别,以及动态库和静态库,总的来说就是静态库会臃肿
属性配置
cherno 有个视频是在 window 设置 c++ 的视频,建立 c++ VS 项目的最好方式,会介绍配置
-
通用 -> 属性管理器 -> 活动解决方案平台,直接把 x86 删掉,因为 32 位的系统很少人用了
-
同理把下面的,项目上下文 -> 平台下拉框,把 Win32 删掉
-
通用 -> 配置类型,.exe 可执行文件改成 .dll 动态链接文件
-
这个 engine.dll 文件将会输出到解决方案目录下的 bin 文件夹,通过文件的类型存储到对应文件夹,文件夹名字为 “Release/Debug - 平台(x64)”,再放到 Hazel 文件夹下面
-
中间目录就是 VS 创建的所有文件(比如 obj 文件)将会输出到的地方,所以最好还是使用一个区别于输出目录 bin 的文件夹 bin-int,这样你完全可以把 bin-int 给删掉,因为我们自己实现的部分全部在 bin 文件夹
-
在解决方案内(注意不是 Hazel )创建一个游戏 Sandbox 使用 Hazel,重复一遍 Hazel 的配置项设置,区别在于 Sandbox 是一个可执行文件 .exe,并设置为启动项目
这里有个小技巧,当打开某个项目的属性面板时,直接双击别的项目,也会打开该项目的属性面板
-
使用 vscode 打开 sln 文件,把 Sandbox 改成第一项,这样别人从 github 克隆下来打开解决方案时,也能以 Sandbox 为启动项目
本地的启动项目为 Sandbox 被记录在 .vs 文件中,.vs 文件一般不会上传到 github
-
给 Sandbox 添加 Hazel 的引用,打开 Sandbox 属性 -> 链接器 -> 命令行 -> Debug 配置,和视频中的不一样,我这里没有 Hazel.lib 文件
但是在解决方案面板中可以看到是有的
这里解释为什么我们设置生成的文件是 .dll,但视频中链接的确是 .lib 文件,因为 VS 编译 dll 文件时,会生成 lib 文件,包含了 dll 中所有函数
-
折叠 Hazel 所有的文件,创建我们需要的 src 文件夹,Sandbox 同理。这个显示所有文件的功能好神奇,要么显示 src 文件夹,要么显示初始化的所有文件夹
-
这个时候可以看到 bin 路径下已经有了 dll 文件,需要说明的是,实际运行代码的是 dll 文件,lib 文件会被链接到 Sandbox 中,这样就知道有哪些函数可以用以及这些函数在 dll 文件的什么地方
-
Sandbox 中引入命名空间后,执行 Hazel::Print(),编译不会出错,但直接执行会导致 dll 文件缺失,这种问题怎么解决?其实有自动化的方法,也可以直接把 dll 文件复制到 Sandbox.exe 同目录下
EntryPoint 入口
除非是静态链接库,其他都有入口,包括动态链接库
-
将析构方法 ~Appliaction() 变为虚函数,因为这个类会被(Sandbox)继承
- 支持多态
- 确保基类指针创建的派生类对象调用对应的析构函数时,正确释放内存
-
因为 __declspec(dllexport) 只在 Win 中被使用,所以定义一个宏限定 Win 使用,这个宏需要在解决方案的配置项内设置:c/c++ -> 预处理器 -> 预处理器定义
HZ_PLATFORM_WINDOWS;HZ_BUILD_DLL
。并且需要注意的是,这里应该设置为 Debug 配置时候定义宏, 映射到 __declspec(dllexport),而 Release 没有,映射宏为 __declspec(dllimport) -
为什么 Hazel::CreateApplication() 要在客户端定义,并且定义在类 Sandbox 的外面,而不是直接定义为 public 内?
-
extern 是怎么找到外部的 Hazel::CreateApplication() 的?这里的 extern 只是告诉该文件后文出现 CreateApplicaiton() 的地方,该函数是外部定义的
-
目前 EntryPoint.h 文件中有标红,但是执行成功,这个不用管吗?extern 的作用就是把其后的所有内容在项目中找到相似的实现并引用
很神奇的是,在 EntryPoint.h 的 main 函数测试部分添加 printf(“Hello”) (需要在 Hazel.h 中添加 stdio.h 头文件),执行成功再把测试部分删除后,不标红了 -
使用 git 不一定用 git bash,可以直接再资源管理器里面用 cmd
-
alt + d
是资源管理器的搜索栏快捷键 -
.gitignore 中 # 表示注释,.vs/ 可以无视 .vs 文件夹,*.user 可以无视 .user 后缀的文件 (.user 无法无视 Hazel.vcxproj.user 文件)
-
git reset . 退回到上一步
-
我的 github 默认主分支是 main,不是 master
-
git add . 和 git add * 的区别是什么
-
cherno 用的是 git push origin master,但是我的 github 默认主分支是 main,所以我使用 git push origin main
Logging Library 日志记录
记录所有的信息,比如启动信息,系统的信息,文件是否被打开,着色器是否成功编译,引擎正在做哪些事情
错误是红色,警告是黄色,普通信息是绿色,不重要信息是灰色
重点是格式化数据,比如有时候要打印对象,有时候打印数字,字符串,并且参数可能不定
-
使用
git submodule add https://github.com/gabime/spdlog Hazel/vendor/spdlog
将外部依赖库 spdlog 作为子模块导入到主仓库 Hazel/vendor/spdlog 文件夹下,可以保证主仓库和子模块仓库的独立性 -
因为 Logging 需要在所有项目中都能使用,所以把 spdlog 第三方依赖的 include 文件夹路径名添加到 “所有配置” 的附加包目录中去
-
解决方案中左上角显示 “挂起的编辑” 是什么意思
-
inline 什么作用?内联函数,用于降低程序的运行时间,编译期间把函数体复制到函数调用处,减少函数调用时候入栈出栈的开销,适合函数不超过10行 (相比于宏,多了类型检查等好处)
-
Log.cpp 调用的 s_CoreLogger 和 s_ClientLogger 是 Log.h 文件内的 static 标注的变量吗?是的,在 .cpp 文件相当于声明有这个变量,动态链接库会帮忙链接两个变量 (ctrl + 左键能跳转到 static 位置)
-
只是修改了头文件,就不需要更换 dll 文件,那么 dll 文件应该是只包含了 .cpp 文件
premake 构建系统
修改项目操作:
- 修改完代码后保存文件
- 到工程文件根目录,双击 GenerateProject.bat 脚本
- 进入到 VS,会弹窗口,直接全部重新加载
- 按 F5 即可正常运行
-
kind “SharedLib” 表示项目是 dll,在 premake 中,共享库和动态库相同
-
** 表示递归搜索文件
-
cfg: configuration 配置
-
files 表示要包含的所有的文件列表
-
include 用于包含头文件,相当于附加包含目录
-
filter 过滤器,只考虑 windows 的情况,
-
cppdialect 设置语言版本
-
staticruntime, 连接运行时库有关,我们希望静态连接
-
systemVersion win SDK版本
-
postbuildcommands 从配置中复制构建目标的相对路径
-
根据最新的 premake5 的文档修改的代码,两种方式都可以
-- 编译好后移动Hazel.dll文件到Sandbox文件夹下 postbuildcommands{ ("{COPY} %{cfg.buildtarget.relpath} ../bin/".. outputdir .."/Sandbox/") --("{COPY} %{cfg.buildtarget.relpath} \"../bin/".. outputdir .."/Sandbox/\"") }
-
如何运行 premake.lua ?
-
从官网下载下来的 premake 文件夹有很多 dll 文件,但是如果删了之后好像也能运行
这样也可以
事件系统
- 可以处理窗口事件,比如:关闭窗口、调整窗口大小、鼠标键盘输入事件
- 如果要做一个渲染器,没有事件系统也没问题。但是对于一个引擎来讲,是可视化创作工具,需要很多设计功能,鼠标键盘就能操作,比如一个可以通过鼠标键盘或其他工具移动的相机。
- 自定义事件是立即处理事件,没有缓冲事件
缓冲事件:键盘 A 一直按,第一个A输出后,停顿一下再一直输出 - 事件都是阻塞事件,意味着没有被缓冲,事件没有被延迟。只要发生,比如鼠标被点击,那么整个应用会停止,优先调度(get dispatched)点击事件。
区别于更加复杂也更优秀的过程:先从事件中获取信息,再推送到某个队列或某个缓冲区,这会产生延迟的现象,直到真正处理该事件。例如像传递、或者通过某事件总线之类的方式传递。
事件系统(下)_实现
目的是让 Application 创建一个窗口 Window,并且 Window 不会知道 Application 的信息,所以 Application 在创建窗口的时候需要创建一个 callback。
https://blog.csdn.net/alexhu2010q/article/details/106942099
- 创建一个 Events 文件夹,包含有 Event.h、KeyEvent.h、MouseEvent.h、Application.h 头文件,其中以 Event.h 为主,是整个事件系统的主要文件,其他三个作为不同的事件独立
- 需要确定的是事件系统的接口是啥?如果要自己创建一个事件,需要做什么?
- 包含的如 standard.h 这样的原生 c++ 文件应该包含在预编译头文件中,甚至于应该包含在 Core.h 文件中
- 事件系统是阻塞型事件,前面说过,不像是从事件中获取信息,将其推送到某个队列缓冲区,没有事件总线一样的东西进行传递
- Event:作为事件的基类,最基本的接口有:获取该事件的类型GetEventType(); 获取该事件的名字 GetName();同时还可以有一个 ToString() 函数方便打印一些消息
- EventType: 将事件的类型写成 enum 形式,当需要在运行时候获取到事件的类型名字时,就不用使用 dynamic_cast 之类的函数
- EventCategory: 事件的类型 enum 形式用于过滤掉某些事件,比如说:Application 类会接受所有的事件,但是只关心键盘事件或鼠标事件
EventType 关注的是某个具体的鼠标事件,EventCategory 表示一种类型,某些时候需要所有的鼠标类型表述
也是一个位字段,BIT(),这个函数是宏定义在 Core.h,#define BIT(x) (1 << x)
,左移1位,相当于 2 x 2^x 2x
目的:让它不局限于 0 1 2 3 4,因为一个事件可以分成多个分类,比如:键盘、鼠标和鼠标按钮事件都是输入事件 EventCategoryInput,而鼠标按钮事件又是 EventCategoryMouse 事件。创建一个位字段,就可以设置多个位,简单取掩码,看看什么样的分类和事件,或者一个事件属于什么分类
那为什么 EventType 没有写 - m_Handler: 表示这个事件是否被处理,调度事件去处理不同的层时,不想让事件进一步传递。比如:屏幕上有一个按钮,点击了鼠标,鼠标落在按钮的范围内,事件就被处理。这个时候游戏世界并没有收到点击事件,因为按钮已经被处理过了,m_Handler 就是说明按钮是否被处理
- IsInCategory() 是非常简单的工具函数,判断事件是否在给定的 EventCategory 中,用于快速过滤某些事件,只需要对实际分类按位于运算,返回这个 bool 值即可
- GetName() 暂时设置为空:一般不会去检索一个事件名称
- ToString() 只是用于调试,纯虚函数必须实现,默认返回事件的名称,如果要打印更多信息,直接在函数内添加即可
KeyEvent: 重点
- KeyPressedEvent(int keycode, int repeatCount) : KeyEvent(keycode), m_repeatCount(repeatCount) {} 这句话里面的 KeyEvent(keycode) 什么意思,是给哪个变量初始化?是不是调用 KeyEvent 构造函数之后,也会生成一个 m_KeyCode 变量
- KeyEvent 的构造函数被放在 protected 内,说明其他类不能构造,只有派生类可以
- 对于构造函数中的 repeatCount 是有必要的,防止有时候按下一个键,会触发两次按键事件
- 宏定义 #define const char* GetName(x) { return #x; },就是将 x 转化为字符串字面量并返回
- GetStaticType() 获取静态类型,有需求是在运行时候能够知道是什么类型,那么不想先实例化一个 KeyPressedEvent 之后,再调用 GetEventType(),所以获取类型最好作为静态函数
- 在 EventDispatcher 类中使用,作为调度器,需要检查所有的待处理事件,只有当待处理事件满足目标事件类型,才应该放到待执行列表中等待执行
using EventFn = std::function<bool(T&)>;
这句话,function() 的作用是啥- function() 是一个模板类,可调用一切可调用的实体(lambda 函数、函数指针、函数对象等)
- 这句话内,EventFn 是一个类型别名,定义了一个 function 对象
- function 对象接受以 T& 参数,返回 bool 值的一个函数
- 这里的 T 是模板类型
- 这里的 function() 能作为一个接口,存储任何返回 bool 值并接受一个 T& 类型引用作为参数的函数。这样调用者就不用关心函数的实现细节,直接调用。
- function 在回调机制、事件驱动编程和泛型编程中作用很大
- m_Event.m_Handled = func(*(T*)&m_Event); 这句意思是无论 func 是什么函数,只要返回值是 bool,参数是 T& 类型,那么这个函数在此处都会被调用
- 更新、渲染、时钟函数都有对应的事件类,可以以事件的形式传播
- 重载的输出流符号作用于日志库,能更方便调用事件的 ToString(),记录事件
HZ_TRACE(e);
Application.cpp 文件中运行这句话,因为已经重载过了输出流符号- 必须添加头文件
#include "spdlog/fmt/ostr.h"
spd 的输出流操作符,这样就可以自定义输出类型
预编译头
就是一个头文件,源文件添加后可以避免某些头文件被反复编译
- hapch.cpp 本来是不需要的,但是 VS 会自动生成
- 在 premake.lua 中添加 pchheader “hzpch.h” 和 pchsource “Hazel/src/hzpch.cpp”,后者只有 VS 需要,其他 IDE 会无视。其中,前面那句效果相当于把所有包含了 hzpch.h 的文件属性中预编译头设置为 “使用”,而后面那句效果相当于把 hzpch.cpp 文件属性中的预编译头设置为 “创建”
- 再次 Build 工程后,项目配置会提醒是否使用 pch
- 之后再所有的 .cpp 文件中包含 hzpch.h (实际上没有使用的 cpp 文件也需要添加)
- 需要有一个 hzpch.cpp 文件,告诉编译器把 hzpch.h 预编译头文件的内容全部编译完 (因为 VS 的编译系统以一个个文件为单位,当一个文件发生修改,那么所有文件都会被重新编译,所以把一些不太变动的文件放在预编译头文件中,告诉编译器提前编译好,之后就不会改变,提升编译效率)
技巧
Git
- 使用 git 不一定用 git bash,可以直接再资源管理器里面用 cmd
alt + d
是资源管理器的搜索栏快捷键- .gitignore 中 # 表示注释,.vs/ 可以无视 .vs 文件夹,*.user 可以无视 .user 后缀的文件 (.user 无法无视 Hazel.vcxproj.user 文件)
- git reset . 退回到上一步
- 我的 github 默认主分支是 main,不是 master
- git add . 和 git add * 的区别是什么? git add . 会忽略 .gitignore 的文件,但是 git add * 不会
- cherno 用的是 git push origin master,但是我的 github 默认主分支是 main,所以我使用 git push origin main
- 如何回退到上一版本:
git reset --hard HEAD^
- 撤回回退:
git reflog
查看历史命令,要回退到哪个版本
git reset --hard 版本号
- 撤回 commit 和 add .:
git reset (--mixed) HEAD^
,括号里的可有可无 - 撤销 git add . :
git reset .
即可 - github 上的版本如何回滚,网页版是没有回滚操作的,只有 Github For Win 客户端有,如果想在网页端操作,可以先按照这个教程
https://blog.csdn.net/weixin_44259720/article/details/112608864
创建一个新的分支,之后把默认分支换成新的分支,同时改名成功,并且把不想要的分支都删掉。这种操作可以在新分支的 Activity 中见到操作记录,但已经是比较好的解决方案。 - 所以必须在 push to github 之前查阅将会出现的变化,仔细核对,否则推送到 github 后非常麻烦。或者可以fetch到一个地方之后,再推送到 main 版本
- 需要知道 push 哪些文件,是必须的,哪些文件不是必须的,尽量保证 github 仓库中文件的干净程度?可以新建一个分支,先推送试试,如果clone下来发现无法正常运行,可以再尝试?
- 首先 bin/ 和 bin-int/ 是编译的结果,可以不 push 到 github;.sln 文件也不需要;.vcxproj 等文件也不需要
- .gitignore 只能忽视原来没有被跟踪的文件,如果要解除跟踪,使用
git rm --cached <file>
。比如这里需要git rm --cached *.sln
这样能把 .sln 文件给解除跟踪 - 提交规范
feat: 新功能、新特性
fix: 修改 bug
perf: 更改代码,以提高性能(在不影响代码内部行为的前提下,对程序性能进行优化)
refactor: 代码重构(重构,在不影响代码内部行为、功能下的代码修改)
docs: 文档修改
style: 代码格式修改, 注意不是 css 修改(例如分号修改)
test: 测试用例新增、修改
build: 影响项目构建或依赖项修改
VS
- alt + 鼠标左键,竖向选中代码,用于删除代码,或者统一缩进
其他
- alt+d 文件资源管理器中快捷定位到路径栏,cmd 可以打开 cmd 窗口
报错
找不到头文件
为什么引用头文件说找不到?改成双引号就行 “Test.h”
第二种方法,想要引用头文件可以用尖括号 <>,可以进入属性配置面板:c/c++ -> 常规 -> 附加包含目录输入 $(SolutionDir)Hazel\src
<> 头文件会去系统目录寻找,“” 先找项目目录,再找系统目录
Sandbox.exe 无法找到入口
因为 dll 文件没有更新,去把最新的 dll 文件更新下就行,最新的 dll 文件需要看引擎的 dll 文件输出到哪里,从配置属性"常规"里面可以看到
报错窗口乱码怎么解决,同时报错的窗口英文字母也是乱码怎么解决
inconsistent dll linkage 不一致的 dll 链接
可能是 dll 链接拼写错误
“配置属性面板 -> c/c++ -> 预处理器”
看看里面的宏有没有拼写错误导致 dll 链接失败
error LNK2019: 无法解析的外部符号 & fatal error LNK1120: 1 个无法解析的外部命令
SandboxApp.obj : error LNK2019: 无法解析的外部符号 "class Hazel::Application * __cdecl Hazel::CreateApplication(void)" (?CreateApplication@Hazel@@YAPEAVApplication@1@XZ),函数 main 中引用了该符号
这里是 main 函数中引用的外部引用 extern CreateApplication() 无法解析
extern Hazel::Application* Hazel::CreateApplication();
查看实现 CreateApplication 的文件 Sandbox.cpp 中,发现未添加命名空间
改为 Hazel::Application* Hazel::CreateApplication()
即可
error LNK2019: 无法解析的外部符号,函数 main 中引用了该符号
error LNK2019: 无法解析的外部符号 "public: static void __cdecl Hazel::Log::Init(void)" (?Init@Log@Hazel@@SAXXZ),函数 main 中引用了该符号
这里仍然是 main 函数内的链接问题,这里的 Hazel::Log::Init() 函数识别不到
- 检查 Log 类定义的时候是否添加命名空间
- 检查 Log 定义的时候有没有添加 __declspec(dllexport),必须添加字段表示将类导入 dll
我犯了第二个错误
更改为class HAZEL_API Log
即可(宏 HAZEL_API 在我的工程中被映射为 __declspec(dllexport))
git push origin main报错 连接不到 443
Failed to connec t to github.com port 443 after 21083 ms: Couldn't connect to server
首先看下 clash pro 的请求日志 Logs,发现都能正常访问
于是重新 git push,弹出窗口要验证码,选择网页验证,输入身份验证器中的号码即可
git 拉取超时
git submodule add
拉取超时
因为拉取的包太大了
有两种错误提示
error: RPC failed; curl 18 HTTP/2 stream 5 was reset09.00 KiB/s
- fetch-pack: unexpected disconnect while reading sideband packet
把命令改成
git submodule add --depth 1 https://github.com/gabime/spdlog Hazel/vendor/spdlog
添加关键字 ‘–depth 1’,表示只拉取最新一次更新
error C2597: 对非静态成员的非法引用
error C2597: 对非静态成员“Hazel::Log::s_CoreLogger”的非法引用
那就是说明 s_CoreLogger 是非静态成员,但是用成了静态成员,比如说 static 函数的返回值是 s_CoreLogger&,那么就出现错误,static 函数必须返回 static 成员
error C2039: “stdout_color_mt”: 不是 “spdlog” 的成员
spdlog::stdout_color_mt 这么些报错
是因为没有包含定义 stdout_color_mt 的头文件,这个头文件定义 stdout_color_mt 应该也是定义在 spdlog 这个命名空间内
error C2040: 间接寻址级别不同
error C2040: “s_CoreLogger”:“std::shared_ptr<spdlog::logger>”与“std::shared_ptr<spdlog::logger> &”的间接寻址级别不同
在 Log.cpp 中声明了 Log.h 的变量,但是没有保持两个变量相同内容,.h 中是引用,.cpp 中是变量
这里不要被 .h 文件上面的 public 函数误导,那是公开给别的对象的函数,返回的是引用
弹窗报错:Sandbox.exe 无法找到入口
无法定位程序输入点 Hazel::Log::Init() ,在动态链接库上
还是 dll 链接的问题,我这里是没有更新 dll 文件
No Premake script (premake5.lua) found!
必须命名 premake 文件为 premake5.lua
注意把当前 vs 打开的 premake.lua 文件关闭,重命名之后再打开,否则在 vs 修改的记录按 ctr + s 后,vs 会保存一个 premake.lua 文件到原位置,premake5.lua 的内容不会改变
Error: [string “src/base/api.lua”]:606: bad argument #2 to ‘deferredjoin’ (string expected, got table)
error MSB3191: 无法创建目录,因为同名文件或目录已存在。
error MSB3191: 无法创建目录“..\bin\Debug-windows-x86_64\Sandbox\”。无法创建“D:\Projects\Hazel\Hazel\bin\Debug-windows-x86_64\Sandbox”,因为同名文件或目录已存在。
把 {COPY} 改成 {COPYFILE} 不知道为啥报错
-- 编译好后移动Hazel.dll文件到Sandbox文件夹下
postbuildcommands{
("{COPYFILE} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")
}
https://github.com/TheCherno/Hazel/issues/623
复制这个答主的就好了,真他妈高血压
error MSB3073
error MSB3073: 命令“IF EXIST ..\bin\Debug-windows-x86_64\Hazel\Hazel.dll\ (xcopy /Q /E /Y /I ..\bin\Debug-windows-x86_64\Hazel\Hazel.dll ..\bin\Debug-windows-x86_64\Sandbox > nul) ELSE (xcopy /Q /Y /I ..\bin\Debug-windows-x86_64\Hazel\Hazel.dll ..\bin\Debug-windows-x86_64\Sandbox > nul)
再点一次 F5 就可以了
把 Hazel 的 dll 文件复制到 Sandbox 里面发现可以,其实就是一开始检测目录发现是空文件,如果再按一次 F5 再次运行时候,就能检测到存在的文件
https://github.com/TheCherno/Hazel/issues/9
error MSB3073: 命令“copy /B /Y ..\bin\Debug-windows-x86_64\Hazel\Hazel.dll "\bin\Debug-windows-x86_64\Sandbox"
继续报错,不是引号的问题,如果在 cmd 中是可以复制成功的
现在我想知道,执行 copy 的这条语句是在哪里
error MSB3073: 命令“copy /B /Y "..\..\bin\Debug-windows-x86_64\Hazel\Hazel.dll" "..\..\bin\Debug-windows-x86_64\Sandbox\"
退回两层去找 bin,可能是 Hazel/src,但如果是这里,这句命令可以执行成功(cmd 中可以)