I've got a bullet script with a particle system and a decal.
I think that it has something to do with these events not being able to fire in time or with fps in update. Not sure yet.
So, it's being late.
The ellow points are where the particles start to play. They should be right on these wooden walls. There should be three particles working and three bullet holes, kinda bullet penetrating one wall and getting destroyed on the second one.
THE QUESTION IS HOW TO MAKE IT WORK NORMAL, SO THAT THE TRIGGERS WORK WHEN NEEDED AS WELL AS THE PARTICLES AND THE DECALS? Maybe there's a way to excellerate the code to work on time? Or maybe there's another problem with that?
The screenshot:
The Code:
public class BulletScript : MonoBehaviour {
public bool isThrough = true;
public float BulletSpeed = 100;
public float CurrentDamage;
public float EnterLuft = -0.005f;
public float ExitLuft = 0.05f;
public GameObject woodParticle;
private ContactPoint CollisionPoint;
public GameObject BulletMarkPref;
Rigidbody bullet;
private void Start()
{
bullet = this.GetComponent<Rigidbody>();
}
void FixedUpdate () {
bullet.velocity = Vector3.forward * BulletSpeed;
//this.transform.Translate(Vector3.forward * BulletSpeed * Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
Transform hitPoint = this.transform;
LevelObject.LvlObjectType objectType = other.gameObject.GetComponent<LevelObject>().objType;
if(objectType == LevelObject.LvlObjectType.obstacle)
{
if (isThrough)
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(this.transform, true);
}
else
{
Instantiate(woodParticle, hitPoint.localPosition, Quaternion.LookRotation(-hitPoint.forward)).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
else if(objectType == LevelObject.LvlObjectType.obstacle)
{
Destroy(this.gameObject);
}
else if(objectType == LevelObject.LvlObjectType.wall)
{
LeaveBulletMark(hitPoint, true);
Destroy(this.gameObject);
}
}
private void OnTriggerExit(Collider other)
{
Transform hitPoint = this.transform;
Instantiate(woodParticle, hitPoint.localPosition, hitPoint.rotation).GetComponent<ParticleSystem>().Play();
LeaveBulletMark(hitPoint, false);
}
void LeaveBulletMark(Transform hitPoint, bool ifEnter)
{
GameObject TemporaryBulletMarkHandler;
TemporaryBulletMarkHandler = Instantiate(BulletMarkPref, hitPoint.localPosition, Quaternion.LookRotation(ifEnter ? hitPoint.forward : CollisionPoint.normal)) as GameObject;
isThrough = false;
TemporaryBulletMarkHandler.transform.Translate(hitPoint.forward * (ifEnter ? 0.005f : -0.005f));
}
}
I don't think your problem is something simple with the code. There is an inherent issue with calculating fast moving objects like bullets with true physics calculations especially if they are small. Often between physics updates, they pass through wall colliders completely without registering.
You have to think of it like this to see the problem. The bullet isn't tracked continuously along its trajectory. It has a starting location, a formula for its movement and it calculates a new location at each physics update. You could fire a bullet straight at a wall, and in one update the bullet would be several meters in front of the wall, and in the next, it would be several meters behind the wall without ever triggering a collision. This is why so many game use ray tracing to calculate bullet trajectories. The bullet movement isn't perfectly accurate, especially for long shots, but obstructions to the bullet path are registered.
By default unity's Physics Engine runs at 50 frames per second. A modern bullet travels between 1200 and 1700 m/s. That gives you a distance between 24 and 34 meters traveled between frames. Even a small object falling at terminal velocity (54 m/s) might pass through a collider unnoticed. If you made a 1-meter thick box collider, you would likely register a falling object but not a bullet.
I think you could do some clever combination of ray tracing and bullet physics to get the best of both worlds. Maybe you could ray trace from the bullet at each fixed update or there may be some better technique already invented for this exact situation that I don't know about.
Related
In my 2D Unity game, balls can bounce off each other. My goal is to have them bounce off each other as you would expect physically in the real world.
The only difference is that the speed of the balls decreases, but they can never come to a stop.
I have different approaches to map the physical behavior. But none of them leads to a good result.
(1)
private void OnCollisionEnter2D(Collision2D collision) {
var point = collision.GetContact(0);
var curDire = transform.TransformDirection(Vector2.up);
var newDir = Vector2.Reflect(curDire, point.normal);
transform.rotation = Quaternion.FromToRotation(Vector2.up, newDir);
speed *= 0.75f;
}
I don't know how describe exactly: It can happen that the balls push past each other. They collide, but do not bounce off each other, but maintain their direction.
Another approach I have read about is to add an AddForce to the velocity of Rigidbody2D.
Then the following code should do the physics.
private void Start()
{
rb = GetComponent<Rigidbody2D>();
_velocity = new Vector3(1f, 1f, 0f);
_rb.AddForce(_velocity, ForceMode2D.Force);
}
private void OnCollisionEnter2D(Collision2D collision) {
var inDirection = GetComponent<Rigidbody2D>().velocity;
var inNormal = collision.contacts[0].point;
var newVelocity = Vector2.Reflect(inDirection, inNormal);
rb.velocity = newVelocity;
}
The results are really random. I think I use a wrong AddForce or the velocity-Vector is strange. I fall here the deeper mathematical understanding to fully comprehend this solution.
(3) Then I read about Physic Material 2D. This sounds best. I created one (Asset -> Create -> 2D -> Physic Material 2D). Set bounciness to 1 the friction to 0.1. I select my Ball-prefab and add the physic material to my collider. I added it to the Rigidbody2D, too. But this asset has no effect. (My balls have no gravity. I don't know if this is important.)
I imagine Unity has a super simple solution for this problem. But I just can't find it. Does anyone have any ideas or approaches for me? I actually just want shot balls to bounce off each other. This should look as much as possible like it would look in real life.
Update Question
I move the ball like that
private Vector3 _inDirection;
private void Start()
{
_inDirection = Vector3.right * (speed * Time.deltaTime * (_didCollide ? -1 : 1));
}
private void FixedUpdate()
{
transform.Translate(_inDirection);
}
I'm looking not so much for a throw as just maintaining motion after a ball is released by the hand in Hololens 2. Currently, I'm using the MRTK IMixedRealityTouchHandler interface, mainly the functions public void OnTouchStarted(HandTrackingInputEventData data) and OnTouchCompleted(HandTrackingInputEventData data).
On the Hololens 2 Emulator, when I release the ball with my hand (mouse), it drifts off in the air in the general direction I was pointing it towards relatively slowly, which is exactly what I want. I achieved this by reducing drag. However, once I build to the HL2 device itself, this motion is not emulated and the ball stops midair immediately after it is released. Why is this happening?
I've tried adding the line rb.AddRelativeForce(Vector3.forward * magnitude, ForceMode.Force); in OnTouchCompleted which wasn't successful. How can I maintain the ball's motion after it is released by my hand?
In general (I don't see the rest of your code) you can keep updating the velocity relative to the last frame and finally apply it.
Somewhat like e.g. (pseudo code)
private Vector3 velocity;
void BeginDrag()
{
rb.isKinematic = true;
rb.velocity = Vector3.zero;
lastFramePos = rb.position;
}
void WhileDrag(Vector3 position)
{
velocity = position -rb.position;
rb.position = position;
}
void EndDrag()
{
rb.isKinematic = false;
rb.velocity = velocity;
}
or actually even easier and probably more accurate you can directly use the
public void OnTouchCompleted(HandTrackingInputEventData data)
{
rb.velocity = data.Controller.Velocity;
}
See
HandTrackingInputEventData.Controller
IMixedRealityController.Velocity
So I am trying to make a multiplayer game with abilities sort of like Overwatch/Paladins. All in all, one ability should be a sort of projectile that moves across the ground and allows that player to teleport to its position at any time while it is alive. I can't find the solution to teleporting only the player that shot it since thus far in my tests, when one player activated their ability, all players would teleport. How can I solve this?
My code:
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
GetComponent<playerController>().heldAbility = "gateCrash";
if (GetComponent<playerController>().heldAbility == "gateCrash")
holding = true;
else
holding = false;
if (holding && Input.GetMouseButtonDown(0))
PhotonNetwork.Instantiate(Path.Combine("PhotonPrefabs", "GateCrashModel"), spawnPos, transform.rotation, 0);
}
This code is attached to the projectile:
public float speed = 10;
PhotonView pv;
private void Awake()
{
pv = transform.GetComponent<PhotonView>();
}
private void FixedUpdate()
{
transform.Translate(transform.forward * speed * Time.fixedDeltaTime);
}
I guess that I should make have something as instatntiation parameter but idk what.
A simple approach would be to add a shotOwner property to each of your projectiles. Every time a projectile is fired, update shotOwner to point to the player object that fired the shot. (This will also let you implement "Player_X killed Player_Y" functionality, among other things.)
I want to make sure that various objects moving at high speed cannot pass through walls or other objects. My thought process was to check via Raycast if a collision has occurred between two moments of movement.
So the script should remember the previous position and check via Raycast for collisions between previous and current position.
If a collision has occurred, the object should be positioned at the meeting point and moved slightly in the direction of the previous position.
My problem is that works outside the map not inside. If I go from inside to outside, I can go through the walls. From outside to inside not.
Obviously I have misunderstood something regarding the application with raycasts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObsticalControll : MonoBehaviour
{
private Vector3 positionBefore;
public LayerMask collisionLayer = 9;
private Vector3 lastHit = new Vector3(0, 0, -20);
// Start is called before the first frame update
void Start()
{
positionBefore = transform.position;
}
private void OnDrawGizmos()
{
Gizmos.DrawCube(lastHit, new Vector3(.2f,.2f,.2f));
}
// Update is called once per frame
void Update()
{
Vector3 curruentMovement = transform.position;
Vector2 dVector = (Vector2)transform.position - (Vector2)positionBefore;
float distance = Vector2.Distance((Vector2)positionBefore, (Vector2)curruentMovement);
RaycastHit2D[] hits = Physics2D.RaycastAll((Vector2)positionBefore, dVector, distance, collisionLayer);
if(hits.Length > 0)
{
Debug.Log(hits.Length);
for (int i = hits.Length -1 ; i >= 0 ; i--)
{
RaycastHit2D hit = hits[i];
if (hit.collider != null)
{
Debug.Log("hit");
lastHit.x = hit.point.x;
lastHit.y = hit.point.y;
Vector3 resetPos = new Vector3(hit.point.x, hit.point.y, transform.position.z) + positionBefore.normalized * 0.1f;
transform.position = new Vector3(resetPos.x, resetPos.y, transform.position.z);
}
}
}
positionBefore = transform.position;
}
}
Theres a better way to deal with this that unity has built in.
Assuming the object thats moving at a high speed has a RigidBody(2d in your case) you can set its Collision Detection to Continuous instead of Discrete.
This will help collisions with high speed collision, assuming that its moving at high speed and the wall is not moving.
If for some reason you cannot apply this to your scenario, Ill try to help with the raycast solution.
However, I am still wondering about the collision behavior of my raycast script. That would be surely interesting, if you want to calculate shots or similar via raycast
Alright, so your initial idea was to check if a collision had occurred, By checking its current position and its previous position. And checking if anything is between them, that means a collision has occurred. And you would teleport it back to where it was suppose to have hit.
A better way todo this would be to check where the GameObject would be in the next frame, by raycasting ahead of it, by the distance that it will travel. If it does hit something that means that within the next frame, it would have collided with it. So you could stop it at the collision hit point, and get what it has hit. (This means you wouldn't have to teleport it back, So there wouldn't be a frame where it goes through then goes back)
Almost the same idea but slightly less complicated.
Problem would be that if another object were to appear between them within the next frame aswell, it could not account for that. Which is where the rigidbody.movePosition shines, And with OnCollisionEnter you can detect when and what it collided with correctly. Aswell as without the need to teleport it back
I'm making 2D mobile game and I have a ball, but my ball doesn't have a constant moving speed. What I need to do?
When I build game on my android device, ball have a various speed. In that case, my game is not playable.
I hope that someone can help me. Thanks.
This is on my start function:
void Start () {
GetComponent<Rigidbody2D> ()
.AddForce (new Vector2 (1f, 0.5f)* Time.deltaTime * force);
}
Is it a good practice if I used in code " Application.LoadLevel ("__Scena_0"); " to load existing scene when I lose " life" ? Problem happens when I lost " life " and try playing game in second chance.
My update function is about OnTriggerEnter2D when my ball hit trigger objects.
EDIT 23.12.2015. : problem solve with adding physics material (fiction 0) and adding to script:
using UnityEngine.SceneManagement;
...
SceneManager.LoadScene ("MainScene");
The problem is the calculation of the force vector:
new Vector2 (1f, 0.5f) * Time.deltaTime * force
You are using Time.deltaTime as a factor, which means that the applied force is not constant, but depending on the deltaTime, which is the duration of the last frame. This explains why it changes randomly.
I don't think Time.deltaTime is what you want here, try just removing the factor and adjusting the "force" factor accordingly. You should now have a constant force applied, independent from the platform you play on.
Create a new Physics Material, set friction zero and add it to your object. If your object has no friction, its speed cannot be decreased. Then, use AddForce() on Rigidbody2D to speed up.
Assuming that your ball and colliding walls have bouncy materials with no friction. Try
public float _ballSpeed = 2.5f;
Rigidbody2D _rb;
void Start()
{
_rb = GetComponent<Rigidbody2D>();
_rb.velocity = Vector2.one * _ballSpeed;
}
void Update()
{
}
From this you can control ball speed through _ballSpeed
//========== OR ==========//
If you want to retain speed even after ball destruction.
Retain _ballSpeed in any global static variable.
Suppose you have a class named Globals, declare a variable like,
public class Globals
{
public static float BALL_SPEED = 2.5f;
}
Now in Ball Script
Rigidbody2D _rb;
void Start()
{
_rb = GetComponent<Rigidbody2D>();
_rb.velocity = Vector2.one * Globals.BALL_SPEED;
}
void Update()
{
}