Unity2D: Pushing Player away on collision with enemy - c#

I'm trying to add a script to my game so that whenever the player collides with an enemy he takes damage but is also pushed away in opposite direction. I have tried implementing the Rigidbody2D extension code posted here
Here is part of my player script:
private void OnCollisionEnter2D(Collision2D other)
{
var magnitude = 5000;
if (other.gameObject.CompareTag("Monster"))
{
TakeDamage(25);
GetComponent<Rigidbody2D>().AddExplosionForce(magnitude, this.transform.position, 500);
}
}
When I run the game in Unity I get this error whenever the player collides with an enemy:
Rigidbody2D.AddForce(force) assign attempt for 'Player' is not valid. Input force is { NaN, NaN }.
UnityEngine.Rigidbody2D:AddForce(Vector2, ForceMode2D)
Rigidbody2DExt:AddExplosionForce(Rigidbody2D, Single, Vector2, Single, Single, ForceMode2D) (at Assets/Scripts/Rigidbody2DExt.cs:20)
Player:OnCollisionEnter2D(Collision2D) (at Assets/Scripts/Player.cs:94)

Welcome to StackOverflow.
I would recommend you to try with:
private void OnCollisionEnter2D(Collision2D other){
Vector2 impulse = new Vector2(-7, 2);
if (other.gameObject.CompareTag("Monster")){
TakeDamage(25);
GetComponent<RigidBody2D>().AddForce(impulse, ForceMode2D.Impulse);
}
}
Also you have to find a way to get the desired direction to push the player. For example, if you approach the enemy from the right, you may want to be pushed to the left. In my example, you will be pushed to the left with some up impulse. Let me know if you still have problems :)
References here

Related

How to make a pushable rigidbody not get stuck in a non-pushable rigidbody

