Does a speed of an object in unity affect Destroy()? - c#

So I am making a small game in unity where you have to shoot the enemy. However, when I made the script for the bullet and enemy, it half worked and half didn't. Sometimes, the bullet would hit the enemy and destroy the enemy, however, sometimes, it would take multiple shots for it to work. But when I turn the speed of the bullet to 1 (the speed of the bullet was 500), the bullet always destroys the enemy. So this leads me to think that this has something to do with the speed of the bullet. Here is my script
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
Destroy(other.gameObject);
Destroy(gameObject);
Debug.Log("e");
}
For the movement of the bullet, I just used transform.Translate(Vector3.up * Time.deltaTime * speed). How can I fix this?

The problem is not that Destroy do not work with a certain speed, the problem is that with certain speed you are not triggering the "OnTriggerEnter".
This fenomenon is called "tunneling" it happens when the object goes too fast.
That provokes that in one frame the projectile is on one side of the collider, and in the next frame is on the other side of the collider, giving the sensation like a teleport, so that's the why it do not collide, cause in any frame the engine has detected the collide.
If you're having troubles with high speed stuff, try to set your rigidbody (the one that is moving) to Interpolate, or use raycasts to fake bigger projectile colliders.

In addition to Lotan's answer
I just used transform.Translate(Vector3.up * Time.deltaTime * speed)
whenever using physics you do NOT want to set anything via Transform as this bypasses the physics engine and thereby breaks collision detection etc as in your case.
You rather want to through the Rigidbody component and e.g. use Rigidbody.MovePosition
Rigidbody.MovePosition moves a Rigidbody and complies with the interpolation settings
like
private Rigidbody _rigidbody;
private void Awake()
{
_rigidbody = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
_rigidbody.MovePosition(_rigidbody.position + _rigidbody.rotation * Vector3.up * Time.deltaTime * speed);
}
or why not simply set it only once
private void Start()
{
GetComponent<Rigidbody>().velocity = transform.up * speed;
}
Additionally you want to set the interpolation to dynamic.

A tricky way : bullet form A point to B point by one frame , so you can fake a point on a to b like
fake point: A+B/2
Point a ;
Point b ;
Update()
{
Point ab = (a+b)/2;
bullet.point = ab;
//check collider some .
bullet.point = b; // set it back.
a = b;
}
Not a good solution . but it have double hit rate.

Related

Weird - Transform.Translate

I want to move object to right and to left and i have a weird problem when doing it. I am doing Transform.Translate in update method with If(Input..)
This is my code for moving to right
player.transform.Translate(Vector3.right * Time.deltaTime * varLeftRight, Space.World);
(I tried to use ("player") with gameobject attached in inspector and I tried to use rigidbody ("b") but it doesn't help same issue happens))
where the variable varLeftRight is 125.
I am adding force to go forward in oncollision like this:
b.AddForce(Vector3.forward * speed);
And lets say my player is on coordinates:
-0.074 ->x
-1.5166 ->y
4.173041 -z
I put on player rigidbody is kinematic because I want to test why same issue happens when playing. The problem is when I click once it works and moves right
Cordinates with one click:
1.592691
-1.5166
4.173041
But the problem is when i click twice very fast the player position brakes and goes too much to right.
With twice fast clicks to go to right he goes too much and when I get back one field he need to be on this coordinates:
1.592691
-1.5166
4.173041
and he is on this cordinates:
2.101478
-1.5166
4.173041
Idk did I described issue good. I hope you will understand me,
so when I click once to move with trasnform.translate everything works fine
when I click twice quickly he goes too much to right and when I get back one field to see where is he with one click he is not on right place, to go back to left one field is the same method only with adding left parametar like this:
player.transform.Translate(Vector3.left * Time.deltaTime*varLeftRight, Space.World);
I tried removing the Space.World but it doesn't help.
btw: my obstacle collision have all rigidbody is kinematic to true (Tried without rigidbody same issue happens.
In on Collision I am adding force to go forward no other scripts in code.
public class PlayerControl : MonoBehaviour
{
public float bounce;
public float speed;
public float right;
Rigidbody rb;
public GameObject player;
public float varLeftRight;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
rb.AddForce(Vector3.forward * speed);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("add force");
player.transform.Translate(Vector3.right * Time.deltaTime * varLeftRight, Space.World);
}
if (Input.GetMouseButtonDown(1))
{
player.transform.Translate(Vector3.left * Time.deltaTime*varLeftRight, Space.World);
}
}
private void OnCollisionEnter(Collision collision)
{
rb.AddForce(Vector3.forward * speed);
}
}
I couldn't make sense of why you're adding force on collision, but I have a feeling that's where the issue lies.
I'd comment that out to see if you're still having an issue.
Say that's really the problem, maybe consider only adding force on collision on the proper circumstances (ie. if the player is just colliding with the ground should they get force? or just if colliding with certain elements? etc)

