游戏引擎学习第283天:“让‘Standing-on’成为一个更严谨的概念

如果同时使用多个OpenGL上下文,并且它们都有工作负载,GPU或GPU驱动程序如何决定调度这些工作?我注意到Windows似乎优先处理活动窗口的OpenGL上下文(即活动窗口表现更好),挺有意思的……

当多个OpenGL上下文同时使用且都有工作负载时,GPU或GPU驱动程序如何决定调度这些工作负载实际上并不清楚。Windows似乎会优先处理当前活动窗口的OpenGL上下文,这点很有趣。

OpenGL在Windows环境中本身就存在一些复杂问题,因为微软倾向于推动自己的标准,对OpenGL的支持并不充分。但驱动程序厂商希望支持OpenGL,因此通常会有自己的解决方案。

以NVIDIA为例,调度策略可能会依赖于使用的显卡型号。高端显卡的驱动程序甚至允许将不同的GPU单独分配任务。理论上,如果有多个GPU,驱动可能会在它们之间平衡负载。

但具体如何平衡不同OpenGL上下文的工作负载,尤其是在多个应用同时运行时,比如三个应用都在用OpenGL,驱动如何切分资源分配,是个复杂问题,不同厂商的实现可能也不一样。优先级是否会影响OpenGL上下文的执行资源分配,目前没有明确的公开信息。

这方面的细节并不是很清楚,也很少有人深入研究,最好的方式是直接问显卡驱动厂商。

你怎么看基于流程图的编程?你觉得它们在游戏编程中有效吗?

对于流程图式编程的看法以及它在游戏编程中的效果,实际上并不太清楚“流程图式编程”具体指什么。虽然对流程图的概念很清楚,但并不确定将编程描述为“基于流程图”的意思是什么。

如果指的是类似UML(统一建模语言)这样的图示,那个人并不认为这类图示对编程有用,尤其是对任何类型的编程都不认为它们有实际帮助。但也可能这并不是“流程图式编程”的真正含义,所以整体上并没有明确的判断或结论。

我更多是想说类似UE4用蓝图实现的那种

如果说流程图式编程是指那种视觉编程语言,比如用来制作简单效果或功能,通过拖拽节点来实现的系统,从所见过的情况来看,这类系统的价值并不大。它们主要帮助用户做非常简单的事情,不需要写代码,直接拖拽节点就能完成,但制作简单东西对我们来说并不难,也不需要特别的工具辅助。

编程真正难的地方是做复杂的系统和功能,而这正是这些视觉编程系统的弱点。复杂起来的时候,节点之间关系混乱,界面变得杂乱无章,根本看不懂,也很难管理。到目前为止,没有看到视觉编程语言能够做出真正有趣且复杂的东西。

这并不意味着视觉编程没有潜在价值,只是还没有人成功利用它们的图形特性,来让编程变得更简单、更高效。反而目前的做法往往让复杂的东西更难管理,适得其反。理想状态下,图形化编程应该用图形的优势来简化编程过程,而不是让它更难。

你怎么看结构化编辑器?

我们对结构化编辑器的看法是:这个概念本身挺吸引人的,觉得它是一种不错的发展方向,特别是它让编辑的重点更聚焦在“编辑的内容本身”上,而不是仅仅作为一段普通文本来处理。这种思路比起传统的纯文本编辑方式更具针对性和结构性。

我们喜欢结构化编辑器的理念,认为它值得探索,是一种值得并行推进的工具演进方式。然而,虽然认可这种方向,但实际上我们并没有真正使用过这类编辑器,因此对它的实际效果没有太多经验,不清楚是否已经有谁真正实现了一个好用、成熟、实用的结构化编辑器。

也就是说,理论上我们支持这种形式,但实践中是否真的能带来提升,还不确定。有待进一步观察是否有人能真正将其做成一个好用的工具。

我觉得盒子和箭头类型编程的主要用途是让非程序员觉得自己没在写代码

我们认为“盒子和箭头”式的编程方式(即图形化编程、节点式编程)主要的使用场景,其实是为了让非程序员感觉自己没有在“编程”。换句话说,它的核心作用并不是真正提高编程效率,而是降低编程的心理门槛——让那些原本可能会被代码吓退的人,也能参与到逻辑构建中来。这种方式很适合向非技术用户“伪装”成一种非编程的操作方式。

虽然从技术角度来看,它并不一定有助于构建复杂系统,但在教育、演示、或低门槛应用开发场景中,它确实有其“说服性”作用。简单直观的图形界面加上逻辑连接线,很容易给人一种“只是在搭建流程”的感觉,从而弱化了“写代码”的印象。

开始今天的内容

目前我们要做的是开启模拟系统中的一个新方面,并让它的更新机制更完善。之前我们已经修复了模拟相关的一些问题,现在正在逐步朝着更接近最终游戏代码的方向前进。

我们需要着手处理一些更严肃的问题,特别是关于物理系统渲染系统的内容。之前我们使用的是一种临时的、非常简单的物理与移动逻辑,只是为了快速搭建基础系统。但接下来我们要构建的是一种更“完整”的方式。

所谓“更完整”,并不意味着模拟真实世界的物理行为,而是要构建一种更加专业、结构清晰、设计简洁的物理处理方式,让这部分代码的架构变得可靠且可维护。

今天我们需要做两件事:

  1. 制定并实现新的物理系统,让实体的移动和交互更合理,具备更清晰的状态控制;
  2. 开始解决关于**多层深度(z轴多重叠层)**的渲染问题。昨天我们已经提出了目标,现在要正式着手编码实现,使其在渲染中表现得更加高效、清晰。

运行游戏,设置当天场景

现在我们要开始今天的开发任务。

首先要明确一点:在着手构建新系统之前,应该先打下基础,空着什么都不做是没意义的。所以我们先运行一下当前的状态,看看到哪一步了。

目前我们处理的是**多层高度(Multiple Heights)**的问题。我们之前在这部分做过一些尝试,但现在可以看到存在明显的排序错误。例如角色的位置排序不正确,角色的头部显示在了错误的位置。这是因为当前系统使用的是一种带偏移的排序逻辑(bias sorting),而偏移值并不能正确表示实际的空间顺序。

我们有很多地方需要修复,现在正是一个很好的时机来全面重新梳理物理系统Z轴高度(z)的处理机制,从而将当前这些零散不规范的系统整理成一个可靠、稳定的状态,避免在将来的开发中不断返工。

接下来我们计划分两步进行:

  1. 先处理世界空间中Z轴的表达方式:我们要先确保高度的表达结构合理、清晰、易维护,先不涉及渲染;
  2. 再将这一表达方式传递给渲染系统:这样我们可以改善目前的排序错误,比如让头部总是显示在身体上方等,这样的规则渲染系统目前并不了解。

目前渲染器的排序能力是不错的,但输入数据太差,导致最终渲染效果不理想。比如渲染系统不知道“头永远在身体上方”这种逻辑,因此没法正确处理绘制顺序。这些逻辑我们现在开始要系统性地考虑和处理。

接下来,我们观察到一个物理上的问题:比如角色无法往北移动。这是因为角色的头部会撞到地形的碰撞体积(collision volume)。这些地砖(棕色小块)虽然我们没有在图形上画出来,但它们是有碰撞体积的。当头部在物理模拟中“撞到”这些碰撞体积时,会阻止角色继续移动。

这其实是合理的现象,因为我们希望有碰撞逻辑。但目前还没有去精细设计这些体积的形状、大小和放置方式。随着我们对系统行为越来越清晰,现在正是时候去定义合理的碰撞体积结构,并且构建出更完整的碰撞检测系统。

更进一步地,由于这款游戏的设计具有特定的机制:角色的头部和身体是分开的两个部分,控制的是“头部”的移动,而“身体”会自动跳跃移动到特定的格子上。这种设计让我们不能采用标准的物理模拟,而必须实现一种**具有明确二值判定(能不能跳过去)**的游戏逻辑。

如果我们采用现实物理模拟,跳跃和移动行为将变得连续而模糊,而这正违背了我们设计中那种“格子跳跃式”的清晰判定。比如是否能跳到下一个地块,不应该依赖于“是否差一点点撞上”,而是要有明确的判定:可以或不可以