Brand new to Unity/C# so this might be a stupid error. I have a player and a push-able box that when pushed towards another object (that usually the player cannot walk through) causes the box to stop moving and the player to get stuck and be unable to move but stuck mid-animation.
They basically just all get stuck in eachother
https://i.stack.imgur.com/rkNtu.png
I followed tutorials for a lot of these things but couldn't manage to find one for pushing the box so I did it by myself, which is what I'm thinking caused these issues.
The player has a 2D circle collider and a 2D rigidbody (with a dynamic body type and discrete collision detection).
It also has all of its code to do with walking that looks like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
//Movement speed variable
public float moveSpeed;
//Giving information of where the collisions layer is (edited in actual unity)
public LayerMask collisionsLayer;
//Variable to check if the character is moving
private bool isMoving;
private Vector2 input;
//Animation SetUp
private Animator animator;
private void Awake()
{
animator = GetComponent<Animator>();
}
//End of Animation SetUp
private void Update()
{
//If the player is NOT moving
if (!isMoving)
{
//'GetAxisRaw' gives 1 or -1 --> right = 1, left = -1
input.x = Input.GetAxisRaw("Horizontal");
input.y = Input.GetAxisRaw("Vertical");
//Making it so that the player cannot move diagonally
if (input.x != 0) input.y = 0;
if (input != Vector2.zero)
{
//Animation section
animator.SetFloat("moveX",input.x);
animator.SetFloat("moveY",input.y);
//End of animation section
var targetPos = transform.position;
//Adds 1 or -1 depending on the key input (GetAxisRaw)
targetPos.x += input.x;
targetPos.y += input.y;
if (IsWalkable(targetPos))
//Calls coroutine 'IEnumerator Move'
StartCoroutine(Move(targetPos));
}
}
//for animation
animator.SetBool("isMoving",isMoving);
//Special type of function w/ 'IEnumerator' as return type. Used to do something over a period of time.
IEnumerator Move(Vector3 targetPos)
{
isMoving = true;
//Checks if the diff between the target position & the players current position is greater than a vry small value
while ((targetPos - transform.position).sqrMagnitude > Mathf.Epsilon)
{
//If there is a diff btwn targetpos & the current position --> we will move the player towards the target position by a very small amount
transform.position = Vector3.MoveTowards(transform.position, targetPos, moveSpeed * Time.deltaTime);
//Stops execution of the coroutine, resumes it in the next update function
yield return null;
}
//Sets the players current position to the target position
transform.position = targetPos;
isMoving = false;
}
}
//Checking if the next available tile is actually able to be walked on / if the next tile is blocked
private bool IsWalkable(Vector3 targetPos)
{
if(Physics2D.OverlapCircle(targetPos, 0.1f, collisionsLayer) != null)
{
return false;
}
return true;
}
}
The box has a box collider 2D and a 2D rigidbody (dynamic bodytype; discrete collision detection)
And the collisions tilemap (which is set to be used by the composite) has a tilemap collider 2D, rigidbody 2D (static) and a composite collider 2D
If anyone actually knows how to make it so that the entire game doesn't break when I try to move the box past objects, that'd be really helpful because I've been working on this for ages and haven't figured anything out at all
You should try running the debugger. I think you're finding yourself in an interlocked condition, where your character can't get to the target position (because it's blocked), but it hasn't reached the target position (because it's blocked), so it keeps trying to move, which prevents you from giving it a new command.
I think the easiest way to work around this is to cache your target directions - should be in positive x, positive y, or negative x, or negative y. Then, if you detect the user trying to move in the opposite direction you cancel the move and start a new one.
There are lots of ways to work around this, though, and I do see you are trying to check the target before moving to it, but you're checking the raw input, which may overshoot an obstacle. For example, if the tree is at 0.5 meters, but you're looking at a target of 1 meter, then the target is clear. You get blocked by the tree at 0.5 meters, and because you never reach 1 meter you never exit the coroutine.
You've got to run a debugger and step through the code and see what specifically isn't responding.

How to convert my Character Controller into Rigibody Movement in Unity?

I'm making a 3D Side-Scroll Platformer Game,
I have trouble with my character when it steps on the moving platform it will not come along on the platform. I want my character to stay on the moving platform so I think converting my Character Controller into Rigibody will help me,
I need help to give me ideas on how I can reuse my Character Controller Script in Rigibody. This is my code, how can I reuse this in Rigibody script?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public CharacterController controller;
private Vector3 direction;
public float speed = 8;
public float jumpForce = 30;
public float gravity = -20;
public Transform groundCheck;
public LayerMask groundLayer;
public bool ableToMakeADoubleJump = true;
public Animator animator;
public Transform model;
void Start()
{
}
// Update is called once per frame
void Update()
{
if (PlayerManager.gameOver)
{
//play death animation
animator.SetTrigger("die");
//disable the script
this.enabled = false;
}
float hInput = Input.GetAxis("Horizontal");
direction.x = hInput * speed;
animator.SetFloat("speed", Mathf.Abs(hInput));
bool isGrounded = Physics.CheckSphere(groundCheck.position, 0.15f, groundLayer);
animator.SetBool("isGrounded", isGrounded);
if (isGrounded)
{
//Jump Codes
ableToMakeADoubleJump = true;
if (Input.GetButtonDown("Jump"))
{
Jump();
}
}
else
{
direction.y += gravity * Time.deltaTime;
if (ableToMakeADoubleJump & Input.GetButtonDown("Jump"))
{
DoubleJump();
}
}
if (hInput != 0)
{
Quaternion newRoattion = Quaternion.LookRotation(new Vector3(hInput, 0, 0));
model.rotation = newRoattion;
}
//Move the player using the character controller
controller.Move(direction * Time.deltaTime);
}
private void DoubleJump()
{
//Double Jump Codes
animator.SetTrigger("doubleJump");
direction.y = jumpForce;
ableToMakeADoubleJump = false;
}
private void Jump()
{
direction.y = jumpForce;
}
}
I would not recommend switching between the two. It would get tricky, and think about it, you are alternating between two very different things. One is movement and one is physics.
However, I would reccomend adding to your current script so that the player would move with the moving platform.
There is a lot of stuff in this answer, so read the whole thing.
Btw, when I talk about velocity, in your case it is direction.
Since it seems like you know how to code pretty well, I won’t write out the script, rather tell you some physics ideas to get you going in the right direction.
The reason people can stand on a moving platform and not fall off is because of friction.
If you are standing on a gameObject with enough friction (you could add a physics material the gameObject you stand on and change friction there. Note that physics materials only work with rigidbodies, but you might want to use it to just read the value)
First of all, you are going to want to raycast down to obtain the object you are standing on. From there you can get the physics material from hit.collider.sharedMaterial (or any other hit. to obtain data about what object you are standing on.
If they friction is too low, just make the character slip off, like it was before (I assume)
If the friction is above a threshold, get the velocity from the object you are standing on. If it was a rigidbody, hit.rigidbody.velocity. If it is controlled by script, use hit.collider.gameObject.GetComponent<scriptname>().velocityvariablename This part is continued later on
This is not necessary but useful: You can think of this as grabbing on a rope. When you are grabbing on a slippery rope, and someone pulls it (Like tug of war), You won’t move because the rope will slide through your hands. If the rope had grip tape on it and someone pulled it, you would come with it because it has more friction. You can think of the platform the same way. Now on to the more complex part: When you grip a rope that is stationary, and someone pulls it, you come with it as its velocity changes. When the rope is already being pulled, so its velocity is not stationary and it is already something. You grab onto it and a similar thing happens. It is like you are becoming a part of that rope. Similar to how if you are running, the arms and legs and head is a part of you. If you lose grip, you are no longer a part of that body, like your arms falling off when running. In other words, you become part of the body when you attach yourself to it.
Bottom line:
Get the velocity of the platform and set platformVel to it, do not add that to velocity, rather do a seperate controller.Move(platformVel).
A small customization:
Vector3.Lerp the platformVel to 0, so it doesn’t change while on the platform, but gradually goes to (0,0,0) when you get off. This way, there is a little momentum maintained from standing on the platform.
Feel free to ask anything in the comments.

How to move a RigidBody2D to a position while still checking for collision

I am currently creating a game in Unity, in which you move a ball around using OnMouseDrag(), a CircleCollider2D and a RigidBody2D. This is how I set the position of the ball:
private void OnMouseDrag()
{
Vector2 mouseInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
playerRb.position = new Vector3(mouseInWorld.x, mouseInWorld.y, 0);
}
I still want the ball to slide on collision while the mouse moves around. Is there a way to do this?
I have tried RigidBody2D.MovePosition(), but the ball jumped around from one point to another, and Raycasts but couldn't get that to work either.
EDIT:
This is what I've got now:
playerRb.velocity = new Vector3(mouseInWorld.x - playerRb.position.x, mouseInWorld.y - playerRb.position.y, 0);
Now the problem is, that the ball lags behind the mousePosition.
When you use RigidBody.MovePosition, you don't call the physics engine and so it ignores collisions. If you want collisions to happen you need to use RigidBody.Velocity instead.
Doing this change will require you to make some change to your code though because what you give to RigidBody.Velocity is a velocity and not a position so you will need to calculate the velocity required in x,y (and z if you are in 3d) to reach your destination.
I invite you to read the Unity page about velocity for more info
https://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html
Note: This will make the player/ball stick to collisions.
Modifying the velocity could cause the ball to bounce around unexpectedly when the ball collides with the wall. I would use a CircleCast for this, check if it hit anything, then use MovePosition accordingly:
float cursorDepth;
Rigidbody2D playerRb;
CircleCollider cc;
void Awake()
{
playerRb = GetComponent<Rigidbody2D>();
cc = GetComponent<CircleCollider>();
}
private void OnMouseDrag()
{
Vector2 mouseInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 posToMouse = mouseInWorld - playerRb.position;
RaycastHit2D hit = Physics2D.CircleCast(playerRb.position,
cc.radius * transform.lossyScale.x, posToMouse, posToMouse.magnitude);
if (hit.collider != null)
{
mouseInWorld = hit.centroid;
}
playerRb.MovePosition(mouseInWorld);
}
But notice that if the ball can't move all the way to the mouse, it might cause the drag to end. So, plan accordingly.

Unity: Detect which collider component of gameobject is colliding with something

It's a bit of a question to fit in the title. But the question is, how do i detect which collider (of a certain gameobject) is colliding with something?
Heres my example: My character (gameobject) in unity has two colliders (BoxCollider2D & CapsuleCollider2D) and the capsulecollider is my character's feet. I only want him to for example stay "grounded" if the feet (cap.collider) is colliding with something, or only be able to jump if the feet is colliding with something, and not just the BoxCollider2D (the body).
So far i use a "OnCollisionEnter2D" function to detect, and that only shows me the other object, and what it is etc. But i want to know which of my characters colliders collided, so i can specify what is going to happen when certain areas of my character is colliding.
Example on a problem: My character can touch a wall (meaning the BoxCollider2D has been hit, but not the feet) and can use the jump script, because he is "grounded" just by touching something (with box coll), which then transfers to my function "OnCollisionEnter2D" that sets the bool isGrounded to true.
I found the answer. By writing this code in the "OnCollisionEnter2D" function i managed to get it working! It was the "otherCollider" variable i was needing.
void OnCollisionEnter2D(Collision2D col)
{
// Debug.Log("Player has collided with " + col.collider.name);
isGrounded = true;
marioFalling = false;
gameObject.GetComponent<Rigidbody2D>().velocity = new Vector2(moveX * marioSpeed, 0); // Väldigt viktig, återställer hastigheten (i y) till noll, istället för att lägga till tidigare hastighet på nästa hopp.
if (col.otherCollider is BoxCollider2D)
{
Debug.Log("Marios body is being tickled");
}
if (col.otherCollider is CapsuleCollider2D)
{
Debug.Log("Marios feet is being touched");
}
}

Unity 2D Game - Making rigidbodies fly in a straight line

I want to create a 2D game and for that i need to create a number of rigidbodies 2D that fly in a constant speed without stopping (at least for the start). I created an array of GameObjects and everythink was good (I also created a bounds check).
My problem is that I gave every gameobject an equal velocity that remains equal (every fixedupdate), but for some reason they do not keep moving in a straight line (one gameobject is getting closer to another and away from another gameobject etc).
Here is the physics code:
private void FixedUpdate(){
if (ballsAreMoving) {
foreach(GameObject ball in balls){
ball.gameObject.GetComponent<Rigidbody2D> ().velocity = ball.gameObject.GetComponent<Rigidbody2D> ().velocity.normalized * ballSpeed;
}
}
}
IEnumerator FireBalls() {
foreach(GameObject ball in balls){
////////////////Create ball
ball.transform.position = transform.position;
ball.SetActive(true);
////////////////Realese ball
ball.gameObject.GetComponent<Rigidbody2D> ().velocity = (new Vector2(lineDirection, 10) * ballSpeed);
yield return new WaitForSeconds(shootDelay);// Delay
}
}
The FireBalls function is called once after the balls are created and the ballsAreMoving bool is true after FireBalls is called.
Thanks in advance.

Categories

Resources