Hey Guys, Happy New Year by the way! I am trying to print the end results of my mini-game from WebGL, at the moment I have tried using a .jslib and C# which fires my javascript. My javascript so far has been: Window.Print() opens dialog and prints blank page. and a more elaborate: Code (JavaScript): var dataUrl = document.getElementById('canvas').toDataURL(); //attempt to save base64 string to server using this var var windowContent = '<!DOCTYPE html>'; windowContent += '<html>' windowContent += '<head><title>Your Wheel of Life</title></head>'; windowContent += '<body>' windowContent += '<img src="' + dataUrl + '">'; windowContent += '</body>'; windowContent += '</html>'; var printWin = window.open('','','width=1134,height=638'); printWin.document.open(); printWin.document.write(windowContent); printWin.document.close(); printWin.focus(); printWin.print(); printWin.close(); That also just opens a new dialog and prints a blank page. So does anyone have any suggestions because I am a loss? Cheers, Jordan.
Hello QuantumRevenger. The reason why you are getting a blank page is because the canvas drawing buffer is empty at the moment of the image capture. Your code would only work reliably if the canvas webgl context was created with preserveDrawingBuffer flag. For example, you could add the following line to your game js Code (JavaScript): ... ,createContext:(function(canvas,webGLContextAttributes){ webGLContextAttributes.preserveDrawingBuffer=true; if(typeof webGLContextAttributes.majorVersion==="undefined"&& ... and this would guarantee reliable webgl canvas capture with .toDataURL() at any given moment. However, this would also affect the game performance and therefore is not recommended. Luckily, Unity provides you with a mechanism to determine an appropriate moment for the frame capture. The following adjustment would make your code work: Code (CSharp): [DllImport("__Internal")] private static extern void PrintFrameJs(); IEnumerator PrintRoutine() { yield return new WaitForEndOfFrame (); PrintFrameJs (); } void PrintFrame () { StartCoroutine (PrintRoutine ()); } Moreover, you can go even further and make your code even more reliable, avoiding js toDataURL call, and providing the plugin function with already prepared image data: Code (CSharp): [DllImport("__Internal")] private static extern void PrintFrameJs(byte[] img, int size); IEnumerator PrintRoutine() { yield return new WaitForEndOfFrame (); Texture2D tex = new Texture2D (Screen.width, Screen.height); tex.ReadPixels (new Rect(0, 0, Screen.width, Screen.height), 0, 0); tex.Apply (); byte[] img = tex.EncodeToPNG(); PrintFrameJs (img, img.Length); } void PrintFrame () { StartCoroutine (PrintRoutine ()); } which can be handled with the following plugin: Code (JavaScript): var PrintPlugin = { PrintFrameJs: function(img, size) { var binary = ''; for (var i = 0; i < size; i++) binary += String.fromCharCode(HEAPU8[img + i]); var dataUrl = 'data:image/png;base64,' + btoa(binary); // use dataUrl for printing etc. }, }; mergeInto(LibraryManager.library, PrintPlugin); And Happy New Year by the way!
Hello Alexsuvorov, Thank you very much for the advice above, my client is very happy with the end product. I have encountered another snag when it was uploaded onto the site, how do I make the canvas scale with browser size and retain a 16:9 aspect ratio? Kind regards, Jordan.
document.getElementById('canvas') doesnt work anymore in unity 5.6, returns null. all my Javascript plugin code is now broken!!
Hello nsmith1024. In Unity 5.6 you can access the game canvas in the following ways: a) From a .jslib plugin it should be accessible as Module.canvas b) From the embedding page it should be accessible as gameInstance.Module.canvas, where "gameInstance" is the game instance the canvas belongs to. P.S. You might also want to check the alternative solution using ReadPixels (described above), which is more reliable.
@alexsuvorov Do you know how to set preserveDrawingBuffer=true for a Unity 2017.2? I cannot do your second option of triggering the right time to print at the end of the build because my application of this is for reporting unity crashes to my API. I have a js program that overrides the js alert to trigger a new HTML screen, API call, and hopefully send a screenshot to my API if I can figure it out. Thanks!
The default value of webglContextAttributes.preserveDrawingBuffer is now defined in the generated json file and you can override it at instantiation time: Code (csharp): var gameInstance = UnityLoader.instantiate("gameContainer", "%UNITY_WEBGL_BUILD_URL%", {onProgress: UnityProgress, Module: { webglContextAttributes: {"preserveDrawingBuffer": true}, } });
Can you at some point document all those module options that we can set or override? https://docs.unity3d.com/Manual/webgl-templates.html Now we need to reverse engineer or search or ask in the forums to see an answer.
@Marco-Trivellato Also is there any performance implications from setting preserveDrawingBuffer to true? Is there any downsides to setting this to true?
Here is that the WebGL specs say: "While it is sometimes desirable to preserve the drawing buffer, it can cause significant performance loss on some platforms. Whenever possible this flag should remain false and other techniques used." You may want to try with your project on different browsers to see if performance becomes an issue.
@alexsuvorov @Marco-Trivellato , I had a question. Would it be possible to snap a screenshot of the application without using the preserveDrawingBuffer? On the Three.js forums they say that if you run the ReadPixels/toDataUrl directly after a render() the image is not yet cleared and you can retrieve it. Would be possible, without changing anything to the source of the game, to tap into this render moment? I tried things like running a command in the Module.postMainLoop without success. I see a lot of things are being exposed on window.unityGame, but I'm not sure where to look for this render moment. Thanks in advance for your answer!