Search Unity

Unity Windows Standalone Launcher - Run your game directly from your own standalone launcher

Discussion in 'Windows' started by RedicionStudio, Dec 22, 2020.

  1. RedicionStudio

    RedicionStudio

    Joined:
    Sep 11, 2016
    Posts:
    11
    If you want to launch your game directly from your own winform game launcher that you created in visual studio then follow these steps below:

    1. If you build your game, select "x86_64" as the architecture and windows as the target platform in your standalone unity project build settings.


    2. Create a new C# Windows Form App in Visual Studio:


    3. Add a button to your form from where you want to start your game:


    4. Display the code of your form:


    5. Now you need to use the Interop services, because you need to call unmanaged code from the "UnityPlayer.dll". So add the "System.Runtime.InteropServices" using directive:


    6. Use the "DllImport" attribute to import the "UnityPlayer.dll", then you have to declare the "UnityMain" method and add the [MarshalAs(UnmanagedType.LPWStr)] attribute to it.


    7. Add the "System.Diagnostics" using directive:


    8. Now add the code below to your "Start Game" button so that the "UnityPlayer.dll" can execute your game process in fullscreen mode.


    9. Open the app properties and select "Any CPU" and uncheck "Prefer 32-bit" in the general settings of the "Build" tab.


    10. Build your game launcher application.

    11. After a successful build, copy your built game launcher executable into your game directory where your "UnityPlayer.dll" and "_Data" directory are located.

    Very important: If your built launcher executable is called
    e.g. "Game_Launcher", your "_Data" directory must be named "Game_Launcher_Data" (If they are not identical, the game will not be executed).

    12. Now, start your built game launcher and press the "Start Game" button to execute your game.
    (If you quit your game with
    Application.Quit(); your launcher will close with all hidden forms as well.)

    Your code should look exactly like this:

    Code (CSharp):
    1. using System;
    2.  
    3. using System.Diagnostics;
    4. using System.Runtime.InteropServices;
    5. using System.Windows.Forms;
    6.  
    7. namespace Game_Launcher
    8. {
    9.     public partial class Form1 : Form
    10.     {
    11.         [DllImport("UnityPlayer")]
    12.         private static extern int UnityMain(IntPtr hInstance, IntPtr hPrevInstance,
    13.             [MarshalAs(UnmanagedType.LPWStr)]ref string lpCmdline, int nShowCmd);
    14.  
    15.         public Form1()
    16.         {
    17.             InitializeComponent();
    18.         }
    19.  
    20.         private void Start_Game_Button_Click(object sender, EventArgs e)
    21.         {
    22.             var commandArgs = "-screen-fullscreen";
    23.             UnityMain(Process.GetCurrentProcess().Handle, IntPtr.Zero, ref commandArgs, 1);
    24.  
    25.             this.Hide();
    26.         }
    27.     }
    28. }
    I hope that everything works for you, do not forget to like this thread and write a comment.;)


    Credits:
    These scripts, codes, method and the post was created by Florian Lauka from Redicion Studio.
     

    Attached Files:

    Last edited: Feb 22, 2021
  2. Mistak

    Mistak

    Joined:
    Sep 9, 2018
    Posts:
    1
    Thx, but commandline arguments isn`t work by this way
    Because this :
    Code (CSharp):
    1.  private static extern int UnityMain(IntPtr hInstance, IntPtr hPrevInstance,
    2.       [MarshalAs(UnmanagedType.LPWStr)]ref string lpCmdline, int nShowCmd);
    Rightly must look like this :
    Code (CSharp):
    1.  private static extern int UnityMain(IntPtr hInstance, IntPtr hPrevInstance,
    2.       [MarshalAs(UnmanagedType.LPWStr)] string lpCmdline, int nShowCmd);
    Without ref before "string lpCmdline"
     
    Last edited: Jun 13, 2021
    atcarter714 likes this.
  3. NiklasMoller

    NiklasMoller

    Joined:
    Dec 12, 2018
    Posts:
    6
    @RedicionStudio Nice job! Will this work as well for a WPF App in .NET Core?
     
  4. atcarter714

    atcarter714

    Joined:
    Jul 25, 2021
    Posts:
    65
    Of course, it's C#! You're just using the built-in PInvoke interop system it's had since way back in the old days! It's really, really powerful and pretty easy to do. :)

    You simply use the DllImportAttribute to specify what native DLL you want to call from C# code (which can be pretty much any valid DLL library with exported "C-style" symbols -- from Window's kernel32.dll to the UnityPlayer.dll for your game build, or even native libraries on Android/iOS and other systems, all the way to your own custom C/C++ libraries that you write yourself) ... the attribute is applied to a method you declare and mark as static extern and create the correct method signature for. The "signature" of a method/function is just what we call the combined characteristics of its name, return type and parameter list (and in C# and C++ that can also include access modifiers and other modifiers like "static" and "const").

    PInvoke is super powerful and not really all that hard to use for the basic, direct calling techniques for extern "C-style" functions used here: the most basic and standard way to interoperate from C# to native code. It just requires you to read the documentation a bit to understand the rules and limitations. It also has the capability to do some really complex and fancy things which require a good bit of low-level knowledge to learn to effectively wield, but the possibilities are all there. C# supports pointers and "unsafe" code and can do a lot of things people don't often think about. There's even things like COM interop and "runtime-callable wrappers", reverse COM interop, WinRT (Windows Runtime) language projections, C++/CLR interop and numerous other advanced interop techniques beyond PInvoke. I've always really loved the C# interop services and how you can "talk to" external code in native executables, or even invoke some Javascript ... and the Javascript can even be held in a string in your C# class!

    All the small example here is doing is calling the UnityPlayer.dll entry point called "UnityMain", and it matches the signature: and it uses special attributes to give the compiler and runtime some additional hints about how C# and C/C++ types should be marshaled ("marshaling" is basically just a fancy word for "translating" or converting the data from one form to another so it works in both ecosystems and can be passed back and forth).

    So you can definitely do the same thing in WPF, if you want to. Only big limitation I see here is that it is, of course, probably only going to work on Windows and it's not cross-platform. Winforms and WPF have that inherent platform limitation. That issue, however, could potentially be solved in a MAUI application for .NET 6 or 7 ... not sure, I'd really have to try and see if I run into any show-stopping problems, but I know there's at least some way it can be done with enough effort and finesse! We would have to find out how Unity's build system packages the player binary for each of the other target-able platforms like Android, iOS, Mac, Linux, etc and then we'd have to create the appropriate PInvoke signatures to work with it to account for any platform-specific differences. It may be more or less the same thing on other platforms, however, if not identical. Won't know until I investigate that! You can go even beyond this and write some C++ code that will create the Unity Player literally inside of a Win32 window or another native GUI shell or window system. I just read a bit about it the other day and plan to try that soon enough ... ideally, I'd be able to wrap that in C# interop code and do it straight from a managed Windows application. Apparently, there's not a lot of documentation available and there are some limitations and issues on platforms other than Windows, but it sounded like something that could be really powerful and full of major potential, and I'll be doing it eventually (when time allows) to figure it all out.

    It might even be worth making a more in-depth tutorial about PInvoke and C# interop services, if people would be interested in it ... there's so many crazy powerful things you can do with interop that it truly blows the mind. One of my personal projects is creating my own .NET 6 and 7 interop layer for the DirectX 11 and 12 APIs so I can use straight-up, native DirectX in a C# project (and it's the supporting "glue" for an engine I'm building). It already works surprisingly well and I'm preparing it for a preview release and will make it free and open source for the C# community. I also try to pitch in and help the Microsoft engineers working on the WinMD (Windows SDK Metadata) project and the CsWin32 code generator for C# projects -- these things let us not only use the whole, entire Windows SDK in C#, it also auto-generates the PInvoke and COM interop code for you automagically! o_O

    If people are interested in learning more about PInvoke and interoperation, leveraging native code in C# and Unity applications and the idea of embedding a Unity Player instance into C# and C++ applications, let me know ... that's something I think is probably well worth doing some videos and projects about.
     
  5. GeekBrony

    GeekBrony

    Joined:
    Feb 17, 2017
    Posts:
    7
    @atcarter714 You'd probably be the best person to ask about this then.
    I'm trying to link a Unity build to my C# program so I can spin up headless shard instances of my servers.

    PInvoke works wonders on Windows. However, I target Linux and macOS server builds as well.
    While debugging my C# code on Mac (with Rider), I can't seem to find the
    UnityEngine
    assembly.
    Is there any way I can reliably invoke that from this setup?
     
    atcarter714 likes this.
  6. atcarter714

    atcarter714

    Joined:
    Jul 25, 2021
    Posts:
    65
    Interesting question. I have limited experience with MacOS and iOS, or real-world servers, but I do know that PInvoke and interop services does indeed work on other platforms in .NET Core (worked for me in 5, 6 and 7) and, theoretically, it should work fine from Unity/Mono, as it is necessary for so many things to work properly. A lot of .NET APIs PInvoke into native libraries provided by the OS. So, I suspect you're having some other issues here ...

    I'd need to see what the file system (folder/file) structure of your build looks like, your PInvoke signature in the C# class and some other details to give you more precise advisory. And you can pm me or hit me up on Discord if you like, I'll send my Discord username# to you.