Quick answer: Capture the screen contents into a texture when the bug report UI opens, before any overlay is drawn. In Unity, use ScreenCapture.CaptureScreenshotAsTexture(). In Godot, use get_viewport().get_texture().get_image(). In Unreal, use FScreenshotRequest or read from the viewport render target.
This guide covers automated screenshot capture for game bug reports in detail. A bug report that says “the texture is glitched on the wall in the cave” becomes ten times more useful with a screenshot. Instead of guessing which cave, which wall, and what kind of glitch, the developer sees exactly what the player saw. Automated screenshot capture removes the burden from the player — they press the bug report button and the game captures the screen automatically. Here is how to implement this in Unity, Godot, and Unreal with proper timing, compression, and privacy handling.
The Timing Problem
The most important detail in screenshot capture for bug reports is when you capture. If you capture after showing the bug report overlay, the screenshot includes the overlay. If you capture too early, the screenshot may show a different state than what the player saw when they decided to report.
The correct approach is: when the player presses the bug report key, capture the current frame’s render output immediately, store it in memory, and then show the bug report UI on the next frame. The stored screenshot reflects the game state at the moment the player hit the button, without any overlay.
Some implementations use a rolling buffer of the last N frames, so the screenshot captures the moment before the player opened the report UI. This is slightly better because the player may have paused or looked away before pressing the button, and the issue was visible a few frames earlier.
Unity Implementation
Unity provides ScreenCapture.CaptureScreenshotAsTexture() which captures the screen at the end of the current frame. You can also read from a RenderTexture for more control over what is captured.
using UnityEngine;
using System.Collections;
public class BugReportScreenshot : MonoBehaviour
{
private Texture2D _capturedScreenshot;
private byte[] _screenshotBytes;
// Call this when the player presses the bug report key
public void CaptureAndShowReportUI()
{
StartCoroutine(CaptureScreenshot());
}
private IEnumerator CaptureScreenshot()
{
// Wait for end of frame so the render is complete
yield return new WaitForEndOfFrame();
// Capture the screen into a Texture2D
int width = Screen.width;
int height = Screen.height;
if (_capturedScreenshot != null)
Destroy(_capturedScreenshot);
_capturedScreenshot = new Texture2D(width, height,
TextureFormat.RGB24, false);
_capturedScreenshot.ReadPixels(
new Rect(0, 0, width, height), 0, 0);
_capturedScreenshot.Apply();
// Encode to JPEG (smaller file size)
_screenshotBytes = _capturedScreenshot.EncodeToJPG(85);
Debug.Log($"Screenshot captured: {_screenshotBytes.Length} bytes");
// Now show the bug report UI on the next frame
ShowBugReportUI();
}
public byte[] GetScreenshotBytes()
{
return _screenshotBytes;
}
}
For higher quality or specific camera angles, use a RenderTexture:
public byte[] CaptureFromCamera(Camera cam, int width, int height)
{
RenderTexture rt = new RenderTexture(width, height, 24);
cam.targetTexture = rt;
cam.Render();
RenderTexture.active = rt;
Texture2D screenshot = new Texture2D(width, height,
TextureFormat.RGB24, false);
screenshot.ReadPixels(new Rect(0, 0, width, height), 0, 0);
screenshot.Apply();
// Clean up
cam.targetTexture = null;
RenderTexture.active = null;
Destroy(rt);
byte[] bytes = screenshot.EncodeToJPG(85);
Destroy(screenshot);
return bytes;
}
Godot Implementation
In Godot, the viewport contains the rendered frame. Use get_viewport().get_texture().get_image() to capture the current frame as an Image object.
# bug_report_screenshot.gd
extends Node
var screenshot_bytes: PackedByteArray
var screenshot_image: Image
func capture_screenshot() -> void:
# Wait for the current frame to finish rendering
await RenderingServer.frame_post_draw
# Get the viewport texture as an Image
screenshot_image = get_viewport().get_texture().get_image()
if screenshot_image == null:
push_error("Failed to capture screenshot")
return
# Optionally resize for smaller file size
var max_width := 1920
if screenshot_image.get_width() > max_width:
var scale := float(max_width) / screenshot_image.get_width()
var new_height := int(screenshot_image.get_height() * scale)
screenshot_image.resize(max_width, new_height, Image.INTERPOLATE_BILINEAR)
# Encode to PNG
screenshot_bytes = screenshot_image.save_png_to_buffer()
print("Screenshot captured: %d bytes" % screenshot_bytes.size())
func get_screenshot_bytes() -> PackedByteArray:
return screenshot_bytes
func get_screenshot_as_texture() -> ImageTexture:
# For displaying a preview in the bug report UI
if screenshot_image:
return ImageTexture.create_from_image(screenshot_image)
return null
In Godot 4, you can also use RenderingServer.frame_post_draw as a signal to ensure you capture after the frame is fully rendered but before it is displayed. This avoids the timing issue where the screenshot might capture an incomplete frame.
Unreal Implementation
Unreal provides multiple approaches for screenshot capture. The simplest is FScreenshotRequest, which triggers the engine’s built-in screenshot system. For more control, read directly from the viewport render target.
// Simple approach: request a screenshot from the engine
void UBugReportSubsystem::CaptureScreenshot()
{
FScreenshotRequest::RequestScreenshot(
TEXT("BugReport"), false, false);
// The screenshot is saved to disk by default.
// To capture to memory instead, use the viewport approach below.
}
// Advanced approach: read from viewport render target
void UBugReportSubsystem::CaptureToMemory()
{
if (!GEngine || !GEngine->GameViewport) return;
FViewport* Viewport = GEngine->GameViewport->GetGameViewport();
if (!Viewport) return;
TArray<FColor> Bitmap;
FIntVector Size(Viewport->GetSizeXY().X,
Viewport->GetSizeXY().Y, 0);
bool bSuccess = Viewport->ReadPixels(Bitmap);
if (!bSuccess || Bitmap.Num() == 0)
{
UE_LOG(LogTemp, Error, TEXT("Failed to read viewport pixels"));
return;
}
// Compress to PNG on a background thread
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask,
[this, Bitmap, Size]()
{
TArray<uint8> CompressedData;
FImageUtils::CompressImageArray(
Size.X, Size.Y, Bitmap, CompressedData);
// Store for later attachment to bug report
ScreenshotData = MoveTemp(CompressedData);
UE_LOG(LogTemp, Log,
TEXT("Screenshot compressed: %d bytes"),
ScreenshotData.Num());
});
}
The viewport ReadPixels approach reads from the GPU, which causes a pipeline stall. For production use, consider capturing to a RenderTarget2D asynchronously using the GPU readback API to avoid frame hitches.
Compression and File Size
Raw screenshots at 1920x1080 with RGB24 are about 6 MB. That is too large to upload quickly, especially on slow connections. Compress before uploading.
JPEG at 85% quality produces 100–400 KB files. Good enough for most bug reports. Use JPEG for general gameplay screenshots.
PNG produces 500 KB–2 MB files but is lossless. Use PNG when exact pixel accuracy matters, such as for UI rendering bugs or texture artifact reports.
Downscale before encoding. If the game runs at 4K, a 3840x2160 screenshot is overkill for a bug report. Downscale to 1920x1080 before encoding. This cuts the file size dramatically and reduces encoding time.
Capture at full resolution, then downscale. Never upscale a screenshot — you lose the detail that makes the screenshot useful in the first place. If the player runs at 720p, send the 720p screenshot as-is.
Privacy Considerations
Screenshots can contain sensitive information: player names in chat, personal messages in multiplayer, screen names of other players, or even visible desktop content if the game runs in windowed mode.
Inform the player. Before capturing, display a notice that a screenshot will be included with the bug report. Provide an option to disable screenshot capture or to preview and approve the screenshot before sending.
Mask player names. In multiplayer games, consider drawing rectangles over other players’ name tags before encoding the screenshot. This protects other players’ privacy without removing useful visual context about the bug.
Exclude chat and social UI. If your game has a chat window, hide it before capturing the screenshot. Chat messages often contain personal information that has nothing to do with the bug being reported.
// Unity: hide sensitive UI before capturing
public IEnumerator CaptureWithPrivacy()
{
// Hide sensitive UI elements
chatPanel.SetActive(false);
playerNameLabels.SetActive(false);
// Wait one frame for the UI change to render
yield return null;
yield return new WaitForEndOfFrame();
// Capture the screenshot
var screenshot = ScreenCapture.CaptureScreenshotAsTexture();
_screenshotBytes = screenshot.EncodeToJPG(85);
Destroy(screenshot);
// Restore UI
chatPanel.SetActive(true);
playerNameLabels.SetActive(true);
ShowBugReportUI();
}
Uploading the Screenshot
Upload the screenshot asynchronously after the player submits the bug report. Never block the game thread on a network request. Show a progress indicator or let the upload happen in the background.
If the upload fails (network timeout, server error), store the screenshot locally and retry on the next application launch. Do not silently discard screenshots — they are too valuable to lose.
# Godot: async upload with retry
func upload_bug_report(description: String) -> void:
var http := HTTPRequest.new()
add_child(http)
var headers := ["Content-Type: multipart/form-data"]
var body := _build_multipart_body(description, screenshot_bytes)
http.request_raw(
"https://api.bugnet.io/v1/reports",
headers,
HTTPClient.METHOD_POST,
body
)
var result = await http.request_completed
if result[1] != 200:
# Save locally for retry
_save_report_locally(description, screenshot_bytes)
push_warning("Upload failed, saved locally for retry")
Related Issues
For a complete guide on integrating bug reporting SDKs that handle screenshot capture automatically, see our SDK integration guide. If you need to capture crash context along with screenshots, check automated crash reporting for indie games for crash dump collection strategies.
Capture the screenshot before showing the report UI. A screenshot of your own bug report form helps nobody.