XNA, smoother jumping animations - c#

I'm trying to make jumping functionality in my Movement test. My character jumps and comes back down, but it's very choppy and not smooth at all.
What happens is he juts up to his max height, then comes down smoothly.
I can spot the problem, the for loop doesn't want to play nicely with the code. However, I don't know how to circumvent this. Is there any way to keep the button press and have him jump up nicely?
Code:
if (leftStick.Y > 0.2f && sprite.Position.Y == position.Y || isPressed(Keys.Up) == true && sprite.Position.Y == position.Y)
{
if (wasLeft == true)
{
sprite.CurrentAnimation = "JumpLeft";
}
else if (wasLeft == false)
{
sprite.CurrentAnimation = "JumpRight";
}
//This for loop is my issue, it works but it's jumpy and not smooth.
for (movement.PlayerHeight = 0; movement.PlayerHeight < movement.PlayerMaxHeight; movement.PlayerJump())
{
sprite.Position.Y -= movement.PlayerJump();
}
}
sprite.StartAnimation();
}
else
{
leftStick = NoInput(leftStick);
}
private Vector2 NoInput(Vector2 leftstick)
{
if (sprite.Position.Y < position.Y) //(movement.PlayerSpeed > 0)
{
sprite.Position.Y += movement.PlayerHeight;
movement.PlayerHeight -= movement.Player_Gravity;
//sprite.Position.Y += movement.PlayerSpeed;
//movement.PlayerSpeed -= movement.Player_Decel;
}
else
{
sprite.Position.Y = position.Y;
}
}
Movement class:
public float PlayerMaxHeight = 15f;
public float PlayerHeight = 0;
public float Player_Gravity = 0.01f;
private const float Player_Jump = 0.35f;
public float PlayerJump()
{
PlayerHeight += Player_Jump + Player_Gravity;
if (PlayerHeight > PlayerMaxHeight)
{
PlayerHeight = PlayerMaxHeight;
}
return PlayerHeight;
}

The best way to do jumping I found is to implement a property that will deal with acceleration.
A brief list of what to do:
Create a property that stores the current Y velocity.
Increment the Y velocity by a set amount each step - generally represented by a gravity property somewhere.
Increment1 the Y position by the Y velocity each step.
When you jump, simply subtract1 a said amount from the Y velocity - which will cause your player to jump up in an easing-out motion (start fast and slow down as he reaches the high of the jump). Because you're always incrementing the Y velocity, you will eventually reverse direction and return back to the surface.
When touching a surface, reset the Y velocity to zero.
1 Pretty sure that the Y axis is inverted in XNA (I work in Flash), so where I say increment the Y velocity you may need to decrement it instead - same deal for subtracting from it to jump.

My general approach to get a jump really quickly is to use a bleed off value to make slightly smoother looking movement. I can't look at any code/xna right now but my first thought would be something like below.
Define variables:
float bleedOff = 1.0f;
bool jumping = false;
Input update:
if(input.JumpKey())
{
jumping = true;
}
Jumping update:
if(jumping)
{
//Modify our y value based on a bleedoff
//Eventually this value will be minus so we will start falling.
position.Y += bleedOff;
bleedOff -= 0.03f;
//We should probably stop falling at some point, preferably when we reach the ground.
if(position.Y <= ground.Y)
{
jumping = false;
}
}
bleedOff = MathHelper.Clamp(bleedOff, -1f, 1f);
Obviously the bleedOff value should be calculated with a bit more randomness, probably using a gravity value, to it to make it look right but this will give the illusion of acceleration/decceleration with the jump as they rise and fall.
Rising very fast to begin with and slowing down and eventually starting to fall again and that will speed up. The clamp at the bottom will be your maximum vertical velocities.
I just wrote this off the top of my head at work so apologies if it's not quite what your looking for but I tried to keep it a bit more general. Hope it helps.

Related

Counting a full rotation with a circular drive

