游戏引擎学习第59天

embedded/2024/12/26 20:31:14/

回顾并计划接下来的一天

在处理实体的空间划分时,遇到了一些问题。例如,虽然树和玩家应该在某些情况下被排除在外,但目前的系统仍然会出现不合逻辑的渲染结果,这在视觉上并不符合预期。尽管这些问题主要是渲染上的,并不影响核心功能,但可能会对后续的开发产生一些困扰。对于这些问题,虽然当前并不急于解决,但随着开发进度,我们可能会在合适的时候进行调整。
在这里插入图片描述

接下来,计划开始在系统中添加一些简单的实体。例如,可以添加另一个玩家副本,使其跟随当前玩家移动。这个功能的实现会涉及到一定的代码量,可能需要几天的时间来完成。在此过程中,可能会遇到一些新的需求,比如路径寻找系统(path finding),以及实体如何与环境中的其他物体互动。

此外,可能还会涉及到一些敌对实体的设计,比如怪物攻击玩家。开发过程中,会尝试实现一种机制,使得玩家和怪物之间能够互动,而这些功能的实现将帮助我们更好地理解所需的系统结构和功能需求。这些初步的工作将为构建更复杂的系统奠定基础,避免在后期开发中出现过多集成和调整上的困难。

总的来说,尽管这些任务看起来简单,但实际的开发将涉及到一定的复杂性,尤其是当我们需要实现更复杂的功能时。

game.h:添加 EntityType_Familiar 和 EntityType_Monster

现在计划在第一个房间里放置一个怪物,作为测试的一部分。怪物和一个Familiar的实体将一起进入这个房间。通过使用新的相机位置,可以判断这些实体应该如何移动和定位。在当前的开发中,已有一些简单的概念,比如敌人、英雄和墙壁,但这些只是最基本的部分。

在此基础上,还可以继续扩展系统,加入更多的实体类型,例如测试用的怪物。通过这些简单的测试,可以进一步完善系统结构,并为后续的复杂功能做好准备。

在这里插入图片描述

game.cpp:引入 AddMonster 并更新 AddLowEntity

在当前的开发过程中,首先决定通过添加不同类型的实体来继续进行测试,尤其是怪物实体。接着,通过使用新相机的位置来决定这些实体的移动方向和位置。当前已经有了英雄、墙体等实体,接下来可以添加更多不同类型的实体,比如怪物。

在实现过程中,尝试简化和优化一些操作,比如通过添加低实体类型函数来简化代码。通过为低实体添加AddLowEntity,可以更高效地处理实体的状态和行为。该方法还可以避免每次都执行重复的无意义操作,确保代码更加简洁和高效。

另外,考虑到不同实体类型(如怪物和墙体)的处理,需要保持一致性,确保命名和功能的一致性。例如,怪物实体的宽度和高度可以暂时与玩家的实体保持一致,并且实体之间的碰撞也要一并考虑。在处理这些实体时,通过返回相关的实体索引和指针,可以更方便地管理和操作这些实体。

此外,代码中通过检查和处理实体的冲突,确保实体的行为符合预期。最后,添加玩家实体时,直接从现有实体中获取低索引,简化了操作。整体流程和功能已经完成,当前的实现达到了预期效果。

在这里插入图片描述

添加一个怪物

我们决定继续添加怪物到游戏中。在添加怪物时,考虑到当前相机的位置,我们需要将怪物放置在靠近相机的区域。可以通过相机的瓦片位置来定位怪物的具体位置。例如,利用相机的 X、Y 和 Z 坐标,计算出相应的瓦片坐标,并将怪物放置在这些坐标附近。
为了实现这一点,可以通过简单的数学操作调整怪物的位置,比如将相机的瓦片坐标加上一个偏移量(例如 CameraTileX + 2, CameraTileY + 2)。这样就能确保怪物出现在相机周围的位置,从而实现怪物在玩家视野附近的展示。
这种方法简单且有效,可以使得游戏中的怪物在添加时与相机位置保持合理的关系,提升游戏的互动性和可玩性。
在这里插入图片描述

运行游戏并查看怪物

如果运行当前的代码,似乎会遇到一些问题。最初预期怪物会显示为树,因为树是唯一能绘制的对象,但怪物未能按预期显示。这是因为相机设置的调用是关键,若未正确调用 set camera,则低频实体(如怪物)不会被引入到集合中,因此无法显示在场景中。

这个问题引发了对是否需要检查添加的对象是否处于相机视野内的思考。需要确认在添加实体时,是否应先确保它在相机视野范围内。如果不在视野内,可能不需要将其加入系统中。

解决方案可能是在添加实体时,检查它们是否在相机范围内,确保它们被正确添加到系统中并能够显示。

在这里插入图片描述

重申 SetCamera 调用将一切引入 HighEntity 集合

基本上,问题出在相机设置上。相机设置通常只在初始阶段被调用一次,用于将所有实体加入到高频实体集合中。直到屏幕上有玩家出现,相机设置才会再次被调用。如果相机没有移动,就不会重新调用相机设置。问题发生在玩家没有出现在屏幕上,直到按下空格键并创建玩家。只有当玩家创建后,相机设置才会被再次触发,并且实体才会显示在屏幕上。

因此,这个问题已经修复了,因为相机设置已经移到了正确的位置,确保玩家能够出现在屏幕上。这个问题的解决是简单的,虽然可能不太重要,但还是容易修复的。
在这里插入图片描述

game.cpp:为每个 EntityType 编写绘制案例

现在的目标是将实体绘制得更加易于识别。当前,实体的绘制并没有明确的标准,因此在此阶段并不打算解决这个问题,计划将其推迟到开始使用动画时再处理。为此,可以从最基本的做起,先切换低频实体类型并进行检查,确保能够正确绘制每一种类型的实体。

接下来,会尝试绘制墙壁类型,并使用现有的绘制方式呈现出来。除此之外,还会绘制其他类型的实体,例如“EntityType_Monster”类型。对于未知类型的实体,可以通过断言来确保代码路径有效,这样可以避免忘记处理某些实体类型。通过这种方式,如果忘记处理某种敌人类型,断言会提醒。

最终的目标是绘制出能够表示实体的基本形状,比如地图中的英雄或其他物体,可能仅仅是一个简单的躯干或头部形状。此时,代码仍然处于占位符阶段,只要能正常工作即可。

