Unity UI - Interacting with worldspace UI when cursor is locked - c#

I'm trying to interact with world space UI using a first person controller when Cursor.lockState is set to CursorLockMode.Locked.
world space UI and a character
But when cursor is locked, cursor position is set to (-1, -1),
which is told from the Inspector.
cursor position of (-1, -1)
I performed a graphic raycast with EventSystem.RaycastAll, Sreen.width/2 and PointerEventData. EventSystem.current.RaycastAll collects all UI objects in the middle of screen, but no events is sent to them.
I also tried ExecuteEvents.Execute<IEventSystemHandler> to manully send event to UI targets. This works for button when I send 'submit' event to it. Obviously this is not an elegant solution. I have no idea how to send message to slider either.
// manully send a 'submit' event to UI elements
List<RaycastResult> results = new List<RaycastResult>();
void Update() {
if (Input.GetButtonUp("Fire1")) {
PointerEventData data = new PointerEventData(EventSystem.current);
data.position = new Vector2(Screen.width / 2, Screen.height / 2);
EventSystem.current.RaycastAll(data, results);
foreach (var result in results) {
ExecuteEvents.ExecuteHierarchy<ISubmitHandler>(
result.gameObject, data,
ExecuteEvents.submitHandler
);
}
}
}
This crazy attempt works when played full-screen on Windows. 2333
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SetCursorPos ( int x , int y );
void SetCursorPositionToCenter()
{
  SetCursorPos(Screen.width/2, Screen.height/2);
}
Relavant Resources

I had a lot of problems working with Graphics Raycaster and Global UI elements as well. I tried a lot of things as well but the event system didn't seem to work. But I was simulating a mouse with a touchpad and maybe I didn't completely simulate how a mouse works with UI elements.
I ended up abandoning graphics raycaster and instead add a box collider to all UI elements. That way a regular physics raycaster would be able to detect it and run some code. For the slider, it is just increasing the slider's value as your x or y of your pointer increase.
Hope this helps a little.

What you're doing with the submit handler is similar to the many different ways this can be done. I've not dealt with sliders specifically, because it's not very easy to control sliders in this method (player controller or, in the case of VR, the user's head, does not do straight lines very well). So for slider input we usually go with +/- buttons. Another idea could be that when the user is pointing at a UI element that you release the cursorLock when they click. When click is released you can resume cursor lock.
UI elements are also often in need of states like hover/onEnter, onExit, sometimes pointer up, and usually pointer down. So we've often used Selectable class for this. I think all the interactable UI elements inherit from this so it's a good spot to access the UI element from. This will usually trigger the proper events but it can be a little tricky. Examine some of the many readily available gaze input systems as they're doing the same thing. I don't think I've ever seen one handle sliders though (they're usually meant for VR and gaze-to-slider control would be bad UX).
Cheers and good luck!

If you're trying to access UI elements without affecting gameplay you can simply set Time.TimeScale = 0; which basically pauses time but UI elements will still be active. Then just unlock your cursor and use it freely.

Related

How to get mouse movement information when cursor is off-screen

