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
Related
I'm creating a top down 2D game, where the player has to break down trees. I made it so the player casts a ray toward the mouse, and when the ray hits a tree, it should lower the tree's health. I don't get any errors when I run the game or click, but it seems like the tree isn't detecting the hits.
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
RaycastHit2D hit = Physics2D.Raycast(playerRb.transform.position, mousePosition - playerRb.transform.position, 2.0f);
if (hit.collider != null)
{
if (hit.collider == GameObject.FindWithTag("Tree"))
{
hit.collider.GetComponent<TreeScript>().treeHealth--;
}
}
}
}
Still pretty new to coding and I'm teaching myself, so please make your answer easy to understand to help me learn.
Input.mousePosition is equal to the pixel your mouse is on. This is very different than the location your mouse is pointing at in the scene. To explain further, Input.mousePosition is where the mouse is. Think about it. If the camera was facing up, the mouse positon would be the same, but where they are clicking is different.
Instead of using Input.mousePosition, You should pass this into a function called Ray Camera.ScreenPointToRay();
You just input the mouse position and then use this new ray to do the raycast.
ANOTHER EXTREMELY IMPORTANT THING 1: Do not use Camera.main in Update(), as it uses a GetComponet call, which can cause perormance decreases. Store a reference of it in your script and use that.
Extremely important thing 2: I notice you are using GetComponent to change the tree's health. This is fine, but do not use GetComponent if you don't have to.
Like this:
Camera cam;
void Start()
{
cam = Camera.main; //it is fine to use this in start,
//because it is only being called once.
}
void Update()
{
...
if (Input.GetMouseButtonDown(0))
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray);
...
}
}
You need to convert your mouse position from screen point to world point with Z value same as the other 2D objects.
Vector3 Worldpos=Camera.main.ScreenToWorldPoint(mousePos);
Also use a Debug.DrawRay to check the Raycast
Debug.DrawRay(ray.origin, ray.direction*10000f,Color.red);
Source
The player goes on top of some objects when he walks toward them, how can I prevent that from happening? Here is an example image of that:
I did not jump to be on the couch but yet it still goes on top of it when I walk to it. Here is my player information:
I don't want to change the player's movement, but I don't want it to go on top of objects when I'm walking.
Make the Step Offset in character controller 0. More info on it here
The character controller Step Offset solves this problem by increasing it value, but you may find that after adding the value, the character controller does not generate any gravitational force on its own.
To solve the problem of gravity, it is enough to first get the component.
private CharacterController controller;
public void Start()
{
controller = GetComponent<CharacterController>();
}
And then apply gravity to the object with the following instructions, you have already obtained the moveInput axis with the Input.GetAxis method.
private Vector3 velocity;
private void Update()
{
var moveInput = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
// === AFTER CALCULATING MOVE INPUT
controller.Move(moveInput*Time.deltaTime);
velocity += Physics.gravity * Time.deltaTime;
controller.Move(velocity); // Apply Gravity
if (controller.isGrounded) velocity = Vector3.zero;
}
I'm trying to do a little game on mobile using Unity and I've got a problem with the rotation of a maze.
To add context :
When your moving your finger on the screen, the maze is rotating on himself. There is a ball in it and you need to make it go on a cell to win the level.
When the maze is rotating too fast, the ball falls down and go through the ground and I don't know how to fix it.
I tried to play with the gravity, colliders...
This is the same when the ball is jumping (when the maze is going up and down quickly).
For the moment I just reset the ball position when you're falling.
{
ball.transform.position = new Vector3(0, 2, 0);
maze.transform.position = Vector3.zero;
maze.transform.rotation = Quaternion.identity;
}
Do you guys have some ideas ? Thanks
I had a similar problem in a tilt maze mini-game I worked on. Ideally implementing jkimishere's solution will work but I assume the maze is moving too fast for the collisions to register properly. You'll need to smooth the maze's rotation with a Lerp. In our case we had pressure plates with a tilt value, so it doesn't directly translate to your mobile use but perhaps give you a nudge in the right direction. We used:
public GameObject maze;
private float _lerpTime;
private bool _isRotating;
private Quaternion _startingRot, _desiredRot;
private void Awake()
{
_startingRot = maze.transform.localRotation;
}
private void Update()
{
//Don't want to move the maze if we don't ask for it
if(!_isRotating)
return;
//Lerp the maze's rotation
_lerpTime = Mathf.Clamp(_lerpTime + Time.deltaTime * 0.5f, 0f, 1f);
maze.transform.localRotation = Quaternion.Lerp(_startingRot, _desiredRot, _lerpTime);
//Once the maze gets where it needs to be, stop moving it
if(affectedObject.transform.localRotation.Equals(_desiredRot)
_isRotating = false;
}
private void ActivateTilt()
{
//Set the new starting point of the rotation.
_startingRot = maze.transform.localRotation;
//However you want to calculate the desired rotation here
//Reset our lerp and start rotating again
_lerpTime = 0f;
_isRotating = true;
}
This will ease the rotation of your maze over time. So that the ball can adapt to the new collider positions.
In the rigidbody(for the ball), make the collision detection to continuous, and in the rigidbody for the maze(if you have one) set the collision detection to continuous dynamic. Hope this helps!
I think that is unavoidable if you allow the player to move the platform freely. I suggest you restrain the speed at wich the player can tilt the platform. This way, the ball will have more frames to adapt to the new slope
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.
I make a game in Unity3D (+2D ToolKit) for iOS.
It is 2d runner/platformer with side view. So on screen is moving background and obstacles. And we control up/down the main character to overcome obstacles.
The problem is that the moving picture has a visual lag. In other words, it moves jerkily or creates the tail area of the previous frames on iOS-devices. In XCode the number of frames is 30 and over.
I have objects 'plain' with the following script:
public class Move: Monobehavior
{
private float x;
public float Speed = 128.0f;
void Start()
{
x = transform.position.x;
}
void Update()
{
x += Time.DeltaTime *Speed;
Vector3 N = transform.position;
N.x = mathf.FloorInt(x);
transform.position = N;
}
}
The question is how to make the move of background smoother, without jerks and flicker on screen while playing? Maybe the problem is in framerate parameter.
Can anybody help me to find a solution?
I'd say it's the use of the FloorInt function which will move the background only in steps of 1 which is rather not smooth. It should get better when you comment that line out. Do you have any special reason why you are doing the FloorInt there?
The use of floor will definitely hurt your performance. Not only is it one more thing to calculate, but it is actually removing fidelity by removing decimals. This will definalty make the movement look 'jerky'. Also, update is not always called on the same time inteval, depending on what else is happening during that frame, so using Time.delaTime is highly recommended. Another thing, you do not need to set variables for x and Vector3 N, when you can update the transoms position like the code below. And if you have to, you sill only need to create one variable to update, and set your position to it. The code below just updates the players x position at a given rate, based on the amount of time that has passes since the last update. There should be no 'jerky' movement. (Unless you have a serious framerate drop);
public class Move: Monobehavior
{
public float Speed = 128.0f;
void Update()
{
transform.position =
(transform.position.x + (speed*Time.DeltaTime),
transform.position.y,
transform.position.z);
}
}