ECS 生存者零件 VII – X
ECS Survivors Parts VII – X

原始链接: https://blog.ptidej.net/ecs-survivors-parts-vii-x/

## ECS Survivors:近期更新总结 经过七个月的停滞,ECS Survivors项目在四个更新中取得了显著进展。该项目现在具有改进的视觉效果,集成了使用Tiled编辑器和tmxlite库的**瓦片地图**。通过实施用于瓦片渲染的“截图”方法以减少绘制调用,以及**贪婪合并算法**以大幅减少碰撞体数量,从而优化了性能。 通过添加**空间哈希网格**以加速碰撞检测,进一步提高了性能,从而在处理大量实体时将速度提高了10倍。 通过**升级系统**引入了游戏进程,允许玩家在击败敌人后获得强化道具。 最后,一次重大**重构**将代码库组织成分层架构,并采用新的文件层次结构和CMake配置,从而能够创建单独的模块(输入、渲染等)和应用程序——包括潜在的编辑器和无头服务器,从而改善了代码组织和未来的可扩展性。 开发者承认过于雄心壮志减缓了进度,但该项目现在处于稳定状态,未来的开发将侧重于核心游戏玩法功能,例如近战攻击。可在Itch.io上获取可玩版本,并在GitHub上获取源代码。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 ECS 生存者第七至第十部分 (ptidej.net) 7 分,作者 yann-gael 27 分钟前 | 隐藏 | 过去 | 收藏 | 1 条评论 帮助 yann-gael 27 分钟前 [–] 我是软件工程教授,我要求我的研究生撰写关于他们研究的博客。这篇文章由 Laurent Voisard 撰写,他研究用于开发许多游戏的实体-组件-系统,以及(通常)它对软件质量的影响。回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

Well, a lot of time has passed since the last time I made an update to the ECS Survivors project. Life has a way of well... getting in the way of things, or you could also say I got a bit lazy. But at long last, I've come to tell you all about the shiny new features that ECS Survivors holds!

In this instalment of the series, I'll cover the many systems and features that were introduced over the 4 "parts" or 7 months (has it really been that long!?!). My memory might not recall some of the details for each update, so I will give a surface-level explanation rather than going more in-depth as I did with the other posts. Without further ado, I now unveil the weathered, dust-riddled curtains! (I've been reading a lot of Tolkien recently and find myself in awe before his imagery, metaphors and similes. I want to integrate that style into my casual writing, so sorry about that 😄)

Part VII - Tilemaps

I wanted to give a little life to the game; So far at this point, the background was of a simple, dull gray. Tilemaps are fundamental to most 2D platformers, top-down adventures, survivors, and others of the likes. In this instance, I didn't want to reinvent the wheel and create my own over-engineered level-editing tool, no! Tiled is a fantastic tool to quickly create intricate levels and embed metadata along with them. Identifying collidable objects, such as the map edges very easy. I used the tmxlite library to load-in the tilemap file. Early on, I simply created a new tile instance for each of the ones specified in the original file and drew each one individually. The same logic was used for the colliders of the tilemap; I created a box collider for each tiled which has the embedded metadata.

Now, the keen of you will notice that both of these implementations introduce sub-optimal behaviour.

  • For one, drawing each tile individually, while not a tremendous issue, can quickly get out of hand as the size of the map increases. A simple tilemap of size 64 by 64 will result in 4096 draw calls, and that's just too much to do every frame. A rather clever and trivial solution exists. We will only draw each tile once, then take a "screenshot" using the Raylib RenderTexture, and from that point, we only need to draw the screenshot, reducing the draw calls to 1. Later, we might have dynamic elements on the tilemap, such as destroyable boxes, opening doors, and those we can draw individually, since there will be very few.
  • The second issue is quite more performance hungry. Again, in the case of a 64 x 64 tilemap, in the worst possible case, there are 4096 new colliders. This is unacceptable for performance so we need to simplify the colliders. A basic, yet efficient approach is to implement some sort of greedy meshing algorithm to maximise the surface area of the colliders. I found this blog post for Roblox, it breaks down the algorithm in depth. Using this technique, we can drastically reduce the amount of colliders, especially in larger areas such as the map boundaries.

Part VIII - Accelerated Collisions

At this point, our collisions worked well, as soon as we want to scale up the difficulty of the game, by introducing thousands of enemies, then our current, quadratic, implementation of collision detection and resolution will not be up to the task. Hence, we will add a simple yet efficient acceleration method for collision lookups — a spatial hashing grid. Simply put, right now, when we detect collisions for one entity, we perform checks with every other entity (n x n). In a spatial hashing grid, we split up the area into multiple cells, and now looking for a collision with an entity will only look for other entities within its neighbouring cells. I recommend you look at Ericson's Real-Time Collision Dectection Book (2004), specifically chapter 7.1 - Uniform Grids. There is not much else to talk about for this update, apart from maybe the performance gains from the acceleration structure. We go from 600 to 7000 colliders now, more than 10x performance gain!

Part IX - Levelling Up

We're making a game, of course, we need some kind of progression mechanic. What's typical for survivors-like games is that after you have gained enough experience through picking up somekind of experience resource, or just through eliminating an enemy, you get to pick from three random power-ups. I decided that we would only need to slay enemies to gain experience, and for now, at least, the player would have fixed choices when they level up. This will likely change once there are more abilities and ways to enhance them. It's really just that simple, I get to use the GUI framework I built and voila! We have player progression in our game.

Level Up Popup Window.

Part X - Yet Another Refactor

So this part is the one that took the most time and burned me out a little. At this point after finishing the levelling up, I wanted to work on more gameplay features, specifically I had been playing around with procedural generation (see my other post) and wanted to introduce it in the game. But in order to do that, would need some kind of tool to build the procedural generation rules and such. But to build the tool, I would need to refactor my code yet another time, to allow me to have the same window manager for both the game or any other app. You can probably see now that this is somewhat of an endless spiral of requirements and heavy alterations of the current codebase. In any case, I worked and thought for a long time to properly separate my code into somekind of layered architecture. I'll do my best to detail everything.

Tentative Architectural Layers

File Hierarchy and CMake Configuration

So first thing I wanted to reorganise the file hierarchy because it was starting to be messy, but this was mostly so I could generate different libraries and build targets. This allows me to, if necessary, create a GitHub repo for each module and keep them truly as separate projects, avoiding tight coupling. It also makes it more explicit and harder to import from a module that you shouldn't be using, since the target links are defined in the CMake files. For example, including any file from the rendering module into the input module is impossible. The only limitation to this system is that I might be tempted to include a module I shouldn't, out of laziness.

input/cmakelists.txt

target_link_libraries(input PUBLIC
        base
        core
        raylib #only using the datatypes
        flecs::flecs_static
)

input/input_module.cpp

#include rendering/components.h // this will complain that rendering cannot be found.

Applications

Next, I thought it would be a good idea to build separate apps, using the same new modules I refactored. So I have a base Application class, with an init() and run() function. This clas,s however, does not depend of Raylib, so it can be instead used as a headless application for something like a web server if I eventually want to make some networking features. I also have a GraphicalApplication class, which is essentially the same but includes Raylib for the windowing. From these, I was able to create ECS-Survivors, the original game, an Editor for ECS-Survivors, and finally, a basic headless app, just for the proof of concept really.

联系我们 contact @ memedata.com