Handling mouse for first person game in monogame - c#

I am trying to make a first-person game on Monogame and so far all I have come with or found on the internet didn't meet my standards.
this is how I am currently handling the mouse:
private void HandleMouse()
{
Vector2 mouseDifference;
mouseNow = Mouse.GetState();
if (mouseNow.X != mouseDefaultPos.X || mouseNow.Y != mouseDefaultPos.Y)
{
mouseDifference.X = mouseDefaultPos.X - mouseNow.X;
mouseDifference.Y = mouseDefaultPos.Y - mouseNow.Y;
leftrightRot += mouseSens * mouseDifference.X;
updownRot += mouseSens * mouseDifference.Y;
Mouse.SetPosition((int)mouseDefaultPos.X, (int)mouseDefaultPos.Y);
UpdateViewMatrix();
}
}
private void UpdateViewMatrix()
{
Matrix cameraRotation = Matrix.CreateRotationX(updownRot) * Matrix.CreateRotationY(leftrightRot);
Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);
Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);
Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);
viewMatrix = Matrix.CreateLookAt(new Vector3(0,0,0), cameraRotatedTarget, cameraRotatedUpVector);
}
My problem is that while this solution works, it is extremely inconsistent when it comes to how far the camera should rotate.
for example, happens when I make circles with the mouse and see that sometimes the mouse randomly jumps more than expected.
My main assumptions inconsistencies in fps that cause the time between frames to change, thus affecting the distance the mouse can move within that time.
I don't know if this is the reason or if my assumption can even cause this, but I would like to find a way to get consistent mouse movement.
I can provide a video of the problem if needed.
Thank you in advance.

The mouse in Windows is updated 240 times per second. The game loop runs at 60 frames per second. The discrepancy can lead to large mouse deltas. The solution is to limit the change in the mouse delta:
Vector2 mouseDifference;
const float MAXDELTA = 6; // Set to the appropriate value.
mouseNow = Mouse.GetState();
if (mouseNow.X != mouseDefaultPos.X || mouseNow.Y != mouseDefaultPos.Y)
{
mouseDifference.X = Math.Min(MAXDELTA, mouseDefaultPos.X - mouseNow.X);
mouseDifference.Y = Math.Min(MAXDELTA, mouseDefaultPos.Y - mouseNow.Y);
leftrightRot += mouseSens * mouseDifference.X;
updownRot += mouseSens * mouseDifference.Y;
Mouse.SetPosition((int)mouseDefaultPos.X, (int)mouseDefaultPos.Y);
UpdateViewMatrix();
}

This is an old thread, but I figured I would share a solution. The solution that's worked for me to use the GameTime object to relate how much the player should rotate by. In other words, rotate by (delta * rotationSpeed * GameTime.ElapsedTime.TotalSeconds) so that each rotation is relative to how much time has passed since the last frame. This protects it against frame drops, which I've found has been the problem for me.

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.