因此,我们将不采用真实物理引擎那种连续碰撞检测方式,而是要开发一种非常特定的、服务于本游戏风格的离散跳跃判定机制

黑板讲解:原子跳跃和浮动位置

我们希望传达的核心思想是:这个游戏的移动和碰撞机制,不能依赖传统物理模拟,而需要一套自定义、明确、原子化的判定规则系统

假设我们设计了两个角色都试图跳跃到同一个目标格子,我们必须定义一个机制来处理这种**“同时入场”冲突**。

我们设想了几种解决方案:

  1. 双方弹回:两个角色在空中相撞,谁都无法落地,双双回到出发格子。
  2. 先到者得:谁先进入落点区域,谁就占据该格子,另一个回退。
  3. 共享位置:允许两者共处一格(但这种方案一般不太可行)。

无论选择哪种方案,都将引发更复杂的连锁问题。例如,如果多个角色同时朝多个方向跳跃,某个角色原地不动但被其他跳入路径阻断,就会出现没有落点可回退的问题,也就是角色“被困在空中”的状态。

因此,需要设计一套规则,确保每个角色总是有一个明确、合法的落点,不可能悬而未决

一种解决思路是引入**“原子跳跃(Atomic Hop)”**的机制:

  • 每个角色从一个格子起跳,进入“目标格子”的中线边界区时,会触发一次原子性尝试
  • 如果此时目标格子允许被占据(未被他人优先取得),则角色会原子性地释放当前格子并成功占据目标格子
  • 如果目标格子无法占据,角色立即放弃跳跃并退回原位,此操作也是原子的,不会出现“悬空”状态。

这种机制是和物理模拟完全不同的设计,强调的是空间控制权的原子判定,而不是连续运动和碰撞处理。

此外,还将引入另一类物体行为:**浮动类(Floating)或投射类(Projectile)**对象:

  • 它们不像角色那样“占据格子”,而是可以在空间中自由移动、穿越,不会对格子产生“拥有权”;
  • 它们使用更常规的运动规则,比如速度、碰撞等,更偏向传统物理模拟。

总结目前的物理系统划分,我们将其分为两类:

  1. 原子跳跃类型(Atomic Hop)

    • 用于角色行为,强调空间控制权,不能被物理模拟替代;
    • 如角色的身体(body),必须落在格子上,并决定是否成功跳跃;
  2. 浮动/投射类型(Floating / Projectile)

    • 用于头部、飞行物、某些敌人或技能;
    • 在空间中自由移动,不持有格子,不参与原子跳跃逻辑。

这种划分也符合当前游戏的特殊性:头部和身体是分离的,身体必须精确落点,而头部则在空间中“自由移动”,不与地面交互。

虽然这些规则听上去清晰,但在实现上仍存在一些边界情况的微妙问题,比如角色被击退时穿越多个格子,该过程也需要特殊处理以保持一致性。因此,整个物理系统虽然不复杂于传统物理模拟,但更具有规则性与特殊性,需要细致地构建与验证。

我们目前面对的问题是关于游戏中角色移动与空间占用的设计逻辑,尤其是在存在击退、冲突、投射物等复杂情况时,如何确保角色在跳跃和移动中始终拥有稳定的站立格子,避免“无处可落”的错误状态。

一、空间占用必须是事务性的

我们明确了一点:角色从一个格子移动到另一个格子,这个动作必须是事务性的,即:

  • 只有在目标格子明确可占用的情况下,角色才会原子性地释放当前格子并占据目标格子
  • 如果目标格子不可用(已被其他单位占用或其他限制),则角色必须保留当前格子并中止移动,不会进入“悬空”状态;
  • 这个机制适用于所有关键单位,尤其是需要“落地”的角色身体部分。

这不再是简单的跳跃逻辑,而是一种基于占格权的移动协议


二、击退与滑动也必须遵循事务性原则

当一个角色被击退时,他可能会在连续多个格子之间滑动。但这个滑动也必须建立在同样的事务性逻辑上:

  • 每一次位移都必须检查下一个格子是否可被占用;
  • 若下一格不可用,则滑动立即中止,角色停留在最后一个合法格子;
  • 项目符号移动仍然基于“占据点”的原子判断,而不是简单物理模拟。

三、投射物行为逻辑与主体不同

我们将投射物等单位与角色分开处理:

  • 投射物不拥有格子,不占据空间位置;
  • 它们可以自由飞行,只在命中检测时产生交互;
  • 如果它与一个需要“归位”的角色存在空间冲突,必须让位,确保角色始终有地方可落;
  • 这是因为角色“落点”是一级优先权,不可被任何投射物阻断。

四、极端情况举例与规则适用

我们思考了一个情况:多个单位(如敌人与玩家)几乎同时向同一格子移动,可能导致某方明明视觉上看到目标格子已被腾出,但在逻辑上却因事务机制未释放成功,导致被“挡住”。

我们承认这类情况在视觉表现上可能造成误解,但为了系统一致性和无死锁,我们仍要优先保证事务机制,而不是依赖时序精细对齐。

我们考虑的补充措施包括:

  1. 提前请求转移权:如果一个单位要前往一个格子,而当前格子内单位正在尝试离开,可以判断该单位是否有下一个目标格子且可以立即移动,若可以则释放当前格子,为后者让路。

  2. 即时占用空格:一旦一个单位开始移动并目标格子空闲,即立刻标记为被占用,避免“后来的单位”误以为它仍可进入。

无论采用何种具体规则,它们都必须构建在事务性占据模型之上,即:

  • 每次移动必须是“释放当前 + 占据目标”的原子操作;
  • 永远不允许处于“已离开当前格子但未成功进入下一个格子”的中间状态。

五、系统构建方式与总结

我们强调:

  • 无论将来我们如何细化规则、调节体验、做视觉反馈,只要底层是基于事务性占格模型构建的,就能避免“无法落地”或“站位冲突”等根本性Bug
  • 这样一来,即便我们未来引入各种特殊敌人行为、复杂连击、多人抢位、连锁反应等系统,也都能在这一机制下良好运作;
  • 它是整个格子控制系统的根基

所以,我们当前所需做的,是在底层建立起一个清晰、严格、永远可靠的事务性占格逻辑系统,并将所有移动、攻击、滑动、投射等行为构建在这个框架之上。这样我们才有信心面对游戏玩法扩展与未来所有复杂情况的挑战。

黑板讲解:旋转木马的问题和可能的斩首风险

我们在构建空间占用与物理行为系统时遇到了一些更复杂的场景和边缘情况,这些都需要我们进行深入思考与处理,确保系统健壮且不影响玩家体验。


一、旋转平台与跟随问题

我们设想了一个旋转的平台,平台上有若干个可站立点,一个实体(如玩家)站在上面时,平台旋转的同时实体也会跟着一起旋转。这部分实现相对简单,因为:

  • 每个可站立点都有相对于平台中心的固定偏移;
  • 我们只需记录该偏移,在平台旋转时重新计算实际世界坐标;
  • 实体的位置即可随平台自然更新。

这套机制本质是“父子坐标系”的变换,属于常见的场景处理。


二、头部与身体的分离问题

问题在于,如果实体具有高低不同的部分(如身体和头部),而环境中存在只阻挡头部的障碍物时,就会出现一种特殊情况:

  • 实体站在平台上随平台旋转;
  • 平台将头部旋转进某个障碍物(例如横梁)中;
  • 由于身体在低处未被阻挡,但头部无法穿过障碍物,导致“头部与身体分离”;
  • 若该逻辑成立,将相当于“砍头”或“实体解体”,这在玩家角度显然是极度不友好的。

我们意识到这种机制可能会造成以下问题:

  • 若平台上只有一个站立点,且头部被障碍阻挡,则无法将身体挪至其他位置;
  • 如果平台周围存在其他可行站点,但此时被怪物等占据,也同样无法“回退”;
  • 即使空间设计合理,实际运行时也可能出现此类无法预判的失败情况。

三、可能的解决策略

为解决上述问题,我们提出了几种可行的策略:

