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
Related
I have a game where map/background is made up of prefabs. My player and prefabs both have rigidbodies and colliders. Neither of them have is trigger checked and the prefabs have collision detection set as continuous dynamic. The player's collision detection is set on continuous. Each of the prefabs have a mesh collider and the individual walls of the prefabs have box colliders (none are set to is trigger). I keep trying to test it on my phone using Unity Remote 5, and every time I move the player it goes through the walls. If anyone has any advice on how to prevent my player from going through the walls, I would really appreciate it!
My movement script is:
public class Movement : MonoBehaviour
{
private Touch touch;
private float speedModifier;
void Start()
{
speedModifier = 0.01f;
}
void Update()
{
if(Input.touchCount > 0)
{
touch = Input.GetTouch(0);
if(touch.phase == TouchPhase.Moved)
{ transform.position = new Vector3(transform.position.x + touch.deltaPosition.x * speedModifier,
transform.position.y,
transform.position.z + touch.deltaPosition.y * speedModifier);
}
}
}
}
It would really help to see your movement script however it sounds like you are moving player by manipulating transform.position or using rigidbody.MovePosition() without setting isKinematic to true. The documentation says;
If the rigidbody has isKinematic set to false, it works like transform.position=newPosition and teleports the object to the new position (rather than performing a smooth transition).
This behaviour will ignore the collision. Also you don't need rigidbody on every GameObject in the scene. 1 rigidbody on the player is enough to collide it with other objects.
depend on what I got from your question, when you are playing and the player hit the wall, it goes inside; so the problem might be the mass of the wall. if you must add a Rigidbody to the wall so you need to add more mass to the wall also edit the border of the box collider and make them reasonably a bit wider, otherwise if you dont need a rigidbody on the wall simply keep just the box collider and it will works good. hope this answer help you, and it will be better if you can explain more the situation using some pics.
Using the Rigidbody position or applying force tends to cause the Rigidbody to clip through other colliders. I recommend changing rigidbody.velocity instead of directly changing the position.
My game is a topdown 2D game, and when the player collides with another object while it is moving the player starts bouncing off. I've tried using a 2D physics material with 0 bounciness and it doesn't solve the problem. I think it has something to do with the way my movement code. I tried using Rigidbody.MovePosition, but it didn't work and my player was glitching out. This is my movement code.
float horizontal = Input.GetAxis("Horizontal") * moveSpeed * Time.deltaTime;
float vertical = Input.GetAxis("Vertical") * moveSpeed * Time.deltaTime;
transform.Translate(new Vector2(horizontal, vertical));
Try using the MovePosition method on your Rigidbody inside of FixedUpdate instead of changing the position of the player's transform. If that does not solve the issue, try also changing the collision detection mode of the player's rigidbody to continuous.
When working with physics and Rigidbodies in Unity, you would want to do most of the physics related things in the FixedUpdate method. This method runs in sync with Unity's physics loop and all the physics calculations are done right after all of the FixedUpdate methods have been called. This allows for smooth looking interactions and physics.
I am working on a 2D Platform game, and i realized that the player's jumping function doesn't work the same way every time, for example. The jumping height is different if the player jumps while moving/running or if the player jumps without moving.
I have 2 separated functions Move() and Jump(), Move() uses transform.Translate to make the player move, and Jump() uses rigidBody.AddForce() to make the player jump. I've already tried to change the player Move() function to use rigidBodies to make the player move instead of using transform.Translate(). And it didn't worked.
I've also tried make the player jump using transform.Translate, which solved the inconsistent jumping height problem, but the player just teleports up instead of jumping
this is a representation of my code structure, not the actual code, because the actual code is like 600 lines
public class Player
{
float JumpSpeed;
bool isGrounded;
void Update()
{
if (Input.GetKey(KeyCode.A))
Move(Directions.Left);
if (Input.GetKey(KeyCode.D))
Move(Directions.Right);
if (Input.GetKeyDown(KeyCode.Space))
Jump(JumpSpeed);
}
public void Move(Directions dir)
{
Vector2 speed;
//figure out speed and etc...
//makes the player move in the right direction and speed
transform.Translate(speed * Time.deltaTime);
}
public void Jump(float speed)
{
if(isGrounded)
rigidBody.AddForce(new Vector2(0, speed * Time.deltaTime), ForceMode2D.Impulse);
}
}
Not sure if this is specifically your issue, but using translate to move the player and then adding force to jump isn't really the best way to approach the problem.
I would look into using the velocity part of the rigidbody for both the jump and the movement. This would prevent any weirdness that Translating the object could cause.
In my game I want the character to be able to have a pickaxe or weapon that can rotate around the player but when I do so it goes through the objects and is very glitchy.
I want it to still follow the mouse (which is invisible) and still stop when it has a collision. My code right now is just pretty standard code for mouse rotation and its attached to an empty gameobject which is the parent of the pickaxe/weapon
//rotation
Vector3 mousePos = Input.mousePosition;
mousePos.z = 5.23f;
Vector3 objectPos = Camera.main.WorldToScreenPoint(transform.position);
mousePos.x = mousePos.x - objectPos.x;
mousePos.y = mousePos.y - objectPos.y;
float angle = Mathf.Atan2(mousePos.y, mousePos.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
Whenever you have a GameObject that you want to be physics enabled, you don't move it directly. You add a Rigidbody and use RigidBody.MoveRotation to rotate your GameObject. RigidBody has its set of movement and rotation methods that take into account collisions. Moving the Transform directly will cause problems.
This can still cause problems in some situations. If you're still experiencing physics weirdness then you can eliminate them by using a "physically correct" solution and use Rigidbody.angularVelocity. Based on the angle you can set the velocity to either rotate the object right or left until it faces the way you want.
Essentially the physics system hates teleportation. Instant movements and instant rotations are basically teleportation to it. So there's a hierarchy of how "physics system friendly" some actions are. And they go like this:
Adding Force to the RigidBody
Adding Velocity to the RigidBody
Moving the GameObject through appropriate RigidBody methods.
Moving the GameObject through Transform.
I want a gameobject spinning around its y-axis. This spinner should have a initial movement direction and when colliding with something, it should change its direction.
I created a little picture to show what I mean, but I want the behaviour for a 3D game.
So I just started with the rotation of the spinner,
public class Spinner : MonoBehaviour
{
[SerializeField]
private float movementSpeed; // speed when moving
[SerializeField]
private float rotationSpeed; // speed when rotating
private Rigidbody rigid;
private Vector3 movementDirection; // the direction the spinner is moving
private void Start()
{
rigid = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
transform.Rotate(0, rotationSpeed, 0); // rotate the spinner around its y-axis
}
private void OnCollisionEnter(Collision col)
{
// set new direction
}
}
How can I move the spinner, that it moves along a direction and whenever it collides with something, it changes its direction. It should never stop moving or rotating.
I would like to point out a few things:
The Unity Physics Engine will make collisions to absorb part of the force which moves your spinner, so unless you keep adding "artificial" forces to the spinner it will eventually stop.
The "air friction" in your scene will also reduce the force of your
spinner, so it will slow it down. You should add a material to the
spinner which has 0 Dynamic Friction
Based on the comment you left in #reymoss' answer, you may consider
to add the bouncy material to the walls, and not to the spinning
GameObject.
To sum up, the issue here is if you want a GameObject to bounce against a wall using the Physics Engine, the object will eventually stop, because some of the forces will be absorbed in each collision. That means you will need to keep adding external forces every time a collision takes place, to keep it moving endlessly.
Something you can try is,
1- Add a bouncy material to the walls and remove the dynamic friction of your spinner. In the following link you can learn about this:
https://docs.unity3d.com/Manual/class-PhysicMaterial.html
2- Add to the Spinner a Collider, so when it detects a collision with a wall (you can tag the walls as so for example) add an additional force to the spinner in the direction it is already moving, so it will compensate the energy lost during the collision.
void OnTriggerExit(Collider other) {
if(other.tag == "wall") {
rigidbody.AddForce(rigidbody.velocity.normalized * Time.deltaTime * forceAmount);
}
}
The idea is to let the Engine decide the direction of the Spinner after the collision with the wall, and as soon as the GameObject leaves the trigger, you add an additional force in the current direction.
Then you can play with the Bounciness of the wall's material and with the forceAmount, until your spinner moves as you have in mind.
Note: Since you will have 2 materials, one in the walls and another in the spinner, maybe playing with the Friction Combine and Bounce Combine you will be able to avoid the force lost during collisions, so you will not need to add the external force I mention in the second step. However I have never tried this.
Let me know if it works. I can't try myself the solution until I arrive home.
If you give the object an initial velocity, attach a collider, create and assign a Physics Material 2D to the collider (to apply bounciness), and attach Colliders to the walls, you can have it bounce around with minimal code.
private void Start()
{
rigid = GetComponent<Rigidbody>();
rigid.velocity = new Vector3(2f, 3f, 1f) // initialize velocity here
}