Reuse or copy collision force - unity3d

Whenever my character collides with an object, i add a rigidbody to that object; and i also would like to reapply the same force it should have received with the collision on the same point, so it reacts with physics.
This is my attempt so far, but force is way too much as all objects fly from the scene:
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.GetComponent<Rigidbody>() == null) {
collision.gameObject.AddComponent<Rigidbody>().AddForceAtPosition(collision.impulse, collision.contacts[0].point);
}
}
I also tried with:
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.GetComponent<Rigidbody>() == null) {
collision.gameObject.AddComponent<Rigidbody>().AddForceAtPosition(collision.impulse / Time.fixedDeltaTime, collision.contacts[0].point);
}
}
What i'm doing wrong/missing?

Your point of collision might be to far from the origin of the object.
Check:
if the collision box is proportionnal to the object.
the colision point makes sense with the actual position of the object.
the mass of the rigidbody is adequate.
Note that when position is far away from the center of the rigidbody
the applied torque will be unrealistically large.
from unity doc page
as for the colision interpretation in the code, i found this piece of code in this article describing How to get the impact force of a collision in Unity
public class Character : MonoBehaviour {
private void OnCollisionEnter (Collision collision) {
float collisionForce = collision.impulse.magnitude / Time.fixedDeltaTime;
if (collisionForce < 100.0F) {
// This collision has not damaged anyone...
}
else if (collisionForce < 200.0F) {
// Auch! This will take some damage.
}
else {
// This collision killed me!
}
}
}

Related

2D object not being propelled by AddForce

I'm programming a top-down survival type game, and I added slimes that have a simple AI, which just follows the player when in range. I want to make it so when the slimes touch the player, you will take damage, and the slime will get knocked back a little. This is the code I have to add force to the slime. I've tried reversing the kbDirection, the player and slime are both not kinematic, the "KB" debug does show up when they collide, and the player still loses health.
public void AddKnockback()
{
Vector2 kbDirection = transform.position - player.transform.position;
kbDirection.Normalize();
slimeRb.AddForce(kbDirection * 100, ForceMode2D.Impulse);
Debug.Log("KB");
}
void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.tag == "Player")
{
player.GetComponent<PlayerActions>().playerHealth--;
AddKnockback();
}
}
I suspect that it might have something to do with the way the slime's movement is calculated. This is what I have for the movement:
public void AddKnockback()
{
Vector2 kbDirection = transform.position - player.transform.position;
kbDirection.Normalize();
slimeRb.AddForce(kbDirection * 100, ForceMode2D.Impulse);
Debug.Log("KB");
}
void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.tag == "Player")
{
player.GetComponent<PlayerActions>().playerHealth--;
AddKnockback();
}
}
Most prob your slime follows the target by overriding its position. like slime.transform.position = targetPosition; That is why the force effect can't change the position of the slime. That's only an assumption since I don't know how you move them. But sounds possible to me that is why I shared it with you.

Why is this Projectile Script Continuing to Hit Targets After I've Destroyed It?

