您的位置:首页 > 游戏 > 手游 > 广州白云区疫情最新_建筑施工平台_免费开店的电商平台_软考十大最靠谱it培训机构

广州白云区疫情最新_建筑施工平台_免费开店的电商平台_软考十大最靠谱it培训机构

2024/12/27 18:44:44 来源:https://blog.csdn.net/TM1695648164/article/details/144472754  浏览:    关键词:广州白云区疫情最新_建筑施工平台_免费开店的电商平台_软考十大最靠谱it培训机构
广州白云区疫情最新_建筑施工平台_免费开店的电商平台_软考十大最靠谱it培训机构

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

这一节作者改的比较多

回顾上次内容

目前的进展是在回顾之前的工作并确定接下来的重点任务。我们发现核心问题在于碰撞逻辑尚未完善。现有的碰撞循环并不理想,导致角色动作无法完全达到预期的效果。

在角色运动初步实现的基础上,运动的手感已经有所改善,但碰撞问题仍然是亟待解决的重点。下一步需要集中精力优化碰撞处理,以确保角色与场景或其他物体的交互更加真实流畅。

解决论坛中发现的一个漏洞

我们发现了一个问题,这个问题非常有趣且出乎意料。只有论坛上的一个人注意到了,而我们自己之前并没有发现。问题的核心是关于控制器输入的处理。我们最初以为系统已经正确处理了控制器的模拟和数字输入,但实际上情况并非如此。

代码中,处理控制器输入的逻辑是:如果控制器是模拟的,则进行特定的处理;否则,就使用数字输入来控制移动。由于当前只有一个数字控制器(键盘),我们假设这个分支只会被执行一次。然而,实际情况是系统会对每个控制器循环多次,即使只有一个数字控制器存在。

进一步调试后发现,模拟摇杆的状态并未被正确设置为模拟输入。这导致了玩家移动的代码被执行了五次,而不是预期的一次。换句话说,角色会因为这段逻辑的错误移动五次,这是完全不正确的表现。

这一问题的根源在于,我们的代码仅在某些情况下初始化了模拟输入状态。如果模拟摇杆未被使用,系统默认将其视为“非活动”,这虽然不会导致程序崩溃,但显然与预期逻辑不符。因此,这段代码需要调整,确保控制器的输入处理逻辑更准确、合理。

在这里插入图片描述

思考如何处理输入

我们需要深入思考当前面临的问题,以及如何规划解决方案。尽管这并不是当前最迫切需要解决的内容,但它与整体逻辑密切相关,值得我们提前讨论并构建清晰的处理思路。
目前,机器上可能连接了多个控制器,例如键盘、Xbox控制器或PlayStation控制器。即使只有一个玩家参与游戏,我们也需要明确这些控制器与玩家之间的映射关系。
一种简单的解决方案是,让所有控制器都默认映射到唯一的玩家。这意味着玩家可以随意切换控制器,例如在键盘和游戏控制器之间自由切换,无需特别配置。这种方法实现起来相对简单,可以满足单玩家的基本需求。
然而,如果我们考虑支持多玩家的情况,问题就变得复杂。例如,在同一台机器上支持两个玩家,一个使用键盘,另一个使用控制器。这种情况下,我们需要明确不同玩家和控制器之间的对应关系,以避免混乱。
设想一个场景:第二个玩家加入游戏,与主角一起进行冒险。此时,如何管理第二个玩家的输入,以及如何确保两名玩家的控制互不干扰,将成为需要解决的核心问题。
这一切需要我们制定更加灵活且清晰的映射逻辑,不仅要支持单玩家无缝切换控制器的便利性,还要为多玩家场景提供合理的输入管理方案。这不仅是一个功能性的需求,也是对游戏体验优化的重要环节。

哪个控制器映射到哪个角色?

当前的问题集中在如何正确地将控制器映射到屏幕上的玩家角色上。假设场景中有两个角色和三个控制器(包括键盘和两个手柄),需要明确每个控制器控制的角色,以避免输入上的混乱。

单一玩家情况下的处理

对于单一玩家场景,一个直观的解决方案是将所有控制器默认映射到玩家。这意味着玩家可以随意切换输入设备,例如在键盘和手柄之间切换。这种方式简单直接,可以轻松实现,并提供了便利性。

然而,键盘的特殊性可能会带来一些挑战。例如,如果采用“按下键盘上的任意键即可加入”的方案,可能会导致误操作。比如,用户可能只是尝试返回主菜单,但按下了某个键,这可能被视为输入,导致错误加入游戏。因此,需要一种更明确且不易误触的方式来处理键盘输入。

多玩家情况下的挑战

当涉及多玩家时,情况变得复杂。例如,一个玩家使用键盘,另一个玩家使用手柄。需要一种机制来确定每个控制器的输入是映射到哪个角色的。如果一个玩家通过按下手柄上的“开始”键加入游戏,这种方式清晰易懂,已经是主流做法,但键盘却缺少类似的标准化输入模式。这使得在多玩家场景下处理键盘的加入变得棘手。

目前没有一个操作系统层面的统一标准来解决这一问题。理论上,这种控制器映射的管理应该由操作系统负责,它可以为所有游戏提供统一的配置界面,用户只需设置一次,而不需要在每款游戏中重新配置控制器。然而,由于操作系统在游戏支持方面的历史缺失,这种统一方案并未实现。

解决方案规划

我们需要一个既简单又透明的输入映射系统,既能避免用户的混淆,又能支持多控制器输入。理想情况下:

  1. 单玩家模式:所有控制器默认映射到玩家角色,支持玩家在不同输入设备间无缝切换。
  2. 多玩家模式:引入清晰的控制器绑定机制。例如,通过特定的手柄按键触发角色加入,并为键盘设计一个明确的加入流程。

此外,在代码实现层面,需要一种灵活的框架来管理控制器与角色之间的映射关系。这样的设计不仅有助于改善玩家体验,也能提升系统的扩展性,以适应未来可能的多种输入设备和复杂场景。

通过这种方式,我们希望能在不增加用户学习成本的情况下,提供简洁有效的控制器管理方案,同时为可能的多玩家支持打下基础。

我们将如何处理这个问题

