如何在Java中让VLCj的EmbeddedMediaPlayerComponent显示在JPanel上方?
Java Swing游戏中VLCj过场动画播放异常问题
问题现象
- 播放过场动画时仅能听到声音,画面显示GamePanel的黑色背景
- 视频结束后无法恢复游戏界面
- 尝试用
setContentPane设置播放器组件,视频结束后画面停留在最后一帧,无法回到游戏界面
代码实现
CutscenePlayer类(视频播放核心类)
import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent; import uk.co.caprica.vlcj.player.base.MediaPlayer; import uk.co.caprica.vlcj.player.base.MediaPlayerEventAdapter; public class CutscenePlayer extends MediaPlayerEventAdapter { GamePanel gp; public static EmbeddedMediaPlayerComponent mediaPlayerComponent; public boolean playing; public String video; public CutscenePlayer(GamePanel gp) { this.gp = gp; } public void setCutscene(String filePath) { video = filePath; } public void playCutscene() { mediaPlayerComponent = new EmbeddedMediaPlayerComponent(); Main.window.add(mediaPlayerComponent); mediaPlayerComponent.mediaPlayer().events().addMediaPlayerEventListener(new MediaPlayerEventAdapter() { @Override public void finished(MediaPlayer mediaPlayer) { System.out.println("Hello"); mediaPlayerComponent.release(); gp.gameState = gp.cutsceneState; playing = false; } } ); mediaPlayerComponent.mediaPlayer().media().play(video); } public void play() { mediaPlayerComponent.mediaPlayer().controls().pause(); } public void pause() { mediaPlayerComponent.mediaPlayer().controls().pause(); } }
CutsceneManager调用代码
if (isCutscenePlaying == false) { gp.csPlayer.setCutscene("test3.mp4"); gp.csPlayer.playCutscene(); isCutscenePlaying = true; gp.gameState = gp.cutscenePlayingState; } else { scenePhase++; isCutscenePlaying = false; }
GamePanel核心代码(游戏主界面)
@Override public void run() { final double drawInterval = 1000000000 / FPS; double delta = 0.0; long lastTime = System.nanoTime(); long currentTime; while (gameThread != null) { currentTime = System.nanoTime(); delta += (currentTime - lastTime) / drawInterval; lastTime = currentTime; while (delta >= 1.0) { if (gameState != cutscenePlayingState) { update(); drawToTempScreen(); drawToScreen(); } --delta; } } } public void update() { if (gameState == playState) { //PLAYER player.update(); //NPC for (int i = 0; i < npc[1].length; i++) { if (npc[currentMap][i] != null) { npc[currentMap][i].update(); } } for (int i = 0; i < monster[1].length; i++) { if (monster[currentMap][i] != null) { if (monster[currentMap][i].alive == true && monster[currentMap][i].dying == false) { monster[currentMap][i].update(); } if (monster[currentMap][i].alive == false) { monster[currentMap][i].checkDrop(); monster[currentMap][i] = null; } } } for (int i = 0; i < projectile[1].length; i++) { if (projectile[currentMap][i] != null) { if (projectile[currentMap][i].alive == true) { projectile[currentMap][i].update(); } if (projectile[currentMap][i].alive == false) { projectile[currentMap][i] = null; } } } for (int i = 0; i < particleList.size(); i++) { if (particleList.get(i) != null) { if (particleList.get(i).alive == true) { particleList.get(i).update(); } if (particleList.get(i).alive == false) { particleList.remove(i); } } } for (int i = 0; i < iTile[1].length; i++) { if (iTile[currentMap][i] != null) { iTile[currentMap][i].update(); } } eManager.update(); } if (gameState == pauseState) { //nothing } } public void drawToTempScreen() { //DEBUG long drawStart = 0; if (keyH.debugOn == true) { drawStart = System.nanoTime(); } //TITLE SCREEN if (gameState == titleState) { ui.draw(g2); } // //MAP SCREEN // else if (gameState == mapState){ // map.drawFullMapScreen(g2); // } //OTHERS else { //TILE tileM.draw(g2); //INTERACTIVE TILE for (int i = 0; i < iTile[1].length; i++) { if (iTile[currentMap][i] != null) { iTile[currentMap][i].draw(g2); } } //ADD ENTITIES TO THE LIST entityList.add(player); for (int i = 0; i < npc[1].length; i++) { if (npc[currentMap][i] != null ) { entityList.add(npc[currentMap][i]); } } for (int i = 0; i < obj[1].length; i++ ) { if (obj[currentMap][i] != null) { entityList.add(obj[currentMap][i]); } } for (int i = 0; i < monster[1].length; i++ ) { if (monster[currentMap][i] != null) { entityList.add(monster[currentMap][i]); } } for (int i = 0; i < projectile[1].length; i++ ) { if (projectile[currentMap][i] != null) { entityList.add(projectile[currentMap][i]); } } for (int i = 0; i < particleList.size(); i++ ) { if (particleList.get(i) != null) { entityList.add(particleList.get(i)); } } //SORT Collections.sort(entityList, new Comparator<Entity>() { @Override public int compare(Entity e1, Entity e2) { int result = Integer.compare(e1.worldY, e2.worldY); return result; } }); //DRAW ENTITIES for (int i = 0; i < entityList.size(); i++) { entityList.get(i).draw(g2);; } //EMPTY ENTITY LIST entityList.clear(); //ENVIORNMENT eManager.draw(g2); // //MINIMAP // map.drawMinimap(g2); //CUTSCENE MANAGER csManager.draw(g2); //UI ui.draw(g2); //TIME AMULET SCREEN if (gameState == timeAmuletState){ g2.setColor(new Color(0, 0, 0, 150)); g2.fillRect(0, 0, screenWidth, screenHeight); dClock.drawClock(g2); } } //DEBUG if (keyH.debugOn == true) { long drawEnd = System.nanoTime(); long passed = drawEnd - drawStart; System.out.println("Draw Time: " + passed); g2.setFont(getFont().deriveFont(20F)); g2.setColor(Color.WHITE); int x = 10; int y = 400; int lineHeight = 20; g2.drawString("Draw Time: " + passed, x, y); y += lineHeight; g2.drawString("Col: " + (player.worldX + player.solidArea.x)/tileSize, x, y); y += lineHeight; g2.drawString("Row: " + (player.worldY + player.solidArea.y)/tileSize, x, y); y += lineHeight; g2.drawString("God Mode: "+ keyH.godModeOn, x, y); //y += lineHeight; } } public void drawToScreen() { if (gameState != cutscenePlayingState) { Graphics g = getGraphics(); g.drawImage(tempScreen, 0, 0, screenWidth2, screenHeight2, null); g.dispose(); } }
Main类初始化代码
public static void main(String[] args) { window = new JFrame(); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.setResizable(false); window.setTitle("Time Maze"); new Main().setIcon(); window.add(gamePanel); gamePanel.config.loadConfig(); if (gamePanel.fullScreenOn) { window.setUndecorated(true); } window.pack(); window.setLocationRelativeTo(null); window.setVisible(true); gamePanel.setUpGame(); gamePanel.startGameThread(); }
问题分析
- 画面被覆盖:GamePanel是JFrame的默认添加组件,直接调用
Main.window.add(mediaPlayerComponent)会将播放器组件添加到默认层,组件层级上GamePanel仍在播放器上方,导致视频画面被遮挡。 - 视频结束后无法恢复:视频结束时仅调用
mediaPlayerComponent.release()并修改游戏状态,但未将播放器组件从JFrame中移除,也未触发GamePanel的重绘;使用setContentPane时,替换内容面板后未恢复原GamePanel,导致画面停留在最后一帧。
解决方案
方案1:替换内容面板并恢复
修改CutscenePlayer的playCutscene方法,保存原内容面板,视频结束后恢复:
public void playCutscene() { mediaPlayerComponent = new EmbeddedMediaPlayerComponent(); final Container originalContent = Main.window.getContentPane(); Main.window.setContentPane(mediaPlayerComponent); Main.window.revalidate(); Main.window.repaint(); mediaPlayerComponent.mediaPlayer().events().addMediaPlayerEventListener(new MediaPlayerEventAdapter() { @Override public void finished(MediaPlayer mediaPlayer) { mediaPlayerComponent.release(); // 恢复原内容面板 Main.window.setContentPane(originalContent); Main.window.revalidate(); Main.window.repaint(); // 切换回游戏状态 gp.gameState = gp.playState; playing = false; gp.repaint(); } }); mediaPlayerComponent.mediaPlayer().media().play(video); }
方案2:使用GlassPane显示视频
将播放器组件添加到JFrame的GlassPane,无需替换内容面板:
public void playCutscene() { mediaPlayerComponent = new EmbeddedMediaPlayerComponent(); mediaPlayerComponent.setBounds(0, 0, gp.getWidth(), gp.getHeight()); JComponent glassPane = (JComponent) Main.window.getGlassPane(); glassPane.setLayout(null); glassPane.add(mediaPlayerComponent); glassPane.setVisible(true); mediaPlayerComponent.mediaPlayer().events().addMediaPlayerEventListener(new MediaPlayerEventAdapter() { @Override public void finished(MediaPlayer mediaPlayer) { mediaPlayerComponent.release(); glassPane.remove(mediaPlayerComponent); glassPane.setVisible(false); gp.gameState = gp.playState; playing = false; gp.repaint(); } }); mediaPlayerComponent.mediaPlayer().media().play(video); }
额外注意事项
- 组件添加/移除操作需在Swing事件调度线程(EDT)中执行,可使用
SwingUtilities.invokeLater()包裹代码。 - 检查游戏状态切换逻辑,确保视频结束后设置为
playState而非cutsceneState。
内容的提问来源于stack exchange,提问作者ArtOfCode
相关产品推荐
相关产品推荐

