I'm currently creating a 2D top down game with neural network controlled enemies. I am using the rigidbody2d to control their movement but when they bump into eachother they go straight through and don't collide. Please help! the code if below (output[] are the outputs from the nn):
void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
outputs = GenerateOutputs();
Debug.Log(outputs[0]+"||"+outputs[1]);
currentRotation = outputs[0] * rotationSpeed;
myRigidbody.MoveRotation(myRigidbody.rotation + currentRotation);
currentSpeed = ((outputs[1] + 1) / 2) * speed;
Vector2 velocity = transform.up * currentSpeed;
myRigidbody.MovePosition(myRigidbody.position + velocity);
}
Here is the Rigidbody2d/CircleColider2D setup:
Here is the layer collision matrix (the enemies are on the 'Predator' layer:
You need to make the RigidBody2d have a BodyType of "Dynamic". This will allow the physics to take control.
I'm not sure of your exact control model, however, you may run into an issue with multiple objects using that FixedUpdate code. MoveRotation and MovePosition will affect physics objects and be affected by physics (kind of). However, since they are setting the position and rotation, the objects with that code won't dynamically bounce around. You would want to use AddForce and AddTorque if you want everything bouncing around.
Related
How to create collisions for a flying camera in Unity 3d? to prevent the camera from falling into objects on the scene. My camera script snippet:
public class FlyCamera : MonoBehaviour
{
void FixedUpdate()
{
if (!isAlternative)
SetCameraMovement();
}
private void SetCameraMovement()
{
lastMouse = Input.mousePosition - lastMouse;
lastMouse = new Vector3(-lastMouse.y * camSens, lastMouse.x * camSens, 0);
lastMouse = new Vector3(transform.eulerAngles.x + lastMouse.x, transform.eulerAngles.y + lastMouse.y, 0);
if (Input.GetMouseButton(1))
transform.eulerAngles = lastMouse;
lastMouse = Input.mousePosition;
GetBaseInput();
transform.position = Vector3.Lerp(transform.position, NewPosition, Time.deltaTime * movementTime);
}
private void GetBaseInput()
{
Speed = Input.GetKey(KeyCode.LeftShift) ? superSpeed : mainSpeed;
if (Input.GetKey(KeyCode.W))
NewPosition += transform.forward * Speed;
if (Input.GetKey(KeyCode.S))
NewPosition += transform.forward * -Speed;
if (Input.GetKey(KeyCode.A))
NewPosition += transform.right * -Speed;
if (Input.GetKey(KeyCode.D))
NewPosition += transform.right * Speed;
}
}
If you want to achieve a RTS-like flying camera, you can set the camera to a parent dolly-like object and keep it looking at that object:
Object (Camera Dolly) -> this is moved by wasd and is rotated to the desired view angle
Child object: Camera -> only the camera's position.y is ever changed
Then, on a custom script on the dolly in Update(), send a LineCast from the dolly to the camera. When the cast hits a collider (important to use e.g. tags here so it does not collide with anything, like e.g. units) in the world, gradually (over time) reduce the camera's position.y and if nothing is hit, gradually increase it to the desired height. It will not entirely prevent the camera from clipping trough bigger objects but move it so it doesn't stay "inside" a collider. Something like that is also typically used in RPG third person cameras.
Please let me know if more clarification is needed!
Edit: I'd do that only for really big view-blocking objects though, not for the terrain or smaller buildings/units - at least if your goal is more of an RTS or sim game.
Edit2: In a solar system setting, it is a bit more complicated. A few options:
Before translating the camera, do a SphereCast (in the desired (final) movement direction with sphere radius of minimum allowed distance to any object) against all celestial object SphereColliders and set your "NewPosition" to the nearest hitPoint minus the desired minimum allowed distance to a celestial object. Also add some margin here, so the camera doesn't get stuck. This would be a continuous collision detection approach.
Everytime after translating the camera , do a CheckSphere (sphere radius is allowed minium distance to any object) against all celestial objects and as long as there is any hitpoint, lerp the camera position out of it over time. This is a "softer" looking approach, but if you fly very fast, it can still clip into something.
You can savely do both in LateUpdate().
Better put you camera movement in the LateUpdate() as adviced in the docs.
With a rigidbody and a collider properly set for the collision detection. Check docs so that you can detect the collisions properly.
Check that " Notes: Collision events are only sent if one of the colliders also has a non-kinematic rigidbody attached", so your camera could be one gameObject with no rigidbody, so that you can fly free and collide.
I case you might need physics behaviour for yout camera and you want it also to behave as a rigidbody I would set the camera movement with rigidbody.SetPosition instead of with transform.position = new Vector3(), as you need to choose to set the transform in the geometric or the physics world.
Also you would need to uncheck the gravity option not to fall.
Hello guys my Player is walking on the Stone and through the Stone. The Player called Champ has a Box Collider and the Stone has a Mesh Collider. Also the Player has Rigidbody. I tried everthing i found but nothing helped me with my problem.
MovePlayer.cs Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovePlayer : MonoBehaviour
{
Rigidbody rb;
public float speed = 10f;
private Vector3 moveDirection;
public float rotationSpeed = 0.05f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
moveDirection = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")).normalized;
}
void FixedUpdate()
{
rb.MovePosition(rb.position + transform.TransformDirection(moveDirection * speed * Time.deltaTime));
RotatePlayer();
}
void RotatePlayer()
{
if (moveDirection != Vector3.zero)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(moveDirection.normalized), rotationSpeed);
}
transform.Translate(moveDirection * speed * Time.deltaTime, Space.World);
}
}
Player Settings in Inspector
Stone Settings in Inspector
Scene Preview
Thank you for help guys! :)
So guys i found out the Solution with the help of the guys posted above.
The problem was that my player speed was too high in the Code the speed was on float 10, but i changed the velocity in the Unity Inspector of Player to float 50.
So my first step to solve the problem was to set the speed down to float 10, but i still wanted to move with a speed of 50f...
The Solution for this problem was that in Unity 2020.3.24f1 and higher (probably lower) you can go to Edit>Project Settings>Physics and set the "Default Max Depenetration Velocity" to the speed you want the objects stopping and not going through. In my case i wanted to move with speed = 50f so i needed to change Default Max Depenetration Velocity to 50.
I hope i can help someone with this Answer in future!
Best Wishes
Max G.
Tested your code and collisions seem to be working fine on my end.
Tested it by adding the script to a GameObject with box collider and creating a small level using cubes. Also made a wall that I modified to use mesh-collider instead of box collider. Player collided normally with objects in the scene.
You should double check your Layer collision matrix from Project Settings > Physics whether you've set layers player and wall to collide.
You could also try adding new cube to the scene and setting its layer to wall to see if player collides with it. If it does then the there might be issues with the mesh of the stone.
If not then I would disable animator and Gravity Body components from the player to make sure they're not interfering with the collisions
Rigidbody.MovePosition basically makes the player teleport which can cause unexpected behaviors. It's generally recommended to use Rigidbody.AddForce instead. For precise movement ForceMode.VeloictyChange can be used.
public float maxVelocityChange = 5.0f;
void moveUsingForces(){
Vector3 targetVelocity = moveDirection;
// Doesn't work with the given RotatePlayer implementation.
// targetVelocity = transform.TransformDirection(targetVelocity);
targetVelocity *= speed;
// Apply a force that attempts to reach our target velocity
Vector3 velocity = rb.velocity;
Vector3 velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
rb.AddForce(velocityChange, ForceMode.VelocityChange);
}
In this code you have applied motion twice and the problem is that transform.Translate is used. Remember that Rigidbody class methods are sensitive to colliders and recognize them, but transform is not the same and only applies a point-to-point shift. To solve the problem, I think you will not need a duplicate motion code with translate in the rotate section.
void RotatePlayer()
{
if (moveDirection != Vector3.zero)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(moveDirection.normalized), rotationSpeed);
}
// removed translate
}
I tested and this script cause lags in physic. It seems to me that it call physic not per 0,2 seconds, but much-much more rarely and because of this object falling much more slower.
Here's a gif to demonstrate what happens: The purple one without script, the blue one with script.
So how can I fix that?
public class PlayerMovement : MonoBehaviour
{
Rigidbody2D rb;
Vector2 movement = new Vector2(0, 0);
public float movementSpeed = 10f;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
movement = new Vector2(Input.GetAxisRaw("Horizontal"), 0);
}
void FixedUpdate()
{
rb.MovePosition(rb.position + movement * movementSpeed * Time.fixedDeltaTime);
}
}
As said MovePosition overrules the movement => don't use it if you want physics (force) based movements and reaction to collisions etc.
You want to use the input only for the horizontal movement but use physics for the vertical. MovePosition affects both directions.
Try directly setting the Rigidbody.velocity instead
private void Update()
{
// Get the current velocity
var velocity = rb.velocity;
// Only overwrite the x velocity
// since the velocity is only applied in the FixedUpdate anyway
// there is no problem setting this already in Update
// And since it is the velocity which is already frame-rate independent
// there is no need for Time.deltaTime
velocity.x = Input.GetAxisRaw("Horizontal") * movementSpeed;
// Assign back the changed velocity
rb.velocity = velocity;
}
There is an issue with the logic of your code if I understand what you want to do.
You seem to be moving in a vertical fashion yet only have the x component of your vector2 from the horizontal input.
new Vector2(Input.GetAxisRaw("Horizontal"), 0);
If the GIF you attached, the object is just in freefall, so that would be a vertical drop, or the y component of the vector2, which you are setting to 0. Think of this as (x,y) or (move horizontally (left/right), move vertically (up/down)).
I would change the above line to:
movement = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
You can now move vertically using vertical input. Something else which would not cause your issue, but is mentioned on the MovePosition docs is to mark your Rigidbody2D as Interpolate. It is the bottom field in the Rigidbody2D component labeled Interpolate, set it from None to Interpolate.
The reason your object is moving slowly without any input is because you still have your Gravity Scale on the Rigidbody set to 1 instead of 0. If you are trying to simulate gravity with this script, add the vertical component to your input vector and set the Gravity Scale field on the Rigidbody to 0. The MovePosition is fighting the current gravity attempting to affect it.
I'm very new in Unity and I'm trying to move a simple square on a classic 2D map (Super Mario like).
I use addForce to jump, it goes as planned.
But now, I'm trying to move my character horizontally.
First, I tried to use tramsform.translation(), I quickly notice that isn't the proper way, cause this method "teleports" the character, and if it moves too quickly, it can teleport behind a wall. I also try with addForce, but I want that my character has a constant speed, and it gives inertia, so it doesn't stop instantly when I release the key. I also try with .MovePosition(), but the character is shaking with this method (apparently, due to gravity).
So, whats the proper way to move a character (Rigidbody2D) horizontaly?
Here's my code with the different try:
using UnityEngine;
using System.Collections;
public class test : MonoBehaviour {
private Rigidbody2D rb;
public Vector2 velocity;
public float jumpforce;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
Debug.Log(velocity);
if (Input.GetKeyDown("space")){
rb.AddForce(new Vector2(0, jumpforce), ForceMode2D.Impulse);
}
if (Input.GetKey("a")){ // move to the left
rb.AddForce(-velocity * Time.deltaTime, ForceMode2D.Impulse);
//rb.MovePosition(rb.position - velocity * Time.fixedDeltaTime);
//transform.Translate(-velocity);
}
if (Input.GetKey("d")){ // move to the right
rb.AddForce(velocity * Time.deltaTime, ForceMode2D.Impulse);
//rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
//transform.Translate(velocity);
}
}
}
Personally, I use a setup like this:
Rigidbody2D rb;
[SerializeField] [Range(0, 1)] float LerpConstant;
//Other stuff here
FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal");
Vector2 movement = new Vector2(h, rb.velocity.y);
rb.velocity = Vector2.Lerp(rb.velocity, movement, LerpConstant);
}
Lerp simply means "take a Vector2 that is 'x' amount from A to B, and return it". So the code creates a movement vector that is your horizontal movement (user input), vertical movement (the vertical movement the rigidbody already has), and then lerp the current velocity to it. By modifying velocity directly, it ensures your movement will stay smooth and constant, so long as you know how to do it right.
If you want a "Physics" based movement then you should apply forces to the Rigidbody. A benefit of acting on the rigidbody is that it will take into account colliding with objects (like walls).
Here is a great intro tutorial (it's 3D but the same concepts apply for 2D). This is the code from that tutorial with some amendments to make it 2D:
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour {
public float speed; // Here we set a float variable to hold our speed value
private Rigidbody2D rb; // This is to hold the rigidbody component
// Start is called as you start the game, we use it to initially give values to things
void Start ()
{
rb = GetComponent<Rigidbody2D>(); // Here we actually reference the rigidbody.
}
void FixedUpdate ()
{
// We assign values based on our input here:
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
// Here we assign those values to a Vector2 variable.
Vector2 movement = new Vector2 (moveHorizontal, moveVertical);
rb.AddForce (movement * speed); // Finally we apply the forces to the rigidbody
}
}
We can alter the manner in which the force acts on the rigidbody by changing the AddForce.ForceMode2D parameter. For example, ForceMode2D.Force will
Add a force to the rigidbody, using its mass.
ForceMode2D.Impulse will
Add an instant force impulse to the rigidbody2D, using its mass.
which is better for things like jumping.
Note - it is better to put physics-based method calls in FixedUpdate, not in Update, because of frame-rate dependency.
Also note - as you are applying a force to an object, it will accelerate because you are acting on a mass (the rigidbody) and decelerate based on other forces (friction etc.) If you want your player to slow to a halt instead of stop dead, think about the forces acting upon the player. Furthermore, if you apply a force to the rigidbody once per FixedUpdate, this will result in the constant speed you want if you choose ForceMode2D.Forceas other forces acting in the opposite direction will balance it out (see below image - credit to http://www.school-for-champions.com/ for the image).
EDIT: Regarding the comment by rutter above, here is an introductory tutorial on 2D Character controllers.
I am trying to make a game where you bounce a ball up and down on a platform. I added a rigidbody component to the ball, making the ball "Use Gravity", as well as have material in the SphereCollider so it will have "bounciness" or a coefficient of restitution. The ball follows gravity nice and dandy on a stationary platform/plane.
After, I tried adding the platform component. This component, by the way, simply moves up and down on the Y-axis (as if someone is just bouncing a ping pong ball up and down on a paddle). To make it go up, I used this C# script.
if (Input.GetKeyDown(KeyCode.W))
{
heldDownW = true;
}
if (Input.GetKeyUp(KeyCode.W))
{
heldDownW = false;
}
if (heldDownW)
{
transform.position = new Vector3(transform.position.x, (transform.position.y + 1f), transform.position.z);
}
This simply checks if the W key is being held down, move the platform up. I have the same thing for down on the S key. The platform moving component works well. However, here is my problem. The ball works fine on a stationary platform, or when I am not pressing the W or S key. It bounces and obeys gravity. However, when I am moving the platform while the ball is in motion, the ball goes straight through the platform.
I think I know the reason why- it is the transform.position. It is simply moving the platform even though the ball is still in the air, thus changing the position of the platform right through the ball. However, I am not sure how to proceed from here. I tried using OnCollisionEnter, but I don't know what to put in the OnCollisionEnter method because the gravity/collision components are not coded in a script. I want the ball to bounce on the platform regardless of the platform moving or not. Any help would be greatly appreciated. Thank you.
You will have to attached a Rigidbody to that platform thing, then set the 'isKinematic' field to 'true' in the inspector.
Then you will need to use the RidigBody.MovePosition() instead of transform.position
if(heldDownW)
{
rigidbody.MovePosition(new Vector3(transform.position.x, (transform.position.y + 1f), transform.position.z));
}
Also, based on what I read, it will be also nice to put that if-block to FixedUpdate() instead, because FixedUpdate() are for physics related stuff.
void Update()
{
if(Input.GetKeyDown(KeyCode.W))
{
heldDownW = true;
}
if(Input.GetKeyUp(KeyCode.W))
{
heldDownW = false;
}
}
void FixedUpdate()
{
if(heldDownW)
{
rigidbody.MovePosition(new Vector3(transform.position.x, (transform.position.y + 10f * Time.deltaTime), transform.position.z));
}
}
Another thing is to use Time.deltaTime when your are doing speed calculation :
if(heldDownW)
{
rigidbody.MovePosition(new Vector3(transform.position.x, (transform.position.y + 10f * Time.deltaTime), transform.position.z));
}
by using Time.deltaTime you are like saying 'move 10 meters per second upwards'
if you dont use Time.deltaTime, you are moving it 10 meters per frame
which is not you what you want.