1. 彻底禁止“头部层级”的障碍物
  • 游戏中不再存在只对“高层”造成碰撞的障碍;
  • 所有障碍物都必须从上到下贯通,确保头和身体共享同一碰撞判断;
  • 这样就不会再出现“身体能过头不能过”的情况;
  • 这种方式简单直接,能彻底避免逻辑混乱。
2. 遇障即停止平台旋转
  • 如果平台旋转时将导致实体部分(如头部)与障碍物发生冲突;
  • 则平台旋转动作会被立即中止;
  • 相当于“自动制动”,防止冲突导致穿模或破坏角色完整性;
  • 缺点是可能影响旋转机制的自由度,但稳定性更高。
3. 允许被动推开、寻求退路
  • 若头部发生碰撞,而身体未被阻挡;
  • 尝试将实体整体“推开”,退至周围可站立的备选点;
  • 若存在空格子则移动,若无可用空间(如被怪物占据),则中止;
  • 但如果连退路都不存在,会面临“无法保命”的情况,因此风险依然存在。

四、对头部独立存在的重新思考

我们也进一步分析了“头部与身体分离”机制是否真的必要:

  • 目前我们暂未发现明确的游戏机制需要头部能单独绕开路径障碍;
  • 即使未来加入相关能力(如头部漂浮、魔法探路等),仍可用“完整体积”代替,仅在表现层分离;
  • 所以,没有必要强行支持头部单独遇阻的复杂情况。

五、系统化重构的方向

当前系统中,只有主角的代码实现了“事务性站点切换”的逻辑。为了系统统一性和可拓展性,我们计划将其改造为普遍适用于所有实体的通用机制:

  • 所有单位(包括敌人、NPC、平台上的物件等)都要接入事务性站点机制;
  • 每个单位都需要有明确的“我占据了哪个点”这一状态;
  • 移动必须基于“释放旧格子 + 占据新格子”的原子操作,确保逻辑安全;
  • 这一机制将作为物理交互和空间占用的底层基础设施,服务于所有游戏实体。

六、总结与原则

为构建一个健壮且不易出错的游戏系统,我们明确以下核心原则:

  1. 事务性格子占用:任何实体移动必须是原子操作,确保占用状态一致;
  2. 避免头部与身体解耦:不允许只对某个身体部分产生碰撞,防止脱节逻辑;
  3. 优先保护玩家体验:系统设计不能因技术逻辑导致玩家突然失败或死亡;
  4. 平台交互可控性:旋转、推动等机制必须能中止、回退或自我修正;
  5. 通用机制统一入口:所有实体应接入统一占点与移动系统,避免特例代码。

我们将优先完成这一底层系统的搭建,然后再通过规则层进行玩法扩展与特性调整。在此基础上,所有复杂行为都可稳定运行而不会产生结构性Bug。

game_entity.h:引入 traversable_reference,并添加两个到实体

我们当前在处理实体(如主角)移动逻辑时,仍然采用一种临时且重复计算的方式。每一帧都会重新计算主角头部和身体所处的位置,也就是每次循环都对空间中最近的“移动点”进行一次实时搜索。这种方式虽然在早期开发中勉强可用,但效率不高、结构混乱,也不具备扩展性。因此我们计划对这部分逻辑进行系统性的改造,使其变得更加正式、统一和高效。


一、引入正式的“站点”概念

我们决定将“主角当前站在哪个点”这个信息进行显式记录:

  • 不再每帧实时计算当前位置;
  • 而是在明确进入某个区域时,就记录下当前所站的那个移动点;
  • 系统化地提供一个查询函数,用来获取“当前位置的最近可站点”;
  • 并将其抽象为一个统一的数据结构,方便所有实体通用使用。

二、结构抽象:可通行引用(traversable reference)

为描述实体“从哪里移动到哪里”,我们引入一种新的引用结构:

  • 类似于以往的“实体引用”(entity reference);
  • 但“可通行引用”不仅包括了实体自身的引用,还会记录其下的“移动点索引”;
  • 即这个引用告诉我们:实体X下的第N个站点,是某个角色实际站立的位置。

我们将“移动起点(movement from)”与“移动终点(movement to)”都改写为这种形式:

  • 每个移动行为都表示为:从某个实体的某个可通行点出发,到另一个实体的某个可通行点;
  • 这种结构更具语义性,并且为系统提供更大的灵活性和拓展空间。

三、处理逻辑的延迟更新机制

由于在模拟过程中实体可能会发生移动,因此我们采取一种“延迟更新”策略,确保在正确的时机处理这些移动信息:

  • 我们不会在添加数据的同时立即解析“可通行引用”;
  • 而是在主循环中,模拟系统进行到合适阶段之后再进行处理;
  • 这样可以避免在实体移动时引用失效或错误指向,确保数据一致性。

例如,在添加实体进入模拟区域时(通过类似 add 操作),以前是直接用 chunk delta 偏移量来快速还原实体位置:

  • 而现在我们不再通过 chunk delta 来计算偏移;
  • 而是根据“可通行引用”去准确获取目标站点信息。

四、进一步优化方向

当前结构仍处于过渡阶段,未来还有进一步优化的空间,例如:

  • 将实体引用和通行点索引完全拆分,支持更细致的动态移动;
  • 对所有敌人、物体等扩展相同机制,实现统一的空间行为模型;
  • 可能还会在更高层封装出更简洁的行为接口,方便逻辑系统调用。

此外,当前用于处理的函数名(如 add)并不直观,后续也需要命名重构,使其更准确地表达功能语义。


五、总结

我们正在进行一项核心架构的调整与系统化设计,目标是:

  1. 避免重复查询,显式记录实体所站点;
  2. 引入统一的数据结构“可通行引用”来表示实体移动路径;
  3. 利用延迟处理机制,保证模拟过程中的数据一致;
  4. 准备将主角的特殊逻辑,推广为所有实体通用的标准接口;
  5. 为后续构建稳定、可拓展的物理与行为系统打好基础。

这一系统将成为所有空间相关逻辑的通用底层,确保行为、状态与物理结构三者统一,提升整体效率与可维护性。
在这里插入图片描述

game_sim_region.cpp:引入 LoadTraversableReference 和 StoreTraversableReference

我们将会把某些对象变成可以加载的可遍历引用(traversable reference)。具体来说,会把“movement from”和“movement to”这两个动作都作为可遍历引用进行加载。加载的过程会在“load entity reference groups”里实现,当我们在哈希表中查找时,逻辑是一样的,只是变成了对可遍历引用的处理。

在流程中,首先是有一个已经降低(lowered)的可遍历引用,我们通过这个引用尝试加载实体引用(entity reference)。这里的关键步骤是尝试获取“enmity”(敌意)部分,如果成功就不需要从其他地方拉取额外数据,因为这里不需要再去拿“to be three”之类的内容,所以只要加载敌意部分就完成了。

关于反向引用(reverse),同样需要存储对应的可遍历记录。反向存储目前只是一个简单的函数调用(thunk),因为暂时不需要做额外的处理。它的功能主要是调用“store entity reference”方法,作用对象是这个可遍历引用中敌意部分的反向引用。

编译时发现“sim regan”并不存在,所以需要修正代码。接下来,在存储任何引用时,也需要同样处理可遍历引用的存储,流程和加载时是对称的,只不过是进行翻译转换。

在处理“movement from”和“movement to”的相关代码时,应该不会太复杂。因为已有的“get closest traversable”功能已经包含了必要的信息,所以只需基于它进行扩展即可。

总结来说,主要工作包括:

  • 将“movement from”和“movement to”作为可遍历引用加载和存储。
  • 加载时通过哈希表查找,尝试获取敌意部分,完成实体引用加载。
  • 反向引用存储时调用已有的存储方法,暂时没有复杂操作。
  • 存储时同样转换处理为可遍历引用,保持和加载对称。
  • 通过已有的“get closest traversable”功能,简化“movement from/to”的实现。
  • 编译过程中修正不存在的符号,保证代码正确性。

接下来就是根据以上思路实现相应代码。整体流程结构清晰,核心就是对可遍历引用的统一加载和存储管理。
在这里插入图片描述

在这里插入图片描述

game_sim_region.cpp:从 game_world_mode.cpp 移动 GetClosestTraversable 并让它记录索引