目前的规划是简化游戏中的控制器管理。假设任何时候玩家都可以通过按下控制器上的“开始”按钮加入游戏。对于键盘,我们会自行定义一个明确的“开始”键。基本的思路如下:

  1. 自由加入与退出

    • 每个控制器只要按下“开始”按钮,就会加入游戏。
    • 如果再次按下“开始”按钮,则会退出游戏。
    • 这种机制让玩家可以自由地加入或退出游戏,操作简单且易于理解。
  2. 保持简单

    • 当前实现的系统不会引入复杂的绑定规则,也不会针对多玩家场景进行额外的处理。
    • 先通过这种简单直观的机制运行,后续再根据需要调整或改进。
  3. 未来扩展的灵活性

    • 由于最终游戏是否会支持多个玩家同时参与尚不明确,因此暂时不会设计过于复杂的系统。
    • 如果未来需要支持更复杂的场景,可以在现有基础上引入额外的功能。
  4. 代码实现方向

    • 在控制器代码中,需要明确每个控制器与角色之间的映射关系。
    • 映射的依据是按下的“开始”按钮。按下按钮的控制器将绑定到一个角色,从而实现角色与控制器的关联。

通过这种设计,我们希望在保持简单和易用性的同时,为未来的扩展保留足够的灵活性。这种以“开始”按钮为核心的机制简单直观,用户容易理解,也方便后续维护和调整。

将控制器映射到角色

当前的任务是优化代码结构,将玩家的运动逻辑分离并整理,以简化后续的操作和扩展能力。具体规划如下:

  1. 分离运动代码

    • 将与玩家运动相关的代码完全从主逻辑中抽离出来。
    • 这些代码会被放置到一个独立的位置,便于针对每个玩家分别执行。
    • 这种分离操作是为了暂时清理现有逻辑,便于在代码中执行其他调整。
  2. 为运动逻辑设置占位符

    • 在原有位置设置一个简单的占位符,标记玩家运动逻辑的位置。
    • 具体内容如:从玩家速度的处理开始,到与玩家运动直接相关的所有逻辑。
    • 这些代码将被视为单独的“玩家运动”模块,暂时移出主逻辑。
  3. 控制器索引处理

    • 分离运动代码后,需要处理控制器的索引逻辑。
    • 需要明确如何将多个控制器映射到对应的玩家。
    • 这一部分是当前调整的核心,目的是设计一种有效的方式,根据控制器输入动态匹配玩家。
  4. 映射控制器到玩家的策略

    • 计划引入一种映射机制,根据控制器的状态决定它绑定到哪一名玩家。
    • 具体实现细节将在整理代码后进行细化。

通过这一过程,将逻辑模块化,使运动相关代码和控制器映射逻辑独立管理。这种结构不仅提高了代码的可读性和维护性,也为未来增加更多功能(例如多玩家支持)奠定了基础。
在这里插入图片描述

在这里插入图片描述

将玩家信息引入“实体”结构以支持多名玩家

当前的目标是将代码结构改进,以支持多玩家的逻辑,同时保持代码的灵活性和可扩展性。具体工作流程如下:

  1. 引入实体概念

    • 尽管还没有正式的实体系统,但可以先引入一个简单的“实体”概念,作为后续扩展的基础。
    • 这个实体的结构将包含当前用于处理单一玩家的所有相关内容,如运动状态和输入状态。
  2. 抽离单一玩家的相关逻辑

    • 将现有与玩家相关的逻辑和数据从原来的代码中分离出来。
    • 这些逻辑包括玩家的输入处理、状态更新以及与游戏环境的交互。
    • 抽离后的逻辑会存储在“实体”结构中,以支持多个玩家。
  3. 扩展以支持多玩家

    • 创建一个包含多个实体的数组,用于存储所有玩家的状态。
    • 每个控制器对应一个实体,这样就可以动态地根据控制器输入管理多个玩家。
  4. 优化数组管理

    • 确保实体数组可以灵活扩展以支持任意数量的玩家。
    • 在现有平台图形中检查如何管理这些数组的内存分配和容量,保证系统的稳定性。
  5. 代码组织结构改进

    • 将实体的创建和管理逻辑从主循环中分离出来,设计专门的模块或函数处理。
    • 这样可以减少主逻辑的复杂度,提高代码的可读性和维护性。
  6. 动态映射控制器到实体

    • 确定如何根据控制器输入动态绑定或创建新的实体。例如,按下“开始”按钮时,可以为该控制器生成一个新实体。
    • 实现机制以便移除不再活动的实体(例如玩家退出)。

通过引入实体的概念和支持多玩家的结构,代码不仅更易扩展,也为未来的功能开发提供了清晰的逻辑基础。这一过程可以确保每个控制器动态对应一个独立的玩家,使游戏支持多人模式成为可能。

在这里插入图片描述

修改游戏输入以支持多名玩家

我们需要确定当前控制器的数量,动态分配对应数量的玩家,并初始化每个玩家的状态。以下是核心步骤的总结:

  1. 确定每个控制器对应一个玩家。控制器的数量将决定当前游戏中存在的玩家数量。
  2. 创建一个逻辑结构,通过动态调整玩家的数量来适应控制器的增减。
  3. 使用一个方法(如 AddPlayer 函数)来初始化新加入的玩家状态,包括起始位置和初始速度。
  4. 对每个玩家循环执行相关的逻辑(例如移动、输入处理等),而不再将逻辑绑定到单一玩家对象上。

具体实现方法总结如下:

  1. 确定控制器数量
    确保可以检测控制器数量,并为每个控制器创建一个对应的玩家对象。

  2. 动态创建玩家对象
    为每个控制器分配一个玩家对象,通过 struct entity 结构体表示每个玩家。可以使用动态数组存储玩家列表。

  3. 初始化每个玩家状态

    • 在检测到新的控制器时,调用 AddPlayer 函数来为该控制器分配一个新玩家。
    • 初始化玩家的起始位置 PlayerP,例如设置为游戏地图的默认起始位置。
    • 初始化玩家的速度 dPlayerP,通常为零,表示初始时静止。
  4. 循环处理玩家逻辑

    • 遍历所有玩家对象,执行与玩家相关的游戏逻辑,如处理输入、更新位置等。
    • 这样可以实现支持多个玩家,同时保持代码结构清晰。
  5. 断言与容错
    在实现中加入断言,确保代码逻辑符合预期。例如,确保添加的玩家数量不会超出系统支持的最大数量。