总的来说,尽管目前没有所有人能看到英雄的地图,但这是为方便自己而做的工作,之后可以根据需要进行进一步的优化和完善。
在这里插入图片描述

运行游戏并首次看到我们的怪物

创建了一个没有四肢的怪物角色,实际上只是角色的躯干部分。怪物看起来像是一个怪物车,虽然只是一个简单的图像,但它的设计意图是作为怪物的表现。除此之外,可以为这个怪物添加阴影,使它的呈现更为完整。同时,也考虑到其他角色是否需要阴影,可以给所有相关角色添加阴影效果。
在这里插入图片描述

在这里插入图片描述

game.cpp:添加一个Familiar的实体

我们将添加一个“EntityType_Monster”和一个“EntityType_Familiar”。对于“Familiar的角色”,目前不确定它是否应该与其他物体发生碰撞,因为它可能是一个漂浮的物体。暂时可以让它自由活动,虽然最终可能会涉及飞行和地面之间的概念,即它仍然可以与其他物体发生碰撞。这样,虽然它的高度是完整的,但它仍然能够悬浮在其他物体之上,类似于标准的游戏机制。

添加“EntityType_Monster”和“EntityType_Familiar”后,两个角色可以同时出现在屏幕上,这是非常好的。为了让这两个角色的位置更加明显,可以将它们放置在屏幕的两侧,相对位置上显示。
在这里插入图片描述

运行游戏并首次看到我们的Familiar的实体

现在两个实体已经被成功地放置在游戏场景中,接下来需要处理控制器逻辑,特别是如何让非玩家的高频实体进行更新。首先,当前的设置允许玩家通过键盘或操纵杆进行控制,确保了玩家能够移动和与其他实体进行交互。
在这里插入图片描述

然而,对于非玩家角色(例如Familiar的实体和怪物),需要设计一个新的机制来控制它们的行为。特别是对于Familiar的实体,可能希望让它们跟随附近的玩家,如果玩家离得太远,它们就停止跟随。

怪物则会基于附近是否有英雄角色做出反应。它们可能会尝试攻击英雄,或者执行其他动作,比如发射投射物,具体动作取决于后续的设计需求。

总之,接下来的任务是为这些非玩家实体设置适当的行为逻辑,以便它们根据周围环境做出反应,完成它们的目标。在此基础上,可以继续测试并调试,确保这些功能正常运行。

game.cpp:考虑如何更新 HighEntities

为了更新高频实体,首先需要遍历所有的高频实体并更新它们的位置。这意味着需要对每个高频实体进行处理,确保它们的状态得到更新。

在实现过程中,有一个问题是操作的顺序需要明确。虽然可以在一个遍历过程中同时进行更新和呈现,但由于目前的代码中存在一些冗余部分(如不再需要的绘制操作),可能会导致混淆。为了清晰起见,应该在合适的地方执行这些更新操作,而无需过多依赖已经过时的部分。

因此,更新操作应该放在一个合适的地方,结合现有的循环结构来实现。这样,既能确保高层次实体的更新,又能避免不必要的冗余和复杂度,使得代码结构更加清晰。

在这里插入图片描述

引入 UpdateEntity

首先,更新实体时,应该传递一个Entity,并使用低亲和力的结构来处理这些实体。对于每个实体,低指数和高指数将分别代表低级和高级状态。通过这种方式,实体将能够在低级和高级状态之间平稳过渡。

在更新过程中,会使用已经定义的时间量(如dt)来进行更新。这是经过的时间,意味着每次更新都考虑了时间的流逝。这个步骤看起来是合理的,因此可以继续执行。

另外,在处理实体时,已检查了其类型,以便正确渲染它。考虑到这一点,可以通过简化调度过程,使其更加高效和智能。这种方法可以使系统更加灵活和优化。

最后,更新过程似乎有一定的可行性和可信度,因此可以继续进行,看看实际效果如何。
在这里插入图片描述

考虑“跳跃代码”并进行一些清理

首先,处理跳跃代码时,存在一些问题,原本把它放置在一个看似无害的地方,但现在意识到它的位置并不合适。跳跃代码并没有清晰的作用,尤其是更新运动方程中与z相关的部分。虽然它可以继续存在,但它的逻辑不够明确。

在考虑如何更新z时,决定将这一部分代码清理,并重新审视是否真的需要将跳跃代码放在此处。经过一番思考,决定将其留在现有位置,如果它有效就不做修改。接下来,计算z的更新将会继续进行,并且考虑是否能将这一部分逻辑整合为通用的运动方程。这样不仅能让所有实体的更新统一处理,还能简化代码结构。

尽管最初有所犹豫,但最终决定继续保留跳跃代码,并将其包含在更新过程中。此外,未来可以进一步优化这些部分,让它们更具逻辑性,并根据需要进行绘制和调整。

game.h:引入 entity_visible_piece 和 entity_visible_piece_group

现在,决定将渲染的概念进行合并。创建一个新的结构,可能命名为“实体可见部分”或其他名称,具体目的是存储与实体渲染相关的信息。这个信息包括实体渲染时的位置偏移(例如玩家的场地位置),以及可能的三维向量和透明度(alpha)。目的是为每个实体创建一个可见的部分,包含多个组件。

这个结构的设计目前较为简单,不涉及太多复杂的逻辑。每个实体的可见部分可能包括多个组件,这些组件暂时没有特别的关联,仅作为一个简单的堆叠,可能会随机分配。

接着,每个实体的可见部分会被组织成一个小的部件组,进一步定义这些部件的属性和行为。这些部件在渲染时将以特定的格式进行处理,并在渲染过程中使用相关信息(如位置偏移、透明度等)。

在这里插入图片描述

将 DrawBitmap 调用更改为 PushPiece

