Home  >  Article  >  Technology peripherals  >  In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

WBOY
WBOYforward
2023-04-11 22:16:071493browse

More than two years ago, Adobe issued an attention-grabbing announcement that it would end support for Flash on December 31, 2020, announcing the end of an era.

Two years later, Adobe has already deleted all archives of early versions of Flash Player from its official website and blocked Flash-based content from running. Microsoft has also ended support for Adobe Flash Player and banned it from running on any Microsoft browser. The Adobe Flash Player component was permanently removed via Windows Update in July 2021.

After Flash was taken off the shelves, in a corner of the world, this "old comrade" was still exerting his remaining energy.

Hapland is a Flash puzzle game launched in 2005, and it is also a childhood memory for many people. In the game, players need to find ways to open levels by enlisting the help of people in this world without letting them be eaten by monsters or blown up by landmines.

The graphics of this game are drawn in Flash, the code is written in Flash, and all animations are completed in the Flash timeline. It can be understood this way: this game has "Flash in its bones."

As a member of the game development industry, Robin Allen noticed that people seemed to particularly like the Hapland game, so he wanted to make some fixes to the Steam version of the Flash-based game, including drawing. Better graphics, increasing the frame rate to 60FPS, adding some extra "secrets" and more.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

What to do at this time? The author describes the experimental process in detail.

Some failed experiences

Failed attempt 1:

My first attempt The thing was getting Flash to export the game as an executable file, but that failed because the performance was just as bad as it was in 2005. I want to make something that runs at contemporary frame rates. I want to get rid of Flash Player.

Failed Attempt 2:

Secondly, I spent too much time fiddling with Adobe AIR (Flash Desktop runtime ) and Starling (a library for drawing Flash scenes on the GPU).

I ended up giving up on this, partly because AIR had so many issues and was terrible, but also because I didn't want to end up with a weird Adobe result at the end of it all; I wanted to have my own thing , can do what I want. For example, what if I want to move to Linux?

The path forward was obvious: I had to make my own Flash player.

Plan

Here’s how Hapland works. There's a sprite tree here, and in Flash, animation sprites can attach code to certain frames that run when the play arrow gets there. Hapland uses this approach often. The traveling paths of the game characters are all very long timeline animations, and the characters often have frame-by-frame actions, such as opening a door after it is closed, or triggering a trigger if it reaches a minefield before it explodes.

The little “a” in the timeline is the frame action.

Luckily, .fla files are just XML. I just had to parse it, export the relevant data to a simple custom format and write a player to read it, draw the scene, handle the input and run the animation.

Hapland is still a Flash project, written and maintained in the Flash Editor; only the Flash Player will be replaced.

Rasterized Vector

Flash does support raster graphics, but is actually designed for vector graphics. That's why Flash movies load quickly even over a dial-up connection.

All Hapland graphics are vector graphics. The GPU doesn't like drawing vector graphics very much, but likes large batches of textured triangles. So, I need to rasterize these vectors.

I decided to rasterize them offline and package the raster files into the game. It would be fun to rasterize them and become this tiny executable while the game is running, but I don't want to have those extra moving parts. I like to have as much code running on my development machine as possible so I can keep an eye on it.

Flash stores vector graphics in XML format. You might say that XML is a bad choice for graphics data, but you're not a product manager at Macromedia. Take a look at this:

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

The vector data seen in the .fla file.

I'm not complaining, it makes my job easier.

Although I don't have access to spec, rasterizing this isn't a problem. The Bezier curve model of vector graphics has been ubiquitous since PostScript. All these APIs work the same way. After some trial and error, I wrote a program to parse these shape definitions and render them into PNGs using the Mac's CoreGraphics library.

CoreGraphics is a questionable choice. I chose it because I work on a Mac and have a lot of dependencies. But it did work, so I always had to rasterize graphics on the Mac, even on the Windows version. If I were doing this again, I'd probably choose a cross-platform library.

After rendering these PNGs, will the exporter assemble them into an atlas? No, it just sorts everything by height and then goes line by line like the text in the document. It's far from optimal, but it's good enough.

For simplicity, the atlas is 2048×2048 pixels, which is the minimum texture size that an OpenGL 3.2 implementation must support.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

Illustration set from Hapland 3.

Rasterizing shapes is very slow, so to keep build times reasonable I need to skip rendering things that haven't changed. The compressed XML format used by Flash does have last modified fields for each file, but Flash doesn't seem to use them correctly, so you can't rely on them.

