如何截取pygame屏幕?
我该如何捕捉并保存pygame屏幕上的一系列图片或视频呢?
简单来说,我想把我的游戏视频分享在YouTube上,还想制作一个教程。
这个游戏主要是在一个循环中运行的:
def main():
while True:
GetInput()
Move()
Shift()
Draw()
在这个循环中,Draw()
函数负责处理所有的blit()
操作,然后再执行pygame.display.flip()
。
4 个回答
当然可以!请看下面的内容:
在编程中,有时候我们需要让程序在特定的条件下执行某些操作。这就像给程序设定了一些规则,只有当这些规则被满足时,程序才会继续运行。比如说,如果你想让程序在用户输入正确的密码后才能进入系统,你就需要设置一个条件来检查这个密码。
此外,程序还可以根据不同的情况做出不同的反应。这就像是你在生活中遇到不同的情况时会有不同的选择。例如,如果天气很好,你可能会选择去外面玩;但如果下雨了,你可能会选择待在家里看电影。在编程中,我们可以使用“如果...那么...”这样的结构来实现类似的逻辑。
总之,编程就是在给计算机下达指令,让它根据我们的要求去做事情。通过设置条件和规则,我们可以让程序更加智能和灵活。
希望这些解释能帮助你更好地理解编程的基本概念!
x3 = pygame.surfarray.pixels3d(screen)
x3 = x3[:,:,::-1]
被接受的答案提到你可以用 pygame.image.save
方法来保存当前的屏幕。
这个主意不错,不过在游戏运行的时候,你可能不想把图片保存到硬盘上,这个问题也被提到了。相反,你应该在程序中保存屏幕,然后在游戏停止后再处理这些屏幕。
下面是我的代码,里面有注释,主要展示了屏幕录制的基本思路。它使用了 opencv(配合 numpy)和 pygame,但你需要安装 ffmpeg 来把图片转换成视频(可以在终端试试 ffmpeg
命令)。不要让程序运行太久,因为保存的过程还是需要一些时间,而且大约和录制的帧数成正比。为了提高效率,你可以只录制每隔一帧的画面。
from contextlib import contextmanager
import os
import time
import cv2
import numpy as np
import pygame as pg
from pygame import surfarray
def pg_to_cv2(cvarray:np.ndarray)->np.ndarray:
cvarray = cvarray.swapaxes(0,1) #rotate
cvarray = cv2.cvtColor(cvarray, cv2.COLOR_RGB2BGR) #RGB to BGR
return cvarray
def timer_wrapper(func):
def inner(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
#print("Finished:" ,func.__name__ ,end-start)
return end - start
return inner
@contextmanager
def video_writer(*args,**kwargs):
video = cv2.VideoWriter(*args,**kwargs)
try:
yield video
finally:
video.release()
@timer_wrapper
def save_frames(frames: list, average_dt: float|list, file_type: str = "mp4", name: str = "screen_recording"):
if type(average_dt) is list: average_dt = sum(average_dt)/len(average_dt) # force average_dt to be a float
size = frames[0].get_size()
codec_dict={
"avi":'DIVX',
"mp4":'MP4V'
}
codec = cv2.VideoWriter_fourcc(*codec_dict[file_type])
with video_writer(name+"."+file_type, codec, 1000/average_dt, size) as video: # file_name, codec, average_fps, dimensions
for frame in frames:
try:
pg_frame = surfarray.pixels3d(frame) # convert the surface to a np array. Only works with depth 24 or 32, not less
except:
pg_frame = surfarray.array3d(frame) # convert the surface to a np array. Works with any depth
cv_frame = pg_to_cv2(pg_frame) # then convert the np array so it is compatible with opencv
video.write(cv_frame) #write the frame to the video using opencv
def draw_fps(s:pg.Surface,clock:time.Clock):
fps = clock.get_fps()
sysfont.render_to(s,(100,100),str(fps),fgcolor=nice_green)
# initializing globals (colors, fonts, window, etc.)
pg.init()
sysfont = pg.freetype.SysFont(None,40)
BLACK = (0,)*3
nice_green = pg.Color("chartreuse2")
size=(1000, 600)
pg.display.set_caption("Screen Recording")
window = pg.display.set_mode(size)
# this is to save the frames
frames = []
dts = []
clock = pg.time.Clock()
running=True
try:
while running:
dt = clock.tick(60) # aim for ... fps
for event in pg.event.get():
if event.type == pg.QUIT:
running=False
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
running=False
window.fill(BLACK)
draw_fps(window,clock)
window_copy = window.copy() # if we don't copy the window then we will have the same image in all frames at the end
frames.append(window_copy) # We save the current frame together with the time passed between the frames
dts.append(dt)
pg.display.flip()
#if len(frames) >= 100: running = False # uncomment this to stop the game after ... frames for similar results in every run"
finally:
pg.quit()
# At this stage, the game ended and the cleanup process can start. For this we convert the frames to opencv images
# Only then we will write the video to the hard drive (That is what makes the save so slow).
# General information about the recording
frame_num = len(frames)
dt_sum = sum(dts)
average_dt = dt_sum/frame_num
# This is only an approximation:
# for each frame we have width * height many pixels -> frame_num * width * height
# A Surface needs get_bytesize() many bytes per pixel (In this case 4 bytes, because we set the depth of the display to 32 bits)
memory_usage_approx = frame_num * size[0] * size[1] * frames[0].get_bytesize() #https://www.pygame.org/docs/ref/surface.html#pygame.Surface.get_bytesize
print("Total time:" , dt_sum/1000,"s")
print("Average time per frame:" , average_dt,"ms")
print("Number of frames:", frame_num)
print("Memory usage approximation" , memory_usage_approx/1000, "KB")
args = (frames,dts,"avi","screen_recording")
time_for_save = save_frames(*args)
file_name = args[3]+"."+args[2]
video_memory_usage = os.path.getsize(file_name)
print("Video memory usage:" , video_memory_usage/1000, "KB")
with open("test.txt", "a") as f:
print("Total time:" , dt_sum/1000,"s\nNumber of frames:", frame_num,"\nSize:",size,"\nTime for save:",time_for_save,"s\nSaved in file:",file_name,file=f)
print("_"*100,file=f)
或者你可以直接使用一个库,比如 Pygame Recorder。
在你的屏幕表面上使用 pygame.image.save
来保存图像:
window = pygame.display.set_mode(...)
...
pygame.image.save(window, "screenshot.jpeg")
需要注意的是,这样做会让你的程序变得非常慢。如果你的程序是基于时间的,可能在捕捉图像时需要伪装一下帧率。