UNITY : Ball showing odd behaviour - c#

I have a Ball(sphere) and a floor in my game scene. Ball.cs is attached to the ball to control its movement in the game(Ball only moves in the vertical direction). Both Ball and floor have colliders attached to them and whenever ball touches the floor, the game should ideally end.
OnCollisionEnter2D Method from the Ball.cs script.
private void OnCollisionEnter2D(Collision2D collision)
{
// Zero out the ball's velocity
rb2d.velocity = Vector2.zero;
// If the ball collides with something set it to dead...
isDead = true;
//...and tell the game control about it.
GameController.instance.PlayerDied();
}
Update function
void Update () {
//Don't allow control if the bird has died.
if (isDead == false)
{
//Look for input to trigger a "flap".
if (Input.GetMouseButtonDown(0))
{
//...zero out the birds current y velocity before...
rb2d.velocity = Vector2.zero;
// new Vector2(rb2d.velocity.x, 0);
//..giving the bird some upward force.
rb2d.AddForce(new Vector2(0, upForce));
}
}
}
But what's happening is whenever ball touches the ground, it starts rolling on the ground. It moves few units on +X-axis then rolls back and then ultimately stops.
position.X should ideally be 0(as the ball is moving only in Y-axis and it is during the game) but as soon as ball collides with the floor it starts moving.
I am new to Unity and I have no idea what is wrong.
Why is this happening?
EDIT:
Programmer's answer does work but I still don't understand where the horizontal velocity is coming from(there is horizontal velocity component associated with the ball). I need to know why ball is moving in horizontal direction.

I noticed that you are setting isDead to true when collision happens. If you don't want the ball to move again then set velocity to Vector2.zero; in the Update not only in the OnCollisionEnter2D function. Do this only if isDead is true.
void Update()
{
if (isDead)
{
rb2d.velocity = Vector2.zero;
}
}
Another option is to freeze the constraints when the collision happens. If you want the ball to start rolling again then unfreeze it.
private void OnCollisionEnter2D(Collision2D collision)
{
//Zero out the ball's velocity
rb2d.velocity = Vector2.zero;
//Freeze constraints
rb2d.constraints = RigidbodyConstraints2D.FreezeAll;
// If the ball collides with something set it to dead...
isDead = true;
//...and tell the game control about it.
GameController.instance.PlayerDied();
}
Execute rb2d.constraints = RigidbodyConstraints2D.None; to unfreeze it after.

Related

Unity 2d player jumping infinitely

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.

Move Object to the last position or position just outside the collision area

I am using a Player rigid body object and there are walls around the Player. These walls are restricting the Player to go through. The Player gets collided with these walls and then falls. The Player uses teleport function to jump from one area to next. Is there a way to make the Player jump to a position just outside the collision area after the Player is collided with these walls?
That is, Player A gets collided with the wall and does not jump to last position, but the position before the collision happened?
public GameObject Player;
public Vector3 PlayerPos;
public bool RecordPos = true;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(RecordPos == true)
{
PlayerPos = Player.transform.position;
}
}
public void OnTriggerEnter(Collider col)
{
if(col.gameObject.name == "Cube(3)" )
{
RecordPos = false;
Player.transform.position = PlayerPos;
}
}
In this script, the Player moves to last position it teleported from.
The "last" position before colliding is 1 frame before the collision. If you capture this position, the character will probably just fall on the obstacle again. Imagine you have a platform ___...___ and an obstacle. The easiest solution is to have 1 trigger at the left side of the obstacles and 1 trigger at the right side. If the player hasn't overcome the obstacle yet, he will be teleported to a chosen destination by you (before the obstacle) and if he's already overcome the obstacle, he will be teleported at the right side. __S_..._S__ (S stands for save/checkpoint trigger)
You need the following script on the gameobject with the Trigger collider. You also need to create a child object to the gameobject with the trigger:
private void OnTriggerEnter2D(Collider2D collision)
{
SaveManager.Instance.LastCheckpointOnHit = transform.GetChild(0).position;
}
And I suppose you have some sort of a singleton for data persistance. Now you can move the child gameobject whereever you want to teleport the player. And BTW I named the property LastCheckpointOnHit, because I was thinking of Holow Knight where if you get hit by spikes it instantly teleports you.
Then you just move the player: Player.transform.position = SaveManager.Instance.LastCheckpointOnHit;
In general when dealing with Rigidbody you shouldn't apply positions through the Transform component but rather through the Rigidbody.
I could imagine something like if you collide with a wall you get pushed away from the wall a bit in the direction where you came from like e.g.
[SerializeField] private float someDistanceThreshold = 0.01f;
[SerializeField] private Rigidbody _rigidbody;
private void Start()
{
if(!_rigidbody) _rigidbody = Player.GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
PlayerPos = _rigidbody.position;
}
public void OnTriggerEnter(Collider col)
{
// instead of the name I would rather use a tag later to cover all obstacles
if(col.gameObject.name == "Cube(3)")
{
_rigidbody.position -= (_rigidbody.position - PlayerPos).normalized * someDistanceThreshold;
// For stopping the player here
_rigidbody.velocity = Vector3.zero;
}
}

How to follow the unit when you click the mouse

