How to check if my ball stopped rolling? - c#

I want to check when my ball stopped rolling (moving) for more then 5 seconds and then execute some code. I'm working on an android game and sometimes the ball gets stuck, the ball should be moving constantly so I want to check if it's moving and if it's not I need to respawn it.
What is the best way to do this?

Each time Update is called, you could check the magnitude of the object's velocity. If it's lower than a small amount (say 0.1 or something depending on your game's scale), add Time.deltaTime to a member variable, otherwise, set that variable to 0. If that variable is ever over 5, you know your object has been not moving (or close) for 5 seconds. Then execute your code.

You can create a simple counter for count 5 seconds and check the gameObject.getComponent.velocity.magnitude;
And create a if and check this magnitude is smaller than your wanted level

Put this as a C# script component onto your ball gameobject.
public class MyBall : MonoBehaviour{
private Transform _myTransform = null;
private Vector3 _lastPosition = null;
private void Awake(){
_myTransform = transform;
_lastPosition = _myTransform.position;
}
private void Update(){
if(_lastPosition == _myTransform.position){
Debug.Log("Did not move");
}else{
Debug.Log("Moved");
}
_lastPosition = _myTransform.position;
}
}

Related

C# - Unable to increment an int beyond 1. Console prints 1 and it does not increase, despite triggering multiple times

The game is simple, your Player has a running animation and must jump to dodge obstacles, but isn't moving. The obstacles and background are.
My gut instinct is that it is out of scope somehow or is perhaps overflowing - the score increment was increasing far beyond what I had anticipated when I had the AddScore() inside of the Destroy(gameObject); if condition instead of its own function.
However, at this point I am very confused why it isn't working. As a bonus, I cannot get Audio to play from the second commented bit (Value cannot be null.) As for why that happens, no idea. I definitely have the source I have it attached to to the prefab that is spawned, and said spawn should trigger that sound to play when it passes under the player when they jump, I originally thought that there was an issue where the object was deleted before it could reference its audio source but I am unsure.
Edit: I am going to leave the 'bonus' issue above even though I literally fixed it as I typed this up. (I had to add an Audio Source component to the prefab I was spawning.)
I still, for the life of me, cannot get an integer to go above 1 and print to the console. It might be driving me a little insane. Please help friends. I have googled a ton and none of the suggestions from other comments fixed my issue.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLeft : MonoBehaviour
{
private float speed = 30;
private PlayerController playerControllerScript;
private float leftBound = -15;
private float scoreZone = -3;
public AudioClip scoreSound;
private int score = 0;
private AudioSource playerAudio;
// Start is called before the first frame update
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();
playerAudio = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
// Moves objects as long as it is not game over.
if (playerControllerScript.gameOver == false)
{
transform.Translate(Vector3.left * Time.deltaTime * speed);
}
//Assigns scoring system, buggy. Commented out sound because of errors.
if (transform.position.x < scoreZone && gameObject.CompareTag("Obstacle"))
{
//playerAudio.PlayOneShot(scoreSound, 1);
}
//Destroy object out of bounds
if (transform.position.x < leftBound && gameObject.CompareTag("Obstacle"))
{
Destroy(gameObject);
AddScore();
}
}
void AddScore()
{
//Score goes to 1.
score++;
//prints 1 in console but does not increase over 1.
Debug.Log("Score! Your Score is" + score);
Debug.Log(score);
}
}
Tried: Numerous changes to configuration of ++, x = x +1, x++, x +=, etc. No idea. Am lost.
This is an issue caused by the local member int. You have an object, which has been created and this MoveLeft component is attached to it. You then destroy this object on collision, and therefore this component as well. You’ll have added one to the local instance int score, but then you destroy the component and lose this local instance.
I suspect what you thought was happening is that all the scripts/components shared the same variable values. That would only be true if you if you made the int score member a static int score, which means the member is the same shared amongst all instances of this component.
Instead of making score static, you might want to have a “Game Manager” that exposes public functions to allow you to increment and retrieve the current score.