通过这种方式,游戏可以灵活应对不同的控制器数量,同时支持动态调整玩家状态,实现更灵活和可扩展的多玩家模式。
在这里插入图片描述

改变处理方式的想法

我们正在逐步完善多玩家支持的逻辑,主要涉及以下几点核心改动和设计考量:


1. 动态分配和管理实体

  • 引入了一个通用的实体数组,用于存储游戏中的所有实体(包括玩家)。
  • 实体数组通过标志位(例如 exists)判断某个实体是否处于活动状态。
  • 在添加新玩家时,会从实体数组中分配一个未被占用的实体索引并初始化该实体。
  • 初始化过程中:
    • 重置实体结构体的所有字段为零,以清除之前可能存在的数据。
    • 设置实体的 exists 标志为 true,表示该实体现在处于活动状态。

2. 控制器与玩家的映射

  • 为每个控制器分配一个对应的玩家索引,以便处理输入逻辑。
  • 玩家与控制器的映射通过一个 playerIndex 数组维护,记录每个控制器当前关联的玩家索引。

3. 初始化和更新逻辑

  • 初始化阶段,所有实体的 exists 标志默认为 false,表示它们尚未被分配。
  • 在需要添加玩家时:
    • 调用 AddPlayer 函数,为新的玩家初始化位置和速度。
    • 确保该函数的逻辑足够通用,可以处理多个玩家。

4. 摄像机逻辑与多玩家支持

  • 摄像机当前设计为跟随单一玩家(假设为主玩家)。
  • 在未考虑分屏模式的情况下,摄像机默认始终跟随所有玩家的一个中心点(可能是第一个玩家)。
  • 对于支持多玩家的可能性,添加了一个注释待办事项(TODO):
    • 如果未来支持多玩家模式,可以考虑引入分屏机制,使每个玩家有自己的摄像机视角。
    • 也可以探索是否需要强制玩家在同一屏幕内合作,以保持游戏体验的一致性。

5. 设计讨论与可能的改进

  • 当前设计默认支持一个玩家,但考虑到了未来扩展为多玩家模式的可能性。
  • 引入分屏机制可能会改变游戏玩法的核心设计,例如:
    • 分屏是否适合该类型的游戏。
    • 多玩家模式下,玩家是否能够分散在地图的不同区域。
  • 虽然未直接实现分屏逻辑,但通过 TODO 留下设计思路,便于后续扩展。

总结

当前代码的调整目标是将原本基于单一玩家的逻辑抽象化,以支持多个玩家的需求。通过实体数组和通用的初始化逻辑,游戏可以动态管理多个玩家实体。同时,针对多玩家模式的设计讨论明确了未来可能的改进方向,例如分屏机制或强制合作机制的取舍。这种灵活的设计为未来的扩展提供了足够的空间。
在这里插入图片描述

在这里插入图片描述

CameraFollowingEntityIndex

我们有一个摄像机,它会跟随特定的实体索引。基本上,只要有一个实体出现在视野内,摄像机就会自动跟随这个实体的位置。这个功能确保了游戏中玩家的视角始终聚焦在他们的活动范围内,而不是固定在一个地方。
在这里插入图片描述

让摄像机跟随某个玩家

我们在处理一个摄像机跟随的实体时,会根据摄像机的实体索引来捕捉和获取相应的实体。只要摄像机的实体索引有效,并且在实体数组的范围内,我们就会获取该实体。这样,当我们需要绘制所有的实体时,摄像机的操作会集中在这一实体上,而不仅仅是玩家的位置。这种方法简化了管理和更新实体的过程,并使得绘制代码更加清晰和易于维护。

对于实体的初始化,我们还提供了一个索引来管理特定的玩家。通过使用这些索引和操作来确定实体的存在性以及摄像机的跟随逻辑,能够确保游戏状态的正确性,并保持程序的清晰性和结构的整洁性。

在这里插入图片描述

绘制所有玩家

这段内容主要涉及处理实体位置、方向以及循环绘制等相关逻辑。以下是详细的总结:

  1. 时间差与向量替换
    首先提到了一个时间差的概念,即计算当前时间与先前时间的差值。这个时间差可以作为一个三维向量的一部分,并计划将其最终替换为一个完整的三维向量表达式。尽管目前实现的是二维逻辑,但未来计划扩展为三维。

  2. 处理实体的循环
    这里提到了一种“最简单”的方式来循环处理所有实体。

    • 遍历所有实体的逻辑是从索引 0 开始,一直到实体数组的上限。
    • 当前实现只是将实体简单地存储在数组中,每个实体通过索引访问。
    • 未来可能会优化存储结构,但目前这种实现是足够直接且可行的。
  3. 判断实体存在性并绘制
    对于每个实体,首先检查其是否存在。如果存在,则直接绘制。当前仅使用了简单的位图来表示实体,例如将实体作为“英雄”绘制。

    • 提到未来计划根据实体的类型决定不同的绘制方式,例如绘制敌人和障碍物时可能使用不同的表现形式。
  4. 面向方向的处理
    添加了“面向方向”的概念,目前暂时将其作为实体的一个属性存储。面向方向将决定实体的朝向,比如英雄的朝前方向。

  5. 指针的增量更新
    在每次循环中,指针会自动递增,指向下一个实体。这种处理方式确保所有实体都能够依次被访问和绘制。

  6. 未来的改进方向

    • 当前代码中对“实体存在性”的检查较为直接,但未来可能会改进为更复杂的逻辑,基于实体的类型或状态做不同的处理。
    • 当前对方向的处理暂时通过简单赋值完成,但未来可能需要更精细化的逻辑。
    • 存储实体的方式(目前为简单数组)未来可能会被更高效的数据结构替代,例如使用链表或空间分区等。

总结来看,这段逻辑的重点是实现了一个基础的实体处理框架,包括时间差计算、循环绘制、实体方向和状态的初步逻辑处理,并为后续的扩展留出了改进空间。

在这里插入图片描述

修复 HeroFacingDirection