上面的内容涉及了一段关于游戏开发的技术描述,特别是在处理图形和位图渲染的过程中。主要讨论了如何通过改变图形的绘制方式、调整位图的对齐和位置来优化渲染和更新过程。以下是内容的详细总结:

  1. 图像和位图处理:

    • 描述了如何通过调整位图调用的方式来改变图形的显示。提到了“PushPiece”图像并通过循环来管理多个图像的渲染。
    • 讨论了如何通过拆分代码来让更新调用决定图像的具体位置,从而实现更灵活的渲染逻辑。
  2. 更新和渲染:

    • 提到了将多个更新和渲染的操作合并成一个逻辑流,以简化代码结构。
    • 强调了在渲染过程中如何处理位图的偏移和对齐问题,以及如何通过调整Z轴值来控制图形的层级显示。
  3. 调试和优化:

    • 提到调试时,可能需要使用矩形来显示实体的位置,从而帮助跟踪图像的渲染情况。
    • 讨论了如何通过推送调用的方式来管理多个图像的渲染,将它们推入堆栈中进行处理。
  4. 对齐和补偿:

    • 讲解了如何处理图像的对齐方式,并通过调整偏移量来确保图像正确渲染。还涉及了不同图像部件(如手或漂浮部件)的位置和对齐方式。
    • 强调了如何通过精确的对齐和偏移来使渲染更符合逻辑和预期。
  5. 图像渲染流程:

    • 介绍了如何处理多个图像位图的加载,并在更新时处理其对齐和Z轴的位置。
    • 提到通过减少复杂的计算和渲染调用,来提升性能和简化代码逻辑。
  6. 延迟渲染:

    • 讨论了延迟渲染的概念,即在渲染过程中推迟某些计算,直到必要时才进行处理。这有助于优化性能和减少不必要的计算。

总结来说,这段内容主要涉及图像渲染优化的策略,包括位图的加载、对齐、偏移、Z轴控制,以及如何在更新和渲染过程中管理多个图像对象。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

考虑直接进行渲染,而不是延迟

我们发现当前的情况非常复杂,并感到难以决策。目前的思考是,可能并不需要当前所采取的实现方式。或许更合理的做法是直接通过调用运动方程更新的函数来完成任务,而不是将逻辑分散在不同的模块中。这种方法可能会更加直观和简洁。

目前的实现方式尝试将渲染逻辑延迟到更后面的位置,目的是在中间执行一些其他的逻辑。但逐渐意识到,这些逻辑完全可以直接嵌入到现有的模块中,而无需额外的延迟或复杂的结构。

虽然这样调整可能简化部分逻辑,但为了验证当前的设计是否合理,我们决定继续完成当前的方案,这样可以清晰地评估其优缺点。在此基础上,我们可以判断这种设计是明智的还是存在问题。接下来,我们将保持当前路径,同时在之后的开发过程中进一步观察是否有更优的方式优化相关逻辑。

完成实现 PushPiece

我们正在尝试实现“PushPiece”功能,并确认是否有必要性。当前逻辑涉及加载位图、偏移和对齐的处理。我们认为可能不需要当前的PushPiece逻辑,这部分可能是多余的。

对于加载的位图,需要检查是否有足够空间来避免溢出。当空间足够时,继续执行,将数据附加到结构的末尾。通过这种方式完成数据的对齐处理,同时可以将相关的计算内联以减少复杂性。

接下来,处理偏移量时,通过减去对齐值并存储到变量中,同时设定默认参数,以便有些情况可以省略传递。这部分逻辑并不需要返回值,只需完成设定即可。

在过程中,命名变量时发现初始设计可能更为合理,因此对部分变量名进行了调整以更清晰表达其含义。在检查代码时,编译器提示一些初始化问题,例如变量需要从零开始计数等,这些细节已被逐步修复。

同时,在测试时发现布局有所改变,但并非预期行为,可能是由于在错误位置添加了代码导致。因此调整了代码的执行位置,确保每次执行中相关变量的处理是正确的。

最终,我们通过逐步修复和调整,确保了每个功能模块的逻辑清晰,并捕获了可能的断言问题,以确保代码执行的正确性和稳定性。

在这里插入图片描述

运行游戏并注意到我们正在做的事没有变化

我们目前正在做的事情与之前完全一样,没有什么特别之处。然而,现在已经将这些内容整理到了一个分类的形式中。通过这种形式,接下来可以开始将其逐步拆解,以更清晰、更整洁的方式来处理。

game.cpp:为实体添加 Update 调用

目前可以开始更新相关部分,其中墙体并不需要进行更新,但一些附属实体需要更新。因此,需要为这些附属实体添加更新调用。类似地,对于怪物,也需要一个更新调用。这个调用会包含针对这些实体的一系列逻辑操作。

这些操作将会应用于之前已经准备好的实体以及游戏状态,同时也可能需要用到一些参数,比如时间增量 dt。在更新调用完成后,可以继续添加具体逻辑,用于处理这些实体的更新代码。

目前的代码存在一个问题,例如阴影透明度的计算。阴影透明度的值实际上依赖于实体的实际高度(Z 值),因此必须在计算出实际的 Z 值之后再确定阴影透明度。然而,这里逻辑的顺序可能不太正确,导致透明度计算和绘制调用存在一定的逻辑冲突。

进一步来看,或许需要一个专门的实用函数来完成这些计算,然后再进行绘制调用。关于如何调整这一部分,目前尚未完全有一个清晰的答案,但这是一个值得深入思考的问题,后续可能需要针对逻辑顺序进行优化和调整。

在这里插入图片描述

当前错误计算 ShadowAlpha

目前可以暂时计算阴影透明度,虽然结果可能是不正确的。为了简化处理,可以暂时将透明度的计算放在前面,但这实际上是错误的,因为阴影透明度应该在更新逻辑完成之后计算。

当前的解决方案是先在代码中标注这一问题,例如明确说明“此处的透明度计算是不正确的,应该在更新之后进行计算”。这将作为一个临时的解决方法,同时留下改进的提示。

之所以现在进行这些操作,是为了更清晰地观察代码中各种逻辑发生的位置以及原因,从而了解问题的来源并确定后续调整的方向。这种方式有助于理清代码的执行顺序,并在需要优化的地方进行标记,以便后续改进逻辑设计。
在这里插入图片描述

实现这些 Update 调用并为Familiar的实体添加一些逻辑

