実現したいこと
GPUを用いて(パフォーマンスを落とすことなく)描画をしながらも、アプリケーションでフルスクリーン表示をした時も正常に描画されるようにしたい
発生している問題・分からないこと
Eclipseを用いてエクスポートした実行可能jarファイルを、jpackageを使ってJVM引数を-Dsun.java2d.opengl=Trueと設定してインストーラを作り、それを使ってアプリケーションをインストールして実行すると、以下のメソッドを使ってフルスクリーン表示させた時に、Jpanelインスタンス上にdrawImageやdrawStringを用いて描画したものが表示されなくなる。加えて、カーソルも表示されない。しかし、jarファイルをコマンドプロンプトで実行した時(JVM引数は同じ)は、正常に描画される。-Dsun.java2d.opengl=Falseと設定してインストーラを作った時にも正常に描画される。
該当のソースコード
Java
1 public static void toggleScreenMode(final boolean fullScreen) { 2 3 if (gameFrame != null) {gameFrame.dispose();} 4 gameFrame = new JFrame(); 5 setFrameDefaultValue(gameFrame); 6 7 //フルスクリーンにする 8 if (fullScreen == true) { 9 gameFrame.setUndecorated(true); // タイトルバーを削除 10 if (device.isFullScreenSupported()) { 11 device.setFullScreenWindow(gameFrame); // フルスクリーンに設定 12 } 13 14 isFullScreen = true; 15 } 16 //フルスクリーン解除する 17 else if (fullScreen == false) { 18 gameFrame.pack(); //サブコンポーネントの推奨サイズおよびレイアウトに合わせて、ウィンドウサイズを変更 19 gameFrame.setLocationRelativeTo(null); 20 gameFrame.setVisible(true); 21 22 isFullScreen = false; 23 } 24 25 }
Java
1 2// Threadのインスタンスのstartメソッドから呼び出される 3// 1/60秒ごとにupdateメソッドとrepaintメソッドを実行する 4@Override 5public void run() { 6 7gameIsRunning = true; //gameIsRunningがtrueの時のみキーを操作可能にする 8 9while(gameThread != null) { 10currentTime = System.nanoTime(); 11if (currentTime - lastTime >= drawInterval) { //drawInterval == 1000000000.0/60.0 12update(); //各インスタンスのupdateメソッドを実行する 13repaint(widthOfBlackScreen, 0, monitorWidth-2*widthOfBlackScreen, monitorHeight); //paintComponentメソッドを呼び出す(widthOfBlackScreenは、ゲーム画面が描画される部分を横:縦=3:4にするために両脇に表示させる黒い部分の幅) 14lastTime = currentTime; 15} 16} 17} 18
Java
1 2@Override 3public void paintComponent(final Graphics g) { 4super.paintComponent(g); //コンポーネントの背景をクリア 5 6g2 = (Graphics2D)g; 7 8// Debug 9drawStartNanosecond = 0; 10if (keyH.getDrawTimeIsBeingChecked() == true) {drawStartNanosecond = System.nanoTime();} 11 12// Background 13bgHandler.draw(g2); 14 15 16if (gameState != titleState) { 17// タイルの描画 18tileM.draw(g2); 19 20// Itemのサブクラスのインスタンスの描画 21for (int i = 0; i < item.length; i++) { 22if (item[i] != null) {item[i].draw(g2);} 23} 24 25// worldYによってentitiesの描画する順番を変える 26entities[0] = player; 27for (int i = 1; i < 1+npc.length; i++) { 28if (npc[i-1] != null) {entities[i] = npc[i-1];} 29} 30for (int i = 1+npc.length; i < 1+npc.length+animatePicture.length; i++) { 31if (animatePicture[i-1-npc.length] != null) {entities[i] = animatePicture[i-1-npc.length];} 32} 33for (int i = 1+npc.length+animatePicture.length; i < entities.length; i++) { 34if (obj[i-1-npc.length-animatePicture.length] != null) {entities[i] = obj[i-1-npc.length-animatePicture.length];} 35} 36UtilityTool.sort(entities, this); 37for (int i = 0; i < entities.length; i++) { 38if (entities[i] != null) {entities[i].draw(g2);} 39} 40} 41 42// UI描画 43uiDrawer.draw(g2); 44 45// Debug 46if (keyH.getDrawTimeIsBeingChecked() == true) { 47drawEndNanosecond = System.nanoTime(); 48passedNanosecond = drawEndNanosecond - drawStartNanosecond; 49g2.setColor(Color.white); 50g2.setFont(uiDrawer.maruMonica.deriveFont(50F)); 51String s = (Integer.toString((int)passedNanosecond)); 52g2.drawString("DrawingTime: "+s+"ns"+"("+monitorWidth+":"+monitorHeight+")", widthOfBlackScreen+tileSize/2, UtilityTool.getCenteredTextY(s.toString(), this, g2)); 53} 54 55g2.dispose(); 56}
簡略化したソースコード(+ エラーをtxtファイルに出力する)
Main.java
Java
1package main; 2 3import java.awt.GraphicsDevice; 4import java.awt.GraphicsEnvironment; 5import java.io.FileWriter; 6import java.io.IOException; 7 8import javax.swing.JFrame; 9 10public class Main { 11 12 private static JFrame gameFrame = new JFrame(); 13 private static final GamePanel gp = new GamePanel(); 14 private static GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); 15 private static GraphicsDevice device = env.getDefaultScreenDevice(); 16 17 private static FileWriter fileWriter; 18 19 public static void main(String[] args) { 20 try { 21 try { 22 fileWriter = new FileWriter("該当フォルダのパス\\Debug_Main_main.txt", true); 23 fileWriter.write("Start"); 24 fileWriter.close(); 25 } catch (IOException e) { 26 e.printStackTrace(); 27 } 28 29 gameFrame = new JFrame(); 30 gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 31 gameFrame.add(gp); 32 33 gameFrame.setUndecorated(true); 34 device.setFullScreenWindow(gameFrame); 35 36 gp.startGame(); 37 38 } catch (Exception e) { 39 try { 40 fileWriter = new FileWriter("該当フォルダのパス\\Debug_Main_main.txt", true); 41 fileWriter.write(e.getMessage()); 42 fileWriter.close(); 43 } catch (IOException e1) { 44 e1.printStackTrace(); 45 } 46 } 47 48 } 49}
GamePanel.java
Java
1package main; 2 3import java.awt.Graphics; 4import java.awt.Graphics2D; 5import java.io.FileWriter; 6import java.io.IOException; 7 8import javax.swing.JPanel; 9 10public class GamePanel extends JPanel implements Runnable { 11 //FPS 12 public final int FPS = 60; 13 private final double drawInterval = 1000000000.0/FPS; 14 long lastTime = 0; 15 long currentTime = 0; 16 17 // SYSTEM 18 private Graphics2D g2; 19 private Thread gameThread; 20 private FileWriter fileWriter; 21 22 public void startGame() { //Mainクラスから呼び出される 23 try { 24 //パネルのThreadを開始 => run() 25 gameThread = new Thread(this); 26 gameThread.start(); 27 } catch (Exception e) { 28 try { 29 fileWriter = new FileWriter("該当フォルダのパス\\Debug_GamePanel_startGame.txt", true); 30 fileWriter.write(e.getMessage()); 31 fileWriter.close(); 32 } catch (IOException e1) { 33 e1.printStackTrace(); 34 } 35 } 36 } 37 38 @Override 39 public void run() { 40 while(gameThread != null) { 41 try { 42 currentTime = System.nanoTime(); 43 if (currentTime - lastTime >= drawInterval) { 44 repaint(0, 0, 1920, 1080); 45 lastTime = currentTime; 46 } 47 } catch (Exception e) { 48 try { 49 fileWriter = new FileWriter("該当フォルダのパス\\Debug_GamePanel_run.txt", true); 50 fileWriter.write(e.getMessage()); 51 fileWriter.close(); 52 } catch (IOException e1) { 53 e1.printStackTrace(); 54 } 55 } 56 } 57 } 58 59 //repaintメソッドから呼び出される 60 @Override 61 public void paintComponent(final Graphics g) { 62 try { 63 super.paintComponent(g); 64 65 g2 = (Graphics2D)g; 66 67 g2.drawString("A", 960, 540); 68 69 g2.dispose(); 70 } catch (Exception e) { 71 try { 72 fileWriter = new FileWriter("該当フォルダのパス\\Debug_GamePanel_paintComponent.txt", true); 73 fileWriter.write(e.getMessage()); 74 fileWriter.close(); 75 } catch (IOException e1) { 76 e1.printStackTrace(); 77 } 78 } 79 } 80} 81
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
- Googleで「Java OpenGL フルスクリーン」などと調べ、setExtendedStateメソッドを使ったフルスクリーンの方法も試したが、解決しなかった。
- ChatGPT(GPT-4)を用いて以上の質問を入力し、以下のような結果がいくつか得られたが、解決には至らなかった。
[ChatGPTの回答]
問題の状況について理解しました。jpackageを用いて作成したJavaアプリケーションでフルスクリーン時に問題が発生するが、コマンドプロンプトから同じJVM引数で実行すると問題が発生しないということですね。この問題は、jpackageでパッケージ化されたアプリケーションの設定や環境が、直接コマンドプロンプトから実行する場合と異なることに起因している可能性があります。
以下にいくつかの改善策を提案します。
- JVM引数の確認
jpackageで作成したアプリケーションのJVM引数が正しく設定されているかを再確認してください。パッケージ化のプロセスで引数が適切に引き継がれているか、特に-Dsun.java2d.opengl=Trueが含まれているかを確認します。
2. 環境設定の確認
パッケージ化されたアプリケーションが実行される環境(パス、依存関係など)が、コマンドプロンプトから実行する環境と同じかどうかを確認します。異なる環境設定が影響している場合があります。
3. 描画関連のコードの確認
Graphics2DのdrawImageメソッドに関連するコードが、OpenGLとの互換性を持つように最適化されているかを確認します。特にフルスクリーンモードでの描画方法に注意してください。
4. グラフィックス設定の確認
使用しているグラフィックスカードの設定やドライバーが、OpenGLとの互換性を持つように設定されているかを確認します。ドライバーの更新や設定の変更が必要な場合があります。
5. デバッグ情報の利用
アプリケーションをデバッグモードで実行し、フルスクリーンモード時に発生するエラーや警告を確認します。ログ出力を増やして、問題の原因を特定する手がかりを探します。
6. 他のJVMオプションの試行
他のJVMオプションを試してみるのも一つの方法です。例えば、-Dsun.java2d.noddraw=trueや-Dsun.java2d.d3d=falseなど、異なるグラフィックス関連のオプションを試してみると良いでしょう。
7. jpackageの設定の再確認
jpackageを使用する際の設定やコマンドラインオプションを再確認し、必要な設定が漏れていないか確認します。
これらのステップを踏むことで、問題の原因を特定し、適切な解決策を見つけることができるはずです。また、問題が解決しない場合は、Javaの開発者コミュニティやフォーラムで支援を求めることも検討してください。
補足
開発や実行はすべてWindows11(64ビット)を用いて行い、JDK17とEclipseを用いてJava17のライブラリのawtとswingを使って2Dゲームを作成。awtのrepaintメソッドが1秒間に60回実行される(以下のrunメソッド参照)ことで、Graphics2DのdrawImageやdrawStringを用いた描画(paintComponentメソッドは以下参照)。jpackageを使うにあたってWiXのv3.11.2.4516をダウンロードし、以下のようなコマンドをコマンドプロンプトで実行してインストーラを作成。
「jpackage --type exe --vendor "Shun" --win-menu --win-dir-chooser --win-shortcut --win-shortcut-prompt --input Jarファイルがあるディレクトリのパス --name MyGame --main-jar MyGame.jar --main-class main.Main --java-options "-Dsun.java2d.dpiaware=true" --java-options "-Dsun.java2d.uiScale=1.0" --java-options "-Xmx2048m" --java-options "-Dsun.java2d.opengl=True" --dest Jarファイルがあるディレクトリのパス」
回答1件
あなたの回答
tips
プレビュー