在这段代码逻辑中,我们需要优化处理角色的移动方向。目前的方法是基于玩家施加的方向推力,但这并非最佳方式。以下是改进的思路:

  1. 处理实际移动时计算速度
    在角色移动逻辑中,通过计算其实际速度来确定方向,而不是单纯依赖施加的推力方向。速度是基于角色的移动向量dd_player来确定的。

  2. 通过向量确定象限
    我们将通过向量dd_player的分量来判断角色运动的象限。判断方法如下:

    • 如果y方向分量的绝对值大于x方向分量的绝对值,说明角色主要在垂直方向上移动,即向上或向下。
    • 如果x方向分量的绝对值大于y方向分量的绝对值,说明角色主要在水平方向上移动,即向左或向右。
    • 如果两者相等,可以定义一个优先级,例如保持先前的方向或默认一个方向。
  3. 具体实现逻辑
    首先需要绝对值函数来处理方向向量分量的大小。

    • x方向分量大于y方向分量:
      • 如果x > 0,角色向右移动。
      • 如果x < 0,角色向左移动。
    • y方向分量大于x方向分量:
      • 如果y > 0,角色向上移动。
      • 如果y < 0,角色向下移动。
  4. 处理速度为零的特殊情况
    如果速度为零,则角色保持之前的朝向。只有当速度不为零时,才更新角色的朝向方向。

  5. 代码实现的逻辑分支

    • 首先判断x分量的绝对值是否大于y分量的绝对值。
    • 根据分量值的正负,更新角色的方向。
    • 如果速度为零,角色方向不变。
  6. 结果总结
    通过这种方式,我们能够根据角色的速度向量动态更新方向,且在速度为零时保持其最后的朝向方向,确保逻辑的稳定性和可预测性。

这种方法逻辑清晰,便于维护,同时避免了冗余的判断逻辑,适用于角色的方向更新场景。

在这里插入图片描述

绝对值内置函数

我们正在讨论如何实现绝对值函数,并确保其高效运行。这里提到了一些重要的实现细节和逻辑:

  1. 实现绝对值函数的必要性

    • 为了计算向量的大小,需要实现一个绝对值的内置函数。
    • 绝对值的作用是获取一个值的大小,而忽略其符号位。
    • 这一操作应该由编译器直接生成代码,而不是调用外部的 C 库函数。
  2. 关于编译器的行为

    • 编译器通常会通过内置的 fabs 或类似函数,直接输出与绝对值相关的机器指令。
    • 这一过程避免了不必要的函数调用,直接生成高效的代码流。
  3. 后续验证工作

    • 在实现完成后,需要检查生成的汇编代码,确保绝对值的计算是直接内联到代码中的,而不是通过外部库实现。
  4. 逻辑框架的构建

    • 在代码的测试阶段,需要逐步整合各模块,确保基本功能可以正常工作。
    • 当前的测试逻辑暂未连接任何实体,因此在屏幕上不应显示任何内容。
    • 初步的功能是确保代码可以通过编译,这样才能进入下一步调试阶段。
  5. 摄像机跟踪功能

    • 摄像机功能已经实现,其作用是跟随一个特定的实体。
    • 为了确保逻辑的正确性,对某些代码部分进行了调整,例如修正括号的使用。
  6. 当前的预期结果

    • 在当前实现中,没有连接任何实体,因此屏幕应该为空。这符合预期的结果,也是检查代码状态的第一步。
    • 下一步将是逐步测试功能模块,并确保正确的显示输出。

通过这一系列操作,可以确保代码逻辑的严谨性,同时为下一步的功能开发打下基础。

在这里插入图片描述

将实体添加到游戏中

现在我们需要添加一个实体,并为每个给定的控制器进行处理。如果某个控制器被映射到一个实际的玩家,我们将把控制传递给该玩家;否则,我们将保持等待状态,直到“开始”按钮被按下。

当检测到“开始”按钮按下时,我们会为这个控制器创建一个新的玩家实体。如果没有按下,则保持不变,不执行任何操作。如果控制器已经映射到了某个玩家,我们接下来会处理与玩家相关的运动逻辑。

在处理运动时,可以利用 ddPlayer 来表示玩家的运动方向。对于支持模拟输入的控制器,可以将其摇杆的平均值赋值给 ddPlayer,作为玩家的移动方向和速度信息。这一逻辑通过读取控制器的模拟输入,计算出水平方向(StickAverageX)和垂直方向(StickAverageY)的平均值,然后将这些值作为玩家运动的输入。

逻辑步骤总结

  1. 检查控制器状态:

    • 如果“开始”按钮被按下,则为该控制器创建一个新的玩家实体。
    • 如果控制器未映射到玩家且“开始”按钮未被按下,则保持等待状态。
  2. 映射到玩家后:

    • 读取控制器的模拟输入。
    • 计算摇杆的水平和垂直平均值,分别存储为 StickAverageXStickAverageY
    • 使用这些值更新 ddPlayer,表示玩家的运动方向和速度。
  3. 根据 ddPlayer 值进一步处理玩家的运动逻辑,包括方向和速度的计算。

通过以上步骤,可以为每个控制器动态地分配玩家实体,并根据输入处理运动逻辑,从而实现对玩家控制的动态响应和灵活适配。

注意

  • 需要验证 ddPlayer 的数据处理逻辑是否与模拟输入匹配,确保在不同控制器类型下都能正确解析输入数据。
  • 当前实现只是逻辑的初步搭建,可能需要进一步测试和调整,尤其是在处理非法事件和边界情况时。

在这里插入图片描述

初始化一个玩家

我们需要重新修改的加载代码来调用它,但首先要进行真正的玩家初始化。这涉及到获取控制器索引,并存储每个控制器对应的玩家和地图信息。我们需要等待开始按钮的按下来创建一个新的实体。如果没有返回控制实体,则需要等待开始按钮被按下。这些决定将在接下来的一段时间内逐渐清晰。
在这里插入图片描述

崩溃!

我们应该知道,在初始化玩家时,如果控制器没有索引,程序应该创建一个新的索引。这样可以确保有一个新的玩家实体可用,以便进行后续的初始化。添加实体时,只需增加实体计数,并将游戏状态的实体槽指向下一个空的槽,这样可以顺利进行后续的操作。虽然这不是最终的解决方案,但它允许我们在当前的开发阶段快速启动,并逐步完善实体系统。

