【问题标题】:JavaFX image ghosting while moving quickly快速移动时出现 JavaFX 图像重影
【发布时间】:2015-02-20 01:05:43
【问题描述】:

我偶然发现了一个问题:如果图像在屏幕上高速移动,则会错误地渲染,从而产生重影效果。我认为我们可以排除我的显示器的问题,因为这种类型的运动在挥杆时完美无缺(具有相同的帧速率)。

看起来像:

代码(从 3 个类合并而来):

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.awt.Dimension;
import java.awt.Toolkit;


public class Constructor extends Application{

        Image player, shot;
        static Dimension screen = new Dimension(Toolkit.getDefaultToolkit().getScreenSize());
        static int wid = screen.width;
        static int hei = screen.height;
        static boolean up, down, left, right, rotleft , rotright;
        static double x = (wid/2)-109;
        static double y = (hei/1.5)-132;
        static double velx = 0, vely = 0, velx2 = 0, vely2 = 0;
        static double forspeed = 0, sidespeed = 0;
        static int rotat = 0;

        public void load(){
                player = new Image("res/sprite.png");
        }

        @Override
        public void start(final Stage frame) throws Exception{

                load();

                frame.setTitle("DEFAULT");
                frame.initStyle(StageStyle.UNDECORATED);

                Group root = new Group();              
                final ImageView ship = new ImageView();
                ship.setImage(player); 
                root.getChildren().add(ship);

            frame.setScene(new Scene(root,  wid,  hei, Color.BLACK));

                frame.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>(){
                        public void handle(KeyEvent key) {
                          if(key.getCode()==KeyCode.W)
                                   up = true;
                          if(key.getCode()==KeyCode.S)
                                   down = true;
                          if(key.getCode()==KeyCode.Q)
                                   left = true;
                          if(key.getCode()==KeyCode.E)
                                   right = true;
                          if(key.getCode()==KeyCode.A)
                                   rotleft = true;
                          if(key.getCode()==KeyCode.D)
                                   rotright = true;

                        }
                });
                frame.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>(){
                        public void handle(KeyEvent key) {
                          if(key.getCode()==KeyCode.ESCAPE)
                          {
                              frame.close();
                                  System.exit(0);  
                          }
                          if(key.getCode()==KeyCode.W)
                                   up = false;
                          if(key.getCode()==KeyCode.S)
                                   down = false;
                          if(key.getCode()==KeyCode.Q)
                                   left = false;
                          if(key.getCode()==KeyCode.E)
                                   right = false;
                          if(key.getCode()==KeyCode.A)
                                   rotleft = false;
                          if(key.getCode()==KeyCode.D)
                                   rotright = false;

                        }
                });
                frame.setAlwaysOnTop(true);
                frame.setHeight(hei);
                frame.setWidth(wid);
                frame.setResizable(false);
                frame.setFullScreen(true);
                frame.show();

                 new AnimationTimer() {
                    @Override
                    public void handle(long now) {

                        gameloop();
                        ship.setTranslateX(x);
                        ship.setTranslateY(y);
                        ship.setRotate(rotat);
                    }
                }.start();
        }

        public static void gameloop(){
        if(Shared.up)
                forspeed += 1;
        if(Shared.down)
                forspeed -= 1;
        if(Shared.right)
                sidespeed += 1;
        if(Shared.left)
                sidespeed -= 1;

        if(Shared.rotleft)
                rotat -=3;
        if(Shared.rotright)
                rotat +=3;

        velx = Math.cos(Math.toRadians(rotat-90))*forspeed + Math.cos(Math.toRadians(rotat))*sidespeed;
        vely = Math.sin(Math.toRadians(rotat-90))*forspeed + Math.sin(Math.toRadians(rotat))*sidespeed;

        if(!Shared.up && !Shared.down)
        {
                if(forspeed > 0)
                        forspeed -= 0.2;
                else if (forspeed < 0)
                        forspeed += 0.2;
        }
        if(!Shared.right && !Shared.left)
        {
                if(sidespeed > 0)
                        sidespeed -= 0.2;
                else if (sidespeed < 0)
                        sidespeed += 0.2;
        }

        x += velx;
        y += vely;

        screencolisions();
        }

        private static void screencolisions() {
                // LEFT RIGHT
                if(x < 0)
            {
                x = 0;
                sidespeed = 0;
            }
            else if (x+218 > Shared.wid)
            {
                x = Shared.wid-218;
                sidespeed = 0;
            }

                // UP DOWN
            if(y < 0)
            {
                y = 0;
                forspeed = 0;
            }
            else if (y+164 > Shared.hei)
            {
                y = Shared.hei-164;
                forspeed = 0;
            }

        }

        public static void main(String[] args){
                Application.launch(args);
    }
}

【问题讨论】:

  • 请学习如何正确插入图片。
  • 可能是一些奇怪的缓冲问题?
  • Kamil,这是我的第一篇文章,前几篇文章不允许发布图片。
  • 我认得这种设计。那是 Kilrathi Gothri 级重型太空优势战斗机。我在 Privateer 1 中的克星。哈哈。

