GML How to Lock Mouse to a Circle Around Player ?

Hello, guys!

So, I made an object Crosshair that follows the mouse while staying in a circle around the player, using this code:

GML:
var dist = min(300, point_distance(obj_player.x, obj_player.y, mouse_x, mouse_y))

var dir = point_direction(obj_player.x, obj_player.y, mouse_x, mouse_y)


x = obj_player.x + lengthdir_x(dist, dir)
y = obj_player.y + lengthdir_y(dist, dir)

The problem is: the mouse itself can still get out of the circle. So, if it's outside the circle and I want to move the crosshair, I have to move the mouse all the way to the circle and then I can move the crosshair. Locking the mouse to the circle would solve it, but it's being really hard to do it, and all the similar questions I found are not helping me.

Any ideas or better solutions ?
 

samspade

Member
I'm not sure I'm following what you're saying. None of the code you posted would mean you have to move the mouse back to the 'circle' in order to move the cross hair. With that code you could move the mouse wherever you want and the cross hair would follow the mouse limited to the edge of the circle if the mouse is far away and directly on the mouse inside the circle.

Do you have other code elsewhere?

One solution would be to disable drawing the mouse, so that the mouse is invisible, and instead use a custom cursor attached to an object that follows the real mouse (I believe there's an extension somewhere to overcome the lag inherent with this solution).
 

Soso

Member
I tested your code and it seems to be working fine.
What event is your crosshair code in?
 
I'm not sure I'm following what you're saying. None of the code you posted would mean you have to move the mouse back to the 'circle' in order to move the cross hair. With that code you could move the mouse wherever you want and the cross hair would follow the mouse limited to the edge of the circle if the mouse is far away and directly on the mouse inside the circle.

Do you have other code elsewhere?

One solution would be to disable drawing the mouse, so that the mouse is invisible, and instead use a custom cursor attached to an object that follows the real mouse (I believe there's an extension somewhere to overcome the lag inherent with this solution).
Well, let's say the mouse is on the right edge of the screen (and the crosshair on the right edge of the circle) and I want to move the crosshair to the left edge of the circle - I have to move the mouse to the left until it enters the circle and THEN I can move the crosshair to the left.

What I want to do is to move the crosshair to the left as soon as I start to move the mouse to the left.


And about the custom cursor, wouldn't it do the exact same thing ?
 
Last edited:

Soso

Member
Well, let's say the mouse is on the right edge of the screen (and the crosshair on the right edge of the circle) and I want to move the crosshair to the left edge of the circle - I have to move the mouse to the left until it enters the circle and THEN I can move the crosshair to the left.

What I want to do is to move the crosshair to the left as soon as I start to move the mouse to the left.


And about the custom cursor, wouldn't it do the same thing ?
ok I see you want to move the crosshair relative to the current mouse_x,y
the tricky part is deciding when you want this to happen

GML:
//create
relative_x = 0;
relative_y = 0;


//step
if(some condition (mouse_click?))
relative_x = mouse_x;
relative_y = mouse_y;


var dist = min(300, point_distance(relative_x, relative_y, mouse_x, mouse_y));

var dir = point_direction(relative_x, relative_y, mouse_x, mouse_y);
 
Last edited:

samspade

Member
Well, let's say the mouse is on the right edge of the screen (and the crosshair on the right edge of the circle) and I want to move the crosshair to the left edge of the circle - I have to move the mouse to the left until it enters the circle and THEN I can move the crosshair to the left.

What I want to do is to move the crosshair to the left as soon as I start to move the mouse to the left.


And about the custom cursor, wouldn't it do the exact same thing ?
I think I understand and @Soso gives a decent solution, but I feel like from a design point, this is an odd, possibly bad, decision. Is there a game you're thinking of that does this? Is it for mobile? I ask because knowing why you want this to happen could help in providing the condition that sets the relative x and y.
 

TheouAegis

Member
So the issue is not trying to change the angle of the crosshair around the circle, but trying to change the radius of the circle relative to the movement of the mouse? Are you hiding the actual mouse and only using your crosshair object? If the object controlling the crosshair would be centered in the view at all times (this would be buggy otherwise), or at least restricted from moving too close to the edges of the view/room, then you could adjust the radius multiplicatively. For example, here's a quick mockup I did:
Code:
var diag = sqrt(sqr(room_width)+sqr(room_height))/2;
var dist = point_distance(x,y,mouse_x,mouse_y)/diag;
dist = clamp(dist,0,1) * 300;
var dir = point_direction(x,y,mouse_x,mouse_y);
obj_crosshair.x = x + lengthdir_x(dist,dir);
obj_crosshair.y = y + lengthdir_y(dist,dir);
If the mouse goes outside the window, it glitches up because we're using mouse_x and mouse_y, of course. And if the object is too close to the edges - that is, if the distance to the edge is less than the value stored in diag - then the crosshair wouldn't be able to fully extend. But still, it's just an idea.
 
ok I see you want to move the crosshair relative to the current mouse_x,y
the tricky part is deciding when you want this to happen

GML:
//create
relative_x = 0;
relative_y = 0;


//step
if(some condition (mouse_click?))
relative_x = mouse_x;
relative_y = mouse_y;


var dist = min(300, point_distance(relative_x, relative_y, mouse_x, mouse_y));

var dir = point_direction(relative_x, relative_y, mouse_x, mouse_y);
I think I understand and @Soso gives a decent solution, but I feel like from a design point, this is an odd, possibly bad, decision. Is there a game you're thinking of that does this? Is it for mobile? I ask because knowing why you want this to happen could help in providing the condition that sets the relative x and y.


That's a very nice point - unfortunately, I don't think I can use a condition here, since I would need it to run constantly.

If I could find a way of getting the previous mouse coordinates, I could set the vector from there...

I can't think about a game with a similar mecanic, but I don't think it is very uncommon.



So the issue is not trying to change the angle of the crosshair around the circle, but trying to change the radius of the circle relative to the movement of the mouse? Are you hiding the actual mouse and only using your crosshair object? If the object controlling the crosshair would be centered in the view at all times (this would be buggy otherwise), or at least restricted from moving too close to the edges of the view/room, then you could adjust the radius multiplicatively. For example, here's a quick mockup I did:
Code:
var diag = sqrt(sqr(room_width)+sqr(room_height))/2;
var dist = point_distance(x,y,mouse_x,mouse_y)/diag;
dist = clamp(dist,0,1) * 300;
var dir = point_direction(x,y,mouse_x,mouse_y);
obj_crosshair.x = x + lengthdir_x(dist,dir);
obj_crosshair.y = y + lengthdir_y(dist,dir);
If the mouse goes outside the window, it glitches up because we're using mouse_x and mouse_y, of course. And if the object is too close to the edges - that is, if the distance to the edge is less than the value stored in diag - then the crosshair wouldn't be able to fully extend. But still, it's just an idea.


Hmm, I tried it but it doesn't seem to work - now the crosshair is not even limited to the circle; it just follows the mouse all along the screen.









Thank you all for the help so far!
 
Last edited:

TailBit

Member
Have a point in this circle that you can move, you place the cursor at the edge by aiming to that point which is always in the circle, when you move the mouse, apply the vector to the point, then pull it back into the circle if it happens to move outside:
1631405866072.png
Maybe that will work
 
Have a point in this circle that you can move, you place the cursor at the edge by aiming to that point which is always in the circle, when you move the mouse, apply the vector to the point, then pull it back into the circle if it happens to move outside:
View attachment 42938
Maybe that will work


I'm not sure I got it. Doesn't the crosshair do that already ? I'll upload a GIF showing the situation:


ezgif.com-gif-maker.gif


Oh, and maybe you know that Player sprite, just provisory xD
 

TheouAegis

Member
In my code, change room_width and room_height to the width and the height of the camera view, if you're using a camera.
 

TailBit

Member
No this is more like the relative position part that Soso told you about
GML:
// create
pos = {x:0,y:0};
prev = {x:mouse_x,y:mouse_y}; // previous mouse position

// step
pos.x += mouse_x - prev.x;
pos.y += mouse_y - prev.y;

prev.x = mouse_x;
prev.y = mouse_y;

var dist = point_distance(0,0,pos.x,pos.y);
var dir = point_direction(0,0,pos.x,pos.y);

if dist>300{
    pos.x = lengthdir_x(300,dir);
    pos.y = lengthdir_y(300,dir);
}

x = obj_player.x + pos.x;
y = obj_player.y + pos.y;
 
In my code, change room_width and room_height to the width and the height of the camera view, if you're using a camera.


Hmm, I'm not using camera, but it still isn't working here. Does it work for you ?



No this is more like the relative position part that Soso told you about
GML:
// create
pos = {x:0,y:0};
prev = {x:mouse_x,y:mouse_y}; // previous mouse position

// step
pos.x += mouse_x - prev.x;
pos.y += mouse_y - prev.y;

prev.x = mouse_x;
prev.y = mouse_y;

var dist = point_distance(0,0,pos.x,pos.y);
var dir = point_direction(0,0,pos.x,pos.y);

if dist>300{
    pos.x = lengthdir_x(300,dir);
    pos.y = lengthdir_y(300,dir);
}

x = obj_player.x + pos.x;
y = obj_player.y + pos.y;


Ah, I see!


Yes, that one is setting the vector correctly - unfortunately, it still doesn't work correctly when the mouse is on the edges of the screen.

For example: if the mouse is on the top right corner and the crosshair is pointing at it, I can no longer move the crosshair up or to the right, since the mouse now can only move down or to the left.


Almost... Maybe trying to lock the mouse in the circle is still the best way to solve it.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
For example: if the mouse is on the top right corner and the crosshair is pointing at it, I can no longer move the crosshair up or to the right, since the mouse now can only move down or to the left.
I don't get the problem here? If the mouse is in the top right and the crosshair is pointing at it then it can no longer go up right anyway as it's already up and right???? I 've read through this topic and I honestly feel like I'm missing something... First, the GIF you posted looks fine, tbh, and second, the code posted by @TailBit is exactly what you seem to be asking for... only it's not? Maybe step back a moment and try to explain, without code, exactly what the concept for this mouse aim is? I'd also say that you DEFINITELY do NOT want to lock the mouse in the screen. Nobody likes the mouse control to be taken away/limited and people will hate it.
 

TailBit

Member
If I had those controlls I would hide the cursor and return it to center of screen, but then previous position would always be the center..

You could use the Esc / P(ause) to free it again? Or when you open some menus? Then it could be a smart trick to already move cursor into the window when called upon.
 
I don't get the problem here? If the mouse is in the top right and the crosshair is pointing at it then it can no longer go up right anyway as it's already up and right???? I 've read through this topic and I honestly feel like I'm missing something... First, the GIF you posted looks fine, tbh, and second, the code posted by @TailBit is exactly what you seem to be asking for... only it's not? Maybe step back a moment and try to explain, without code, exactly what the concept for this mouse aim is? I'd also say that you DEFINITELY do NOT want to lock the mouse in the screen. Nobody likes the mouse control to be taken away/limited and people will hate it.


Hi.


So, here is a representation of what's happening with @TailBit's code:


Sem Título.png


I won't get to move the crosshair up or to the right anymore, unless I go back with the mouse a bit.

Besides that, it's working totally fine.


The problem with the GIF's one is what I explained: if the mouse is, for example on the right side of the screen, the crosshair will not move to the left as soon as I start moving the mouse to the left (as you can see in the GIF itself)


Also, I never understand the problem of locking the mouse... Of course you could pause the game and move it freely. Don't FPS games, for exemple, work like that ?


Plus, I have to add: English is not my native language, so I definetely am not explaining things as well as i'd like to.



If I had those controlls I would hide the cursor and return it to center of screen, but then previous position would always be the center..

You could use the Esc / P(ause) to free it again? Or when you open some menus? Then it could be a smart trick to already move cursor into the window when called upon.


You could pause and move it freely, for sure.
 

TheouAegis

Member
Hmm, I'm not using camera, but it still isn't working here. Does it work for you ?
Yes it worked for me, except where stated.

If the mouse goes outside the window, it glitches up because we're using mouse_x and mouse_y, of course. And if the object is too close to the edges - that is, if the distance to the edge is less than the value stored in diag - then the crosshair wouldn't be able to fully extend. But still, it's just an idea.
And also, if taken at face value, the mouse would be unresponsive at certain distances, but it would be less noticeable. Modifying the "diag" line could alleviate that.

All I can say is post your adaptation of my code, if you want.
 
Yes it worked for me, except where stated.



And also, if taken at face value, the mouse would be unresponsive at certain distances, but it would be less noticeable. Modifying the "diag" line could alleviate that.

All I can say is post your adaptation of my code, if you want.


I just realized your code is supposed to be in the Player object and I was putting it in the Crosshair object. xD

But now, for some reason, the crosshair simply doesn't exist. :D
 

TheouAegis

Member
Like I said, post the code you ended up using. But if all you did was change the object names, it should have worked within reason.
 

Soso

Member
about as close as I could get to what I think you wanted



GML:
//create
out_of_circle = false;

//step
var dist1 = min(600,point_distance(obj_player.x, obj_player.y, mouse_x, mouse_y)) ;

var dist2 = min(300,point_distance(obj_player.x, obj_player.y, mouse_x, mouse_y)) ;
var dir = point_direction(obj_player.x, obj_player.y, mouse_x, mouse_y);



if (dist >= 300 && !out_of_circle)
{
   out_of_circle = true;
}

if (dist <= 299)
{
out_of_circle = false;
}


if (out_of_circle)
{
x = obj_player.x + lengthdir_x(dist1 /2 , dir );
y = obj_player.y + lengthdir_y(dist1 /2 , dir );
}
else
{
x = obj_player.x + lengthdir_x(dist2, dir);
y = obj_player.y + lengthdir_y(dist2, dir);
}
 
Like I said, post the code you ended up using. But if all you did was change the object names, it should have worked within reason.


Yes, I just switched the object names. Here it is:

GML:
var diag = sqrt(sqr(room_width) + sqr(room_height)) / 2;

var dist = point_distance(obj_player.x, obj_player.y, mouse_x, mouse_y) / diag;

var dir = point_direction(obj_player.x, obj_player.y, mouse_x, mouse_y);


dist = clamp(dist, 0, 1) * 300;



x += lengthdir_x(dist,dir);
y += lengthdir_y(dist,dir);

I just added some space and moved the "dir" line, but I first tested it exactly as you posted, just changing the names.



about as close as I could get to what I think you wanted



GML:
//create
out_of_circle = false;

//step
var dist1 = min(600,point_distance(obj_player.x, obj_player.y, mouse_x, mouse_y)) ;

var dist2 = min(300,point_distance(obj_player.x, obj_player.y, mouse_x, mouse_y)) ;
var dir = point_direction(obj_player.x, obj_player.y, mouse_x, mouse_y);



if (dist >= 300 && !out_of_circle)
{
   out_of_circle = true;
}

if (dist <= 299)
{
out_of_circle = false;
}


if (out_of_circle)
{
x = obj_player.x + lengthdir_x(dist1 /2 , dir );
y = obj_player.y + lengthdir_y(dist1 /2 , dir );
}
else
{
x = obj_player.x + lengthdir_x(dist2, dir);
y = obj_player.y + lengthdir_y(dist2, dir);
}


Hmm, you forgot to set the "var dist" here, but I believe you wanted it to be "var dist = point_distance(obj_player.x, obj_player.y, mouse_x, mouse_y);", so I set it like that. Correct me if I'm wrong.

Sadly, it still has that problem with the edges I mentioned, and now the crosshair glitches when the mouse goes in and out of the circle.

Did you test this one ?
 

TheouAegis

Member
x += lengthdir_x(dist,dir);
y += lengthdir_y(dist,dir);
This is different from what I wrote. You are just scaling the crosshair's x and y here. What I said to do is add those lengthdir values to the player's x and y in order to calculate the crosshair's x and y.
 
This is different from what I wrote. You are just scaling the crosshair's x and y here. What I said to do is add those lengthdir values to the player's x and y in order to calculate the crosshair's x and y.


You're right! What a lack of attention! xD

Solved.

Now it works as intended, but that problem with the edges of the screen still occurs, exactly as with @TailBit's code.
 

TheouAegis

Member
I think it is because we have been using mouse_x and mouse_y, which are in the room, even though the mouse technically goes out of the window (probably). You could try messing around with window_mouse_get_x() and *_y().
 
Top