I am creating a simple mini-game in unity as a separate scene. The problem is that the gravity works completely fine on my rigidbody when I start from the mini-game scene, but when I move to the mini-game scene from another the gravity stops working and my player can only go up.
Here is what I've tried:
My Project's Physics 2D settings are fine (Gravity: X = 0, Y = -9.81)
My Project's Time setting is fine (Time Scale: 1)
My Player character has a Rigidbody2D, which is Dynamic, Simulated (true), Gravity Scale = 5, ONLY rotation is frozen, and it's set to never sleep.
The Player also has box and circle colliders (both 2D).
All player inputs are read in Update(), and all physics are done in FixedUpdate() (I am using velocity to move the player).
I do have a GameController object which is carried between the scenes, but none of it's scripts should affect the scene (I can provide the OnLevelWasLoaded() parts of the scripts or any other parts).
Here is the code in my player controller:
void Start()
{
rb = GetComponent<Rigidbody2D>();
box = GetComponent<BoxCollider2D>();
_renderer = GetComponent<SpriteRenderer>();
velVer = 30f;
velHor = 10f;
}
void Update()
{
moveHor = Input.GetAxisRaw("Horizontal");
moveVer = Input.GetAxis("Vertical");
buttonUp = Input.GetButtonUp("Vertical");
_moveVector = new Vector2(moveHor, moveVer);
}
void FixedUpdate()
{
if (IsGrounded() && buttonUp)
rb.velocity = new Vector2(velHor * _moveVector.x, velVer * _moveVector.y);
if (_moveVector.x != 0 && _moveVector.y == 0)
rb.velocity = new Vector2(velHor * _moveVector.x, rb.velocity.y);
}
private bool IsGrounded()
{
RaycastHit2D raycast = Physics2D.BoxCast(box.bounds.center, box.bounds.size, 0f, Vector2.down, 0.1f, layer);
return raycast.collider != null;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag.Equals("Heart"))
{
other.gameObject.SetActive(false);
}
}
I have commented out any references to the GameController object from all scripts in the mini-game scene, so the GameController should not have any impact on my mini-game character controller.
I am desperate, I have searched online for more than 3 hours and haven't found anything that could help me (if I did actually miss a solution my apologies).
I can provide any further information you may require.
EDIT:
Here are Gifs of character behaviour when I start the game from the mini-game scene, and when I move to the mini-game scene from another.
Working (started mini-game directly):
Not Working (moved to mini-game from another scene):
Check for three things:
1) Are you sure that you didn't set timeScale to 0 in any other script?
2) Does your player have the same scale on both levels?
3) Why do you use Physics2D.BoxCast in IsGrounded function? I usually use Raycast2D for the ground checking process.
Okay I have found the issue! I thought the problem would lie inside the scripts that are inside of my mini-game scene, or in the scripts carried into the scene by my GameController, but that was not the case. The problem laid in a script attached to an object located in a scene from which I moved to the mini-game.
Here if the troublesome line:
Physics2D.gravity = Vector2.zero;
Because my game is 2D with Axonometric projection (3/4) I set the gravity to Vector2.zero very early into the development. I have replaced that with simply setting the gravity scale to 0.
rb.gravityScale = 0;
Even though the script with the troublesome line doesn't move to my mini-game scene it modified the gravity setting of the entire project.
VERY silly mistake, which I probably would find, but after many hours of constant development my brain wasn't functioning too well, and I decided to post this question.
Thanks for the help anyway guys!
Related
I'm working on my first Unity project. It's a little game where you have to move your planet to dodge incoming asteroids.
I have set up a simple collision detection system but it's not working at this moment and I'm not entirely sure why.
These are the lines of codes, they're on the movement script for the planet, attached to my gameobject planet:
private void OnTriggeredEnter2D(Collider2D collision)
{
if (collision.tag == "Asteroid")
{
restartPanel.SetActive(true);
}
}
The asteroids are a prefab spawned dynamically in this manner in a script attached to an invisible gameobject:
void Update()
{
float interval = Time.deltaTime;
random = Random.Range(0f, 1f);
Debug.Log(interval);
Debug.Log(random);
if (interval > random) {
GameObject newAsteroid = Instantiate(asteroidPrefab, GetRandomPosition(), Quaternion.identity);
newAsteroid.GetComponent<Gravity>().planet = planet;
}
}
Nothing happens when planet collides with any asteroid, or when asteroids collide with each other, if that matters, and I'm not entirely sure why.
Thank you for your help.
The method name was not correct, I messed up, it should have been OnTriggerEnter2D, not OnTriggeredEnter2D.
Yikes.
Im new to unity and this is my first game but my player is jumping infinitely and I've watched a lot of tutorials and still dont know how to fix it.
heres my code
public float moveSpeed = 5f;
void Update()
{
Jump();
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, 0f);
transform.position += movement * Time.deltaTime * moveSpeed;
Vector3 characterScale = transform.localScale;
if (Input.GetAxis("Horizontal") < 0)
{
characterScale.x = 1;
}
if (Input.GetAxis("Horizontal") > 0)
{
characterScale.x = -1;
}
transform.localScale = characterScale;
}
void Jump()
{
if (Input.GetButtonDown("Jump"))
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 15f), ForceMode2D.Impulse);
}
}
}
There are a couple of things you have there. First you have Input.GetButtonDown("Jump") This means while the player is holding the button down it will execute that script inside the if statement, which applies a force. So this will run every frame and every frame while the player is holding down the button it will apply that force. You can try to do Input.GetButtonUp("Jump") which will be true when the player lets go of the button, then it will apply that force. Now you can keep the GetButtonDown its no problem if thats the feel you are going foor.
But the real problem and the second this is, you need to check if the player is touching the ground or not. If he is touching the ground then you can apply that force. If he is not touching the ground then dont apply the force.
There are couple of ways to go about this, the easiest way is to create a new Layer and call it Ground or something.
You click that drop down and click on Add layer .. then you can add a layer, then go back to ground gameobject and assign that layer to that. Now am assuming that the ground has a collider on it so the player doesnt go through it.
After that you need a reference to the player collider. In the Start() method you can add this:
private Collider2D myCollider;
void Start()
{
// This will get a reference to the collider 2d on the player
myCollider = GetComponent<Collider2D>();
}
void Update()
{
.
.
.
// This means the player is touching the ground layer.
if (myCollider.IsTouchingLayers(LayerMask.GetMask("Ground")))
{
if (Input.GetButtonDown("Jump"))
{
gameObject.GetComponent<Rigidbody2D>().AddForce(new Vector2(0f, 15f), ForceMode2D.Impulse);
}
}
}
So what this script does, it gets a reference to the player collider, and then checks if that player collider is touching the layer called ground. The ground needs to have a collider as well not just to prevent the player from fall through the level, but also to trigger this boolean to be true or false. True if they are touching each other, false if they are not. So, if they are not touching the ground then it doesnt matter how many times the player will press that button, he will not jump. Once they do then it applies the jump force if they are pressing Jump.
This the strangest thing i ever saw, i swear! So i have a script to the player that when you click on screen the cube jumps in the direction of the arrow(the arrow rotates 360 degres).I changed some settings to the rigidbody2d to make the jump better.Now here comes the strange part,when i run the game and the player is selected in the scene or hierrachy the jump works fine,if i select something else for exemple the camera something chenges the jump,from a very good one to a very bad one,i tryed to build the project and test and is the same,the cube dosen't have the same "Jump".I think it's becouse i changed something in the rigidbody,idk.PLS HELP!!!center
P.S if u ask for the script here it is :
public void Update()
{
Lava.transform.position = new Vector2(transform.position.x, Lava.transform.position.y);//other,not important
cam.transform.position = transform.position + offset;//camera follow player
Arrow.transform.RotateAround(gameObject.transform.position, new Vector3(0, 0, 180), ArrowSpeed * Time.deltaTime); //to make the arrow rotate 360
if (Input.GetMouseButtonDown(0))
{
rb.AddForce(Arrow.transform.right * -ImpulseForce * Time.deltaTime, ForceMode2D.Impulse); //this makes the cube jump
}
//other
score = (int) transform.position.y;
ScoreUI.text = score.ToString();
}
Firstly, RotateAround method takes a normalized vector as its second parameter, such as Vector3.forward in your case.
Secondly, applying any force to a rigidbody should be done in FixedUpdate, not Update (see Rigidbody.AddForce and ForceMode)
In your case it's an impulse, and it's only called when you click, so no need to multiply by delta time.
rb.AddForce(Arrow.transform.right * -ImpulseForce, ForceMode2D.Impulse);
It may not solve all of your issues, but without further information it's tough to know. At least you wont have weird physics behaviors anymore.
I am working on a 2D TopDown game in Unity 5. The collision is not working at all.
The player and the obstacle both have a 2D Collider and they are not Trigger. The player has a 2D Rigidbody with Kinematic set to false, gravity scale is equal to zero. Maybe the movement code has something to do with it.
The code is a bit long so I'll just show you the code for moving up:
if (Input.GetAxis ("Up") > 0) {
if (movingDown == false) {
posY += speed * Time.deltaTime;
movingUp = true;
}
} else {
movingUp = false;
}
/.../
transform.position = new Vector3 (posX, posY, 0);
It is always setting the value of the position as long as you are pressing the up button. Is there a way to fix this?
I think that the problem is that you are setting the position directly. So at each frame, you are telling unity exactly were the object should be, which overrides the position that would be computed from collision.
To fix this, you need to modify your movement code to add a force to your rigidbody and leave the position untouched ( see rigidbody doc, and function AddForce (https://docs.unity3d.com/ScriptReference/Rigidbody.html)
Try using
rb.velocity = new Vector3 (rb.velocity.x, rb.velocity.y, ConstantZValue);
This replaces your system with a velocity-based system as updating the transform.postition of a rigidbody is not recommended. With the system you have, the collision is not being detected because the rigidbody isn't being updated.
Im currently developing an Air hockey game in Unity3d. The issue I'm having is that when the player attempts to hit the puck too quickly, the player ends up going through the puck and therefore there is no collision. The game works perfectly as expected if the player stays still and the puck hits the player or if the player hits the puck at a slow pace.
The player has a rigidbody using continuous collision detection using a capsule collider. The puck also has rigidbody with continuous dynamic collision detection and a mesh collider with convex.
I tried setting the fixed timestep to 0.01 but that didn't have an effect. Here is the script for the player movement:
void ObjectFollowCursor()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Vector3 point = ray.origin + (ray.direction * distance);
Vector3 temp = point;
temp.y = 0.2f; // limits player on y axis
cursorObject.position = temp;
}
and here is the code for the puck when it collides with the player:
// If puck hits player
if(collision.gameObject.tag == "Player")
{
Vector3 forceVec = this.GetComponent<Rigidbody>().velocity.normalized * hitForce;
rb.AddForce(forceVec, ForceMode.Impulse);
Debug.Log ("Player Hit");
}
Any help would be much appreciated. Thanks.
The problem you are having its called "tunneling".
This happens because your object is moving at a high speed and in that specific frame the collision is not detected. In frame n the ball is just in front of the bat, but when frame n+1 is calculated the ball has moved behind the bat, thus "missing" the collision completely.
It is a common problem but there are solutions.
I recommend you study this script and try to implement on your game.
This is not my code:
SOURCE: http://wiki.unity3d.com/index.php?title=DontGoThroughThings
using UnityEngine;
using System.Collections;
public class DontGoThroughThings : MonoBehaviour
{
// Careful when setting this to true - it might cause double
// events to be fired - but it won't pass through the trigger
public bool sendTriggerMessage = false;
public LayerMask layerMask = -1; //make sure we aren't in this layer
public float skinWidth = 0.1f; //probably doesn't need to be changed
private float minimumExtent;
private float partialExtent;
private float sqrMinimumExtent;
private Vector3 previousPosition;
private Rigidbody myRigidbody;
private Collider myCollider;
//initialize values
void Start()
{
myRigidbody = GetComponent<Rigidbody>();
myCollider = GetComponent<Collider>();
previousPosition = myRigidbody.position;
minimumExtent = Mathf.Min(Mathf.Min(myCollider.bounds.extents.x, myCollider.bounds.extents.y), myCollider.bounds.extents.z);
partialExtent = minimumExtent * (1.0f - skinWidth);
sqrMinimumExtent = minimumExtent * minimumExtent;
}
void FixedUpdate()
{
//have we moved more than our minimum extent?
Vector3 movementThisStep = myRigidbody.position - previousPosition;
float movementSqrMagnitude = movementThisStep.sqrMagnitude;
if (movementSqrMagnitude > sqrMinimumExtent)
{
float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
RaycastHit hitInfo;
//check for obstructions we might have missed
if (Physics.Raycast(previousPosition, movementThisStep, out hitInfo, movementMagnitude, layerMask.value))
{
if (!hitInfo.collider)
return;
if (hitInfo.collider.isTrigger)
hitInfo.collider.SendMessage("OnTriggerEnter", myCollider);
if (!hitInfo.collider.isTrigger)
myRigidbody.position = hitInfo.point - (movementThisStep / movementMagnitude) * partialExtent;
}
}
previousPosition = myRigidbody.position;
}
}
You were right to try continuous collision detection (CCD). There are some constraints (especially in this case where you want to use CCD with two moving objects rather than one moving object and one static object), but it is designed for this kind of scenario. The Rigidbody documentation goes into these constraints:
Set the collision detection mode to Continuous to prevent the
rigidbody from passing through any static (ie, non-rigidbody)
MeshColliders. Set it to Continuous Dynamic to also prevent the
rigidbody from passing through any other supported rigidbodies with
collision detection mode set to Continuous or Continuous Dynamic.
Continuous collision detection is supported for Box-, Sphere- and
CapsuleColliders.
To sum up, both puck and paddle need to be set to Continuous Dynamic, and both need to be Box-, Sphere-, or Capsule Colliders. If you can make these constraints work for your game you should be able to get continuous collision detection without writing it yourself.
A note about Unity's CCD that bears repeating:
Note that continuous collision detection is intended as a safety net
to catch collisions in cases where objects would otherwise pass
through each other, but will not deliver physically accurate collision
results, so you might still consider decreasing the fixed Time step
value in the TimeManager inspector to make the simulation more
precise, if you run into problems with fast moving objects.
But since you are manually specifying the collision reaction, that might not be an issue.