Tracking mouse movement and magnitude - c#

I'm making a simple game where the player controls a tank. The rotation of the turret will be controlled by mouse movements. The code currently looks like this:
if (Game.MouseState.Y < yMovementBorder)
PossessedTurretPitchValue += dist;
if (Game.MouseState.Y > yMovementBorder)
PossessedTurretPitchValue -= dist;
if (Game.MouseState.X < xMovementBorder)
PossessedTurretYaw += rotationDist / 6;
if (Game.MouseState.X > xMovementBorder)
PossessedTurretYaw -= rotationDist / 6;
xMovementBorder and yMovementBorder are values representing the midpoint of the game screen. The problem is that any movement of the mouse will cause the turret to turn until its maximum pitch/yaw angle. How can I make it such that it will be able to read the mouse movement as well as its magnitude(ie: slight movement of the mouse will only cause a slight pitch/yaw movement of the turret)?

In your update method, you should store references to your previous mouse state and current mouse state. Then use these two variables to figure out how much the mouse moved since the last update. It will take some tweaking to get it just right, but try something like this:
//define your private variables
private MouseState prevMouseState = null;
private MouseState currMouseState = Game.MouseState;
public void Update(GameTime gt)
{
prevMouseState = currMouseState;
currMouseState = Game.MouseState;
//calculate how much the mouse has moved since the last update
var dX = currMouseState.X - prevMouseState.X;
var dY = currMouseState.Y - prevMouseState.Y;
//do your rotating depending on the values of dX and dY
}

I think what you want to do is set limits based on how far from center the mouse is.
Like:
if (Game.MouseState.Y < yMovementBorder)
if (PossessedTurretPitchValue < yMovementBorder*someFactor)
PossessedTurretPitchValue += dist;
And similarly for the other three cases.
Without setting a limit, any deviation from the center will cause the turret to move. It will be moving all the time, and eventually rail somewhere. This is what your code does now, if I understand correctly.

Related

Unity3D Position and Rotation matching on Waypoints

