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");
}
}
Related
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.
I have two objects: a player and an enemy. The main problem is the enemy, which should start shooting at the player when it approaches him at a certain distance (in short, just start the animation).
At first I tried to implement this through Vector 3, as with other ordinary opponents. But he is as stupid and crutch as possible, however, you yourself can see everything in the pinned.
I started to implement it more allegedly correctly, through the trigger and the collider (box collider) of the enemy itself. And everything works right as it should, but there is a nuance. The enemy also has boxing implemented through the box collider, the player, hitting which, causes damage to him. There is only one box collider for these two tasks, and since I had to increase this collider so that the enemy could stop in front of the player at a certain distance. Because of this, the player can hit at a great distance (at which the enemy can shoot) from the enemy and the enemy takes damage anyway.
I tried to make a separate object the size of the enemy himself and use it as a box to receive damage. Then he already transmits data about receiving damage to the enemy object. But this does not work, all links between scripts and objects are made, but he does not want to transfer data. That is, simply making two colliders for different tasks does not work.
In general, here my powers are all. I searched the entire Internet, but I did not find how to implement this mechanic in a different way so that it does not conflict with others. Therefore, I ask the help of the local experts, where I screwed up.
private Animator ch_animator;
// Start is called before the first frame update
void Start()
{
myAgent = GetComponent<NavMeshAgent>();
myAnim = GetComponent<Animator>();
EnemyH = GetComponent<GDHealth>();
}
// Update is called once per frame
void Update()
{
dist = Vector3.Distance(/*checker.*/transform.position, target.transform.position);
if (dist > range)
{
myAgent.enabled = false;
myAnim.SetBool("Idle", true);
myAnim.SetBool("Move", false);
myAnim.SetBool("Attack", false);
}
if (dist <= range & dist > atRange)
{
myAgent.enabled = true;
myAgent.SetDestination(target.position);
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", true);
myAnim.SetBool("Attack", false);
}
if (dist <= atRange)
{
StartCoroutine(Attack());
}
if (PlayerH._health <= 0)
{
atRange = 0;
}
if (EnemyH._health < 0)
{
myAgent.enabled = false;
}
}
public IEnumerator Attack()
{
yield return new WaitForSeconds(0.5f);
myAgent.enabled = false;
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", false);
myAnim.SetBool("Attack", true);
}
void OnTriggerStay(Collider col)
{
if (col.tag == "Player")
{
//gameObject.GetComponent<Animator>().SetBool("Attack", true);
StartCoroutine(Attack());
transform.LookAt(col.transform.position);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
}
}
void OnTriggerExit(Collider col)
{
if (col.tag == "Player")
{
myAgent.enabled = true;
myAgent.SetDestination(target.position);
myAnim.SetBool("Idle", false);
myAnim.SetBool("Move", true);
myAnim.SetBool("Attack", false);
//gameObject.GetComponent<Animator>().SetBool("Attack", false);
}
}
But this does not work, all links between scripts and objects are made, but he does not want to transfer data.
From this description it could be anything: wrong layers, no rigidbody on either of objects, misstyped tags in OnTriggerStay method.
In my project, I successfully created 2 colliders for 2 separate tasks, so this is how I would see it in your problem:
Use two colliders
Attach one collider with one script to the enemy object - this collider should have a size of the enemy. The OnTriggerStay method here should deal damage to the enemy, check for death, etc.
Create child object to the enemy. Attach new collider to it with the size of enemy's attack range. Attach a script with OnTriggerStay method that will stop enemy and begin ranged attack (or whatever you want to do).
If this doesn't work: check collision matrix or try adding a kinematic rigidbody to either of objects.
Measure distance between player and the enemy in update (which you are already doing) and apply necessary code based on distance (stop or attack) thus replacing one of the colliders.
Hope that helps!
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
Recently I have started my first 2D game project on unity and everything has been going well. My only problem so far is with my enemy. My enemy, when attacking, jumps in the air and then falls and hits the ground. I want my script to detect when it falls and how hard it falls then create a force that pushes the player back as if its an "explosion."
So my questions are how do I detect the enemy hitting the ground, after a fall, and then add a force?
I tried using onCollisionEnter2D on Unity but it does not work since technically even when the enemy is moving it's still "falling."
Here is my attempt at checking if the enemy fell then searching for the player and calling the explosion force function.
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.tag == "Ground")
{
foreach (Collider2D Obj in Physics2D.OverlapCircleAll(transform.position, radius))
{
if (Obj.GetComponent<Rigidbody2D>() != null && Obj.gameObject != gameObject)
{
Debug.Log("Calling Function");
Rigidbody2D rb = Obj.GetComponent<Rigidbody2D>();
ExplosionForce2D forceScript = GetComponent<ExplosionForce2D>();
forceScript.AddExplosionForce(rb, force, transform.position, radius);
}
}
}
}
This is my code for adding a force to the object.
public void AddExplosionForce (Rigidbody2D body, float expForce, Vector3 expPosition, float expRadius)
{
var dir = (body.transform.position - expPosition);
float calc = 1 - (dir.magnitude / expRadius);
if (calc <= 0) {
calc = 0;
}
body.AddForce (dir.normalized * expForce * calc);
}
I expect that, if the player is in the enemy radius, and the enemy jumps, falls, and hits the floor it will push the player back as if it was an explosion.
you can use a flag that can check that if the enemy is falling or not, so when your enemy is actually falling set this as true, if not (means moving) then set as false.
Checking for Velocity is a good solution.
So you can do like,
body.velocity.magnitude
// magnitude to remove the direction issue.
// now apply all this as
body.AddForce(dir.normalized * expForce * calc * body.velocity.magnitude);
I try to move my character but he moves through the border of the scene.
When I debug it sayscharTransform has 0 0 0 coords in Vector3.
Transform charTransform;
float leftHorizontalBound;
float rightHorizontalBound;
void Start()
{
charTransform = this.transform;
leftHorizontalBound = camera.ViewportToWorldPoint (new Vector3 (0,0, camera.nearClipPlane)).x;
rightHorizontalBound= camera.ViewportToWorldPoint (new Vector3 (1,0, camera.nearClipPlane)).x;
}
void Update()
{
if(charTransform.position.x <= leftHorizontalBound)
{
charTransform.position = new vector2(leftHorizontalBound + 0.1f);
return;
}
if(charTransform.position.x >= rightHorizontalBound)
{
charTransform.position = new vector2(rightHorizontalBound - 0.1f);
return;
}
//MAKE HERE YOUR MOVEMENT BASED ON INPUT.
}
i cant exactly say what is your problem and wish you assume your problem more accurate but i think you are working something like a boad game with a fixed camera and something like a ball always moves and you just want to keep that object in the scene.
in your code you defined a position by variables and made a position check for every frame. thats not what basically is done in unity and there will be problems. you can make an empty object and put in on the borders you want and check the position of that object with your moving object.
your next way is make an object and add a collider component to it. if your game is 3d add a collider and if its 2d game add a 2d collider and a collider to moving object and after that it never moves toward the borders but still there will be some limits so if it didnt work i think you should add a rigidBody to both of them.