在现阶段,可以通过创建一些测试函数来为游戏的高频实体和低频实体添加基本的逻辑,比如让某个实体靠近最近的玩家。当前这些实体已经在更新,但还没有实际的更新逻辑。为此,我们计划对高频实体进行遍历,寻找某个特定类型的实体,比如“英雄”。在实现过程中,还涉及了一些关键点:

  1. 简单逻辑测试
    测试函数将通过简单逻辑让实体移动到离它最近的玩家身边,以观察功能效果。

  2. 高频实体的空间分区需求
    当前通过遍历所有高频实体来找到目标,但这种方法可能会导致性能问题(O(n²)复杂度)。为了优化,需要为高频实体实现空间分区,这样可以减少无效遍历。

  3. 最大搜索半径
    为了限制计算范围,我们设置了一个最大搜索半径(比如10米),这样只会处理半径范围内的实体。超过此范围的实体将被忽略。

  4. 距离计算
    使用距离平方的方式计算两个实体之间的距离,避免了直接计算平方根以减少开销。在遍历时,仅保留距离最近的“英雄”实体作为目标。

  5. 移动逻辑
    找到最近目标后,让当前实体移动靠近目标。这部分逻辑需要明确目标位置,并逐步调整当前实体的位置,使其向目标靠近。

  6. 代码结构调整
    在实现过程中,对一些函数的命名进行了调整,使其更符合功能描述。例如将一个函数重新命名为“ForceEntityIntoHigh”,以更准确地表达它将低频实体提升为高频实体的功能。

通过这些调整,可以初步实现高频实体的简单行为逻辑,为后续功能的扩展打下基础。同时,在代码逻辑中保留了一些可以进一步优化的空间,比如通过更高效的数据结构来管理高频实体集合,减少不必要的遍历和计算。
在这里插入图片描述

实现 EntityFromHighIndex

在实现移动Familiar的实体时,考虑到应该使用相同的规则来控制移动,首先需要处理实体的高频索引和低频索引。这样就可以在高低索引之间正确地获取和更新实体的状态。

  1. 索引验证:在处理高频索引时,应当保证这些索引是在合理范围内。如果存在不合法的索引,可能会导致逻辑错误,因此建议在获取索引时进行检查,确保索引有效。
  2. 零索引处理:如果索引为零,应该返回零值,避免出现不必要的错误或重复检查。
  3. 操作细节:在循环中,需要基于高频索引的顺序来获取和更新实体的数据。
  4. 性能考虑:尽管需要进行较多的索引验证和数据处理,考虑到可能的性能问题,还是需要合理评估这种处理方式是否过于复杂。虽然这一处理方式可能导致一些性能开销,但在当前的需求下,它是必要的。
  5. 进一步的复杂性:如果这些操作变得更加复杂,可能需要考虑其他优化策略或者不同的数据结构来提高效率。这些问题需要进一步观察,随着系统的需求变化进行调整。

在这里插入图片描述

将 MovePlayer 更改为 MoveEntity

在完成了“MovePlayer”的调用后,考虑到实体的通用性,决定将其更改为“MoveEntity”的调用。这样,不仅限于玩家的移动,其他实体也可以使用相同的机制进行移动。

虽然玩家有特定的动作处理,比如碰到楼梯时的反应,但这种情况其实对所有实体都适用,因此可以将移动逻辑扩展到所有实体。对于玩家来说,可能有更多与楼梯等环境互动的处理,但对于其他实体,移动的逻辑是通用的。

所以,将“MovePlayer”这一功能调整为“MoveEntity”后,可以更简洁地统一处理所有实体的移动。通过传入游戏状态和实体信息,就可以实现对不同实体的移动操作。此外,可能需要重新计算一些与运动相关的值(比如速度等),并在代码中更新相关部分。

最后,需要调整旧的“MovePlayer调用,改为“MoveEntity”的调用,保持代码的一致性和通用性。

在这里插入图片描述

给familiar实体添加一些朝向英雄的移动逻辑

我们在讨论游戏中的英雄和移动系统。首先,我们要考虑如何处理英雄的位置和我们之间的距离。我们通过计算最近英雄与我们之间的向量,减去我们的位置,得到一个基本的方向向量。

接着,这个向量需要被标准化,使它的长度变为1。我们可以通过除以向量的长度来实现这一点,而这个长度在我们的系统中已经有了,比如通过计算距离的平方得到。然后,我们将得到的单位向量与速度进行乘法运算,得到最终的方向和速度信息。

除此之外,还需要考虑速度的概念,即每秒移动多少米。我们必须考虑加速度的影响,这决定了速度的变化。因此,需要一个对加速度的理解,来帮助我们根据加速度调整移动的速度。

总结来说,目标是将英雄之间的距离转化为一个单位长度的方向向量,再通过速度和加速度调整来实现英雄的移动。

加速度公式

加速度通常用于改变物体的速度。在您的代码中,您使用加速度来调整单位方向向量的大小,使其适应当前的速度和方向。

分解步骤

  1. 定义加速度
    Acceleration 被定义为 10.0f,它是一个常数,用于控制实体的加速。

  2. 计算倒数的长度
    OneOverLength 是距离的倒数,用于归一化。具体来说,ClosestHeroDSq 表示最近英雄和实体之间的距离平方。通过取平方根得到距离,然后用加速度除以这个值,得到倒数的长度(即归一化因子)。

    OneOverLength = Acceleration ClosestHeroDSq \text{OneOverLength} = \frac{\text{Acceleration}}{\sqrt{\text{ClosestHeroDSq}}} OneOverLength=ClosestHeroDSq Acceleration

  3. 计算位置变化量 ddP
    ddP 是方向上的位置变化量,它表示实体需要移动的量。通过计算英雄和实体之间的向量差 (ClosestHero.High->P - Entity.High->P),并用 OneOverLength 来缩放该向量,以确保单位长度的方向,并根据加速度调整其大小。

    d d P = OneOverLength × ( P hero − P entity ) \mathbf{ddP} = \text{OneOverLength} \times (\mathbf{P_{\text{hero}}} - \mathbf{P_{\text{entity}}}) ddP=OneOverLength×(PheroPentity)

    其中 P hero P_{\text{hero}} Phero P entity P_{\text{entity}} Pentity 是最近英雄和实体的当前位置向量。

加速度公式总结

  • 加速度公式:
    OneOverLength = Acceleration ClosestHeroDSq \text{OneOverLength} = \frac{\text{Acceleration}}{\sqrt{\text{ClosestHeroDSq}}} OneOverLength=ClosestHeroDSq Acceleration

    • 这里,ClosestHeroDSq 是从实体到英雄的距离的平方。
    • Acceleration 用于控制移动的速度,影响最终的方向向量。
  • 位置变化量:
    d d P = OneOverLength × ( P hero − P entity ) \mathbf{ddP} = \text{OneOverLength} \times (\mathbf{P_{\text{hero}}} - \mathbf{P_{\text{entity}}}) ddP=OneOverLength×(PheroPentity)