[please see update at the end]
I'm an old stack overflow user (as most developers on, you know, Earth), but that's my first question here.
I'm trying to use an "air mouse" for gaming (pointing it to the screen), but as the mouse sensor is a gyroscope, there's some problems with off-screen movement that I'd like to try to fix by software.
With this gyro mouse, when the user moves its arm pointing outside the screen, the cursor stops at the screen limit, which is no problem. However, when he moves his arm back, no matter the distance from the screen, the cursor immediately moves on-screen. This causes a giant difference between the air mouse real position and the cursor.
This could be fixed with a simple control over the number of pixels, and direction, travelled off-screen, in conjunction with some event handling. If I could sum the number of "offscreen" pixels traveled in -X, +X, -Y and +Y, it would be possible to prevent/cancel the mouse move event - or set the cursor to its previous position, at the edge of the screen - until the control tells me that the physical mouse is pointing back to the screen. Just then I'd allow the cursor to move freely.
Maybe this isn't that usefull, but it's an interesting problem, and see this working would be fun as hell!
Currently, based on this great project, I can just state when the mouse is off-screen, but cannot control how far and in which direction it is moving, to properly implement what I'm trying. It seems to me that such kind of information would be too low-level for my current Windows knowledge.
So, to be clear: how can I, in C# (other languages accepted, but I'd have to learn a lot ;), get any kind of "delta position" information or direction of movement, when the cursor is at the limit of screen?
With all due respect, I'm not interested on using different kinds of controllers, as well as "you shouldn't do this" answers. I have this problem to solve, with these elements, and it would be great to make this work!
UPDATE:
Ok, here's my progress up to now.
In order to get raw mouse data and know about the mouse movement 'offscreen', I had to register my application, using some windows API functions, to receive WM_INPUT messages.
Based on a very old code I found, I was able to get mouse raw data and implement exactly what I wanted. Except for the fact that this code is based on a WdnProc callback, and so it only works when my application has the focus. And I need it to also work when the focus is elsewhere - after all, I'm trying to improve the pointing provided by a gyro mouse, for third party games.
It seems that I should use a hook (a good example here), but I have no idea how to hook for input messages. Tried to merge the code of the two links above, but the first one needs the message.LParam that is passed to the WdnProc - which seems to be unavailable when merely hooking mouse events.
Now I'm way out of my league to make some real progress. Any ideas?
One of the simplest solution to get cursor position and then detect its movement regardless where the cursor is to use win32 API from the user32.dll.
Here is a simple code which gets the cursor position in every 10ms using the timer in the C# Windows Form application and displays it as the title of the window.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
public partial class Form1 : Form
{
Timer timer = new Timer();
public Form1()
{
InitializeComponent();
timer.Interval = 10;
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
// do here whatever you want to do
// just for testing...
GetCursorPos(out Point lpPoint);
this.Text = lpPoint.X + ", " + lpPoint.Y;
}
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out Point p);
}

2d Sprite Animations without using XNA or other third-party libraries