Make time measuring independent of FPS (2D C#)

Lets say I'm working in a 2D plane, with a person in the bottom of my screen, and an object that moves down along the Y-axis.
The moving object has following movement code:
transform.position += new Vector3 (0, -1, 0) * speed * Time.deltaTime;
Now, as I want to be able to modify how long it takes for the object to connect with the player, I created this, where spawnTimeDistance is our key factor:
Instantiate (obstacle, player.transform.position + new Vector3(-0.50F , (spawnTimeDistance + obstacleDimensionY + pScript.playerDimensionY), -1), Quaternion.identity);
As can be seen, spawnTimeDistance is a place-holder for the objects Y-distance to the player. However, using this code I get great inconsistencies with my results, especially when comparing devices. I suspect this is caused by distance being FPS dependant, instead of actual time dependant.
To fix this I believe I should implement Time.deltaTime in an extended manner, but I'm insecure on how.
EDIT - spawnDistanceTime related:
void Start() {
spawnTimeDistance = 4F;
}
and
Vector2 sprite_size = obstacle.GetComponent<SpriteRenderer> ().sprite.rect.size;
Vector2 spriteScale = obstacle.transform.localScale;
float sizeAndScaleY = sprite_size.y * spriteScale.y;
float obstacle_local_sprite_sizeY = (sizeAndScaleY / obstacle.GetComponent<SpriteRenderer> ().sprite.pixelsPerUnit) * 0.5F;
obstacleDimensionY = obstacle_local_sprite_sizeY;
Above code, same thing for my player.
Edited the Instantiate to include the obstacleDimensions, had them removed before as I thought it would give a simpler question.

Change position of current gameObject in C# on condition

I have been searching and racking my brains for a while now to try and get this code working, to no avail. Hopefully you guys can help.
I have a simple set up with a cube that moves position every frame. I need the cube to go to an x position when it reaches a different location.
Example: Cube starts at position 0, moves forward in the x axis until it gets to position 15, then reverts back to 0 and stops.
Vector3 startingPosition;
void Start ()
{
startingPosition = gameObject.transform.position;
}
void Update ()
{
if (gameObject.transform.position.x == 15) {
gameObject.transform.position = startingPosition;
} else {
float translation = Time.deltaTime * 2;
transform.Translate (0, 0, translation);
transform.Translate (Vector3.forward * translation);
}
}
}
Currently the cube continuously moves (no stopping point), it's x position having no affect on the positioning.
Change your == to >= and see if that makes a difference. My guess is that position.x is never exactly equal to 15, either due to floating-point precision errors or due to your translation logic skipping over 15 from one frame to the next.
I have had an issue with a animation not working correctly and feel like I have scoured the internet for days trying to get it to work, so this was very helpful. In my situation, the animations' GameObject was at a position of y=1 and should have moved to y=9, but would not update unless I clicked the transition in the animator. I changed the code from x == 15 to y <= 9 and Vector3.forward to Vector3.up and it works perfectly now. Hope this might help someone else with the same issue. using Unity v 2017.1.2

Smoothly Lerp object based on button press Unity

Im creating a speedometer in Unity and I want my speedo arrow to smoothly lerp up to it's max angle based on the wither or not a button is pressed on. When the button is let go, I'd like the arrow to fall back to it's original rotation value.
However, I'm having issues trying to work out how to make my object lerp from its original position, to it's new position and back again. I've got it so when I press a button the objects jumps to a position, however, I need it to be smoother. Could someone please look over my code and point out what I need to do please?
float maxAngle = 100.0f;
float maxVel = 200.0f;
Quaternion rot0;
public float rotateValue;
// Use this for initialization
void Start ()
{
rot0 = transform.localRotation;
}
// Update is called once per frame
void Update ()
{
if(Input.GetKey(KeyCode.Space))
{
SetNeedle(rotateValue);
}
}
void SetNeedle(float vel)
{
var newAngle = vel * maxAngle / maxVel;
transform.localRotation = Quaternion.Euler(newAngle,0,0) * rot0;
float angle = Mathf.LerpAngle(minAngle, maxAngle, Time.time);
transform.eulerAngles = new Vector3(0, angle, 0);
}
From the documentation:
static float Lerp(float from, float to, float t);
Interpolates between a and b by t. t is clamped between 0 and 1.
Which means t goes from 0% to 100%. When t == 0.5f the result will be the middle angle between from to to.
Hence, if the needle needs to be moved in a smooth and constant speed, you should do:
time += Time.deltaTime; // time is a private float defined out of this method
if(time >= 1.5f) // in this example the needle takes 1.5 seconds to get to its maximum position.
time = 1.5f // this is just to avoid time growing to infinity.
float angle = Mathf.LerpAngle(from, to, time/1.5f); // time/1.5f will map the variation from 0% to 100%.
But that requires the from and to to be constant during the lerp fase.
So what you need to do is the following:
when you press Space, set time = 0, from = currentAngle and to = maxAngle; Obviously this must be done only once during the key down event.
when you release Space, set time = 0, from = currentAngle and to = 0. Also do this only once.
All right?
You should use Time.deltaTime, not Time.time. That should make it move correctly.
EDIT: deltaTime won't solve the problem alone, however. Sorry I didn't fully read your code at first.
In order to smoothly move the needle, you need to know where the needle is currently, where it needs to go, and how much time has elapsed since the last time it moved.
We know how much time has elapsed with Time.deltaTime. But your code currently doesn't take into consideration where the needle is. float angle = Mathf.LerpAngle(minAngle, maxAngle, Time.deltaTime); will always evaluate to roughly the same value, so your needle will never move beyond a tiny bit.
What you should do instead is lerp from the needle's current position towards the maxAngle.
float angle = Mathf.LerpAngle(currentAngle, maxAngle, Time.deltaTime);