这样,您就能通过加速度调整实体的移动,使它朝着最近的英雄靠近,并根据距离来归一化方向。

在这里插入图片描述

改一下段错误

在这里插入图片描述

运行游戏并调整familiar实体的逻辑

在这个讨论中,目的是调整游戏中的角色(特别是英雄)的加速度和速度,使其行为更加自然。通过观察英雄的运动方式,发现英雄有些过于活跃或迅速,需要通过调整加速度使其动作变得更慢、更迟钝。具体来说,调整加速度的目的是让英雄在跟随玩家时更加平滑,而不是过于迅速或不自然。

首先,发现了加速度不容易确定,可能存在一些问题或错误。通过检查代码,发现可能是计算方式或参数设置不合适,导致英雄的移动速度过快。为了让英雄的动作更符合预期,决定通过调节加速度的值来让它变得更加迟钝,避免过于灵活的反应。

接着,讨论了如何根据单位向量的长度和距离来计算实体的移动。通过乘以“单位长度的倒数”,并根据英雄和玩家之间的距离调整移动的速度。这个过程需要考虑加速度的影响,通过调节 dtp(位置变化量)来控制移动的平滑度。

最后,考虑到英雄的加速度可能是造成这种现象的原因,因此建议先检查英雄的加速度值,以便更好地理解和调整英雄的移动行为,确保它在跟随玩家时变得更加缓慢和迟钝,符合预期的动作表现。
在这里插入图片描述

发现:玩家的速度是固定在 MoveEntity 中的

在这个讨论中,ddP 是位置变化量,表示实体应该沿着某个方向移动的量。讨论中提到,ddP 的计算实际上是根据英雄的速度来调整的,意味着玩家的速度已经融入到这个计算中,导致了玩家和英雄速度的完全一致。

这是因为函数中的某些参数(如速度)已经被直接用来计算 ddP,使得最终的结果与英雄的速度保持一致。因此,玩家的速度在计算过程中并没有单独被区分开来,导致其与英雄的速度同步。

总结来说,ddP 是通过融入玩家的速度,计算出与英雄之间的运动关系。这种方式导致玩家和英雄的速度完全相同,从而可能产生了一些意外的同步效果。
在这里插入图片描述

使familiar实体的速度为英雄的一半,并在游戏中查看

在这个讨论中,主要目标是调整实体的移动行为,使其更加自然。当前问题是,实体的速度完全与英雄的速度一致,这样导致实体的行为不够灵活。为了解决这个问题,计划将实体的移动速度设置为英雄速度的一半,这样实体就不会过于迅速,而是能够以较慢的速度靠近英雄。

此外,还需要对实体的行为做进一步的优化。当玩家与实体的距离足够远时,实体应该停止跟随并停下来,而不是一直跟随。这个停下的过程应该是平滑的,而不是突然停止。因此,计划引入一种更慢的运动方程,使得实体在不再跟随玩家时能够逐渐减速。

接着,讨论了如何让实体在没有目标可跟随时仍然保持滑行状态,直到停下来。这意味着如果没有目标,实体会继续滑行,直到最终停下,而不是突然停止。

最后,还提到了一个路径寻找的问题:当实体被困在某个地方无法到达玩家时,需要更智能的路径寻找系统。希望通过引入更高级的特性,使得实体能够在自己的环境中智能地导航,避开障碍物,而不是盲目地跟随玩家。这将使得游戏更具挑战性和智能性,避免游戏中的怪物行为显得过于愚蠢。

在这里插入图片描述

跟随的过不去门

在这里插入图片描述

问:如果有人用 OOP 重写你的代码并且它运行得更快,你会怎么做?

讨论中提到,如果某人使用面向对象编程(OOP)重写代码并且让代码运行得更快,这样做可能有助于开发效率的提升。然而,重点并不完全在于速度优化,而是更多地关注于开发设施和方法的改进。

问:为什么你想要能够超越你的Familiar实体?难道不得不停下来等它追上来不烦人吗?

目标是让游戏中的角色能够在离开玩家一定距离后“失去目标”,并在玩家靠近时重新“找到”目标。这涉及到设置一个搜索半径的概念,其中角色会根据距离判断是否能够找到玩家。

同时,提到当前并不是在进行游戏设计,而是正在实现一些功能,以便理解它们如何在引擎中工作。对角色的初始位置和分开启动角色的方法也进行了讨论,特别是确保新角色与其他角色之间有一定的空间,而不是重叠在一起。

此外,提到添加多个角色时,确保它们不会互相重叠,位置可以通过相机加上偏移来调整。虽然目前没有打算立刻处理这些细节,但目标是能有效管理角色之间的空间。

问:能否让头部上下晃动,同时跟随玩家?

讨论中提到的目标是让头部在跟随玩家时上下起伏。实现这一效果的关键是为头部位置设置一个基于正弦波的z轴偏移量,这样就能使头部在垂直方向上实现周期性的起伏。
具体步骤包括:

  1. 为实体创建一个控制头部上下浮动的函数,这个浮动可以通过正弦波来模拟。
  2. 在更新头部位置时,将z轴的偏移量设置为正弦波函数的值,这样头部就能随着时间上下起伏。
  3. 调整浮动的速率和幅度,以控制起伏的频率和幅度。
  4. 如果需要,可以调整头部起伏的表现方式,以便它看起来更加自然,避免过大或过小的浮动。
    此外,讨论中提到,头部的上下浮动可以在角色远离玩家时停止,直到玩家接近时重新开始跟随。这个效果可以通过周期函数来实现,确保头部浮动的平滑性和自然感。

在这里插入图片描述

问:我们最终会使用基于组件的实体系统,而不是使用实体类型的枚举吗?如果不使用,为什么?

  1. 关于“基于组件的能量系统”的质疑

    • 提到“基于组件的能量系统”这个术语不够具体,可能意味着很多不同的东西,这种模糊性使得该术语难以准确理解。
    • 表达了希望没有人发明这个术语的想法,因为它的含义太宽泛,没有特定的指向性。
  2. 关于是否使用基于组件的系统

    • 讨论了是否最终会使用基于组件的实体系统的问题,这取决于游戏规则如何发展和代码的实际实现。
    • 表示可能会有“可组合的实体块”的概念,但也可能仍然保留传统的实体类型的概念用于其他目的。
  3. 关于压缩和权衡

    • 认为是否使用基于组件的系统完全是一个关于压缩和权衡的问题。
    • 强调需要根据实际需求来看选择哪些系统和组件来实现,选择的过程是权衡不同因素的结果。
  4. 对于“基于组件的系统”的不理解

    • 表示并不理解为什么人们通常会做出“基于组件的系统”与其他系统之间的区别,觉得这区分是很随意的。