why my player rotates in unity

I attach a movement script to the player. with reference to the roller ball tutorial and modified it for jump. the problem is that when may player moves in any direction the player starts to rotate in that direction and even if i am standing still at the one position it starts to rotate and fall down the platform. the player has a rigidbody, boxcollider components.
void Awake ()
{
playerRigidbody = GetComponent<Rigidbody>();
Coll = GetComponent<CapsuleCollider>();
}
/*private void Update()
{
}*/
// Update is called once per frame
void FixedUpdate ()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
bool down = Input.GetKeyDown(KeyCode.Space);
if (down)
{
playerRigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}
Move(h, v);
}
private bool IsGrounded()
{
return Physics.CheckCapsule(Coll.bounds.center, new Vector3(Coll.bounds.center.x,Coll.bounds.min.y,Coll.bounds.center.z),Coll.height * 9f,Ground);
}
void Move (float h, float v)
{
movement.Set(h, 0f, v);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition(transform.position + movement);
}
In your code,you use AddForce function,so I confirm your player rigidbody doesn't set Kinematic to true.
When your charactor collision with any other objects ,such as ground,wall, your charactor maybe rotate.
You could freeze rigidbody's rotation via inspector like Bijan says.
I don't recommand your use AddForce and MovePosition together.It may has other problem.
like this
This is due to the nature of the Rigidbody component. This component is very lifelike and when force is added and removed, the object still has inertia and therefore the object is still moving.
When it comes to the rotation it is due to the fact that when a force is applied to an object, the object also acquires rotational motion (also known as angular motion).
You can freeze the rotation using the constraints on the Rigidbody component.
Have a deeper look into this component [here].1
In my opinion, it is one of the core components of Unity and it is definitely worth looking into.
I hope I answered your question!

Unity OnTriggerEnter2D hit the ball to floor on high speed doesn't work