I am new to Unity2D. I am trying to make castle defense game. When the spawners start to inheritance the enemies overlap (they should), but when the archer arrow collides whit the enemies it kills them all. I searched everywhere for the answer of this but nothing...
My questions is:
Is there a way to only hit one target at time?
Arrow script:
void Start()
{
target = GameObject.FindGameObjectWithTag("Enemy").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speedProjectile * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Enemy"))
{
Destroy(gameObject);
}
}
Enemy script:
void Start()
{
target = GameObject.FindGameObjectWithTag("target3").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
transform.position = Vector2.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject.CompareTag("arrow"))
{
EHealth -= HDamage;
}
if (EHealth <= 0)
{
Destroy(gameObject);
}
Your enemies are all taking damage because destroying a GameObject isn't something that happens immediately (for good reason). Because of this, in a single frame, any number of enemies can be hit by the same arrow.
If you'd like to rely on these collision methods, I'd suggest controlling the damage from the arrow instead of from the enemy, so you can be sure it only hits something once:
private bool dealtDamage = false;
private void OnTriggerEnter2D(Collider2D col) {
// Only deal damage once
if (dealtDamage) {
return;
}
// Does the thing I hit have this "EnemyScript" ?
var enemy = col.gameObject.GetComponentInChildren<EnemyScript>();
if (enemy == null) {
return;
}
// Make this "DealDamage()" method public on your EnemyScript
enemy.DealDamage();
dealtDamage = true;
Destroy(gameObject);
}
Then get rid of the Enemy Script's OnTriggerEnter2D because the arrow is handling it all. I don't know what the name of the script is on your enemies so I just called it EnemyScript. This is also calling a DealDamage() method that you'd have to make (which would probably look a lot like the one you current have listed in your "Enemy Script.")
its hard to say with the information given, based on what you have said it seems that all the entity's are just 1 entity(so when you kill 1 enemy you kill the only enemy which is all of them). you can have them run independently from each other.
Maybe you can use collision enter function to check bullet is hitting enemy's body.
You can use this code below...
private bool isEntered = false;
void OnCollisionEnter(Collision collision)
{
if(isEntered) return;
if(collision.gameObject.tag == "enemy") isEntered = true;
....
....
}
I hope it will work for you.
Finally figured it out. First of all the script is dealing damage so I should hit the target specific number of times, so I can't stop the collision of the arrow without turning it back on. So I made a method, that is being Invoked (InvokeRepeating()) after the arrow collides, that turns a bool back to false. Which method (function) of course should be in Update();
I am not sure if I am saying it right bc I am a beginner but I hope this helps somebody. :D
And here is the code:
Arrow Script:
private void DealDamage()
{
if (hit == true)
return;
eHealth.EHealth -= hDamage.HDamage;
hit = true;
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("enemy"))
{
DealDamage();
Destroy(gameObject); //Destroying arrow on collision
}
}
private void HitSetter()
{
hit = false;
}
void Update()
{
InvokeRepeating("HitSetter", 0f, 1.1f);
}
Enemy Script:
void Update()
{
if (EHealth <= 0)
{
anim.SetBool("EnemyDie", true);
Destroy(gameObject, 0.833f);
}
Also thanks to #Foggzie

Unity Jumping Issue - Character won't jump if it walks to the platform

(This is a 2D project)
I have a jumping issue where if my character WALKS into an X platform, she won't jump, but when she JUMPS ONTO the X platform, she can perform a jump.
For the platforms I am currently using 2 Box Collider 2Ds (one with "is trigger" checked)
For the character I am currently using 2 Box Collider 2Ds (one with "is trigger" checked) and Rigidbody 2D.
Below is the code for jumping and grounded I am currently trying to use.
{
public float Speed;
public float Jump;
bool grounded = false;
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
if (grounded)
{
GetComponent<Rigidbody2D>().velocity = new Vector2(GetComponent<Rigidbody2D>().velocity.x, Jump);
}
}
}
void OnTriggerEnter2D()
{
grounded = true;
}
void OnTriggerExit2D()
{
grounded = false;
}
}
Issue arises on the same part of every platform. (Each square represents a single platform sprite and they have all the same exact characteristics, since I copy pasted each one of them). Please check the photo on this link: https://imgur.com/a/vTmHw
It happens because your squares have seperate colliders. Imagine this:
There are two blocks : A and B. You are standing on block A. Now you try to walk on block B. As soon as your Rigidbody2D collider touches block B, your character gets an event OnTriggerEnter2D(...). Now you claim, that you are grounded.
However, at this moment you are still colliding with block A. As soon as your Rigidbody2D no longer collides with block A, your character receives OnTriggerExit2D(...). Now you claim, that you are no longer grounded. But in fact, you are still colliding with block B.
Solution
Instead of having bool variable for checking if grounded, you could have byte type variable, called collisionsCounter:
Once you enter a trigger - increase the counter.
Once you exit a trigger - decrease the counter.
Do some checking to make sure you are actually above the collider!
Now, once you need to check if your character is grounded, you can just use
if (collisionsCounter > 0)
{
// I am grounded, allow me to jump
}
EDIT
Actually, after investingating question further, I've realized that you have totally unnecessary colliders (I'm talking about the trigger ones). Remove those. Now you have only one collider per object. But to get the calls for collision, you need to change:
OnTriggerEnter2D(...) to OnCollisionEnter2D(Collision2D)
OnTriggerExit2D(...) to OnCollisionExit2D(Collision2D)
Final code
[RequireComponent(typeof(Rigidbody2D))]
public sealed class Character : MonoBehaviour
{
// A constant with tag name to prevent typos in code
private const string TagName_Platform = "Platform";
public float Speed;
public float Jump;
private Rigidbody2D myRigidbody;
private byte platformCollisions;
// Check if the player can jump
private bool CanJump
{
get { return platformCollisions > 0; }
}
// Called once the script is started
private void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
platformCollisions = 0;
}
// Called every frame
private void Update()
{
// // // // // // // // // // // // // //
// Need to check for horizontal movement
// // // // // // // // // // // // // //
// Trying to jump
if (Input.GetKeyDown(KeyDode.UpArrow) && CanJump == true)
Jump();
}
// Called once Rigidbody2D starts colliding with something
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.collider.tag == TagName_Platform)
platformCollisions++;
}
// Called once Rigidbody2D finishes colliding with something
private void OnCollisionExit2D(Collision2D collision)
{
if(collision.collider.tag == TagName_Platform)
platformCollisions--;
}
// Makes Character jump
private void Jump()
{
Vector2 velocity = myRigidbody.velocity;
velocity.y = Jump;
myRigidbody.velocity = velocity;
}
}
Here can be minor typos as all the code was typed inside Notepad...
I think there are a couple of issues here.
Firstly, using Triggers to check this type of collision is probably not the best way forward. I would suggested not using triggers, and instead using OnCollisionEnter2D(). Triggers just detect if the collision space of two objects has overlapped each other, whereas normal collisions collide against each otehr as if they were two solid objects. Seen as though you are detecting to see if you have landed on the floor, you don't want to fall through the floor like Triggers behave.
Second, I would suggest using AddForce instead of GetComponent<Rigidbody2D>().velocity.
Your final script could look like something like this:
public class PlayerController : MonoBehaviour
{
public float jumpForce = 10.0f;
public bool isGrounded;
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void OnCollisionEnter2D(Collision2D other)
{
// If we have collided with the platform
if (other.gameObject.tag == "YourPlatformTag")
{
// Then we must be on the ground
isGrounded = true;
}
}
void Update()
{
// If we press space and we are on the ground
if(Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
// Add some force to our Rigidbody
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
// We have jumped, we are no longer on the ground
isGrounded = false;
}
}
}

