优化2D Flash游戏的过渡/移动平滑度

时间:2009-08-16 17:52:04

标签: actionscript-3 flash transition smoothing

更新6

Fenomenas建议我尽可能简单地重新创造一切。我怀疑这会有什么不同,因为算法保持不变,而且性能似乎不是问题。无论如何,这是我在这里得到的唯一建议:

  1. 30 FPS:http://www.feedpostal.com/test/simple/30/SimpleMovement.html
  2. 40 FPS:http://www.feedpostal.com/test/simple/40/SimpleMovement.html
  3. 60 FPS:http://www.feedpostal.com/test/simple/60/SimpleMovement.html
  4. 100 FPS:http://www.feedpostal.com/test/simple/100/SimpleMovement.html
  5. 代码:

    package {
        import flash.display.Sprite;
        import flash.events.Event;
        import flash.events.KeyboardEvent;
        import flash.utils.getTimer;
    
        [SWF(width="800", height="600", frameRate="40", backgroundColor="#000000")]
    
        public class SimpleMovement extends Sprite
        {
            private static const TURNING_SPEED:uint = 180;
            private static const MOVEMENT_SPEED:uint = 400;
            private static const RADIAN_DIVIDE:Number = Math.PI/180;
            private var playerObject:Sprite;
            private var shipContainer:Sprite;
            private var moving:Boolean = false;
            private var turningMode:uint = 0;
            private var movementTimestamp:Number = getTimer();
            private var turningTimestamp:Number = movementTimestamp;
    
            public function SimpleMovement()
            {
                //step 1: create player object
                playerObject = new Sprite();
                playerObject.graphics.lineStyle(1, 0x000000);
                playerObject.graphics.beginFill(0x6D7B8D);
                playerObject.graphics.drawRect(0, 0, 25, 50);
                //make it rotate around the center
                playerObject.x = 0 - playerObject.width / 2;
                playerObject.y = 0 - playerObject.height / 2;
                shipContainer = new Sprite();
                shipContainer.addChild(playerObject);
                shipContainer.x = 100;
                shipContainer.y = 100;
                shipContainer.rotation = 180;
                addChild(shipContainer);
    
                //step 2: install keyboard hook when stage is ready
                addEventListener(Event.ADDED_TO_STAGE, stageReady, false, 0, true);
    
                //step 3: install rendering update poll
                addEventListener(Event.ENTER_FRAME, updatePoller, false, 0, true);
            }
    
            private function updatePoller(event:Event):void
            {
                var newTime:Number = getTimer();
    
                //turning
                if (turningMode != 0)
                {
    
                    var turningDeltaTime:Number = newTime - turningTimestamp;
                    turningTimestamp = newTime;
                    var rotation:Number = TURNING_SPEED * turningDeltaTime / 1000;
                    if (turningMode == 1) shipContainer.rotation -= rotation;
                    else shipContainer.rotation += rotation;
                }
    
                //movement
                if (moving)
                {
                    var movementDeltaTime:Number = newTime - movementTimestamp;
                    movementTimestamp = newTime;
                    var distance:Number = MOVEMENT_SPEED * movementDeltaTime / 1000;
                    var rAngle:Number = shipContainer.rotation * RADIAN_DIVIDE; //convert degrees to radian
                    shipContainer.x += distance * Math.sin(rAngle);
                    shipContainer.y -= distance * Math.cos(rAngle);
                }
            }
    
            private function stageReady(event:Event):void
            {
                //install keyboard hook
                stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown, false, 0, true);
                stage.addEventListener(KeyboardEvent.KEY_UP, keyUp, false, 0, true);
            }
    
            private final function keyDown(event:KeyboardEvent):void
            {
                if ((event.keyCode == 87) && (!moving))  //87 = W
                {
                    movementTimestamp = getTimer();
                    moving = true;
                }
                if ((event.keyCode == 65) && (turningMode != 1)) //65 = A
                {
                    turningTimestamp = getTimer();
                    turningMode = 1;
                }
                else if ((event.keyCode == 68) && (turningMode != 2)) //68 = D
                {
                    turningTimestamp = getTimer();
                    turningMode = 2;
                }
            }
    
            private final function keyUp(event:KeyboardEvent):void
            {
                if ((event.keyCode == 87) && (moving)) moving = false; //87 = W
                if (((event.keyCode == 65) || (event.keyCode == 68)) && (turningMode != 0)) turningMode = 0; //65 = A, 68 = D
            }
        }
    }
    

    结果如我所料。绝对没有改善。我真的希望有人有另一个建议,因为这件事需要修复。另外,我怀疑这是我的系统,因为我有一个非常好的系统(8GB内存,Q9550 QuadCore英特尔,ATI Radeon 4870 512MB)。此外,到目前为止我问过的其他人都和我的客户有同样的问题。

    更新5:另一个流畅的Flash游戏示例,只是为了证明我的运动绝对不同!见http://www.spel.nl/game/bumpercraft.html

    更新4 :我追溯渲染前的时间(EVENT.RENDER)和渲染后的正确时间(EVENT.ENTER_FRAME),结果如下:

    rendering took: 14 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 14 ms
    rendering took: 14 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 24 ms
    rendering took: 18 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 232 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 16 ms
    rendering took: 12 ms
    rendering took: 14 ms
    rendering took: 12 ms
    

    范围是12-16毫秒。在这些差异中,震惊/翘曲/闪烁运动已经开始。还有1个232ms的峰值,此时有一个相对较大的扭曲。然而,这不是最大的问题,最大的问题是在正常运动期间连续的小扭曲。这会给任何人一个线索吗?

    更新3 :经过测试,我知道以下因素导致我的问题:

    • 位图的质量 - >用photoshop更改为更加丑陋的8色优化图形,完全没有任何改进。
    • 转弯时图像的恒定旋转 - >禁用它,完全没有任何改进
    • 浏览器呈现 - >试图单独使用flash播放器,完全没有改进

    我100%确信问题出在我的代码或算法中。拜托,帮帮我吧。现在已经差不多两周了(我问过这个问题的一周),我仍然需要得到我的回答。

    更新1:请参阅完整的Flex项目源代码的底部以及演示我的问题的实时演示。

    我正在开发一款2D游戏。玩家船只作为对象创建:

    ships[id] = new GameShip();
    

    当移动和旋转信息可用时,这将被定向到相应的船只:

    ships[id].setMovementMode(1); //move forward
    

    现在,在此GameShip对象中,移动使用“Event.ENTER_FRAME”事件:

    addEventListener(Event.ENTER_FRAME, movementHandler);
    

    然后运行以下功能:

    private final function movementHandler(event:Event):void
            {
                var newTimeStamp:uint = UtilLib.getTimeStamp(); //set current timeStamp
                var distance:Number = (newTimeStamp - movementTimeStamp) / 1000 * movementSpeed; //speed = x pixels forward every 1 second
                movementTimeStamp = newTimeStamp; //update old timeStamp
                var diagonalChange:Array = getDiagonalChange(movementAngle, distance); //the diagonal position update based on angle and distance
                charX += diagonalChange[0];
                charY += diagonalChange[1];
                if (shipContainer)
                { //when the container is ready to be worked with
                    shipContainer.x = charX;
                    shipContainer.y = charY;
                }
            }
    
    private final function getDiagonalChange(angle:Number, distance:Number):Array
            {
                var rAngle:Number = angle * Math.PI/180; //convert degrees to radian
                return [Math.sin(rAngle) * distance, (Math.cos(rAngle) * distance) * -1];
            }
    

    当对象不再移动时,将删除事件侦听器。正在使用相同的方法进行旋转。一切都近乎完美。

    我已将项目的目标FPS设置为100并创建了一个FPS计数器。根据FPS计数器,firefox中的平均FPS大约为100,而顶部是1000,底部是22.我认为底部和顶部FPS只发生在客户端初始化(启动)。

    问题是船看起来几乎是非常光滑的,而它应该只是没有“几乎”部分。这几乎就像船快速地“闪烁”,你实际上看不到它,但是当它用眼睛移动时很难聚焦在物体上。而且,偶尔会出现一些帧速率峰值,好像客户端正在跳过几帧,然后你会看到它快速变形。

    很难解释真正的问题是什么,但总的来说,运动并非完全平滑。那么,您对如何使物体的运动或过渡完全平滑有任何建议吗?

    更新1:

    我重新创建了客户端来演示我的问题。请检查一下。

    客户: http://feedpostal.com/test/MovementTest.html

    动作脚本项目(完整来源): http://feedpostal.com/test/MovementTest.rar

    平滑Flash游戏的一个例子(不是我创建的):http://www.gamesforwork.com/games/swf/Mission%20Racing_august_10th_2009.swf

    我花了很长时间重新创建这个客户端版本,我希望这有助于解决问题。

    请注意:是的,实际上非常顺利。但这绝对不够顺畅。