当崩溃发生时,我们应确保不写入空指针,这一点非常重要。我们需要从数组的最后一个计数开始,以防止数组越界问题,并确保实体数组为空时不发生错误。这些都是为了在未来进一步优化和完善游戏的系统设计。
在这里插入图片描述

MovePlayer()

我们有一个程序逻辑是处理玩家的运动。首先,从游戏状态中获取一个新的实体,这个实体是要控制的玩家。接着,我们会根据玩家输入的加速度(在模拟控制器上通过摇杆的平均值来设置;在数字控制器上通过按键状态来设置)来进行运动。如果没有控制器索引,我们会通过“开始”按钮来初始化一个新的玩家实体。

接下来,我们会在处理玩家的运动时,将速度计算与时间步长一起应用,以确保玩家在屏幕上正确移动。通过访问当前的游戏状态,我们可以获取到世界和玩家的相关信息,从而准确地进行玩家的更新。碰撞检测和实体属性(如碰撞大小)也可能成为玩家实体的一部分。

总之,这段代码实现了如何处理和更新玩家的移动,确保玩家在游戏中的正确位置和状态。它既考虑了输入的直接映射(按键状态)也考虑了模拟输入(摇杆操作)。
在这里插入图片描述

将玩家宽度/高度移动到 InitializePlayer()

玩家具有高度的属性,并且它们需要被移动。这些属性不应被复杂化,而是应该简单、直接地在初始化播放器时分配。每个玩家都可以有不同的大小,这样他们就可以在游戏世界中以不同的方式互动。

我们将宽度和高度作为实体的一部分,并且这些属性在实体内部使用。这种方式使得所有需要使用这些属性的人必须从实体获取它们,从而确保这些属性不会被误用或过度复杂化。

当这些属性被分配和初始化时,它们并不需要特别的前设计,只是随着游戏的发展逐渐出现。这种方式让我们不需要事先考虑复杂的设计,而是根据实际需求来进行代码的修改和扩展。这种代码导向的方式使得我们能够在需要时增加新的抽象或功能,而不会陷入过度设计的困境。

通过这种方式,我们确保了代码的简洁性和可维护性,使得每一步的操作都是直观的,并且代码需求始终引导着我们的开发过程。

在这里插入图片描述

在这里插入图片描述

调用移动代码

我们需要调用移动代码,以便控制器可以操控实体。当一个控制器控制了一个实体时,游戏需要执行相应的移动操作,以确保玩家能够在游戏中控制这些实体。然而,当前的实现中我们没有实际调用这个运动逻辑。因此,我们需要为每个控制器索引项检查它们是否正在控制一个实体。如果是的话,就应该调用运动代码来处理这些控制的实体。

虽然现在这个实现没有问题,但如果未来游戏的需求发生变化,我们可能需要考虑更多的细节,例如如何处理不同类型的控制器输入或实体状态。这种方式确保了在现有的代码结构上扩展时不需要进行大规模的重构,而是可以根据实际需求逐步增加新的功能或逻辑。

目前的实现并没有传递到控制实体的运动逻辑的输入,这可能是我们需要补充的部分。这一方面可能涉及到如何处理控制器的输入以及如何将这些输入映射到相应的实体运动逻辑上。确保这些输入正确地被传递到运动代码中是接下来我们需要解决的问题。

在这里插入图片描述

调试移动代码

在初始化玩家时,我们发现需要解决一些问题。例如,在每次初始化过程中,我们需要确保为每个控制器分配一个实体并正确关联这些控制器。这保证了在之后的游戏过程中,每个控制器都有一个关联的实体来控制。

另外,当游戏真正启动时,我们还需要保留一个无实体的槽位,这样可以避免对该槽位进行无效操作,确保它的用途仅限于初始化状态。这样可以防止后续对该无实体槽位的误操作,从而避免潜在的错误。

此外,初始化过程还涉及为每个实体设置控制器索引以及其他必要的数据,这确保了每个控制器都有一个有效的实体,并且能够进行相应的操作。在处理控制器和实体关联时,避免了由于未正确初始化而导致的控制失效问题,这确保了整个游戏逻辑的稳定性。

这种方式不仅使代码更清晰可维护,还便于未来的扩展和维护,因为每个控制器和实体都被正确地初始化和关联在一起。
在这里插入图片描述

再次修复朝向方向

在这里插入图片描述

调整移动常量

在这里插入图片描述

演示多玩家支持

我演示一个机械键盘,一个电脑软键盘模拟的手柄
在这里插入图片描述

在这里插入图片描述

关于分屏模式,你认为每个部分的分辨率是多少?

有时候,你需要调整参数比原来的更高一些来获得更好的效果。我们的加速度设置过高了,使得角色的动作看起来非常快速。调整后,虽然这感觉好多了,但我仍然需要进一步加固角色的动作。经过一些测试和调试,我们发现问题出在当我们调用循环时,控制器的动作过于频繁,导致角色行为不稳定。现在我们已经修复了这个问题,并且能够有多个玩家在游戏中同时参与,这对整体游戏体验非常重要。未来可能会考虑添加不同颜色来区分不同的玩家,以增强互动性。通过这些改进,我们的游戏变得更加流畅且多样化。

你曾说过,可视化问题从不浪费时间。我们应该用它来进行碰撞检测吗?

目前的状态是,我们还没有实现完整的渲染代码,因此无法真正进行可视化工作。我们只能做一些基础的功能,如碰撞检测和简单的文本显示。只有当我们实现了完整的渲染后,才能进行更为复杂的可视化工作,如在屏幕上显示线条和文字。这将是我们需要的四大要素之一,以便真正理解游戏中发生的事情,并有效地进行调试和优化。

实体只是为玩家准备的,还是包括所有游戏对象?

目前的状态是,我们只专注于处理能够移动的实体,即玩家角色。这是因为我们还没有达到可以处理所有游戏对象的复杂度。例如,游戏中的物体、道具等目前还没有被纳入系统的考虑范畴。我们只是在处理能够进行四处移动的角色,因为我们还没有开始考虑到更复杂的游戏元素。

当两名玩家同时触发一个动作时,如何处理输入?

在多人游戏中,当两个玩家同时触发一个动作时,问题不应该很严重。因为在这个环境下,所有的输入都会是相对平等的,它们都是同时间触发的。在这样的情况下,主要英雄实体可能会被赋予优先权,即在冲突中优先处理它们的输入。这种方法能够确保游戏逻辑能够平稳地处理多个玩家的同时输入,避免导致不必要的冲突或混乱。

