C# frames running twice? - c#

This has me utterly confused. I am using unity3d and c# for the scripts and it seems as if the code is running twice per frame. However on button down I have a sprite change position and it only changes once at least I think it does.
I added the Debug in and I am getting results like this:
score 1 at 3.569991 at frame 168
score 2 at 3.57414 at frame 168
score 3 at 3.818392 at frame 183
score 4 at 3.820178 at frame 183
and so forth carrying on. I am not updating the score in any other scripts. There is more to this script but it is just printing the score out on screen.
Is there any reason why a script may run like this?
full script:
using UnityEngine;
using System.Collections;
public class Score : MonoBehaviour {
public static int highScore;
public static int myScore;
public static bool allowScore;
public GUIText myText;
public static bool WhichScene;
//only allows score to start when first object has passed player object
void OnTriggerEnter2D(Collider2D collisionObject) {
allowScore = true;
Debug.Log ("allowScore is true");
}
void Start () {
highScore = PlayerPrefs.GetInt("highScore");
int scale = Screen.height / 20;
myText.fontSize = scale;
}
//add 1 to score every switch
void Update () {
// need to stop score counting
if (DeathOnImpact.dead == true) {
allowScore = false;
} if (Input.GetMouseButtonDown (0) && allowScore == true && WhichScene == true) { // added SpawnerObjectMovement.WhichScene == true
//Input.GetMouseButtonDown (0)
//Input.GetKeyDown("space")
myScore = myScore + 1;
Debug.Log ("My score is " + myScore + " point(s)" + " at time:" + Time.realtimeSinceStartup + " at frame:" + Time.frameCount);
} if (myScore > highScore) {
highScore = myScore;
PlayerPrefs.SetInt("highScore", highScore);
}
myText.text = myScore.ToString ();
//myText.text = "Score: " + myScore.ToString ();
if (Score.WhichScene == false) {
int scale = Screen.height / 40;
myText.fontSize = scale;
myText.text = "practice mode";
}
}
}
The script is attached to a TriggerObject, a sprite, and a Gui Text
WhichScene referes to which button I pressed, 'play' for normal play or 'practice mode' for an easier version. Score is disabled for 'practice mode'.
UPDATE: I have just edited out everything that I have added since the problem arose and it has not been fixed. Im going to check all unity setting to see if anything has changed. It seems in build setting an old Scene which I deleted around when the problem arose is not selcted but just 'shadowed' so I cant select it. The Scene was an exact copy of the PlayScene. Is this a sign of the problem?
SOLUTION: It appears that separating the script into two smaller scripts has solved the issue. I am still unsure why it has only arisen now since it was working before as one, but oh well I guess. Thank you.

