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:
- 30 FPS: http://www.feedpostal.com/test/simple/30/SimpleMovement.html
- 40 FPS: http://www.feedpostal.com/test/simple/40/SimpleMovement.html
- 60 FPS: http://www.feedpostal.com/test/simple/60/SimpleMovement.html
- 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.