cherno引擎课 -

news2024/11/24 17:44:56

感谢b站星云图形的翻译:【双语】【最佳游戏引擎教程实战】【入门】(1):Introducing the GAME ENGINE series!_哔哩哔哩_bilibili

Introducing the GAMEENGINE series 

希望:它是一个制作(互动)3D实时渲染应用程序的引擎。

本质是一个数据转换机器,基本上就是读取数据,然后引擎对数据进行某些处理。

什么是游戏引擎

我们要构建的是一种,读取文件、转换、然后展现到屏幕上的东西,且有交互能力。程序的具体行为是面向数据的,而非硬编码的。

游戏引擎通常包含一个平台、一套工具,可以创建那些资产。它为我们提供创建资源的方式。

比如用 PS 做纹理、用 3D Studio Max 或者 Maya 或者 Blender 等制作3D模型,但是工作的最后仍然需要将那个3D模型转换为游戏引擎真正接受的格式。因为对于大多数正经的大型游戏引擎,几乎从来不会直接读取 png、jpeg 或者 obj 模型之类的。它们通常会读取游戏引擎特定的内建的自定义的格式。可能是纹理、模型、关卡等等

内容创作者有职责把模型从第三方格式转换为游戏引擎格式,比如要定义所有引擎需要的东西,要对那个模型做一些事情。因为通常来说,引擎可能需要更多从 Maya 导出的模型本身包含的数据。

为了让我们的数据转换更方便,我们需要大量的各种各样的系统,包括平台抽象层,能让我们的代码运行在不同的平台上。

DESIGNING our GAME ENGINE

先聚焦于构建需要的最小的基础框架,能够运行,然后我们不停迭代、增强、增加更多特性、提高稳定性、更好的性能和优化,和其他之类的事情。

一个游戏引擎需要些什么(只是看看要包括些什么,并不是实现的顺序)。

第一点要做的是需要一个入口点:Entry Point

一个入口点本质上就是当我们希望我们的应用程序或者游戏使用这个引擎启动的时候会发生什么。比如是什么控制了 main 函数?什么控制了main函数的代码执行?

之后要有一个 Application Layout (应用层):处理程序的生命周期和事件之类的东西的那部分代码。比如说我们的主循环,也就是保持我们的程序运行和渲染的那个循环,也就是保持时间流动的那个循环。也就是执行(驱动)我们的游戏希望执行的全部代码的那个循环。关于事件比如改变窗口大小或者关闭窗口,或者输入事件,比如鼠标或者键盘,所有这些东西都要在应用层处理。

我们需要一种方式来让我们的游戏或者引擎作为一个实际的应用程序运行在任何可能要运行的平台上,这就是应用层要解决的。

接下来需要一个 Window Layout (窗口层)。然后在这一层里面我们要处理 input (输入)和 event (事件)。由于输入可以放到事件里面(输入事件),所以我们的事件管理系统会处理输入事件,从我们的窗口和应用程序捕获输入事件。事件管理系统也会是非常重要的东西,我们需要构建一个非常基础的消息和广播系统。基础的意思是说只要,我们应用程序内的某些层可以订阅事件。并且当事件发生的时候,可以从应用层的堆栈获得通知。当事件传播到那些层的时候,那些层可以选择是否要把事件传播到哪些东西,就像一个所有者系统。某种程度上它就是一个基础的消息系统。当事件发生的时候,我们可以中断,或者不是真的中断。总之事件发生的时候,然后我们会得到通知,本质上就是因为我们的 onEvent() 函数会被调用。

然后是渲染器 Render ,可能会是我们需要处理的最大的系统之一。渲染器就是实际渲染图形到屏幕上的那个东西。

接下来是 Render API abstraction 。最开始我们只会用 OpenGL ,之后还需要各种 API 。我们要把所有的东西设置成API无关的,这样新增一种 API 就不是完全重写。显然有的东西不得不是API独特的,比如我们可以就写一个叫 “上载纹理” 的函数,然后为四种API各做一个实现不同,因为我们创建渲染器的方式会因为API而不同。因为如果你用Vulkan的方式,相比于使用OpenGL的方式做特定的事情的时候vulkan可能会更高效。因此我们仍然需要写两份代码,而不是把每个 OpenGL 的函数拷贝一份

