Unity: RigidBody2D, how to move GameObject in straight lines (no curves) - c#

Hello all, I'm very new to and very confused by Unity.
I'm creating a top-down, gravity-free test game, similar to PacMan (i.e. think of the movement of characters in straight lines).
Currently, I'm trying to move an object in straight lines (without curvature) (see image).
My GameObject has RigidBody2D attached, as well as Linear, Angular and Gravity set to 0.
I'm currently controlling my GameObject by adding force:
private Rigidbody2D _playerRB2D;
public float _speed;
private void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector2 movement = new Vector2(moveHorizontal, moveVertical);
_playerRB2D.AddForce(movement * _speed);
}
The idea is that the game object won't get to slow down, so is constantly in motion (in straight lines). Collision with objects is obviously important too!
Any help or information to look at will help a lot, thank you!

Unity has a built-in physics engine that calculates movement based on velocity (and collisions, etc.) By using Rigidbody2D.AddForce, you are adding to your rigidbody's velocity. This means that if you press the up arrow and then the right arrow, the velocity from pressing the up arrow remains, causing a diagonal velocity and curved path. If you want straight lines, you can use Rigidbody2D.velocity or just use Transform.translate.
_playerRB2D.velocity = movement * _speed;
Edit: You said that collisions were important, so keep the rigidbody
or remove the rigidbody2D component and use
transform.Translate(movement * _speed);

The idea is basically to reset the force being applied on the rigidBody2D on each changed direction.
In fact, you would have to test if the direction input from horizontal is greater than vertical for x axis, and the inverse for y axis , and thus if it is true, change the opposite to 0f.
You would have normally:
Vector2 movement = new Vector2(moveHorizontal < moveVertical ? moveHorizontal: 0f, moveVertical < moveHorizontal ? moveVertical: 0f);
_playerRB2D.velocity = movement * speed;
Edit: By testing the input value, you can move your RigidBody2D with a analogic controller, and thus being straight as you wished !

Related

How do I get a GameObject to rotate and face away from a MouseClick location in Unity?

So in my Unity exercise, I have to rotate a GameObject to face away from where I clicked my mouse. Also, I can only rotate around the Y-axis i.e. the GameObject is only allowed to rotate either purely to the right or purely to the left, and cannot tip towards the ground at any point. Also, I've got to do this without RayCasting (I've already done it WITH RayCasting, so as an exercise, I've got to do it without). Here's the code I've written after multiple attempts but it doesn't seem to be effective enough:
Vector3 clickLocation = Input.mousePosition;
Vector3 clickWorldLocation = Camera.main.ScreenToWorldPoint(new Vector3(clickLocation.x, clickLocation.y, transform.position.x)); //the transform.position.x is just to add some depth
transform.LookAt(new Vector3(clickWorldLocation.x, transform.position.y, clickWorldLocation.z), Vector3.up);
This code works fine if my GameObject remains in its starting position, but fails if I move it to another location and attempt the same action. Could someone help me out please?
First, call Camera.main as infrequently as you can, as it uses an expensive operation (FindGameObjectsWithTag) and doesn't cache the result.
private Camera mainCamera;
void Awake()
{
mainCamera = Camera.main;
}
To answer your question, don't use ScreenToWorldPoint in this situation, because a depth isn't most easily calculated. You don't have anything meaningful to use for the z component, and transform.position.x makes no sense here.
Instead, create a Plane that you want to click into (such as the plane parallel to the view plane and intersecting the object), use ScreenPointToRay to turn the mouse position into a Ray, then find where the ray and plane intersect. Plane.Raycast is much, much faster than a physics raycast. Then, find the direction from the mouse world position to the object, and use Quaternion.LookRotation to turn that direction into a rotation you can assign to the object's rotation:
Ray mouseRay = mainCamera.ScreenPointToRay(Input.mousePosition);
Plane mousePlane = new Plane(mainCamera.transform.forward, transform.position);
float rayDistance;
if (mousePlane.Raycast(mouseRay, out rayDistance))
{
Vector3 mouseWorldPos = mouseRay.GetPoint(rayDistance);
// make the z component of mouseWorldPos the same as transform.position
mouseWorldPos.z = transform.position.z;
Vector3 awayFromMouseDir = transform.position - mouseWorldPos;
transform.rotation = Quaternion.LookRotation(awayFromMouseDir, Vector3.up);
}

How to smooth transform position?

I'm looking for solution to smooth transform position of my object.
To moving to new position I'm using that code
transform.position += Vector3.left * Time.deltaTime * 100f;
The effect of moving is to fast, so I want to make it more smooth. There is any option to change this code for better effect? Like the small bricks in this video when ball destroy big brick
https://youtu.be/mqj7eYna3Ds
You can also use this:
transform.Translate(Vector3.left * Time.deltaTime * 100f);
This should make it a bit smoother. Just remember if you would ad a velocity to the object, Transform.Translate wil not work nice!
if you want more float like movement you can give a Addforce to the attached rigidbody.
rigidbody.AddForce(transform.left * 10, Forcemode.Impulse);
Note: If you use Translate there wil not be any acceleration!
Rigidbody.AddExplosionForce may solve your problem but you cannot put the collision point as the origin. you may have to move your origin of an explosion a little bit below than the collision point. Let me know if it solves the issue.
https://docs.unity3d.com/ScriptReference/Rigidbody.AddExplosionForce.html
Using AddForce or manipulating the velocity variable on the Rigidbody is better if you want smooth movement.
However, if your object doesn't have a rigidbody, then you can just use the functions provided with the Transform class (e.g: Rotate(), Translate(), SetPositionAndRotation())
You can use this block of code to smooth out the movement of the player
public Transform player;
public Vector3 targetPosition;
public float smoothFactor = 2;
void Update()
{
player.transform.position = Vector3.Lerp(player.transform.position, targetPosition, Time.deltaTime * smoothFactor);
}