问:你会考虑为familiar实体使用比例积分微分控制(PID)来跟随吗?

这段内容讨论了使用比例积分和微分(PID)控制来让familiar实体跟随玩家,并提出了让这些实体滑翔到停止的位置的想法。具体总结如下:

  1. 关于PID控制的讨论

    • 提到是否考虑使用比例积分和微分控制(PID控制),这样familiar实体可以更平滑地跟随玩家。PID控制可以帮助实现更精确的追踪,避免实体突然停止或移动不稳定。
  2. 让实体滑翔到停止的位置

    • 提出让familiar实体滑翔到靠近玩家的地方,并平滑停止。这样可以避免实体过于突兀地停下,而是通过滑翔的方式逐渐减速,平稳地停在玩家附近。
  3. 碰撞系统的影响

    • 讨论了碰撞系统对实体停止的作用。当实体靠近玩家时,虽然理想情况下实体应该平稳地停下来,但实际上,当前的实现中,实体是通过碰撞系统被停止的,可能导致一些不如预期的行为。虽然碰撞系统可能不会完全阻止实体的运动,但它在实体接触时发挥了一定的作用。
  4. 领域问题与引擎架构

    • 强调这类问题(例如实体滑翔到停止)更多是属于领域问题,即与游戏中的具体行为相关,而非游戏引擎架构的设计。因此,这类问题可能不需要对引擎架构做出重大调整。
  5. 未来的可能性

    • 提到虽然当前问题可能不需要立即处理,但未来可能会考虑更完善的解决方案,比如通过PID控制来改进实体的行为。

总的来说,这段讨论了如何改善实体跟随玩家的方式,考虑了使用PID控制实现更平滑的跟随行为,同时也提到当前碰撞系统的局限性,以及这类问题对于引擎架构的影响较小。

问:关于 EntityType 上的那个切换,我有点不安。你认为它在接近最终架构时会被淘汰吗?

讨论了关于实体类型切换的问题,并提出了对最终架构的预期。具体内容总结如下:

  1. 对实体类型切换的担忧

    • 表达了对目前实体类型切换的方式感到不安,担心随着架构的完善,这种切换方式可能不再适用。
  2. 切换方式的可能变化

    • 强调实体类型切换的方式可能会发生变化,不再严格依赖于单一的实体类型。切换可能会基于多个部分,而非仅仅依赖于实体的类型。
  3. 满足基于组件的实体系统的需求

    • 这种变化的方式将会更加符合一些开发者对于基于组件的实体系统的需求。通过这种方式,可以更加明确地识别每个实体所包含的组件。
  4. switch语句的使用

    • 尽管切换的方式可能会发生变化,switch语句仍然会存在,可能不会完全去除。但它的使用不再局限于实体类型,而是可能与其他部分的切换有关。

总结来看,虽然目前的实体类型切换方式可能会改变,但更灵活的架构设计将使其更适应基于组件的系统,同时仍保留一些传统的结构(如switch语句),以确保功能的可维护性和可扩展性。

问:碰撞是仅限 XY 方向,还是会考虑 Z 轴,例如跳过另一个实体?

讨论了碰撞处理时,特别是在2D游戏中的Z轴问题。以下是详细总结:

  1. 碰撞处理的范围

    • 讨论了游戏中的碰撞是否只在X和Y坐标轴之间发生,或者是否考虑Z轴,特别是涉及跳跃越过其他实体的情况。
  2. Z轴的处理

    • 由于2D游戏的特殊性,Z轴并不像3D游戏那样有实际的物理位置,而是更多的概念化。Z轴通常不像X和Y那样直接处理,而是需要做出特殊的让步,以适应正交视角和游戏的需要。
  3. Z轴的影响

    • Z轴的考虑不会简单地视为独立的坐标,可能需要通过某种形式的映射或概念化处理。它的处理方式将更加符合游戏的视觉效果和物理模拟,而不是完整的几何形状。
  4. Z轴与游戏效果

    • 尽管Z轴的处理可能没有像X和Y轴那样明确,但为了使游戏的物理和视觉效果更自然,需要对Z轴进行特殊的处理。这并不是为了模拟真实的三维空间,而是为了在2D环境中尽可能合理地表现物体的行为。

总结来说,Z轴的处理将是一个更加概念化的过程,游戏将做出必要的妥协,以适应2D游戏的视角和物理特性。这将确保游戏中的实体行为和视觉效果更加协调。

问:我感觉你的低频实体如果有 AI 之类的功能,会非常庞大。对于一个巨大的世界来说,这会是个问题吗?也许如果你保持将瓦片数据分块存储,可以在需要时压缩一些实体,这样会更好。

讨论了如何在游戏中管理低频实体和存储数据。以下是详细总结:

  1. 低频实体的存储

    • 在处理低频实体时,存储数据是不可避免的。必须存储所有需要的数据,以便在需要时进行访问和使用。
  2. 数据压缩

    • 尽管可以尝试在运行时进行数据压缩,考虑到实体的大小和内存需求,可能并不需要频繁地压缩数据,因为这些实体的大小可能不够大,不至于需要通过压缩来节省内存。
  3. 存储策略

    • 尽管可以使用压缩方法来优化存储,但实际存储数据的策略要求在没有其他选择的情况下,仍然必须存储这些数据并确保其正确性。
  4. 性能提升

    • 在低频实体的存储和管理中,优化存储数据的方式对于性能是至关重要的。确保只在必要时存储和加载数据可以有效提升整体性能。

问:低频/高频交换所带来的性能提升,什么时候会体现出来?

  1. 性能优化

    • 通过将实体分为不同的层级,减少每次循环时需要遍历的实体数量,从而大大降低了性能负担。原本需要遍历大量实体的情况,现如今只需遍历较小的层级集合。
  2. 层级循环

    • 在旧的方案中,所有实体都需要被一一检查,导致性能下降。但现在,只需针对层级进行循环,范围更小,提升了效率。
  3. 分割优化

    • 这些层级可能会进一步细分,进一步缩小每次需要处理的数据量,从而实现更高效的性能。
  4. 性能提升

    • 通过这种方法,性能已经得到了显著提升,优化使得实体管理更加高效。