首先,我们准备对“get closest traversable”(获取最近的可遍历对象)这部分进行改造。目前这个功能可能是在代码的某个较高层次,但现在认为它应该限制在同一区域内处理,并且需要加速处理过程。

我们打算把这个函数放到合适的位置,暂时放在代码的底部,以便后续调整。调用这个函数时,不再传递一个简单的三维坐标点(position vector),而是传递一个“可逆引用”(reversible reference)。这样在执行过程中就能知道是否成功找到对应的可遍历对象。

在调用该函数时,会先清空结果,如果没有找到合适的可遍历对象,就将结果置零,表示无效。搜索的过程是在体积组(volume group)里遍历每个可遍历对象,找到最靠近的那个。关键改变是:我们不再存储该对象的位置,而是直接存储实体指针(entity pointer)和用于找到它的可遍历索引(traversable index,也称p index),语义上表示实体“站立”的那个对象是什么。

因此,这样就改变了原本基于空间点的表示方法,改为直接通过可遍历实体来表达位置关系。当调用“get closest traversable”时,返回的不再是一个空间点,而是我们实际应该站立的那个“东西”。

为了适应这个变化,需要提供一种方式将该可遍历引用转换为具体的空间点。具体实现上,会调用类似“get sim space traversable”这样的函数,把可遍历引用转换成世界空间坐标(world space position)。这里可能还涉及一些实现细节,比如传递参数、inline优化等。

在这个过程中,为了减少重复输入“traversable”相关代码,会定义一些辅助函数或变量,简化调用。

总体流程是:给“get sim space traversable”传递一个可遍历引用,得到对应的空间点,这样现有的代码逻辑可以在不大改动的情况下继续使用。

接下来,处理遍历引用时,提取其中的P值(位置索引)进行操作。这个引用当前只有一个值,未来可能扩展其他信息。

我们通过可遍历引用找到最近的对象后,将其存储下来。接着,在执行移动操作(movement from 和 movement to)时,直接记录当前实体所“站立”的那个可遍历对象作为起点和终点。

这样,原先代码中用实际位置(point)表示的“from”和“to”,现在被可遍历引用所替代,改成基于实体站立对象的逻辑。

但在实现时会遇到一个问题:目前并没有跟踪实体当前到底站在哪个可遍历对象上。这需要做额外处理,特别是对于“hopping”类型的实体,必须知道当前站立的位置,从而保证后续逻辑的准确执行。

具体来说,在任何需要用到“movement from”或“movement to”时,都会先通过实体调用“get sim space traversable”得到对应的空间位置,从而完成位置相关的计算。

总结关键点:

  • “get closest traversable”函数改为传递可遍历引用,返回的是实体站立的对象,而非纯空间点。
  • 通过辅助函数,将可遍历引用转换成具体的世界空间位置,保持兼容性。
  • 在体积组中搜索最近可遍历对象时,存储实体指针和索引,而非空间位置。
  • 移动操作中使用可遍历引用表示起点和终点,语义更明确。
  • 解决目前没有跟踪实体当前所在可遍历对象的问题,特别是“hopping”实体必须知道自己站在哪。
  • 对代码进行了简化封装,减少重复输入,优化函数调用。
  • 目前整体结构仍需调试和完善,但思路清晰,核心是以“实体当前站立的可遍历对象”为核心来替代单纯的位置点逻辑。

这是整个过程的详细逻辑梳理,后续工作是完成具体代码实现和调试,确保上述设计正确无误地落地。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

game_entity.h:让实体跟踪当前所在的 traversable

在处理实体的移动逻辑时,特别是对于“hopping”(跳跃)类的行为,我们将移动的概念从传统的“movement from”和“movement to”改为更语义化的“站立在某处”(standing on)和“移动到某处”(moving to)。
具体来说:

  • 原先的“movement from”和“movement to”会被替换为“standing on”和“moving to”这两个可遍历引用。
  • 这里的“standing on”表示实体当前所站立的那个可遍历对象,而“moving to”表示实体即将移动到的目标可遍历对象。
  • 在跳跃或者移动过程中,不需要频繁更新“standing on”,因为它始终是被追踪和维护的。每次移动时,只需要更新“moving to”即可。
  • 当设置实体的位置引用时,比如实体指针(entity p),不仅会设置“moving to”,还会同时更新“standing on”,确保两个引用都保持同步且准确。
  • 这种设计是为了为后续实现更复杂的事务性处理(transactional process)做准备,目的是能以更严谨和安全的方式管理实体的移动和状态转换。
  • 当前还不能完全保证“standing on”引用随时有效,因此暂时还不能直接依赖它来进行所有操作,但整体思路是逐步将这套系统搭建起来,最终形成一个中心化的事务系统来统一管理。
  • 通过将“movement from/to”抽象为“standing on/moving to”,代码结构和逻辑变得更清晰,语义更加贴合实体移动的真实含义。
  • 未来在处理移动时,会以这两个引用为核心,相关的分组和逻辑也围绕它们展开。

总结而言,我们正在将实体移动的底层表示从位置点向基于可遍历引用的“站立点”和“目标点”转变,为实现事务性移动管理打基础,并逐步建立更安全、集中管理的移动状态体系。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

game_world_mode.cpp:让 AddPlayer 接受一个 traversable_reference 并在最近的 traversable 上创建玩家

我们目前虽然编译没有错误了,但仍然存在一个逻辑上的问题:

问题在于,当我们初始化一个实体时,并没有为其分配具体的位置,也就是说,实体在创建后并没有被放置在任何一个明确的“格子”或可遍历位置上。现在我们系统假设所有实体在任意时刻都必须处于某个明确的“格子”中,这种初始化方式就会造成问题。

因此,我们必须改变实体创建的流程,确保每个实体在被创建的那一刻起,就已经明确地被放置在某个有效的位置上。不能再任意地把实体“扔”到某个位置上然后再依赖系统去推导其站立点或可遍历引用。

我们有两个方向可以处理这个问题:

  1. 创建时自动修正:如果实体被创建时没有指定明确位置,可以在初始化阶段“自动吸附”到最近的合法位置上,即自动寻找一个最近的可遍历格子并将实体放置上去。这是个“容错”方案,适合临时处理。

  2. 强制指定位置(推荐):

    • 明确要求:所有实体在创建时必须提供其初始“站立位置”(即一个有效的可遍历引用)。
    • 这样可以保证后续所有系统运行时对实体状态的假设都能成立,也更符合我们正在构建的事务性移动系统的设计原则。
    • 如果初始没有提供合法位置,系统就拒绝创建该实体,或者抛出错误。

我们倾向于采用第二种方式,因为这是一个更清晰、更一致的设计方向,更符合“事务性”系统的原则——每个状态都必须明确、合法、可追踪。

总结要点:

  • 系统假设所有实体都必须在任意时刻处于一个明确的、合法的可遍历格子中。

  • 当前实体初始化时没有自动设置该状态,导致潜在逻辑错误。

  • 解决方案是:

    • 优先采用强制要求初始化时指定合法站立点。
    • 可选采用自动修正机制,将未设置位置的实体吸附到最近的合法格子中。
  • 此调整为后续事务性系统打基础,确保每个状态都完整合法,避免系统出现不一致或非法状态。

“我不想让它们一开始就无效”β

我们目前已经实现了错误消除,但仍然存在一个关键问题:我们在初始化实体时,并没有为其设置一个合法的初始位置。由于现在系统设定所有支持跳跃的实体必须处于一个明确的“可遍历位置”(traversable reference)上,所以我们不能再随意初始化实体,而不指定它的站立点。我们必须确保所有实体一开始就处于有效状态,而不是默认处于非法状态。

所以我们必须修改当前的实体创建逻辑,使得:

  1. 每个可以跳跃的实体都必须在初始化时被赋予一个合法的 traversable reference,这就意味着它一开始就必须站在某个格子上,而不是漂浮在空中或处于无效状态。
  2. 即使逻辑上这个实体目前没有“腿”或不能移动,但它的位置在语义上必须是有效的格子。
  3. 实体的“世界坐标” p 值也可能需要从其所在的可遍历格子推导出来,或者相对于该格子进行偏移,但这部分细节可以稍后再讨论。

