2년여 전, Adobe는 2020년 12월 31일에 Flash 지원을 종료하고 시대의 종말을 알리는 주목할만한 발표를 했습니다.
2년 후 Adobe는 이미 공식 웹사이트에서 Flash Player 초기 버전의 모든 아카이브를 삭제하고 Flash 기반 콘텐츠 실행을 차단했습니다. 또한 Microsoft는 Adobe Flash Player에 대한 지원을 종료하고 모든 Microsoft 브라우저에서 실행을 금지했습니다. Adobe Flash Player 구성 요소는 2021년 7월 Windows 업데이트를 통해 영구적으로 제거되었습니다.
플래시가 진열대에서 벗어난 후에도, 세계의 한구석에서 이 "오래된 동지"는 여전히 남아있는 에너지를 발휘하고 있었습니다.
Hapland는 2005년에 출시된 플래시 퍼즐 게임으로, 많은 사람들에게 어린 시절의 추억이기도 합니다. 게임에서 플레이어는 몬스터에게 먹히거나 지뢰에 폭파되지 않고 이 세계의 사람들의 도움을 받아 레벨을 열 수 있는 방법을 찾아야 합니다.
이 게임의 그래픽은 Flash로 그려지고, 코드도 Flash로 작성되며, 모든 애니메이션은 Flash 타임라인에서 완성됩니다. 이는 이렇게 이해될 수 있습니다. 이 게임에는 "플래시"가 포함되어 있습니다.
게임 개발 업계의 일원인 Robin Allen은 사람들이 특히 Hapland 게임을 좋아하는 것 같다는 사실을 알아차리고 Steam 버전의 Flash 기반 게임에 더 나은 그래픽 그리기, 프레임 속도를 60FPS로 높이고 "비밀" 등을 추가하세요.
이때 어떻게 해야 할까요? 저자는 실험 과정을 자세히 설명합니다.
실패한 시도 1:
처음 시도한 것은 Flash에서 게임을 실행 파일로 내보내는 것이었지만 2005년과 성능이 동일했기 때문에 실패했습니다. . 나는 현대적인 프레임 속도로 실행되는 무언가를 만들고 싶습니다. Flash Player를 제거하고 싶습니다.
실패한 시도 2:
둘째, Adobe AIR(Flash 데스크톱 런타임) 및 Starling(GPU에서 Flash 장면을 그리기 위한 라이브러리)을 조작하는 데 너무 많은 시간을 보냈습니다.
결국 저는 이것을 포기했습니다. 부분적으로는 AIR에 문제가 너무 많고 형편없었기 때문이기도 하지만 결국에는 이상한 Adobe 결과를 낳고 싶지 않았기 때문이기도 했습니다. 내가 원하는 것을 할 수 있는 나만의 것이 있다. 예를 들어, Linux로 이동하고 싶다면 어떻게 해야 합니까?
앞으로 나아갈 길은 분명했습니다. 나만의 플래시 플레이어를 만들어야 했습니다.
Hapland의 작동 방식은 다음과 같습니다. 여기에는 스프라이트 트리가 있으며, Flash에서는 애니메이션 스프라이트가 재생 화살표가 거기에 도달할 때 실행되는 특정 프레임에 코드를 첨부할 수 있습니다. Hapland는 이 접근 방식을 자주 사용합니다. 게임 캐릭터의 이동 경로는 모두 매우 긴 타임라인 애니메이션이며, 캐릭터는 문을 닫은 후 열거나 폭발하기 전에 지뢰밭에 도달하면 트리거하는 등 프레임별 동작을 수행하는 경우가 많습니다.
타임라인의 작은 "a"는 프레임 동작입니다.
다행히 .fla 파일은 XML일 뿐입니다. 그냥 파싱하고, 관련 데이터를 간단한 사용자 정의 형식으로 내보내고, 이를 읽고, 장면을 그리고, 입력을 처리하고, 애니메이션을 실행하는 플레이어를 작성하면 되었습니다.
Hapland는 여전히 Flash 프로젝트이며 Flash Editor에서 작성 및 유지 관리되며 Flash Player만 대체됩니다.
Flash는 래스터 그래픽을 지원하지만 실제로는 벡터 그래픽용으로 설계되었습니다. 이것이 바로 전화 접속 연결을 통해서도 Flash 동영상이 빠르게 로드되는 이유입니다.
Hapland의 모든 그래픽은 벡터 그래픽입니다. GPU는 벡터 그래픽 그리기를 별로 좋아하지 않지만 질감이 있는 삼각형을 대량으로 배치하는 것을 좋아합니다. 따라서 이 벡터를 래스터화해야 합니다.
오프라인에서 래스터화하고 래스터 파일을 게임에 패키징하기로 결정했습니다. 게임이 실행되는 동안 이를 래스터화하여 이 작은 실행 파일로 만드는 것은 재미있을 것입니다. 그러나 저는 그렇게 추가로 움직이는 부분을 갖고 싶지 않습니다. 저는 개발 컴퓨터에서 가능한 한 많은 코드를 실행하여 계속해서 지켜볼 수 있기를 바랍니다.
Flash는 벡터 그래픽을 XML 형식으로 저장합니다. XML이 그래픽 데이터에 적합하지 않다고 말할 수도 있지만 귀하는 Macromedia의 제품 관리자가 아닙니다. 이것 좀 보세요:
.fla 파일에 표시된 벡터 데이터.
불평하는 게 아니라 일이 더 쉬워지거든요.
사양에 액세스할 수 없어도 래스터화하는 것은 문제가 되지 않았습니다. 벡터 그래픽의 베지어 곡선 모델은 PostScript 이후 널리 사용되었습니다. 이러한 모든 API는 동일한 방식으로 작동합니다. 몇 번의 시행착오 끝에 저는 이러한 모양 정의를 구문 분석하고 Mac의 CoreGraphics 라이브러리를 사용하여 PNG로 렌더링하는 프로그램을 작성했습니다.
CoreGraphics는 의심스러운 선택입니다. Mac에서 작업하고 종속성이 많기 때문에 선택했습니다. 하지만 효과가 있었기 때문에 저는 항상 Mac에서, 심지어 Windows 버전에서도 그래픽을 래스터화해야 했습니다. 이 작업을 다시 수행한다면 아마도 크로스 플랫폼 라이브러리를 선택할 것입니다.
이 PNG를 렌더링한 후 수출업체에서 이를 모아 지도책으로 만들까요? 아니요, 모든 것을 높이별로 정렬한 다음 문서의 텍스트처럼 한 줄씩 정렬합니다. 최적과는 거리가 멀지만 충분히 좋습니다.
단순화를 위해 아틀라스는 2048×2048 픽셀이며, 이는 OpenGL 3.2 구현이 지원해야 하는 최소 텍스처 크기입니다.
Hapland 3의 일러스트 세트입니다.
모양 래스터화는 매우 느리므로 빌드 시간을 합리적으로 유지하려면 변경되지 않은 항목 렌더링을 건너뛰어야 합니다. Flash에서 사용하는 압축된 XML 형식에는 각 파일에 대해 마지막으로 수정된 필드가 있지만 Flash는 이를 올바르게 사용하지 않는 것 같으므로 이에 의존할 수 없습니다.
대신에 각 모양의 XML을 해시하고 변경된 경우에만 다시 빌드합니다. Flash는 때때로 변경되지 않은 객체에서 XML 태그를 재배열하는 것을 좋아하기 때문에 이 방법도 실패하지만, 이것으로도 충분합니다.
내보내기는 애니메이션 데이터를 사용자 정의 바이너리 형식으로 씁니다. 타임라인을 프레임별로 진행하고 각 프레임의 모든 변경 사항을 기록합니다.
바이너리 파일에 직접 작성하는 대신 여기에 어셈블리 목록을 작성하는 것을 생각했는데 마음에 듭니다. CPU 명령은 없고 데이터만 있으므로 16진수 편집기에서 바이트를 찾아보는 대신 어셈블리 파일을 보고 생성된 내용을 확인할 수 있으므로 디버깅이 더 쉬워집니다.
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
어느 것을 디버깅하시겠습니까?
어셈블러를 사용하지 않고 내보내기 프로그램에서 다른 파일에 별도의 텍스트 목록을 작성하면서 한 파일에 바이트를 쓰도록 할 수도 있었지만 그렇게 하지 않았습니다.
1) 어셈블러가 이미 존재합니다.
2) 디버깅할 필요가 없습니다.3) 태그를 지원합니다.
导出器的其余部分大多不够有趣;它只是 walk the tree 并将变换矩阵、颜色效果等事物,然后继续游戏程序本身。我选择用 C++ 编写这个,因为我已经知道它,并且新事物让我害怕。 Hapland 非常适合场景图。这是 Flash 使用的模型,Hapland 就是围绕它设计的,因此尝试使用不同的模型是没有意义的。 我将场景存储在内存中,作为一棵节点树,每个节点都有一个变换,可以自行绘制并接受鼠标点击。每个具有自己行为的游戏对象都是其自己类的实例,派生自 Node.js。「面向对象」目前在游戏开发圈子里并不流行,但我使用的是 Flash,所以显然不关心这个问题。 Hapland 使用的 Flash 功能,如颜色变换和遮罩,都是存在的。不过我没有像 Flash 那样实现任意遮罩,只是实现了矩形剪辑并编辑了我所有的图形,所以所有的遮罩都是矩形。 几乎所有的 Hapland 逻辑都包含在附加到时间轴帧的 ActionScript 中。要如何导出所有这些东西?我可不想在我的游戏中包含 ActionScript 解释器。 一个简单的帧动作。 最后,我们使用了一些技巧,我的导出器从每一帧读取 ActionScript 并应用大量正则表达式以尝试将其转换为 C++。例如,crate.lid.play () 可能会变成 crate ()→lid ()→play ();。这两种语言在句法上非常相似,这对于许多更简单的框架动作来说效果很好,但它仍然留下了相当多的错误代码,除了手动重写所有剩余的框架动作之外别无他法。 对于 C++ 中的所有框架脚本,它们在构建时被提取并成为每个符号的 Node 子类上的方法。还会生成一个调度方法以在正确的时间调用,看起来像这样: 需要指出的最后一件事是脚本系统最终是某种静态类型的,这有点难受。游戏输出的最终游戏对象如下所示: 因此,即使一切仍然是大量的自动字符串名称查找,类型安全的单板会阻止你在错误的对象上调用错误的函数,从而使你免于在动态语言中遇到的那类烦人的 bug。 HD 重置版游戏都会遇到画面拉伸的问题,最初的 Flash 游戏很多是页游,甚至没有全屏运行的能力,所以它们只是使用设计者喜欢的宽高比,大多是 3:2 左右。 如今最常见的纵横比似乎是 16:9,16:10 在笔记本电脑上也很流行。我希望游戏在其中任何一个方面看起来都不错,没有任何黑条或拉伸。要做到这一点的唯一方法是从原件上切掉一些部分,或者在上面添加一些部分。 所以,我为游戏画面画了两个矩形,一个比例为 16:9,另一个比例为 16:10。然后游戏根据屏幕的宽高比在它们之间进行插值,并使用插值矩形作为视图边界。只要所有重要的游戏元素都在这些矩形的交叉点内,并且它们的公共边界矩形不超出场景边缘,就可以很好地工作。 Hapland 2 的 16:10 和 16:9 框,与原来的 3:2 不同。 몇 가지 테스트를 거친 후 Flash가 선형 공간이 아닌 지각 공간에서 알파 블렌딩 및 색상 변환을 수행한다는 사실을 발견했습니다. 이것은 수학적으로 모호하지만, 반면에 우리는 많은 그래프 프로그램이 이와 같이 작동한다는 것을 알아야 합니다. 비록 이것이 수학자에게는 일종의 문제이지만 소비자 도구가 사람들이 기대하는 방식으로 작동하기를 원합니다. 그러나 근본적으로 이것은 잘못된 것입니다! 앤티앨리어싱과 같은 문제가 발생할 수 있습니다. 벡터 그래픽을 래스터화하고 앤티앨리어싱 출력이 필요한 경우 래스터라이저는 소위 "커버리지 값"이라고 하는 알파 값을 출력합니다. 즉, 주어진 픽셀이 벡터 모양으로 반쯤 가려지면 해당 픽셀은 알파 = 0.5로 출력됩니다. 그러나 Flash에서 알파 값이 0.5인 경우 이는 지각적으로 전경색과 배경색의 중간에 있다는 의미입니다. 이것은 전혀 같은 것이 아닙니다! 불투명한 검정색 픽셀 위에 그려진 반쯤 덮인 흰색 픽셀은 50% 회색으로 인식되어서는 안 됩니다. 그것은 빛이 작동하는 방식이 아니며 벡터 래스터화가 작동하는 방식도 아닙니다. 래스터라이저는 배경색을 모르면 "이 픽셀이 배경색과 전경색 사이의 xx%를 감지해야 합니다"라고 말할 수 없습니다. 지각(sRGB) 공간에서 블렌딩이 완료되었습니다. 상단: 투명한 흰색 바탕에 검정색; 중간: 흰색 바탕에 투명한 검정색; 선형(물리적으로 정확한) 공간에서 동일한 회색 혼합이 수행됩니다. 50% 적용 범위는 50% 회색과 다르게 보입니다. 따라서 앤티앨리어싱된 래스터화된 모양은 알파의 한 정의를 사용하는 반면 플래시에서 내보낸 알파 투명도, 그라디언트 및 색상 변환은 다른 정의를 사용합니다. 하지만 렌더링 파이프라인에는 알파 채널이 하나만 있습니다. 그렇다면 렌더러는 알파 값을 어떻게 해석해야 할까요? 이를 지각적 혼합 요소로 해석하면 반투명 개체는 올바르게 보이지만 앤티앨리어싱된 모든 가장자리는 잘못 보입니다. 이를 적용 범위 값으로 해석하면 그 반대도 마찬가지입니다. 뭔가 항상 잘못된 것 같습니다! 여기에는 두 가지 심각한 해결책이 있다고 생각합니다. 1) 두 개의 알파 채널을 설정합니다. 하나는 오버레이용이고 다른 하나는 지각 블렌딩용입니다. 2) AA 없이 모든 모양을 래스터화하고 모든 것을 매우 큰 프레임 버퍼에 그린 다음 필터링합니다. 축소하려면 축소하세요. 이러한 아이디어 중 어느 것도 구현되지 않았음을 인정해야 합니다. 이러한 반투명한 것들은 Flash와 게임에서 이상해 보였고, 게임이 괜찮아 보일 때까지 그래픽을 점진적으로 조정했습니다. Flash의 투명 객체는 제가 의도한 것과 전혀 다르지만 그 수가 많지도 않고 별 문제도 아닙니다. 다른 모든 것이 올바른지 확인하기 위해 다양한 강도의 다양한 색상, 색조 회전 효과 10 등을 사용하여 "색상 테스트" 그래픽을 만들어 게임에 표시하고 올바르게 실행되는지 확인했습니다. 플래시에서. 색 비교의 문제가 됩니다. 원래 Flash 게임의 명목상 프레임 속도는 24FPS이지만 실제로는 Flash Player가 원하는 프레임 속도로 실행됩니다. Flash를 사용하면 24FPS를 요청했는데 15FPS를 얻을 수도 있고, 30FPS를 요청했는데 갑자기 24FPS를 얻을 수도 있는데, 이는 전혀 엄격해 보이지 않습니다. 저는 게임을 60FPS로 리메이크하고 싶었는데, 이는 Hapland가 제작 당시 약 24FPS에서 플레이할 것으로 예상되었다는 사실을 조작하는 것을 의미했습니다. Flash의 애니메이션 도구는 연속적인 시간이 아닌 개별 프레임을 기반으로 합니다. 먼저 내보내기자에게 모든 프레임을 두 배로 늘려 각 타임라인 프레임에 대해 두 개의 프레임을 내보내도록 요청했습니다. 이로 인해 24FPS가 48FPS로 직접 향상되었지만 여전히 60은 아니었고 필요한 애니메이션은 여전히 25% 더 빨랐습니다. 해결책은 구식 수동 작업입니다. 게임을 완전히 진행하고 지금은 너무 빠르게 보이는 애니메이션에 추가 프레임을 수동으로 추가합니다. 이 시점에서 우리는 적어도 1~2년 동안 최신 컴퓨터에서 확실히 실행될 Hapland 게임의 꽤 좋은 C++ 변환을 얻었습니다. 하지만 뭔가 추가적인 가치를 제공하려고 노력해야 한다는 느낌을 지울 수 없었기 때문에 새로운 가치를 추가하는 것은 불가피했습니다. 기존 그래픽과 애니메이션을 다시 그리는 것 외에도 몇 가지 주요 변경 사항을 적용했습니다. 저는 Hapland 3를 덜 압도적으로 만들어야 한다고 생각합니다. 이 게임의 레벨은 길고 죽어서 다시 시작해야 하는 곳이 많습니다. 2006년에는 재미있었을 수도 있지만 이제 우리는 성인이고 그럴 시간이 없습니다. 상태 저장은 에뮬레이터에 있어야 하는 기능입니다. "상태 저장"을 누르면 콘솔의 메모리를 파일에 덤프하여 현재 게임의 전체 상태를 기록합니다. 그런 다음, 엉망이 된 경우 "로드 중 상태"를 누르면 다시 시도하고 싶은 위치로 돌아올 것입니다. Flash는 프로그래머에게 전체 상태에 대한 액세스 권한을 제공하지 않기 때문에 원본 Flash 게임에서 저장 상태를 구현하는 것은 불가능합니다. 하지만 이번에는 제가 직접 만든 코드를 모두 사용하고 있기 때문에 가능합니다. 모든 메모리를 고정된 크기 블록에 할당하는 할당자인 Zone이라는 것이 있습니다. 모든 장면 노드는 현재 영역 내에 할당됩니다. 저장 및 복원을 구현하려면 활성 영역과 별도의 "상태 저장 영역"이라는 두 영역만 필요합니다. 상태를 저장하기 위해 활성 영역을 저장된 상태 영역으로 memcpy합니다. 상태를 로드하기 위해 memcpy를 다른 방법으로 반환합니다. Hapland의 게임 시간은 총 3개가 있지만 특별히 길지는 않지만, 우리는 항상 플레이어에게 게임 시간을 몇 시간 더 주고 싶습니다. 그래서 저는 각 게임에 "두 번째 퀘스트"를 주기로 결정했습니다. 원래 레벨을 약간 다른 레이아웃과 퍼즐로 수정한 버전입니다. 이러한 두 번째 퀘스트를 만드는 것은 완전히 새로운 게임을 만드는 것보다 노력이 덜 들지만 여전히 추가적인 가치를 제공합니다. 세컨드 퀘스트를 만들게 되면서 약 15년 만에 플래시 퍼즐 게임 개발을 다시 시작하게 되었는데, 솔직히 기분이 좋았습니다. 레트로한 플래시 UI가 훌륭하고, 버튼에 모서리가 있고, 아이콘이 사실적이며, 공간 활용도가 좋습니다. 옛날 UI를 사용하면 마치 잊혀진 로마 기술을 발견한 고고학자가 된 듯한 느낌이 듭니다. UI 디자인의 잃어버린 예술, 깔끔합니다. 이게 무슨 마술인가요? Flash는 버그도 많고, 느리고, 극히 기본적인 기능도 부족하지만 기본적으로 사용하는 것을 싫어하지 않으며, 물론 최신 애플리케이션을 사용하는 것이 더 편합니다. 두 번째 미션이 첫 번째 미션과 너무 비슷해 보이는 것을 방지하기 위해 새로운 배경이 필요했고 전체 장면이 가로로 뒤집혔습니다. 햅랜드 3. Hapland 3의 두 번째 퀘스트. BGM의 경우 내 하드 드라이브의 콘텐츠를 사용하고 추가 음악을 만들어 각 게임에 대한 빠른 주변 사운드트랙을 만들었습니다. 한번은 일본 여행 중 아무 이유 없이 산 정상에서 현장 녹음을 했는데, 뭔가 쓸 수 있어서 좋았어요. 나는 타이틀 화면 음악을 만들기 위해 인터넷에서 음악가를 찾았고 엔딩 크레딧을 위해 직접 기타 코드를 녹음했는데 효과에 빠져 있었기 때문에 내가 나쁜 기타 학습자라고 말할 수는 없습니다. 도구는 음악에 따라 로직이나 라이브를 사용해요. 녹음에는 Logic이 더 좋고 사운드 디자인에는 Live가 더 좋습니다. Steam에서 플레이어는 항상 업적을 보고 싶어하는데, 이는 게임 디자이너의 아이디어에 따라 업적 설정이 달라지지만 실제로는 큰 문제가 아닙니다. Steam에 업적 시스템을 업로드하는 것은 고통스럽습니다. 목록을 정의하고 명령줄 도구에 제공할 수는 없지만 대신 느리고 혼란스러운 Steam 파트너 사이트 PHP 프레임워크를 헤쳐나가서 하나를 추가해야 합니다. 하나씩. 크고 중요한 게임 스튜디오라면 이를 참을 필요가 없고 대량 업로드 도구도 제공하는 것처럼 보이지만 저는 분명히 그들 중 하나가 아닙니다. 그래서 저는 그것이 구축한 HTTP 호출을 살펴보고, 로그인 쿠키를 저장하고, 직접 작성했습니다. 몇 번의 수정 끝에 저는 적당한 수준의 업적 세트를 결정했습니다. 각 Hapland 게임 완료를 위한 하나, 두 번째 퀘스트마다 하나, 그리고 더 큰 비밀을 풀기 위한 두 개입니다. 누구도 알아낼 수 없는 어리 석고 모호한 비밀은 성취가 아니며, 일어나는 일에 만족해야 합니다. Steamworks UI의 업적 시스템. 주로 Mac에서 게임을 개발하지만, 개발 과정에서 Apple은 "공증"을 개발했습니다. 새 버전의 MacOS에서 어떤 응용 프로그램을 실행하면 Apple에 네트워크 요청을 합니다. 앱 개발자가 Apple에 연회비를 지불하는지 묻습니다. 개발자가 연회비를 지불하지 않으면 MacOS는 앱이 손상되었으며 실행을 거부함을 강력히 암시하는 대화 상자를 표시합니다. 따라서 Windows는 이 게임의 최초이자 아마도 유일한 출시 플랫폼이 될 것입니다. 최종 사용자에게 전달되는 소프트웨어의 경우 일반적으로 종속성을 최소한으로 유지하고 싶지만 일부 고품질 소프트웨어를 사용하는 것도 필요합니다. OpenGL 및 표준 운영 체제 외에도 Hapland Trilogy 실행 파일이 연결되는 전체 라이브러리 목록은 다음과 같습니다. 场景图
框架脚本
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() { ... }
};
纵横比
색 공간 문제
Framerate
시간 절약
반복 레벨
Music
업적 시스템
공증의 문제
사용된 라이브러리
결론
위 내용은 어린 시절의 추억을 보존하기 위해 개발자는 고대 프로그래밍을 사용하기로 결정했습니다. 즉, Flash로 게임을 고화질로 리메이크하는 것입니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!