Greetings,
CONTEXT:
I’ve been fooling around lately with some platform layer and custom hot-reloading stuff. Basically write the entire ‘game’ code externally, compile in VS to a DLL, and load the functions in Unity via reflection. The DLL should sit outside of the “Assets” folder so that Unity doesn’t recompile when the DLL changes (yes I could use the Assembly locking API but I’d rather not complicate things if I could help it)
PROBLEM:
No matter what I do, I can’t seem to be able to debug/step into my code from Unity even if I manually generate an mdb file for the DLL. If however, I throw the DLL in Unity and try to step, it works just fine.
The only way I was able to generate an mdb is by using JB Evain’s tool here: [.[/URL]
All the other tools in mono/bin or mono/lib or MonoBleedingEdge or whatever, they all crash horribly with different type of exceptions/errors (e.g. “Runtime critical type System.RuntimeType not found”)
It’s very frustrating because the mdb that JB’s tool generate, is exactly the same size of the one that Unity generates when I throw the DLL in the Assets folder.
And yes I’ve tried testing without having any coroutines in the DLL cause I read about some issues regarding IEnumerator, didn’t help.
And I also tried putting the DLL under Assets, have Unity generate the .mdb for me, copy that .mdb to my external directory where my DLL exists, delete the DLL I just put in Assets, try debug, same issue.
I’m baffled because the code that you normally write in Unity gets compiled to an external DLL and put under Library/ScriptAssemblies anyways (which you could step into of course, with its own .mdb), so it’s not like I’m trying to do anything special here. I did try and put my DLL under ScriptAssemblies but Unity refreshes that folder everytime it recompiles, and no I was still not able to step even when the DLL was in there.
USING:
VS 2015 Community Edition
Unity 2017 1.1p2
QUESTION:
How can I actually step into a dll that’s located outside of the Assets folder?
CODE:
In Unity:
using System;
using System.IO;
using System.Reflection;
using UnityEngine;
public delegate void game_func_update(float dt);
public delegate void game_func_init();
public delegate void game_func_reload();
public class GameCodeDef
{
public Assembly DLL;
public game_func_init Game_Init;
public game_func_update Game_Update;
public game_func_reload Game_Reload;
public bool IsValid;
}
public class Game : MonoBehaviour
{
GameCodeDef GameCode;
void Start()
{
GameCode = LoadGameCode();
GameCode.Game_Init();
}
[ContextMenu("Reload")]
void ReloadGameCode()
{
GameCode = LoadGameCode();
GameCode.Game_Reload();
}
GameCodeDef LoadGameCode(string Filename="Game.dll")
{
GameCodeDef Result = new GameCodeDef();
string AssetsPath = Application.dataPath;
DirectoryInfo RootDir = Directory.GetParent(AssetsPath);
string GameDLLPath = RootDir.FullName + "/GameDLL/" + Filename;
try
{
byte[] DLLBytes = File.ReadAllBytes(GameDLLPath);
Result.DLL = Assembly.Load(DLLBytes);
Type GameType = Result.DLL.GetType("Game");
BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
MethodInfo InitMethod = GameType.GetMethod("Game_Init", Flags);
Result.Game_Init = Delegate.CreateDelegate(typeof(game_func_init), InitMethod) as game_func_init;
MethodInfo UpdateMethod = GameType.GetMethod("Game_Update", Flags);
Result.Game_Update = Delegate.CreateDelegate(typeof(game_func_update), UpdateMethod) as game_func_update;
MethodInfo ReloadMethod = GameType.GetMethod("Game_Reload", Flags);
Result.Game_Reload = Delegate.CreateDelegate(typeof(game_func_reload), ReloadMethod) as game_func_reload;
Result.IsValid = true;
}
catch (Exception e)
{
Debug.LogError("Could not load Game.dll: " + e);
Result.Game_Update = (float dt) => { Debug.Log("Dummy Update"); };
Result.Game_Init = () => { Debug.Log("Dummy Init"); };
Result.Game_Reload = () => { Debug.Log("Dummy Reload"); };
Result.IsValid = false;
}
return(Result);
}
void Update()
{
float dt = Time.deltaTime;
GameCode.Game_Update(dt);
}
}
In the DLL:
using System;
using UnityEngine;
using Random = UnityEngine.Random;
public class PlayerDef
{
public string Name;
public int Id;
public Transform Transform;
public override string ToString()
{
return string.Format("Name={0}, Id={1}", Name, Id);
}
}
public class GameStateDef
{
public PlayerDef Player;
public float T;
}
public class Game
{
static GameStateDef GameState;
public static void Game_Update(float dt)
{
if (Input.GetKeyDown(KeyCode.P))
{
Debug.LogError("Player: " + GameState.Player);
}
if (Input.GetKeyDown(KeyCode.R))
{
GameState.Player.Id = Random.Range(1, 100);
GameState.Player.Name = (new string[] { "ALI", "JEFF", "JON", "PER"})[Random.Range(0, 4)];
}
Transform Target = GameState.Player.Transform;
float Speed = 2;
Vector3 OP = Target.localPosition;
Vector3 TP = OP + Random.insideUnitSphere * 10;
if (GameState.T < 1)
{
Target.position = Vector3.Lerp(OP, TP, GameState.T);
GameState.T += Time.deltaTime * Speed;
}
else
{
GameState.T = 0;
}
}
public static void Game_Init()
{
Debug.LogError("Game_Init");
GameState = new GameStateDef();
GameState.Player = new PlayerDef();
GameObject PlayerObject = GameObject.CreatePrimitive(PrimitiveType.Sphere);
GameState.Player.Transform = PlayerObject.transform;
}
public static void Game_Reload()
{
Debug.LogError("Game_Reload");
}
}
Cheers!](pdb2mdb for Visual Studio 2015 · GitHub)