Asset for Visual Studio Debugging and Multiplayer

Hey guys, I’ve developing what I think is an awesome workflow for Unity projects, and I’d like to get your feedback to help me understand the best way to release this to the Unity Asset Store.

With this asset, you organize your code into two layers: gameplay and graphics. Then you can build and debug your gameplay code directly in Visual Studio. That is, you use Visual Studio’s built-in debugger to debug your gameplay code. You press F5 to launch, then ALT-Tab over to Unity to see your game running. Graphics code runs in Unity, and gameplay runs in your C# Visual Studio app. The gameplay and graphics layers send messages to to each other using sockets.

Let’s remember what the built-in Visual Studio debugger gives us:

  • edit, build, and debug in one place
  • not having to manually attach/detach a debugger
  • lag-free debugging
  • very few debugger bugs, debugger does not lock-up and neither does Unity
  • F5 to launch. Debugger does not really affect the launch time or execution of the app
  • see the values of just about any object (never getting “object has been unloaded”)
  • ability to move the instruction pointer (Set Next Statement)
  • ability to debug threads other than the main one
  • I assume the built-in MonoDevelop debugger has most of these features too. I’m talking about the normal built-in MonoDevelop debugger opposed to the Unity’s version of MonoDevelop debugging.

Another cool thing is that this asset lets you code and debug mobile apps without having to constantly redeploy to mobile. You work right there in VS, press F5 and see the results on your phone. If you have to make any changes to your graphics layer, then yes, you need to redeploy to your mobile device.

When it comes time to deploy your app to customers, the gameplay layer can be bundled with the graphics layer in a single Unity-based app. Or you can keep the layers separate, for a server or cloud-based app.

Since your code is organized with gameplay being separate from graphics, adding co-op or pvp multiplayer support to your game is much easier. Gameplay code runs in a single server instance which all clients share. The server instance can be standalone or bundled with a client. If you bundle the server with your client, your multiplayer mode is server-less (something like p2p).

Cloud-based computing is a form of a standalone server. The new consoles coming out this year are supposed to have some cool cloud support. This asset makes it easier to setup your game to run in the cloud.

Cloud/server-based gaming is cool for a bunch of reasons:

  • cloud computing power can do processing on behalf of a weak device (like a weak phone or tablet)
  • updating server-based software means updating when you want, not having to wait for an appstore review, and customers don’t need to update
  • software running in the cloud has inherent protection against piracy

Ok, here are some details on workflow:

When you create a new component, the asset makes two new classes instead of one. One is for your C# gameplay layer, one is for the graphics layer. The graphics class inherits MonoBehaviour and works just any other Unity script. The gameplay class is normal C# with no dependencies on Unity. It can be built in Unity or built by another C# compiler. The two classes are automatically linked together. When the graphics version object is instantiated in Unity, the gameplay version gets automatically instantiated (whether in the same process or on a remote server). You can send messages between the two classes using a simple function call.

I’m using the term “server” for the gameplay layer, and “client” for the graphics layer. The client components have not only graphics code but input and sound too. They have Unity’s physics and anything else Unity provides. Server code can be everything else.

A negative of this asset is that you have to think about two layers of your code instead of one. For example, if there’s an OnCollision event that a server component wants to know about, you have to include some code to forward this event to the server component.

I personally think the separation of graphics and non-graphics is a positive as it is a nice way to organize your code. This is actually how this asset started out. I was organizing my code into graphics and non-graphics. When I got frustrated with Unity’s debugger, I had the idea of extending my codebase so that I could debug my non-graphics code in VS.

I’m planning on releasing this to the Unity Asset Store and I’d love to hear your feedback. Also, what would be a good name for this asset?

Simple Client Class (Graphics Layer) Example

//using Core.Server; not allowed here
using UnityEngine; // ok only in graphics classes
using System;
using Core;

namespace Core.Client
{
    public class JumperU : CoreNetComponentU<JumperDef> // CoreNetComponentU inherits MonoBehaviour
    {
        public Rigidbody Rigidbody;

        public override void OnInit() // all core components have been created
        {
            Rigidbody = this.rigidbody;

            if(Rigidbody == null)
                Error("Jumper must have a Rigbody script: " + Core.Entity.ToString());
        }

        [MsgReceiver(Msgs.Jump)]
        public void OnJump()
        {
            Rigidbody.AddForce(new Vector3(0, 100.0f * Def.JumpHeight, 0));
        }
    }
}

Simple Server Class (Gameplay Layer) Example

//using Core.Client; not allowed here
using System.Collections.Generic;
using System;
using Core;

namespace Core
{
    [NetSerialize][Serializable] // Serializable to show in Unity editor
    public class JumperDef : CoreComponentDef
    {
        public float JumpHeight = 3.0f;
        public float JumpRate = 4.0f;
    }
}

namespace Core.Server
{
    public class Jumper : CoreNetComponent<JumperDef>
    {
        public override void OnInit() // all core components have been created 
        {
            if(Def.JumpHeight > 0)
            {
                Timeline.AddEvent(UpdateJump, Def.JumpRate);
            }
        }

        public float UpdateJump()
        {
            Send(Msgs.Jump);
            return Def.JumpRate;
        }
    }
}

Pretty interesting way of doing things. I can see this being useful but would require some changes in coding practices. I am not sure how many people would be willing to do that but it is an interesting work around you have.

Dane