8 个答案:

答案 0 :(得分:9)

答案 1 :(得分:2)

您的代码顺利运行给我。没有任何尖峰。 使用updatePoller函数末尾添加的以下代码对其进行测试。

var shadow:Sprite = new Sprite();
shadow.graphics.beginFill(0xFFFFFF, 0.01);
shadow.graphics.lineStyle(1, 0xFFFFFF, 0.8);
shadow.graphics.drawRect(0, 0, 25, 50);
this.addChildAt(shadow, 0);
shadow.x = shipContainer.x;
shadow.y = shipContainer.y;
shadow.rotation = shipContainer.rotation;

100 fps版本趋向于获得不均匀的模式,但这是正常的,因为根据您的计算,如果计算帧花费超过10 ms,则无法在一秒内渲染100帧。 所以,至少对我来说,最后一个代码以30fps顺利运行。

至于模糊部分,一个主要的dooh,希望你不会生气我问: 模糊/模糊效果是否有可能是因为你的显示器? 即使在液晶显示器上的响应时间为10毫秒时,静态黑色背景上的白色快速移动的东西也会显得模糊。

答案 2 :(得分:1)

我认为问题的根源是“vertical sync”与屏幕的不同。这与在60hz屏幕上观看24fps电影时出现的问题相同。更新不会完美匹配(在你的情况下是100/60),当它们达到更大的跳跃时,它看起来像是一个小的抖动。