When I set the speed of Ball = 10 OnTriggerEnter2D to test the ball hit on the floor work fine, but when I set the speed higher (20), OnTriggerEnter2D doesn't work and the ball falls down through the floor
My code:
void Start () {
rigiBody = GetComponent<Rigidbody2D>();
ballLayer = 1 << LayerMask.NameToLayer("Ball");
}
void OnTriggerEnter2D(Collider2D other) {
if (other.CompareTag(Constants.FLOOR_TAG))
{
Debug.Log("FLOOR_TAG");
if (HitFloor != null)
HitFloor(this);
}
}
void FixedUpdate() {
Vector2 tempVect = Direction;
tempVect = tempVect.normalized * Speed * Time.deltaTime;
Vector2 newPos = rigiBody.position + tempVect;
rigiBody.MovePosition(newPos);
timer += Time.deltaTime;
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * Time.deltaTime * 1.2f, ~(ballLayer));
if (!hit)
return;
...
Inspector of Ball below
What's wrong with this code?
ps I'm using Unity 2017.1.1f1 Personal
You have to change the "Collision Detection" property of rigidbody. It should be "continuous" not "discrete". If you choose discrete value you are telling rigidbody to check collision in discrete time intervals. If you move in a high speed, rigidbody will probably miss collision.
Set the collision detection mode in the Rigidbody2D component to Continuous. Documentation
And maybe changing
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * Time.deltaTime * 1.2f, ~(ballLayer));
to
RaycastHit2D hit = Physics2D.Raycast(newPos, Direction, Speed * 1.2f, ~(ballLayer));
will fix the problem aswell.
Why do you cast the Ray from the newPosition? Imo. You should cast it from its current Position.
Solution of my problem was really close
A couple of lines were added and changed
add [RequireComponent(typeof(Rigidbody2D))] for class Ball
replace variable RigiBody to property
Now everything work well, and balls not fall through the floor on high speed
Code after changes look like this
[RequireComponent(typeof(Rigidbody2D))] // Added this code
public class Ball : MonoBehaviour {
private Rigidbody2D _rigiBody;
public Rigidbody2D RigidBody { //And this property
get {
if (_rigiBody == null)
_rigiBody = GetComponent<Rigidbody2D>();
return _rigiBody;
}
}

Building a rotatable lift in Unity

I got a "cube" in Unity. This cube has a Trigger, when moving into it, the object gets grabbed into the air. This is working as a lift, you can see an example what I mean here. It is a small scene taken from Spyro 2.
https://youtu.be/f8wWMa4N5mE?t=643
The code I use is really small for now
private float liftSpeed = 10; // the speed, the object is flying up
private void OnTriggerStay(Collider col)
{
Rigidbody objectRigid = col.gameObject.GetComponent<Rigidbody>(); // get the rigidbody from the object in the trigger
if (objectRigid != null) // does it have a rigidbody?
objectRigid.velocity = new Vector3(objectRigid.velocity.x, liftSpeed, objectRigid.velocity.z); // make it fly in the air
}
So I have a lift, that totally works fine. But I when I rotate my lift I want it to work aswell.
Some examples (my gamne is 3D)
So how can I make my lift work for all "rotations"?
You can use transform.up to get the up direction of your lift, and then multiply by the lift speed.
objectRigid.velocity = transform.up * liftSpeed;
transform.up changes depending on how the object is rotated, so if your lift is rotated to the left, then the lift will carry objects to the left.
You can use the transforms RotateAround Method for a rotation like in the video clip.
Transform t = col.gameObject.GetComponent<Transform>();
transform.RotateAround(Vector3.zero, Vector3.up, 20 * Time.deltaTime);
The given snipet lets the object rotate around it's axis, which is pointing up.
Make the player child of the lift OnTriggerEnter.
Move the lift as you do
You can rotate the lift using RotateAround function. The player should also rotate along with the lift, as it's a child of the lift now.
make player's parent to null when it's out of lift trigger using OnTriggerExit
This is my solution to the problem.
The lift and the EndPoint will be Empty GameObjects. If you want the lift has a platform or a base you can add a GameObject as child of the lift with the shape you prefer.
You need to attach a Collider to the lift and set it as trigger.
Then you add the following script to the lift Empty GameObject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLift : MonoBehaviour {
public Transform end;
float speed = 4f;
bool liftActivated = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(liftActivated)
{
float step = speed * Time.deltaTime;
//To move up the lift
transform.position = Vector3.MoveTowards(transform.position, end.position, step);
//To spin the lift
transform.RotateAround(Vector3.up, 2 * Time.deltaTime);
//To stop spining the lift when it reaches the end of the path
if(transform.position.Equals(end.position))
liftActivated = false;
}
}
void OnTriggerEnter(Collider other){
liftActivated = true;
other.gameObject.transform.parent = this.transform;
other.gameObject.GetComponent<Rigidbody>().isKinematic=true;
}
}
Then you should decide if once the lift reaches its destination it is the player who has to move out of the platform, or if you unparent it and let it fall down as the end of the trip

Air hockey game - Player bat goes through puck if moved too fast