问:我们能生成一堆这些漂浮的头部实体吗?

在解决问题时,我们发现了几个关键点和需要改进的地方:

  1. 漂浮物体的生成问题
    • 在处理漂浮头部物体时,最初的实现没有生成正确的坐标,导致了不合适的位置计算,尤其是当生成物体时它们与其他物体重叠。
    • 使用了随机数生成来调整位置,但发现没有一个有效的随机数生成器,导致需要手动调整生成的逻辑。

在这里插入图片描述

  1. 偏移与对齐

    • 在生成物体时,需要考虑如何处理与其他实体的对齐。特别是在瓷砖映射和块对齐时,必须确保正确计算每个块的中心位置。之前的实现存在问题,导致坐标计算错误,生成的物体位置不准确。
    • 为了确保生成物体时不发生位置冲突,需要在块的边界上进行对齐,并调整偏移量。
      在这里插入图片描述
  2. 代码中的错误和调试

    • 在调试过程中,发现了一些错误,特别是在初始化和更新熟悉物体时,导致它们出现意外行为。错误可能是因为没有正确地更新熟悉物体的状态,或者在计算位置时没有考虑到特殊情况(如与其他物体重叠的情况)。
    • 通过逐步检查,发现错误与瓷砖转换中的坐标计算有关,需要进行进一步的修正。
      在这里插入图片描述

在这里插入图片描述

这段代码返回的结果非常大,是由于以下问题:

代码分析

float result = ((uint32_t)(0) - (int32_t)(0) - (int)(8)) * float(1.4);
1. 数据类型的作用
  • uint32_t 是一个无符号整数类型,范围是 [0, 2^32 - 1]
  • int32_t 是一个有符号整数类型,范围是 [-2^31, 2^31 - 1]
  • 在表达式中,如果有无符号数参与运算,可能会导致隐式类型转换。
2. 具体运算过程
(uint32_t)(0) - (int32_t)(0) - (int)(8)
  • 第一步:
    (uint32_t)(0)(int32_t)(0) 都为 0,此时进行减法:
    0 − 0 = 0 0 - 0 = 0 00=0

  • 第二步:
    0 - (int)(8) 的实际计算是:

    • (int)(8) 是一个有符号整数,值为 8
    • (uint32_t)(0) 是无符号整数,减法时会将 int 转换为 uint32_t,而 -8 在无符号上下文中会溢出。
  • 溢出:
    在无符号数中,-8 会被解释为 UINT_MAX + 1 - 8,即:
    2 32 − 8 = 4294967288 2^{32} - 8 = 4294967288 2328=4294967288

3. 最终结果
result = 4294967288 * float(1.4);

计算:
4294967288 × 1.4 = 6012954203.2 4294967288 \times 1.4 = 6012954203.2 4294967288×1.4=6012954203.2

因此,result 的值非常大。


解决办法

如果不希望出现溢出问题,可以显式地将所有数值统一为有符号类型。例如:

float result = ((int32_t)(0) - (int32_t)(0) - (int)(8)) * float(1.4);

在这种情况下,所有运算都将在有符号整数范围内进行:
0 − 0 − 8 = − 8 0 - 0 - 8 = -8 008=8
然后乘以 1.4
− 8 × 1.4 = − 11.2 -8 \times 1.4 = -11.2 8×1.4=11.2

这样,结果就不会异常。

负值的偏移量的问题

在这里插入图片描述

在这里插入图片描述

  1. 偏移量计算中的问题
    在实现中,我们尝试计算某个实体所在的块以及偏移量。对于负值的偏移量,目前的计算方式会导致错误,因为当前的实现未正确处理零边界和负值情况。例如,当偏移量为负时,计算会错误地将其归类到前一个块而不是下一个块。

  2. 问题的成因

    • 当偏移量接近块的边界时(尤其是跨过零边界),偏移量被截断并导致错误计算。
    • 这种错误发生在分割点附近,由于数学运算中的截断特性,程序选择了错误的块。
  3. 解决思路

    • 我们可以通过检测偏移量是否为负值来进行调整:如果偏移量是负数,可以将其“向上移动”一个单位来确保正确的块归属。
    • 这种调整方式简单但不优雅,也可能带来精度问题,因此需要更高效的方法。
  4. 熟悉实体的生成

    • 当前代码在地图中添加多个熟悉实体(familiars),其位置通过随机偏移计算得出。
    • 如果随机生成的偏移量过大,熟悉实体可能被放置在地图范围之外,这可能导致一些问题。
    • 需要限制偏移量的范围,确保熟悉实体的生成位置合法且在地图范围内。
  5. 熟悉实体的行为问题

    • 在生成的熟悉实体中,它们会堆叠在一起,导致视觉效果较差。
    • 由于熟悉实体的行为是追逐同一个目标,它们可能集中到一个点,失去了多样性和趣味性。
  6. 改进方向

    • 碰撞处理:让熟悉实体之间可以发生碰撞,避免它们过于集中。这种方式能够增加互动性并改善视觉效果。
    • 位置生成优化:在生成熟悉实体的位置时,可以引入规则或约束,确保它们分布更均匀,同时避免位置冲突。
    • 实体行为改进:可以调整熟悉实体的AI逻辑,使其行为更具独立性,而不是简单地集中到同一个目标。
  7. 潜在问题与未来优化

    • 引入熟悉实体间的碰撞可能导致路径阻塞,需要进一步优化路径规划和实体推挤逻辑。
    • 随机生成的熟悉实体需要更合理的逻辑约束,例如根据玩家位置动态调整生成区域。
    • 如果继续增加复杂性,需要关注性能问题,避免过多的实体导致计算量过大。

整体来说,通过优化偏移量计算、熟悉实体的位置生成以及行为逻辑,可以有效改善当前实现中的不足,提升系统的稳定性和趣味性。
在这里插入图片描述

在这里插入图片描述

问:Z 轴是如何工作的?

我们正在考虑如何处理游戏中的Z坐标。当前的目标是允许角色在空中漂浮,比如在飞行时或其他特殊情况下,从而使角色能够越过某些障碍。

此外,还提到一种可能性,即通过地面上的一个洞向下查看更低的区域,这种设计可能会增加游戏的趣味性。因此,我们认为这可能是一个值得尝试的方向,但具体实施还需要进一步研究和决定。

