Hi there, I needed to have custom hardware cursos in my PC/MAC game, due to low FPS on slow machines. I found no solution, so I've written this very simple plugin. It enables custom hardware cursor support in Unity standalone applications for PC/MAC. All you have to do is download the package, and follow the tutorial provided in PDF. The plugin requires Unity Pro. Minimum Unity version is 3.0. Drop me a line if you find it useful, or in case you had problems or questions. Calveit
Oh man I've wanted this for a while! Never had the Cocoa/Carbon/MFC or whatever knowledge to attempt it, though. I'll put it in our project now and let you know how it goes.
Can you recommend a .cur editor for Mac OS? The cursor editing market seems to be mostly malware and crapware.
Ok, so I think I've followed your instructions: I added CP 0.1.unitypackage to my project I put a cursor file at Resources/Cursors/aero_unavail.cur I added the following script to an object in the first scene: Code (csharp): using UnityEngine; using System.Collections; [AddComponentMenu("Cursors/Custom Cursors")] public class CustomCursors : MonoBehaviour { protected void Awake() { string cursorPath = Application.dataPath + "/Cursors/aero_unavail.cur"; Debug.Log("Loading custom cursor from: " + cursorPath); CursorPlugin.InitializeCursor(0, cursorPath); CursorPlugin.SetCurrentCursor(0); } } Aside from the debug statement, I can see no change in my build. The standard Mac OS cursor is there from the beginning. No errors or anything. This is an Intel-only build on Mac OS X 10.6.5. What might I be doing wrong?
Hi Daniel, Thanks for trying the plugin, and your feedback! I guess there's a bug in the document, which says cursors are copied to Application.dataPath / Cursors, but in fact the path should be: Application.dataPath/. Please try the same code with the path: Code (csharp): string cursorPath = Application.dataPath + "/aero_unavail.cur"; I will change the document if that works for you. Maciek
Oops, that was my mistake. I just put my cursor in Resources/Cursors for organizational purposes. I didn't realize that the directory would be flattened in the build. However, now that I've confirmed that the logged path and the path to the cursor in the build are the same, it still doesn't seem to do anything. Any ideas?
Ok, I think I understand what was happening. I had forgotten that the contents of Resources is not just copied into the build: only assets in that directory are copied automatically. Your PostprocessBuildPlayer scripts copy the cursor files, and they expect to find the cursors in Resources/Cursors. I must have messed something up the time I thought I was doing it properly. In any case, it seems to work fine if you do exactly as you say: Put the cursor files in Assets/Resources/Cursors Load files from Application.dataPath + "/" + cursorNameIncludingExtension The only problem I've found so far is that under Mac OS, the cursor reverts to the system cursor when you click on the menu bar.
Cameron, if you use this you don't have to rely on your game's FPS in matter of displaying the cursor. In other words, if your game runs smoothly (ie. at 60 FPS), there's no need to use this plugin. But if the FPS drops below 20 - 25 FPS, you will notice that your cursor starts to float and does not update its position instantly. That's because it depends on your Update call frequency. This plugin delegates cursor handling to the OS, so it does not depend on your FPS. The problem in most cases is: why does your game run so slowly? But there are situations in which you are obliged to support very old machines, or where FPS does not impact the rest of the gameplay (ie. Hidden Object / Puzzle games). Daniel, I will look into the problem you've found and post a soulution as soon as I manage to fix it.
I find cursor lag very noticeable. Even 16-33ms (which is about what you get when running at 60fps) feels slightly off when compared to hardware cursors.
I think I've fixed this issue. New version (0.2) is available as attachment to the first post of this thread. You do not need to to change your code, only switch OS X library (.bundle) to new version.
Hey, thanks for this contribution. However, the doc/pdf need to be corrected: the call to CursorPlugin.InitializeCursor CursorPlugin.SetCurrentCursor should be: HardwareCursors.InitializeCursor HardwareCursors.SetCurrentCursor Even so, I didn't manage to make it to work. I have a cur file in Resources/Cursors and I have a GO in my scene with a .cs script like this: Code (csharp): void Start() { HardwareCursors.InitializeCursor((int)0, Application.dataPath + "/hand.cur"); HardwareCursors.SetCurrentCursor((int)0); Screen.showCursor = true; } I Build and Run and see nothing... What could be wrong?
Thanks for your feedback immFX! You're right, I will correct the tutorial document. Your script seems ok and the plugin should work. Could you provide some more details on your situation? What OS you're running? Has Postprocess script copied the .cur files to the Application.dataPath of your build folder? Also, have you tried to build sample project?
Oh, that was it. The Postprocess script didn't copy the .cur file (as expected). I manually copied it in Application.dataPath and this fixed it. Lovely, thanks!
Hi Calveit, I just had a chance to test the new version on a Mac. It definitely behaves better when in windowed mode (going to the system cursor when the cursor leaves the game window is a good idea), but it seems to have problems with fullscreen: 1. Switching to or from fullscreen changes the cursor back to the system default. It will be changed again when SetCurrentCursor() is called. 2. If you switch from fullscreen to windowed mode (windowed>fullscreen>windowed or fullscreen>windowed and then call SetCurrentCursor(), the behaviour reverts back to what I saw in version 1: the cursor switches properly, but does not change at the edges of the window. However, clicking on a menu item will cause the cursor to switch back to the system cursor. 3. Finally, if the game starts in fullscreen then no cursor changes will be respected at all until you switch back to windowed mode. If you can fix 2. and 3. then I can work around 1. Thanks for your continued work!
I've tested this plugin further, and on Windows XP I'm getting a DllNotFoundException. It provides the path where it is looking, and I double-checked it to make sure it is correct. Apparently this can happen if the plugin has a dependency that cannot be satisfied. The plugin works fine on Windows Vista and 7.
Having looked further into this issue, I might be able to give you more specific information after trying Dependency Walker. I'll do that sometime soon.
Thanks, Calveit, very nice start! I wonder, is there possibility for some Generic GUI class, which will detect when mouse cursor is over some button? Will try looking into it right now.
Very Nice! Thanks for sharing! ... might it also be possible to set the cursors position to a specific location on screen?
Hi Calvert, and kudos for this! It'll help a lot of people. However, we came up with our own solution some time ago on Windows by relying on the cursor-controlling functions offered by user32.dll. We not simply replace the mouse cursor icon but also get/set its position, clipping rectangle (clipped to the game window, so that the mouse doesn't leave it in either windowed mode or if the user has more that one screen) and manage different behaviors if the game isn't in the foreground (we then release the clipping rectangle so that the mouse isn't locked to inside the game's window if the player alt-tabbed to find cheat codes on the web or whatever ^_^). I looked in doing the same kind of things on OSX, but I haven't been lucky there, and couldn't find anything I can use to have as much control over the mouse cursor. May I enquire as to how you managed to work on the cursor in OSX, if you used a particular library or did it another way? Would you be ok with showing us the source code of your Mac bundle? In advance, thank you.
Hey guys, I'm glad that you find the plugin helpful. I'm afraid I don't have as much time as I'd like to, to work on the plugin, so I decided to share the bundle and dll code. I've included the packages in the first post of this thread. Feel free to use, modify, redistribute it. Best, Calveit
There's nothing amazingly difficult once you've found and put user32.dll into your plugin directory. Actually I'm not even sure you need to put it in there as this file is available to any program running on Windows. Anyway, in user32.dll, you'll find these functions (code copied/pasted from our CS script): Code (csharp): [DllImport( "user32.dll" )] static extern System.IntPtr GetForegroundWindow(); [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )] public static extern System.IntPtr SetCursor( System.IntPtr hCursor ); [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )] public static extern System.IntPtr SetClassLong( HandleRef hWnd, int nIndex, long dwNewLong ); [DllImport( "user32.dll", EntryPoint = "SetClassLong" )] public static extern uint SetClassLongPtr32( HandleRef hWnd, int nIndex, uint dwNewLong ); [DllImport( "user32.dll", CharSet = CharSet.Auto, SetLastError = true, ThrowOnUnmappableChar = true, BestFitMapping = false )] public static extern System.IntPtr LoadImage( System.IntPtr hInstance, string lpImageName, uint uType, int cxDesired, int cyDesired, uint fuLoad ); [DllImport( "user32.dll" )] [return: MarshalAs( UnmanagedType.Bool )] static extern bool SetCursorPos( int X, int Y ); [DllImport( "user32.dll" )] [return: MarshalAs( UnmanagedType.Bool )] static extern bool GetCursorPos( out POINT lpPoint ); [DllImport( "user32.dll" )] [return: MarshalAs( UnmanagedType.Bool )] public static extern bool GetClipCursor( out RECT rcClip ); [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )] [return: MarshalAs( UnmanagedType.Bool )] public static extern bool ClipCursor( ref RECT rcClip ); [DllImport( "user32.dll" )] [return: MarshalAs( UnmanagedType.Bool )] static extern bool GetWindowRect( int hWnd, ref RECT lpRect ); [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )] public static extern int GetDesktopWindow( ); Once you've declared them, you just need to use them! For a few of them, you'll also need those structures: Code (csharp): [System.Runtime.InteropServices.StructLayout( LayoutKind.Sequential )] private struct POINT { public int X; public int Y; } [System.Runtime.InteropServices.StructLayout( LayoutKind.Sequential )] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } That's about it really.
You need to install "Microsoft Visual C++ 2008 Redistributable Package (x86)" on Windows XP in order to use this utility.
That's not really something I can explain to hundreds of thousands of customers. Surely the same functionality can be accomplished through MFC or something else that is standard with XP?
Another choice of hardware cursor for windows standalone application. http://forum.unity3d.com/threads/83019-Unity-Plugin-for-turning-on-off-IME?p=564153#post564153
I've been considering using this one, but the 15 cursor slots are not enough for our purposes. Has anyone managed to raise the limit? I'd probably do it myself, but I have no experience on building dlls or osx bundles so just learning that bit would take a lot of time. EDIT: We can work around the limitation but changing the UI design a bit, but the cursor's hotspot seems to be on the bottom of the icon rather than on top of it.
Since Unity 4 there is HW-Cursor support: http://docs.unity3d.com/Documentation/ScriptReference/Cursor.SetCursor.html however, this doesnt annihilate the Unity inherent mouseInput lag -is there any way to get the HW-mouseposition into Unity?
Hardware cursor should be rendered exactly where the OS thinks the cursor is... because the OS is rendering it. If you are seeing something that is not like this please raise a bug.
it is not a question of its rendering position, the render-position is on spot (you can see it if you use a long vertical line as HW cursor and moving the cursor out of the webplayer) it is a question of getting the very exact position of this cursor for use in update, it seems that the SW-Cursor of unity and its Input.mousePosition is lagging behind way more than 1 frame
Just a side note: this is pretty common practice. Nearly every game I have installed from Steam (and this is on Win7) has required some additional install. The trick is to either bundle it or prompt the user during installation. I think most setup creation tools can do this.
Hello there. First, thanks very much for sharing. I have a problem though, it seems that cursor is always flipped back to the default windows arrow when i move the mouse. Setting it in LateUpdate won't help as the hw cursor refreshes faster than the update is called. Any idea? PS: Plugin works with *.ani too
We need to revive this, there is still no in-engine solution for the horrible input lag Unity has, PLEASE, when will we get real RAW input? omg...
It is. Input.mousePosition is still sux. No exact position, and as your FPS drops it getting worse and worse. Even when your game is smooth (let's say constant 60FPS), mouse position will be never as where HW cursor set it. It's a shame.