Im currently developing an Air hockey game in Unity3d. The issue I'm having is that when the player attempts to hit the puck too quickly, the player ends up going through the puck and therefore there is no collision. The game works perfectly as expected if the player stays still and the puck hits the player or if the player hits the puck at a slow pace.
The player has a rigidbody using continuous collision detection using a capsule collider. The puck also has rigidbody with continuous dynamic collision detection and a mesh collider with convex.
I tried setting the fixed timestep to 0.01 but that didn't have an effect. Here is the script for the player movement:
void ObjectFollowCursor()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 point = ray.origin + (ray.direction * distance);
Vector3 temp = point;
temp.y = 0.2f; // limits player on y axis
cursorObject.position = temp;
}
and here is the code for the puck when it collides with the player:
// If puck hits player
if(collision.gameObject.tag == "Player")
{
Vector3 forceVec = this.GetComponent<Rigidbody>().velocity.normalized * hitForce;
rb.AddForce(forceVec, ForceMode.Impulse);
Debug.Log ("Player Hit");
}
Any help would be much appreciated. Thanks.
The problem you are having its called "tunneling".
This happens because your object is moving at a high speed and in that specific frame the collision is not detected. In frame n the ball is just in front of the bat, but when frame n+1 is calculated the ball has moved behind the bat, thus "missing" the collision completely.
It is a common problem but there are solutions.
I recommend you study this script and try to implement on your game.
This is not my code:
SOURCE: http://wiki.unity3d.com/index.php?title=DontGoThroughThings
using UnityEngine;
using System.Collections;
public class DontGoThroughThings : MonoBehaviour
{
// Careful when setting this to true - it might cause double
// events to be fired - but it won't pass through the trigger
public bool sendTriggerMessage = false;
public LayerMask layerMask = -1; //make sure we aren't in this layer
public float skinWidth = 0.1f; //probably doesn't need to be changed
private float minimumExtent;
private float partialExtent;
private float sqrMinimumExtent;
private Vector3 previousPosition;
private Rigidbody myRigidbody;
private Collider myCollider;
//initialize values
void Start()
{
myRigidbody = GetComponent<Rigidbody>();
myCollider = GetComponent<Collider>();
previousPosition = myRigidbody.position;
minimumExtent = Mathf.Min(Mathf.Min(myCollider.bounds.extents.x, myCollider.bounds.extents.y), myCollider.bounds.extents.z);
partialExtent = minimumExtent * (1.0f - skinWidth);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate()
{
//have we moved more than our minimum extent?
Vector3 movementThisStep = myRigidbody.position - previousPosition;
float movementSqrMagnitude = movementThisStep.sqrMagnitude;
if (movementSqrMagnitude > sqrMinimumExtent)
{
float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
RaycastHit hitInfo;
//check for obstructions we might have missed
if (Physics.Raycast(previousPosition, movementThisStep, out hitInfo, movementMagnitude, layerMask.value))
{
if (!hitInfo.collider)
return;
if (hitInfo.collider.isTrigger)
hitInfo.collider.SendMessage("OnTriggerEnter", myCollider);
if (!hitInfo.collider.isTrigger)
myRigidbody.position = hitInfo.point - (movementThisStep / movementMagnitude) * partialExtent;
}
}
previousPosition = myRigidbody.position;
}
}
You were right to try continuous collision detection (CCD). There are some constraints (especially in this case where you want to use CCD with two moving objects rather than one moving object and one static object), but it is designed for this kind of scenario. The Rigidbody documentation goes into these constraints:
Set the collision detection mode to Continuous to prevent the
rigidbody from passing through any static (ie, non-rigidbody)
MeshColliders. Set it to Continuous Dynamic to also prevent the
rigidbody from passing through any other supported rigidbodies with
collision detection mode set to Continuous or Continuous Dynamic.
Continuous collision detection is supported for Box-, Sphere- and
CapsuleColliders.
To sum up, both puck and paddle need to be set to Continuous Dynamic, and both need to be Box-, Sphere-, or Capsule Colliders. If you can make these constraints work for your game you should be able to get continuous collision detection without writing it yourself.
A note about Unity's CCD that bears repeating:
Note that continuous collision detection is intended as a safety net
to catch collisions in cases where objects would otherwise pass
through each other, but will not deliver physically accurate collision
results, so you might still consider decreasing the fixed Time step
value in the TimeManager inspector to make the simulation more
precise, if you run into problems with fast moving objects.
But since you are manually specifying the collision reaction, that might not be an issue.

Categories