Unity3D is giving me unexpected output: Instantiating objects at too high of a rate

Alright, so I'm making a game in Unity, and I tried to spawn in enemies randomly around a player. To control the rate of the spawning, I created a private bool spawnCooldown variable. Then, there was an if {} statement which controlled the rate of spawning. The original code is below:
private bool spawnCooldown;
private void Start
{
spawnCooldown = Time.time;
}
private void Update
{
if (spawnCooldown < Time.time)
{
Instantiate(original, position, rotation);
spawnCooldown = Time.time + 3f;
// Note that original, position and rotation are just placeholders
// and not the actual code.
}
}
What's wrong with this code? Currently, it's just instantiating every frame update, and it seems that it's not testing the if statement at all.
You need to add the seconds of cooldown you want on the spawner when you declare it too, E.G spawnCooldown = Time.time + coolDownPeriodInSeconds;
You also need to set spawnCooldown to be a float, which stores numbers.
Currently you have stored it as a bool, which only stores true or false values, and therefore cannot be compared to Time.time further in the code.
Lastly you are missing the closing } character in the if block
What is happening currently is you are doing the following:
Setting spawn Cooldown to be the current Time
For every frame after that, checking if the new current time is greater than the old time you set as spawnCooldown. Since it always is, the code will then run through the spawn script.
If you change it to
private float spawnCooldown;
void Start
{
spawnCooldown = Time.time +3f ;
}
void Update
{
if (spawnCooldown < Time.time)
{
Instantiate(original, position, rotation);
spawnCooldown = Time.time + 3f;
}
//Note that original, position and rotation are just placeholders and not the actual code.
}
Then it works fine.
Good luck with Unity!
Ok, so I've managed to fix the problems with StarshipladDev's answer. The problem was that the clones were cloning themselves, as I was instantiating the gameobject itself.

Player unable to move after being teleported/transported Unity3d

I've been trying to fix this one bug in my code for over 7 hours now, upon being teleported, the movement controls cease to function, the mouse works fine, you can look around, but you can't move around.
I wanted to set up some simple code that would teleport the player to a "checkpoint" upon achieving a negative or null y level. I was doing this for a parkour based game, if the player fell off the platform they would have to start over, but after teleporting, it becomes impossible to move as I'm sure I have already said. My code is pretty simple:
public class Main : MonoBehaviour
{
float Fall;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 Checkpoint = new Vector3 (0,3,0);
GameObject Player = GameObject.FindGameObjectWithTag("Player");
Fall = GameObject.FindGameObjectWithTag("Player").transform.position.y;
if (Fall<-4)
{
Player.transform.position = Checkpoint;
}
}
}
You would think that this would just simply change the coordinates of the player, but I think this might be screwing with the FPSController script.
I am using Unity3d, with Standard Assets imported, All of the code is in C#.
Instead of checking the Y value of your character, I would instead place a death collider under the map. Make this a trigger and if the player touches this trigger, then teleport them back. Nothing with your code should screw with the FPS controller so it might be something else. I would also highly recommend not using a FindGameObjectWithTag in the Update() method as it is extremely expensive to use this every frame, especially twice. If you would rather keep the Update() Y component of the position check, please rewrite the code to something like this:
public class Main : MonoBehaviour
{
// assign this object of your player in the inspector - it stores the reference to reuse
// instead of grabbing it every frame
[SerializeField] private Transform playerTransform = null;
// make this a variable as it is not changing - might as well make this const too
private Vector3 checkpoint = new Vector3(0, 3, 0);
// constant value of what to check for in the Y
private const int FALL_Y_MARKER = -4;
// Update is called once per frame
void Update()
{
if (playerTransform.position.y < FALL_Y_MARKER)
{
playerTransform.position = checkpoint;
}
}
}
With your current code, there should be nothing breaking your input/movement, but with that said, we can not see your input/movement code. All the above snippet does is check if the Y component of the player objects position is below a certain value, and if it is, it sets the position to a new vector. Can you post a bit more movement code or somewhere else it can go wrong that you think is the issue?

