如何将Moviepy视频调整为全屏适配640x400的Pygame游戏窗口?
Great question—since Pygame’s old movie module is long deprecated, MoviePy is the right replacement for video playback, but I totally get the frustration with sparse documentation. Let’s get that video fitting your 640x400 window properly, whether you want to maintain aspect ratio (no distortion) or stretch it to fill the screen. Here’s a complete, tested solution:
Step 1: Resize the Video Clip in MoviePy
First, you’ll need to adjust your video clip to match your window dimensions. You have two main options:
Option 1: Fit to Window (Maintain Aspect Ratio)
This keeps the video’s original proportions, adding small black bars (letterboxing/pillarboxing) if the video’s aspect ratio doesn’t match 640x400 (16:10). This is the recommended approach to avoid distortion:
from moviepy.editor import VideoFileClip import pygame # Load your video clip = VideoFileClip("your_cutscene.mp4") # Target window dimensions TARGET_W, TARGET_H = 640, 400 # Calculate scaling factor to fit within the window scale_factor = min(TARGET_W / clip.size[0], TARGET_H / clip.size[1]) resized_clip = clip.resize(scale_factor)
Option 2: Stretch to Exact Window Size
If you don’t mind distorting the video to fill the entire window, use this instead:
resized_clip = clip.resize((TARGET_W, TARGET_H))
Bonus: Fill Window Without Black Bars
To fill the entire window while maintaining aspect ratio (cropping excess video), use a combination of resize and crop:
# Resize to fill the window height, then crop the width to 640 resized_for_height = clip.resize(height=TARGET_H) cropped_clip = resized_for_height.crop(x_center=resized_for_height.size[0]/2, width=TARGET_W) # Or resize to fill the window width, then crop the height resized_for_width = clip.resize(width=TARGET_W) cropped_clip = resized_for_width.crop(y_center=resized_for_width.size[1]/2, height=TARGET_H)
Step 2: Integrate with Pygame
Now you need to render the resized video frames to your Pygame window. The key here is converting MoviePy’s numpy frame arrays to Pygame surfaces and syncing the frame rate correctly:
# Initialize Pygame pygame.init() screen = pygame.display.set_mode((TARGET_W, TARGET_H)) clock = pygame.time.Clock() video_fps = resized_clip.fps frame_delay = 1000 / video_fps # Milliseconds per frame last_frame_time = pygame.time.get_ticks() current_video_time = 0.0 # Main game loop running = True while running and current_video_time < resized_clip.duration: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # Update frame when enough time has passed now = pygame.time.get_ticks() if now - last_frame_time >= frame_delay: # Get current frame from the video frame = resized_clip.get_frame(current_video_time) # Convert numpy frame to Pygame surface (swap axes: MoviePy uses (height, width) vs Pygame's (width, height)) frame_surface = pygame.surfarray.make_surface(frame.swapaxes(0, 1)) # Calculate position to center the frame (only needed for Option 1) x_pos = (TARGET_W - resized_clip.size[0]) // 2 y_pos = (TARGET_H - resized_clip.size[1]) // 2 # Blit to screen and update display screen.blit(frame_surface, (x_pos, y_pos)) pygame.display.flip() # Update time tracking current_video_time += 1 / video_fps last_frame_time = now clock.tick(60) # Keep the loop running smoothly # Cleanup resources resized_clip.close() pygame.quit()
Key Tips
swapaxes(0,1)is critical: MoviePy returns frames as(height, width, channels)arrays, but Pygame expects(width, height)—this fixes the orientation.- Frame rate sync: Using
pygame.time.get_ticks()ensures the video plays at its native speed, avoiding choppy playback. - Resource cleanup: Always call
clip.close()to free up memory after the video finishes.
内容的提问来源于stack exchange,提问作者Moe