在多名玩家的情况下,摄像机跟随谁?【代码变更】

在多人游戏中,当一个玩家离开屏幕时,如何处理相机跟随其他玩家的问题,基本上是考虑谁加入游戏的顺序。如果没有一个有效的实体跟随,那么相机会继续跟随第一个加入游戏的玩家。这种方法能够确保相机在游戏中的运动始终是同步的。然而,这种处理方式仍然需要解决在不同屏幕上和玩家加入后如何保持一致性的问题。例如,当另一个玩家进入时,他应该从初始位置开始而不是继续移动,而现有的相机处理逻辑可能导致画面上的实体错误地显示在相机视角之外。如果相机没有正确检查这些条件,那么就会导致在视觉上出现问题,因此需要检查和调整相机的渲染规则,以确保所有玩家都能被正确显示在相机视角范围内。
在这里插入图片描述

当决定支持多玩家时,为何决定将他们称为实体?

当决定要加入多个玩家时,我们考虑到的是这些玩家控制的实体。最初,我们并没有明确定义这些实体是否都由不同的玩家控制,或是他们只是游戏的一部分角色。在制作游戏的初期阶段,考虑到这可能只是一个单人游戏,我们并没有太多关注这些细节。最终,我们决定将这些实体作为一个控制的单元,而不是为每个玩家单独定义角色。我们希望简单地处理这些控制对象,而不去具体定义它们的功能或属性。这种方式使得我们不需要为每个玩家都特别设定一个明确的角色,进而简化了游戏的开发。

如何避免角色穿过树木?

当我们刚开始制作这个游戏时,我们并没有深入讨论碰撞系统的问题。这位开发者解释说,树木和其他物体在地图上的展示只是一种背景设置,并不是真正作为游戏实体存在于游戏逻辑中。它们只是用来美化画面而存在的,并不涉及到实际的碰撞处理。因此,玩家可以穿过这些物体,而不会触发任何碰撞检测。真正的碰撞系统将会在世界生成的过程中被引入,届时这些物体会成为地图的一部分,并进行碰撞检测,从而限制玩家穿越这些物体。
在这里插入图片描述

在这里插入图片描述

为什么使用基于物理的移动?

我们倾向于采用基于物理的运动控制来实现玩家的移动,因为这样可以使玩家在开始移动时拥有一个渐进的启动过程。这种方式避免了瞬间全速前进的感觉,使得游戏中的动作更加自然和有趣。许多感觉良好的游戏都采用了这种方法,因为它使得玩家的操作更具可预见性和控制感,同时增加了游戏体验的沉浸感。不仅如此,这种方式还可以防止控制的瞬间超速,让玩家在起步时拥有一种更真实的反馈,而不是立即达到最大速度,这在许多玩家看来更加真实和令人满意。

有没有必要捕获加载美术资源失败的情况?【代码变更】

我们会进行大量的图像处理工作,通过代码来实现,这包括处理加载失败的艺术资产情况。即使某些艺术资源没有加载成功,我们也需要有备用方案来支持这些情形。未来我们会更多讨论如何管理这些资产的加载管道,确保即使某些资源不存在,我们的游戏也能继续运行。此外,为了便于开发,我们可能会事先处理一些尚未绘制的代码,以备将来使用。这些措施都是为了在没有所有艺术资源的情况下,仍然能够正常开发和测试游戏。

将所有实体存储在一个数组中的概念会长期保留吗?

现在把所有的实体放在同一个数组中,只是简化了一部分,因为它实际上表示这个结构会持续很久。我们添加和引用实体的方式可能不会一直保持不变,但拥有一个大块的实体或是数组中的实体,可能会成为我们最终的解决方案,甚至会是我们发布的内容。

为什么不使用自动编译器?

使用自动编译器可能会使代码在保存或其他操作时自动编译,这种情况下,代码可能在一个不希望的状态下运行,从而导致当前游戏的崩溃或其他问题。所以,尽管自动编译器不是坏主意,但目前我不确定是否想要使用它。

游戏中断开控制器时如何优雅地处理?

当控制器在游戏过程中断开连接时,我们并不需要做太多来优雅地处理这种情况。它只是会被断开,玩家可以重新连接。如果发生了断开,屏幕上只需显示“请检查控制器”之类的提示即可。此外,如果控制器断开,玩家可能会选择转而使用键盘进行控制,这也是一种处理方法。总之,我们有很多不同的方式可以来应对这个问题,不会导致任何形式的故障。

比较浮点数与零时,不应该检查一个容差范围吗?

当比较浮点数与零时,我们不需要检查它是否在一个容差范围内。这是因为如果这些值不是完全等于零,那么我们可以安全地忽略它们,不会影响面向的方向判断。然而,如果这些值恰好等于零,那么我们就无法确定该图形的面向方向,因为面向的方向是唯一一个图形存在的方向。因此在这种情况下,我们只需将它们忽略掉,不再做进一步的处理。

主角是否会有对角线移动的精灵图?

不太可能会有向英雄倾斜的斜线移动。尽管我有些怀疑这是否是必要的,因为这并没有引起太多问题或被质疑。也许这意味着我们可以尽快结束,每个人都能早点上床睡觉。整个过程似乎已经结束了。

既然提到了小速度,是否会考虑小输入的问题,例如死区?

考虑到小的输入和控制器的死区,我们实际上是考虑到这些细节。我们手动进行死区处理,并将小输入剪辑到控制器的死区中,以便在准备输入发送给游戏时进行处理。此外,即使死区在物理上是圆形的,游戏中我们更倾向于使用方形的死区处理。这是因为在实际游戏体验中,方形的死区处理可能更符合游戏的控制反馈和玩家的感觉。因此,即使死区的物理形状是圆形的,我们可能还是会想要将其处理成方形的视觉效果。
在这里插入图片描述

关于将实体存储在数组中。如果对许多实体进行大量操作,是否可以利用这一点?