我们在创建玩家时进行调整:

  • 玩家有一个身体部分(body),这是实体中真正“落地”的部分。
  • 我们会将玩家身体的 standing_on 字段设置为一个明确的 traversable reference,表示实体当前所处的位置。
  • 在处理“添加玩家”的操作时,我们需要先找到一个合法的格子来安放该玩家。

我们做了如下几个关键性调整:

  1. 控制器输入处理顺序优化
    为了确保在尝试放置玩家之前能够访问到当前的 sim_region(模拟区域),我们将控制器输入处理逻辑移动到了 open_sim_region 之后。这样我们就可以在同一帧中正确地基于场景信息进行决策。

  2. 通过摄像机位置寻找最近可遍历点
    临时方案中,我们通过 get_closest_traversable 函数,以摄像机位置作为参考点,寻找最近的合法格子,并将玩家放置在该格子上。

  3. 未来的更优方案规划
    当前这种方式只是权宜之计。正式的做法应该是:

    • 第一位玩家会被放置在预定义的初始位置上。
    • 后续玩家加入时,应只选择可从当前已有玩家位置连通到的可遍历格子,避免玩家出生在无法逃脱的“孤岛”区域。
    • 如果暂时无法找到合适的出生点,可以显示 UI 提示告知“无法找到合适位置”,并在后续帧中继续尝试寻找。

通过这些设计,我们确保了所有支持移动的实体在创建时就是“合法”的,符合整个系统关于空间逻辑的要求,也为后续引入事务性移动系统打下了良好的基础。系统状态将从一开始就完整一致,不再存在“虚空中漂浮”的未初始化状态。
在这里插入图片描述

运行游戏查看效果,触发 GetSimSpaceTraversable 的断言

我们当前已经成功地添加了玩家,并尝试将其放置在一个合理的可遍历位置上,理论上这一步是完成了的,但在进行跳跃操作(hop)时却出现了问题。

经过分析,我们发现:

  1. 玩家已经被添加成功
    这说明我们确实找到了一个有效的 traversable,并将其赋值给了玩家的 standing_on 字段,同时也设置了 movement_to 字段。这两个字段都应该是有效的引用。因为如果找不到可遍历位置,玩家根本就不会被创建,所以这一步逻辑上没问题。

  2. 断言失败出现在跳跃逻辑中
    在执行跳跃时触发了一个断言,提示我们尝试访问了一个不存在的 traversable,这意味着 movement_to 字段的值在某一时刻是不合法的。

  3. 初步怀疑是字段顺序混淆
    检查代码后发现 standing_onmovement_to 的赋值顺序搞反了,在设置时不小心交换了它们。这种混淆导致了跳跃处理时逻辑错误,因为系统在尝试查找 movement_to 时却找到了错误的值,进而触发断言。

  4. 修正顺序是当务之急
    首先必须修复这两个字段的赋值顺序,确保 movement_tostanding_on 分别设置为它们各自正确的 traversable 引用。修正后才能继续排查跳跃过程中其他可能存在的逻辑问题。

  5. 材质显示问题暂不处理
    虽然当前还有一些与材质显示相关的问题(如纹理错误),但这并不是当前的优先级,可以在后续再处理。

总体而言,我们已经建立起了一个初步的、可落地的实体初始化机制,但还需要仔细验证跳跃操作链路中的所有状态传递是否一致、字段是否在所有路径中都被正确设置。这是构建一个健壮的实体空间管理系统所必须跨过的阶段。
在这里插入图片描述

调试器:触发断言,发现 Head->StandingOn 是 NULL

我们当前的问题出在 standing_on 字段,它在某些情况下并没有被正确设置,这导致在后续逻辑中访问这个字段时发生了错误。

我们逐步排查了整个流程,并得出了以下详细结论:


1. 初始状态下 standing_on 应该是有效的

  • 在添加玩家的过程中,我们确实在 add_player 的逻辑里为玩家的 body 设置了 standing_onmovement_to
  • 设置的位置处,standing_onmovement_to 都被赋值为一个有效的 traversable,看上去一切正常。

2. 进入跳跃逻辑时字段却变为 null

  • 当我们进入跳跃逻辑查看实体状态时,却发现 standing_onmovement_to 都是 null
  • 这说明在某个环节中,原本赋值成功的这两个字段信息丢失了。

3. 排查字段丢失的原因

  • 我们跟踪到了实体被加入世界的那一刻,即调用 enter_into_world 和相关的 chunk 打包逻辑。
  • 发现这里的实现是不完整的,在进行实体打包处理时,相关的状态字段(比如 standing_on)并没有被正确地保存或者拷贝到世界数据结构中。
  • 也就是说,虽然在逻辑层面上我们设置了字段,但在实体真正被写入世界内存时这些字段被丢掉了。

4. 问题本质:打包/写入逻辑缺失关键字段

  • 实体被加入到 world/chunk 的过程中遗漏了对 standing_onmovement_to 字段的写入操作。
  • 这导致实际在游戏世界中的实体是“空的”,没有这些关键字段的引用,于是后续逻辑读取这些字段时就发生了断言失败或错误行为。

5. 解决思路

  • 必须修复 enter_into_world 或相应的打包写入逻辑,确保将实体的所有关键字段(特别是 standing_onmovement_to)一并写入。
  • 不仅仅要赋值,还要确认其在写入结构中确实被序列化、传递和还原。
  • 此外可以考虑添加调试断言,确保所有进入世界的实体在关键字段上都是非空的。

补充说明

  • 这个问题暴露了系统中实体数据生命周期管理上的一个结构性漏洞:逻辑赋值与数据落地脱节,容易造成表面上“设置成功”,实际却“无效”的错误。
  • 需要系统性检查所有实体创建与加入世界的路径,避免类似遗漏。

总结来说,我们发现实体的 standing_on 字段虽然在逻辑上被赋值,但在真正加入游戏世界的过程中,这个值被遗漏,导致后续访问时为空。需要立即修复实体写入逻辑,确保所有关键字段都能正确存储和传递。

在这里插入图片描述

game_world.cpp:在 PackEntityIntoChunk 中完成所有修改

我们发现之前的实体数据写入逻辑存在严重问题,核心在于打包(pack)实体进入世界时没有涵盖所有关键字段的处理,尤其是 standing_onmovement_to 等关键引用字段未被正确存储,导致整个实体的状态是不完整的。这是个根本性的错误,导致实体看似创建成功,实际上却是无效的。

以下是我们对这一过程的详细分析与修复思路:


1. 原因分析:打包逻辑不完整

  • 原本实体进入世界时的打包流程 pack_entity_into_world 只处理了一部分内容,没有将所有必要的数据(如位置信息、状态引用等)正确写入。
  • 由于这些关键字段未被打包,所以实体在世界中是处于“未初始化”状态的,访问这些字段时就会出错。

2. 修复方式:补全打包流程

  • 我们应当将所有字段的处理(包括逻辑状态和位置引用)全部集中在 pack_entity_into_world 中统一处理,不能遗漏。
  • 尤其在打包进入 chunk 后,必须马上处理相关字段的赋值,确保实体的完整性。
  • 因此新增了一个类似 entity_store_data 的结构或逻辑步骤,用于在存储时执行这些打包行为。

3. 理念统一:创建时即应打包

  • 实体的所有“正确性”操作必须发生在打包阶段,而不是在创建阶段零散设置。
  • 创建实体本身不应直接设置任何 “索引” 或特定 chunk 数据,而是设置逻辑引用,最终由打包过程统一转换为实际的世界数据。

4. 问题延伸:add_player 的实现不合理

  • 当前的 add_player 实现也存在相同的问题:在创建阶段直接对实体结构设置了字段(如索引),这是不合理的。
  • 正确方式是:在 add_player 时仅设置逻辑引用或初步状态,真正的 index/id/指针等应由世界系统在打包阶段分配。

5. 后续优化建议

  • 考虑合并 same_regionstorage 系统。这两个系统目前职责过于重叠,操作紧密耦合,合并有利于简化逻辑和维护。
  • 合并后可形成一个更统一、更健壮的实体存储与生命周期管理系统。

