You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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();
}

问题分析

  1. 画面被覆盖:GamePanel是JFrame的默认添加组件,直接调用Main.window.add(mediaPlayerComponent)会将播放器组件添加到默认层,组件层级上GamePanel仍在播放器上方,导致视频画面被遮挡。
  2. 视频结束后无法恢复:视频结束时仅调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 19:45:55