Slow collision detection at low frame rates

I'm experiencing an odd issue with my collision detection. I'm using the Update method to move the player (I don't want to use FixedUpdate because that creates an undesired weird movement). The fixed timestep is set at the default 0.02 (I tried playing with time setting but that didn't work either) . I set the collision detection of the rigidbodies of both objects to "continuous dynamic". Also, I set the target frame rate to 300 and that didn't change anything...
When the framerate is low or the device itself is slow, the collision detection doesn't always work. The player can easily fall through the object it's supposed to collide with, though sometimes it doesn't.
Please tell me what I can do to fix this because I've published a game and many users are reporting this (serious) bug. Thank you for your support.
This is what is supposed to happen:
This is what actually happens:
(as you can see, the cube gets out of the wall and to the other side)
I move the player when the user releases the mouse button:
Script 1:
public Script2 Jumper;
public float TimeToJump;
public void Update()
{
if (Input.GetMouseButtonUp(0))
{
StartCoroutine (Delay (1f/50f)); //Don't mind the time.
}
}
IEnumerator Delay(float waitTime)
{
yield return new WaitForSeconds (waitTime);
if (Jumper != null)
{
Jumper.SetVelocityToJump (gameObject, TimeToJump);
}
}
Script 2 attached to player (cube):
public class Script2 : MonoBehaviour {
GameObject target;
private float timeToJump;
public bool isJumping = false;
public void SetVelocityToJump(GameObject goToJumpTo, float timeToJump)
{
StartCoroutine(jumpAndFollow(goToJumpTo, timeToJump));
this.timeToJump = timeToJump;
this.target = goToJumpTo;
}
private IEnumerator jumpAndFollow(GameObject goToJumpTo, float timeToJump)
{
var startPosition = transform.position;
var targetTransform = goToJumpTo.transform;
var lastTargetPosition = targetTransform.position;
var initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);
var progress = 0f;
while (progress < timeToJump)
{
progress += Time.deltaTime;
if (targetTransform.position != lastTargetPosition)
{
lastTargetPosition = targetTransform.position;
initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);
}
float percentage = progress * 100 / timeToJump;
GetComponent<Rigidbody>().isKinematic = percentage < 100.0f;
transform.position = startPosition + (progress * initialVelocity) + (0.5f * Mathf.Pow(progress, 2) * _gravity);
yield return null;
}
OnFinishJump (goToJumpTo, timeToJump);
}
private void OnFinishJump(GameObject target, float timeToJump)
{
if (stillJumping)
{
this.isJumping = false;
}
}
private Vector3 getInitialVelocity(Vector3 toTarget, float timeToJump)
{
return (toTarget - (0.5f * Mathf.Pow(timeToJump, 2) * _gravity)) / timeToJump;
}
}
The target of the cube is a child of the bigger cube (the wall).
If you require clarification, please leave a comment below. I might give the link to my game if you need more details.
Quote from here (found thanks to #Logman): "The problem exists even if you use continuous dynamic collision detection because fast moving objects can move so fast that they are too far apart from itself from one frame to the next immediate frame. It's like they teleported and no collision detection would ever be triggered because no collision existed, from each frame perspective, and thus from all calculations processed."
In my case, the cube is not going fast, but you get the concept.
There are several issues with your code.
You are asking a Coroutine to yield for 1/50th of a second. The minimum time a yield must occur for is one frame. If Time.deltaTime > 0.02f this is already one of the problems.
You are using Coroutines and yield return null to compute physics calculations. Essentially, you're computing physics in Update(), which is only called once per frame (null is equivalent to new WaitForEndOfFrame(): as mentioned in (1), a running Coroutine cannot be yielding between frames). Under low frame-rate, the amount of motion an object undertook between two frames might exceed the collision range of the target trigger. Assuming linear, non-accelerating motion: ∆S = v∆t where v = velocity, ∆S is movement to cover in the current frame, ∆t is Time.deltaTime. As you can see, ∆S scales proportionally with ∆t.
You have GetComponent<T>() calls inside loops. Always avoid doing this: store a reference as a member variable instead (initialise it in Start()).
My suggestion for the quickest working hack would be to not worry too much about "being clean", and instead create subroutines that you call from FixedUpdate(), and (create and) use member bools to conditionally test which subroutine to "execute" and which to "skip". You can also use member bools or enums as triggers to switch between various "states".
A better solution would be to let Unity handle the kinematics and you instead work with rigidbody mutators (and not transform.positions), but that may be totally unnecessary for an arcade situation, which yours might be. In that case stick to the hack above.
If you really want to control kinematics by hand, use an engine like SFML. A Particle System tutorial would be a good place to start.
It's your float percentage, among other things.
"If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore."
That's from the isKinematic page of Unity's documentation. You're setting it to true when progress hits 100. So at lower framerates, there'll be a sudden jump due to Time.deltaTime steps being a lot higher, progress is suddenly >= 100, isKinematic is set to true and the player is no longer affected by collisions.
I think you're going to have to rethink a lot of the code here and do some heavy optimisations. But the other posters have laid those out already, so I don't need to.
EDIT: Misunderstood the initial question, thought that it meant you were trying to detect collisions but your code wasn't always detecting them. Didn't realise it actually meant getting the collisions to occur in the first place.

Unity C# Player going too fast with Rigidbody2D.AddForce

So I'm trying to make a game in Unity 5. It's a 2D game and I want my character to automatically go forward. But my problem is that since I use Rigidbody2D.AddForce, it keeps adding force every frame and I don't want that. I want my speed to be at 5.0f, not more or less. Is there a way to set a limit or keep a constant speed?
using UnityEngine;
using System.Collections;
public class Player : MonoBehaviour {
public Rigidbody2D player;
public bool grounded = false;
private bool hasJumped = false;
public float movementspeed = 5.0f;
public float jumpforce = 450.0f;
// Use this for initialization
void Start () {
player = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update () {
player.AddForce(transform.right * movementspeed);
// (JumpScript)
}
void FixedUpdate(){
// (JumpScript)
}
}
I am very suprised at the answers here, they are way off. Your problem is that you are adding force in the update, which is called every frame. That means that every frame of the game the object will gain 5 more force and speed up infinitely.
If you want the object to get 5 force and then stay at that speed, just put the add force in the start instead of the update. Like this:
// Use this for initialization
void Start () {
player = GetComponent<Rigidbody2D>();
player.AddForce(transform.right * movementspeed);
}
// Update is called once per frame
void Update () {
// (JumpScript)
}
You could just set the Rigidbody.velocity to 5.
However, sooner or later knowing some physics will come very handy. The absolute minimum is knowing Newton's laws.
In your case, if you are absolutely dedicated to using AddForce() you could use ForceMode.Impulse as the 3rd parameter to give your object an instant push (instead of gradual acceleration). This way you would have to call AddForce() only once, not repeatedly. You can calculate the exact amount of force you need by the formula for Kinetic energy:
Force = 0.5f * Mass * Velocity^2
I'm writing a 2D game like yours in unity too. And I use an if to check the velocity and use another variable for the force like this:
public float acel = 50, speed = 5;
Rigidbody2D rbody;
void FixedUpdate () {
// As you want to move right, we don't have to calculate the absolute value
if (rbody.velocity.x < speed)
rbody.AddForce(Vector2.right*acel);
}
The speed won't always be 5 but maybe that isn't important though.
If you want to use Physics to move your player at a constant speed then you need to apply force at the beginning until the speed you want is reached, only then change force to 0. In order for this to work as expected I would also add a Physic Material to the collider of the character to make sure that no friction is slowing down the character.
P.S: If you are creating some sort of infinite runner game I would consider moving and reusing the environment rather than the player.

Categories