Not really sure how to word this but lets say you are a ball and are bouncing around and moving. Its a flat surface so everything moves fine.
Now introduce some 45 degree ramps. You fall on one straight down and bounce away from it. At the moment you can instantly move back regardless of how soon you hit it or what angle or speed your traveling at where it should be harder from the momentum of the push because it was at an angle. Kind of like a small gradual momentum until you reach 0 velocity, then you can move normally.
if (playerLeft)
{
float xVelocity = (playerBody.velocity.x > 0) ? playerBody.velocity.x - playerSpeed : -playerSpeed;
playerBody.velocity = new Vector3(xVelocity, playerBody.velocity.y, 0);
}
if (playerRight)
{
float xVelocity = (playerBody.velocity.x < 0) ? playerBody.velocity.x + playerSpeed : playerSpeed;
playerBody.velocity = new Vector3(xVelocity, playerBody.velocity.y, 0);
}
Originally i only had 1 line for each where i would set the x velocity equal to the player speed regardless if the velocity was -1 or -50. You can imagine how unrealistic that looks.
I tried adding the velocity which is what i did above. But because its minimum is only ever around 6 below 0. It will reach its max speed in 2 or 3 frames which isnt gradual enough.
This is done with Physic Material.
1.First, create a a Physic Material by going to Assets ---> Create ---> Physic Material.
2.Drag the Physic Material to the Material slot of the Object's collider.
3.Below is what the Physics Material looks like when selected:
You can select the Physics Material and modify it's settings in the Editor or you can do that via script:
SphereCollider yourCollider = ...;
yourCollider.material.dynamicFriction = 0.6f;
yourCollider.material.staticFriction = 0.6f;
yourCollider.material.bounciness = 0.0f;
This allows you to modify the bounciness and friction of the object during run-time. I suggest you watch this video to understand the basic settings.
Related
P.S. I have already seen this question asked here, but this applies to Unity 3D, whereas I am working in Unity 2D.
My main issue is that a ball in my Unity 2D game doesn't bounce correctly when the speed is set to a lower value but works fine at faster velocities. Note that I have already tried to change the bounce threshold in Project Settings, but it does not do anything to solve my issue. I have applied a Rigidbody2D component, BoxCollider2D, and a Physics Material 2D that is supposed to make the ball bouncy.
Here are some of my current settings (if that helps):
Rigidbody2D Settings:
Body Type: Dynamic
Material: Bouncy (name of my physics material)
Simulated: True
Use Auto Mass: False
Mass: 1
Linear Drag: 0
Angular Drag: 0
Gravity Scale: 0
Collision Detection: Discrete
Sleeping Mode: Start Awake
Interpolate: None
Physics Material 2D Settings:
Friction: 0
Bounciness: 1
Here is some of the initialization code that runs when the game loads to start the movement of the ball:
float x = Random.value < 0.5f ? -0.75f : 0.75f;
float y = Random.value < 0.5f ? Random.Range(-0.75f, -0.5f) : Random.Range(0.5f, 0.75f);
Vector2 direction = new Vector2(x, y);
rb.AddForce(direction * speed);
Note: This code is inside a custom class (inherits MonoBehavior) I made for the ball object to manage the physics actions.
For additional information, refer to this video to see how the game responds.
The slower speed for the ball is necessary for my game, and I would really appreciate it if I could get some help on this issue.
The Unity physics system is non-deterministic and does not make promises of conservation of energy, or other features. if you want to fine-tune your physics system to such a degree you may want to consider using other means of controlling the ball's velocity.
But if you do want to continue using the built-in physics engine anyway, you could consider using faster velocities and lowering the timescale to compensate.
For example:
float x = Random.value < 0.5f ? -3f : 3f;
float y = Random.value < 0.5f ? Random.Range(-3f, -2f) : Random.Range(2f, 3f);
and somewhere in Start or Awake
Time.timeScale = 0.25f;
and you'll probably want to increase the velocities of your paddles as well to compensate for the timescale change.
Another option is using faster velocities and increasing the size of everything and making the camera view larger to make everything appear the same size. Requires fewer code changes but potentially many more scene/component changes.
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.
So I have tried several ways to fix a Mega-man 2 camera without actually figuring out a good way of doing it. So I would love if someone could give me some tips for I could solve this issue.
The problem: As I have written I want a Mega-man 2 camera (2D). The thing from it that I have not been able to solve is how to move it in Y like they do in Mega-man 2. What happens is that the camera does not move at all in Y, unless you go outside "camera bounds" and then it "freezes" the player and moves the camera down, see this video: https://youtu.be/NP697v8WtkU?t=5m2s (Hopefully it starts where the camera does this, otherwise go to 5:02).
But I can't get this to work in any good ways for me. What I have tried so far is, I have triggers at the top and bottom of the camera and when the player hit's one of them I start a coroutine where the camera should Lerp, however, it moves too much, so if I need to move it twice in the Y it would be all messed up. (My code is at the bottom but as I said, I need some help how I can fix it)
So please, if anyone have any idea how I can solve this, please let me know. I've been trying for several days without getting it to work perfectly.
public void ChangeY()
{
StartCoroutine(MoveCameraDown());
}
IEnumerator MoveCameraDown()
{
Vector3 CurrentCameraPos = Camera.main.transform.position;
Vector3 NewCameraPos = new Vector3(Camera.main.transform.position.x, Camera.main.transform.position.y - 5.6f, Camera.main.transform.position.z);
for (float t = 0.0f; t < 1; t += Time.deltaTime / 1.5f /* TIME IT SHOULD TAKE TO MOVE THE CAMERA DOWN */)
{
Camera.main.transform.position = Vector3.Lerp(CurrentCameraPos, NewCameraPos, t);
yield return null;
}
}
And as I have already written, I call the ChangeY() when I hit one of the triggers. This is just my testing case if you are wondering why some numbers are hardcoded and all that. But as I said, I have not been able to fix this so if anyone can help me I would really appreciate it.
It is actually easy to do. You asked for some tips and I will give you one.
Assuming you have already built your 2D world just like in the video you posted. There are many ways to solve this problem but I show you the way I consider to be the easiest. I have 5 steps for you to follow.
You need a value you can use each time to move down. Based on your code, you used -5.6f each time. The go down value should never be the same each time when the player touches the bottom trigger. It should be generated during run time.
This will be addressed in step 3.
1) If the player triggers the camera bottom trigger, first thing you should do is to stop the player, by disabling gravity then setting the rigidbody velocity and torque to zero.
For example inside your OnTriggerEnter function attched to the camera:
void OnTriggerEnter(Collider other) {
Rigidbody rgbd;
rgbd= other.gameObject.GetComponent<Rigidbody> ();
rgbd.useGravity = false;
rgbd.velocity = Vector3.zero;
rgbd.angularVelocity = Vector3.zero;
}
2) Immediately throw Ray cast downward(-y) using Physics.Raycast.
The origin position of the raycast should be the position of the player. For example,
GameObject gameOBjectHitFromRaycast;//Store Game Object detected from the raycast
RaycastHit hit;
if (Physics.Raycast(player.transform.position, Vector3.down, out hit)){
gameOBjectHitFromRaycast = hit.collider.gameObject;
}
The information of the object below the player will be stored in RaycastHit.
Use RaycastHit to get the information about the object that the player is about to land on.
3) After getting the information of that object the player is about to land on, get the y-axis position of that object then get the y-axis position of the player. Add them together then divide the result by 2. That is your new y position. The new position you need to move the camera to. For example,
Vector3 cameraNewPos = new Vector3(player.transform.position.x,(gameOBjectHitFromRaycast.transform.position.y+
camera.transform.position.y)/2,player.transform.position.z);
Basically, what we did here is to find the middle of the player's current position and the the landing position so that we can position the camera right in the middle of it. That should be do it but you can also subtract another number from the y-xis if that is not enough.
4) Use a coroutine function to move the camera smoothly to the new y axis position. Inside the corutine function, there should be public bool variable that will be used to determine if this function is done running(if camera is done moving).
Replace this function with the current camera move function you have
bool cameraMoving = false; //This variable can be used to determine when the camera is done moving
private IEnumerator MoveCameraDown (Vector3 toLocationPos, float timeToFinish)
{
float runTime = 0; //Dont change
float timer = 0; //Dont change
float tempDeltaTime;
cameraMoving = true;
//Will keep looping until camera is done moving
while (timer<timeToFinish) {
//Translate camera
Camera.main.transform.position = Vector3.Lerp (Camera.main.transform.position, toLocationPos, runTime);
tempDeltaTime = Time.deltaTime; //Get delta time once to be used multiple times to improve performance
runTime += tempDeltaTime / timeToFinish;
timer += tempDeltaTime; //Increement To exit loop
yield return null;
}
//Camera is done moving
cameraMoving = false;
}
Finally, to move the camera, you can just start the coroutine and pass in the new position calcluation in step 3 and also how much time you want it to take to do the moving. For example, we can pass in the cameraNewPos from step 3 and 2 for seconds it will take to move the camera.
StartCoroutine (MoveCameraDown (cameraNewPos , 2f));
5) When the camera is done moving, enable rigidbody gravity of the player with
Rigidbody rgbd;
rgbd= player.GetComponent<Rigidbody> ();
rgbd.useGravity = true;
I have a game that effectively has two paddles in it, and I've got it working set up so that either the players or an AI can control either paddle. However, I'm trying to figure out how to get my AI to actually smoothly run after the ball and catch it, instead of simply brute-forcing it with a basic "Direction of Ball" = Opposite direction AI moves.
In order to do so, I'm trying to write some code that will allow the AI to predict where it will land, when the ball is in it's court or going to be. It's not going that well. This is a diagram of what I want to achieve:
So, I've been studying Unity3D and came up with this:
Vector3 BallMinder = BallPosition.gameObject.rigidbody.velocity;
float BallX = BallMinder.x;
float BallY = BallMinder.y * Time.deltaTime;
float GroundHitPointX = (BallY + BallX);
Vector3 BallImpactPoint = new Vector3 (GroundHitPointX, 0, 0);
Debug.Log (BallImpactPoint);
Debug.DrawRay (BallImpactPoint, Vector3.up, Color.green);
However, I feel like I've oversimplified it or forgotten something. My calculations are way off, and the ray isn't showing up where it should, if it does at all. What have I gotten wrong?
All you need is the Kinematics equations.
These equations perfectly describe a projectile in motion. Therefore, you can use them to predict where and with what components the ball will land.
Here is a graph to help you understand.
This is going to be simplified by assuming that gravity is downward, and the floor is at Y=0. It could be expanded using vector math for arbitrary floorlines and gravity directions, but for your purposes you should be fine using the two aforementioned assumptions. I'm also assuming for this explanation that BallPosition is a Transform object, but the math would be the same if it weren't. I'm also assuming no air friction on the ball, which would make the math a fair bit more complicated.
Essentially, you need to calculate the time until the ball hits the ground and then extrapolate the ball's X-position at that time.
The classic formula dictating the motion of an accelerating object is d = vt + at^2 / 2, where v is the current velocity, a is the acceleration, and t is the amount of time that has passed. To figure out the time of impact, we simply solve for t.
To figure out when the ball will hit the ground you'll want to set d = BallPosition.position.y * -1, a = Physics.gravity.y, and v = BallPosition.rigidbody.velocity.y. This will give us the number of seconds until impact.
Since gravity is assumed to be entirely downward and no other forces are acting on the ball, we can know that the X-position of the ball at impact time is BallPosition.position.x + BallPosition.rigidbody.velocity.x * impactTime. This would be the X-position your computer player should move towards.
...The formula should work any time the gravity is entirely downward, even if the ball is on the upward portion of its trajectory or moving away from your computer player. You'll probably want to come up with some strategy for what the computer should do while waiting for the human to hit the ball and set the ball's new trajectory, since you probably don't want the computer to try to run towards the human's side of the net. Depending on how your players are able to hit the ball, you might be able to predict the new velocity the ball would have after the human hits it, and then feed that data to this formula.
essentialy you apply gravity to the ball at every step to make it seem natural something like this:
ball is at your position ready to be trown
ball.x = 20;
ball.y = 10;
xVelocity = 0;
yVelocity = 0;
gravity = 0.1;
you trow the ball
When you trow the ball you set xVelocity to a constant lets say 1 and yVelocity to 5. So:
xVelocity = 1;
yVelocity = 5;
ball.x+=xVelocity;
ball.y+=yVelocity; //to start the ball moving
Now in the gameloop you calculate the ball position like this:
tick
{
if(ball.y > 10) //if the ball is off the ground (i asume 10 is ground)
{
ball.x+=xVelocity;
ball.y+=yVelocity;
yVelocity-=gravity;
}
}
with this the ball should fly in a perfect arc now to calculate the landing zone you can simply do the same thing again with dummy numbers
while(dummyBall.y > 10)
{
dummyBall.x+=xVelocity;
dummyBall.y+=yVelocity;
yVelocity-=gravity;
}
landingPosX = dummyBall.x;
You will probably need to tweak the numbers but if i remember everything correctly it should work something like this. Hope it make sense