I have a bullet that is fired with a Impuls on the rigidbody.
Then, each frame i do a raycast forward with the speed of the bullet, which sometimes does, and sometimes doesn't find an enemy.
When i skip through the game frame by frame, i can clearly see my debug-raycast inside the (boc)collider of the enemy, but it still won't find it.
Im pretty sure it doesn't just pass the enemy, in the editor i can clearly see the green raycast inside the collider.
Any suggestions? I also tried with a linecast, which i found on here, but that gives the same result. (the line is on the exact same position and also draws the debug-line inside the collider. The layers are also correct, as i said, sometimes it will, and sometimes it wont find the target...
void Update()
{
//get direction and distance
Vector3 direction = _rigidBody.velocity.normalized;
float distance = (_rigidBody.velocity.magnitude * Time.deltaTime);
//raycast for targets
RaycastHit raycast;
if (Physics.Raycast(transform.position, direction, out raycast, distance, HitLayerMask))
{
Debug.DrawRay(transform.position, direction, Color.red);
}
else
{
Debug.DrawRay(transform.position, direction, Color.green);
}
}
You can see the tiny debug ray, aswell als the collider from side-view. It also it almost right in the middle from the front-view.
I'm thinking it might be because the start and end of the raycast are inside the boxcollider?
the rigidbody is moved by
//Speed = 500 in this case, but lowering doesn't change anything
rigidBody.AddRelativeForce(Vector3.forward * Speed, ForceMode.Impulse);
UPDATE:
After some more debugging and testing, i found that it happens because you won't get a hit from inside a collider.
Here's what happend:
bullet is outside target collider, but raycast is to short to find it.
bullet moves forward, enemy also moves forward (closing in on eachother). Bullet is now inside the collider
raycast won't find enemy because thats just how raycast work
What i could do, is make the distance of the raycast further, but also not so far away that it would result in weird hits that would graphically look weird.
i thought that using
float distance = (_rigidBody.velocity.magnitude * Time.deltaTime);
would be enough (since thats the distance the bullet traveled since last frame, but because the enemy also moves, the above can happen. Well, it would be enough if the enemy wouldn't move :/
One possible solution is to go to Project Settings -> Script execution order and put the bullet's MonoBehavior after Default Time. That way everything else has already finished moving before you do the ray cast. A hacky solution but should work.
Related
I'm making a very simple 3D game in Unity where I have this space shuttle I can move around in space around asteroids and I can shoot when pressing/holding the mouse button. I'm doing this by Instantiating a sphere at the "Emitter" transform.position and then just applying a forward Force to that bullet object.
It all works fine, but the one thing I don't like and also don't know how to fix is how the bullets keep their position when shooting and moving the mouse left-right, instead of keeping a perfectly straight line at all times.
This is how it looks when I'm shooting and moving my camera at the same time:
Screenshot while shooting
Here's a gif for better visualization.
Right now it looks like I'm pissing lasers, which is never good. I tried making the bullet speed a lot faster, but then the bullets become harder and harder to see and it doesn't look as good.
This is the code by which I'm shooting the bullets:
private void Fire()
{
GameObject bullet = Instantiate(laserPrefab);
GameObject bullet2 = Instantiate(laserPrefab);
Physics.IgnoreCollision(bullet.GetComponent<Collider>(), shuttleCollider.GetComponent<Collider>());
Physics.IgnoreCollision(bullet2.GetComponent<Collider>(), shuttleCollider.GetComponent<Collider>());
bullet.transform.position = laserEmitter.position;
bullet2.transform.position = laserEmitter2.position;
/*Vector3 rotation = bullet.transform.rotation.eulerAngles;
Vector3 rotation2 = bullet2.transform.rotation.eulerAngles;
bullet.transform.rotation = Quaternion.Euler(rotation.x, transform.eulerAngles.y, rotation.z);
bullet2.transform.rotation = Quaternion.Euler(rotation2.x, transform.eulerAngles.y, rotation2.z);*/
bullet.GetComponent<Rigidbody>().AddForce(laserEmitter.forward * laserSpeed, ForceMode.Impulse);
bullet2.GetComponent<Rigidbody>().AddForce(laserEmitter.forward * laserSpeed, ForceMode.Impulse);
StartCoroutine(DestroyBulletAfterTime(bullet, bulletDeathTime, bullet2));
}
Don't mind the commented lines, I was just messing around trying to see if I can get it to work. The shooting behaves the same with or without those commented lines.
Of course, a projectile based system will behave and look like a projectile system.
If you want laser behavior use a LineRenderer. Raycast where your laser line should end (either laser max distance or the point of hitting an object in range).
If you don't like the "static" looks of it, change the LineRenderer Material to something that changes over time (search for shaders/ LineRenderer effects).
I am using Unity3D to make a replica of the game "Rolling Sky" which you can find on the Google/Apple app store. I was able to make a simple floor which the ball will move on and I was also able to make the ball (Player) move left and right. After moving the ball a couple of times back and fourth, it starts float in the air and eventually what it seems like is change it's axis.
I came up with a couple of alternatives that I think would work, but I had trouble coming up with the code to make it function properly.
1) Do not rotate the ball at all. Just have it smoothly move back and forth.
^^ Probably the best solution I could come up with.
or
2) Use CharacterController to have complete control of how the ball reacts to different events, scenarios, etc.
^^ This would probably be the most necessary and hardest of I had to guess.
or
3) Move the ball an x amount of pixels everytime I move left or right.
^^ I would assume this would create a lot more glitches than what I have right now.
public class PlayerController : MonoBehaviour
{
public float speed;
void Update()
{
transform.Translate(Vector3.right * speed * Input.GetAxis("Horizontal") * Time.deltaTime);
}
}
EDIT:
I was able to provide my own answer, but if anyone has any alternatives, feel free to post your solution!
To prevent the player ball from floating into the sky, no other code was needed. Simply go to the Rigidbody component on the Player and expand the "Constraints" menu. Next, check the axis boxes that you would like to freeze the rotation on to prevent the ball from spinning like so in the picture.
Click the link to see the picture
here.
In this case, I did not freeze the ball on the x axis because I eventually want to animate it to spin as if it is rolling forward.
You're using transform.Translate
and
You're using Vector3.right
transform.Translate will directly move the ball without thought of rotation. You will need to take the object's rotation into consideration when translating. You can also use the physics engine and use Rigidbody.MovePosition to force position or Rigidbody.AddForce to utilize physics interactions.
Vector3.right will use the constant value equivalent to Vector3(1, 0, 0). This will not be in regard to the object's rotation or facing.
I suggest looking through Unity's tutorial for a ball-roller and see how they complete the task of user input with a user controlled ball.
I'm seeing a curious problem with Raycast. The basic idea of the code below is: when a player runs into a wall, but is pressing the F key, they perform a wall flip. I raycast to check that they are indeed hitting a wall and not some other object, and close enough to the wall to do the flip.
So in the first level, everything works great. The next level, the flip doesn't work whatsoever. Initially I thought the walls in the second level must not have colliders or are set to isTrigger or something, triple checked everything. Then I added a ton of Debug statements to the relevant code to see what's going on, and to my surprise when on the second level, the Raycast never hits anything! Even when the Debug ray is clearly going through a wall that's setup the same way as walls in level 1. Here's an image of the magenta debug ray clearly going through a wall in level 2:
In the lower left is the inspector values of the wall shown in the screencap, which doesn't return a raycast hit. To the lower right is a wall from level 1 that does indeed work properly. You can see they are setup the same exact way (save their X and Z scales being flipped because they are different orientations, but this same bug occurs with walls of the same orientation too).
Basically, the only difference that I can discern between the walls of level 1 and level 2 is that the levels themselves are at different Y positions, which should not in any way affect raycasting. I'm pretty stumped why it works in level 1 but not in 2 or beyond.
if (Input.GetKey(KeyCode.F)){
// Raycast to see if we hit a wall
RaycastHit hit;
Vector3 fwd = transform.TransformDirection(Vector3.forward);
Debug.DrawRay(this.transform.position, fwd, Color.magenta, 2.0f);
if (Physics.Raycast(this.transform.position, fwd, out hit, 2.0f)){
if (hit.collider.gameObject.name.Contains("Wall")){
Debug.LogWarning("WALLFLIP: Attempted wallFlip, raycast did indeed hit a wall!");
// Stuff happens here such as flip animation, not relevant
}
}else{
Debug.LogWarning("WALLFLIP: Raycast hit nothing");
}
}
UPDATE 1: Saw a SO thread that suggested using RaycastAll() instead of Raycast(). When implemented, RaycastAll() returns a list of length 1 in level 1, but returns an empty list in level 2 which confirms the ray doesn't hit anything even though the Debug ray clearly does.
UPDATE 2: Saw this SO thread about doing Raycast stuff in FixedUpdate() instead of Update(). So I moved the WallFlip code out of Update to a FixedUpdate, saw no change. When in FixedUpdate level 1 work, but in level 2 no raycast hits are returned.
UPDATE 3: Thanks to user Sharundaar, I now know that the floor of level 2 was interfering with the Raycast (see his Answer below). Still puzzled with this didn't occur in level 1, or why the floor doesn't return a hit on Raycast(). Below is the inspector for the floor gameObject of level 2 (the floor of level 1 is identical except for the xyz position and X Z scale). Pretty weird issue I've never encountered before, but hey Raycasting from the player's position offset by 0.5f Y works. *shrug*
So from your source I managed to make it work on the other level. The thing is that your transform.position is sligthly underground so the raycast hit the floor and not the wall (I don't quite know why it works specifically on the first level).
What I did is as follow :
Vector3 raycastOffset = new Vector3(0, 0.5f, 0);
if (Physics.Raycast(this.transform.position + raycastOffset, fwd, out hit, 2.0f))
This is of course a quick fix, you'll need to investigate further.
I also changed
Vector3 fwd = transform.TransformDirection(Vector3.forward);
by
Vector3 fwd = transform.forward;
which should be cleaner.
After a little bit of investigation your problem effectively lies into the floor entity, I think it is essentially the MeshCollider that can't compute the collision well, try replacing it with a correctly ajusted box collider and see where it leads you.
I'm still learning the basics so please be a bit gentle. I'm working on the movement script of a roll-a-ball game. I've got the rolling around working perfectly fine, but now I'm trying to add the ability to jump.
In my case the "player" is a basic Sphere. What I'm trying to accomplish is that when space is pressed, the ball leaps into the air in the direction it's currently rolling.
This is what I've tried myself, but gave very strange results:
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
if (Input.GetKey("space"))
{
transform.position += transform.up * 20 * Time.deltaTime;
}
rb.AddForce(movement * speed); //rb = RigidBody
This resulted in the ball sometimes jumping in the wrong direction or simply just speeding into some direction without jumping at all. So obviously I'm getting closer, but not quite there yet. Could anyone explain what I'm doing wrong and how to fix it?
Not looking for a quick fix! Don't learn anything from that.
Have a try at this :)
if (Input.GetKey("space"))
{
rigidbody.AddForce(new Vector3(0, yourJumpForce, 0), ForceMode.Impulse);
}
You also might want to fiddle with the gravity settings in the rigidbody to get your desired jump as well!
EDIT Explanation:
I think it may be because you are applying you movement to your Rigidbody, but your jumping method to the Transform itself.
A Rigidbody is a physics component (see docs.unity3d.com/ScriptReference/Rigidbody.html), where a Transform is to do with position, rotation and scale.
A jump could be considered a physics problem - therefore altering the jump is to do with physics (Rigidbody), not the position (Transform). I tend to do all movement code with the Transform, as you are moving positions, and do all physics based work with the Rigidbody, as you are applying physics to the GameObject! :)
To add to the accepted answer: The jumping into random directions issue could have been caused by the transform.up. That vector is the up direction relative to your current transform. That's usually the same as the actual up direction, except if you rotate the game object. And as the object happens to be a rolling ball in this case, it's very likely that it's rotated.
You can fix it by using the global up direction instead - either by doing it like in the accepted answer or by using Vector3.up.
I have an object with circle collider 2d, and an object with box collider 2d. how do i detect when the circlecollider hits the top edge of the box collider. assuming none objects have any rotation. i want to do this in code C#
OnCollisionEnter triggers when something collides with the object containing this script, used like this:
void OnCollisionEnter(Collider collider)
{
foreach(CollisionPoint contact in collider.contacts)
{
//Do something
}
}
The above will give you a list of contact points for the collision, via which you should be able to determine where the collision occurred. An alternative method if you only need to detect collisions at a specified location, you could place an invisible child object within your cube, with a collider at the spot you want.
Edit:
Since you mentioned raycasts, there's 2 ways I can think of that these could be implemented. The first is to fire it upwards from the cube, but this has the issue of the ray firing from just 1 point, meaning some collisions might be missed (depending on the size of the cube & sphere).
The second method that pops to mind is to fire the ray parallel to the cube. This might sound a bit unorthodox, and I haven't tested it, but in theory it should work. Stick it in your cube:
void Update
{
Vector3 start = this.transform.position;
Vector3 end= this.transform.position;
//This attempts to place the start & end point just above the cube
//This of course assumes the cube isn't rolling around. If that's the case
//then these calculations get quite a bit more complicated
//Additionally the 0.01 might need adjusting if it's too high up off the cube
start.y += this.renderer.bounds.y/2 + 0.01f;
end.y += this.renderer.bounds.y/2 + 0.01f;
start.x -= this.renderer.bounds.x/2;
end.x += this.renderer.bounds.x/2;
Ray ray = new Ray(start, end);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, start.x-end.x) && hit.name == "mySphere")
{
//Theoretically, the sphere hit the top of the box!
}
}