Change mouse cursor icon in C# - c#

I am building a game for Windows PCs and I need to change the cursor icon when the user is over clickable UI elements. I have found this command Cursor.SetCursor(texture2d, vector2) but it has some lag.
When the mouse is over the UI elements it changes but it has some delay which is really annoying (This is what other users said about that anyway).
After some reading, I learned that Unity basically just changes the cursor icon in the software level. i.e. Hides the cursor and displays my image, and make it follow the cursor position.
My question is: How can I change the icon in hardware level, again, in windows builds only.
When I searched "Changing mouse cursor in C#", I have found the windows.forms option (which doesn't work in unity) and a c++ code but it wasn't full (Only methods' names), and I don't know how to run it in C#…

The SetCursor not work very good on all Windows app, but is the right way to do it.
Cursor.SetCursor(cursorTexture, hotSpot, cursorMode);
Another way is to make a Little fake hidding the mouse cursor and making a GUI cursor for your desire case. You can add more conditions for each event you like to customize.
var OnMouseEnterCursor:Texture2D;
var cursorSizeX: int = 32; // your cursor width
var cursorSizeY: int = 32; // your cursor height
var MouseEnterCond = false;
function OnMouseEnter()
{
MouseEnterCond = true;
Screen.showCursor = false;
}
function OnMouseExit()
{
MouseEnterCond = false;
Screen.showCursor = true;
}
function OnGUI()
{
if(MouseEnterCond )
{
GUI.DrawTexture (Rect(Input.mousePosition.x-cursorSizeX/2 + cursorSizeX/2, (Screen.height-Input.mousePosition.y)-cursorSizeY/2 + cursorSizeY/2, cursorSizeX, cursorSizeY),OnMouseEnterCursor);
}
}

If there is a way to enforce hardware cursors in Unity the world has not found it yet.
Unity's own documentation explains it pretty well:
A hardware cursor is preferred on supported platforms through the function which you have yourself found (remember to set the cursor-mode to auto), but it will automatically, and uncontrollably, fall back on software for unsupported platforms and resolutions.
An important thing to note is that there is a special field (which the reference only peripherally mentioned) in the project settings/player settings menu called the "Default Cursor".
This is the only supported hardware cursor on e.g. windows store apps.
And remember to set your textures to type "cursor" in their configurations,.
Finally, keep in mind that windows only supports the 32x32px size. This may force Unity's hand when selecting the render type.

The default cursor settings in the player settings worked in the editor but it did not show up in a build. Also limiting the texture size to 32x32. I resolved this issue by using Cursor.SetCursor method in a script to change the cursor.
[SerializeField] Texture2D cursor;
void Start() {
Cursor.SetCursor(cursor, Vector3.zero, CursorMode.ForceSoftware);
}

Related

Best way to draw a transparent label over a picturebox which moves in Windows Forms?

I'm not sure the best way to title this question, or even ask this, but essentially, I'm building a simple flappy bird clone for a friend, and decided to use windows forms rather than Unity (which I would normally use for games) because of it's simplicity. I, however, ran into the issue of transparent backgrounds of labels not working over other controls (in this case a picturebox). Using this question, I was able to find a workaround for the issue, except that the picturebox in question is the top pipe, which moves every tick.
This code is from the aforementioned question, and how I deal with the issue currently:
if (ScoreText.Bounds.IntersectsWith(PipeTop_PictureBox.Bounds))
{
var pos = this.PointToScreen(ScoreText.Location);
pos = PipeTop_PictureBox.PointToClient(pos);
ScoreText.Parent = PipeTop_PictureBox;
ScoreText.Location = pos;
} else
{
ScoreText.Parent = this;
}
The problem is that it produces a rather clunky result whenever the label intersects with the pipe as seen here (don't mind the sprites, I haven't made the actual ones yet, so I'm using some spare sprites I have on my computer from a previous project). I was wondering if there was any sort of fix for this, or if this is as good as it would get.

How to properly resize app to visible bounds on UWP? (VisibleBoundsMode doesn't seem to work)

I have two UWP apps and after testing them out with Continuum I noticed the app bar of the OS (the bar with the Start button) at the bottom of the screen (it can be at each of the 4 edges of the screen, of course) was covering part of my app.
Now, I'm already using ApplicationView.GetForCurrentView().SetDesiredBoundsMode(ApplicationViewBoundsMode.UseVisible) before calling Window.Current.Activate(), but that doesn't seem to solve the issue.
1) Why is it that setting the DesiredBoundsMode property doesn't seem to work here? Shouldn't that automatically resize the window
content to the visible bounds (ie. excluding system overlays like the
navigation bar or the app bar)?
The workaround I'm using for now on Windows 10 Mobile devices is to subscribe to the VisibleBoundsChanged event and then manually adjust the margins of my Window.Current.Content item to make sure it doesn't show anything behind covered areas of the screen.
Basically, I use the Window.Current.Bounds property and the ApplicationView.VisibleBounds property to calculate the occluded areas on the different edges of the app window, and increase the margins from there.
2) Is there a proper/better way to do this?
I mean, I'm quite sure there's another method that should be used to avoid this issue (considering there are tons of different situations like Continuum, navigation bar etc... that I don't think are supposed to be manually handled one by one).
Thank you for your help!
Use the subscription to the event VisibleBoundsChanged. This is the best solution that I found.
var curr = ApplicationView.GetForCurrentView();
if (curr.IsFullScreenMode == true)
{
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen;
curr.FullScreenSystemOverlayMode = FullScreenSystemOverlayMode.Minimal;
}
else
{
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto;
curr.FullScreenSystemOverlayMode = FullScreenSystemOverlayMode.Standard;
}

MonoGame - Working With Specific Gestures for Different Purposes

Part of my particular dilemma is that I would like to be able to get the initial position of a drag gesture. Depending on the initial position of that drag gesture, the application would either 1) pan the view or 2) display a menu, but not perform these gestures at the same time (which is where part of struggle lies).
For example, if the user initiated a drag from the very left side of their screen and dragged inwards, a menu would pop in instead of the view panning.
I would also like to be able to execute a double tap gesture without also activating a tap gesture, if that's at all possible. I've tried working with boolean flags - for example,
// ...
if (gesture.GestureType == GestureType.DoubleTap)
{
isDoubleTap == true;
}
// ...
public static Vector2 Tap
{
get
{
if (!isDoubleTap)
return gesture.Position;
// ...
But that doesn't work.
I've tried using TouchCollection - if anyone would like me to elaborate on my issues with that I can, but for now I'll say what I tried hasn't worked. It's entirely possible I may have just goofed as I am a novice when it comes to working with touch input.
I've been working on this for a few days and have done as much searching as I can, and nothing I've found has alleviated my issue - if I happened to have missed something, I apologize.
Thank you for your time!
Concerning start position of a drag:
There is a gesture for a drag ending, so if you receive a drag and its the first one since the last drag ended, thats the initial position.
Concerning tap/doubletap:
MonoGame works the same way as XNA as documented here:
The user tapped the screen twice in quick succession. This
always is preceded by a Tap gesture.
This sounds like a input-bindings design problem more than a technical question imo. Consider also what you can move to instead occur on press or release rather than only making use of gestures.

Monitor Cursor Position system wide

I need to make a small system tray app which monitors the cursor position systemwide and displays or hides the onscreen keyboard depending on the cursor handle ID. if the cursor is in a textbox (position equals IBeam) in IE,for example, the keyboard pops up.
I have code for the the system tray app (formless app) but cannot find a way of making it monitor the system. Any help with a function to monitor the system for cursor position would be welcome. thanks.
Monitor the system cursor position:
private void Pos()
{
for (; ; )
{
Thread.Sleep(10);
Point position = Cursor.Position;
//You can use these to pass to your system tray or whereever you need it.
somePublicXVar = position.X;
somePublicYVar = position.Y;
}
}
public void PointPosition()
{
Thread pointThread = new Thread(new ThreadStart(Pos));
pointThread.Start();
}
To be event-driven you'll need to use SetWindowsHookEx. You cannot do it directly through .NET, but must inject a DLL. Here is a MSDN article on making a mouse hook. This is done using System.Runtime.InteropServices to import user32.dll. The MSDN article gives step-by-step instructions on calling SetWindowsHookEx, CallNextHookEx, and UnhookWindowsHookEx from C#. CodeProject also has an article on making system-wide hooks in .NET.

How do I determine which monitor my .NET Windows Forms program is running on?

I have a C# Windows application that I want to ensure will show up on a second monitor if the user moves it to one. I need to save the main form's size, location and window state - which I've already handled - but I also need to know which screen it was on when the user closed the application.
I'm using the Screen class to determine the size of the current screen but I can't find anything on how to determine which screen the application was running on.
Edit: Thanks for the responses, everyone! I wanted to determine which monitor the window was on so I could do proper bounds checking in case the user accidentally put the window outside the viewing area or changed the screen size such that the form wouldn't be completely visible anymore.
You can get an array of Screens that you have using this code.
Screen[] screens = Screen.AllScreens;
You can also figure out which screen you are on, by running this code (this is the windows form you are on)
Screen screen = Screen.FromControl(this); //this is the Form class
in short check out the Screen class and static helper methods, they might help you.
MSDN Link, doesn't have much..I suggest messing around in the code by yourself.
If you remember the window's location and size, that will be enough. When you set the position to the previously used position, if it happened to be on the second monitor it will go back there.
For example, if you have 2 monitors, both sized 1280x1024 and you set your window's left position to be 2000px, it will appear on the second monitor (assuming the second monitor is to the right of the first.) :)
If you are worried about the second monitor not being there when the application is started the next time, you can use this method to determine if your window intersects any of the screens:
private bool isWindowVisible(Rectangle rect)
{
foreach (Screen screen in Screen.AllScreens)
{
if (screen.Bounds.IntersectsWith(rect))
return true;
}
return false;
}
Just pass in your window's desired location and it will tell you if it will be visible on one of the screens. Enjoy!
You can get the current Screen with
var s = Screen.FromControl(this);
where this is the Form (or any control on the Form). As about how to remember that is a little tricky, but I would go for the index in the Screen.AllScreens array, or maybe s.DeviceName. In either case, check before using the settings on startup, to prevent using a monitor that was disconnected.
The location of the form will tell you which screen the form is on. I don't really understand why you'd need to know what screen it is on, because if you restore it using the location you saved it should just restore to the same location (maybe you can expand as to why).
Otherwise you can do something like this:
Screen[] scr = Screen.AllScreens;
scr[i].Bounds.IntersectsWith(form.Bounds);
Each screen has a Bounds property which returns a Rectangle. You can use the IntersectsWith() function to determine if the form is within the screen.
Also, they basically provide a function that does this as well on the Screen class
Screen screen = Screen.FromControl(form);
You can use the 'Screen' object:
System.Windows.Forms.Screen
Start playing with something like this:
Screen[] screens = Screen.AllScreens;
for (int i = 0; i < screens.Length ; i++)
{
Debug.Print(screens[i].Bounds.ToString());
Debug.Print(screens[i].DeviceName);
Debug.Print(screens[i].WorkingArea.ToString());
}
It may get you what you need

Categories