关于Z坐标的设计,我们认为这在两种游戏模式中都会成为一个关键问题,因此需要灵活应对。我们会根据实际需求设计和处理Z坐标的功能,而不是单纯按照常规去做。

在完整的三维游戏中,很多事情的复杂性会增加,但与之相对的,有些方面会变得更加直接。例如,在三维环境中,不需要通过额外的技术去模拟Z方向的效果,这样可以减少某些设计的复杂性。总的来说,Z坐标的处理需要根据游戏的具体需求和玩法来决定。

问:会有一个系统让所有漂浮的头部实体跟随英雄形成群体吗?

目前还没有开始考虑实现漂浮头部飞行英雄的群体系统。这一功能可能会在未来的开发中被引入,但暂时并不在当前的计划中。

问:高频实体的路径寻找会如何实现?它们和远距离的低频实体有什么不同?而且那些总是在移动的低频 NPC 会一直被解决吗?遇到一个充满活力的世界,充满历史、尸体等,会很有趣,但这可行吗?

目前对于NPC的路径发现还不清楚具体如何实现。与远距离的低频实体相比,低频实体通常是结构化的,因此在路径发现上可能需要采取不同的方法。考虑到世界的结构,可以尝试通过某些方法来方便地处理路径发现,从而提升效率。然而,对于远距离的低频实体,还没有一个明确的解决方案,这部分可能还需要进一步研究和探索。

问:将多个摇头实体连接成链条跟随彼此会有多容易?

将多个漂浮的头部串在一起是比较简单的,因为它们只需要跟随某个目标,如英雄或familiar实体。可以通过设置某些条件让这些头部实体跟随目标,例如,当实体类型为“英雄”时,它们会优先跟随。当前的挑战在于,当这些头部聚集在一起时,它们似乎没有足够的灵活性,导致无法与目标更接近。为了解决这个问题,可以引入某些机制,比如如果目标是英雄,头部实体会获得优待或偏向。

此外,也可以尝试一些更复杂的逻辑,比如设置跟随编号或定义某种亲和力系统,使得头部实体与目标之间的距离更为灵活。然而,这样的系统可能还不成熟,需要进一步的实验和调整。现在的计划是暂时只让这些实体跟随英雄,避免过多的复杂性,保持系统的简单性和可控性。
在这里插入图片描述

问:我很高兴听到你认为类基本上是垃圾。我花了太多时间用 C# 学习它们之间的对象交互

表达了对某个系统或概念的强烈不满,认为它在实际应用中不太有效。接着提到花费太多时间在一个问题上,试图将一个对象引入到复杂的操作中去,但那不是核心问题。尽管如此,仍然表示这个问题的存在并不会影响最终的结论。

问:关于链式familiar实体,能否让每个familiar实体检查英雄是否已经有了一个familiar实体,如果有,它会尝试跟随那个familiar实体?

讨论中提到可以通过检查当前是否已经有一个familiar的对象,如果有的话就尝试跟随这个familiar的对象。为了实现这一点,需要将某种计数器机制传递到外部,计数器应该总是取最大值。尽管这一做法在理论上可行,但由于时间有限,最终决定将这一功能推迟到下一个阶段执行。这种做法在实现上并不复杂,可以在未来的版本中继续进行。

仓库: https://gitee.com/mrxiao_com/2d_game


http://www.ppmy.cn/embedded/148988.html

相关文章

nacos-服务发现注册

服务发现注册分为三个角色:服务注册中心、服务提供者、服务消费者 服务注册中心:为服务提供者和消费者提供一个空间,服务提供者将自身服务注册到注册中心,仅对外暴露接口,服务消费者在将自身注册到注册中心的时候也会获…

C语言从入门到放弃教程

C语言从入门到放弃 1. 介绍1.1 特点1.2 历史与发展1.3 应用领域 2. 安装2.1 编译器安装2.2 编辑器安装 3. 第一个程序1. 包含头文件2. 主函数定义3. 打印语句4. 返回值 4. 基础语法4.1 注释4.1.1 单行注释4.1.2 多行注释 4.2 关键字4.2.1 C语言标准4.2.2 C89/C90关键字&#xf…

地理数据库Telepg面试内容整理-数据库设计与性能优化

在开发和维护 Telepg 地理数据库时,合理的数据库设计与性能优化是确保系统稳定、高效运行的关键。以下是针对数据库设计与优化的详细指南。 数据库设计原则 (1) 明确需求 ● 数据类型分析: ○ 确定需要存储的空间数据类型࿰

2. Kafka入门-开发环境准备

Kafka入门-开发环境准备 1. 环境准备2. Centos7安装2.1 镜像安装2.2 初始化配置2.3 JDK1.8安装 ---------------------------------------------------------------------------------------------- 1. 环境准备 2. Centos7安装 2.1 镜像安装 2.2 初始化配置 设置系统时区 …

嵌入式学习-QT-Day07

嵌入式学习-QT-Day07 七、文件IO 1、QFileDialog文件对话框 2、QFileInfo文件信息类 3、QFile文件读写类(重点) 4、UI与耗时操作 5、QThread线程类 5.1 复现程序未响应 5.2 创建并启动一个子线程 5.3 异步刷新 5.4 线程停止 6、数据持久化 七、文件IO 1、…

【QT开发自制小工具】PDF/图片转excel---调用百度OCR API接口

前言 前几年WPS还可以免费处理5页以内的PDF转excel,现在必须付费了,而且百度其他在线的PDF转excel都是要收费的,刚好前几年调研过百度OCR的高精度含位置接口,依然是每天可以免费调用50次,本篇是基于此接口,…

如何使用java来解析一个pdf文件呢?

最近搞到一个任务是要解析一套雅思题目并提取其中的高频单词。那如何使用java来解析一个pdf文件呢? 首先我们要知道这需要springboot框架来进行创建,需要的PDFTextStripper是一个用于PDF文档中提取文本的类,它是Apache PDFBox的一个类用于处…

华为云鸿蒙应用入门级开发者认证考试题库(理论题和实验题)

注意:考试链接地址:华为云鸿蒙应用入门级学习认证_华为云鸿蒙应用入门级开发者认证_华为云开发者学堂-华为云 当前认证打折之后是1元,之后原价700元,大家尽快考试!考试题库里面答案不一定全对,但是可以保证…