总结

我们之前的做法漏掉了实体生命周期中关键的存储与状态转移环节,打包过程未覆盖所有所需字段,导致实体行为不一致、逻辑断裂。现在我们已将打包阶段定义为状态“落地”的唯一入口点,确保所有字段在进入世界之前都通过统一机制处理,从而保证实体行为的正确性和稳定性。接下来也将修复 add_player 的流程,确保它只负责逻辑创建,而不承担状态设定,统一交由打包系统处理。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

运行游戏,成功跳跃

现在我们已经修复了之前的逻辑问题,整体流程终于能够走通,并且角色能够正常地进行跳跃了,说明整个实体初始化与动作系统的流程已经进入正轨。以下是这一阶段的详细总结:


1. 修复后的行为验证成功

  • 之前存在的问题是实体创建后状态不完整,尤其是没有正确设置 standing_onmovement_to,导致动作逻辑异常。
  • 修复后我们确保了所有关键状态字段在实体被打包到世界时统一设置,从而保证了逻辑的一致性和完整性。
  • 现在跳跃操作已经可以顺利执行,说明移动相关的状态引用逻辑已成功生效。

2. 修复的核心改动

  • 实体从创建到存入世界时的路径完全走通,打包过程中确保了数据结构被正确赋值。
  • 所有需要落地的逻辑都集中到了统一的打包处理函数中进行,不再在其他流程中单独设置,避免了重复和遗漏。
  • 这些修复让角色创建后立即处于一个“合法”的状态,也为后续所有交互行为提供了正确的起点。

