some wise words
Want to be notified of new blog posts?


Elwin Verploegen

Email Bug Reports in Unity3D (With Automatic Screenshot)February 15, 2016

By Elwin

For the Fragments of Him PC Beta we wanted to make it easy for players to send in bug reports. We wanted this to do a few things that cover most of the questions that we usually have to ask when a bug report is filed:

  • Type up a bug report
  • Send in a screenshot
  • Attach system specs
  • Automatically email it to us

Luckily, all of this is possible using C#. The following script allows the user to press F1, after which it takes a screenshot and opens up a simple GUI where the user can type in an email address (or just something to identify the user with) and a bug report. When the user clicks the “Send” button the screenshot will be attached to the email and sent to an email account that we can easily access.

Warning: Your login details can be read as plain text with relative ease, only use this when sending builds to trusted testers or when using internally.

#if UNITY_STANDALONE

using UnityEngine;
using System.Collections;
using System;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public class Email : MonoBehaviour{

    //email setup
    private string from = "youremail@yourdomain.com";
    private string password = "yourpwd";
    private string smtp = "smtp.yourdomain.com";
    private string to = "report@tosendto.com";

    private bool isemailing = false;
    private string email = "";
    private string body = "";

    private string filepath = "";

    void Update(){
        if(Input.GetKeyDown(KeyCode.F1)){
            //take a screenshot when opening the form
            StartCoroutine(TakeScreenshot());
        }
    }

    void SetEmailing(bool dir){
        isemailing = dir;
        Time.timeScale = (dir) ? 0f : 1f;
    }

    void OnGUI(){
        if(isemailing){
            GUILayout.BeginArea(new Rect( 100, 100, 400, 600));
            
            GUILayout.Label("Your email: ");
            email = GUILayout.TextField(email, GUILayout.Width(150));

            GUILayout.Label("Bug report: ");
            body = GUILayout.TextArea(body, GUILayout.Height(250));

            if(GUILayout.Button("Send bug report", GUILayout.Width(150))){
                SendEmail();
            }

            GUILayout.EndArea();
        }
    }

    void SendEmail(){
        MailMessage mail = new MailMessage();

        //create the resource to be attached to the email
        LinkedResource inline = new LinkedResource(filepath);
        inline.ContentId = Guid.NewGuid().ToString();
        Attachment att = new Attachment(filepath);
        att.ContentDisposition.Inline = true;

        //create the content of the email
        mail.From = new MailAddress(from);
        mail.To.Add(to);
        mail.Subject = "Debug mail";
        mail.Body = String.Format(
                "Report by: " + email + "<br/><br/>" + 
                "Bug report:<br/>" + body + "<br/><br/><hr>" + 
                GetHardwareInfo() + "<br/><hr>" + 
                @"<img src=""cid:{0}"" />", inline.ContentId);
        mail.IsBodyHtml = true;
        mail.Attachments.Add(att);

        //set up the smtp
        SmtpClient smtpServer = new SmtpClient(smtp);
        smtpServer.Port = 587;
        smtpServer.EnableSsl = true;
        smtpServer.Credentials = new System.Net.NetworkCredential(from, password) 
            as ICredentialsByHost;
        ServicePointManager.ServerCertificateValidationCallback = 
            delegate(object obj, X509Certificate cert, X509Chain chain, SslPolicyErrors sslerrors) 
                { return true; };
        smtpServer.Send(mail);
        
        #if UNITY_EDITOR
            Debug.Log("email sent");
        #endif
        SetEmailing(false);
    }

    //return a string with some essential hardware info
    string GetHardwareInfo(){
        return    "Graphics Device Name: " + SystemInfo.graphicsDeviceName + "<br/>"
                + "Graphics Device Type: " + SystemInfo.graphicsDeviceType.ToString() + "<br/>"
                + "Graphics Device Version: " + SystemInfo.graphicsDeviceVersion + "<br/>"
                + "Graphics Memory Size: " + MBtoGB(SystemInfo.graphicsMemorySize) + "<br/>"
                + "Graphics Shader Level: " + ShaderLevel(SystemInfo.graphicsShaderLevel) + "<br/>"
                + "Maximum Texture Size: " + MBtoGB(SystemInfo.maxTextureSize) + "<br/>"
                + "Operating System: " + SystemInfo.operatingSystem + "<br/>"
                + "Processor Type: " + SystemInfo.processorType + "<br/>"
                + "Processor Count: " + SystemInfo.processorCount.ToString() + "<br/>"
                + "Processor Frequency: " + SystemInfo.processorFrequency.ToString() + "<br/>"
                + "System Memory Size: " + MBtoGB(SystemInfo.systemMemorySize) + "<br/>"
                + "Screen Size: " + Screen.width.ToString() + "x" + Screen.height.ToString();
    }

    //make the shader level more readable
    string ShaderLevel(int level){
        switch(level){
            case 20: return "Shader Model 2.x";
            case 30: return "Shader Model 3.0";
            case 40: return "Shader Model 4.0 ( DX10.0 )";
            case 41: return "Shader Model 4.1 ( DX10.1 )";
            case 50: return "Shader Model 5.0 ( DX11.0 )";
            default: return "";
        }
    }

    //unity returns a weird amount of available MB, this unifies it
    string MBtoGB(int mb){
        return Mathf.Ceil(mb / 1024f).ToString() + "GB";
    }

    //take a screenshot before opening the GUI
    IEnumerator TakeScreenshot(){
        yield return new WaitForEndOfFrame();
        #if UNITY_EDITOR
            filepath = Application.dataPath + "/../debugscreenshot.png";
        #else
            filepath = Application.dataPath + "/debugscreenshot.png";
        #endif

        Application.CaptureScreenshot("debugscreenshot.png");
        SetEmailing(!isemailing);
    }
}
#endif

You should be able to just copy/paste this script into your project (name the script Email.cs) and drop that on any GameObject in your scene. Press F1 to see it in action.

Couple of notes on the script:

  • It’s set up to wait for the screenshot to be taken, and only then will it open the email GUI. This is to ensure that the email GUI is not part of the screenshot. You can extend this to disable other elements in your game if you see fit.
  • You have to ask permission from the user to get their system data! While the data we get doesn’t contain anything identifying, you should always ask for permission (or in our case, if you accept our beta key you agree that we can use this data whenever you send in a bug report).
  • I’ve set it up so it’ll only work for Standalone builds (see first and last line of the script), I have no intention of using this for our Xbox builds, but you can always modify that to be available on other platforms, just make sure that the interface still works!
  • You may have to modify the SMTP port on line 81 to whatever your host uses.
  • You must set unity to use Api Compatability Level .NET 2.0. If you do not change this the script will throw an error once you send the email.

If you have any questions or recommendations, you can get in touch with me on Twitter @elwinverploegen.

Want to be notified of new blog posts?