I have a simple waypoint system. It uses a transform of arrays that act as the bucket what holds the waypoint values.
I use this waypoint system to move a camera throughout a scene by moving towards one point to another. The scene is not big - so everything is close to each other.
The camera moves from one position to another by button click/press. This works fine.
void Start()
{
//Sets the Camera to the first point
Camera = GameObject.Find("Main Camera");
Camera.transform.position = patrolPoints[0].position
currentPoint = 0;
}
//Fixed Update seems to work better for LookAt
void FixedUpdate()
{
//Looks at initial Target
Camera.transform.LookAt(TargetPoints[0]);
if (click == true)
{
Camera.transform.position = Vector3.MoveTowards(Camera.transform.position, patrolPoints[currentPoint].position, moveSpeed * Time.deltaTime);
//Camera.transform.rotation = Quaternion.Slerp(Camera.transform.rotation, patrolPoints[currentPoint].transform.rotation, Time.deltaTime);
Camera.transform.LookAt(TargetPoints[currentPoint]);
}
}
public void onNextClick()
{
if (currentPoint >= patrolPoints.Length)
{
currentPoint = 0;
}
if (Camera.transform.position == patrolPoints[currentPoint].position)
{
currentPoint++;
click = true;
}
}
I am having difficulty with one aspect of the transform that I haven't talked about yet. That is the rotation.
I have used lookAt for setting up the target of the first look at point and that works. However when it runs to the next target in the look at array - the change is sudden.
I have tried an Slerp in the shot as well - and this works when the waypoint has been placed in the appropriate rotation value - and the value Slerps from one position to the next. However, the timing isn't quite aligning up yet. Some position transitions get there quicker - meaning the rotation is trying to get caught up / while others are lagging behind.
I have tried getting the distance between the current waypoint and the next waypoint and treating that as an overall percentage in the update relative to the current position of the camera - I believe this could help work out how far the rotation should be orientated given the position update.
However, I am somewhat lost on it. Many examples suggest looking at iTween - and I wouldn't imagine this would work - however, I want to get into the math a bit.
Can anyone put me in the right direction?
Looks like the Lerp for Position and a Slerp for Rotation done the trick.
MoveTowards wasn't playing ball with a Slerp on rotation - so I believe the timings weren't aligning.
if (click == true)
{
Camera.transform.position = Vector3.MoveTowards(Camera.transform.position, patrolPoints[currentPoint].position, moveSpeed * Time.deltaTime);
Camera.transform.rotation = Quaternion.Lerp(Camera.transform.rotation, patrolPoints[currentPoint].rotation, moveSpeed * Time.deltaTime);
I've been led to believe the lerp values work like a percentage of such - so the same input value works for it.
Finally I used a range on the distance between current position and update on the click - this helped speed up the button click.
if (Vector3.Distance(Camera.transform.position, patrolPoints[currentPoint].position) < PositionThreshold)
{
currentPoint++;
click = true;
}
Thank you for your time.

Unity Circle calculation

for my programm im programming a "clock like" behaviour. Meaning i
order some pictures in a circle (works)
if i click and drag on any item all items should rotate with the mouse (works)
But i get a weird bug. The first time i click and hold the mouse my images "jump" to different positions. if i hold the mouse down i can rotate my clock ust fine.
When i MouseUp and start dragin from the same image it works well. if i go to another image i get this "Jump" again.
When i only have a few images on my clock i see that it doesnt jump. but the start position seems to bee off.
When i only have one item, i can rotate it in a circle, but the moment i start to rotate it jumps away from my mouse and than i can rotate it as desired.
For me it seems to be a wrong "starting point" when first dragging an item. SInce it works fine when i than drag the same item again and again.
Unfortunately i cant find the damn bug, and im searching the whole day already.
#
public void SetLayoutHorizontal ()
{
Debug.Log ("LAYOUT");
for (var i =0; i < Rect.childCount; i++)
{
var PanelPrefab = Rect.GetChild (i) as RectTransform;
Transform ImageObject = PanelPrefab.GetComponentInChildren<Transform>().Find("Image");
if (PanelPrefab == null)
continue;
PanelPrefab.sizeDelta = CellSize;
PanelPrefab.anchoredPosition = new Vector2(radius * Mathf.Sin( CalculateCircleAngle(i) - deltaRadian),radius * Mathf.Cos(CalculateCircleAngle(i) - deltaRadian));
}
}
private float CalculateCircleAngle(int parts)
{
//parts == Number of parts the whole circle is to be cut into
return parts * (360/Rect.childCount) * (Mathf.PI/180);
}
public void OnDrag (PointerEventData eventData)
{
var diffX = eventData.position.x - _rect.rect.width/2; // MouseX - CenterScreenX
var diffY = eventData.position.y - _rect.rect.height/2; // MouseY - CenterScreenY
deltaRadian = Mathf.Atan2(diffY,diffX);
SetDirty();
}
Edit:
Ok i Edited the code but it still is not working.
I added the following method:
public void OnBeginDrag(PointerEventData eventData)
{
originalX = eventData.position.x;
originalY = eventData.position.y;
}
and i changed the drag method acordingly
public void OnDrag (PointerEventData eventData)
{
var diffX = eventData.position.x - originalX;
var diffY = eventData.position.y - originalY;
deltaRadian = Mathf.Atan2(diffY,diffX);
SetDirty();
}
The "Jumping" at the beginning of my drag event is gone, but the speed of the draggin is not on par with my mouse.
The closer i am to my starting point of the drag, the faster it moves, the further away i am the slower it gets.
I dont know if this brought me closer to a solution or further away :(
Edit2:
Ok i think the problem might be that my calculations were all done from the center point of view as 0,0 point.
Unity has the bottom left point as 0,0. So i somehow have to translate all those coordiantes first...
All that was needed was a transformation to kartesian coordinates
//Convert to kartesian coordinates with 0,0 in center of screen
var diffX = (eventData.position.x - _rect.rect.width / 2) % (_rect.rect.width / 2);
var diffY = (eventData.position.y - _rect.rect.height / 2) % (_rect.rect.height / 2);
and an addition of the delta instead of the subtraction
PanelPrefab.anchoredPosition = new Vector2(radius * Mathf.Sin(CalculateCircleAngle(i) + deltaRadian),radius * Mathf.Cos(CalculateCircleAngle(i) + deltaRadian));
Your mouse coordinates are relative to the control you click in. You could re-calculate them using the control's position within its container or you use absolute coordinates.
I don't think you are displaying all the relevant code, but your OnDrag function, specifically diffX, and diffY probably provide the answer for you.
I believe you're taking the absolute coordinates of the location you pick, and using them to generate your angle - which means that almost anywhere you click is a big jump from however the current angle is set. Your initial click handler should save off a starting coordinate, and your OnDrag should compare itself against that initial coordinate, based on how far you've dragged from that saved location.

Unity3d get Mouse Input Axis

I want to rotate a GameObject through mouse movement between -20° and +20°.
I know that there's a possibility to grab the smoothed Input through
Input.GetAxis("Horizontal"); and Input.GetAxis("Vertical"); which returns float values between -1 and 1
It would be nice if there would exist something like
Input.GetMouseAxis("Horizontal"); or Input.GetMouseAxis("Horizontal");.
But I the output shouldn't be influenced by keyboard.
I'm sorry that I don't have much experience with Unity and its API.
is the mouse in unity "captured" in screen resolution or how should I
scale the mouse Position to a value between -1 and 1 ?
In this case the "normalizedSpeed" will be your target
public float mouseDistance;
public Vector2 mouseDown = -Vector2.one;
public float normalizedSpeed;
public void Update()
{
if(Input.GetMouseButtonDown(0))
mouseDown = Input.MousePosition();
if(Input.GetMouseButtonUp(0))
mouseDown = -Vector2.one;
if(Input.GetMouseButton(0))
normalizedSpeed = mouseDown != -Vector2.one ?
Mathf.Clamp((mouseDown - Input.MousePosition()).sqrMagnitude, 0, (mouseDistance * mouseDistance)) / (mouseDistance * mouseDistance)
: 0;
}
i use sqrMagnitude instead of distance because the sqr root call in distance() takes alot of memory, so it is faster to compare the squared distances vs each other
Also keep in mind im wirting this as a pseudo-code so the overall idea is what im going for, im leaving you syntactically responsible for the implementation ;)

mouse picking a rotating sprite in an Xna game

I am working on a simple game where you click on square sprites before they disappear. I decided to get fancy and make the squares rotate. Now, when I click on the squares, they don't always respond to the click. I think that I need to rotate the click position around the center of the rectangle(square) but I am not sure how to do this. Here is my code for the mouse click:
if ((mouse.LeftButton == ButtonState.Pressed) &&
(currentSquare.Contains(mouse.X , mouse.Y )))
And here is the rotation logic:
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
RotationAngle += elapsed;
float circle = MathHelper.Pi * 2;
RotationAngle = RotationAngle % circle;
I am new to Xna and programming in general, so any help is appreciated.
Thanks a lot,
Bill
So you're trying to determine if a point is in a rectangle, but when the rectangle is rotated?
The Contains() method will only work if the current rotation is 0 (I guess currentSquare is a rectangle representing the image position without rotation?).
What you will have to do is do the opposite rotation of the image on the mouse coordinates (the mouse coordinates should rotate around the origin of your image), then calculate if the new position is within currentSquare. You should be able to do all of this using vectors.
(Untested)
bool MouseWithinRotatedRectangle(Rectangle area, Vector2 tmp_mousePosition, float angleRotation)
{
Vector2 mousePosition = tmp_mousePosition - currentSquare.Origin;
float mouseOriginalAngle = (float)Math.Atan(mousePosition.Y / mousePosition.X);
mousePosition = new Vector2((float)(Math.Cos(-angleRotation + mouseOriginalAngle) * mousePosition.Length()),
(float)(Math.Sin(-angleRotation + mouseOriginalAngle) * , mousePosition.Length()));
return area.Contains(mousePosition);
}
If you dont need pixel pefect detection you can create bounding sphere for each piece like this.
var PieceSphere = new BoundingSphere()
{
Center =new Vector3(new Vector2(Position.X + Width/2, Position.Y + Height/2), 0f),
Radius = Width / 2
};
Then create another bounding sphere around mouse pointer.For position use mouse coordinates and for radius 1f. Because mouse pointer will be moving it will change its coordinates so you have to also update the sphere's center on each update.
Checking for clicks would be realy simple then.
foreach( Piece p in AllPieces )
{
if ((mouse.LeftButton == ButtonState.Pressed) && p.BoundingSphere.Intersects(MouseBoundingSphere))
{
//Do stuff
}
}
If you are lazy like me you could just do a circular distance check.
Assuming mouse and box.center are Vector2
#gets us C^2 according to the pythagorean Theorem
var radius = (box.width / 2).squared() + (box.height / 2).square
#distance check
(mouse - box.center).LengthSquared() < radius
Not perfectly accurate but the user would have a hard time noticing and inaccuracies that leave a hitbox slightly too large are always forgiven. Not to mention the check is incredibly fast just calculate the radius when the square is created.

Preventing tunneling with this setup?

I have a simple rectangle-tile collision scheme set up, and it works beautifully.
The only problem is when you start falling off of a ledge. Your speed reaches the point where the change in Y/X each frame is large enough for you to clip into solid objects and glitch about.
Basically my setup is as follows:
To start with, the player's position has its velocity added to it, so the player is now at the place he would be next frame if no collisions happen.
The list below is just a single function, checkIntersectTiles(Vector2 maskPos);
Calculate tiles around the character to check.
Loop through tiles, including those inside the bounding tiles.
Check the player's collision rectangle against each of these tiles.
If there's an intersection, move the largest offending axis out of the tile, then set that axis velocity to 0.
Continue checks.
When you clip into the ground, you jitter around inside, as my algorithm attempts to move you outside the tile that is most colliding with you.
My solution: Check each position from the player's pos, to the player's pos + velocity.
I'm stuck on that bit.
Can anyone give me a hand?
I assume that your code to move the player out of the colliding tile does so in a single step. So if the player collides with a tile you determine that the penetration depth is 5 in the Y direction you immediately adjust the player Y position by -5.
As you suggest, check the player position at each step. So if the Y velocity is 5 then you can adjust the players Y position by 1, check for collision and then repeat 4 more times. See later for calculations handling time stepping. The following is just some basic pseudo code and just in the Y direction.
player.Y += vel;
if (player.CheckCollisions())
{
// handle collision
}
Becomes
for (int i = 0; i < vel; ++i)
{
player.Y += 1;
if (player.CheckCollisions())
{
// handle collision
break;
}
}
That is the simple version if you are not adjusting for ellaped time. If you are, then you rather perform smaller time steps
So
player.Y += vel * elapsedTime;
if (player.CheckCollisions())
{
// handle collision
}
Becomes
float currentTimeStep = 0;
Position startY = player.Y;
while (currentTimeStep < elapsedTime)
{
currentTimeStep = Math.Min(currentTimeStep + stepDelta, elapsedTime); // You need to tune the stepDelta
player.Y = startY + vel * currentTimeStep;
if (player.CheckCollisions())
{
// handle collision
break;
}
}
In the above you will need to tune the time step delta to ensure that you balance performance with accuracy. You might event consider calculating the delta dynamically each frame to ensure that the adjustment is close to 1 pixel.

Categories

Resources