I'm trying to make a very simple race game with spheres, however, I face many problems.
First of all, I'm trying to make a very simple AI system for opponents.
The problem I have here is that I want opponents to detect obstacles and avoid them using Raycast but only a certain type of obstacle , a simple cube is detected.
I've created a simple sphere as opponent and wrote a script so it can move and detect obstacles
Here is update function:
void FixedUpdate()
{
transform.Translate(Vector3.forward * Time.deltaTime * speed);
if (Physics.Raycast(transform.position, transform.forward, 100.0f))
print("There is something in front of the object!");
}
The message is printed only when there is a cube forward and it does not detect any other obstacles. What can be so wrong? Also, is there any idea how to move left or right when opponent raycast an obstacle?
obstacle that is detected
hierarchy
cube01 that is child of obstacle2 that is not detected
Only collider components are detected using raycast, make sure you have an appropriate collider (size of the collider does not necesarily match size of the mesh that gets rendered). Normally also layers on which objects are are important but syntax you are using is not checking for layer mask anyway
Unity Physics Best Practices (As in https://unity3d.com/pt/learn/tutorials/topics/physics/physics-best-practices) recommends that we don't use Raycasts in FixedUpdate, as it is heavy to process and may not always work.
There is also some tips about Matrix Layers that will help you improve performance and avoid such bugs.
Good luck!
You can use Debug.DrawLine() or Debug.DrawLine() to debug the line and see if it cross the obstacles or not.
Here is the documentation for them
DrawRay
DrawLine
For moving right and left I think you can add something like this
void FixedUpdate()
{
Vector3 dir = Vector3.forward * Time.deltaTime * speed;
if (Physics.Raycast(transform.position, transform.forward, 100.0f))
{
print("There is something in front of the object!");
dir += Vector3.Right * horizontalSpeed * Time.deltaTime;
}
}
You might also consider ray casting two rays to detect the direction to lean to if it will be the left or the right.
Related
I'm making a game with ball physics and an FPS camera. I have this bug where the player's movement is increased when I jump.
Example:
Running along the Z axis on a plane:
Suddenly jumping:
My code:
void Update()
{
// Input
moveFwrd = Input.GetAxis("Horizontal");
moveSide = Input.GetAxis("Vertical");
// Player Jump
if (Input.GetKeyDown(jumpKey) && Physics.Raycast(
transform.position,
Vector3.down,
DistanceToTheGround + 0.1f))
{
rb.AddForce(Vector3.up * jumpSpeed, ForceMode.Impulse);
}
// Braking
if (Input.GetKey(brakeKey))
{
rb.angularDrag = brakeForce;
}
else
{
rb.angularDrag = 0.01f;
}
}
void FixedUpdate()
{
// Player Movement
Vector3 movement = cam.transform.right * moveFwrd +
cam.transform.forward * moveSide;
movement = movement.normalized;
rb.AddForce(movement * speed);
}
}
Most likely, the answer is friction. Depending on the physics material used for the player's Rigidbody collider and the physics material used for the floor, the Rigidbody will behave differently:
Static friction describes the friction when both objects are not moving (i.e. before the player starts moving), dynamic/kinetic/sliding friction describes the friction when the player is moving along/on the floor. It will also determine if the rigidbody slides or rolls on the floor, you could experiment with different physics materials to see the effects.
The reason why you see faster movement in mid-air in forward direction after pressing the jump button is that the Rigidbody is no longer affected by "contact friction" between solid materials while airborne, hence it will move faster with the same applied forward force. - However, there's still friction involved: air resistance; which can be controlled by Rigidbody.drag and Rigidbody.angularDrag.
If you want to get rid of this simulated bahavior, you could do various things: Use different physics materials, Rigidbody.drag, Rigidbody.mass values, or try to experiment with a different ForceMode like ForceMode.VelocityChange arguments in Rigidbody.AddForce(). Or do not use the physics engine and rigidbodies at all for motion (colliders and raycasts can still be used).
Side notes:
FixedUpdate() is called at regular intervals, and is the recommended place to do physics related scripting. It is actually called at the same frequency as the internal physics engine is updated, like documented here. The duration between every call is constant: Time.fixedDeltaTime.
Update() on the other hand is called at a variable rate, and has a variable duration Time.deltaTime, which results in different frame rates (FPS). This method can be used for the visual rendering or state processing of the game, and should not be used to update physics related stuff if possible.
In the script above, you use Rigidbody.AddForce() in both methods Update() and FixedUpdate(). In the latter with the default argument ForceMode.Force and in the other case with ForceMode.Impulse. In both cases you do not take the frame time into account, which is fine in the case of an impulse, applied once (after a key has been pressed). In the other case the direction vector gets normalized, so it isn't really a problem either, because the method is called at a fixed rate... but it could be written a bit more clearly.
I'm trying to add knockback to my player, but he won't move. I know the function is correctly called and the if a statement is functional, as he will still take damage, but the player won't move at all.
Weirdly, if I put the addForce somewhere else (Like in the update() method), it will work, but not in this scenario.
Some help would be greatly appreciated
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Enemy")
{
TakeDamage(-20, 21);
theRB.AddForce((collision.transform.position - transform.position).normalized * 100, ForceMode2D.Impulse);
}
}
Here is what the Rigidbody looks like if it helps:
Hey I think the issue here is with your use of
(collision.transform.position - transform.position)
I would instead calculate the direction using the contact point of the collision and save that in a Vector. Then normalize that vector and multiply by -1 to launch the player in the opposite direction. Here's some sample code:
Vector2 dir = collision.contacts[0].point - transform.position;
// Flip the vector and normalize
dir = -dir.normalized;
// Apply Force
theRB.AddForce(dir * 100, ForceMode2D.Impulse);
Ok, so I figured out the answer. The problem lies in the way I managed movement in my Movement() function. To create movement, I used this line :
theRB.velocity = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")).normalized * playerSpeed;
Apparently, velocity doesn't really work well with AddForce, for reasons I am not capable of explaining, as my understanding of Unity is still limited. I'll need to figure out a better way to manage movement, and I'll edit this post once I figure it out in case someone made the same mistake as me.
EDIT : So, turns out I've been looking for weeks, and didn't find anything. My final solution was to use a LERP instead of Addforce(), and just forget about physics alltogether.
I need the Player to follow the moving Target and stop exactly when it reaches the Target.
The Player has to reach the Target almost instantaneously in every frame, so i need a high speed way to do it.
I couldn't use Transform.translate because there's a lot of physics implementations in my game and using Transform.translate or movetowards made the physics buggy.
Is there any physics based way to follow the target? velocity, AddForce, anything? For a 2D game.
Any leads would be greatly appreciated! Thank You!
If you have a Rigidbody2D you want to follow another object, the Rigidbody2D.MovePosition is the proper way to move it.
Do the following:
1.Disable gravity by setting the "Gravity Scale" to 0.
2.Change the BodyType to Kinematic.
3.You can now move the Rigidbody object to follow another GameObject with the Rigidbody2D.MovePosition function. See code below. This should be done in the FixedUpdate function and with Time.fixedDeltaTime instead of Time.deltatime.
Finally, if you still get jerky movement, change Interpolate option from None to Interpolate or Extrapolate. I would also suggest reducing the speed variable below.
//Object to follow
public Transform target;
//Rigidbody to move
public Rigidbody2D rb2d;
public float speed = 7.0f;
//Distance to start moving
public float minDistance = 0.09f;
void FixedUpdate()
{
//Find direction
Vector3 dir = (target.transform.position - rb2d.transform.position).normalized;
//Check if we need to follow object then do so
if (Vector3.Distance(target.transform.position, rb2d.transform.position) > minDistance)
{
rb2d.MovePosition(rb2d.transform.position + dir * speed * Time.fixedDeltaTime);
}
}
Changing the velocity directly is always a bad practice and should be avoided. Instead always work with AddForce.
I would calculate the distance between the target and the body and add a force based on that distance.
var dif = target.transform.pos - body.transform.pos;
bodyRigid.AddForce(dif * multiplier * Time.deltatime);
The only problem that comes with that solution might be the fact that the body 'shakes' around the target once its to close.
You could avoid this by checking if the body is close to target and then freezing it.
var dif = target.transform.pos - body.transform.pos;
if(dif.magnitude > 1) {
bodyRigid.AddForce(dif * multiplier * Time.deltatime);
} else {
bodyRigid.velocity = Vector2.zero;
}
Although I said that setting the velocity directly is a bad habit, using it to just freeze the body should be fine. I have no idea whether that might break your other physics that you use in your game, duo the fact that that just strait up freezes your object.
You can also change the distance (1 in the if statement) that it needs in order to freeze, just play around with it a bit and find a value that fits the game
Is it possible to have a script in a GameEngine script and have it check for collisions between two remote objects that are not connected to the GameEngine script, via Update() or OnTriggerStay()/OnColliderStay()?
My plan for this script is to detect situations such as putting out room that is on fire. My original plan was to have a collider around this room checking for fire particles, and if there are no more particles, the fire is out. If you have a better suggestion, please let me know.
I assume you are referring with linear motion. If so Ray Casting is the solution. Ray Casting is forming a line or vector from a specific point to another point in a 3D plane. The purpose of the ray (vector) is to determine if it intersects with any colliders or other game objects.
It can be simply used like,
void Update() {
Vector3 fwd = transform.TransformDirection(Vector3.forward);
// parameters are origin, direction and length of the ray.
if (Physics.Raycast(transform.position, fwd, 10)){
print("There is something in front of the object!");
}
}
You can find more references and tutorials on the internet. Try Unity official tutorial on Raycasting
Basically if there's collision infront of the player it shouldn't go forward anymore, but the player goes forward no matter what.
if (Input.GetAxis("Vertical") > 0 &&
!(Physics.Raycast(PlayerObject.transform.position,
Vector3.forward,
PlayerObject.collider.collider.bounds.size.z * 1.05f)))
{
PlayerObject.transform.Translate(Vector3.forward * MoveSpeed * Time.deltaTime);
}
The problem here is that you're performing a Raycast using physics, so anything not enabled for physics won't show up. You're also using Vector3.Forward instead of PlayerObject.transform.forward, so you're getting the global forward (0,0,1) instead of the player's actual vector. Furthermore, but using the player's position, it won't notice, say, a low or high-hanging wall.
Note there is also the built-in CharacterController component to handle this sort of behaviour, as well as intelligently handling Ramps and Steps.