Colliding with Rigidbodies throwing player object in the air

I have a Rigidbody attached to my character controller and my enemies. I want the enemies to be able to surround me and have me trapped without me being able to move, so I set the mass property accordingly.
There's just a small problem - if I don't set my mass high enough, the moment the enemies collide with me, my player will go flying into the air. If I don't set my enemies' mass high enough, I will be able to walk right through them. How can I fix this issue? Here is the movement code for the player:
using UnityEngine;
using System.Collections;
public class CharController : MonoBehaviour {
public float speed;
void Start () {
Cursor.lockState = CursorLockMode.Locked;
}
void Update () {
float translation = Input.GetAxis ("Vertical") * speed;
float strafe = Input.GetAxis ("Horizontal") * speed;
translation *= Time.deltaTime;
strafe *= Time.deltaTime;
transform.Translate (strafe, 0, translation);
if (Input.GetKeyDown ("escape")){
Cursor.lockState = CursorLockMode.None;
}
}
}
The problem here is that you're using Transform.Translate() to move your player object. As a result, the player is being moved into clipping with your enemy objects in between physics calculations - so when the next physics calculation comes around, there will be a massive repulsing force to correct this. This is what's sending your player flying.
A better alternative is to use Rigidbody.AddForce() (or one of its variants) to indirectly move your player object. This allows the player object's movement to be taken into account during physics calculations, so it can collide with and be stopped by enemy objects before it starts clipping into them.
If you retrieve and store a reference to the player's Rigidbody in a variable, then you can replace Translate() with something like:
rigidbody.AddRelativeForce(strafe, 0, translation);
Hope this helps! Let me know if you have any questions.
Make sure IsKinematic is checked. UseGravity can be checked or not depend on your situation.
That way, you rigidbody will not be affected by force, collision or joint and is under full control of your script. But maybe they will lose the ability to detect collision as well but I'm not so sure, I haven't tested it yet.
If that was the case, uncheck the IsKinematic and instead, check FreezeRotation and FreezePosition as well. That way, you revoke control of the physics from affecting your position and rotation. You will manually manipulate position & rotation from your script (using CharacterController).
References:
https://docs.unity3d.com/ScriptReference/Rigidbody-isKinematic.html
https://docs.unity3d.com/Manual/class-Rigidbody.html

Getting the player jump correctly in Unity3D

I'm still learning the basics so please be a bit gentle. I'm working on the movement script of a roll-a-ball game. I've got the rolling around working perfectly fine, but now I'm trying to add the ability to jump.
In my case the "player" is a basic Sphere. What I'm trying to accomplish is that when space is pressed, the ball leaps into the air in the direction it's currently rolling.
This is what I've tried myself, but gave very strange results:
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
if (Input.GetKey("space"))
{
transform.position += transform.up * 20 * Time.deltaTime;
}
rb.AddForce(movement * speed); //rb = RigidBody
This resulted in the ball sometimes jumping in the wrong direction or simply just speeding into some direction without jumping at all. So obviously I'm getting closer, but not quite there yet. Could anyone explain what I'm doing wrong and how to fix it?
Not looking for a quick fix! Don't learn anything from that.
Have a try at this :)
if (Input.GetKey("space"))
{
rigidbody.AddForce(new Vector3(0, yourJumpForce, 0), ForceMode.Impulse);
}
You also might want to fiddle with the gravity settings in the rigidbody to get your desired jump as well!
EDIT Explanation:
I think it may be because you are applying you movement to your Rigidbody, but your jumping method to the Transform itself.
A Rigidbody is a physics component (see docs.unity3d.com/ScriptReference/Rigidbody.html), where a Transform is to do with position, rotation and scale.
A jump could be considered a physics problem - therefore altering the jump is to do with physics (Rigidbody), not the position (Transform). I tend to do all movement code with the Transform, as you are moving positions, and do all physics based work with the Rigidbody, as you are applying physics to the GameObject! :)
To add to the accepted answer: The jumping into random directions issue could have been caused by the transform.up. That vector is the up direction relative to your current transform. That's usually the same as the actual up direction, except if you rotate the game object. And as the object happens to be a rolling ball in this case, it's very likely that it's rotated.
You can fix it by using the global up direction instead - either by doing it like in the accepted answer or by using Vector3.up.

Clamp player movement correctly

I have the following code to clamp player movement. It works but i have a problem. For example if the player is at position -3.05 and if I hold the button to move left the player still moves over the -3.05 limit to about -3.56. Once i let go of the button it bounces back to -3.05. Same goes for the right side. I do not want it to go over the limits no matter what.
Vector3 tmpPos = transform.position;
tmpPos.x = Mathf.Clamp(tmpPos.x, -3.05f, 3.05f);
transform.position = tmpPos;
The following is the way i add movement to the player:
rigidbody.AddForce (movement * speed * Time.deltaTime);
You should not mix up transform operation with rigidbody unless it's marked isKinematic. So instead of transform.position, try clamping rigidbody.position inside of FixedUpdate.
void FixedUpdate(){
Vector3 pos = rigidbody.position;
pos.x = Mathf.Clamp(pos.x, minX, maxX);
rigidbody.position = pos;
}
However, since you're using AddForce to move your object, a much simpler way is to make empty game objects with box collider on the left and right of the object, which then will limit your object movement like invisible walls.
Try using rigidbody.MovePosition(tmpPos); instead of setting transform.position.
I solved my problem. Instead of using the AddForce to move the object
rigidbody.AddForce (movement * speed * Time.deltaTime);
I use rigidbody.position to move the object. I use Mathf.Clamp to limit the movement before applying it to rigidbody.position.

Categories

Resources