How to use shared memory in Unity ?

Hello, I want to use shared memory in Unity.
I create flight Simulator.

Here system idea:

process A: Calculate x,y,z position of air plane, and send position data to shared memory.
(Language is C++, C#…?)

unity process: Get position data from shared memory, and translate air plane by new positon data.

processA → sharedMemory → Unity

I don’t know that Unity can use shared memory, or comunicate to any process.

Please help me!

@Kei_Jin I recently had to solve a similar issue myself.

I was using input from a custom USB controller so I had c++ code to handle its input. I created a shared memory location in a c++ program, I used CreateFileMapping since it is stored in ram and so gives the fastest refresh rate. The guides recommend using the global namespace, but that did not work for me, and simply removing it did work.

You will then need to read from that memory address in c#, this is a little complicated as you have to use unsafe code and pinvokes. Also unity will crash if you ever try to access the pointers but they haven’t successfully accessed the memory yet, so be sure to save the project before any testing.

As for transforming your data, just make sure you use the equivalent datatypes on both ends and use Marshal functions to access it. In your case maybe three floats would be best. You may need to cast them from int if there are no marshall float functions.

Useful links:

See code snippetts below for details.

c++ Code:

//For shared memory with Unity
#define BUF_SIZE 256
TCHAR szMapName[] = TEXT("UnityFileMappingObject");
TCHAR szCountName[] = TEXT("UnityFileCountObject");
signed int sharedMemOut[7];

LPCTSTR pBuf;
HANDLE hMapFile;

Init(){

//	 Request shared memory from the OS to share with Unity
	hMapFile = CreateFileMapping(
		INVALID_HANDLE_VALUE,    // use paging file
		NULL,                    // default security
		PAGE_READWRITE,          // read/write access
		0,                       // maximum object size (high-order DWORD)
		BUF_SIZE,                // maximum object size (low-order DWORD)
		szMapName);                 // name of mapping object

	if (hMapFile == NULL)
	{
		printf(TEXT("Could not create file mapping object (%d).

"),
GetLastError());
return -1;
}
pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);

	if (pBuf == NULL)
	{
		printf(TEXT("Could not map view of file (%d).

"),
GetLastError());
CloseHandle(hMapFile);
return -1;
}
}

Update(){
sharedMemOut[0] = (int)count;
	sharedMemOut[1] = X[0];
	sharedMemOut[2] = Y[0];
	sharedMemOut[3] = X[1];
	sharedMemOut[4] = Y[1];
	sharedMemOut[5] = SQ[0];
	sharedMemOut[6] = SQ[1];
	CopyMemory((PVOID)pBuf, sharedMemOut, (7 * sizeof(signed int)));
}

c# unity code:

using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

public class PlaneController: MonoBehaviour {
    //Shared Memory
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern SafeFileHandle OpenFileMapping(
         uint dwDesiredAccess,
         bool bInheritHandle,
         string lpName);
    
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr MapViewOfFile(
        SafeFileHandle hFileMappingObject,
        UInt32 dwDesiredAccess,
        UInt32 dwFileOffsetHigh,
        UInt32 dwFileOffsetLow,
        UIntPtr dwNumberOfBytesToMap);
    
    
    /*
        [DllImport("kernel32.dll", SetLastError = true)]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool CloseHandle(IntPtr hObject);
    */
        string szMapName = "UnityFileMappingObject";
    
        const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
        const UInt32 SECTION_QUERY = 0x0001;
        const UInt32 SECTION_MAP_WRITE = 0x0002;
        const UInt32 SECTION_MAP_READ = 0x0004;
        const UInt32 SECTION_MAP_EXECUTE = 0x0008;
        const UInt32 SECTION_EXTEND_SIZE = 0x0010;
        const UInt32 SECTION_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SECTION_QUERY |
            SECTION_MAP_WRITE |
            SECTION_MAP_READ |
            SECTION_MAP_EXECUTE |
            SECTION_EXTEND_SIZE);
        const UInt32 FILE_MAP_ALL_ACCESS = SECTION_ALL_ACCESS;
        private SafeFileHandle sHandle;
        private IntPtr hHandle;
        private IntPtr pBuffer;
        private int sharedInputCount;
        bool attachSuccessful;
     //
        int count;
        int X0;
        int Y0;
        int X1 = -1;
        int Y1 = -1;
        int SQ0 = -1;
        int SQ1 = -1;
    
    void Start (){
    sHandle = new SafeFileHandle(hHandle, true);
            sharedInputCount = 0;
            attachSuccessful = Attach(szMapName, 256);
    }
    
    unsafe public bool Attach(string SharedMemoryName, UInt32 NumBytes)
        {
            if (!sHandle.IsInvalid) return false;
            sHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, SharedMemoryName);
            Debug.Log("Shared mem open: ");
            if (sHandle.IsInvalid) return false;
            Debug.Log("Shared mem open SUCCESS: ");
            pBuffer = MapViewOfFile(sHandle, FILE_MAP_ALL_ACCESS, 0, 0, new UIntPtr(NumBytes));
            Debug.Log("Shared mem mapped: ");
            return true;
        }
    
    unsafe public void Detach()
        {
            if (!sHandle.IsInvalid && !sHandle.IsClosed )
            {
                //CloseHandle(hHandle); //fair to leak if can't close
                sHandle.Close();
            }
            pBuffer = IntPtr.Zero;
            //lBufferSize = 0;
        }
        void Update()
        {
            if (!attachSuccessful)
            {
                attachSuccessful = Attach(szMapName, 256);
                return;
            }
        }
     void OnApplicationQuit()
        {
            if (attachSuccessful)
            {
                Detach();
            }
        }
     void FixedUpdate ()
    	{
            //get Shared memory Input
            if (!attachSuccessful)
            {
                return;
            }
            count = Marshal.ReadInt32(pBuffer, 0);
            X0 = Marshal.ReadInt32(pBuffer, 4);
            Y0 = Marshal.ReadInt32(pBuffer, 8);
            X1 = Marshal.ReadInt32(pBuffer, 12);
            Y1 = Marshal.ReadInt32(pBuffer, 16);
            SQ0 = Marshal.ReadInt32(pBuffer, 20);
            SQ1 = Marshal.ReadInt32(pBuffer, 24);
    }
}

