Unity pong game ball physics slowing issue - c#

In my game for pong, the ball is supposed to bounce and never become slower. However, the ball is steadily slowing down over time. I will put an image of the ball object and the scripts.
Here is the ball properties on the left
Here is the ball script
using UnityEngine;
using System.Collections;
public class Ball : MonoBehaviour
{
public float ballVelocity = 3000;
Rigidbody rb;
bool isPlay;
int randInt;
void Awake()
{
rb = GetComponent<Rigidbody>();
randInt = Random.Range(1,3);
}
void Update()
{
if (Input.GetMouseButton(0) && isPlay == false)
{
transform.parent = null;
isPlay = true;
rb.isKinematic = false;
if (randInt == 1)
{
rb.AddForce(new Vector3(ballVelocity, ballVelocity, 0));
}
if (randInt == 2)
{
rb.AddForce(new Vector3(-ballVelocity, -ballVelocity, 0));
}
}
}
}
and here is the bounce physics image
and since I have no idea why it won't work, here is my physics project settings

The reason why this is happening is because of the randomness being added. All it takes is it to hit harder in one direction one than the opposite direction. Eventually, idk after how long of a time, but it will finally settle at a velocity of 0. To fix this, you need to remove the drag coefficient if you haven't already. Next, you need to clear whatever the current velocity is from the ball. "rigidbody.velocity = Vector3.zero;" should do it for you. After that, you can either generate a new velocity directly using some maths that I do not know off of my head, or add a new force that is no longer dependent on the previous condition of the ball. I hope this helps, and if not, leave a comment and let's see if we can't find a better solution :)

Related

Sphere Collider, not pushing things when size is changed

So I'm trying to make a little pushback effect in my tests arena, I've got a sphere collider and here is my script:
// PushBack Class Script
if (Input.GetKeyDown(KeyCode.Q))
{
explosion_ball.transform.position = transform.position;
StartCoroutine(WaitAndPrint());
}
IEnumerator WaitAndPrint()
{
float i = 0;
while (i < 1)
{
i += 0.01f;
explosion_ball.radius = curve.Evaluate(i) * 10;
yield return new WaitForSeconds(0.01f);
}
}
//__________//
Sphere collider is set and stuff, but it doesn't push things back like I thought it would.
Thanks!
Edit:
explosion_ball is a sphere collider, I'm changing it with the point on the animation curve and * it by 10
EDIT:Unity Rigid bodies go to sleep so[Also Change interpolation to continuous if collider changes size too quickly]
A.check if obstacle rigid bodies are Sleeping with onTriggerEnter and Wake Up
void OnTriggerEnter(collider){ if(rb.IsSleeping()){rb.WakeUp();}
or
B.Attach this forceWakeUp Script to all Objects you want to be obstacles.
using UnityEngine;
public class forceWakeUp : MonoBehaviour
{
private Rigidbody rb;
void Start()
{
rb = gameObject.GetComponent<Rigidbody>();
}
void Update()
{
if(rb.IsSleeping()){rb.WakeUp();}
}
}
Forcibly Keeping Many objects awake Will impact performance.So ,your decision.
You need to scale up the collider on the object by radius and all the objects it is supposed to wobble need to have Rigid body component attached to them
2.if you ARE doing above things and its not adding any force you could just add a force on all the overlapping objects using the "OnCollisionEnterTrigger" and AddForceMethod radially away from the Sphere
Obstacle.position - Sphere.position is the vector Radially away from Sphere I think.
You don't need coroutines for this I think.
While searching through the Unity scripting API, found a method in there called
Rigidbody.AddExplosionForce(explosionForce, explosionPosition, explosionRadius, upwardsModifier, mode)
In order to get the rigid bodies that are to be affected by the explosion I would need to get them using Physics.OverlapSphere(position, radius) this gets all of the objects within the radius variable, then get the component.
Combining these two would also look like:
Collider[] colliders = Physics.OverlapSphere(transform.position, radius);
foreach (Collider hit in colliders)
{
if (hit.transform.tag == "Interactable")
{
if (hit.gameObject != hitgameObject)
{
Rigidbody rb = hit.GetComponent<Rigidbody>();
if (rb != null)
rb.AddExplosionForce(power, transform.position, radius, upForce, ForceMode.Impulse);
}
}
}
If there is any explaining that you would like me to do about my variables mentioned I will reply :)
Thanks for the help guys.
I don't know your curve and what values that evaluation produces, but you can check your code visually with Window/Analysis/Physics Debugger, or write a gizmo:
private void OnDrawGizmos()
{
Gizmos.color = new(1.0f, 0.0f, 0.0f, 0.5f);
Gizmos.DrawSphere(explosion_ball.transform.position, explosion_ball.radius);
}
Or simply just use https://docs.unity3d.com/ScriptReference/Rigidbody.AddExplosionForce.html

How to fix spasming and buggy instantiation in Unity