Instead I just hash the XML for each shape and only rebuild if it changes. Even this fails because Flash sometimes likes to rearrange XML tags in unchanged objects, but again, this is enough.

Writing binary files in assembler

The exporter writes animation data to a custom binary format. It just goes through the timeline frame by frame and writes out all the changes for each frame.

I thought of writing an assembly list here instead of writing directly to a binary file, and I like that. There are no CPU instructions, just data, which makes debugging easier because I can look at the assembly file to see what was generated instead of browsing bytes in a hex editor.

output.bin

13 92 49 EC : BD 31 E8 FF
09 DD BE DE : C9 5A 1D 36
3F C0 4E 31 : 52 FD 41 C6
8B 5D C0 20 : 19 1F 5F 1F
54 97 8C 27 : 34 1F 30 EA
A9 A9 E0 55 : 40 29 A3 19
89 BC 5F 24 : 3A 98 FD B9
DE 15 F2 D4 : 2A B7 41 2C
4E 9D 37 D9 : E2 13 4B 01
36 3F 40 08 : AC 3C FF 84
E9 AE C5 2C : 11 2F 69 CF
63 CE 85 D1 : A7 CB B1 1A
5F 5B 60 1A : 77 99 71 B0
60 6E C4 C7 : 73 1F EA 1F
31 0D 0C 39 : B0 86 70 42

output.asm

; Left Side
timeline_132:; --- Left Side, Frame 0 ---
.frame_0:; --- Left Side, Frame 0, Layer 22 ---
db Quad
dd 0.152926, 0.162029, 0.184475, 1.000000 ; color
dd 799.599976, -20.950001dd 799.599976, 556.650024dd 46.000000, 556.650024dd 46.000000, -20.950001; --- Left Side, Frame 0, Layer 21 ---
; instance of shape [Left Side] [Wall Shadows] [Frame 0]
dd Shape
dw 1560

Which one would you rather debug?

I could have had the exporter write the bytes to one file while writing a separate list of text to another file without using assembler, but I didn't, Because:

1) The assemblers already exist;

2) I don’t have to debug them;

3) They support tags.

导出器的其余部分大多不够有趣;它只是 walk the tree 并将变换矩阵、颜色效果等事物,然后继续游戏程序本身。我选择用 C++ 编写这个,因为我已经知道它,并且新事物让我害怕。

场景图

Hapland 非常适合场景图。这是 Flash 使用的模型,Hapland 就是围绕它设计的,因此尝试使用不同的模型是没有意义的。

我将场景存储在内存中,作为一棵节点树,每个节点都有一个变换,可以自行绘制并接受鼠标点击。每个具有自己行为的游戏对象都是其自己类的实例,派生自 Node.js。「面向对象」目前在游戏开发圈子里并不流行,但我使用的是 Flash,所以显然不关心这个问题。

Hapland 使用的 Flash 功能,如颜色变换和遮罩,都是存在的。不过我没有像 Flash 那样实现任意遮罩,只是实现了矩形剪辑并编辑了我所有的图形,所以所有的遮罩都是矩形。

框架脚本

几乎所有的 Hapland 逻辑都包含在附加到时间轴帧的 ActionScript 中。要如何导出所有这些东西?我可不想在我的游戏中包含 ActionScript 解释器。

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

一个简单的帧动作。

最后,我们使用了一些技巧,我的导出器从每一帧读取 ActionScript 并应用大量正则表达式以尝试将其转换为 C++。例如,crate.lid.play () 可能会变成 crate ()→lid ()→play ();。这两种语言在句法上非常相似,这对于许多更简单的框架动作来说效果很好,但它仍然留下了相当多的错误代码,除了手动重写所有剩余的框架动作之外别无他法。

对于 C++ 中的所有框架脚本,它们在构建时被提取并成为每个符号的 Node 子类上的方法。还会生成一个调度方法以在正确的时间调用,看起来像这样:

void tick() override {
 switch (currentFrame) {
 case 1: _frame_1(); break;
 case 45: _frame_45(); break;
 case 200: _frame_200(); break;
 }
}

需要指出的最后一件事是脚本系统最终是某种静态类型的,这有点难受。游戏输出的最终游戏对象如下所示:

struct BigCrate: Node {
 BigCrateLid *lid() { return (BigCrateLid *)getChild("lid"); }
 BigCrateLabel *label() { return (BigCrateLabel *)getChild("label"); }

 void swingOpen() { ... }
 void snapShut() { ... }
 void burnAway() { ... }
};

因此,即使一切仍然是大量的自动字符串名称查找,类型安全的单板会阻止你在错误的对象上调用错误的函数,从而使你免于在动态语言中遇到的那类烦人的 bug。

纵横比

HD 重置版游戏都会遇到画面拉伸的问题,最初的 Flash 游戏很多是页游,甚至没有全屏运行的能力,所以它们只是使用设计者喜欢的宽高比,大多是 3:2 左右。

如今最常见的纵横比似乎是 16:9,16:10 在笔记本电脑上也很流行。我希望游戏在其中任何一个方面看起来都不错,没有任何黑条或拉伸。要做到这一点的唯一方法是从原件上切掉一些部分,或者在上面添加一些部分。

所以,我为游戏画面画了两个矩形,一个比例为 16:9,另一个比例为 16:10。然后游戏根据屏幕的宽高比在它们之间进行插值,并使用插值矩形作为视图边界。只要所有重要的游戏元素都在这些矩形的交叉点内,并且它们的公共边界矩形不超出场景边缘,就可以很好地工作。

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

Hapland 2 的 16:10 和 16:9 框,与原来的 3:2 不同。

Problems with color space

After some testing, I discovered that Flash does alpha blending and color transformations in perceptual space rather than linear space. This is mathematically dubious, but on the other hand we should also know that many graphing programs work like this, you want your consumer tools to work the way people expect, although this is a kind of problem for mathematicians. offend. But fundamentally, this is wrong! It can cause problems like anti-aliasing.

When you rasterize vector graphics and require anti-aliased output, the rasterizer will output an alpha value, the so-called "overlay value". This means that if a given pixel is half-covered by a vector shape, that pixel will be output with alpha = 0.5.

But in Flash, when something has an alpha of 0.5, that means it's perceptually halfway between the foreground and background colors.

This is not the same thing at all!

Half-covered white pixels drawn on top of opaque black pixels should not be perceived as 50% gray. That's not how light works, and that's not how vector rasterization works. A rasterizer cannot say "this pixel should sense xx% between the background and foreground colors" without knowing the background color.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

Blending done in perceptual (sRGB) space. Top: black on transparent white; middle: transparent black on white; bottom: same blend of gray done in linear (physically accurate) space. Note that 50% coverage looks different than 50% gray.

So our anti-aliased rasterized shapes use one definition of alpha, while our Flash-exported alpha transparency, gradients, and color transforms use another definition. But we only have one alpha channel in our rendering pipeline. So how should the renderer interpret the alpha value? If it interprets them as perceptual blending factors, the translucent objects will look correct, but the anti-aliased edges of everything will look wrong. If it interprets them as coverage values, then vice versa. Something always looks wrong!

Here, I think there are only two serious solutions: 1) Set two alpha channels, one for overlay and one for perceptual blending; 2) without AA Rasterize all shapes, draw everything to a very large framebuffer, and then scale it down with filtering.

I must admit that none of these ideas have been implemented. These translucent things looked wrong in Flash and in the game, I just gradually adjusted the graphics until the game looked ok. Transparent objects in Flash are never quite what I intended them to be, but there aren't many of them and that's not a big deal.

To make sure everything else was correct, I made a "color test" graphic with a bunch of colors of varying intensities, a hue rotation effect 10, etc., and let the game display it, And make sure it runs correctly in Flash.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

became a matter of comparing colors.

Frame Rate

Original Flash games have a nominal frame rate of 24FPS, but in reality they run at whatever frame rate the Flash Player wants. With Flash, you could ask for 24FPS and get 15FPS, or ask for 30FPS and suddenly get 24FPS, which doesn't seem rigorous at all.

I wanted to remake the game to 60FPS, which meant fiddling with the fact that Hapland was expected to play at around 24FPS when it was created. Flash's animation tools are based on discrete frames rather than continuous time.

I first asked the exporter to double all the frames, exporting two frames for each timeline frame, which directly increased the 24FPS to 48FPS, but still not 60, as needed Animation is still 25% faster. The solution is old-fashioned manual work: go through the game completely and manually add extra frames to the animation that now looks too fast.