关于将实体存储在数组中的问题,似乎有很多可能性使得我们能够对所有实体进行大规模的操作。这种存储方式能够便于执行各种批量操作。然而,我怀疑大多数工作最终并不会专注于每帧处理所有实体。由于如果有成千上万个实体在场景中,频繁处理所有这些实体每帧是没有必要的。相反,我们更有可能优化在特定时间间隔内对那些真正在屏幕上需要更新的实体进行处理。因此,即使拥有大量的实体被存储在一个数组中,这种方式本身的利用可能并不显著。

为什么使用函数调用宏?

在函数调用中使用宏的主要原因是为了简化处理函数指针的方式。通过使用宏,可以确保所有定义的函数都具有相同的函数签名。这种做法可以减少错误的可能性,并确保代码的一致性。尽管在某些情况下,使用宏可能不是特别必要,尤其是当函数数量较少时,但如果有大量函数需要相同的签名,那么这种方式就显得尤为重要。

通过使用宏,可以避免手动定义和修改函数签名,提高代码的灵活性和可维护性。虽然在一些特定场景中可以选择不使用宏,但这是一种有效的编码习惯,有助于在更复杂的代码场景中保持结构清晰和一致性。

能解释一下“控制器死区”的意思吗?

控制器的“死区”是指模拟摇杆在其中心位置附近的一小片区域,在该区域内即使摇杆产生了微小的移动,系统也不会将这些移动识别为有效输入。这种设计主要是为了避免因硬件缺陷或漂移问题导致的不必要的移动指令。

具体而言,模拟摇杆通过一个叫做电位计的装置读取其位置,但由于硬件本身的限制,模拟摇杆并不能始终完全回到精准的中心位置。此外,弹簧的回弹也可能存在一定误差,而电位计可能存在校准不准确或漂移的问题。因此,当摇杆处于中心位置时,即使没有操作,系统仍然可能接收到一些非零值。

为了处理这些不精确性,“死区”被引入。死区定义了一定范围的值(从负到正),只要摇杆的返回值落在这个范围内,系统就会将其视为“未移动”,不对这些输入作出响应。例如,当摇杆的返回值在-400到+400之间时,这些值会被视为死区范围内的“零输入”。

这种设计确保了在用户没有实际操作摇杆时,系统不会因为硬件漂移或噪声误判为移动。同时,也能避免角色或摄像机在静止时发生轻微的、不必要的移动。

在开发过程中,可以通过调试工具直观地观察这些问题。例如,在未触碰控制器的情况下,可以查看控制器的返回值,通常会发现其值并非完全为零,而是存在一定的波动。这些波动就是“死区”需要解决的问题。

为了验证死区的功能,可以设置断点查看控制器的状态。当摇杆移动至死区以外的位置时,系统会开始识别为有效输入。一旦摇杆回到死区内,系统则会停止响应。这种机制保证了控制器输入的可靠性和用户体验的流畅性。

控制器是否有外部死区?

在控制器的操作中,除了中心位置的“死区”之外,有些情况下也会存在“外死区”。外死区是指摇杆接近其最大范围时的一段区域,系统可能会忽略这些值或处理方式不同。这种情况通常会涉及模拟摇杆的硬件设计,比如控制器返回的数值分布并不是一个完美的圆形,而是可能出现某种形状的畸变。

对于某些特定类型的游戏,可能需要解决外死区的问题,以确保在极限操作时的精确性。然而在一般情况下,这种现象通常对游戏的影响较小,尤其是与中心死区的问题相比。因此,在大多数场景中,对外死区的处理并不是一个主要的关注点。

不过,在开发过程中,如果涉及到死区的可视化功能,这确实是一个可以关注的点。通过对外死区进行形象化处理,可以观察摇杆在其最大操作范围时返回的数值分布是否均匀,并验证是否需要针对外死区进行进一步的优化或调整。

整体来说,外死区的问题通常比中心死区要少见且影响较小,因此需要根据具体的游戏需求和控制器的实际表现来决定是否进行特别的处理。

关于位图加载问题的补充:“用 32 位值与运算并使用结果”

在加载位图文件时,为了正确处理颜色数据,需要理解如何操作和移动颜色通道掩码的位。位图文件中可能会包含红色、绿色、蓝色等颜色通道的掩码值(如红色掩码)。在处理这些数据时,需要从掩码中提取颜色信息并对其进行适当的对齐和移动。

假设已经加载了位图文件,并获得了红色掩码。通过对颜色值进行与操作(AND),可以从颜色数据中提取出红色的8位信息。然而,问题在于,这些红色的8位可能并不位于数据的最低有效位(LSB)位置,因此需要进行移动对齐以便正确使用。

为了将提取的红色值移动到目标位置,面临的主要问题是:

  1. 无法直接确定移动方向和位数
    由于不清楚红色信息在掩码中的具体位置,无法简单地通过向左或向右移动来完成对齐。例如,不知道掩码的最不重要位(LSB)距离实际需要的位置有多远。

  2. 需要动态计算移动量
    为了对齐颜色信息,必须先将数据移动到底部(最低有效位),然后再根据实际需要将其移回目标位置。

具体步骤如下:

  • 第一步:将数据移动到底部
    使用掩码的最低有效位确定红色信息的起始位置。可以通过逻辑操作(如位移和与操作)将掩码中表示红色信息的部分对齐到最低有效位。这一步确保了红色数据被正确提取并归零对齐。

  • 第二步:将数据移回实际位置
    根据原始掩码中红色信息的位置,将数据从最低有效位移回正确的目标位置。通过动态计算移动量,可以准确恢复到位图文件中颜色数据的原始位置。

这样的处理方式避免了依赖固定方向的位移操作,确保了适配任何掩码的灵活性。此外,避免直接操作掩码(如重新加载或修改掩码)可以提高效率,尤其是在掩码被频繁使用且需要留在寄存器中时。

这种动态调整掩码对齐和移动的做法在处理复杂位图加载逻辑时非常重要,可以确保颜色数据的正确性和程序的运行效率,同时减少不必要的资源浪费。!
在这里插入图片描述

会使用组件系统吗?

关于是否使用组件系统,将更小、更集中的结构映射到碰撞和图形实体,以支持更高效的循环处理。这个方法的核心思想是在每一帧内,通过组件系统对实体的属性和行为进行解耦,使得系统能够高效地迭代和处理特定的功能模块。