这可以通过降低帧速率来弥补,任何高于屏幕的任何东西都只是浪费处理能力。尽管newer wmodes for flash embedding可能是一种可能的解决方案,但它并不能完全避免。

答案 3 :(得分:0)

我可以建议你过早担心吗?

如果你仔细观察你所连接的“非常流畅”的Flash游戏,你会注意到它会让你感受到流畅运动的“错觉”。

汽车根本没有快速移动 - 可能是每隔几帧一个像素。它是完成大部分运动的背景。仔细看看:有一点抖动和你试图解决的“难以聚焦”效果,但由于它是背景,看起来很正常。即使汽车确实显示出这些效果,背景和游戏玩法也会让您分心注意它们。

我认为你注意到了抖动,因为你现在拥有的只是一艘完全黑色背景的船。一旦游戏的其余部分到位,玩家可能不会有额外的注意力来注意一点抖动。至于“难以聚焦”的效果,当你降低船的移动速度时它会消失。

为什么你不首先在游戏的剩余部分工作?如果仍然存在问题,您可以随后回来调整动作。你花了很多时间在动画工件上。游戏玩法不是更重要吗?

答案 4 :(得分:0)

这是一个非常好的问题。我已经扫描了代码,我有一些建议,虽然我的建议可能不太好。

我认为你可以做很多事情来优化代码。显然,不是在这个早期阶段。但是您可以使用该代码进行测试并快速查看优化代码,然后您就会知道它是否值得继续。

以下是我的“异议”:

  • 你使用了很多分歧。师 比...更贵 乘法。

    var distance:Number =(newTimeStamp - movementTimeStamp)/ 1000 * movementSpeed;

可以很容易地写成

var distance:Number = (newTimeStamp - movementTimeStamp) * .001 * movementSpeed;
  • 你有很多很多参考资料 功能。

    像fixAngle()之类的东西可以在同一个函数中,而不需要经常来回运行的调用。这适用于对外部类和Math.PI或Math.sin等的引用,正如fenomas和Allan指出的那样。

我已经测试了this方法的正弦和余弦,它的速度非常快。当然它会使代码变脏,但这就是为什么你不尽快优化它,直到你的大部分工作方式都需要工作,优化只会让你疯狂,因为代码会变得更难阅读。根据我的经验,sin和cos是相当昂贵的操作。

正如其他人已经提到的那样,你可能会在这一步过度担心。请记住,有很多事情可以加速,直到你的所有逻辑都正常工作,甚至不考虑优化。

答案 5 :(得分:0)

我认为这几乎可以肯定是因为你的速度达到了80fps。 Flash根本无法快速提供一致的帧速率。下降到30fps并继续测试。另外,尝试在实际背景前飞行,我想你会注意到这一点。

答案 6 :(得分:0)

要下载的文件不存在(http://feedpostal.com/test/MovementTest.rar)。

答案 7 :(得分:0)

我已回答了有关此问题的其他问题,请阅读以下内容:

我感觉到你的痛苦,因为我目前正在开发自己的游戏。在默认设置下,无论您生成什么代码,Flash渲染器都会产生可怕的屏幕撕裂/ v-sync问题。

这就是为什么我很高兴能找到最简单,最优雅的答案,那就是没有重新分解代码(这对单个位有帮助,问题是Flash播放器,而不是代码)。

只需在“发布设置”中启用硬件加速即可。有两种不同的选择:

第1级:直接;和第2级:GPU

在官方文档中了解更多相关信息:Specify publish settings for SWF files,并确定哪种选项最适合您的游戏。

目标市场确实在这里发挥作用,如果它对游戏玩家来说是一个严肃的游戏,你不需要担心可能的性能问题,因为大多数游戏玩家都有GPU。

This article并未特别向我提供解决方案,但引导我朝着正确的方向前进。但是,如果您的游戏将在浏览器窗口中,您可能必须使用相同的技术将wmode设置为direct或gpu。