At this point we have a pretty good C conversion of the Hapland game that will surely run on modern computers for at least another year or two. But I just couldn't shake the feeling that I should try to provide some extra value, so adding new ones was inevitable. In addition to redrawing a lot of the old graphics and animations, I also made some major changes.

Save in Time

I think Hapland 3 needs to be made less overwhelming. The levels in this game are long and there are a lot of places where you die and have to start over again, maybe that would have been fun in 2006, but we are adults now and we don't have time for that.

Save state is a function that the emulator should have. If you press "Save state", it will record the entire current game by dumping the console's memory to a file. state. Then, if you mess up, press "Loading State" and you'll be back near where you wanted to try again.

Implementing save states in native Flash games is not feasible because Flash does not give programmers access to its entire state. But since I'm using all my own code this time, it's possible.

I have something called a Zone which is just an allocator that allocates all its memory into a fixed size block. All scene nodes are allocated within the current area. To implement save and restore, I only need two areas, the active area and a separate "save state area". To save the state, I memcpy the active area to the saved state area. To load the state, I return memcpy the other way.

Repeat Levels

Hapland is not a particularly long game, even though there are three in total, but we always want to give players a few more hours of gameplay time. So I decided to give each game a "Second Quest" - a modified version of the original level with a slightly different layout and puzzles. Making such a Second Quest takes less effort than making an entirely new game, but still brings some extra value.

Creating Second Quest meant I got to start Flash puzzle game development again for the first time in about 15 years, and honestly, that felt good. The retro Flash UI is great, the buttons have edges, the icons are realistic, and the space is well utilized.

Using the old-era UI makes me feel like an archaeologist discovering some forgotten Roman technology. The lost art of UI design, neat.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

What kind of magic is this?

Although Flash has many bugs, is slow, and lacks some extremely basic functions, I basically don’t hate using it, and of course it is more comfortable to use modern applications. of.

To prevent the second mission from looking too similar to the first, they needed to have new backgrounds and the entire scene was flipped horizontally.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

Hapland 3.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

Hapland 3’s Second Quest.

Music

For the BGM, I used the contents of my own hard drive and made some additional music to create a quick environment for each game Soundtrack. One time while on vacation in Japan I did a field recording session on a mountain top for no apparent reason and it was great to be able to use it for something. I found a musician off the internet to do the title screen music and recorded some guitar chords myself for the end credits, which were drowned in effects, so you can't say I'm a bad guitar learner.

In terms of tools, I use Logic or Live depending on the music. I find Logic is better for recording and Live is better for sound design.

Achievement System

On Steam, players always like to see achievements, which is not easy to handle. The setting of achievements depends on the ideas of the game designer, but in fact No big deal.

Uploading an achievement system to Steam is a pain, you can't just define a list and feed it to their command line tool, you have to painstakingly click Steam Collaboration Partner website slow, confusing PHP framework and then add them one by one.

It seems like if you're a big, important game studio you don't have to put up with this, they give you a bulk upload tool, but I'm obviously not one of them . So I looked at the HTTP calls it built, saved my login cookie, and wrote my own.

After a few revisions, I settled on a modest set of achievements: one for completing each Hapland game, one for each Second Quest, and two for unlocking more Big secret. Any silly, obscure secret that no one can find out is no achievement, you have to be happy with what happens.

In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash

Achievement system under Steamworks UI.

The problem of Notarization

Although I mainly develop games on my Mac, Apple invented "Notarization" during the development process. If Any app you run on a new version of MacOS will make a network request to Apple asking the app's developer whether to pay Apple an annual fee. If a developer doesn't pay the annual fee, MacOS will pop up a dialog box that strongly implies the app is broken and refuses to launch.

Therefore, Windows will be the first, and perhaps only, release platform for the game.

Libraries used

For the software that is ultimately delivered to the end user, we usually want to keep dependencies to a minimum, but using some high-quality software is also necessary of. In addition to OpenGL and standard operating systems, this is the complete list of libraries that the Hapland Trilogy executable ends up linking against:

  • Steam SDK
  • cute_sound
  • stb_vorbis
  • stb_image

Conclusion

Here's the thing, if you get the technology right, people won't even notice what's behind them when they're playing the game, so sometimes it makes you want to say: Hey, look what I did!

The above is the detailed content of In order to save childhood memories, the developer decided to use ancient programming: a high-definition remake of a game in Flash. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:51cto.com. If there is any infringement, please contact admin@php.cn delete