I'm making a game: side view basketball in Unity3D. I want the script to calculate the force of impact on the ball at which the ball will reach the basket based on the value of the distance from the ball to the basket. But I don’t know how to write it correctly, so I edit the impact force manually. There is also the problem that when the ball hits, in the first two frames the ball travels a much greater distance than in the rest. The Shoot method is called in Update if the ball is in the bot's collision zone. Ball hit code and screenshots (Sequence of screenshots: 1-frame before hit, 2-first frame after hit, 3-second frame after hit, 4-3rd frame after hit):
private void Shoot{
var heading = pointTarget.position - go_ball.transform.position;
var distance = heading.normalized;
var distancemag = heading.magnitude;
go_ball.GetComponent<Rigidbody>().AddForce(distance * distancemag * UpForce * Time.deltaTime, ForceMode.VelocityChange);
go_ball.GetComponent<Rigidbody>().AddForce(distance * distancemag * LeftForce * Time.deltaTime, ForceMode.VelocityChange);}
I also adjust MaxVelocity in a separate ball script method(idk maybe problem hides here)
if (rb_ball.velocity.magnitude > MaxVelocity)
{
rb_ball.velocity = Vector3.ClampMagnitude(rb_ball.velocity, MaxVelocity);
}
As you can see the difference in the distance from the ball in the first two frames is different. What could be the problem?
Related
I have a 2.5d platformer game. The character is using rigidbody movement on a spline (using the curvy splines asset) which curves into 3d space in all sorts of ways, while the camera stays fixed to the side so that you see the path and background turning, but maintain a 2d side scrolling perspective.
I'm essentially creating a look rotation based on the spline, then moving the player using that forward vector, and making sure to remove any velocity perpendicular to the path so that the player stays centered on the path even when curving. I'm removing the velocity on that vector instead of projecting all the velocity in the direction of the path so that the player can still jump and fall like normal.
void SetLookRotation()
{
// get nearest TF and point on spline
Vector3 p;
mTF = Spline.GetNearestPointTF(transform.localPosition, out p);
// Get forward and up vectors of point on spline
_localHorizontal = Spline.GetTangentFast(mTF);
_localVertical = Spline.GetOrientationUpFast(mTF);
// Set look rotation to path
transform.rotation = Quaternion.LookRotation(Vector3.Cross(_localHorizontal, _localVertical), _localVertical);
}
void Movement()
{
Vector3 m = transform.right * groundAcceleration * moveInput;
rb.AddForce(RemoveCrossVelocity(m));
rb.velocity = RemoveCrossVelocity(rb.velocity);
Vector3 localVelocity = transform.InverseTransformDirection(rb.velocity);
localVelocity.z = 0;
rb.velocity = transform.TransformDirection(localVelocity);
}
Vector3 RemoveCrossVelocity(Vector3 v)
{
// get magnitude going in the cross product / perpindicular of localHorizontal and localVertical vector
// (essentially the magnitude on "local Z" or to the sides of the player)
Vector3 crossVelocity = Vector3.Project(v, Vector3.Cross(transform.right, transform.up));
// and remove it from the vector
return v -= crossVelocity;
}
The first 2 functions are happening in FixedUpdate() in the order shown.
The problem is, when hitting sharp corners at high speeds, some inertia causes the player to deviate off the center of the path still just ever so slightly, and a lot of that momentum turns into upward momentum, launching the player upwards. Eventually the player can fall off the path completely (I do have a custom gravity acting towards the spline though). It works perfectly at lower speeds though, even when dealing with sharp corners. At least as far as I can tell.
I tried a bit of code from https://answers.unity.com/questions/205406/constraining-rigidbody-to-spline.html too but no luck.
Is there a way I could constrain the player rigidbody on a vector that is not one of the global x/y/z axes? I've tried a host of other solutions like setting the transform of the player towards at the center of the spline but I can't seem to get it without feeling very jerky. Using forces makes the player "rubber band" towards and past the center back and forth. Maybe there is something in my math wrong. In any case, I'm hoping someone could help me make sure that the player will always stay on the center of the spline but only on the vector to the sides of the player's face direction, so that it doesn't mess with jumping. Thank you very much in advance!
For potential future visitors, I have figured this out. There are a few components (and a lot more if you're trying to do full spline based physics, but just to start with movement...)
First we must orient our character, so that our local coordinate system can be referenced with transform.right etc. Luckily this package provides these functions which return useful vectors. I'm sure there is math beyond me to do this otherwise if you are building your own spline system.
void SetLookRotation()
{
// get nearest TF and point on spline
Vector3 p;
playerTF = currentSpline.GetNearestPointTF(transform.localPosition, out p);
// Get forward and up vectors of point on spline
_localHorizontal = currentSpline.GetTangentFast(playerTF);
_localVertical = currentSpline.GetOrientationUpFast(playerTF);
// Set look rotation to path
transform.rotation = Quaternion.LookRotation(Vector3.Cross(_localHorizontal, _localVertical), _localVertical);
}
Here I am setting a velocity directly but if you're using forces it's the same principle.
if (Mathf.Abs(localVelocityAs_X) <= maxDashSpeed * Mathf.Abs(moveInput))
{
Vector3 m = transform.right * maxDashSpeed * moveInput;
rb.velocity = RemoveCrossVelocity(m);
}
localVelocityAs_X is defined as (ran in fixedUpdate/ physics step):
float currLocalVelocityX = (playerTF - prevPositionX) / Time.deltaTime;
localVelocityAs_X = Mathf.Lerp(localVelocityAs_X, currLocalVelocityX, 0.5f);
prevPositionX = playerTF;
Where playerTF is your position on a spline (in this case, using the curvy spline package from the unity asset store. Those spline positions return very small floats so in my case I multiplied playerTF by around 10,000 to make it a more easily readable metric). This is essentially just manually calculating velocity of the player each frame by comparing current position on the spline to last frame's.
RemoveCrossVelocity is the same as above. Comment explanations should suffice.
Vector3 RemoveCrossVelocity(Vector3 v)
{
// get magnitude going in the cross product / perpendicular of local horizontal and local vertical vectors
// (essentially the magnitude on "local Z" of the player)
Vector3 crossVelocity = Vector3.Project(v, Vector3.Cross(transform.right, transform.up));
// and remove it from the vector
return v -= crossVelocity;
}
Finally the solution to the drift. My crude fix was essentially to just adjust the player to the center of the spline every frame. Horizontally, there is no change because it grabs the closest spline point which is calculated by this package to be sort of a float clamped between the start and end of the spline. Vertically, we are being set to the distance the player is from the spline in the local up direction - a fancy way of saying we're not moving vertically at all. The reason this must be done is to avoid the spline vertical position overwriting the players, and we obviously can't set this vector back to playerPos.y in our local coordinate space, so we must resort to using a direction vector * the distance from our everchanging floor. This isn't absolutely ideal at the end of the day, but it works, and there isn't any extra jitter from it (interpolate on your player's rigidbody and some camera dampening helps). All in all these together combine to make a player able to accelerate quickly around sharp corners of a spline with physics and intertia will never cause the player to fly off or drift from the center. Take that, rocket physics!
void ResetPlayerToSpline()
{
Vector3 P; //closest spline point to player
float pTf = currentSpline.GetNearestPointTF(transform.position, out P);
playerHeight = Vector3.Distance(transform.position, P);
transform.position = P + (transform.up * Vector3.Distance(transform.position, P));
}
Ultimately for those possibly looking to do some kind of implementation in the future, the biggest thing you'll run into is a lack of cardinal direction, global oriented axis-based functions and properties normally provided by a game engine. For a primer, here are a few I would use (not including gravity, which is simply opposite your up vector times whatever magnitude):
This one allows you to create a vector using x and y like normal (and z in theory) and run this function to convert it when you actually use the vector in a local space. That way, you don't have to try and think in directions without names. You can still think of things in terms of x and y:
Vector3 ConvertWorldToLocalVector(Vector3 v)
{
Vector3 c;
c = transform.right * v.x + transform.up * v.y;
return c;
}
This is basically the same as what is happening in RemoveCrossVelocity(), but it's important to reiterate this is how you set velocity in a direction to 0. The second part shows how to get velocity in a certain vector.
void Velocity_ZeroY()
{
rb.velocity -= GetLocalVerticalVelocity();
}
public Vector3 GetLocalVerticalVelocity()
{
return Vector3.Project(rb.velocity, _localVertical);
}
Getting height, since you cannot just compare y positions:
height = Vector3.Distance(transform.position, P);
I think that's all the good stuff I can think of. I noticed a severe lack of resources for created spline based physics movement in games, and I'm guessing now it's based on the fact that this was quite an undertaking. It has since been brought to my attention that the game "Pandemonium"(1996) is a curvy 3d spline based sidescrolling platformer - just like mine! The main difference seems to be that it isn't at all based on physics, and I'm not sure from what I can tell if it has pitch changes and gravity to compliment. Hope this helps someone someday, and thank you to those who contributed to the discussion.
I'm making a game with ball physics and an FPS camera. I have this bug where the player's movement is increased when I jump.
Example:
Running along the Z axis on a plane:
Suddenly jumping:
My code:
void Update()
{
// Input
moveFwrd = Input.GetAxis("Horizontal");
moveSide = Input.GetAxis("Vertical");
// Player Jump
if (Input.GetKeyDown(jumpKey) && Physics.Raycast(
transform.position,
Vector3.down,
DistanceToTheGround + 0.1f))
{
rb.AddForce(Vector3.up * jumpSpeed, ForceMode.Impulse);
}
// Braking
if (Input.GetKey(brakeKey))
{
rb.angularDrag = brakeForce;
}
else
{
rb.angularDrag = 0.01f;
}
}
void FixedUpdate()
{
// Player Movement
Vector3 movement = cam.transform.right * moveFwrd +
cam.transform.forward * moveSide;
movement = movement.normalized;
rb.AddForce(movement * speed);
}
}
Most likely, the answer is friction. Depending on the physics material used for the player's Rigidbody collider and the physics material used for the floor, the Rigidbody will behave differently:
Static friction describes the friction when both objects are not moving (i.e. before the player starts moving), dynamic/kinetic/sliding friction describes the friction when the player is moving along/on the floor. It will also determine if the rigidbody slides or rolls on the floor, you could experiment with different physics materials to see the effects.
The reason why you see faster movement in mid-air in forward direction after pressing the jump button is that the Rigidbody is no longer affected by "contact friction" between solid materials while airborne, hence it will move faster with the same applied forward force. - However, there's still friction involved: air resistance; which can be controlled by Rigidbody.drag and Rigidbody.angularDrag.
If you want to get rid of this simulated bahavior, you could do various things: Use different physics materials, Rigidbody.drag, Rigidbody.mass values, or try to experiment with a different ForceMode like ForceMode.VelocityChange arguments in Rigidbody.AddForce(). Or do not use the physics engine and rigidbodies at all for motion (colliders and raycasts can still be used).
Side notes:
FixedUpdate() is called at regular intervals, and is the recommended place to do physics related scripting. It is actually called at the same frequency as the internal physics engine is updated, like documented here. The duration between every call is constant: Time.fixedDeltaTime.
Update() on the other hand is called at a variable rate, and has a variable duration Time.deltaTime, which results in different frame rates (FPS). This method can be used for the visual rendering or state processing of the game, and should not be used to update physics related stuff if possible.
In the script above, you use Rigidbody.AddForce() in both methods Update() and FixedUpdate(). In the latter with the default argument ForceMode.Force and in the other case with ForceMode.Impulse. In both cases you do not take the frame time into account, which is fine in the case of an impulse, applied once (after a key has been pressed). In the other case the direction vector gets normalized, so it isn't really a problem either, because the method is called at a fixed rate... but it could be written a bit more clearly.
I need the Player to follow the moving Target and stop exactly when it reaches the Target.
The Player has to reach the Target almost instantaneously in every frame, so i need a high speed way to do it.
I couldn't use Transform.translate because there's a lot of physics implementations in my game and using Transform.translate or movetowards made the physics buggy.
Is there any physics based way to follow the target? velocity, AddForce, anything? For a 2D game.
Any leads would be greatly appreciated! Thank You!
If you have a Rigidbody2D you want to follow another object, the Rigidbody2D.MovePosition is the proper way to move it.
Do the following:
1.Disable gravity by setting the "Gravity Scale" to 0.
2.Change the BodyType to Kinematic.
3.You can now move the Rigidbody object to follow another GameObject with the Rigidbody2D.MovePosition function. See code below. This should be done in the FixedUpdate function and with Time.fixedDeltaTime instead of Time.deltatime.
Finally, if you still get jerky movement, change Interpolate option from None to Interpolate or Extrapolate. I would also suggest reducing the speed variable below.
//Object to follow
public Transform target;
//Rigidbody to move
public Rigidbody2D rb2d;
public float speed = 7.0f;
//Distance to start moving
public float minDistance = 0.09f;
void FixedUpdate()
{
//Find direction
Vector3 dir = (target.transform.position - rb2d.transform.position).normalized;
//Check if we need to follow object then do so
if (Vector3.Distance(target.transform.position, rb2d.transform.position) > minDistance)
{
rb2d.MovePosition(rb2d.transform.position + dir * speed * Time.fixedDeltaTime);
}
}
Changing the velocity directly is always a bad practice and should be avoided. Instead always work with AddForce.
I would calculate the distance between the target and the body and add a force based on that distance.
var dif = target.transform.pos - body.transform.pos;
bodyRigid.AddForce(dif * multiplier * Time.deltatime);
The only problem that comes with that solution might be the fact that the body 'shakes' around the target once its to close.
You could avoid this by checking if the body is close to target and then freezing it.
var dif = target.transform.pos - body.transform.pos;
if(dif.magnitude > 1) {
bodyRigid.AddForce(dif * multiplier * Time.deltatime);
} else {
bodyRigid.velocity = Vector2.zero;
}
Although I said that setting the velocity directly is a bad habit, using it to just freeze the body should be fine. I have no idea whether that might break your other physics that you use in your game, duo the fact that that just strait up freezes your object.
You can also change the distance (1 in the if statement) that it needs in order to freeze, just play around with it a bit and find a value that fits the game
I am seeing a camera stutter when using smooth follow to lerp after my player with my camera in my multiplayer browser game. The player position is received from the server, the player lerps to that position, and my goal is for the camera to smoothly follow the player with its own, extra smoothing.
You can find my game at http://orn.io to see the current state, without smooth follow camera lerping, which is better but causes choppy movement and creates a headache. The current code for camera follow is:
void LateUpdate ()
{
if (Target == null) {
return;
}
currentScale = Mathf.Lerp(currentScale, -GameManager.TotalMass, Mathf.Clamp01 (Time.deltaTime * heightDamping));
Vector3 h = currentScale * 2f * Target.up;
Vector3 f = currentScale * 3f * Target.forward;
// tried lerping to this value but it causes choppy stutters
Vector3 wantedPosition = new Vector3(Target.position.x, Target.position.y, currentScale * 2) + h + f;
myTransform.position = wantedPosition;
myTransform.LookAt (Target, -Vector3.forward); // world up
}
and I have tried for days to tinker with the values, use fixed timestamps, put the camera movement in FixedUpdate/Update, use MoveTowards, and other changes, but am still experiencing issues.
Part of my problem that that the player position changes mid lerp, which causes a stutter since the target position changes in the middle of the lerp. This causes the camera to jump/sutter due to the target position of the lerp being changed in the middle of the lerp, and shakes due to the LookAt.
I would appreciate it if anyone could suggest a way to improve the camera following code as it stands now.
Is there any particular reason you need to use the Mathf.Lerp function?
Unity has a function, Vector3.SmoothDamp that is specifically designed for movement lerping:
void FixedUpdate() {
// Code for your desired position
transform.position = Vector3.SmoothDamp(transform.position, wantedPosition, ref moveVelocity, dampTime);
}
The above will smoothly follow the player by giving the SmoothDamp method control of the velocity variable. This is assuming that you supply it with a ref to store the current velocity and the damp time.
You can also adjust the damp time to change how smooth your transition is. This function will also automatically account for player movement mid-lerp.
To clarify, quoting from the documentation, dampTime in the above is:
Approximately the time it will take to reach the target. A smaller value will reach the target faster.
Also consider using Quaternion.slerp to smoothly rotate between the two rotations.
There seem to be a fair amount of questions similar to this but none seem to really answer. I have a gameobject that can jump on a click of a button and automatically keeps going right.
I need the right moving motion to be constant but it ends up building faster and faster over time. If I use Velocity instead of AddForce, the motion is constant. But when I mouse click to jump, it takes like 10 over seconds to reach back down to ground.
Can I please get assistance on how I can keep the automatic movement to the right constant and still able to jump and reach back to ground fast. The following is my code. Thank you.
Edit:
Desired result
Gameobject constantly moving same speed to the right. When I jump, gameobject jumps and comes back down to ground over a period of 0.5 secs.
Jump is expected to be like a smooth flow like a ball that jumps and comes back down smoothly.
Current result Using AddForce to move right
Gameobject starts to move right slowly and picks up speed over time getting faster and faster thus not able to keep constant same speed.
Current result Using Velocity to move right
Gameobject able to keep constant speed as wanted, but when I jump the Gameobject jumps and takes about 10 seconds to get back down to ground (It slowly glides back down).
public float jumpSpeed = 300;
public float maxSpeed = 15;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxSpeed);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
rb.AddForce(Vector3.up * jumpSpeed);
}
}
void FixedUpdate()
{
rb.AddForce(Vector3.right * maxSpeed);
}
//Trying to change velocity instead - Able to keep constant speed but when I jump, takes about 10 secs to get back down to ground.
public float jumpSpeed = 2000;
public float maxSpeed = 5;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxSpeed);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
rb.AddForce(Vector3.up * jumpSpeed);
}
}
void FixedUpdate()
{
rb.velocity = new Vector2(5, 0);
}
I think there are a few points of confusion here, so I'll answer some clarifying questions of my own.
Why does the speed of the object increase without bound when using add force?
The AddForce command will increase the velocity of the object every time you apply it. In the code, this means every call to FixedUpdate is increasing the velocity.
I think the misunderstanding is related to this line of code in your Start method:
rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxSpeed);
Vector2.ClampMagnitude is not persistent. That is to say, it does not clamp the magnitude going forward. What you probably meant was to prevent the velocity from ever breaking "maxSpeed" in which case you should call the clamp method in FixedUpdate.
However, this would have a different effect which you might not intend. When the game object is moving along only one axis, the entire magnitude of the velocity is along that axis. Once the game object begins moving along a second axis, for instance while jumping, the magnitude of the velocity is split across both. This means that while your game object is jumping (has a y component to its velocity) its horizontal speed (the x component of its velocity) would be diminished. In other words, jumping would slow down your game object's movement to the right.
Why does my object "float" slowly downwards when setting the velocity directly?
One of the advantages of using AddForce is that it modifies the targeted axis of a vector without modifying the others. Setting the velocity directly is a little more tricky because you may accidentally clobber the velocity changes caused by physics.
For instance, in your FixedUpdate code:
rb.velocity = new Vector2(5, 0);
is setting the x velocity of your object to 5 - creating the smooth horizontal movement you want - but at the same time, setting the y velocity to 0.
This is why you needed to crank your jumpSpeed up to 2000 to see any effect on the object's height: it only has a split second to move upwards before the FixedUpdate method resets its upward motion to 0. This is also why your object appears to be "floating" back to earth. The internal physics engine is trying to apply gravity to the object but is being foiled by your code which constantly resets the objects downward velocity.
One Other Comment
I noticed you had some code in your Update method that was acting on the object's physics - that is your jump code. A good rule of thumb is to keep all the code working on an object's physics in the FixedUpdate method, since this is called just before the physics engine does it's work while the Update method is called just before the rendering engine does its work to draw the game.
One Possible Solution
The AddForce technique is normally recommended when working on an object's velocity because it creates natural looking physics through acceleration. Setting an objects velocity directly can create strange looking effects because objects in reality don't work this way. For instance, imagine what it would look like to see a car change from 0mph to 60mph in less than a split second.
If you check Unity's documentation on Rigidbody.velocity, you'll see they make this same recommendation, but add that jumping may be a scenario where you want to break this rule. However, as I mentioned earlier, we need to be careful when setting the y velocity explicitly so as to avoid changing the object's speed along other axes.
public float jumpSpeed = 2;
public float maxSpeed = 1;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
var newVelocity = rb.velocity;
if (Input.GetMouseButtonDown (0))
newVelocity.y = jumpSpeed;
newVelocity.x = maxSpeed;
rb.velocity = newVelocity;
}