【问题标题】:Graphics Context will only draw in one thread图形上下文只会在一个线程中绘制
【发布时间】:2017-05-07 13:03:26
【问题描述】:

我正在制作蛇游戏,但每当我尝试在draw() 方法中更新我的画布时,新的蛇都不会绘制。它在run 线程中绘制。我尝试了很多不同的方法,但似乎无法正常工作。

进口:

import javafx.application.Application;
import javafx.event.*;
import javafx.scene.*;
import javafx.stage.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.geometry.*;
import java.io.*;
import java.util.*;
import javafx.scene.input.KeyEvent;
import javafx.event.EventHandler;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.text.*;
import javafx.scene.paint.*;
import javafx.scene.canvas.*;

实际类:

public class Snake extends Application implements Runnable
{
    //Snake parts and locations
    boolean dead;
    int headX=0;
    int headY=0;
    // for the game
    Group root = new Group();
    Scene snakeG = new Scene(root, 550, 550,Color.BLACK);
    final Canvas canvas = new Canvas(550,550);
    GraphicsContext gc = canvas.getGraphicsContext2D();
    //Start Game
    VBox newGame = new VBox(3);
    Scene startC = new Scene(newGame, 200, 200);
    Label info = new Label("Snake Game \nCreated by: Austin");
    Label rules = new Label("Rules\n1.) If you hit the edge you die\n2.) If you touch your snake you die\n3.)Collect fruit to increase your snakes size");
    Button startBut = new Button("Start Game");
    Stage startS;

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

    @Override
    public void start ( Stage primaryStage )throws Exception
    {
        startS=primaryStage;
        startS.setTitle("Snake");
        newGame.getChildren().addAll(info,rules,startBut);
        newGame.setAlignment(Pos.CENTER);
        startS.setScene(startC);
        startS.show();

        startBut.setOnAction(e ->
        {
            startGame();
        });
    }

    public void startGame()
    {
        System.out.println("Success");
        headX=275;
        headY=275;
        dead = false;
        gc.clearRect(0,0,800,800);
        startS.setScene(snakeG);
        gc.setFill(Color.GREEN);
        gc.fillRect(headX,headY,10,10);
        root.getChildren().add(canvas);
        (new Thread ( new Snake())).start();    
    }

    public void run()
    {
        draw(); 
    }

    // draws the snake
    public void draw()
    {
        System.out.println("DRAW STARTED");
        gc.setFill(Color.GREEN);
        gc.fillRect(50,50,10,10);   
    }
}

如果您知道在 JavaFX 中绘制图形的更好方法,请告诉我。这是我能找到的唯一方法。

【问题讨论】:

    标签: java javafx


    【解决方案1】:

    您的方法存在一些问题。

    1. 您不需要自己的线程。
    2. 您只能使用 JavaFX 线程修改活动场景图(包括画布)。阅读JavaFX concurrency documentation

      代表 JavaFX 应用程序的图形用户界面的 JavaFX 场景图不是线程安全的,只能从 UI 线程(也称为 JavaFX 应用程序线程)访问和修改。

    3. 您的主应用程序类不应实现 Runnable

    一些建议:

    1. 您可能会发现为游戏对象使用 SceneGraph 节点比使用 Canvas 更容易,但两者都可以。
    2. 您可以使用AnimationTimer 作为demoed here 来实现您的game loop
    3. 这是using an AnimationTimer for display 的示例。

    【讨论】:

    • 我在自己的线程上使用GraphicsContext2d 从来没有遇到过问题。我认为所有这些绘图操作都可以从 UI 线程中调用。我还查看了源代码,没有检查 Platform.isFxApplicationThread()
    • GraphicsContext Javadoc 明确指出每次调用都会将必要的参数推送到缓冲区,稍后将在脉冲结束时由渲染线程将它们渲染到 Canvas 节点的图像上。 因此,它完全独立于线程。
    • @MouseEvent 这不会使它成为线程安全的。缓冲区没有同步,因此如果您从后台线程修改它,您将面临各种风险:您可能会在 FX 应用程序线程呈现脉冲时更改缓冲区,这意味着 FX 应用程序线程可能会看到不同的在单个脉冲期间,缓冲区的状态不一致。 (还有其他风险,比如活性等)
    • @brian 并非必须在 FX 应用程序线程上执行的每个操作都实际检查它是否在正确的线程上。对于某些操作,检查的性能开销令人望而却步——JavaFX 团队可能认为画布操作就是这种情况。仅仅因为它恰好在您的特定系统上工作并不能保证安全:这个答案中引用的文档非常清楚。从 FX 应用程序线程的任何线程异常执行修改场景图状态的代码是错误的。
    • 在某些情况下,进一步澄清可能会有所帮助。只要画布尚未附加到场景图,就可以从任何线程将图形命令写入画布的 GraphicsContext。通过这种方式,您可以在某个线程上准备画布,而不会减慢 JavaFX 应用程序线程的速度。实际渲染将被延迟,直到您将画布附加到场景图。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 2020-11-09
    相关资源
    最近更新 更多