标签: java image swing javafx-8


【解决方案1】:

首先你在 Javafx 中使用 AWT 类。他们不是朋友(大多数时候)。

不使用 Dimension 和 AWT 工具包, 使用提供的 Javafx Screen 类

Screen screen = Screen.getPrimary();
wid = screen.getBounds().getWidth();
hei = screen.getBounds().getHeight();
// Visual bounds will be different depending on OS and native toolbars etc..
// think of it as desktop bounds vs whole screen

例如:

public class ScreenBounds extends Application {

    @Override
    public void start(Stage primaryStage) {
        Screen screen = Screen.getPrimary();
        System.out.println("ScreenBounds : " + screen.getBounds() + "\nVisualBounds : " +   screen.getVisualBounds());
        Platform.exit();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

打印:

compile-single:
run-single:
ScreenBounds : Rectangle2D [minX = 0.0, minY=0.0, maxX=1680.0, maxY=1050.0, width=1680.0, height=1050.0]
VisualBounds : Rectangle2D [minX = 0.0, minY=40.0, maxX=1680.0, maxY=1050.0, width=1680.0, height=1010.0]

正如您所描述的那样,您没有看到任何语法错误.. 尽管该错误可能是由于您的变量是整数而不是方法返回的双精度数;

ScheduledService 定时器:

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.util.Duration;

/**
 *
 * @author jdub1581
 */
public class NanoTimer extends ScheduledService<Void> {

    private final long ONE_NANO = 1000000000L;
    private final double ONE_NANO_INV = 1f / 1000000000L;

    private long startTime, previousTime;
    private double frameRate, deltaTime;
    private final NanoThreadFactory tf = new NanoThreadFactory();

    public NanoTimer() {
        super();
        this.setPeriod(Duration.millis(16));// equivalent to 60 fps
        this.setExecutor(Executors.newCachedThreadPool(tf));
    }

    public double getTimeAsSeconds() {
        return getTime() * ONE_NANO_INV;
    }

    public long getTime() {
        return System.nanoTime() - startTime;
    }

    public long getOneSecondAsNano() {
        return ONE_NANO;
    }

    public double getFrameRate() {
        return frameRate;
    }

    public double getDeltaTime() {
        return deltaTime;
    }

    private void updateTimer() {
        deltaTime = (getTime() - previousTime) * (1.0f / ONE_NANO);
        frameRate = 1.0f / deltaTime;
        previousTime = getTime();

    }

    @Override
    public void start() {
        super.start();
        if (startTime <= 0) {
            startTime = System.nanoTime();
        }
     }

    @Override
    public void reset() {
        super.reset();
        startTime = System.nanoTime();
        previousTime = getTime();
    }
    private boolean init = true;

    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {                
                updateTimer();
                // perform NON UI calculations here

                return null;
            }
        };
    }

    @Override
    protected void succeeded() {
        super.succeeded();
        //update the UI here
    }

    @Override
    protected void failed() {
        getException().printStackTrace(System.err);

    }

    @Override
    public String toString() {
        return "ElapsedTime: " + getTime() + "\nTime in seconds: " + getTimeAsSeconds()
            + "\nFrame Rate: " + getFrameRate()
            + "\nDeltaTime: " + getDeltaTime();
    }

    /*==========================================================================
        creates a daemon thread for use
    */
    private class NanoThreadFactory implements ThreadFactory {

        public NanoThreadFactory() {
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "NanoTimerThread");
            t.setDaemon(true);
            return t;
        }

    }
}//=============================================================================

无论你把它放在什么类中,只需调用 start 方法,可能最好嵌套为私有类,以便可以在其中使用变量..或者根据需要重写..

我在这里使用它:My Cloth Simulation,它在每帧进行 50k+ 计算时效果很好

【讨论】:

  • 你告诉我的可能是好事,但与问题无关。我使用该工具包仅返回 2 个 int 值,我可以手动输入这些值以获得相同的结果。此外,将成员更改为私有不会以任何方式影响图形呈现。
  • 使用 JavaFX 中的任何 AWT 将启动整个 AWT 堆栈,这会增加内存/进程消耗。
  • 游戏以稳定的 60fps 运行。顺便说一句,您发布的代码给出了语法错误,也许您应该在向人们推荐之前检查这些东西是否有效......
  • 它确实有效,并且在我拥有的许多自定义窗口中都没有错误。我也不建议在测试之前。
  • 好吧,你没有提到它需要在 start void 中(这对我来说不是很有用,因为我需要它可以被其他类访问)。无论如何,这是一个无用的答案,即使没有这个或带有硬编码值的 awt,图形显然保持不变。感谢您的宝贵时间。
猜你喜欢
  • 2016-05-07
  • 2015-10-28
  • 1970-01-01
  • 2017-01-08
  • 1970-01-01
  • 2017-01-29
  • 2012-12-05
  • 2014-10-30
  • 2016-12-16
相关资源
最近更新 更多