I need my unit to move to the enemy when i clicking mouse on enemy and destroy when my unit touch him
For moving i use navmesh and raycast hit
All units have navmesh agent
Enemies moving by points
Lot of ways to do that: i give you the global idea and you adapt to your script
i have set layer for enemy to "enemy" just to be sure to chase a clicked enemy. layer enemy = 8 in my sample
3 phases:
First phase: Click detection and trap the gameobject clicked
private bool chasing = false;
public Transform selectedTarget;
if (Input.GetMouseButtonDown(0))
{
//Shoot ray from mouse position
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit[] hits = Physics.RaycastAll(ray);
foreach (RaycastHit hit in hits)
{ //Loop through all the hits
if (hit.transform.gameObject.layer == 8)
{ //Make a new layer for targets
//You hit a target!
selectedTarget = hit.transform.root;
chasing = true;//its an enemy go to chase it
break; //Break out because we don't need to check anymore
}
}
}
Second phase: chase the enemy. so you have to use colliders and at least one rigidbody, you have plenty of tutorial which explains how to detect collision.
if (chasing)
{
// here i have choosen a speed of 5f
transform.position = Vector3.MoveTowards(transform.position, selectedTarget.position, 5f * Time.deltaTime);
}
Destroy on Collision with OnCollisionEnter (or InTriggerEnter)
void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "enemy")
{
Destroy(col.gameObject);
}
}
The given code is for 3d game, if you are using a 2d game, just adapt the code to 2D, no difficulties to do that .

Capsule collider out of sync with animation

I am creating a player jump function and the collider does not achieve the same height as my players feet on jump.
I am currently trying to manually position the collider based on the position of the player. However, this has caused the capsule to now go way too high and because of the time it takes to fall, the player ends up halfway submerged in the ground.
void Jump() {
if (Input.GetKeyDown(KeyCode.Space)&& isGrounded == true) {
isGrounded = false;
actions.Jump();
rigidbody.AddRelativeForce(Vector3.up * jumpSpeed);
startTime = Time.time;
}
else {
if (transform.position.y < .6f) {
isGrounded = true;
}
}
}
private void Update() {
if (!isGrounded) {
dist = (Time.time - startTime) * capSpeed;
fracOfJourney = dist / journeyLength;
capCollider.center = Vector3.Lerp(start, end, fracOfJourney);
}
else {
// capCollider.center = Vector3.Lerp(end, start, fracOfJourney);
}
}
Rather than calculating the capsule collider position yourself a better solution would be to separate the rendering components of your player from the capsule collider. As a possible solution, you could have in your heirarchy:
Player Root: (rigidbody, player animator, Movement Script)
Renderer: (Mesh of player and renderer here)
Collider: (Capsule Colider here)
After doing this you could match the boundaries of your capsule collider by changing the scale and position as needed during your jumping animation. Separating the collider into it's own gameobject allows you to change the scale without effecting the appearance or position of the player.

Ball slows down in unity2D game

I have a blockbreaker type game coded using C# that mainly works fine but upon play testing i have found the ball will slow right down in certain situations! i.e if it wedges between to game object bricks the force of the ball rapidly slows down! 8 times out of ten this will not happen but other times it does and im unsure why! i will post the Ball script i think you will need to help solve this but should you need anymore information then please ask.
public class Ball : MonoBehaviour {
private Paddle paddle;
private bool hasStarted = false;
private Vector3 paddleToBallVector;
void Start () {
paddle = GameObject.FindObjectOfType<Paddle> ();
paddleToBallVector = this.transform.position - paddle.transform.position;
}
// Update is called once per frame
void Update () {
if (!hasStarted) {
//lock ball relative to the paddle
this.transform.position = paddle.transform.position + paddleToBallVector;
//wait for mouse press to start
if (Input.GetMouseButtonDown (0)) {
//if (Input.GetTouch(0).phase == TouchPhase.Ended){
hasStarted = true;
this.GetComponent<Rigidbody2D> ().velocity = new Vector2 (2f, 10f);
}
}
}
void OnCollisionEnter2D(Collision2D collision){
Vector2 tweak = new Vector2 (Random.Range(0f,0.2f),Random.Range(0f,0.2f));
if (hasStarted) {
GetComponent<AudioSource> ().Play ();
GetComponent<Rigidbody2D>().velocity += tweak;
}
}
}
You are adding directly to the velocity of the ball. The velocity variable defines a direction and speed, not just a speed as you are thinking here.
So, when the ball collides with a block it has a velocity of (+x, +y). After the collision and bounce is has a velocity of (+x, -y). So by adding a random positive value to the velocity when it has the -y velocity means that it will slow down the ball.
This does not happen every time because you are moving the ball in your Update() method. Change that to 'FixedUpdated()'. Fixed Update handles all physics calculations.
Also, I would recommend having a RigidBody2D on your Ball object. Moving physics objects should always have RigidBodies. With this, you can then use AddForce in the forward direction of your ball and that should solve your problem.
EDIT: I see you have a RigidBody2D already. I missed that the first time. Instead of having GetComponent<RigidBody2D>().velocity try GetComponent<RigidBody2D>().AddForce( transform.forward * impulseAmount, ForceMode.Impluse );

Categories