接着是 Debugging Support 调试支持。就比如日志系统,比如性能,为此我们需要一种分析系统,我们希望应用程序能有一种特殊的运行方式,在 VisualStudio 设置之外的特殊的模式。我们希望应用程序能够自己运行调试,因此可以运行在任何平台上。而不用担心,运行特定平台可用的特定工具。我们希望把工具代码插入到自己的代码中,但是(工具代码)只在调试模式下运行。可以为每个函数计时并且汇总成好看的视图,任何也许用某种工具查看之类的。

然后还希望有一些 Scripting 脚本,比如 Lua 或者 C# 或者 Python 之类的。需要脚本语言,避免完全使用 C++。我们可以像艺术家和内容创造者那样轻松地写高级脚本语言而不用担心内存的问题。

还有 Memory System 内存系统。需要管理好资源。以及调试内存之类的。

还需要实体组件系统(ECS,Entity Component System)。这是一种让我们能在世界创建游戏对象的模块化的方式。比如让世界里的每个独立实体或游戏对象能包含特定的组件或系统,因此我们能定义行为以及动作的具体细节。它就是一种让我们能定义引擎要对实体做的事情的方式。

还需要 Physics Solution 物理解算。

还有 File I/O ,VFS(虚拟文件系统)

还有Build System 构建系统,把3D模型或材质需要能转换为自定义格式,那是为我们的引擎优化的格式。所以不用在运行时浪费时间转换格式,因为我们可以离线处理。热交换资产,希望比如开着 PS 在纹理上画了一些东西,按下 Ctrl + S,我们希望在运行时构建系统捕获然后实时地重新构建导入游戏,所以我们能更新东西,甚至比如3D模型,调整一些顶点或者进行某种修改,然后就热交换到引擎里。所以我们能在游戏运行时修改,这不是一个非常重要的系统,现在可能不值得讨论。

不过暂时我们只支持Windows只支持OpenGL。我们的C++代码文件里不会包含任何Windows代码,比如 Win32 API 代码。因为显然引擎要在未来支持其他的平台。所以会抽象那些东西,保证平台或渲染API独特的代码分散在它自己的文件里。在其他平台或者渲染API的时候它们不会被编译

Project Setup

这一节我们要设置好所有东西:Github仓库、Visual Studio 解决方案和项目、依赖配置,然后我们要链接项目到一起,做一个屏幕上打印 Hello World 的简单程序。但是会为我们的游戏引擎组织好结构和配置。

GitHub上建立好工程,选择 Apache-2.0 License ,具体选什么 License 可以看下面这个图:

这里我们把引擎选择编译成一个 DLL ,然后外部链接上最终的 exe ,选择 DLL 的原因是我们可以自己选择加载或者卸载 DLL ,主要的原因是我们会有很多依赖,我们会需要链接很多库到引擎里。如果用静态库的话,我们的所有的静态库都会被链接到游戏里面,我们的引擎依赖那些库,如果使用静态库的话,那些需要链接到引擎的库文件实际上全部会链接到游戏。用DLL的话基本上就像一个exe文件,所以我们可以把所有东西都链接到那个DLL,然后我们的游戏只会依赖那一个单独的包含了全部需要的内容的DLL文件,而不是无数的其他的库文件。

所以本质上我们要做的,就是把所有的依赖都链接到那个引擎的DLL文件,这意味着它们都需要是静态库。我们需要做的就是,把所有静态库链接到引擎DLL然后把引擎DLL链接到游戏。

(👆这一段没读懂,原视频是这么个意思,但是听不懂)

设置引擎为一个库文件(dll),在外部将库文件链接到外部的应用项目(exe)
      (静态库的形式类似于将一大堆库链接到游戏中)
      (动态库的形式类似于将一大堆外部库先链接到dll文件中,再将这个dll文件链接到游戏中,这样我们的游戏只会依赖于这一个dll文件)

Cherno游戏引擎笔记记录(1~14)_c++cherno游戏引擎-CSDN博客 的解释,这个听懂了

一些vs设置可以参考文章:Cherno_游戏引擎系列教程(1):1~16_game engine series-CSDN博客

Entry Point Game Engine Series

代码的结构:Hazel 是一个dll项目,Sandbox(沙盒) 是一个exe

什么是沙盒(sandbox)

sandbox(沙盒)是一种安全机制,用于限制程序的访问权限和行为范围。它创建了一个受限的执行环境,将程序隔离在其中,以防止恶意代码或不安全的操作对系统造成损害。