I've been raking my brain for the last day on how to calculate a full rotation with how to count a full rotation of an object along the X axis thats using the base circular drive from SteamVR.
I thought a simple 3d cube, with the mesh turned off in the the path of the rotation with collision code on it would be a barebone way of doing it, but it doesn't even seem to be registering the detection when the object hits the placed cubes, and i know its not because of me being stupid, as its recycled code from a working part of the project.
Below i have a small piece of code that basically detects when the object has reached the end of the rotation, and then increments the Count by one.
My main problem is that sometimes it manages to clock more than once, and if you can find the right spot, you can just keep it there and it'll keep on adding the count up by. Im wondering how i can stop it and increment only by one, until another full rotation has been made?
EDIT: to be more clear in case there is any confusion, Once the angle is clocked in between 359 and 360, i want it to increment once, whereas currently if you get the angle to sit anywhere in between 359-360 it will carry on adding one to the rotation count, despite no full rotation having been made, so im trying to figure out how to make my code only increment once, and once it does increment once it resets the position to zero, so therefore no more Increments can happen. It's a crank mechanism in VR, along the X axis.
Any help is appreciated, Thanks!
float Test;
float RotationCount = 0;
// Start is called before the first frame update
void Start()
{
// Test = transform.localRotation.eulerAngles.x;
}
// Update is called once per frame
void Update()
{
if (Test > 359 && Test < 360)
{
Debug.Log("Clocked");
count();
}
else
{
// Debug.Log("Nope");
}
if (Test == 0)
{
Debug.Log("Yes");
}
Test = transform.localRotation.eulerAngles.x;
}
void count()
{
RotationCount++;
}
sometimes it manages to clock more than once, and if you can find the right spot, you can just keep it there and it'll keep on adding the count up by.
well in
if (Test > 359 && Test < 360)
{
Debug.Log("Clocked");
count();
}
what happens if your angle is e.g. 359.5?
It is very difficult to just take a current rottaion and know whether it was turned more or less than a certain angle.
I'ld rather store the last rotation, compare it to the current one and add the difference to a variable. Than if the variable exceeds 360 a full rotation was done. Since I also don't like to calculate anything with Quaternion and eulerAngles, way simplier is to use the methods provided by Vector3.
For the local rotation around X (== transform.right) I would use the angle between the current and the last transfrm.up vector.
Something like
public class RotationCheck : MonoBehaviour
{
public int RotationCount;
public float rotatedAroundX;
public Vector3 lastUp;
public UnityEvent On3TimesRotated;
private void Awake()
{
rotatedAroundX = 0;
// initialize
lastUp = transform.up;
}
private void Update()
{
var rotationDifference = Vector3.SignedAngle(transform.up, lastUp, transform.right);
rotatedAroundX += rotationDifference;
if (rotatedAroundX >= 360.0f)
{
Debug.Log("One positive rotation done", this);
RotationCount++;
rotatedAroundX -= 360.0f;
}
else if (rotatedAroundX <= -360.0f)
{
Debug.Log("One negative rotation done", this);
RotationCount--;
rotatedAroundX += 360.0f;
}
// update last rotation
lastUp = transform.up;
// check for fire the event
if (RotationCount >= 3)
{
On3TimesRotated?.Invoke();
RotationCount = 0;
}
}
}
You can use a UnityEvent to get the same thing the Button uses for onClick so you can reference callbacks there via the inspector.
BUT if you don't care about the single rotations but actually only wnat the final RotationCount >= 3 I would actually use
private void Update()
{
var rotationDifference = Vector3.SignedAngle(transform.up, lastUp, transform.right);
rotatedAroundX += rotationDifference;
RotationCount = Mathf.RoundToInt(rotatedAroundX / 360.0f);
// update last rotation
lastUp = transform.up;
// check for fire the event
if (RotationCount >= 3)
{
On3TimesRotated?.Invoke();
RotationCount = 0;
rotatedAroundX = 0;
}
}
which directly reduces the value by one if rotated under the 360 mark instead of waiting for a full negative rotation
*as you can see instead of reaching 3 the RotationCount is reset to 0. This is where the On3TimesRotated event is/would get fired.

The character can't move due to high friction

