Disable buttons after click – in progress

Eventually I want to disable buttons after students have done them a lot, but right now I want to just learn how to disable buttons at all.

This thread has a good script thing:

public void BtnOnClick() { gameObject.GetComponent<Button>().interactable = false; }

This too.

I tried :  gameObject.GetComponent<Button>().interactable = false;

Buuuuut it’s not saving it as disabled when it comes back to the StudentMenu, because it’s resetting every time.  Huh.  (Doublechecked by disabling the scene change, and it’s disabling the buttons as long as the scene doesn’t change!  Oof.)

It might be that I need to make a list of all the buttons that should be disabled in the GM, add to it every time a button get clicked, and then apply that to the StudentMenu set up.  #todo – Eventually I’ll change it to only disable buttons once students are doing really well on a thing (or to disable buttons that are too advanced, a la “unlocking” levels).

Also #todo – I need a review / matching game at the end of these things, to really see how things are going. (This has been Drag&Drop-4-26, moving on to Drag&Drop-4-26b to make changes to the GM.) (#to do – match up blog posts to game versions?)

I’m going to leave this one till later – I think it’s more important that I set up exporting the data from the game, because before I put many parameters on how kids can use the game, I need to know what they actually do with it.

 

Working on buttons – same content across levels

So a problem I’ve been having – that the below returns objects in a random order:

GameObject[] buttons = GameObject.FindGameObjectsWithTag(Button);

        foreach (GameObject button in buttons)
        

is apparently a known thing, and can’t be controlled (as discussed in this thread).  So that means  if I want it left to right, I need to retrieve the children of the Canvas, instead of getting objects tagged as “Button”.  Grrr.

I don’t think I actually need to do this, after all, because what I’m actually going to do is instantiate each of these buttons from prefabs. Easy peasy.

ARE YOU KIDDING ME, PREFABS DON’T SAVE THE MANUAL “ONCLICK” THING INSIDE BUTTONS?  ARHGGHGH WHAT IS EVEN THE POINT OF THAT THEN WHY NOT JUST SCRIPT EVERYTHING.