引擎入口点

引擎入口点(Engine Entry Point)通常指的是一个程序的起始执行位置,也可以被称为主函数(Main Function)。程序从这里开始执行,并按照预定的流程继续执行。
例如,在C语言中,引擎入口点通常被命名为main函数,它是程序的起始位置。在C++中,引擎入口点可以是全局的main函数,也可以是类的静态成员函数。

上一节我们的 Sandbox 的项目的 Application.cpp是这样写的:

namespace HEngine
{
	__declspec(dllimport) void Print();
}

void main()
{
	HEngine::Print();
}

这意味着 main 函数是由 Application 定义的,但是引擎的入口应该由引擎负责定义,我们希望这些是由引擎端控制的。我们要在 Sandbox 项目里创建一个 Application 类,来定义和启动我们的应用程序。我们还要把 __declspec(dllimport) 和 __declspec(dllexport) 写到宏里,然后我们可以重用头文件。

宏(条件判断的实现逻辑)

#define 宏名称 值或代码

#ifdef 标识符
    // 如果标识符已经被定义,则编译这部分代码
#else
    // 如果标识符没有被定义,则编译这部分代码
#endif

宏不会自动定义。如果在 属性页 -> C++ -> 预处理器 中填入一个宏XXX,这意味着在编译代码时会自动在预处理阶段为XXX这个宏定义一个值。
这样不用手动编写一个宏,可以直接使用#ifdef语句进行条件判断。

日志系统

日志就是我们记录事件的一种方式。这里的事件不只是字面意义,因为事件可以是任何东西。

我们要写一个日志库。日志最大的议题是格式化不同的类型。只是打印一个字符串很简单,但是我们希望能打印的不只是文本,所以需要一种好的格式化方式,不定参的格式化。

总之,我们要使用一个叫 spdlog 的库:https://github.com/gabime/spdlog

C++没有定义导入和使用库的方法。很多时候基本上就是选择一个你想用的构建系统,比如 CMake 或者 Premake 之类的。然后保证你使用的每个库写到构建系统里。像这样你可以更新和维护它。或者用 git-submodule 添加它们,如果你用 GitHub 就可以用 git-submodule ,这可能是最好的方式。

我们要做的就是添加一个 .gitmodules 文件,然后我们实际克隆 HEngine 的时候,也会克隆所有 submodule 。这很有用,因为可以持有一个版本的完整代码。

我们希望 HEngine::Log 而不是 spdlog::log,于是我们在引擎中创建了一个Log类。我们要做的是创建两个控制台,一个客户的,一个引擎的,一个叫 Core,一个叫 App。

Log.h

#pragma once

#include "Core.h"
#include "memory.h"
#include "spdlog/spdlog.h"

namespace Hazel {
	class HAZEL_API Log
	{
	public:
		static void Init();

		inline static std::shared_ptr<spdlog::logger>& GetCoreLogger() {
			return s_CoreLogger;
		}
		
		inline static std::shared_ptr<spdlog::logger>& GetClientLogger() {
			return s_ClientLogger;
		}
	private:
		static std::shared_ptr<spdlog::logger> s_CoreLogger;
		static std::shared_ptr<spdlog::logger> s_ClientLogger;

	};
}

Log.cpp

#include "Log.h"
#include <spdlog/sinks/stdout_color_sinks.h>
namespace Hazel {
	std::shared_ptr<spdlog::logger> Log::s_CoreLogger;
	std::shared_ptr<spdlog::logger> Log::s_ClientLogger;

	void Log::Init() {
		spdlog::set_pattern("%^[%T] %n: %v%$");
		s_CoreLogger = spdlog::stdout_color_mt("Hazel");
		s_CoreLogger->set_level(spdlog::level::trace);

		s_ClientLogger = spdlog::stdout_color_mt("APP");
		s_ClientLogger->set_level(spdlog::level::trace);
	}
}

 EntryPoint 调用

#pragma once
#ifdef HZ_PLATFORM_WINDOWAS

extern Hazel::Application* Hazel::CreateApplication();

int main(int argc,char** argv) {

	Hazel::Log::Init();
	Hazel::Log::GetClientLogger()->warn("Initialzed Log!");
	Hazel::Log::GetCoreLogger()->info("Hello!");

	printf("Hazel Engine\n");
	auto app = Hazel::CreateApplication();
	app->Run();
	delete app;
	return 0;
}
#endif // HZ_PLATFORM_WINDOWS