I have trouble with PhysicMaterial2D for the ground and my character. When the character doesn't move, it works as I need (both materials have friction = 1.0). But due to high friction value the character can't move.
Of course, I could use more force (AddForce()) to move the character but then it will move too quickly on other grounds that uses other materials.
How is this problem resolved in other games (i.e. many platformers)? Should I set a friction of the character's material to zero when it moves (I haven't another ideas)? Or has it more pretty solution?
P.S. I tested with higher value of the friction also but it led to the character begun to rotate on the moving.
ADDED
I tried to set boxCollider.sharedMaterial.friction = 0.0f on the movement and return back to 1.0f after the user released button BUT the character continues to move as with zero friction although in the logger I can see it's 1.0f.
private void Update()
{
if (!paused)
{
if (Input.GetMouseButtonDown(0) && !IsOverPauseButton())
{
isPressed = true;
endMovePoint = Input.mousePosition;
isWalk = GetTouchDistance() > jumpTriggerRadius;
if (!(isWalk || isJump))
{
StartCoroutine(Jump());
}
}
else if (Input.GetMouseButtonUp(0) && isPressed)
{
boxCollider.sharedMaterial.friction = 1.0f;
isPressed = false;
isWalk = false;
canFlip = true;
}
if (isWalk)
{
if (!isJump)
{
boxCollider.sharedMaterial.friction = 0.0f;
}
Walk();
}
animator.SetBool(WalkingFlag, isWalk && !isJump);
}
if (isWalk && !isJump)
{
FixSpeed();
}
Debug.Log("Friction=" + boxCollider.sharedMaterial.friction);
}
Rather than using friction, you can dampen the player's movement.
Say your player has a velocity of 10, you want the player to stop moving of it's own accord because that's what's natural.
What you would want to do is subtract an amount from your player's velocity every frame e.g.
player.velocity.x *= 0.9f;
This would set the player's x velocity to 90% of it's original velocity. Do this every frame and soon your player will stop all by itself.
If the above method does not produce the effect you desire, you can always just subtract an arbitrary value from the player's velocity (of course you must account for if the player's velocity is negative):
player.vel.x -= 1;
Both of the methods above can factor in friction if need be, simply multiply the percentage or arbitrary value by the friction coefficient plus or minus a bit to suit your needs.

Get an object to wobble in Unity3D with C#

I am trying to simulate a boat in Unity3D. What I need it to be able to do is wobble like a real boat would while in water whenever it hits something. I have the boat colliding already, and all of its axes are unlocked. However, this means the boat will rotate, and then keep driving at an odd angle (like facing to the sky).
Is there a way to make the boat try to return to its original rotaions, without snapping to the exact values, but simply "rocking" back and forth, and then slowing down to eventually stop at the correct rotations again?
Here is the code am attempting to use:
void FixedUpdate ()
{
wobble();
}
void wobble()
{
if (this.transform.eulerAngles.x < 270)
{
this.rigidbody.AddTorque((float)19, (float)0, (float)0, ForceMode.Force);
}
else if (this.transform.eulerAngles.x > 270)
{
this.rigidbody.AddTorque((float)-19, (float)0, (float)0, ForceMode.Force);
}
else{}
if (this.transform.eulerAngles.z < 0)
{
this.rigidbody.AddTorque((float)19, (float)0, (float)0, ForceMode.Force);
}
else if (this.transform.eulerAngles.z > 0)
{
this.rigidbody.AddTorque((float)-19, (float)0, (float)0, ForceMode.Force);
}
else{}
}
However, now when my object hits something, it just starts spinning out of control. Any ideas?
You can use tweening. A wonderful technique to change values smoothly be-tween two values. In this case you can tween from your awkward bouncing angles to your boats sitting rotation by tweening between the two. There are good plugins to use like iTween which is fantastic but I will show you some half pseudo - half super-cereal code to get you started on the concept for "rotation correction"
Lets say I have my boat hit a nice big wave and its pointing my boat upwards to a 20deg angle.
My euler angle on X is 20 and I can return this to 0 by increasing the decreasing the value at a constant rate. I'm just showing X here but you can repeat the process for Z-axis. I would exclude Y as you will use your direction of your boat on Y and you don't want to screw up your boats navigation.
Update()
float angleDelta;
// check if value not 0 and tease the rotation towards it using angleDelta
if(transform.rotation.X > 0 ){
angleDelta = -0.2f;
} elseif (transform.rotation.X < 0){
angleDelta = 0.2f;
}
transform.rotation.X += angleDelta;
}
This incredibly simple implementation does the job but has the wonderful perk of being incredibly "snappy" and "jittery". So we want to add some smoothing to make it a more lifelike boat.
We do this by adding in a variable to the angleDelta:
Vector3 previousAngle;
float accelerationBuffer = 0.3f;
float decelerationBuffer = 0.1f;
Update()
Vector3 angleDelta;
//assuming that x=0 is our resting rotation
if(previousAngle.X > transform.rotation.X && transform.rotation.X > 0){
//speed up rotation correction - like gravity acting on boat
angleDelta.X += (previousAngle.X - transform.rotation.X) * accelerationBuffer;
} elseif(previousAngle.X < transform.rotation.X && transform.rotation.X > 0
//angle returning to resting place: slow down the rotation due to water resistatnce
angleDelta.X -= (previousAngle.X - transform.rotation.X) * deccelerationBuffer;
}
//If boat pointing to sky - reduce X
if(transform.rotation.X > 0 ){
transform.rotation.X -= angleDelta.X * Time.deltaTime;
//If boat diving into ocean - increase X
} elseif (transform.rotation.X < 0){
transform.rotation.X += angleDelta.X * Time.deltaTime; //Must not forget to use deltaTime!
}
//record rotation for next update
previousAngle = transform.rotation;
}
This is an incredibly rough draft but it gets across the theory that I'm trying to explain - you will need to adjust your own code accordingly as you haven't included any of your own (pastebin a snippet to us and maybe we can elaborate more!)

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.

