# FlashStarlingEnhance **Repository Path**: und666/fse ## Basic Information - **Project Name**: FlashStarlingEnhance - **Description**: FSE是一个用于Flash AS3的轻量GPU混合渲染框架。旨在使用传统Flash开发方法快速构建能够与Unity体验相近的2D高帧率应用。 - **Primary Language**: ActionScript - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2026-02-14 - **Last Updated**: 2026-02-24 ## Categories & Tags **Categories**: game-dev **Tags**: flash, Starling, as3, GPU ## README
# FSE (Flash Starling Enhance) 混合渲染框架 **FSE是一个用于Flash AS3的轻量GPU混合渲染框架。旨在使用传统Flash开发方法快速构建能够与Unity体验相近的2D高帧率应用。** [**🇨🇳 中文**](README.md) | [**🇬🇧 English**](README_en.md) [![GitHub stars](https://img.shields.io/github/stars/und666/FlashStarlingEnhance?style=social)](https://github.com/und666/FlashStarlingEnhance/stargazers) ![visitors](https://visitor-badge.laobi.icu/badge?page_id=FlashStarlingEnhance&left_color=green&right_color=red) https://github.com/user-attachments/assets/65afb2fe-926f-4096-930b-0b4742105d73 github: https://github.com/und666/FlashStarlingEnhance
## 特点 - 快速开始,与传统AS3项目对接 - 不用写Starling代码即可轻松创建Starling项目 - 适用于个人级别轻量化富位图的高帧率GPU项目快速开发 - 支持传统Flash的窗口自适应策略 - 支持传统发光,模糊,投影滤镜 ## 碎碎念 - 今夕是何年?没错,现在是2026年,距离Flash技术正式退出历史舞台已有五年之久。正是在这样的时间点上,FSE(Flash Starling Enhance)混合渲染框架如一次跨越时空的技术“回响”,悄然诞生。 - 我是一名来自中国的热爱独立游戏创作的大三学生,正在逐步向Unity技术栈转型。今年恰是我接触ActionScript 3.0开发的第十年。这些年间,我始终怀有一种愧疚——虽对这一技术有了相当地了解,却未曾用它创作出什么令人瞩目的作品。 - 前段时间,我沉浸于Starling Wiki和GitHub中大量关于Starling框架的资料,同时也陷入了某种瓶颈。我逐渐意识到,在如今的环境中用Flash做出优秀作品实属不易。但作为一个Aser,我仍想为年轻的自己、也为这段技术旅程画上一个更完整的句点。 - 有时候,我们需要寻找一种方式与自己和解,不是吗?这个框架便是我的答案。 - **好吧,用白话说:这个框架只是我本人以学习为目的开发,欢迎学习讨论。** ## 前言 如果你是一位资深的Flash开发工程师,那么你大概率了解过Adobe AIR SDK的GPU模式\ 我先简单介绍一个这个模式,在AIR for Android/AIR for IOS配置中,GPU设置是可用的\ 选择了这个模式以后确实可以让整个画面看起来更流畅,但帧率依然只能限制在60FPS,以及滤镜等一些功能存在兼容性BUG\ 对于AIR for Desktop,GPU模式竟然直接被隐藏了,据小道消息貌似是Adobe推行了一半但因为很多兼容性BUG所以放弃推行了。\ 并且在传统的Flash IDE的导出设置里是没有这个模式选择的 -------------------可爱的分界线----------------------\ \ 那么作为一个Aser,要快速构建高性能高帧率的Flash应用,我们常常会遇到这些问题。\ **Flash IDEA开发**的传统应用体验不佳{**帧率限制60,帧率不稳定**,**大分辨率场景位图移动直接跳帧**} \ 而转向**Starling 框架** 后,性能虽然上来了,但制作动画却变得异常棘手——**缺乏成熟、可视化的动画解决方案**,这几乎是致命的短板。\ 那有没有一种可能,在制作个人级别的**手机、桌面应用或是游戏**时能够兼顾**GPU的高帧渲染效率**,又能够享受到**Flash IDE现有的动画方案**呢?\ 答案:有的兄弟,有的~ **FSE正式进入舞台(一语双关)** ## 开始指南 **1. 首先,准备好FSE框架** - 一共包含3个文件 ``` fse starling fse.as (注意,这个starling包是经过我调整过的,与官方版本starling不兼容) (fse.as这个文件是FSE.as的快捷入口文件) ``` \ **2. 将FSE框架复制到你的项目中** - Flash IDE工程中 ``` /your_project_path =========================== fse starling fse.as =========================== xxx.fla xxx.swf ``` - IDEA/FB/FD工程中 ``` /your_project_path/src =========================== fse starling fse.as =========================== YourMainClass.as ``` \ **3. 在你的项目中注入Starling框架。** - Flash IDE工程中 ```haxe //在舞台根目录第一帧上 import fse.core.FSE; FSE.init(stage,this); //这两行等效于 fse.init(stage,this); //请确保你给出的第二个参数为舞台容器剪辑根 ``` - IDEA/FB/FD工程中 ```haxe //在你的类文件中 package { import flash.display.Sprite; import flash.events.Event; import fse.core.FSE; public class Main extends Sprite { public function Main() { if (stage) { onAddedToStage(); } else { addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); } } private function onAddedToStage(event:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage); FSE.init(stage,this); //你的代码 } } } ``` ## API - **特性** - FSE接管后所有MovieClip都会默认强制暂停播放 - 播放由FSE内置的动画管理器驱动 - 默认情况下,FSE会自动管理所有MovieClip的播放,但你可以通过以下方式来手动控制播放 - **常用API** ```haxe //=====初始化================================================================ fse.init(stage,Object(root)); //常规初始化 fse.init(stage,Object(root),false); //特殊初始化,不进行GPU渲染,只提帧并接管动画系统 //=====动画控制================================================================ mc.play(); //必须改写成 fse.play(mc); //播放动画 mc.stop(); //必须改写成 fse.stop(mc); //停止播放动画 mc.gotoAndStop(index); //必须改写成 fse.gotoAndStop(mc,index); //跳转到指定帧 mc.gotoAndPlay(index); //必须改写成 fse.gotoAndPlay(mc,index); //跳转到指定帧并播放 var v:Boolean = mc.visible; //必须改写成 var v:Boolean = fse.getVisible(mc); //获取MovieClip对象可见性 mc.visible=false; //必须改写成 fse.visible(mc,false); //改变MovieClip对象可见性 //=====循环控制================================================================ //高频循环 (在240帧的情况下每秒刷新240次) //开启 mc.addEventListener(Event.ENTER_FRAME,Update); //关闭 mc.removeEventListener(Event.ENTER_FRAME,Update); //低频循环 (无论在什么情况下都以60次每秒运行[频率在Config.as中可调]) //开启 fse.addEventListener(mc,FSE_Event.FIX_ENTER_FRAME,Update); fse.addLoop(mc,Update); fse.loop(Update); //3句等效 //关闭 fse.removeEventListener(mc,FSE_Event.FIX_ENTER_FRAME,Update); fse.removeLoop(mc,Update); fse.stopLoop(Update); //3句等效 ``` - **层级 “三明治” 问题** ``` 在 Flash Player / AIR 的运行时架构中: 顶层 (Top): 原生 Display List (CPU)。 底层 (Bottom): Stage3D (GPU/Starling)。 视频层: StageVideo (如果有的话,通常在最最底层)。 所以FSE框架的实现在此基础上将Stage3D层分成了3层 每一层级都是一个starling.display.Sprite容器 在舞台的第1层,也就是最底层,会有一层底层用户操作层,使用fse.starlingRootBack进行访问 在舞台的第2层,就是最核心的映射层,fse框架会将你的舞台结构映射到此层 在舞台的第3层,会有一层底层用户操作层,使用fse.starlingRoot进行访问 在CPU渲染层,也就是最顶层,因为CPU层覆盖在GPU渲染层之上,一些存在兼容性问题的元件可以使用cpu层来进行渲染 ``` - **高级API** ```haxe fse.cpu(mc:MovieClip); fse.ban(mc:MovieClip); //与上一行等效 fse.isIgnore(mc:MovieClip):Boolean //获取mc对象的特例状态 //使用cpu渲染 /* 为了解决某些兼容性问题, 被用户标记的剪辑将不会在FSE的Starling舞台上被渲染,而是直接在传统舞台上渲染 注意,这么做会导致mc始终显示在GPU层的上方 */ fse.setKeyRole(mc:MovieClip); //将当前的影片剪辑设置为关键角色 fse.getKeyRole():String //获取设置过的关键角色 /* fse框架允许用户注册一个关键角色剪辑,一旦这个剪辑产生变化Starling马上渲染下一帧 这个方法一般应用与哪些与玩家输入操作相关的剪辑,比如鼠标跟随,键盘控制移动等 用于实现用户一输入就马上相应的效果 */ fse.setNodeCached(mc:MovieClip,v:Boolean); //设置缓存特例 /* 设置特例后,这个对象的以及当前容器节点的所有肉子节点(Bitmap、Shape)都不参与缓存系统 */ fse.gpuClear(); //强制清楚缓存面板上的所有缓存 //=====与Starling混用============================================================ fse.starlingRoot:starling.display.Sprite //为用户提供的Starling舞台根容器顶层(推荐用于添加粒子效果以及特效) fse.starlingRootBack:starling.display.Sprite //为用户提供的Starling舞台根容器底层(推荐用于添加背景等底层内容元件) ``` ## Config.as ``` package fse.conf { import flash.system.Capabilities; import flash.text.TextField; import flash.text.TextFieldType; import flash.display.DisplayObject; import flash.display.SimpleButton; import starling.textures.TextureSmoothing; /** * FSE 全局配置类 * 用于统一管理逻辑帧率、渲染精度等静态参数 */ public class Config { // ------------------------------------------------- // 舞台显示相关(Stage Display) // ------------------------------------------------- public static const DEVICE_W:uint = Capabilities.screenResolutionX; //设备窗口大小 public static const DEVICE_H:uint = Capabilities.screenResolutionY; public static const FULL_SCREEN:Boolean = false; public static var AUTO_ADAPT:String = "AUTO"; //舞台自适应方案 // ***可选项 //"FULL" 填满视窗适配,无论如何填满视窗(不保证舞台比例) //"SYN_HEIGHT" 舞台画面紧贴屏幕上下两边,并保证舞台比例 //"SYN_WIDTH"舞台画面紧贴屏幕左右两边,并保证舞台比例 // "AUTO" 缩放边界自动决定,始终保持舞台比例 // "NONE" 框架不干预适配行为,但依然会控制渲染窗口和舞台高宽同步(我也不知道这个选项有什么用) //舞台对齐,除非有特殊开发需要不然一般不修改此项设置 //特殊说明,如果舞台自适应方案与此项冲突,则此项设置无效(比如你的舞台始终紧贴左右两边,那你又设置了左对齐,那就失去意义了) public static var ALIGN_X:String = "CENTER"; // ***可选项 //"CENTER" 锚定屏幕中央位置,这是最推荐的设置 //"LEFT" 紧贴屏幕左侧 //"RIGHT" 紧贴屏幕右侧 public static var ALIGN_Y:String = "CENTER"; // ***可选项 //"CENTER" 锚定屏幕中央位置,这是最推荐的设置 //"TOP" 紧贴屏幕上侧 //"BOTTOM" 紧贴屏幕下侧 public static const BG_COLOR:uint=0x211F20; //背景颜色 public static const EXT_FPS:uint=400; //Starling 最高帧限(通常设置为超过大多数屏幕刷新率) // ------------------------------------------------- // 画面配置相关(Quality) // ------------------------------------------------- public static var TEXTURE_SMOOTHING:String = TextureSmoothing.BILINEAR; //纹理平滑设置 // ***可选项 //TextureSmoothing.NONE (不平滑/最近邻插值) ###如果你的游戏的像素风格游戏推荐使用这个选项 //TextureSmoothing.BILINEAR (双线性过滤 - 默认值) //TextureSmoothing.TRILINEAR (三线性过滤) // ------------------------------------------------- // 缓存策略相关(Cache) // ------------------------------------------------- public static const CACHE_THRESHOLD:uint = 3; //持久化阈值:如果场景同时出现超过这个数的同样纹理,那么这个纹理将被持久化存入缓存 public static const WATCHER_COLD_TIME:uint = 20; // ------------------------------------------------- // 调试相关(Debug) // ------------------------------------------------- public static const TRACE_CORE:Boolean = false; //无关紧要的一些启动信息 public static const TRACE_DEBUG:Boolean = true; //Starling GPU性能信息 public static const TRACE_WATCHER:Boolean = false; //节点数监控调试信息 public static const TRACE_NODE:Boolean = false; //单个节点行为调试信息 public static const TRACE_CACHE:Boolean = false; //缓存器信息 // ------------------------------------------------ // 游戏配置 (Game) // ------------------------------------------------ public static const STOP_ALL:Boolean = true; //在接管后默认暂停所有影片剪辑 private static var _logicFrameRate:int = 60; //逻辑帧率 private static var _logicTimestep:Number = 1000.0 / _logicFrameRate; private static var _case_render:Array = [isInputText,isSimpleButton]; //经过这些断言判断为真的话不用starling渲染 //输入文本断言 private static function isInputText(obj:DisplayObject):Boolean { if (obj is TextField) { var textField:TextField = obj as TextField; // TextFieldType.INPUT 是静态常量,值为 "input" return textField.type == TextFieldType.INPUT; } return false; } //按钮断言 private static function isSimpleButton(obj:*):Boolean{ // 检查是否为flash.display.SimpleButton实例 return obj is SimpleButton; } // ------------------------------------------------ // 公共参数 // ------------------------------------------------ /** * 最大的追赶时间 (毫秒) * 如果设备极度卡顿,每一帧最多只处理这么长时间的逻辑,防止死循环 * 默认 200ms (即最差情况每帧追赶约 12 个逻辑帧) */ public static var maxAccumulator:Number = 200; /** * 纹理缩放系数 (未来用于支持 Retina/高清屏) * 1 = 原倍, 2 = 2倍高清 */ public static var contentScaleFactor:Number = 1.0; // ------------------------------------------------ // Getter / Setter // ------------------------------------------------ public static function get case_render():Array{ return _case_render; } /** * 目标逻辑帧率 (默认为 60) * 修改此值会自动更新 timestep */ public static function get logicFrameRate():int { return _logicFrameRate; } public static function set logicFrameRate(value:int):void { if (value < 1) value = 1; // 安全限制 if (_logicFrameRate == value) return; _logicFrameRate = value; _logicTimestep = 1000.0 / _logicFrameRate; trace("[FSE_Config] Logic FPS set to: " + _logicFrameRate + " (Timestep: " + _logicTimestep.toFixed(2) + "ms)"); } /** * [只读] 每一逻辑帧的时间步长 (毫秒) * 例如 60fps = 16.666ms */ public static function get logicTimestep():Number { return _logicTimestep; } } } ``` ## 注意事项 - 如果你的项目中出现兼容性问题,请按需求添加渲染特例 ## 基本原理 - 我们都知道,使用starling的flash项目,传统的cpu渲染会在starling的上层,并且一旦开始GPU渲染,flash项目帧限会从60fps到与刷新率同步例如,在400hz的电脑上可以跑到400fps - 那么我们做一个游戏混合渲染方案 - 像**简单静态的内容**,比如高清背景图片等,或者简单移动的场景图片,我打算使用**starling进行渲染**。 - 如果**复杂的动态内容**,比如动画中右嵌套的动画,比如枪械开火动画绑定在人物手部动画上,那我们将使用传统的Flash cpu的方式渲染,但我们会将cpu渲染层所有的传统剪辑强制隐藏(visible=false),只有当flash.display.movieclip更新(其中某个影片剪辑或者子剪辑的帧发生变化)后则更新draw出位图数据作为显示纹理,再由starling进行渲染。 - 那么到了这里肯定会有小朋友要问了 - “Starling框架不使用图集并高频率上传纹理不会导致性能爆炸吗?” - “怎么说好呢,我们这个框架本来就不是用来开发专业应用的,对于个人级别的项目,如果您的游戏中能保证动态更新部分较少,并且能控制纹理大小(控制在几百像素以内),那么我认为不会影响性能 况且,我制作的框架具有一些性能优化算法,能解决一部分你的疑虑” - 这么说来的话,我实际做的也就是这几件事情 - **逻辑驱动控制** - 比如一些预期的动画只能由60fps播放 - 一些逻辑代码只能以60fps执行 - 使用框架后的一切逻辑和动画都要由新的60帧逻辑驱动 - 不能因为主观设备(屏幕刷新率)的不同导致程序运行效果/动画效果有出入 - **舞台监控** - 对Flash传统Stage生成实时管理的场景结构树 - 场景树上的每一个节点对应着一个flash.display.DisplayObject对象,并存储他们的所有状态(例如currentFrame,visible,alpha,x,y,scaleX,scaleY,rotation等),最关键的,**锚点信息** - 对场景树进行遍历检查,并比对每个子剪辑是否发生变化,**变化分为两种** - **1. 形变(影片剪辑的帧数,子剪辑集合改变,内容改变等)** - **2. 属性值改变(transfer属性改变,同步数值即可)** - **Starling渲染** - 对于flash传统stage生成实时管理的场景树,Starling舞台要实时同步场景树上影片剪辑发生的变化,同步到Starling舞台上 - 在同一父剪辑内的所有starling.display.DisplayObject对象的图层顺序可以按照传统剪辑同步过来的childrenIndex值按照大小进行显示图层排序 - **纹理缓存管理** - 为了减少DrawCalls,本框架使用MaxRects算法进行二维空间uv装箱 - 对场景里的每帧/每个DisplayObject对象,都生成纹理相应的唯一哈希字符串 - **输入事件转发** - 由于CPU层的所有内容被隐藏,这意味着注册在他们身上的鼠标点击事件都将失效 - 我设计的输入转发器可以转发鼠标输入事件到对应的flash.display.DisplayObject身上 ## 框架 - 包类结构为 - **fse** - **core包** - **FSE** (核心静态类,接口封装) - **FSE_Kernel** (核心静态类的真身) - **FSE_Manager** (脏活累活都他干,此类需要实例化,使用单例模式) - **FSE_Input** (输入事件转发器) - **display包** - **Watcher** (场景树监控) - **Node** (存储单个影片剪辑详细信息,并包含Node子集) - **Scanner** (用于扫描传统舞台上的场景树) - **Controller** (动画剪辑逻辑控制器) - **StatusSaver** (状态保存器) - **events包** - **FSE_Event** (这个其实作用不大,就是写一些事件常量,比如FIX_ENTER_FRAME) - **conf包** - **Config** (配置类) - **starling包** - **StarlingMain** (初始化Starling舞台) - **StarlingVO** (Starling影片剪辑根) - **StarlingManager** (负责同步并管理) - **cache包** - **AtlasPage** (图集分页管理器) - **CacheManager** (纹理缓存管理器) - **Cache** (纹理缓存实体类) - **utils包** - **Hash** (BitmapData快速哈希) - **MD5** (散列加密) ## 联系作者 - 作者: undefined (一位有理想的独立游戏制作人) - 微信: hbx098hbx123 (欢迎交朋友) - 邮箱: 2199182141@qq.com - 你可以添加作者微信,反馈BUG或者建议 --- > 祝大家2026年马到成功,**代码会老,但创造的心永远年轻。**\ > 如果这个框架,能让你在未来的某天,更轻盈、更自由地做出心中所想,那便是它全部的意义。\ > 感谢 AS3,感谢仍在这里的你。\ > **2026.1.29**\ > 于一个即将春暖花开的日子前