如何使公共类成员可以被其他类C ++访问但不能更改C

问题描述

这可能以前曾被问过,但是除了一般的private/public/const解决方案之外,我找不到其他东西。基本上,创建class text的实例时,需要将字体加载到数组中。 text类在我的text.h文件中,并在text.cpp中定义。在这两个文件中,它们还包含一个Fonts class,因此我希望我的fonts类将选择的fonts预加载一个数组中,以供{{1 }}类在创建第一个实例之后。我希望text类可以访问这些字体,但不能对其进行更改。每次创建text时,我无法在TTF_Font *Get_Font()类中创建fonts方法,它会加载需要手动关闭的内存,因此我无法完全关闭它在超出方法范围后,我想做些类似的事情,例如在创建字符时,调用font,它将选择TTF_RenderText_Blended(Fonts::arialFonts[10],"123",black);的字体类型。>

解决方法

我不确定您在使用哪种字体的应用程序,但是我已经使用OpenGL和Vulkan完成了一些3D图形编程。这可能有帮助,也可能无济于事,但应为您提供有关Game Engine开发中常用框架结构的某种上下文。

通常,我们将具有一个Texture类或结构,该类具有一个三色或四边形矢量,它们代表图像中每个像素的颜色数据。其他类将包含将应用于纹理的UV坐标。我们通常还具有加载纹理或图形文件(如PNG,JPG,TGA等)的功能。您可以编写相当简单的代码,也可以使用很多如果您要进行图形类型编程,则可以使用其中的开源加载器库。 Texture类将包含其他属性,例如mipmap,重复或镜像的质量,质量等等。因此,我们通常将加载纹理并为其分配ID值,并将其存储到哈希表中。如果试图从文件中再次加载该纹理,则代码将识别出该纹理存在并退出该函数调用,否则它将将此新纹理存储到哈希表中。

由于大多数渲染文本都是2D的,因此无需创建3D模型并将其发送到要由model-view-projection矩阵处理的渲染中...我们创建了通常称为Sprite的东西。这是另一类。它具有构成其多边形边缘的顶点。由于精灵是QUAD,因此通常会有4个顶点。它还将具有与其关联的纹理坐标。我们不将纹理直接应用于子画面,因为我们要实例化一个仅在内存中具有单个副本的子画面。我们通常在这里要做的是,将其引用与ID一起发送给渲染器以及对纹理的引用,并发送转换矩阵以更改其形状,大小和世界位置。当GPU通过着色器对其进行处理时,由于它是2D对象,因此我们使用正交投影。因此,这在每个顶点的顶点着色器中保存了一个矩阵乘法。基本上,它将由视图投影矩阵处理。在这里,我们的Sprites和我们的纹理将存储在具有关联ID的哈希表中。

现在,我们有了一个精灵,它基本上是绘制到屏幕上的图形图像,一个可以调整大小并轻松放置在任何地方的简单四边形。我们只需要一个内存就可以了,但是可以绘制成百上千个,因为它们是通过引用计数实例化的。

这对文本或字体有何帮助?您希望将Text类与Font类分开。文本类将包含您要绘制的位置,使用的字体,字体的大小,要应用的颜色以及文本本身……字体类通常会继承基本的Sprite类。 >

我们通常将创建一个单独的工具或迷你控制台程序,使您可以按名称获取任何已知的True Type字体或Windows字体,从而为您生成2个文件。您将标志与其他命令一起传递到程序的命令行参数中,例如-all用于所有字符,或-“ abc123”仅用于所需的特定字符,-12用于字体大小,这样做将生成所需的文件。第一个是单个纹理图像,我们称它为“字体图集”,它基本上是Sprite Sheet的一种特定形式,另一个文件将是CSV文本文件,其生成的值用于每个字符四边形的纹理定位。

在主项目中,字体类将在这两个文件中加载,第一个很简单,因为它只是我们之前已经完成的纹理图像,而第二个是用于获取所需信息的CSV文件。生成所有合适的四边形,但是“ Font”类确实有很多复杂的计算需要执行。同样,当将Font加载到内存中时,我们将执行与以前相同的操作。我们检查文件名或ID是否已将其加载到内存中,如果不是,则将其存储到具有生成的关联ID的哈希表中。

现在,当我们使用Text类渲染文本时,代码可能看起来像这样:

void Engine::loadAssets() {   
    // Textures
    assetManager_.loadTexture( "assets\textures\shells.png" );
    assetManager_.loadTexture( "assets\textures\blue_sky.jpg" );
    
    // Sprites
    assetManager_.loadSprite( "assets\sprites\jumping_jim.spr" );
    assetManager_.loadSprite( "assets\sprites\exploading_bomb.spr" );
    
    assetManager_.loadFont( "assets\fonts\"arial.png",12 );
    // Same font as above,but the code structure requires a different font id for each different size that is used.
    assetManager_.loadFont( "assets\fonts\"arial.png",16 ); 
    assetManager_.loadFont( "assets\fonts\"helvetica.png" );
}

现在,所有这些都作为一个实例存储在我们的AssetManager类中,该类为每种不同类型的资产包含多个哈希表。它的作用是管理它们的内存和生存期。每个实例只有一个实例,但是我们可以引用它们1000次...现在在代码的其他地方,我们可能有一个文件,其中包含一堆独立的enumerations ...

enum class FontType {
    ARIAL,HELVETICA,};

然后在我们的渲染调用或loadScene函数中...。

void Engine::loadScene() {
    fontManager_.drawText( ARIAL,18,glm::vec3(-0.5,1.0,0.5),glm::vec4(245,169,108,128),"Hello World!"); 
    fontManager_.drawText( ARIAL,12,glm::vec3(128,128,0),glm::vec4(128,255,244,80),"Good Bye!");
}

drawText函数将是一个带有ARIAL id并将引用存储到该存储字体的哈希表中的函数。渲染器使用位置和颜色值,字体大小和字符串消息... ID和Size用于检索适当的Font Atlas或Sprite Sheet。然后,将匹配消息字符串中的每个字符,并使用适当的纹理坐标将其应用于根据您指定的字体大小而具有适当大小的四边形。

所有文件处理,打开,读取和关闭的操作都已在loadAssets函数中完成。所有必需的信息已经存储在我们通过实例化引用的一组哈希表中。此时无需担心存储器管理或堆访问,所有这些都是通过利用缓存来完成的。当我们将文本绘制到屏幕上时,我们仅通过矩阵变换通过着色器来操纵像素。

还没有提到引擎的另一个主要组件,但是我们通常使用Batch Process和Batch Manager类,该类处理用于发送顶点,UV坐标,颜色或纹理数据的所有处理。等等...到视频卡。跨总线和/或PCI-Express通道的CPU到GPU的传输被认为很慢,我们不想每帧发送10,000、100,000甚至100万个单独的渲染调用!因此,我们通常会创建一组具有优先级队列功能的批处理,并且当所有存储桶都装满时,优先级值最高或存储桶最全的存储桶将被发送到GPU,然后清空。一个存储桶可以容纳10,000-100,000个基本体...其中一个基本体可以是一个点,一条线,一个三角形列表,一个三角形扇形等,这使代码效率更高。很少使用堆。 BatchManager,AssetManager,TextureManager,AudioManager,FontManager类等都存在于堆中,但是它们的所有存储资产都被引用使用,因此我们可以实例化一个对象一百万次!我希望这个解释会有所帮助。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...