Xna adding gravity to a 2d sprite

I am trying to simulate gravity in my first xna 2d game. I have the following
//Used for Jumping
double elapsedAirTime = 0.0;
double maxAirTime = 8.35;
//End Jumping
So I am trying to move the sprite up by a certain amount while the elapsedAirTime < maxAirTime
However, there is some issue where my code only seems to move the sprite up once and not multiple times during this segment of time. here is the code in my "player.cs" class, or the update method of the class.
if (newState.IsKeyDown(Keys.Space))
{
if(oldState.IsKeyUp(Keys.Space))
{
//if we are standing or jumping then change the velocity
if (playerState == PlayerStates.Standing)
{
playerState = PlayerStates.Jumping;
this.position.Y -= (float)(30.0 + ((1.2)*elapsedAirTime)*elapsedAirTime);
}
}
}
//if we are jumping give it some time
if (playerState == PlayerStates.Jumping)
{
if ((elapsedAirTime < maxAirTime) && position.Y < 3)
{
this.position.Y -= (float)(30.0 + ((1.2) * elapsedAirTime)*elapsedAirTime);
elapsedAirTime += gameTime.ElapsedGameTime.TotalSeconds;
}
//otherwise time to fall
else
{
playerState = PlayerStates.Falling;
}
}
//add gravity to falling objects
if (playerState == PlayerStates.Falling || playerState == PlayerStates.Standing)
{
//if we are above the ground
if (this.position.Y < windowBot - 110)
{
//chnage state to falling
playerState = PlayerStates.Falling;
this.position.Y += 3.0f + ((float)(gameTime.ElapsedGameTime.TotalSeconds));
}
else
{
playerState = PlayerStates.Standing;
elapsedAirTime = 0.0f;
}
}
Any help is much appreciated, please and thank you!
To give your sprite the feel of gravity, you should add velocity and acceleration to your Sprite class. Then, create an Update method for the Sprite, and have acceleration be added to your velocity every update, and velocity added to position every update. Position should not be based on the amount of elapsed air time. You can set the acceleration to a constant gravitational value, and then add to the velocity of the Sprite whenever you jump. This will create a flowing parabolic jump that looks nice. If you want to include timing, you can pass the GameTime into the Sprite's Update method, and use it as a modifier on the velocity. Here is an example Update method:
void Update(GameTime gt)
{
int updateTime = gt.ElapsedGameTime.TotalMilliseconds - oldgt.ElapsedGameTime.TotalMilliseconds;
float timeScalar = updateTime / AVG_FRAME_TIME;
this.velocity += this.acceleration * timeScalar;
this.position += this.velocity;
oldgt = gt;
}
If you use timing, this method is a little complicated. You have to keep track of how much time the update took, then divide it by the average amount of time an update or frame should take to get the amount you should adjust your velocity by. Without timing, the method is very simple:
void Update()
{
this.velocity += this.acceleration;
this.position += this.velocity;
}
I would suggest using the simpler method until you understand exactly how timing works and why you need to implement it.
It looks like this line is at fault:
this.position.Y -= (float)(30.0 + ((1.2) * elapsedAirTime)*elapsedAirTime);
I think you will find that this updates the sprites position quicker than you imagine, the sprite will move 330 pixels up the screen in 10 updates (assuming Game.IsFixedTimeStep == true) that is 1 tenth of a second realtime
It is likely that this is just updating so quickly that you don't get a change to see it rise before the && position.Y < 3 condition kicks in and changes the playerState.
It looks like you are trying to say - jump at a rate of x pixels per second for upto 8.5 seconds so long as space is held.
What you need for that is to change the calculation to this.position.y -= (float) (30 * gameTime.ElapsedGameTime.TotalSeconds), this will give a very liner movement to the jump action but it will mean that the sprite jumps at exactly 30 pixels per second.
If Game.IsFixedTimeStep == true - which is the default - the update gets called 60 times per second so gameTime.ElapsedGameTime.TotalSeconds is going to be about 0.1 every update. If something happens to cause an update to skip (rendering issues for example) then update will get delayed and gameTime.ElapsedGameTime.TotalSeconds may be 0.3 (the 2 updates skipped) but the formular still works out the correct jump rate.

Categories

Resources