I'm creating a basic pool game in Unity with C#, what im trying to do is that if the cue ball is moving, the stick will disappear, and once it becomes stationary again, it will reappear to where the cue ball is located. This is my code so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class stickDeplacement : MonoBehaviour
{
public bool bIsOnTheMove = false;
Vector3 lastPos;
public GameObject Stick;
void Start()
{
}
void Update()
{
var stick = Instantiate(Stick, gameObject.transform.position, gameObject.transform.rotation);
if (this.transform.position != lastPos)
{
Destroy(stick);
Debug.Log("Is moving");
}
else
{
Debug.Log("Is not moving");
}
lastPos = this.transform.position;
}
}
But what happens is that the ball, along with the stick, will just spasm and be buggy right from the start (when I open and play the game). Am I missing something here?
This is extremely inefficient and dangerous!
Why instantiate a stick every frame just to eventually already destroy it in that very same frame? And if the ball is stationary you want an additional stick to be spawned every frame?
Instead of all the time instantiating and destroying it at all you should rather keep one stick and only (de)activate it.
In your case you could do this in a single line
bIsOnTheMove = transform.position == lastPos;
stick.SetActive(!bIsOnTheMove);
Also I doubt a lot you would like the stick to have the same rotation as a rolling ball! Of course this will behave awkward
Most certainly you do not simply want to clone the ball's orientation. I would e.g. try to determine the closest point of the table edge to the current ball's position (iterate through the wall colliders and use Collider.ClosestPoint) and let the stick face the direction from that edge point towars the ball position (+ maybe an offset in X so the stick is slightly inclined by default).
And finally you anyway do not want to assign that rotation every frame, since you most probably later want your users to be able to rotate that stick. You only want to apply this once when the ball becomes stationary.
Something like e.g.
// The stick is not a prefab anymore but simply always exists in the scene!
[SerializeField] private Transform stick;
[SerializeField] private Vector3 eulerOffset;
[SerializeField] private Collider[] wallColliders;
public bool bIsOnTheMove;
private Vector3 lastPos;
private void Start()
{
lastPos = transform.position;
}
private void Update()
{
// is the ball currently moving?
var isMoving = transform.position == lastPos;
last Post = transform.position;
// Did this state change since the last frame?
if(bIsOnTheMove == isMoving) return;
bIsOnTheMove = isMoving;
// (de)activate the stick accordingly
stick.gameObject.SetActive(!isMoving);
// Do this ONCE when ball becomes stanionary
if(!isMoving)
{
var ballPosition = transform.position;
// among the wall colliders find which one is closest to the ball
Vector3 closestPoint;
var smallestDistance = float.PositiveInifinity;
foreach(var wall in wallColliders)
{
var edgePoint = wall.ClosestPoint(ballPosition);
var distane = (edgePoint - ballPosition).sqrMagnitude;
if(distance < smallestDistance)
{
closestPoint = point;
smallestDistance = distance;
}
}
// then make the stick look towards the ball from that edge
var direction = ballPosition - closestPoint;
var rotation = Quaternion.LookRotation(direction);
// optional add the offset
rotation *= Quaternion.Euler(eulerOffset);
stick.rotation = rotation;
}
}

friction not working correctly in my 2d game - unity

