2 Stimmen

Optimierung von Übergängen/Bewegungsabläufen für ein 2D-Flash-Spiel

Aktualisierung 6 :

Fenomenas schlug mir vor, alles so einfach wie möglich neu zu gestalten. Ich hatte meine Zweifel, dass dies einen Unterschied machen würde, da der Algorithmus derselbe bleibt und die Leistung nicht das Problem zu sein schien. Wie auch immer, es war der einzige Vorschlag, den ich bekam, also hier ist er:

  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

Der Code:

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
        }
    }
}

Die Ergebnisse waren so, wie ich es erwartet hatte. Absolut keine Verbesserung. Ich hoffe wirklich, dass jemand einen anderen Vorschlag hat, da diese Sache behoben werden muss. Außerdem bezweifle ich, dass es an meinem System liegt, da ich ein ziemlich gutes habe (8GB RAM, Q9550 QuadCore intel, ATI Radeon 4870 512MB). Außerdem hatte jeder andere, den ich bisher gefragt habe, das gleiche Problem mit meinem Client.

Update 5: ein weiteres Beispiel für ein flüssiges Flash-Spiel, nur um zu zeigen, dass meine Bewegungen definitiv anders sind! Siehe http://www.spel.nl/game/bumpercraft.html

Aktualisierung 4 : Ich habe die Zeit vor dem Rendering (EVENT.RENDER) und direkt nach dem Rendering (EVENT.ENTER_FRAME) verfolgt, die Ergebnisse:

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

Der Bereich liegt bei 12-16 ms. Während dieser Unterschiede war die schockierende/verzerrender/flimmernde Bewegung bereits im Gange. Es gibt auch eine Spitze von 232 ms, zu dieser Zeit gab es eine relativ große Verwerfung. Das ist aber nicht das größte Problem, das größte Problem sind die ständigen kleinen Verwerfungen bei normalen Bewegungen. Gibt dies irgendjemandem einen Anhaltspunkt?

Update 3: Nach Tests weiß ich, dass die folgenden Faktoren no mein Problem verursacht:

  • Bitmap-Qualität -> mit Photoshop in eine hässliche 8-Farben-optimierte Grafik geändert, keine Verbesserung.
  • Ständige Drehung des Bildes beim Drehen -> deaktiviert, keine Verbesserung
  • Browser-Rendering -> versucht, den Flash-Player allein zu verwenden, keine Verbesserung

Ich bin zu 100 % überzeugt, dass das Problem entweder in meinem Code oder in meinem Algorithmus liegt. Bitte, helfen Sie mir weiter. Es ist jetzt fast zwei Wochen her (1 Woche, in der ich diese Frage auf SO gestellt habe) und ich habe immer noch keine Antwort auf meine Frage bekommen.

Aktualisierung 1: siehe unten für vollständige Flex-Projekt-Quelle und eine Live-Demo demonstriert mein Problem.

Ich arbeite an einem 2d-Flash-Spiel. Spielerschiffe werden als Objekt erstellt:

ships[id] = new GameShip();

Wenn Bewegungs- und Rotationsinformationen verfügbar sind, werden diese an das entsprechende Schiff weitergeleitet:

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

Innerhalb dieses GameShip-Objekts funktioniert die Bewegung nun über das Ereignis "Event.ENTER_FRAME":

addEventListener(Event.ENTER_FRAME, movementHandler);

Die folgende Funktion wird dann ausgeführt:

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];
        }

Wenn das Objekt nicht mehr in Bewegung ist, wird der Ereignis-Listener entfernt. Die gleiche Methode wird für die Rotation verwendet. Alles funktioniert nahezu perfekt.

Ich habe die Ziel-FPS des Projekts auf 100 gesetzt und einen FPS-Zähler erstellt. Laut dem FPS-Zähler liegt die durchschnittliche FPS in Firefox bei 100, während die obere Grenze bei 1000 und die untere bei 22 liegt. Ich denke, dass die unteren und oberen FPS nur während der Initialisierung des Clients (Startup) auftreten.

