I am doing a Voxel game and my engine was using Physics.raycasts and Mesh colliders for getting the coordinates of the block you clicked on until now. I decided to remove the mesh collider, because it was just eating too much performance in some situations and I got fps dropdowns to 0.1fps for a few seconds (eg you should be able to scroll through the y-layers) and I only needed it for raycasting. I don't use any other physics related stuff. Without the mesh colliders the framerate is stable at 60 to 100 fps, but now I find it hard finding another way getting the information on which block I am clicking.
Any suggestions?
So far I was using this piece of code:
public Vector3? GetBlockCursor(){
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit)) {
hit.point += (hit.normal * -0.5f);
hit.point = new Vector3(Mathf.RoundToInt(hit.point.x), Mathf.RoundToInt(hit.point.y),
Mathf.RoundToInt(hit.point.z));
return hit.point;
}
else return null;
}
Hard to say as it always depends on the game logic what and where optimisations are efficient. Basically one or a few ray cast(s) per frame should not be a big deal for PhysX, so I think there are other culprits. Some Suggestions (maybe you have considererd them already):
Use compound colliders especially box and sphere s. Rigidbody manual
Always attach rigidbodies to moving items
Limit the calls to GetBlockCursor to once per Update or even less by calling it every 2nd or 3rd Update only
If you need more than one raycast consider using Physics.RaycastAll
If the physics engine is to blame for significant frame rate Drops, check whether you can use layers to optimise calculations
Use the profiler (provided you have Pro license)
Related
I'm creating a pool game my problem now is adding a force to ball when it get hits by other ball. Is it ok I'll just adjust the mass of other ball?
There is many ways of doing that, which you can see by searching the Rigidbody Methods in Unity Docs.
One of them, and the one that i use is the AddForce() method because of the ease. Check the official docs for more info: https://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html
I would suggest that you calculate the direction subtracting de position of both balls in the moment of impact.
About the mass, yes, you can change the mass of the other balls, but i recommend that you study a little bit more of Unity's Physics system in order to achieve your goals, maybe your problem isn't in the balls mass.
Unity has better ways to do this. But I'm assuming you want to find the basics of physics. This explains the power angle obtained by subtracting the position of the hit ball from the striking ball. We normalize it to the unit and you can define the amount of force statically or dynamically.
public float force = 10f;
private void OnCollisionEnter(Collision collision)
{
if (!collision.transform.CompareTag("Ball")) return;
var direction = (transform.position - collision.transform.position).normalized;
collision.transform.GetComponent<Rigidbody>().AddForce(direction*force);
}
To dynamize the force, just multiply the speed of the first ball in it. rigidbody.velocity.magnitude does this, but if rigidbody is used, the above steps are performed automatically and very accurately.
According to this article is possible to solve collision problems with fast moving objects in unity.
If you have particle system firing 1 sphere particle (ball throw for example) with collider and gravity modifier. And another moving collider with rigidbody (baseball bat for example), is there any possibility to make these object to NOT going through when they will collide in "high speed"? You cannot set "collision detection" on particle.
For better understainding I am adding two screenshots bellow.
Feel free for asking any question.
Collider(wall) is not moving, particle will bounce
Collider(wall) is moving towards ball with speed of "swing", particle goes through
The problem is because your objects are moving at a speed that they go through the collider. Say your object would move 10 units a frame, and it was 1 unit away from the collider. If the collider was 5 units long, it wouldn’t stop the moving object. This is because the next update will push it too far ahead of the next object.
First, don’t use a particle system for this. Just use a Rigidbody, because it is easier to handle in these situations.
You could solve this by doing a sweep test. A sweep test checks for colliders, where the object is going. You could just stop it from moving at the point it hit.
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
Sweep();
}
void Sweep()
{
RaycastHit hit;
if (Physics.SweepTest(rb.velocity, out hit, rb.velocity.magnitude))
{
rb.velocity = rb.velocity.normalized * hit.distance;
}
}
Make sure this script is on your ‘baseball’, and add force somewhere in this script. Make sure that you are not using particle system, but you are using Rigidbody.
This was untested; let me know if it doesn’t work or you get an error.
Basic Problem: Unity's physics engine produces weird collisions when a player is moving over a flat surface made of more than one Collider. The ghost collisions occur at the joints between Colliders, and express as two behaviors:
This seems to be a problem with physics engines in general, based on this talk by Bennett Foddy:
https://www.youtube.com/watch?v=NwPIoVW65pE&ab_channel=GDC
Game Specifics:
In my case, the player is moving through a procedurally generated wormhole, composed of Segment objects using a MeshCollider. The wormhole twists randomly through 3D space, while the width and height change dynamically. The player can strafe 360 degrees around the inside of the tunnel (direction of gravity is relative to position).
This makes the simpler solutions I've found impractical. Those include:
Using a single object instead of many
Placing redundant Colliders behind the joints
I've managed to flag these erroneous collisions in OnCollisionEnter(). This method on the PlayerController works fine to identify these erroneous collisions, and raise a flag.
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag != "Tunnel"){return;}
// Bit mask for tunnel layer.
int tunnelLayerMask = 1 << 10;
// Get the direction from the nearest Segment's origin to the collision point.
Vector3 toCollision = other.contacts[0].point - nearestSegment.transform.position;
if (Physics.Raycast(nearestSegment.transform.position, toCollision, out RaycastHit hit, 100f, tunnelLayerMask))
{
// Flag the collision if the detected surface normal
// isn't equal to the collision normal.
if (other.contacts[0].normal != hit.normal) { colFidelityFlag = true; }
}
}
But I'm at a complete loss when it comes to gracefully resolving the issue.
Currently, I'm just caching the player's velocity each frame. If a collision is flagged, I overwrite the resulting velocity with the cached velocity from the previous frame. This works for most conditions: the player ghosts imperceptibly into the floor for a frame, and gets past the offending joint.
But under high enough velocities or interactions with obstacles inside the tunnel, it is possible for the player to be ejected through the floor, and into space. I don't want to limit the velocity of the player too much, since the feel of the game partially relies on high velocity and hard collisions.
Does anyone know of a better way to resolve these erroneous collisions?
This is called "ghost vertices", and happens if you have like 2 box colliders (or something equivalent) and they are connected to each other. You could try to join the colliders so that instead of 2 separate connected colliders you have a single one.
Here ghost vertices are explained in more detail: https://www.iforce2d.net/b2dtut/ghost-vertices
I make a 2D-game. In this game, I have a projectile, which I move the fires by myself like this:
void Update()
{
gameObject.transform.position = new Vector3(
gameObject.transform.position.x + baseVelocity * Time.deltaTime,
gameObject.transform.position.y + baseVelocity * Time.deltaTime,
gameObject.transform.position.z);
}
And also, I use void OnTriggerEnter2D(Collider2D other) to know when collisions occurs.
The problem is when game running on a weak phone with 30-FPS it won't detect collision, while same fire in an 60-FPS phone will collide.
I think this is because the fires move 2x more in 30-FPS phones.
One option was use FixedUpdate() method for moving fiers, but it gave me jerky movement, and I used Update() method for moving because it gives me smooth movement(in both of them I used Time.deltaTime).
Can you please suggest me how make more accurate while using smooth movement?
like move object in Update() method but check object position in more(offset) positions than where it is!
I forget to note that I changed the value of Fixed TimeStep to 0.01 for getting more accurate physics.
Thanks in advance.
edit:
I finally end up with using FixedUpdate() for move objects by myself, for so many reasons I can't use Physics engine, I set FixedUpdate TPS(tick per second) around 60 to some how be match with my 60 FPS Update (and still can't figure out why increase FixedUpdate TPS will make object movement jerky!).
And will keep this question open for finding better answer.
You seem to be doing the physics manually. The issue with manually updating the position is that you don't know about the position of your projectile between frames. Even if you were to use FixedUpdate, you could still be in the situation where two FixedUpdate calls cause your projectile to miss an object.
So instead of manually updating the position, you should use a RigidBody2D, with the collision detection set to "Continuous". A continuous collision detection will interpolate object collision in between physics update cycles, so you hit your target even at very high speeds. Also, this solution does not depend on frame rate.
Note that the above will only work if you don't manually update your positions yourself. That is because the physics engine uses velocity to calculate collision in between physics update cycles.
So instead of updating the position yourself (which is bad for physics), use forces instead.
Or alternatively, if you don't want to mess with forces, update the velocity instead:
GetComponent<RigidBody2D>().velocity = baseVelocity;
I'm new to Unity and after watching and reading some tutorials I'm now trying to make a simple 2D platformer kind of game. In the said game both enemies and player can jump to different platforms and traverse it like the old SnowBros game.
The problem I'm facing is related to programming the enemy movement. In my game there would be several types of enemies but generally for now there are two types, one that can jump from the platform and one that only walks upto the length of the platform, wait for 1 second then flip and walk backwards; meaning they don't get off the platform. Now this is an issue I'm having trouble with. I can't seem to find a way to calculate the length of the underlying current platform. I thought of using the collider.bound.min.x and max.x to come around the problem but the issue is I can't seem to find a simple way to reach the current platform's collider without fetching the script and then going through it.
Since, there would be many platforms of many different sizes and each platform is made up of a prefab of other platforms, it just doesn't seem like a workable solution to use the platform script and then traverse through it.
You can use Physics2D.Raycast to "sense" for collisions on a Ray. Imagine the enemy putting their toe one step forward, feeling if there is still solid ground, and then deciding whether to stop.
void Update()
{
Vector2 newPosition = transform.position + velocity * Time.deltaTime;
Vector2 downwardDirection = Vector2.down; // you may have to replace this with your downward direction if you have a different one
RaycastHit2D hit = Physics2D.Raycast(newPosition, downwardDirection);
if (hit.collider != null)
{
// solid ground detected
transform.position = newPosition;
}
else
{
// no ground detected. Do something else...
}
}
You can and should define the Layers your Raycast is supposed to hit, so it can ignore the enemy itself and avoid self-collision.
Well, I hope that you have prefabs of platform and in those prefabs put colliders at start and at end. You can specify layers and tags so that only enemy can detect those colliders and then you can detect tags on collision from enemy script and make your operation.
First create layers by opening Edit -> Project Settings -> Tags and Layers. Create two Layers
Create layers for enemy and enemyColliders and Goto Edit -> Project Settings -> Physics2D. Set collision matrix as EnemyCollider will collider with enemy only.
And your prefab will be looks like,
I hope this would help you.