3. 状态字段工作机制已稳定

  • 现在系统能够在实体创建并进入世界的过程中,正确设置:

    • 当前站立的位置(standing_on
    • 当前目标位置(movement_to
  • 这两个字段已经成为实体行为驱动的基础,在跳跃等行为触发时起到了核心作用。

  • 避免了无效或未定义状态导致的断言失败。


4. 当前成果与下一步方向

  • 跳跃功能运行正常,初步验证了整体移动机制是可靠的。
  • 这也为后续更复杂的系统(如路径查找、动态碰撞处理等)提供了良好基础。
  • 下一阶段可以考虑对更多交互行为做统一测试,并评估是否需要扩展或重构与实体行为相关的其它逻辑模块。

总结

我们成功通过修复实体初始化与打包流程中的漏洞,实现了跳跃等行为的正确执行。所有关键数据字段都已在正确的生命周期阶段进行赋值,角色行为也已回归正常。系统整体的行为驱动模型已经初具雏形,后续可以在此基础上构建更丰富、更可靠的实体交互机制。
在这里插入图片描述

game_world_mode.cpp:让 HeroBody 恢复位置,引入 AddFloatyThing

当前阶段我们开始探索实体与“可站立物体”之间的交互机制,主要思路是通过创建类似“浮动平台”的新实体类型,让角色能够在这些平台上“站立”和“被移动”。以下是对这部分逻辑的详细总结:


1. 在“站立”状态下重设实体位置的概念

  • 我们设想:当实体处于“站立”状态时,应始终将其位置设置为所依附对象的位置,即“站立在哪,就在那里”。
  • 虽然当前系统中这段代码没有实际功能,但这个逻辑为未来实现“移动载具”或“移动平台”等交互打下了基础。
  • 比如玩家站在一艘船或平台上,这个平台移动时,玩家的 entity.p 位置也应随之更新。

2. 引入“浮动物体”作为新实体类型

  • 我们引入了 floaty_thing 类型的实体,作为测试使用的一种“浮动地面”。
  • 它和普通地面类似,都可以被“站立”,但未来可能会有特殊的移动逻辑或交互功能。
  • 创建方式类似于其他地面对象,暂时不涉及额外碰撞逻辑(即不做特殊的碰撞处理)。

3. 渲染和逻辑系统的集成

  • 在渲染逻辑中增加了对 floaty_thing 类型的支持,使其可以在世界中可见。
  • 暂时复用了地面渲染逻辑,因此可视效果与地板相似,但这仅作为占位设计。
  • 实体类型的系统未来将逐步淘汰,可能会过渡到更通用的组件系统。

4. 场景构建中的使用实例

  • 在标准房间构建逻辑(如 add_standard_room)中,我们测试性地添加了一个 floaty_thing
  • 具体位置在某个指定 tile 坐标(例如 tile x=7, y=7)。
  • 用这种方式测试浮动平台的存在、渲染及是否能被实体“站立”。

5. 系统结构与未来扩展方向

  • 当前系统仍有很多临时代码,如位置判断、硬编码坐标等,仅作为功能验证使用。
  • 随着逻辑成熟,将逐步移除硬编码内容,引入更健壮的空间管理与交互检测机制。
  • 未来还会探索让平台移动,并同步移动其上的角色,实现动态载具系统或浮动平台关卡。

总结

我们为实体系统引入了“站立依附”的机制,允许角色在特定平台上移动,同时保证其空间状态与依附物保持同步。新添加的 floaty_thing 为后续更复杂的物体交互打下了基础。通过初步集成渲染和实体逻辑,我们验证了这一机制的基本可行性,未来可以进一步拓展为完整的移动平台、载具交互或移动环境机制。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

game_world_mode.cpp:让 FloatyThingForNow 上下移动

我们在这个阶段实现了“浮动物体”(floaty thing)的基本运动逻辑,并尝试通过对其垂直坐标(Z轴)的动态修改,实现一个周期性上下浮动的效果。具体内容如下:


1. 浮动物体已经添加成功

  • 虽然一开始看不到浮动物体,但经过确认它确实已经被添加进了世界中。
  • 后续在画面中可以看到它和地面重叠地堆叠在一起,说明其确实被正确渲染和放置。

2. 添加浮动物体的移动逻辑

  • 与普通地面(floor)不同,浮动物体被赋予了“主动移动”的能力。
  • 通过在逻辑处理中添加对 floaty_thing 类型的判断,我们为这类物体添加了动态更新的逻辑。
  • 主要是控制其 entity.p.z 值,让其上下浮动。

3. 使用时间函数实现上下浮动

  • 通过正弦函数(sine)控制其 Z 坐标值,实现自然的周期性浮动。

  • 使用了 tBob 或类似变量表示当前帧的时间偏移量。

  • 更新逻辑类似于:

    entity.p.z = sin(tBob * 0.5f) * 2.0f;
    

    表示以一定频率和幅度(比如 ±2)做垂直方向的震荡。


4. 浮动行为的意义

  • 虽然目前浮动物体只是在 Z 轴方向做简单的上下运动,但这已经为后续更多交互场景打下了基础:

    • 玩家或其他实体可以“站在”浮动物体上,跟随其上下移动;
    • 可以模拟诸如升降台、电梯、浮桥等功能;
    • 若与碰撞检测结合,可扩展为更复杂的游戏机制,比如动态地形、移动谜题等。

5. 后续可扩展内容

  • 当前上下浮动是纯视觉与位移效果,还未影响“站在其上”的实体位置,需要进一步扩展同步逻辑;
  • 可以添加横向位移,形成更复杂的运动轨迹;
  • 支持多个浮动物体、不同频率与相位、更多参数可调的浮动方式;
  • 最终可能将这类浮动物体设计为一种独立的“可骑乘”或“可承载”组件,供其他实体挂载。

总结

我们已实现浮动物体的基础动态行为,通过在逻辑更新中修改其 Z 轴位置,实现周期性浮动效果。这个机制可广泛用于游戏中的动态交互场景,如浮桥、移动平台等,并可进一步扩展为支持实体挂载、平台联动等高级系统。这个步骤标志着实体系统朝动态环境与物理交互方向迈出了关键一步。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

站上面抖动的厉害

运行游戏,尝试站在 FloatyThingForNow 上

当前我们实现了一个上下浮动的浮动物体,并试图让玩家实体站在其上,但出现了一些问题与不符合预期的行为。我们对这些现象进行了分析,并对系统进行了微调,具体过程和结论如下:


1. 浮动物体上下浮动逻辑生效

  • 浮动物体已经能够周期性上下浮动。
  • 浮动是通过改变实体的 z 坐标实现的,采用的是类似 sin 函数控制的平滑运动。

2. 玩家实体无法稳定站立在浮动物体上

  • 理论上实体应该可以站在浮动物体上,并随着其一起上下移动。
  • 实际上实体不断地“弹跳”(hop),无法保持稳定。
  • 出现了反复跳起然后落下的异常行为,表现出“剧烈晃动”或“持续弹跳”的效果。

3. 异常行为原因分析

  • 异常的根本原因是:玩家的头部系统(head)在每一帧都在寻找最近的可行走表面(traversable)
  • 浮动物体因为上下移动,其位置在时间轴上不断变化,导致玩家头部每一帧都认为“脚下不是最近的 traversable”。
  • 因此每帧都在尝试跳跃或重新校准位置,从而形成了这种不稳定的“弹跳”行为。

4. 跳跃终止逻辑问题

  • 原先以为跳跃是纯时间驱动的,但现在可以确认:跳跃的终止条件还涉及当前位置是否仍然在可行走表面上
  • 由于浮动物体不断变动,导致系统判断为“不再有效”,从而不断重启跳跃动作。

5. 临时缓解方案

  • 为了避免这种剧烈弹跳现象,我们暂时降低了浮动物体的浮动幅度,使得其在玩家头部逻辑判断中更加“稳定”。
  • 此举减轻了由于坐标浮动造成的逻辑扰动,使得站立更接近于“静止状态”。

6. 后续需要处理的问题

  • 头部逻辑与实体跟随机制的脱节:头部依赖“最近 traversable”的判断方式并不适用于动态平台,应该考虑“绑定”当前站立表面。
  • 站立状态与移动平台同步:当玩家站在移动平台上时,应有专门的跟随逻辑,而不是每帧重新计算位置。
  • 跳跃逻辑中断机制改进:需要添加对动态平台的适配机制,比如标记“当前站立平台仍在可用范围内”,避免误判导致跳跃。
  • 未来可扩展为完整的挂载系统:例如平台可以通知其承载实体做同步更新,而非依赖实体自身去追踪平台状态。

总结

当前系统成功让浮动物体实现了浮动行为,但在玩家实体站立其上时出现了弹跳异常。问题的根源在于头部系统对“最近 traversable”的判断方式不适用于动态表面。我们通过降低浮动幅度临时缓解了现象,但后续需从逻辑架构上调整平台与实体的绑定方式,以支持更复杂的动态交互系统。

game_world_mode.cpp:让 AddStandardRoom 只在位置添加 FloatyThingForNow

我们对房间(standard room)的地面生成逻辑进行了调整,目标是使房间中某个特定坐标位置不再生成普通地面,而是生成一个可以上下浮动的“浮动物体”,用于测试实体在动态平台上的行为。具体操作和推理如下:


1. 修改地面生成逻辑

  • 原始的地面生成方式是统一调用添加地板(ground)并赋予碰撞属性(floor collision)。
  • 为了在特定位置替换为浮动物体,加入了一个条件判断结构。

2. 加入条件判断

  • 判断依据是 tile 坐标的偏移值 axay

    • ax == 2 && ay == 2 时,不再添加普通地面。
    • 而是添加一个浮动物体(floaty thing)作为替代。
  • 其余坐标位置仍照常生成地面。


3. 设计目的

  • 这种做法可以确保在特定坐标处创建一个特殊的平台(浮动物体),其余部分不变,便于对比和测试。
  • 由于位置明确,避免了控制逻辑(如玩家移动判断)因不确定性而出错。

4. 浮动物体显示问题

  • 出现了浮动物体未能正确渲染的问题,因此临时放弃了浮动物体渲染的独立逻辑。
  • 改为简单地使用已有的逻辑结构直接绘制(“does that it this way”)以保证视觉上能看到该实体。

5. 总体目标

  • 利用这种静态房间结构内的条件判断方式,实现在指定区域插入一个动态平台。
  • 为后续实现如“站在移动平台上并随之移动”等更复杂的玩法机制提供验证基础。

后续方向

  • 优化浮动物体的渲染与实体绑定机制,使其行为在动态更新中更加一致。
  • 进一步探索玩家与动态平台之间的相互作用方式,如自动同步、乘坐逻辑、跳跃过渡等。

总结

我们通过修改标准房间地面生成逻辑,在特定坐标位置生成一个浮动物体,替代普通地面,从而为后续动态平台的交互测试打下基础。同时对浮动物体的显示做了调整,确保可以看到效果。整套逻辑在不影响其他控制系统的情况下实现了定点插入特殊实体的功能。
在这里插入图片描述

运行游戏,再次尝试站在 FloatyThingForNow 上

当前发现了一个奇怪的问题,具体表现为角色的“跳跃”(hop)行为异常频繁和不合理。分析后发现原因可能是跳跃的触发条件设计有误,现有的跳跃代码并不是基于角色头部与目标点之间的合理距离判断,而是每次都尝试跳到最近的点,这导致角色会在两个点之间不断地反复跳跃,非常不自然。


具体问题点:

  • 跳跃代码缺少判断条件,没有排除“跳跃到相同点”的情况。
  • 控制逻辑是每次都根据头部当前位置寻找最近点,但没有考虑当前的运动模式和头部与身体的位置差异。
  • 导致角色头部位置稍微移动,就会反复触发跳跃,出现不稳定的跳跃抖动。

解决思路:

  • 在跳跃动作执行前,需要先检查实体的运动模式是否为“planted”(即已站稳状态)。
  • 如果是站稳状态,需要先更新相关状态,再进行最近点的搜索和跳跃。
  • 只有当头部距离身体足够远,才允许触发跳跃,避免无意义的频繁跳跃。
  • 这要求重写控制器代码,使其基于头部与身体距离的判断,控制跳跃触发时机。

当前状态和后续工作:

  • 修正运动模式更新的顺序,确保跳跃判断逻辑前状态正确。
  • 仍然存在问题,因为头部持续寻找最近点,跳跃动作反复出现。
  • 计划对控制器逻辑进行重新设计,加入“只有距离超过阈值时才跳跃”的限制。
  • 这一问题暂定为后续重点调试和修复的“明日任务”。

总结:

当前跳跃机制缺少合理的触发判断,导致角色在头部位置变化时反复跳跃。需要先根据运动模式和头部与身体的距离进行判断,避免无意义跳跃。解决方案是调整跳跃前状态更新顺序并重写控制器逻辑,确保跳跃行为自然合理。
在这里插入图片描述

在这里插入图片描述

问答环节

如果我理解没错,这听起来像是用滞后机制解决的问题

目前的控制逻辑是在没有考虑“不可通行区域”的情况下编写的,导致每次都简单地选择距离最近的点进行跳跃,这样的设计不够合理。现在需要改进控制器逻辑,使其能够识别哪些区域是可通行的,避免角色跳到无法通过的位置。


改进之后,代码会更智能地考虑角色意图和环境限制,从而提升头部的行为表现,使其跳跃选择更加合理和自然。这个改动相对简单,但会大幅优化整体的动作逻辑和角色移动体验。


总结来说,现有控制逻辑过于简单,没有过滤不可通行区域。通过增加对可通行性的判断和角色意图的考虑,控制逻辑将变得更健壮,头部跳跃行为也会更加合理和有效。

他们为Greenfoot实现了结构化编辑器

Greenfoot 是一个面向初学者的交互式编程环境,主要用于教学和学习面向对象编程,特别是Java语言。它通过一个图形化界面和简单的结构化编辑器,让用户可以创建基于二维图形的游戏和模拟。Greenfoot 让编程更直观,能够直观看到代码对游戏世界的影响,适合入门级别的编程学习。虽然界面看起来不够美观,但它的重点是教学和简化编程过程,而不是视觉设计。

所以,我们打算让头永远是脱离状态吗?

我们计划让头部永远与身体分离。这意味着头部始终独立移动,身体则跟随头部移动。虽然在图形表现上,我们会尽力绘制得让头部和身体看起来像是物理上连接在一起,但实际上它们是分开控制的,头部的运动先行,身体的运动紧随其后。这样做的目的是让视觉效果保持连贯,但在逻辑和运动上实现分离控制。

手套在哪里?你会编写自定义手套动作吧?

Abner戴上手套后,会为手套编写自定义的移动程序。手套实际上是用来攻击的工具,所以手套的移动不是自由移动,而是根据攻击动作来控制。也就是说,手套不会像普通肢体那样随意移动,而是在执行攻击时才会做出相应的动作。

你计划什么时候加入正确透视的美术,让英雄看起来不那么奇怪?

关于何时添加正确的透视图,我们还没有具体计划。英雄的画面看起来有些奇怪,因为艺术资源的准备进度有所耽搁,主要是获取艺术素材方面的问题,目前还没有完成,所以这方面的工作还在等待中。

理论上你可以用第二个英雄和Xbox手柄测试,确保他们不会站在同一个格子上?

理论上,可以通过添加第二个英雄来测试,主要是为了确保它们不会处于相同的状态或位置。但由于事务处理机制还没有实现,这意味着两个英雄可能会同时站在同一个地方,这种情况目前并没有被阻止或检测到。同时,当前也没有将它们的状态打印出来进行监控。

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

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

相关文章

深度学习让鱼与熊掌兼得

通常,一个大的复杂的模型的loss会低,但是拟合方面不够,小的模型在拟合方面更好,但是loss高,我们可以通过深度学习来得到一个有着低loss的小模型 我们之前学过,peacewise linear可以用常数加上一堆这个阶梯型函数得到,然后因为peacewise linear可以逼近任何function,所以理论上…

如何在 AWS 上构建支持 AVIF 的前端图片优化方案

一、为什么使用 AVIF 图片格式? 优势点 说明 高压缩率 在相似质量下,AVIF 文件比 JPEG/PNG/WebP 更小,能有效节省带宽和存储空间。 更高画质 即使在低码率下也能保持清晰细节,减少压缩带来的马赛克或模糊问题。 支持透明度 …

C++中的std::allocator

C中的std::allocator 文章目录 C中的std::allocator1.std::allocator1.1C中的placement new 和operator new1.2一个custom allocator的实现1.3使用std::allocator_traits实现allocator 1.std::allocator C中的std::allocator默默工作在CSTL中的所有容器的内存分配上&#xff0…

CodeBuddy编程新范式

不会写?不想写? 腾讯推出的CodeBuddy彻底解放双手。 示例 以下是我对CodeBuddy的一个小体验。 我只用一行文字对CodeBuddy说明了一下我的需求,剩下的全部就交给了CodeBuddy,我需要做的就是验收结果即可。 1.首先CodeBuddy会对任…

QML学习01(设置宽度、高度、坐标点、标题,信号与槽,键盘事件)

QML学习 1、前言2、QML3、QML和QWidget的区别3、QtQuick下的Windows应用4、总结 1、前言 记录一下QML学习的过程,方便自己日后回顾,也可以给有需要的人提供帮助。 2、QML QML是 Qt 框架中的一种声明式编程语言,专门用于快速设计和开发用户…

在VSCode中接入DeepSeek的指南

本文将介绍三种主流接入方式,涵盖本地模型调用和云端API接入方案。 一、环境准备 1.1 基础要求 VSCode 1.80+Node.js 16.x+Python 3.8+(本地部署场景)已部署的DeepSeek服务(本地或云端)1.2 安装必备插件 # 打开VSCode插件面板(Ctrl+Shift+X) 搜索并安装: - DeepSeek Of…

机器学习-计量经济学

机器学习 不要事前决定变量关系,关键是谁也不知道啊,机器学习学习的模型(那也不是真实的关系啊) 这就是自然学科的好处:只要不断的优化这个未知的东西(函数),然后在数据上&#xff…

五、Linux账号与权限管理

1、管理用户和组账号 1.1、用户 1.1.1、用户的概念及作用 在Linux系统中,用户(User)指的是可以访问系统资源的个体实体。每个用户都有一个唯一的用户账号,用于标识和管理其在系统中的活动和访问权限。 用户的重要性和功能: 身份认证和访问控制: 用户账号用于身份认证,确…

精益数据分析(61/126):移情阶段评分体系构建与实战案例解析

精益数据分析(61/126):移情阶段评分体系构建与实战案例解析 在创业的移情阶段,如何科学评估用户需求的真实性与紧迫性,是决定后续产品方向的关键。今天,我们结合《精益数据分析》中的评分框架,…

完成反射宇宙的最后一块拼图:泛型集合

反射,c#的黑科技,一手打造漂亮的,专属于自己的属性框 之前分享的: 如何写一个自定义属性控件的功能,但是只是对基础的类型,比如String,bool,int等,但是对list<T>,Vector<T>这种泛型集合类型支持的不是很好,刚好最近重新研究了一下,将这个非常重要的功能完成了. 效…

Redis--基础知识点--26--过期删除策略 与 淘汰策略

Redis 的过期策略和淘汰策略是内存管理的核心机制&#xff0c;分别用于处理键的自动失效和内存不足时的数据清理。以下是详细说明&#xff1a; 1 、过期删除策略&#xff08;Expiration Policy&#xff09; 处理已设置过期时间&#xff08;EXPIRE&#xff09;的键&#xff0c;…

第六天——贪心算法——字符串分隔

1. 题目 给定一个字符串 s&#xff0c;我们需要将其划分为尽可能多的部分&#xff0c;使得同一字母最多出现在一个部分中。 例如&#xff1a;字符串 "ababcc" 可以划分为 ["abab", "cc"]&#xff0c;但要避免 ["aba", "bcc&quo…

[原创](现代Delphi 12指南):[macOS 64bit App开发]: 注意“回车换行“的跨平台使用.

[作者] 常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 开发工具: Visual Studio、Delphi、XCode、…

Maven 插件参数注入与Mojo开发详解

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

扩增子分析|R分析之微生物生态网络稳定性评估之节点和连接的恒常性、节点持久性以及组成稳定性指数计算

一、引言 周集中老师团队于2021年在Nature climate change发表的文章&#xff0c;阐述了网络稳定性评估的原理算法&#xff0c;并提供了完整的代码。自此对微生物生态网络的评估具有更全面的指标&#xff0c;自此网络稳定性的评估广受大家欢迎。本文将介绍网络稳定性之节点和连…

人体肢体渲染-一步几个脚印从头设计数字生命——仙盟创梦IDE

人体肢体动作数据集-太极拳 渲染代码 # 初始化Pygame pygame.init()# 设置窗口尺寸 WINDOW_WIDTH 800 WINDOW_HEIGHT 600 window pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("动作回放")# 设置帧率 FPS 30 clock pyg…

强化学习入门:马尔科夫奖励过程

文章目录 前言1、组成部分2、应用例子3、马尔科夫奖励过程总结 前言 最近想开一个关于强化学习专栏&#xff0c;因为DeepSeek-R1很火&#xff0c;但本人对于LLM连门都没入。因此&#xff0c;只是记录一些类似的读书笔记&#xff0c;内容不深&#xff0c;大多数只是一些概念的东…

腾讯开源实时语音大模型VITA-audio,92mstoken极速响应,支持多语言~

简介 VITA-Audio 是一个由腾讯优图实验室&#xff08;Tencent Youtu Lab&#xff09;、南京大学和厦门大学的研究人员共同开发的项目&#xff0c;旨在解决现有语音模型在流式生成&#xff08;streaming&#xff09;场景下生成第一个音频令牌&#xff08;token&#xff09;时的高…

测序的原理

Sanger 测序原理 https://v.qq.com/x/page/d0124c0k44t.html illumina 测序原理&#xff1a; https://v.qq.com/x/page/i0770fd7r9i.html PacBio 第三代 SMRT 单分子测序 https://v.qq.com/x/page/r03534cry7u.html Ion torrent 测序原理 https://v.qq.com/x/page/v01754s6r82.…

高项-逻辑数据模型

逻辑数据模型的核心理解 1. 定义与特点 逻辑数据模型&#xff08;Logical Data Model, LDM&#xff09;&#xff1a; 是一种抽象的数据结构设计&#xff0c;用于描述业务实体&#xff08;如客户、订单&#xff09;及其关系&#xff08;如“客户下单”&#xff09;&#xff0c…