组件系统通常会将复杂的实体拆解为若干独立的功能模块,例如碰撞处理模块和图形渲染模块。通过这种方式,可以实现以下目标:

  1. 提升性能
    将实体的功能分离后,每个模块只需处理与自身相关的数据,减少了不必要的逻辑判断和处理步骤。

  2. 提高可维护性和扩展性
    使用组件系统后,每个功能模块可以独立开发和调试,便于后续的功能添加或优化。

  3. 支持灵活的循环机制
    在每一帧内,通过针对性地迭代组件(如只更新碰撞相关的组件),可以显著提高资源利用率。

尽管这种方法在理论上有显著优势,但在实际应用中需要权衡。当前需要优先解决的实体系统相关问题可能更为重要,例如:

  • 如何设计实体的生命周期管理:包括创建、销毁以及数据同步等问题。
  • 如何定义组件的接口和依赖关系:确保各模块之间的协作高效且不会导致意外行为。
  • 实体系统的性能瓶颈:在复杂场景下如何优化组件的调度和资源分配。

因此,虽然引入组件系统是一个值得探索的方向,但应在解决基础的实体系统问题后再深入研究和实施。当前最关键的任务是梳理实体系统的整体架构,确保其基本功能的稳定性和性能,然后再逐步引入更精细的组件化结构以优化运行效率。

估计什么时候会出现 GUI?

讨论的内容围绕一个游戏设计展开,明确了其定位和特点:

  1. 游戏风格与定位
    游戏将被设计为一款动作游戏,专注于快节奏的战斗和互动元素。这意味着游戏中不会加入如菜单或其他复杂的UI系统,而是尽可能简化玩家的操作界面,以增强沉浸感和流畅性。

  2. 独特的游戏机制
    游戏的目标是打造一个完全以动作为核心的体验,不会使用如浮标等界面提示或非必要的交互元素。这表明游戏的设计方向更倾向于直接和纯粹的玩家输入反馈,而非依赖外部辅助。

  3. 灵感来源与实现
    游戏可能会从自然或其他抽象概念中获取灵感,如风的动态效果。这种灵感将通过视觉、声音或物理模拟等方式融入到游戏中,成为游戏氛围和机制的一部分。

  4. 设计简化的必要性
    简化的设计不仅能降低玩家的学习成本,还可以增强游戏的沉浸感和节奏感,使得玩家能够专注于游戏的核心内容,而不会被菜单或其他复杂系统分散注意力。

总的来说,这是一种强调动作核心和简化交互的设计理念,力图为玩家提供直接、纯粹的游戏体验。

聊天提到:有旋转指令

  1. 旋转指令的可用性探讨

    • 讨论了旋转指令(rotate instruction)的具体支持情况以及它在不同位宽(如16位、64位)下的实现可能性。
    • 提及了可能的误解,认为旋转指令仅适用于16位数据,但经过验证发现可以在64位上实现。
  2. 指令测试与验证

    • 为确认旋转指令是否真实有效,尝试使用示例代码进行测试,并查看其在汇编层的表现。
    • 使用了具体指令调用的方式进行验证,例如使用 __rotl 函数处理数据,尝试将位移逻辑直接映射到旋转指令上。
    • 在测试中提到可能需要优化设置才能观察到实际效果,因为简单的代码可能不会直接触发底层硬件的旋转指令。
  3. 旋转指令的应用价值

    • 如果旋转指令可以直接使用,那么在处理位运算时,尤其是在需要多次移位和掩码处理的场景下,将显著简化逻辑并提高性能。
    • 通过单一旋转操作即可完成复杂位移逻辑,而无需多次执行左移和右移操作,从而降低指令执行次数和寄存器占用。
  4. 开发中的优化思路

    • 提出了如何通过旋转指令的使用减少代码冗余并优化性能的可能性。
    • 强调通过对掩码(mask)的精确控制和位移的优化,可以避免传统移位操作中的复杂性。

总结来说,讨论核心在于探索如何利用底层硬件指令来优化位操作逻辑,验证旋转指令的有效性,并评估其在代码性能优化中的潜力。如果能够确认旋转指令的可用性,将极大简化相关代码逻辑并提升运行效率。
在这里插入图片描述

【代码变更:使用旋转指令】


1. 旋转指令的引入与目标

  • 探讨了通过单一旋转操作(rotate left/right)来简化移位操作,避免传统的双向移位组合方式。
  • 利用旋转指令的目标是高效地将不同颜色通道(红、绿、蓝、透明度)从一个整数值中提取并重新排列。

2. 旋转操作的实现逻辑

  • 确定旋转操作的核心:找到颜色通道的目标位置,并计算其与当前位移位置之间的差值。
    • 通过扫描位操作(bit scan)获取当前通道的有效位索引。
    • 计算目标位置与当前索引的差值,得出需要的旋转偏移量。
  • 使用旋转指令(如 rotate leftrotate right)将颜色值移动到正确位置。
  • 对每个颜色通道(红、绿、蓝和透明度)重复上述逻辑,依次计算并完成相应的位移。

3. 代码实现和验证

  • 具体步骤:
    • 通过计算当前位扫描结果,确定需要的旋转方向和位移量。
    • 调用旋转指令进行计算,将结果存储在对应变量中(如红移、绿移、蓝移等)。
    • 通过断言(assert)确保计算逻辑和结果正确。
  • 在汇编层验证旋转指令的实际效果,确认编译器正确调用了底层硬件支持的旋转指令。

4. 旋转指令的优势

  • 简化位操作逻辑:
    • 避免了传统逻辑中多个掩码(mask)和移位操作的复杂组合。
    • 单一旋转指令即可完成原本多个移位指令才能实现的功能。
  • 性能优化:
    • 减少了指令执行次数和数据传输的开销,提高运行效率。
  • 应用场景:
    • 在位操作密集的任务中(如图像处理、颜色数据提取等),旋转指令可提供显著的性能提升。

5. 结论与后续优化方向

  • 通过验证发现,在当前架构(如x64)中确实支持旋转指令,并能够直接调用。
  • 此发现解决了之前未使用旋转指令的技术瓶颈,同时也为代码优化提供了新的思路。
  • 后续可进一步扩展旋转指令的使用场景,评估在其他位操作场景中的实际表现和潜在改进空间。

总结来说,利用旋转指令优化位操作逻辑,不仅简化了实现,还显著提高了代码性能,特别是在需要频繁移位和掩码操作的任务中。
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com