Grrrr.  Oh.  I see.  It loses that connection because it was inside the Editor that it was connected to the Main Camera’s script.  So.. do I need a prefab of the Main Camera or something?   Cool.  Did that, updated the prefabs from the buttons to connect to the Main Camera prefab (this feels a little cheaty slash convoluted but oh well?), and now things work great.

       foreach (string level in levels) // yield sign says level isnt used ...? 
        {

        // everything down here is totally redundant but it works so oh well 
            if (xMult == 0)  
            {
                yMult = yMult1;
                xMult = 3;
                xSpace = 0; 

                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
                GameObject button = Instantiate(buttonLearn, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                xMult += 1;
                xSpace += 160; 
            }
            else if (xMult == 2)
            {
                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
                GameObject button = Instantiate(buttonPractice, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                xMult += 1;
                xSpace += 160; 
            }

            else
            {
                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
                GameObject button = Instantiate(buttonShowOff, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                xMult += 1;
                xSpace += 160; 
            }

So now it’s making buttons in the right color, prefab, order, etc.  Now I need it:

  • to look prettier / be better spaced
  • not change levels until the row is done  – that’s something about the name script
  • be well-labelled for kiddos (clear about how many points each button is)
  • have levels that make more sense for learning early concepts

Alright I made it so it doesn’t change levels till row is done, but now things are SERIOUSLY buggy.  It’s not resetting because of something with the Notebook/Trash entries.  Doesn’t seem to have anything to do with the code I put into NameButtons():

            buttonSpacer += 1;
            if (buttonSpacer == 3)
            {
                buttonCounter += 1;
                buttonSpacer = 0;
            }

As always, that particular problem is caused by it not reading in the .txt correctly.  It’s something about converting the string to an integer.  Have no idea what it is about the Unity Editor that messes with that, but … oh well.  (#todo – continue google searching for why this is happening.  Someday I’ll find the answer!)  Fixed it by changing what .txt file it was reading in.  Le sigh.

This one restructures it so that there’s space between the rows and the columns.

  void CreateButtons()
    {

        GameObject canvas = GameObject.Find(Canvas);

        // pull in info 
        TextAsset txtFile = (TextAsset)Resources.Load(PuzzleBotGoogleLevel, typeof(TextAsset)); // redundant :(  lots of scripts do this ... condense? put it in GM? 
        string[] levels = txtFile.text.Split(\n); // #todo why does \r not work? 

        // space buttons out on screen 
        float bufferX = Screen.width  50f;
        float bufferY = Screen.height  100f;
        float baseX = (bufferX / 3f); 
        float baseY = bufferY / 3f; 

        int xMult = 0; // set it to 0, if loop will convert to 3  
        int yMult = 4; 
        int xSpace = 0;
        int ySpace = 150 ;
       
        int buttonCounter = 0;

        foreach (string level in levels) // yield sign says level isnt used ...? 
        {

        // everything down here is totally redundant but it works so oh well 
            if (xMult == 0)  
            {
                yMult = yMult1;
                xMult = 3;
                xSpace = 0; 

                xSpace += 160; 
                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult + ySpace, 10f);
                GameObject button = Instantiate(buttonLearn, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                xMult += 1;

            }
            else if (xMult == 2)
            {
                xSpace += 160;

                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult + ySpace, 10f);
                GameObject button = Instantiate(buttonPractice, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                xMult += 1;
                 
            }

            else
            {
                xSpace += 160; 

                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult + ySpace, 10f);
                GameObject button = Instantiate(buttonShowOff, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                xMult += 1;
                ySpace -= 50; 
            }

        } 

    }

Let’s go back to my list above:

So now it’s making buttons in the right color, prefab, order, etc.  Now I need it:

  • to look prettier / be better spaced 
  • not change levels until the row is done  – that’s something about the name script 
  • be well-labelled for kiddos (clear about how many points each button is)
  • have levels that make more sense for learning early concepts

And I’m going to add:

  • label buttons appropriately (this is going to do a weird thing where it’s going to make it hard for me to call up the level, since I’ve been using button numbers to do that.  How about I keep the level number in there, to just keep things simple for now.)
    • This now just says level:  buttonText.text = levels[buttonCounter].Split(\t)[0];
    • This one says level and word family :  buttonText.text = levels[buttonCounter].Split(\t)[0] + \t + levels[buttonCounter].Split(\t)[2];    // switched to this so it just says level, not content
    • #todo – in playtesting, figure out what kids like in these buttons – how much info to give?

Just changed all the button prefabs to be 80 wide (kept 30 height).  Looks MUCH better.  I thought I had to change size through scale, but was wrong (yayy cuz scale affects text in unpleasant ways).

Okay so now I need to be true to my word and actually have scoreValue change inside each type of button. [#todo – playtest if it’s better to shower them with points, or if it’s better to be medium, or to have it be relatively conservative?]

I define a public int inside the GM that’s different for each type of Scene (which I already have if{} loops for so it defines where the audio happens).

    public void Audio()
    {
        Scene scene = SceneManager.GetActiveScene();
        if (scene.name == 1Learn)
        {
            audioList.Add(Onset);
            audioList.Add(Ending);
            audioList.Add(Answer);

            scoreValue = 5; 
        }
        else if (scene.name == 2Practice)
        {
            audioList.Clear();
            audioList.Add(Ending);

            scoreValue = 10;
        }
        else if (scene.name == 3ShowOff)
        {
            audioList.Clear();

            scoreValue = 15; 
        }

In PlayerFinalWord.cs, I changed it from defining scoreValue inside that loop to it pulling it in from the GM instance.

GameManager.instance.AddScore(GameManager.instance.scoreValue); 

Huh.  Just realized it’s only making 2 rows of buttons (6 total), where it the txt file actually has 9 rows (27 total buttons).  Why does it stop after the second row?   I only really want 3 rows, but it should be able to do 9.   Therrrrre we go, CreateButtons() was referring to the wrong .txt file.  Yay that was easier than I thought it would be!

#todo – draw a map of everything in the project, how scripts connect, etc.  Do some “official” documentation (that is, outside of this blog).

 

Recovering deleted prefabs

Welp, apparently I needed that prefab!  I have it in old versions – so turns out I can just drag and drop from another unity window to my current one.  I do this by having two versions of unity on my computer, so I can have two projects open at the same time to check changes, etc.  I’d rather they both be the most updated version of Unity – #todo – look into if I can just download it twice.  Or if there’s a way to have multiple projects open without having two versions, cuz that seems silly.

So it turns out that prefab is on objects in the scene, and now instead of being named “1” and tagged “Onset”, they’re named Missing Prefab.  How do I plug the prefab back in?

OOH even better.  I took the whole set of objects from the scene in the old version (4 gameobjects nested under an empty gameobject, “Onsets”), made Onsets a prefab, and then moved Onsets into the prefabs in the new version.  Then I put that prefab back into the scene, and voila!  All happy again.

There were multiple other resources I glanced through, but didn’t feel like this was necessary to do through scripting.  For future reference, here were some things that may or may not be helpful:

http://answers.unity3d.com/questions/1066738/fix-missing-prefab-issue.html

http://www.unifycommunity.com/wiki/index.php?title=ReplaceSelection

https://bitbucket.org/thisishydra/hydracommon/src/master/Scripts/Editor/Windows/ReplaceGameObjects.cs?at=master&fileviewer=file-view-default

Multiple tags for game object – in progress

So I don’t know if this is good or bad form, but I’d really like to be able to tag all of the gameobjects that should have audio played as “Audio.”  The other option is to specify each one, which would be to specify these tags in the script : “Onset,” “Ending,” and “Answer.”

Honestly maybe I’ll just do that to start.  Tagging them with “Audio” seems a little redundant.   Ohhh I remember why I can’t do it that way.  I want to tag the objects themselves in the scene – I don’t want the script to have to do it, or else I’ll have to have different scripts for every type of audio scene, and that’s soooo redundant and clunky.

Okay so I went ahead and tried it the way I described above, even though it’s not what I eventually want.

This works.  A problem that I will encounter is that other parts of the script look for the audio retrieved in this.  MissingComponentException: There is no ‘AudioSource’ attached to the “2” game object, but a script is trying to access it.
You probably need to add a AudioSource to the game object “2”. Or your script needs to check if the component is attached before using it. PlayerCoroutine+<OnTriggerEnter2D>c__Iterator0.MoveNext () (at Assets/Scripts/PlayerCoroutine.cs:82)

Perhaps I still put the audio component on, but just don’t play it?  That might be the easiest so that I only have one of those if{} loops.

Oops no I’ll still need multiple if{} loops, because then it’ll still say the onset when it lists them out in the coroutine.

    // when object is clicked, play its sound 
    void OnMouseDown()
    {
        string[] audios = { Ending, Answer }; // this forces the scripts to be different, as opposed to having the tags be different
        foreach (string audio in audios)
        {
            
            if (gameObject.tag == audio)
            {
                PlayAudio();
            }
        }

    }

A horrible thing I could do is instead of having multiple tags on an object, I could have if{} loop at the beginning that defines audios[] by the Scene name.  That could work, and could make it so I don’t have to handtag everything.  That actually might be the best version!  Not sure if it’s best practice, or fast, or anything.  But … gonna try that.

Ugh I need if{} loops for every time I play a thing.  Huh.  I could do something pretty cheaty and load a blank sound if it’s not in audio, and then whenever it plays it, it still exists, but it just plays nothing.  Honestly I think that’s the easiest – only has one if{} loop, and as long as I have a comment documenting why, it shouldn’t confuse any possible future programmers. (right?)

Okay so I just did this, and it works BEAUTIFULLY.   Now just to set up the audios[] definitions in each script that needs it – or … maybe make it accessible from game manager?  Not really sure … but gosh that sounds nice so I only have to define it once, then just put the if loops in what sound file it retrieves in PlayerCoroutine.cs and PlayerFinalWord.cs.

Okay gonna give that a go.

To get the name of the active scene (from this thread):

  1. Scene scene = SceneManager.GetActiveScene();
  2. scene.name; // name of scene

Also this is a good reason to have objects have their audio added in the same way, so that I only have to have one if{} loop for onsets & codas.  I’ll still need a separate one for Answer, cuz that audio gets added in the middle of the game (depending on what letters are put together).

Having the problem I often have, which is that I still don’t quite understand how to make list (#todo).   If I put this at the top,

public string[] audios;

with this in another function,

 

        if (scene.name == 2Practice)
        {
            audios = {Ending, Answer}; 
        }

Then I get this error: Assets/Scripts/GameManager.cs(53,22): error CS1525: Unexpected symbol `{‘

This is because I apparently desperately want to add multiple items to a pre-made list in one line.  Haven’t seen any solutions I love (usually they involve making more functions), so I’m just gonna do it one line at a time.

Cool this is working now, and it’s working calling it from GameManager.  Buuuuut just got a fatal error that stopped the game and had to quit Unity overall (it froze in playmode):

!attach.depth || !attach.depth->backBuffer

This thread thinks maybe it’s a 3rd party asset.  The only one I have in this is CVS2Table, and I’m not using it, so I’ll just delete that.

Ah, just realized it’s not that my if{} loop is working, it’s that it’s only attached to Onsets, and all of them all going through the else{} where it assigns the blank .wav.  Sigh.

Okay, apparently I can’t have an OnClick script attached to a button AND manually enter in what it should do OnClick.  This is sad and gives me an eye twitch.  I want both because I want three columns of buttons – 1st column takes you to 1Learn scene, 2nd column takes you to 2Practice scene, 3rd column takes you to 3ShowOff scene (this was being done in the button’s manual OnClick thing).  But they still need to get the level from the button, which is done through the added script.

Wait nevermind.  Both things TOTALLY work.  YAYYYY.

To check if an item is in a list, List.Contains(item) (from this thread).   #todo – check if I have any foreach() loops that should actually be if (List.Contains(item)) .

 

Fix finishing a level

Now that I’ve got my massive playtesting grid set up, something’s gone wrong with the game recognizing the last move in a level – it’s not accepting either answer, as if it doesn’t have the dictionary entry in there, or as if it doesn’t have a collider.

OHH it’s not just the last move, it’s the last dictionary entry in the .txt file. So this is just (lol just) a formatting problem.  Bane of my existence, formatting …

Alright, so the key isn’t being found in the dictionary.  Huh. When I do this, it finds the right value.

Debug.Log(Dictionary is  + dict.WordDict[zug]); 

I’m … kinda at a loss.  It’s clear that the moment things went wrong was when I went copy/paste crazy in the Google sheet (although I suppose there’s a chance that it happened earlier, with button-y stuff).  Gonna go restore my old version, bring that back into Unity, and see if it fixes it.  Then I’ll know a little more about when this problem started.

Ooookay.  So maybe the problem isn’t formatting.  The old version still doesn’t work for these last dictionary entries.  Something about button gridding messed it up.  How?  No clue.  Let’s go back and look …

Huh.  So “zug” (and all of the other dictionary values at the end of a row) are all clearly in the game dictionary.  And yet somehow the game keeps reporting that they’re not in there!  When I added another key,value entry into the row, zug started working.  Unclear how it knows it’s in the dictionary, returns the value for “zug” when I type it in manually, but can’t return the value for thisText.text (which is equal to “zug”).  Something weird about the formatting, or about how it’s reading it in?  Can’t for the life of me see any differences, because when I have it Debug.Log(thisText.text), it returns “zug”.  Soooo.  Hmph.  Can’t see any differences between “zug” and “sug”, which works (because it’s earlier in the row).

I tried adding the character separator to the end of each row, but that went poorly (as it had the last time I’d done that).  So… the way to fix this appears to be adding a dummy to the end.  That isn’t much fun!

So I just added a new column to the right of the dictionary entries.  Now it’s recognizing them.  I guess it’s something about the ‘\t’ splits?  I’m confused because this wasn’t a problem before, but … ah well.  Now it works.  #todo – at some point figure out why/how and do something better than making a dummy column at the end to fix it.

Oh noooo I spoke too soon.  The other rows aren’t working.  Only the first row works.  Before, all rows worked except for the last dictionary entry.  Now, only the first row works.  This is fascinating / deeply annoying.  Walking away for now.

Trying again.  Using this thread to see what’s actually in the dictionary.

 foreach (string key in attachStats.Keys)
 {
     float val = attachStats[key];
     Debug.Log(key + " = " + val);
 }

I cannot for the life of me figure out what’s going wrong.  I moved these away from Start() and into the collider.   Got notebook entries working again, but feels stupid.  [Put these back in Start() and that’s working fine now, bc of solution tmrw]

if (other.tag == Notebook)

            {
                notebookEntries = GameObject.Find(NotebookEntries); // these used to work in Start() but stopped

 

 

OMG HOLY COW the problem was the debugging I’d been doing about “zug” not working –

//        Debug.Log(Dictionary for zug is  + dict.WordDict[zug]); // okay so it has it in it here ... 
//        Debug.Log(Dictionary for sug is  + dict.WordDict[sug]); // okay so it has it in it here ... 

As soon as I took those out, everything worked again.  SIGHHHH.

Okay.  So now everything works, I was able to take out the dummy variable, I’m not even entirely sure what was wrong to begin with.  Welp ! Just relieved it works again.  Sometime maybe I’ll go back through these notes and figure it out (#todo).  I’m gonna risk it and try to add a whole bunch more levels and see if things still work.

NOOOO as soon as I add the other levels it stops working.  What is going on with this??

Something is going on w the formatting.  When I went into the editor and made a linebreak and deleted it again, it started working.  Sigh.

So apparently even the elaborate system from GoogleSheets to TextEdit to Unity is just *not* working.  Blargle. That being said, I also can’t type new rows straight into the Editor.  It doesn’t read in integers correctly.

So now I’m going from TextEdit to Unity, and then manually hitting “enter” and then “delete” after each line.  That works.  And sucks!  I guess I just do a template that I can edit from the editor?  Just did that, works well now.  I guess I could also try a different character for linebreak, like “$” (#todo).  I’d tried that before, but I think there were other problems going on with it.   At least I got it working again!

Space buttons evenly

I want my automatically created buttons to be spaced evenly across the game space.

So now the part of the tutorial that I kinda ignored is going to be helpful (the RogueLike Board Manager).  Hmmm, actually, that’s not that helpful, because I’m not creating a grid first – I just have a single-color background right now.  To the google!

This one’s cool because it spaces things evenly in a circle.  I want it in a grid (so maybe I have to act like I have a grid anyhow … hmmm.).

This person has the same problem as me, but no one chimed in.

This thread talks about making things along a path.

Looks like there’s a $20 solution in the asset store.   Unclear if it would interact well with my instantiating. Okay so it looks like I do need to pretend I have a grid.

  1. First question – can the script figure out how big the gamespace is?

Ooh – this looks useful.  Oh gosh.  GUI coordinates start at (0,0) in the upper left (go to (Screen.width, Screen.height) in the lower right), vs. screen coordinates start at (0,0) in the lower left.

So I’m using GUI coordinates.  I want to start at (0,0), go all the way across to (0, Screenheight), etc.  Maybe I use the BoardManager from RogueLike, but just replace the coordinates.

I was lazy and am trying to just do it myself.

Vector3 buttonPosition1 = new Vector3((float)Screen.width/2f, (float)Screen.height/2f, 10f);

This is NOT centered along any axis.

For when I want to know about more specific things: http://forum.unity3d.com/threads/get-screen-dimensions.110777/

Huh.  When I put it at (0,0,0), it’s dead center.  This doesn’t make much sense.  Apparently this is what I should’ve expected?  From this guy’s pictures …  http://answers.unity3d.com/questions/994599/gameobject-with-000-position-not-being-in-the-cent.html

Maybe this has something to do with how my camera is viewing things.  When I change it to world space, it can’t see any buttons.   Or maybe something to do with being childed to a canvas?  Or maybe it’s working exactly how it should.

This is top left:  Vector3(400,250,0)

Ah HA!  I needed to tell it what space I was defining vectors in.  Turns out “Camera.main.ScreenToWorldPoint” was actually kinda important.  Now the below is showing dead center. 

Vector3 buttonPosition1 = Camera.main.ScreenToWorldPoint(new Vector3((float)Screen.width/2f, (float)Screen.height/2f, 10f)); //(float)Screen.height

But … the /2f on both of them doesn’t matter.  If I take it out, it’s still dead center.  If I change it to /4f, it’s still dead center.  I need to quarter my x axis, so …

This must have something to do with World Space vs other types of space?  Or orthographic vs. perspective? (My camera’s currently an orthographic one.)

This does the same thing :

ScreenToViewportPoint

This is for calculating camera bounds.  What I did below still puts it dead center.

        float horzExtent = Camera.main.orthographicSize * Screen.width / Screen.height;
        Debug.Log(horzExtent);
        float vertExtent = Camera.main.orthographicSize * Screen.height / Screen.width;
        Vector3 buttonPosition1 = (new Vector3(horzExtent, vertExtent, 0f));

Sigh.

And more.   This uses a linecast.

#todo – add this to my thing about preventing player from leaving bounds

 

This works to get the lower left and I have no idea why.

Vector3 buttonPosition1 = new Vector3(((float)Screen.width / 2f)*-1.5f, ((float)Screen.height / 2f)*-1.5f, 10f);

This works to get top right and I have no idea why.

Vector3 buttonPosition2 = new Vector3(((float)Screen.width / 2f)*1.5f, ((float)Screen.height / 2f)*1.5f, 10f);

Also it looks like I don’t need those pesky (float)s in there.

OHHH this makes more sense maybe?

Vector3 buttonPosition1 = new Vector3((Screen.width/4f)*-4f, (Screen.height/4f)*4f, 10f); // top left

This divides the width by 4, then goes to the 4th position to the left.

If I give it a little buffer on each side, it fits better:

        float bufferX = Screen.width  150f;
        float bufferY = Screen.height  50f;

Not sure how to automate that so I don’t have to do it by eye, but that’ll be a later #todo.

YESSSS: this one does a cute little stacking thing.  Not at all what I wanted, but it’s getting closer!

    void CreateButtons()
    {
        GameObject canvas = GameObject.Find(Canvas);

        // pull in info 
        TextAsset csvFile = (TextAsset)Resources.Load(PuzzleBotGoogle, typeof(TextAsset)); //PuzzleBotData2.txt
        string[] levels = csvFile.text.Split(\n); // #todo why does \r not work? 

        float bufferX = Screen.width  150f;
        float bufferY = Screen.height  50f;
        float baseX = bufferX / 4f; 
        float baseY = bufferY / 4f; 

        float xMult = 4; 
        float yMult = 4; 
       
        int buttonCounter = 0;
        foreach (string level in levels)
        {
            Vector3 buttonPosition = new Vector3(baseX * xMult, baseY * yMult, 10f) ;
            GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
            button.transform.SetParent(canvas.transform, false) ;    
            buttonCounter += 1;
            xMult += 1;
            yMult += 1;
        } 

    }

Ah.  That’s because it’s just counting up for both xMult & yMult at the same time.  I need it to go through all of xMult first, then to iterate on yMult.

Now I need to account for the fact that buttons are overlapping.  Add in how wide buttons are. My buttons are 160 wide, so this fixed that (#todo – would be nice for script to know how wide button is without me checking):

        foreach (string level in levels)
        {
            Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f) ;
            GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
            button.transform.SetParent(canvas.transform, false) ;    
            buttonCounter += 1;
            Debug.Log(xMult);
            xMult += 1;
            xSpace += 160; 

        } 

Made yBuffer a little bigger so you can still see the score in the top right.

Now to iterate across yMult … This does *not* work.  It just marches on across the x axis forever.

        foreach (string level in levels)
        {
            Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
            GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
            button.transform.SetParent(canvas.transform, false);    
            buttonCounter += 1;
            Debug.Log(xMult);
            xMult += 1;
            xSpace += 160; 
            if (xMult == 4)
            {
                yMult -= 1;
                xMult = 4;
            }

        } 

Moving the if loop up above everything else also does not work.

 

        foreach (string level in levels)
        {
            if (xMult == 4)
            {
                yMult -= 1;
                xMult = 4;
            }

            Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
            GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
            button.transform.SetParent(canvas.transform, false);    
            buttonCounter += 1;
            Debug.Log(xMult);
            xMult += 1;
            xSpace += 160; 

        }


    }

 

Not even an if else loop works.

 

        foreach (string level in levels)
        {
            if (xMult == 4)
            {
                yMult -= 1;
                xMult = 4;

                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
                GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                Debug.Log(xMult);
                xMult += 1;
                xSpace += 160; 
            }
            else
            {
                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
                GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                Debug.Log(xMult);
                xMult += 1;
                xSpace += 160; 
            }

        } 

 

Becaaaaause it turns out xMult is only going up till 0.  Also I need to reset xSpace to 0.  Now it is beautiful and perfect.

grid!

        foreach (string level in levels)
        {
            if (xMult == 0)
            {
                yMult = yMult1;
                xMult = 4;
                xSpace = 0; 

                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
                GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
                xMult += 1;
                xSpace += 160; 
            }
            else
            {
                Vector3 buttonPosition = new Vector3(baseX * xMult + xSpace, baseY * yMult, 10f);
                GameObject button = Instantiate(buttonTemplate, buttonPosition, Quaternion.identity) as GameObject;
                button.transform.SetParent(canvas.transform, false);    
                buttonCounter += 1;
//                Debug.Log(xMult);
                xMult += 1;
                xSpace += 160; 
            }

        } 

Automatically create new buttons for each level

At first I was going to keep a button template inside the MainMenu hierarchy, find that with a tag, and then replicate that.

Then I remembered that that’s basically doing a sloppy prefab, so I’m doing that instead, and pulling the prefab into a slot in “NameButton.cs” that is on the Main Camera in that scene.

So now in Awake(), it’ll first make the buttons, then it’ll name them (or maybe at the same time?  Haven’t gotten into the nitty gritties yet).

It’s working, but I can’t see the buttons (the rest of my script is even filling in the text for them!)

     void Awake()

    {
        
        // pull in info 
        TextAsset csvFile = (TextAsset)Resources.Load(PuzzleBotGoogle, typeof(TextAsset)); //PuzzleBotData2.txt
        string[] levels = csvFile.text.Split(\n); // #todo why does \r not work? 

        // find out how many levels there are
        Debug.Log(levels.Length);

        foreach (string level in levels)
        {
            Instantiate(buttonTemplate, new Vector3(150f, 200f, 0f), Quaternion.identity);
        } 

I think the reason it’s not working is that they’re not child objects of the Canvas.

Reddit has a few suggetstions : https://www.reddit.com/r/Unity3D/comments/2v58mz/how_do_i_get_an_gameobject_to_instantiate_as_the/

Apparently the phrasing I should use is “New GUI objects created dynamically from scripting.”

Here we go:

        foreach (string level in levels)
        {
            GameObject button = Instantiate(buttonTemplate, new Vector3(150f, 200f, 0f), Quaternion.identity) as GameObject;
            button.transform.parent = GameObject.Find(Canvas).transform;    
        } 

Note that I have to explicitly call out that the new GameObject, button, is a GameObject at the end of the Instantiate() command (casting it as GameObject).

Still can’t see any of the buttons, but they’re childed correctly.  Prefab is alright, cuz if I drag it back into the hierarchy it’s happy (although when I click it, it doesn’t do anything, but I think that’s a problem with the scripting).

This official tutorial says to use Transform.SetParent().  Apparently this works better (as talked about here).:

“Hit this same issue and just to chime in…

Doesn’t scale prefab to UI scale:
go.transform.parent = this.rows.transform;

Doesn’t scale prefab to UI scale:
go.transform.SetParent(this.rows.transform);

DOES scale prefab to UI scale :):
go.transform.SetParent(this.rows.transform, false);”

Okay so now I can see my buttons when they appear. They no longer are getting their text filled in, so maybe I need separate functions to do that.

Now the below is working in everything EXCEPT the buttons actually clicking changing scenes.  They’re even labelling left to right now! (Because that’s how I’m instantiating them, I think.)

So the important thing to note – the prefab doesn’t work as a clicky thing when I pull it back into the hierarchy manually.  So it’s not necessarily that there’s something weird with my script – there’s something funky going on with the prefab interacting with the UI system.

Is it because I’ve turned them into GameObjects?

I got all excited because maybe it was that NameButtons() was in Start() function instead of the Awake() function.  That didn’t fix it:(

Even tried creating a new UI button and putting the GetButtonLevel script on it.  No go!  Something’s really gone weird.

OMG it’s because I deleted the damned Event System out of the hierarchy. Note to self – write down when I delete random things!  Thank goodness I kept a copy before I started doing major messing with stuff, so I could refer back to it!

Woooooo now it works great!  Next step – look back at the Unity tutorials about getting a grid system from the board (BoardManager in RogueLike, I believe), and use something like that to place buttons automatically instead of defining each one.

using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Linq; // so I can convert to int? nah

public class NameButton : MonoBehaviour {

     public GameObject buttonTemplate; // pull prefab in 
     private TextAsset csvFile;
     private string[] levels;

     void Awake()
    {
        CreateButtons();

    }

    void Start()
    {
        NameButtons();
    }

    void CreateButtons()
    {
        GameObject canvas = GameObject.Find(Canvas);

        // pull in info 
        TextAsset csvFile = (TextAsset)Resources.Load(PuzzleBotGoogle, typeof(TextAsset)); //PuzzleBotData2.txt
        string[] levels = csvFile.text.Split(\n); // #todo why does \r not work? 
        Vector3 buttonPosition1 = new Vector3(400f, 200f, 0f);
        Vector3 buttonPosition2 = new Vector3(200f, 200f, 0f);
        Vector3 buttonPosition3 = new Vector3(0f, 200f, 0f);
        Vector3 buttonPosition4 = new Vector3(200f, 200f, 0f);
        Vector3[] buttonPositions = { buttonPosition1, buttonPosition2, buttonPosition3, buttonPosition4 };
        int buttonCounter = 0;
        foreach (string level in levels)
        {
            GameObject button = Instantiate(buttonTemplate, buttonPositions[buttonCounter], Quaternion.identity) as GameObject;
            button.transform.SetParent(canvas.transform, false) ;    
            Debug.Log(buttonPositions[buttonCounter]);
            buttonCounter += 1;

        } 

    }

    void NameButtons()
    {
        // now do things to the buttons
        int buttonCounter = 0;
        GameObject[] buttons = GameObject.FindGameObjectsWithTag(Button);

        TextAsset csvFile = (TextAsset)Resources.Load(PuzzleBotGoogle, typeof(TextAsset)); //PuzzleBotData2.txt
        string[] levels = csvFile.text.Split(\n); // #todo why does \r not work? 

        foreach (GameObject button in buttons)
        {
            
            Text buttonText = button.gameObject.GetComponentInChildren<Text>();
//            Debug.Log(buttonText.text);
            buttonText.text = levels[buttonCounter];    
//            string buttonTextContent = buttonText.text;
            string[] buttonTextSplit = buttonText.text.Split(\t);
//            string level = buttonTextSplit[0];
//            Debug.Log(level);

            buttonCounter += 1; // its really interesting, it does it right to left 
//            int levelNumber = int.Parse(level); // convert level to integer
//            Debug.Log(levelNumber);

        }
    }

}

Access GameManager.instance

Decided to have the LevelDone() function be defined in GameManager but actually happen in PlayerFinalWord().  This makes it so that I can wait until feedback for the last word is complete before it switches back to the Main Menu.  As is often happening for me, I forgot that GameManager isn’t what I’m referring to, but instead *GameManager.instance* (because of singletons etc.)   Below worked:

GameManager.instance.LevelDone();

 

Change scene using key click

Now, also for playtesting, I need to make it so I can return to the Main Menu by hitting a specific key or set of keys (I should really change my scene names so it’s not “Main Menu” vs. “Main” … #todo). I don’t want a button because 8 year olds like hitting buttons, and I want the functionality of the game to be such that they can only finish that level – not skip around themselves.

Eventually I’d love for it to change the color of a button when it’s been clicked (#todo) – but I’m not super sure that’s necessary right now.  I can just have a paper worksheet and keep their “score” on it with stickers until I have more rewarding things in the game (#todo).

This seems useful, with slightly outdated functions but easily convertible.  I’ll put this on my Main Camera in my Main scene, and set it to take it back to the Main Menu scene.   (#todo – should I put it somewhere other than Main Camera?)

function Update(){

If (Input.GetKey(KeyCode.F2)){ Application.Loadlevel(“level2”) } else if (Input.GetKey(KeyCode.F3)){ Application.Loadlevel(“level3”) }

}

my updated version (that doesn’t work:

        if (Input.GetKey(KeyCode.M))

        {
            SceneManager.LoadScene(MainMenu);
        }
    }

Well I was all excited and it didn’t work at all!  (#todo – look through this, looks interesting but a bit unrelated right now: http://answers.unity3d.com/questions/934527/ui-button-click-through-keyboardcode.html)

OH – turns out it DOES work, it’s just the scene hadn’t been loaded into the build yet.

Scene ‘MainMenu’ (-1) couldn’t be loaded because it has not been added to the build settings or the AssetBundle has not been loaded.
To add a scene to the build settings use the menu File->Build Settings…

I’ve come across this problem before and know just what to do ! (#todo – add link to post)

Click button to load scene while incorporating level

This isn’t for the user, but for me.  I need a version of my game that I can adapt to kid user and pick different levels for them.  (Although I’ll have some sort of main menu for the user, as well.  That’s just not what I’m working on right now.)

This is a great explanation.  The only thing I can see won’t work in Unity 5 is Application.LoadLevel(), which is now deprecated.  Also note that you don’t have to explicitly make a Canvas anymore – UI elements like buttons come with them.  So use this instead (talked about in my post here):

SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);

Ah.  It also doesn’t work because Buttons in Unity 5 actually work differently.  Here’s a great official tutorial.  The part I care about now is the Event System, at 8:30 (although the changing color when you put the mouse over it, and changing color when you click it, is great too).  This is kinda all back to the I need to think about things as nouns as opposed to verbs thing.

Sadly, this other tutorial is outdated.  #todo – watch to see if there are things that are still useful.  Probably so!

Alright, so now I’ve got my button taking me back to my Main scene, where all the magic happens.  Lol.  But the problem I’m realizing now – I don’t have separate scenes for separate levels, because I call those elements into the Main scene using my .txt file.   What I really want is buttons corresponding to my .txt file that then take you to that the Main scene with those elements in it.  Uhh so now I need my .txt file to create the buttons.  Um.  Huh.

I am *not* confident that Unity was built with that in mind.  That being said, let’s go for it!

So first I started with my base script for the button changing scene, and just adapted it to make sure I could put whatever name I wanted into it.  Hmmm now that I think about it better that doesn’t fix anything because what I need is to access the .txt file, not to change what the scene name is.

public class ButtonNextLevel : MonoBehaviour 

{

    private string scene;

    public void NextLevelButton() // oh gosh how do I pass through from button to what I want it to load? 
    {
        scene = Main;
        SceneManager.LoadScene(scene); // do I need the .buildIndex part? 

    }

Okay so now to access the dictionary.  Maybe what needs to happen is the dictionary gets accessed in the button, *not* in the level.  (Or in both?)   And then I need to pass through what I got from the dictionary into the level.  I really don’t feel equipped for this.

Do I need to rewrite the function that changes the onset and ending letters so that it’s pulling them?  That way I can pass them through the script on the button, into the script inside the level.  I think?

So to start by making a copy of this, or else I’m at risk of ruining everything. (#todo – set this all up on Git so it’s easier to do this)

OH!  Maybe I could pull from the .txt file to make a different button for each level, and a script makes a new .txt file for each button within that gets used.   That could work.  Right?   Oh!  I wouldn’t even need a .txt file… I can just have a global static variable or list (lol great, really good form there)…

Maybe I could have a blank prefab object that has placeholders for each of these things, then gets filled up by the button, and then gets pulled into the level and the info gets extracted from it there?

This is way more complicated than I thought it’d be.  Huh. Turns out things that are easy-ish in python aren’t easy here…

 

New idea

Can I have multiple loader scripts for my GameManager?

 

This is exactly what I was looking for to change the text of the button:

ThisButton.GetComponentInChildren(Text).text = “testing”;

 

Buuut tha’ts not quite right bc it’s in javascript *and* it doesn’t super work with unity’s new button setting.

http://stackoverflow.com/questions/31220004/unity-5-change-button-text-created-with-unityscript

 

Alright I put my script onto my button explicitly.  This is how I’m changing its text:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class NameButton : MonoBehaviour {

    // Use this for initialization
    void Start () {
        Text thisText = gameObject.GetComponentInChildren<Text>();
        thisText.text = new;
    
    }

Not sure if I like this bc this’ll mean I have to load in the .txt here as well as the “gotolevel()” function.  But I can make this more concise when I have it functioning.  Ugh just realized that this way it’s harder to automate different text for each button.  Would require a different script for each button.  I am not thrilled about this option as it seems stupid.

Okay so I’ve got it in the thing that the button is pulling in, not a separate script.

Interesting note, as I go through each of the buttons in a foreach loop, it’s labelling them from right to left, instead of from right to left.  #todo – reorganize them so they go in that direction?  Or tell it to do left to right.  Huh.

Okay so now I’ve got it iteratively labelling the buttons.

    void Start()
    {
        int buttonCounter = 1;
        buttons = GameObject.FindGameObjectsWithTag(Button);

        foreach (GameObject button in buttons)
        {
            
            Text buttonText = button.gameObject.GetComponentInChildren<Text>();
            Debug.Log(buttonText.text);
            buttonText.text = buttonCounter.ToString();
            buttonCounter += 1; // its really interesting, it does it right to left 

        }

    }

Now I need to have the buttonCounter be the index on the linesplits in the .txt.

Woooo this works below:

 

    void Start()
    {
        int buttonCounter = 0;
        buttons = GameObject.FindGameObjectsWithTag(Button);

        TextAsset csvFile = (TextAsset)Resources.Load(PuzzleBotGoogle, typeof(TextAsset)); //PuzzleBotData2.txt
        string[] levels = csvFile.text.Split(\n); // #todo why does \r not work? 

        foreach (GameObject button in buttons)
        {
            
            Text buttonText = button.gameObject.GetComponentInChildren<Text>();
            Debug.Log(buttonText.text);
            buttonText.text = levels[buttonCounter];           
            buttonCounter += 1; // its really interesting, it does it right to left 

        }

    }

Okay so now it’s pulling in the text components I want.  Now I need it to load the correct level.

For the onbutton click thing, if I try to put “gameObject.name” in there, it returns “Main Camera”.  So how do I access the button that’s being clicked?

Intrigued by writing a separate script instead of using the OnClick() automated function, because I need to specify which button is being clicked.  I like that here.

  1. using UnityEngine;
  2. using UnityEngine.UI; // <-- you need this to access UI (button in this case) functionalities
  3. public class ButtonController : MonoBehaviour {
  4. Button myButton;
  5. void Awake()
  6. {
  7. myButton = GetComponent<Button>(); // <-- you get access to the button component here
  8. myButton.onClick.AddListener( () => {myFunctionForOnClickEvent("stringValue", 4.5f);} ); // <-- you assign a method to the button OnClick event here
  9. myButton.onClick.AddListener(() => {myAnotherFunctionForOnClickEvent("stringValue", 3);}); // <-- you can assign multiple methods
  10. }
  11. void myFunctionForOnClickEvent(string argument1, float argument2)
  12. {
  13. // your code goes here
  14. print(argument1 + ", " + argument2.ToString());
  15. }
  16. void myAnotherFunctionForOnClickEvent(string argument1, int argument2)
  17. {
  18. // your code goes here
  19. print(argument1 + ", " + argument2.ToString());
  20. }
  21. }

Gonna give this a shot.

Next day – Woooo this works.  Also looked at few other sites to wrap my brain around it:

http://answers.unity3d.com/questions/797772/example-of-setting-up-a-button-onclick-event-via-s.html

http://docs.unity3d.com/ScriptReference/UI.Button-onClick.html

Now I have two scripts.  One is “NameButton”, and I put it on my main camera in the Main Menu scene (I might be supposed to make an object for this … #todo – check this?)  This script goes through and finds all the buttons, then populates their text with the level details from the .txt file (this way I know what level I’m going into and its contents, for fast playtesting and switching around difficulties).  Its functionality happens in Awake(), so that everything gets labelled first.

The other script is “GetButtonLevel”.  This one is attached to each button, and has its functionality happen in Start(). That way it acts after the Awake() function in Main Camera’s “NameButton”, and all the buttons already have their text labels.  When I click on a button, it retrieves its text, identifies the level number by splitting the text up, converts the level to an integer, changes the global variable “level” to this level, then loads the Main scene using that level.  WOOOOOO

Text below (not cleaned up yet):

NameButton():

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Linq; // so I can convert to int? nah

public class NameButton : MonoBehaviour {

     Button myButton;
 
     void Awake()
     {
//         myButton = GetComponent<Button>(); // < you get access to the button component here
 
//         myButton.onClick.AddListener( () => {myFunctionForOnClickEvent(stringValue, 4.5f);} );  // < you assign a method to the button OnClick event here
//         myButton.onClick.AddListener(() => {myAnotherFunctionForOnClickEvent(stringValue, 3);}); // < you can assign multiple methods

//
//            myButton.onClick.AddListener(() =>
//            {
//                GetLevel(); // dont know this formatting but its cool? 
//            });
//
//        myButton.onClick.AddListener(() => GetLevel());
        int buttonCounter = 0;
        GameObject[] buttons = GameObject.FindGameObjectsWithTag(Button);

        TextAsset csvFile = (TextAsset)Resources.Load(PuzzleBotGoogle, typeof(TextAsset)); //PuzzleBotData2.txt
        string[] levels = csvFile.text.Split(\n); // #todo why does \r not work? 

        foreach (GameObject button in buttons)
        {
            
            Text buttonText = button.gameObject.GetComponentInChildren<Text>();
//            Debug.Log(buttonText.text);
            buttonText.text = levels[buttonCounter];    
//            string buttonTextContent = buttonText.text;
            string[] buttonTextSplit = buttonText.text.Split(\t);
//            string level = buttonTextSplit[0];
//            Debug.Log(level);

            buttonCounter += 1; // its really interesting, it does it right to left 
//            int levelNumber = int.Parse(level); // convert level to integer
//            Debug.Log(levelNumber);

        }
    }

}

 

GetButtonLevel():

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Linq; // so I can convert to int? nah

public class GetButtonLevel : MonoBehaviour {

     Button myButton;
     private Text buttonText;
     private int buttonLevel;
 
     void Start()
     {
         myButton = GetComponent<Button>(); // < you get access to the button component here
 
//         myButton.onClick.AddListener( () => {myFunctionForOnClickEvent(stringValue, 4.5f);} );  // < you assign a method to the button OnClick event here
//         myButton.onClick.AddListener(() => {myAnotherFunctionForOnClickEvent(stringValue, 3);}); // < you can assign multiple methods

//
//            myButton.onClick.AddListener(() =>
//            {
//                GetLevel(); // dont know this formatting but its cool? 
//            });
//
        myButton.onClick.AddListener(() => GetLevel());

//        GameObject[] buttons = GameObject.FindGameObjectsWithTag(Button);

        TextAsset csvFile = (TextAsset)Resources.Load(PuzzleBotGoogle, typeof(TextAsset)); //PuzzleBotData2.txt
        string[] levels = csvFile.text.Split(\n); // #todo why does \r not work? 

            
        buttonText = gameObject.GetComponentInChildren<Text>();
//            Debug.Log(buttonText.text);
        Debug.Log(buttonText.text); 
//            string buttonTextContent = buttonText.text;
        string[] buttonTextSplit = buttonText.text.Split(\t);
        string level = buttonTextSplit[0];
//        Debug.Log(level);

//        buttonCounter += 1; // its really interesting, it does it right to left 
//            int levelNumber = int.Parse(level); // convert level to integer
//            Debug.Log(levelNumber);

        
    }

     

     public void GetLevel()
     {
        Text buttonText = gameObject.GetComponentInChildren<Text>();
        string[] buttonTextSplit = buttonText.text.Split(\t);
        string level = buttonTextSplit[0];
//            Debug.Log(buttonText.text);
        buttonLevel = int.Parse(level); // convert level to integer
        Debug.Log(buttonLevel);
        GameManager.instance.levelCounter = buttonLevel; // buttonCounter; // alright this gets to level I need 
        SceneManager.LoadScene(Main); // do I need the .buildIndex part? 
     }
 
//     void myFunctionForOnClickEvent(string argument1, float argument2)
//     {
//         // your code goes here
//         print(argument1 + ,  + argument2.ToString());
//     }
// 
//     void myAnotherFunctionForOnClickEvent(string argument1, int argument2)
//     {
//         // your code goes here
//         print(argument1 + ,  + argument2.ToString());
//     }
 

}

Early programmer documenting coding problems & solutions – Unity3D / C#