I hope that puts you on the right track.

@Batlad

Hello Thank you for a good example.

But, Would I able to ask sample unity project for shared memory?

I can’t clearly understand how it works in Unity

Have you solved the problem? I have no problem with the way upstairs at windows PC,but how to deal with Android and IOS?

@Batlad
Hello

!!! Thank you very much for the perfect code !!!

When I have read your sentence “I was using input from a custom USB controller so I had c++ code to handle its input.” I believed you are are talking of my device :slight_smile:

I have developed a small camera which can take 1600 - 10000 pictures per second and therefore can detect the positions of 80 and more very short flashing laser pointers (100microseconds flash per pointer). Now, this allows 80 peole to control a Unity game with up to 80 individual cursors (following the laser pointer). Since the short flash is invisble for human eye, I need computer generated cursors there. My camera reports 80pointers * struct{X,Y,Buttons,ValidStatus} * 20 times each second. While I have implements the C++ code on the Windows side using shared memory (same as you have done), our game development partner had no idea how to access this shared memory from Unity C#. Your code helped us very much, thank you again.

But now we have a problem: We have the data within Unity, but how do we feed it into the InputSystemUIInputModule Component which - as far as I understand - was new (2019) to Unity.

Is Unity able to draw those cursors (graphical shape must be under game control). I expect something like a SEND function where we can send X, Y,button states and a flag if the cursors (the laser pointer) is on screen at all, so that the cursor disappears while not on screen or moves to a kind of cursro garage somewhere on the display (not sure about that).

Can you give me a short advice what to do with the data (your values X0, Y0, X1, Y1, SQ0, SQ1 in your function FixedUpdate). What do we have to call so that Unity moves Cursor00 … Cursor79 to the given position, draws it there with the application given shape (we must be able to also attach the gamer alias (string) to the cursor for identification purpose) and handles the multi input click/hover/button down/up events.

My collegue (a unity developer) is also watching this thread.

Maybe if my camera and my laser pointers become available in large quantities in several months (in the moment we have 20 cameras and 200 pointers only) a lot of new group multi pointer games on large screens may enter the market.

I hpe that a lot of Unity developers will like multi user interaction on a single projection (we also support up to 16 cameras on a single PC where the PC controls several video projectors).

Thank you.
Harald