可以使用宏来减少使用时写的代码量

#define HZ_CORE_ERROR(...) ::Hazel::Log::GetCoreLogger()->error(__VA_ARGS__)
#define HZ_CORE_WARN(...) ::Hazel::Log::GetCoreLogger()->warn(__VA_ARGS__)
#define HZ_CORE_INFO(...) ::Hazel::Log::GetCoreLogger()->info(__VA_ARGS__)
#define HZ_CORE_TRACE(...) ::Hazel::Log::GetCoreLogger()->trace(__VA_ARGS__)

 Premake

本期会讨论CMake之类的构建问题。首先是为什么需要项目生成,而不是直接VS呢?主要就是不同平台的问题了。

我们要用的是 Premake:Releases · premake/premake-core · GitHub

Premake 是用 Lua 来写的

博文参考:Cherno_游戏引擎系列教程(1):1~16_game engine series-CSDN博客

Cherno游戏引擎笔记记录(1~14)_c++cherno游戏引擎-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2238147.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux git-bash配置

参考资料 命令提示符Windows下的Git Bash配置&#xff0c;提升你的终端操作体验WindowsTerminal添加git-bash 目录 一. git-bash配置1.1 解决中文乱码1.2 修改命令提示符 二. WindowsTerminal配置git-bash2.1 添加git-bash到WindowsTerminal2.2 解决删除时窗口闪烁问题 三. VS…

秃姐学AI系列之:GRU——门控循环单元 | LSTM——长短期记忆网络

RNN存在的问题 因为RNN模型的BPTT反向传导的链式求导&#xff0c;导致需要反复乘以一个也就是说会出现指数级别的问题&#xff1a; 梯度爆炸&#xff1a;如果的话&#xff0c;那么连乘的结果可能会快速增长&#xff0c;导致梯度爆炸梯度消失&#xff1a;如果的话&#xff0c;…

Java面试要点06 - static关键字、静态属性与静态方法

本文目录 一、引言二、静态属性&#xff08;Static Fields&#xff09;三、静态方法&#xff08;Static Methods&#xff09;四、静态代码块&#xff08;Static Blocks&#xff09;五、静态内部类&#xff08;Static Nested Classes&#xff09;六、静态导入&#xff08;Static…

【测试】【Debug】vscode pytest 找不到测试用例测试文件 行号部位没有绿色箭头

出现这种情况首先检查&#xff1a; 是否安装pytest点击vscode的这个图标如果其中都是空的&#xff0c;没有识别上&#xff0c;并且写好的.py测试文件的行号前面没有运行符号&#xff0c;要检查名称是否按照pytest的要求写&#xff0c;不然会识别不到。 命名规则注意&#xff1…

区块链技术在电子政务中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 区块链技术在电子政务中的应用 区块链技术在电子政务中的应用 区块链技术在电子政务中的应用 引言 区块链技术概述 定义与原理 发…

【Java Web】Servlet

文章目录 Servlet APIServlet生命周期Servlet配置注解配置文件全局参数 Servlet获得内置对象升级示例3 Servlet 是一种用于构建 Java 服务器端应用程序的技术&#xff0c;允许开发者创建动态的 web 内容。Servlets 在 Java 平台上运行&#xff0c;并由 Java Servlet API 提供支…

linux命令详解,存储管理相关

存储管理 一、内存使用量&#xff0c;free free 命令是一个用于显示系统中物理内存&#xff08;RAM&#xff09;和交换空间&#xff08;swap&#xff09;使用情况的工具 free -m free -m -s 5参数 -b 功能: 以字节&#xff08;bytes&#xff09;为单位显示内存使用情况。说…

流体力学ansys Fluent二次开发scheme_eval模块剖析

在ANSYS Fluent的二次开发中&#xff0c;scheme_eval 是 Scheme 编程语言中一个非常重要的模块&#xff0c;它允许用户执行动态的 Scheme 表达式和函数&#xff0c;从而扩展 Fluent 的功能。scheme_eval 模块通常与 Fluent 的计算和自定义脚本操作紧密结合。下面我们会对这个模…

中仕公考:大四在校生考公怎么规划?