I am developing a 2d game in unity, and I am trying to add friction to slow the player down when he is on the ground. I launch the player into the air with the rigidbody's add force. I have heard of physics materials, and am currently trying to use them. I am using the 2d version of them, I made one material which had high friction. I add the material to the floor, and then I play the game, and my character is still sliding on the floor. I thought the problem might be with the player not having a physics material, so I added one. It still didn't work.
I tried doing different combinations with the different materials.
I tried attaching a rigidbody, but kinematic to the floor.
I tried looking it up, and couldn't find an answer.
Here is the code that moves the player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public bool isGrounded = true;
public float fireForce;
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
camera = camObj.GetComponent<Camera>();
}
void Update()
{
RotateToPoint();
Fire();
}
void OnCollisionEnter2D(Collision2D obj)
{
if (obj.gameObject.tag == "surface")
{
isGrounded = true;
}
}
void OnCollisionExit2D(Collision2D obj)
{
if (obj.gameObject.tag == "surface")
{
isGrounded = false;
}
}
void Fire()
{
if (Input.GetKeyDown(KeyCode.Mouse0) && isGrounded)
{
rb.AddForce(transform.up * fireForce, ForceMode2D.Impulse);
}
}
}
I do not know if I am approaching the problem correctly. If I am not, does anyone know how to avoid slippery game objects. Any help would be appreciated.
Adding a higher angular drag was the solution to my problem. Angular drag is what slows the object down along its rotation. Friction may not always be the problem.
Notice the two highlighted variables. You might want to raise these values (I changed angular drag from its default) to something higher. Adding the physics material might help, you should try this if this first solution doesn't work.
The problem here is that you are not limiting the velocity of the player, so what you can do is set an if statement if the player is touching the floor and the velocity is higher than what you want it to be.
Example:
void FixedUpdate()
{
if (isGrounded == true && rb.velocity.x > 1 || isGrounded == true && rb.velocity.z >1)
{
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
}

My Player Movement is presenting some Issues

I'm starting to learn C# with Unity and I guess my code present some issues, the movement of my player works but not that good. Let me explain, I have a player at the bottom of the screen, moving only from left to right by itself, using transform.translate (I didn't used rigidbody) because on collision with the sides it stops, and is not a constant movement, the idea is that, once collides with the sides, it changes of direction, but same velocity.
The issue happens when the player colides with the walls (removed the mesh renderer to keep them transparent) but if at the same time you press the key to trigger also the change of direction the player gets stuck at the corner, as shown on this gif example:
Wait for the Player to get stuck at the corners, the gif takes like 15 secs.
And here's my actual Script for the Player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PMovement : MonoBehaviour
{
public float playerSpeed = 8.0f;
void Update()
{
float moveX = playerSpeed * Time.deltaTime;
transform.Translate(moveX, 0, 0);
if (Input.GetKeyUp(KeyCode.Space))
{
playerSpeed = playerSpeed * -1;
}
}
void OnCollisionEnter(Collision target)
{
if (target.gameObject.tag.Equals("SideWalls") == true)
{
playerSpeed = playerSpeed * -1;
}
}
}
Your code looks ok for what you want to achieve. Unfortunately, in Unity it may not be enough to pin point the problem, as the code heavily depends on the setting from the editor. One possible solution to this or similar problems:
If you still have blocking colliders, (The collision functions need some sort of rigidbody.), or if you clamp your player's position, to stop overflow, the following can happen:
Collider on right side is hit
Player direction change to left
Space is hit while colliders are still touching
Player direction change to right again
There is no collider on right side left to start the colliderEnter event again.
In this case block your space recognition while the player is touching one side.
If you change direction when you hit the wall, but you press Space at just the right time, you'd end up inside the wall a bit most likely. Then it'd keep thinking it was entering the wall collision and reversing direction over and over. Here's what I'd do.
public class PMovement : MonoBehaviour
{
private List<GameObject> Collisions = new List<GameObject>();
public float playerSpeed = 8.0f;
void Update()
{
float moveX = playerSpeed * Time.deltaTime;
transform.Translate(moveX, 0, 0);
if (Collisions.Count <= 0 && Input.GetKeyUp(KeyCode.Space))
{
playerSpeed = playerSpeed * -1;
}
}
void OnCollisionEnter(Collision target)
{
if (!Collisions.Contains(target.gameObject) && target.gameObject.tag.Equals("SideWalls") == true)
{
Collisions.Add(target.gameObject);
playerSpeed = playerSpeed * -1;
}
}
void OnCollisionExit(Collision target)
{
if (Collisions.Contains(target.gameObject)) {
Collisions.Remove(target.gameObject);
}
}
}
By keeping a list of wall objects you're inside, you can disallow the space bar if you're inside a wall. Keep in mind there's probably better ways to do the things you're doing, but this should fix the issue you're having for now.

Unity2D jumping with convex shapes as possible ground

Sorry if the title is confusing. Basically, what I'm trying to do is make sure the player is grounded (so they can jump again), problem is in my game there will be convex shapes that the player could land on.
The current way I'm doing this is with a raycast, but since the single raycast could only come from somewhere like the centre, I made it so the raycast was going along the bottom of the player instead (this removed the problem where the player couldn't jump if more than half of their body was off a platform).
The raycast along the bottom however made it so if I tried to jump while on a shape like a ramp, it would cause me to go incredibly high, which I don't want.
Can anyone help me fix this?
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterMovement : MonoBehaviour {
public Rigidbody2D rBody;
[Range(1, 10)]
public int Speed;
private float lastDistance;
public bool isGrounded = true;
private LayerMask environment;
void Start () {
environment = LayerMask.GetMask("Environment");
}
void FixedUpdate() {
// Check if the user is attempting to jump
if (Input.GetKey(KeyCode.A)) {
rBody.velocity = new Vector2(-Speed, rBody.velocity.y);
}
if (Input.GetKey(KeyCode.D)) {
rBody.velocity = new Vector2(Speed, rBody.velocity.y);
}
if (Input.GetKey(KeyCode.Space)) {
RaycastHit2D hit2D = Physics2D.Raycast(rBody.position+new Vector2(0,-1.5f), Vector2.right,1f,environment);
if (hit2D) {
if (hit2D.distance < lastDistance) {
lastDistance = hit2D.distance;
}
else {
lastDistance = 100f;
rBody.AddForce(new Vector2(0, 10), ForceMode2D.Impulse);
}
}
}
}
}
What happens if you try with Input.GetKeyDown(KeyCode.Space)?
Unless the game design it to keep jumping as long as you press space, this might be the reason you are jumping extra high.
If the Raycast solution is still wonky, another way of detecting if the player is grounded is using a Collider for the floor and a flag that tells you when you're colliding with it (you can set this flag using OnCollisionEnter/Exit or OnTriggerEnter/Exit)

Categories

Resources