2
votes

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.

2
Have you only attached this script to a single GameObject, or is it possible you have multiple GameObjects that have an instance of this script attached? Also, which method did you place this code in?crush
Just checked and its only attached to three which are needed. The really odd thing is, is that it worked not too long ago.user5046043
I'd really need to see the entire script, or at least know the method on your MonoBehavior that you've placed this snippet in. Also, if it's attached to multiple GameObjects, despending on what method you've placed this in, the same code could be running for all three objects per frame. Just going to need more information about your script, and how it's configured in order to help!crush
Okay, so you are saying you have attached this Score MonoBehavior script to 3 GameObjects, right?crush
@Frohlich It's a static int, so no. They will be influenced by each instance because that value is shared across all instances. Update() gets called per frame, for each instance of the script. There are 3, so on each frame, that logic is getting called 3 times.crush

2 Answers

3
votes

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:

  1. 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.
  2. 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.
  3. 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.
0
votes

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!
    }
}