还在就读的大学生&#xff0c;大三下学期就可以备考&#xff0c;大四以应届生的身份参加考试&#xff0c;中仕不建议大家把备考战线拉得太长或者太短&#xff0c;时间太长容易疲惫&#xff0c;时间太短容易备考不到位。 考公流程&#xff1a; 查看公告职位表→网上报名一资格…

【363】基于springboot的高校竞赛管理系统

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统高校竞赛管理系统信息管理难度大&#xff0c;容错率低&am…

网站架构知识之Ansible模块(day021)

1.Ansible模块 作用:通过ansible模块实现批量管理 2.command模块与shell模块 command模块是ansible默认的模块&#xff0c;适用于执行简单的命令&#xff0c;不支持特殊符号 案列01&#xff0c;批量获取主机名 ansible all -m command -a hostname all表示对主机清单所有组…

计算机网络:网络层 —— 软件定义网络 SDN

文章目录 软件定义网络 SDN远程控制器OpenFlow协议SDN 广义转发流表简单转发负载均衡防火墙 SDN 控制器 软件定义网络 SDN 软件定义网络&#xff08;Software Defined Networking&#xff0c;SDN&#xff09;是一种新兴的网络架构&#xff0c;旨在通过网络控制与数据转发的分离…

使用 .NET Core 7 和 SignalR 构建实时聊天应用程序

动动你可爱的小手&#xff0c;请关注下本人公众号&#xff0c;继续为您提供更多原创技术文章。谢谢给为的支持 SignalR 是一个 ASP.NET 库&#xff0c;它支持实时 Web 功能&#xff0c;允许服务器立即将更新推送到客户端&#xff0c;从而提高应用程序的效率和响应能力。它通过简…

响应式网页设计--html

一&#xff0c;HTML 文档的基本结构 一个典型的 HTML 文档包含了几个主要部分&#xff0c;基本结构如下(本文以下出现的所有代码都可以套入下面示例进行测试)&#xff1a; <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8&q…

欢迎 Stable Diffusion 3.5 Large 加入 Diffusers

作为Stable Diffusion 3的改进版本&#xff0c;Stable Diffusion 3.5 如今已在 Hugging Face Hub 中可用&#xff0c;并可以直接使用 &#x1f9e8; Diffusers 中的代码运行。 https://hf.co/blog/sd3 本次发布包含两套模型参数: https://hf.co/collections/stabilityai/stable…

Ubuntu22.04中使用CMake配置运行boost库示例程序

Ubuntu22.04中使用CMake配置运行boost库示例程序 boost是一个比较强大的C准标准库&#xff0c;里面有很多值得学习的东西&#xff0c;比较asio网络库可以用来编写C TCP客户端或者TCP服务端接收程序。本文主要讲解如何在Ubuntu22.04中使用Cmake配置boost库&#xff0c;以及运行…

高效档案管理案例介绍:文档内容批量结构化解决方案解析

一、项目背景介绍 档案文件中包含的内容丰富且形式多样&#xff0c;通常不仅包括文本&#xff0c;还可能包含表格、图片、标题、段落、注释等多种内容元素。传统的档案管理往往依赖人工对文档内容进行归档、分类和标注&#xff0c;这种方式耗时费力&#xff0c;且在管理庞大数…

如何在vscode中安装git详细新手教程

一、安装git后点击vscode中的设置 今天教大家如何在VScode中编写代码后提交到git仓库&#xff0c;如果我们不想切换到git的命令行窗口&#xff0c;可以在VScode中配置git&#xff0c;然后就可以很方便快捷的把代码提交到仓库中。 二、在输入框中输入 git.path &#xff0c;再点…

负梯度方法与Newton型方法-数值最优化方法-课程学习笔记-4

今天我们继续来学习数值最优化方法的第三章内容的后续知识 Newton方法 Newton方法是Newton方法的基础, 本节主要讨论的是基本Newton方法, 阻尼Newton方法以及修正Newton方法的构造和特性, 这类方法适合解决中小型最优化问题 基本Newton方法 对于 f ( x ) f(x) f(x)如果有连续…

element-plus的Tree 树形控件添加图标

该文章为本菜鸡学习记录&#xff0c;如有错误还请大佬指教 本人刚开始接触vue框架&#xff0c;在使用element-plus组件想实现树形控件&#xff0c;发现官网的组件示例没有图标区分显示 实现效果 代码 <temple 部分 <el-tree :data"data" node-click"hand…