Das Problem ist, dass das Schiff fast perfekt glatt zu sein scheint, während es genau das sein sollte, ohne den "fast" Teil. Es ist fast so, als ob das Schiff sehr schnell "flackert", man kann es nicht wirklich sehen, aber es ist schwer, sich auf das Objekt zu konzentrieren, während es sich mit den Augen bewegt. Außerdem scheint es hin und wieder einen kleinen Framerate-Spike zu geben, so als ob der Client ein paar Frames überspringt und man dann sieht, wie er sich schnell verzieht.

Es ist sehr schwierig zu erklären, was das eigentliche Problem ist, aber im Allgemeinen liegt es daran, dass die Bewegung nicht vollkommen glatt ist. Haben Sie irgendwelche Vorschläge, wie man die Bewegung oder den Übergang von Objekten perfekt glatt machen kann?

Aktualisierung 1:

Ich habe den Client neu erstellt, um mein Problem zu demonstrieren. Bitte sehen Sie es sich an.

Der Kunde: http://feedpostal.com/test/MovementTest.html

Das Actionscript-Projekt (vollständiger Quelltext): http://feedpostal.com/test/MovementTest.rar

Ein Beispiel für ein flüssiges Flash-Spiel (nicht von mir erstellt): http://www.gamesforwork.com/games/swf/Mission%20Racing_august_10th_2009.swf

Es hat mich ziemlich viel Zeit gekostet, diese clientseitige Version zu erstellen, ich hoffe, dass dies bei der Lösung des Problems hilft.

Bitte beachten Sie: Ja, es ist tatsächlich ziemlich glatt. Aber es ist definitiv nicht glatt genug.

0voto

Iain Punkte 9298

Ich denke, das liegt mit ziemlicher Sicherheit daran, dass Sie mit 80 Bildern pro Sekunde unterwegs sind. Flash kann einfach keine konsistente Bildrate in dieser Geschwindigkeit liefern. Gehen Sie auf 30fps herunter und testen Sie weiter. Versuchen Sie auch, das Schiff vor einem echten Hintergrund zu fliegen, und ich denke, Sie werden dies weniger bemerken.

0voto

Die herunterzuladenden Dateien sind nicht vorhanden ( http://feedpostal.com/test/MovementTest.rar ).

0voto

Daniel Carvalho Punkte 537

Ich habe eine andere Frage zu diesem Thema beantwortet, lesen Sie unten:

Ich kann Ihren Schmerz nachempfinden, denn ich bin gerade dabei, mein eigenes Spiel zu entwickeln. Bei Standardeinstellungen produziert der Flash-Renderer schreckliche Screen Tearing / V-Sync-Probleme, unabhängig davon, welchen Code Sie produzieren.

Deshalb war ich froh, die einfachste und eleganteste Lösung gefunden zu haben, bei der es nicht darum ging, den Code umzufunktionieren (was kein bisschen hilft, das Problem ist der Flash-Player, nicht der Code).

Aktivieren Sie einfach die Hardware-Beschleunigung in Ihren Veröffentlichungseinstellungen . Es gibt zwei verschiedene Möglichkeiten:

Ebene 1: Direkt und Ebene 2: GPU .

Lesen Sie mehr darüber in der offiziellen Dokumentation: Festlegen der Veröffentlichungseinstellungen für SWF-Dateien und entscheiden Sie, welche Option für Ihr Spiel am besten geeignet ist.

Wenn es sich um ein ernsthaftes Spiel für Gamer handelt, brauchen Sie sich keine Sorgen über mögliche Leistungsprobleme zu machen, da die meisten Gamer einen Grafikprozessor haben.

Dieser Artikel hat mir zwar nicht die Lösung geliefert, aber den richtigen Weg gewiesen. ABER, wenn Ihr Spiel wird in einem Browser-Fenster zu sein, müssen Sie möglicherweise die gleiche Technik der Einstellung wmode auf direkte oder gpu als auch verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X