I want to create a simple game, similar to what can be created with RPG Maker. What I am primarily looking for at the moment is a tutorial which can guide me on how to accomplish it without using XNA or any other specific library.
The ideal would be a step-by-step tutorial or a good, easy-to-understand example.
My goal is to do it on my own, and come to an understanding of how the overall process works with regards to animation elements relating to one another.
I've already built a simple snake game in the past using a simple tutorial, but there was no movement animation in it and that's the primary thing I'm wanting to learn next.
Edit:
All of the tutorials I have found so far use third-party libraries. I did come across this link, but I'm looking for something which doesn't include XNA.
There are a number of ways to approach the topic you're describing. I'm going to give a bit of an overview, and then hopefully provide some resources which can give you examples to get you started.
Essentially, sprite-based animations revolve around having a series of similar images which, when displayed sequentially, create the appearance of motion, similar to a flip-book.
The trick is to understand when your sprite is moving (and therefore should be animated) - and when it is standing still (and therefore should not be animated). In other words - assuming that your game's character is only supposed to move while you hold ▲, ▶, ▼ or ◀, you need to detect when one of those keys starts and stops being pressed, so that you can start/stop your animation accordingly.
Imagine that for simplicity, you only have 2 sprites. The first (left, below) represents your character standing still, and the second represents your character mid-step (right, below):
When the ▶ button is not pressed, you simply continually display the first image. When the ▶ button is pressed, you toggle between the two every x milliseconds (depending on how fast you want the animation to appear).
An animated .gif is one format in which you can contain a series of simple image frames, which are intended to be displayed as a series (and therefore create the illusion of animation). If you were to create your sprites using this format, you could use code similar to that found in this SO discussion, which provides some example code for how to use C# to animate an animated .gif and control its start/stop.
Alternatively, if you wanted to use a sprite file (like the one I included above), you could use something similar to this CodeProject code, which focuses on GDI interaction with the Windows environment in order to extract and paint a portion of the sprite onto a target canvas. By repainting every x milliseconds (as mentioned above), this can provide the same effect.
A few things to keep in mind:
You'll need to handle transparency in your sprites (the Mario sprite above, as an example, has a transparent background) - so that the background of your game environment shows through. If using GDI - this all has to do with how you call the painting methods. If using an animated .gif - the method to use depends on how you display it on your window.
For some additional resources / examples / references, check out the following resources:
Intermediate C# Game Making Tutorial - 2 - Sprites
2D Game Primer (Visual C) - an older article which talks quite a bit about the concepts of sprite animation, timing and such - and gives some example code (some DirectX in examples)
Sprite.cs - an example of some C# manipulation code for dealing with sprites and sizing (uses OpenGL, so may not be applicable)
And for Sprite development:
SpritePad - a tool for creating Sprite Sheets
http://makeagif.com/ - a tool for creating animated gifs online
http://picasion.com/ - another animated gif creator
I threw together than example of what I think it is that you were after. This example can be applied to buttons or picture boxes. I chose this way of out of simplicity.
Each instance of an animation holds a timer, and a list of images. Updating the image of the target control whenever the timer fires its event.
I have uploaded my project file here. http://mcspazzy.com/code/ParTest.zip
Hopefully it is enough to help. Just ask if you need more explanation.
The class
public class Animation
{
readonly Timer _animtimer = new Timer();
public List<Image> Frames;
public int FrameIndex;
private Button _target;
private PictureBox _ptarget;
public void Target(PictureBox target)
{
_ptarget = target;
}
public void Target(Button target)
{
_target = target;
}
public int FrameSpeed
{
get { return _animtimer.Interval; }
set { _animtimer.Interval = value; }
}
public Animation()
{
Frames = new List<Image>();
_animtimer.Interval = 100;
_animtimer.Tick += Update;
}
public void Play()
{
_animtimer.Start();
}
public void AddFrame(string file)
{
Frames.Add(Image.FromFile(file));
}
public void Stop()
{
_animtimer.Stop();
}
private void Update(object sender, EventArgs eventArgs)
{
FrameIndex++;
if (FrameIndex == Frames.Count)
{
FrameIndex = 0;
}
_target.Image = Frames[FrameIndex];
_ptarget.Image = Frames[FrameIndex];
}
public static implicit operator Image(Animation a)
{
return a.Frames[a.FrameIndex];
}
}
This was in my Form load. Can really go anywhere that stuff is initialized.
private void Form1Load(object sender, EventArgs e)
{
_testAnim.AddFrame(#"F:\Im\Black.png");
_testAnim.AddFrame(#"F:\Im\Blue.png");
_testAnim.AddFrame(#"F:\Im\Green.png");
_testAnim.AddFrame(#"F:\Im\Orange.png");
_testAnim.AddFrame(#"F:\Im\Red.png");
_testAnim.Target(ButtonTest);
_testAnim.Target(PicBox);
_testAnim.Play();
}
On quick search I found this example.
I'm not very experienced in c# graphics, but here are few points I have learned working with non-graphic oriented languages:
Declare where/what you want to draw
Create a loop to run until abort/event to end the loop (like object colliding with something)
In the loop: wait, clear the old drawing area, recalculate new position, draw in new position
If needed you can change the image to draw too, but then you need separate images for each "frame" you want to have
multithreading is a good idea, so you can separate running the graphics from other game logic
try keeping the time from clearing the drawing area and re-drawing as short as possible to prevent flickering
Keep track of the size of objects you draw, makes it easier to check for collision (like center of the sprite + radius, then you can easily calculate a circle area around it to check if two sprites are too close to each other)

MouseDown and Click conflict

I have a Drag() method on form_MouseDown event. I also have a click event on the form. The problem is that if I click on the form, MouseDown event gets triggered and it never gets the chance to trigger click event.
What is the best way to solve this issue? I was thinking counting pixels if the form is actually dragged, but there has to be a better way. Any suggestions?
I was thinking counting pixels if the form is actually dragged, but there has to be a better way.
Nope, that's exactly how you have to do it.
This isn't just a software limitation; it's very much a practical one as well. If you think through the problem from a user's perspective, you'll immediately see the problem as well as the solution. Ask yourself, what is the difference between a click and a drag?
Both of them start with the mouse button going down over the object, but one of them ends with the mouse button going back up over the object in the same position and the other one ends with the mouse button going back up in a completely different position.
Since time machines haven't been perfected yet, you have no way of knowing this in advance.
So yes, you need to maintain some kind of a distance threshold, and if the pointer moves outside of that distance threshold while it is down over the object, then you consider it a drag. Otherwise, you consider it a click.
That distance threshold should not be 0. The user should not be required to hold the mouse completely still in order to initiate a click. A lot of users are sub-par mousers. They are very likely to twitch slightly when trying to click. If the threshold is 0, they'll end up doing a lot of inadvertent dragging when they try to click.
Of course, you don't actually have to worry about any of this or compute the drag threshold yourself. Instead, use the Windows default values, obtainable by calling the GetSystemMetrics function and specifying either SM_CXDRAG or SM_CYDRAG. (These might be exposed somewhere by the WinForms framework, but I don't think so. It's just as easy to P/Invoke them yourself.)
const int SM_CXDRAG = 68;
const int SM_CYDRAG = 69;
[DllImport("user32.dll")]
static extern int GetSystemMetrics(int index);
Point GetDragThreshold()
{
return new Point(GetSystemMetrics(SM_CXDRAG), GetSystemMetrics(SM_CYDRAG));
}
In the field of UX/UI, this sort of thing is called hysteresis or debouncing, by analogy to the use of these terms in physics and electronics.
I found this solution, although it is for a double-click and a mouse down events:
void pictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && e.Clicks ==1)
{
PictureBox pb = (PictureBox)sender;
DoDragDrop((ImageData)pb.Tag, DragDropEffects.Copy);
}
}
source: http://code.rawlinson.us/2007/04/c-dragdrop-and-doubleclick.html
Unfortunatelly, at the point of time when "button-is-pressed" you don't know yet if the desired action is just a click or a drag-drop. You will find it out it later.
For a click, the determinant is "no movement" and "button up".
For a drag, the determinant is "movement" and "button up".
Hence, to disambiguate those interactions, you have to track not only the buttons, but also the movement. You do not need to track the overall movement, only the movement between button-down and button-up is interesting.
Those events are therefore a good place to start/stop the Mouse.Capture mechanisms (to dynamically present drag adorners and drop location hints), or, in simplier form - to store the origin and target of movement vector and check if the distance is > D (even if movement occurred, there should be some safe minimal distance within which the DRAG is canceleed. The mouse is "jaggy" sometimes, and people would really don't like your app to start dragging when they double click at the end of fast mouse pointer movement :) )

How can I get rid of jerkiness in WinForms scrolling animation?

I'm writing a simple control in C# that works like a picture box, except the image is constantly scrolling upwards (and re-appearing from the bottom). The animation effect is driven by a timer (System.Threading.Timer) which copies from the cached image (in two parts) to a hidden buffer, which is then drawn to the control's surface in its Paint event.
The problem is that this scrolling animation effect is slightly jerky when run at a high frame rate of 20+ frames per second (at lower frame rates the effect is too small to be perceived). I suspect that this jerkiness is because the animation is not synchronized in any way with my monitor's refresh rate, which means that each frame stays on the screen for a variable length of time instead of for exactly 25 milliseconds.
Is there any way I can get this animation to scroll smoothly?
You can download a sample application here (run it and click "start"), and the source code is here. It doesn't look horribly jerky, but if you look at it closely you can see the hiccups.
WARNING: this animation produces a pretty weird optical illusion effect which might make you a little sick. If you watch it for awhile and then turn it off, it will look as if your screen is stretching itself vertically.
UPDATE: as an experiment, I tried creating an AVI file with my scrolling bitmaps. The result was less jerky than my WinForms animation, but still unacceptable (and it still made me sick to my stomach to watch it for too long). I think I'm running into a fundamental problem of not being synced with the refresh rate, so I may have to stick to making people sick with my looks and personality.
You would need to wait for a VSYNC before you draw the buffered image.
There is a CodeProject article that suggests using a multimedia timer and DirectX' method IDirectDraw::GetScanLine().
I'm quite sure you can use that method via Managed DirectX from C#.
EDIT:
After some more research and googling I come to the conclusion that drawing via GDI doesn't happen in realtime and even if you're drawing in the exact right moment it might actually happen too late and you will have tearing.
So, with GDI this seems not to be possible.
(http://www.vcskicks.com/animated-windows-form.html)
This link has an animation and they explain the way they accomplish it. There is also a sample project that you can download to see it in action.
Use double buffering. Here are two articles: 1 2.
Another factor to ponder is that using a timer doesn't guarantee you to be called at exactly the right time. The correct way to do this is to look at the time passed since the last draw and calculate the correct distance to move smoothly.
I had a similar problem a couple of months ago, and solved them by switching to WPF. The animated control ran a lot smoother than with a standard timer-based solution and I didn't have to take care of synchronization any more.
You might want to give it a try.
You need to stop relying on the timer event firing exactly when you ask it to, and work out the time difference instead, and then work out the distance to move. This is what games, and WPF do, which is why they can achieve smooth scrolling.
Let's say you know you need to move 100 pixels in 1 second (to sync with the music), then you calculate the time since your last timer event was fired (let's say it was 20ms) and work out the distance to move as a fraction of the total (20 ms / 1000 ms * 100 pixels = 2 pixels).
Rough sample code (not tested):
Image image = Image.LoadFromFile(...);
DateTime lastEvent = DateTime.Now;
float x = 0, y = 0;
float dy = -100f; // distance to move per second
void Update(TimeSpan elapsed) {
y += (elapsed.TotalMilliseconds * dy / 1000f);
if (y <= -image.Height) y += image.Height;
}
void OnTimer(object sender, EventArgs e) {
TimeSpan elapsed = DateTime.Now.Subtract(lastEvent);
lastEvent = DateTime.Now;
Update(elapsed);
this.Refresh();
}
void OnPaint(object sender, PaintEventArgs e) {
e.Graphics.DrawImage(image, x, y);
e.Graphics.DrawImage(image, x, y + image.Height);
}
Some ideas (not all good!):
When using a threading timer, check that your rendering time is considerably less than one frame interval (from the sound of your program, you should be fine). If rendering takes longer than 1 frame, you will get re-entrant calls and will start rendering a new frame before you've finished the last. One solution to this is to register for only a single callback at startup. Then in your callback, set up a new callback (rather than just asking to be called repeatedly every n milliseconds). That way you can guarantee that you only schedule a new frame when you've finished rendering the current one.
Instead of using a thread timer, which will call you back after an indeterminate amount of time (the only guarantee is that it is greater than or equal to the interval you specified), run your animation on a separate thread and simply wait (busy wait loop or spinwait) until it is time for the next frame. You can use Thread.Sleep to sleep for shorter periods to avoid using 100% CPU or Thread.Sleep(0) simply to yield and get another timeslice as soon as possible. This will help you to get much more consistent frame intervals.
As mentioned above, use the time between frames to calculate the distance to scroll, so that the scroll speed is independent of the frame rate. But note that you will get temporal sampling/aliasing effects if you try to scroll by a non-pixel rate (e.g. if you need to scroll by 1.4 pixels in a frame, the best you can do is 1 pixel, which will give a 40% speed error). A workaround for this would be to use a larger offscreen bitmap for scrolling and then scale it down when blitting to screen, so you can effectively scroll by sub-pixel amounts.
Use a higher thread priority. (really nasty, but may help!)
Use something a bit more controllable (DirectX) rather than GDI for rendering. This can be set up to swap the offscreen buffer on a vsync. (I'm not sure if Forms' double buffering bothers with syncing)
I had the same problem before and found it to be a video card issue.
Are you sure your video card can handle it?
I modified your example to use a multimedia timer that has a precision down to 1 ms, and most of the jerkiness went away. However, there is still some little tearing left, depending on where exactly you drag the window vertically. If you want a complete and perfect solution, GDI/GDI+ is probably not your way, because (AFAIK) it gives you no control over vertical sync.
Well, if you wanted to run the timer at a lower speed, you can always change the ammount the image is scrolled in the view. This gives better preformance, but makes the effect look kinda jerky.
Just change the _T += 1; line to add the new step...
Actually, you could even add a property to the control to adjust the step ammount.

How to limit cursor movement in WPF based app?

I'm working with a WPF app, more specifically a Canvas with draggable elements.
Once an item is being dragged, I'd like to limit the scope of cursor movement to inside the canvas where the items are being dragged about.
The event which can start a drag is shown below
private void WidgetCanvas_PreviewHeaderLeftMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
... logic to figure out if this is a valid drag, blah blah blah ...
this.IsDragging = true;
// TODO: clip the available cursor movement to actual width of Canvas
}
On the Preview-MouseUp, I'd like to simply "unclip" the cursor movement back to it's normal state.
I'll be monitoring the movement of the mouse once I start dragging (PreviewMouseMove), so worst case, I could manually check the position of the mouse and keep it constrained to the canvas, but that seems a little ugly.
Anyone have a better way to limit the cursor boundaries?
There's no clean way to do this and the not-so-clean ways will make your mouse cursor "jitter" at the border of the clipping area.
Moreover, I'd question if this is really a good idea. The user should really own the mouse and he or she generally gets frustrated when you try to artificially limit things that he or she owns.
If you want to provide feedback when the mouse leaves your canvas, maybe you could leave the item being dragged stuck along the border while the mouse button is still down? This would tell the user that he or she has left the target area without trying to impose limitations on where the mouse can go.
Good luck!
You should be able to do it using the ClipCursor native API.

Categories