Reset model rotation when input stops

I have the model representing the player's ship gradually leaning when the player strafes. For instance, here's the code that leans the ship right:
In Update() of the Game class:
if (ship.rightTurnProgress < 1 && (currentKeyState.IsKeyDown(Keys.D)))
{
ship.rightTurnProgress += (float)gameTime.ElapsedGameTime.TotalSeconds * 30;
}
In Update() of the Ship class:
if (currentKeyState.IsKeyDown(Keys.D))
{
Velocity += Vector3.Right * VelocityScale * 10.0f;
RotationMatrix = Matrix.CreateRotationX(MathHelper.PiOver2) *
Matrix.CreateRotationY(0.4f * rightTurnProgress);
}
This is what I'm attempting to do to make it ease back out of the lean when it stops strafing:
In Update() of the Game class:
if (ship.rightTurnProgress > 0 && currentKeyState.IsKeyUp(Keys.D))
{
ship.rightTurnProgress -= (float)gameTime.ElapsedGameTime.TotalSeconds * 30;
}
In Update() of the Ship class:
if (currentKeyState.IsKeyUp(Keys.D) && rightTurnProgress > 0)
{
RotationMatrix = Matrix.CreateRotationX(MathHelper.PiOver2) *
Matrix.CreateRotationY(-0.4f * rightTurnProgress);
}
Since easing into the lean works no problem, I thought easing out of the lean would be a simple matter of reversing the process. However, it tends to not go all the way back to the default position after a long strafe. If you tap the key, it snaps all the way back to the full lean of the -opposite- direction. This isn't what I expected at all. What am I missing here?
I suggest you represent the rotation of you ship as a quaternion. That way you can use an interpolation function such as slerp. Simply have a second quaternion that represents you targeted lean angle and the ship will smoothly rotate until it achieves the targeted angle.
Here's a good tutorial on quaternions. If you want to avoid quaternions use MathHelper.Lerp to smoothly transition from the current value to the target.
if (currentKeyState.IsKeyDown(Keys.D))
{
ship.TurnProgress = MathHelper.Lerp(ship.TurnProgress, 1, somefloat * timeDelta);
}
else if (currentKeyState.IsKeyDown(Keys.a))
{
ship.TurnProgress = MathHelper.Lerp(ship.TurnProgress, -1, somefloat * timeDelta);
}
else (currentKeyState.IsKeyDown(Keys.D))
{
ship.TurnProgress = MathHelper.Lerp(ship.TurnProgress, 0, somefloat * timeDelta);
}
Edit: Also there is a GameDev stack overflow so check it out if you have more questions.
Unless you know how long the turn is or you have some kind of acceleration vector you will have to wait until the turn is stopped before returning the sprite angle to neutral, then what happens when the player turns left before the sprite has reached its neutral position? I assume that when you turn right using RightTurnProgress you also have a LeftTurnProgress I suggest you combine them into one variable to keep it smooth and avoid the snapping effect you are getting.
You are creating an 'absolute' rotation matrix so you don't need to flip the sign to -0.4f. Why not just have a variable called ship.lean and calculate the rotation matrix every update. Then you just need logic to ease ship.lean between -1 (left lean) and 1 (right lean) or 0 for no lean.

Categories

Resources