Based on your comments, where you have said that your script is attached to 3 GameObjects, that means that the Update() method is getting called 3 times per frame.
public class Score : MonoBehaviour {
public static int myScore;
You have declared myScore as a static int. This functionally means it will be shared by all instances of the Score script that run.
The Update() method of MonoBehaviour is called once per frame for every GameObject that has this script attached. You have 3 GameObjects with this script attached. Therefore, each will call Update() on their individual instance of the Score script.
I'm not sure what you exactly intend to happen, so it's hard for me to give any advice beyond pointing out the problem.
I think that you need to split this script into multiple scripts. This script is doing too much. It's violating the Single Responsibility Principal (SRP). This is one of the most important principles to follow in programming. I'd suggest splitting this into at least 3 scripts. I'd probably make the following scripts:
PlayerStatistics - Attach this script to your player object (I'm assuming that is the sprite you mentioned). It will simply hold the statistics, including score, for your player. There should only be one attached to each player.
ScoreBoard - Attach this script to your GUI component. It will take a reference to the PlayerStatistics. Notice this is a reference to the single instance that is on your Player. Your ScoreBoard script will only read the value of the score from the PlayerStatistics script.
ScoringTrigger - Attach this to your TriggerObject. It would also have a reference to the PlayerStatistics script. It would have the code that checks to see if scoring should be done, and updates the value of the PlayerStatistics script.

To avoid some events to run twice a frame use Events to get imput actions:
if(Event.current.type == EventType.MouseDown){
if(Event.current.button == 0 && allowScore && WhichScene) {
// do it once!
}
}

Related

How can I execute code if something is true, continue executing the code indefinitely, but stop evaluating the if statement?

I'm just starting out please excuse vast ignorance.
I'm writing a c# script in unity as part of the essentials training. I'm doing the 3d audio module and I thought I'd try and get a little bit fancier than the scope of this particular lesson which is supposed to be having an object fly through a window in a pre-built scene and make a 3d sound as it moves.
I wanted to make the movement of the object conditional upon a player moving close to it in 3d space. I figured out how to trigger the movement of an object in a script with an if statement that changes the transform parameters of the object the script is attached to when a 'distanceFromObject' variable is < 2. It works, however the script runs in the update section of the script which runs once every frame. This means that the object's transform parameters are changed every frame as expected but of course stops doing so when the distance between the object that's moving and the player exceeds 2.
I see the mistake I've made because if the object moves away when the player gets close then it will inevitably eventually move far enough away that the distanceFromObject variable will grow bigger than 2 whereupon it stops and just hovers in place. I don't know how to fix it though.
I need the script to check the distance between the object and the player every frame so that it will trigger the instance the player gets close enough, and when they get close enough, I need the object to move away, however once it has been triggered to move, I need the object to continue moving, but the script to stop checking what the distance is anymore.
The script looks like this
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyOff : MonoBehaviour
{
public Vector3 rotateChange;
public Vector3 positionChange;
public float distanceFromObject;
public GameObject character;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
Use flags instead of writing your logic in the if statement :
public class FlyOff : MonoBehaviour
{
// fields removed for more readability
// use a flag that's set to true/false
private bool isCloseEnough = false;
void Update()
{
distanceFromObject = Vector3.Distance(character.transform.position, this.gameObject.transform.position);
print (distanceFromObject);
// set the flag to true when player is close enough
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
// even if the player gets far, the flag will remain true
if (isCloseEnough)
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
}
You can even apply the opposite logic to stop the object to move away when it has reach a certain distance :
if (distanceFromObject < 2)
{
isCloseEnough = true;
}
else if (distanceFromObject > SomeValue)
{
isCloseEnough = false;
}
If I understand correctly you could just add a bool flag and set it once you are close enough. Then you can start moving and skip further distance checks but keep moving forever.
private bool flyAway;
void Update()
{
if(!flyAway)
{
distanceFromObject = Vector3.Distance(character.transform.position, transform.position);
print (distanceFromObject);
if (distanceFromObject < 2)
{
flyAway = true;
}
}
else
{
transform.Rotate (rotateChange);
transform.position += positionChange;
}
}
In general: Avoid using print every frame! Even if you user doesn't see the log in a built app it is still causing overhead!

OverlapCircle won't find player

I've been trying to give to the NPCs of a game I'm working on the ability to "sense" when the Player is near. I've made this script and, for reasons unknown, the bool "found" stays false and, when I automatically set it to true, it reverts back to false (it still sends the player position to the goTo script, so at least that works). Does anyone know how to resolve this?
public class NPCLookForPlayerScript : MonoBehaviour {
public bool found; //player found
public float awareness; //how large is the circlecast
public int keepLooking; //for how much time, after losing sight of the player, he tries to keep on looking for him
public GameObject player; //variable to lock on to the player
int timer; //variable to decrement while player isn't in line of sight
NPCGoToScript goTo;
// Use this for initialization
void Start () {
goTo = GetComponent<NPCGoToScript>();
}
// Update is called once per frame
void Update () {
//he's always looking for the player
Collider2D coll = Physics2D.OverlapCircle((Vector2)transform.position, awareness);
//if the player is found, keep looking for him
if (coll.gameObject == player)
{
found = true;
timer = keepLooking;
}
//if the player was found,
if (found)
{
timer = timer - 1; //less time to look for the player
goTo.newPosition(player.transform.position);
}
//if the player is out of sight for too much time, stop looking for him
if (timer <= 0)
{
found = false;
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.green;
Gizmos.DrawWireSphere((Vector2)transform.position, awareness);
}
}
Since someone has made me notice that the values of the variables aren't written in the code, let me be clear: I'm working on unity, this script has public values that can be modified by the inspector, which is very usefull since this script has to be used for different kinds of NPCs. So the values are not zero. awareness = 5f and keepLooking = 20. The player field does have the player GameObject.
You are never setting initial values for any of your fields :).
timer is declared but never set to anything so essentially it equals 0. Also awareness (your radius) is never set either so is 0.0f by default.
So your code...
Executes the Physics2D.OverlapCircle with a radius of zero (i.e. no circle)
Skips the logic in the coll == player statement cause there is no circle to collide with the player.
Skips the logic in the found statement cause found of false
Then executes the logic in the timer <= 0 statement cause the timer is 0 (was never defined)
Continues to set found to false every frame, forever and ever and ever, into the long good night
The player getting the the transform data should only happen whe you manually set found to true in the inspector and then only for one frame cause it reverts back to false in the very next statement.
You need to give your fields value greater than 0.

Unity - C# : how can I use a value in different scenes?

I have made my first Unity3D game. It's a racing game with the infinite road. I made some pickup coins too.
the problem is when I pick up the coins and then I go to shop menu to see whether it's added to the total coins or not, it is not added till I play another round and then the coins i collected the round before will be added.
It's like a delay or something.
Is it because I don't know how can I use a value in different scenes? or it,s something else.
someone told me to use PlayerPrefs, I used it in my scripts but for this, I mean the count of coins, I don't know how to use it.
The script bellow is my player script in which I count the pickup coins and some other things related to the player. I only bring the Ontrigger and count related codes.
void Start () {
CameraAnimation = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Animator>();
_GameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
_AudioSource = GetComponent<AudioSource>();
count = 0;
SetCountText ();
}
void OnTriggerEnter(Collider other){
if(other.gameObject.tag == "Coins") {
count = count + 1;
SetCountText ();
}
}
public void SetCountText(){
CountText.text = count.ToString ();
}
The code below is my calculateCoin in which I add a count of collected coins to the previous value and show the total in shop scene textbox:
public class CalculateCoin : MonoBehaviour{
// Use this for initialization
public static int Coin = 10000;
public Text ShowCoin;
void Start () {
ShowCoin.text = PlayerPrefs.GetInt("Coin").ToString();
Coin =PlayerPrefs.GetInt("Coin")+Player.count;
PlayerPrefs.SetInt ("Coin", Coin);
}
}
First Option,
You can add 'DontDestroyOnLoad' for specific GameObject that include data as container. so It could be used as shared data between scene.
or Make 'Singleton' class and use it as data container it might be option.

Creating Simple Endless Moving Platform with cubes

Trying to create simple endless moving platform with 3 cubes of scale 70 on z(Player will not move forward, will just move left/right). The RepositionPlatform script is attached to each platform/cube which is responsible for movement and checks the z position of each platform and if it is <= -100.0f, then position is changed to (0,0,200.0f).
Problem is sometimes there is a little gap between the platforms(cubes) or there is a little overlap which I don't want.
Platforms should be placed one after each other without any gap or overlap!!!
Can anyone help find the issue looking at the script or suggest any other better way ?
The script below is attached to 3 platform game objects!!!
public class RepositionPlatform : MonoBehaviour
{
private GameObject platformGO;
[SerializeField]
private float speed;
// Start is called before the first frame update
void Start()
{
platformGO = this.gameObject;
Debug.Log("In RepositionPlatform Start method - "+ platformGO.name);
}
// Update is called once per frame
void Update()
{
Debug.Log("In RepositionPlatform Update Method- " + platformGO.name);
platformGO.transform.Translate(Vector3.back * Time.deltaTime * speed);
Transform platformTransform = platformGO.transform;
if(platformTransform.position.z <= -100.0f)
{
platformTransform.position = new Vector3(0,0,200.0f);
}
}
}
Probably because speed is a floating point value. You should read up on them if you haven't already.
Long story short, you aren't accounting for how far below -100 the value might have gone, you're just resetting it.
If you translate it instead, you will preserve any extra distance beyond -100 that the transform might have gone.
Try this instead:
If (transform.position.z < -100){
transform.Translate(new Vector3(0,0,200));
}
Edit
Should be Z value, not X

simple score system in unity5

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class score : MonoBehaviour {
public int ballValue;
public Text scoretext;
// Use this for initialization
void Start () {
ballValue = 0;
}
// Update is called once per frame
void Update () {
scoretext.text = ballValue.ToString();
}
void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.tag == "bucket")
{
ballValue = ballValue + 1;
}
}
}
ok guys what am i doing wrong over here ,i am a beginner.what i am trying to achieve here is i want my ball to fall down to the bucket and get 1 point or score ,my ball has a actual circle collider and a rigidbody and my bucket has box collider which is a trigger and both of these are prefabs which is being used multiple times in the games just in case if anyone want to know.so can anyone tell me what i am doing wrong hereor can someone guide me to the right tutorial .thank you
(after playing with it for a whilei am able to get 1 point it does not increase and i am getting this error)
Object reference not set to an instance of an object.
and it refers to this line.
void Update () {
scoretext.text = ballValue.ToString();
}
ok guys i just found the real problem,as i said the bucket is a prefab which randomly generating like the pipes in flappy bird, so after creating my core system i drag and drop the text ui into the given place ,and i apply the changes and delete that prefab and when i go back to asset and check that prefab the given place for text says none.so how can i link the text ui directly to the script so it wont dlete it self.
A way to get around this, is having a static class that holds the score and then have your buckets invoke a method in the static class to increase the score.
All you have to do then, is have an empty game object with the static class attached.

Categories

Resources