how do I make a platform disappear when I bounce on it in unity?

My goal is to have a platform destroy if it is landed on. Landing on the platform also bounces me up (it is an endless jumper).
The problem is the destroying of the layers isn't consistent. Sometimes the bounce and destroy work as intended, sometimes the platform won't get destroyed (if I bounce on it again it will destroy), other times the platform will destroy and the bounce won't initiate. I am not sure how to make it work every time and why it isn't working properly.
This is the code I am using for the bounce:
public bool platformTouch; //true or false if you are grounded
public Transform groundCheck; //Object which will check if we are grounded
public bool groundedTouch;
public LayerMask ground; //Decide which layers count as grounded.
float groundRadius = .2f; //Radius around ground check object will check if grounded
public LayerMask platform; //Decide which layers count as grounded.
public Rigidbody2D Player;
void FixedUpdate()
{
platformTouch = Physics2D.OverlapCircle(groundCheck.position, groundRadius, platform);
groundedTouch = Physics2D.OverlapCircle(groundCheck.position, groundRadius, ground);
}
Here is the destroy code I am using. If the player's velocity is less than 0 (falling) it should trigger the destroy. There is also a delay in the destroy, so that the bounce triggers.
public Rigidbody2D Player;
void Update()
{
Player = GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>();
}
void OnTriggerEnter2D(Collider2D collider)
{
if (collider.gameObject.tag == "Destroyer")
{
if (Player.velocity.y <= 0)
{
StartCoroutine(DestroyPlatforms());
}
}
}
IEnumerator DestroyPlatforms()
{
yield return new WaitForSeconds(.1f); //waits .1 seconds
Destroy(gameObject); //this will work after .1 seconds.
//play sound
}
I recommend that you increase your collider on the platform and use OnTriggerExit with Destroy(this,0.1) , again I can be wrong. But mb it will help
Physics engines are not deterministic, and it's usually not a good practice to have 2 objects checking for the same collision (one object could trigger and not the other in the same frame) I think that's the problem here.
The collision should only be handled by the platform and call a bounce method on the player in the OnTriggerEnter2D right before calling the StartCoroutine(DestroyPlatforms());

how to an object sticks to another object and these come back to start point in Unity

I would like to catch an object with another object. And then carry it to the start point of first object, in 2D.
I'm throwing an object to another object with a constant velocity.
The first one collides with a second one, after that I defined reverse velocity of the first one to the standing second object. When a collision happens (onCollisonListener) it doesn't turn back naturally, so the angle of turning back is wrong.
They go everywhere randomly. How can I make it so that turning back functions properly? Which function could I use? I am using only velocity.x and velocity.y
Thanks for reading my question and for your help,
Sincerely Yours.
Pseudo code:
public class Catcher : MonoBehaviour
{
Vector2 startingPoint;
void Awake()
{
startingPoint = transform.position;
}
void Update()
{
//Your moving code
}
void OnTriggerEnter(Collider other)
{
if(other.tag == "someTag")
{
other.transform.parent = transform;
StartCoroutine("MoveBack");
}
}
private IEnumerator MoveBack()
{
while(transform.position != startingPoint)
{
// Move this towards starting point
// Use Vector2 functions like Lerp, MoveTowards or SmoothDamp
// Could also use Mathf functions
yield null;
}
}
}
And maybe add a flag/check to know if the object is moving back or forward.