1. Hey! Guest! The 36th GMC Jam will take place between February 27th, 12:00 UTC - March 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

Help with understanding Arrays

Discussion in 'Programming' started by Halas, Jan 9, 2020.

  1. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    So I am trying to learn Arrays, and it definitely is something that is hard to wrap my mind around.

    What I am trying to do is create an array for an RPG character to handle his levels and stats.
    So each level, the player will get one ability point to put into stats.

    What I have so far, is not much at all. haha, but please let me know if I am doing this correct and what my next steps would be.

    I have an object that if the player picks up, gets experience.

    if not instance_exists(other) exit;
    audio_play_sound(a_expr, 1, false);
    with (other)
    {
    experience += 1;
    if (experience >= max_experience)
    {
    level += 1;
    experience = experience - max_experience;
    max_experience += max_experience;
    Cabilitypt += 1;
    }

    now, for the array. Where and how should I set this up?

    I'd like it to be something like.

    If (Cabilitypt <= 1)
    {
    Levelup_script;
    }


    Levelup_script

    Code:
    /// Create Choices
    
    /// Character Stats 2D Array
    
    /// 0-Levels (50)
    /// 1-Vitality (Health, Stamina)
    /// 2-Intelligence (Mana, Spell damage)
    /// 3-Strenght (Melee Damage)
    /// 4-Dexterity (Ranged Damage)
    /// 5-Defence (Reduced Damage taken)
    
    /// Initialize
    
    for (var i = 50; i < array_height_2d(Player_Stats); ++i;)
       {
       for (var j = 6; j < array_length_2d(Player_Stats, i); ++j;)
          {
          Player_Stats[i, j] = -1;
          }
       }
        
        
    

    Now I need to add in, I think strings to draw the choices and for the player to select the choice. Am I heading in the correct direction?
     
  2. TheouAegis

    TheouAegis Member

    Joined:
    Jul 3, 2016
    Posts:
    7,434
    Is this array the BASE stats at every level? Or is it how many points leveling up grants at each level gain? If it's for neither of those, then you need to rethink your strategy. Even then, I'd still reconsider this and use an algorithm instead.

    Also your first for loop i think is wrong.
     
  3. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    No it would be an addition to the base stats.
     
  4. OblivionSkull21

    OblivionSkull21 Member

    Joined:
    Sep 26, 2017
    Posts:
    318
    So what am I gathering is:
    1.) Every level up, the player gets 1 ability point to spend.
    2.) The player can spend that 1 ability point on 1 of 6 abilities.

    You want to keep track of how many ability points each ability has, correct?
    Can the abilities have an unlimited amount of points, or do they have a max limit?
    What are the initial point values for those abilities, 0?

    What I would do is initialize the array with all the initial point values for each ability. Let's have "a" be the ability itself and "ability_selected" be the ability selected to add a point to. Let's also say you start off with no points in any of them. You don't need a 2d array. The amount of points will be the VALUE of the 1d array, which references the ability.
    Code:
    //initialize the array
    
    for(a=0;a<6;a++){
    Player_Stats[a] = 0; //zero points
    }
    Now when you want to add a point to an ability...
    Code:
    Player_Stats[ability_selected] += 1;
    Now you just need to set ability_selected to the one you've picked, which should be a number.
     
  5. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    Can the abilities have an unlimited amount of points, or do they have a max limit?

    Max level being 50, the player would have 50 points to put wherever they chose. So upto 50 in one ability, 0 in the others.

    What are the initial point values for those abilities, 0?
    Yes ideally it would start at 0.

    For example, base health would have 10 points.
    At 100 health. For every point, it increases by 10.

    So initialize 6 spots.
    Then declare them?
    Player_stats[0] = Vitality
    Player_stats[1] = intelligence
    Etc?

    Then on each level.
    Create buttons on the character stats screen that activate this (how would this be done)

    Player_Stats[ability_selected] += 1;
    Once selected delete other buttons?

    ?
     
  6. OblivionSkull21

    OblivionSkull21 Member

    Joined:
    Sep 26, 2017
    Posts:
    318
    No. The array will NEVER have the actual name of the ability as its value... that's what the chart you provided is for (to be used as a reference). Each ability will be identified by a number, with 0 being vitality, 1 being intelligence, etc.

    The actual value for the array is the point amount, which starts at 0. Declare the array exactly as I did.

    Selecting a button can be done in several different ways, but I don't know if you want to use the mouse to select and click the button or to select it with the keyboard.

    Once the button is selected, you'll do Player_Stats[ability_selected] += 1 and then destroy ALL the buttons, including the one you just pressed.

    EDIT: I just realized you only have 5 abilities, so that for loop should be this instead:
    Code:
    for(a=0;a<5;a++){
    Player_Stats[a] = 0; //zero points
    }
    And you'll want to move each ability down a number and get rid of the first one you had, as the current level is NOT an ability:
    Code:
    /// 0-Vitality (Health, Stamina)
    /// 1-Intelligence (Mana, Spell damage)
    /// 2-Strength (Melee Damage)
    /// 3-Dexterity (Ranged Damage)
    /// 4-Defense (Reduced Damage taken)
     
    Last edited: Jan 9, 2020
  7. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    Okay so ability selected is = to 0-6 depending on the choice.

    I'd like it to be a button press.

    For example the attached image. A + sign shows up beside each of the abilities when CharAbility <= 1. For the player to hover over and click.

    Okay I will remove the level. I had assumed it was needed for a 2D array , but I guess this is a 1D array that should be fine.
    I would draw a "+" Sprite to the stats gui screen that's accessed when the player hits "C"

    I'm not sure how to implement the choice aspect.
    Is it more, if mouse click,
    Player_stats[0] += 1
    Instance_destroy();

    (should I be destroying buttons, for instance if CharAbility = 2 it should be more
    CharAbility -= 1 since we have in the beginning
    If CharAbility <= 1
    Do all this)

    Where would I store this function? In a persistent game object? Or is this a script?
    Or should I use global variables for the stats since it will be called by the player, level objects and eventually an inventory system.
     

    Attached Files:

  8. OblivionSkull21

    OblivionSkull21 Member

    Joined:
    Sep 26, 2017
    Posts:
    318
    ability_selected will be from 0-5.

    I will try my best with the button press. I've never done this in my game before, so this may not work.

    For the button press, all the buttons need to be unique, so I'd suggest creating five objects with the button sprite set for each. Call them obj_button1, obj_button2, obj_button3, obj_button4, and obj_button5.

    Then, in each of those objects, have a Mouse Left event. In that event is where you'll want to update ability_selected and add the point to the correct ability. I don't know which of your objects initialized/contains the array, so just change obj_levelup to that object.

    So, for the first button (obj_button1):
    Code:
    with(obj_levelup){
    ability_selected = 0;
    if global.level <= 50{
    Player_Stats[ability_selected] += 1;
    instance_destroy(obj_button1);
    instance_destroy(obj_button2);
    instance_destroy(obj_button3);
    instance_destroy(obj_button4);
    instance_destroy(obj_button5);
    }
    }
    Second button (obj_button2):
    Code:
    with(obj_levelup){
    ability_selected = 1;
    if global.level <= 50{
    Player_Stats[ability_selected] += 1;
    instance_destroy(obj_button1);
    instance_destroy(obj_button2);
    instance_destroy(obj_button3);
    instance_destroy(obj_button4);
    instance_destroy(obj_button5);
    }
    }
    Third button (obj_button3):
    Code:
    with(obj_levelup){
    ability_selected = 2
    if global.level <= 50{
    Player_Stats[ability_selected] += 1;
    instance_destroy(obj_button1);
    instance_destroy(obj_button2);
    instance_destroy(obj_button3);
    instance_destroy(obj_button4);
    instance_destroy(obj_button5);
    }
    }
    Fourth button (obj_button4):
    Code:
    with(obj_levelup){
    ability_selected = 3;
    if global.level <= 50{
    Player_Stats[ability_selected] += 1;
    instance_destroy(obj_button1);
    instance_destroy(obj_button2);
    instance_destroy(obj_button3);
    instance_destroy(obj_button4);
    instance_destroy(obj_button5);
    }
    }
    Fifth button (obj_button5):
    Code:
    with(obj_levelup){
    ability_selected = 4;
    if global.level <= 50{
    Player_Stats[ability_selected] += 1;
    instance_destroy(obj_button1);
    instance_destroy(obj_button2);
    instance_destroy(obj_button3);
    instance_destroy(obj_button4);
    instance_destroy(obj_button5);
    }
    }
    In that first box in your picture, you'll just want to display the level... I'd make it global.level. Don't forget to initialize that as well... global.level = 1;
     
    Last edited: Jan 9, 2020
  9. IndianaBones

    IndianaBones Member

    Joined:
    Jul 5, 2016
    Posts:
    2,389
    I recommend you try using enums for your stat types. Then when you are referencing the array it is easy to read the code, plus you can leverage them for looping through the array as well as writing code that can handle additional stats without much modification.

    Code:
    enum Stats
    {
     Vitality,
     Intelligence,
      Strength,
      Dexterity,
      Defense,
      MAX
    }
    
    ability_selected = Stats.Vitality;
    
    enums are just a way to put human readable labels on numbers, by default the values in an enum start at 0 and increase by 1 for each value.

    The computer still sees them as numbers, but makes it easier for coding for us humans.

    To loop through the stats array, you can just start from 0 and go to Stats.MAX

    Code:
    for ( var i =  0; i < Stats.MAX ; ++i)
    {
        // Do stuff
        Player_Stats[i] = 100;
    }
    
     
  10. OblivionSkull21

    OblivionSkull21 Member

    Joined:
    Sep 26, 2017
    Posts:
    318
    I've never used enums. Good to know!
     
  11. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    I am a bit confused by this part:

    {
    // Do stuff
    Player_Stats = 100;
    }

    Could you please elaborate on this part? We are initializing the array, I wasn't sure what else you would more like, could do during this stage.

    Also we referenced Player_stats - do you mean Stats? (enum Stats)
     
  12. Rob

    Rob Member

    Joined:
    Jul 12, 2016
    Posts:
    765
    You can set an entry in an array to whatever you want whilst initialising it at the same time.

    You don't have to set every entry in an array to -1 before you can use it.

    If i put:

    Code:
    a_stats[4] = "Hi";
    
    
    An array will be created called "a_stats" containing 5 entries and the fifth entry (a_stats[4]) will equal "Hi". The other 4 entries will automatically be set to 0.

    I definitely recommend using an enumerator, even if you only have 6 stats. You could also use a 2D array to display the NAME of the stat as well as its value eg:

    Code:
    enum Stats
    {
     Vitality,
     Intelligence,
      Strength,
      Dexterity,
      Defense,
      MAX
    }
    
    enum Stats_Info{
       Value,
       Name,
    }
    
    a_stats[Stats_Info.Value, Stats.Vitality] = 3;
    a_stats[Stats_Info.Name, Stats.Vitality] = "Vitality";
    
    I would just use one object and have 6 instances of it as buttons too - each button having its own individual variable that lets it know what entry in the array it is (actually I'd use a for loop and not have the extra object at all but its all personal preference)
     
    Joe Ellis and Bentley like this.
  13. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    How exactly would that work?

    I have this currently inside my Draw GUI Event

    Code:
    #region Stats Screen
        if stat_screen
            {
                ///Draw Stats Sprite
            draw_sprite(spr_stats_screen, 0, 0, 0)      
                ///Draw Text
            draw_set_font(Font_MMenu);
            draw_set_color(c_yellow);
            draw_set_halign(fa_center);
            draw_set_valign(fa_middle);      
                ///Draw Title
            draw_text(guiWidth-950, guiHeight-700, "Stats Screen");      
                ///Draw Stats
                _tw = 490
            draw_text(_tw, 125, + string(level));    /// Character Level
            draw_text(_tw, 265, + string(Obj_Player.max_hp));    /// Character Vitality
            draw_text(_tw, 325, "10");    /// Character Intelligence
            draw_text(_tw, 375, "10");    /// Character Strenght
            draw_text(_tw, 435, "10");    /// Character Dexterity
            draw_text(_tw, 495, "10");    /// Character Defence
            draw_text(_tw, 575, + string(CharAbility));    /// Character Ability points
           
                if Obj_PlayerStats.Stats_Buttons = true
                    {
                        _twb = 548
                        draw_sprite(spr_stats_button, 0, _twb, 265)
                        draw_sprite(spr_stats_button, 0, _twb, 325)
                        draw_sprite(spr_stats_button, 0, _twb, 375)
                        draw_sprite(spr_stats_button, 0, _twb, 435)
                        draw_sprite(spr_stats_button, 0, _twb, 495)      
                    }
       
           
           
            }
    #endregion


    Do I need to add additional code by the Draw_sprite(Spr_stats_button) to make them into buttons that are selectable?


    Or do I have to Instance create a unique object button per ability?
     
    Last edited: Jan 10, 2020
  14. Rob

    Rob Member

    Joined:
    Jul 12, 2016
    Posts:
    765
    There's nothing wrong with using the draw_gui and in fact I prefer it rather than using an extra object - here's how I would update your draw_gui (I missed some things out like Character Level but the objective is to show you how to increase
    an array's value from within the for loop and you'll need to set up the 2D array for this to work (fill it with entries) - make sure the array and variables are named properly if you're using different names than I have. Also I can miss out brackets when typing code like this so look for any red marks on the left and they'll tell you whats wrong)

    Code:
    #region Stats Screen
        if stat_screen{
           draw_sprite(spr_stats_screen, 0, 0, 0);
    
           ///Draw Text
           draw_set_font(Font_MMenu);
           draw_set_color(c_yellow);
           draw_set_halign(fa_center);
           draw_set_valign(fa_middle);
    
            ///Draw Title
            draw_text(guiWidth-950, guiHeight-700, "Stats Screen");
            ///Draw Stats
    
            var startX = 490;
            var startY = 265;
    
            //This for loop will run 5 times EVERY step, each time it runs, all of the variables in the loop will be updated
            for (var display_stats = 0; display_stats < Stats.MAX; display_stats ++){
               var drawX = startX;
               var drawY = startY + (display_stats * 60);
    
               var stat_value = a_stats[Stats_Info.Value, display_stats ]; //display_stats will first equal 0 then 1/2/3/4 before the for loop is done (this runs every step)
               var stat_text = a_stats[Stats_Info.Name, display_stats ];
               var half_text_width = string_width(stat_text) / 2;
               var half_text_height = string_height(stat_text) / 2;
     
               /*
                 First I'm going to check to see if the mouse is over the stat
                 The text is being drawn from the center and I want to know if the mouse is less than half the width and less than half the height from this origin point - the abs function is great for this
                 What should happen is when the mouse is over the stat text like "Vitality" Vitality should turn green. We now know the mouse is over it and with 1 click we can add a point to the vitality stat
               */
               if ( abs(device_mouse_x_to_gui(0) - drawX) <= half_text_width && abs(device_mouse_y_to_gui(0) - drawY) <= half_text_height ){
                   draw_set_colour(c_green);
    
                   //Add a point to vitality with a mouse click
                   if mouse_check_button_pressed(mb_left) a_stats[Stats_Info.Value, display_stats ] += 1; //This doesn't remove an ability point or even check if there are ability points to use but hopefully it's not a big leap for you from here
               }else draw_set_colour(c_white);
    
                //Now we have the value of the stat and the name of the current stat to be drawn, draw it:
               draw_text(drawX, drawY, stat_text + ": " + string(stat_value));
            }
        }
    #endregion
    


    [EDIT] I didn't actually answer your question because I was trying to avoid overloading you with too much stuff. If you still want to know how to make 6 instances from the same object it would look like this:

    Code:
    //In the step event of a controller object
    
    if (keyboard_check_pressed(ord("C"))){
        stat_screen = !stat_screen;
    
       //Check to see if we need to create or destroy stat objects
    
       if (stat_screen == true){
          //Create 5 instances
    
          var startX = 490;
          var startY = 265;
    
          for (var i = 0; i < Stats.MAX; i ++){
            var instanceX = startX;
            var instanceY = startY + (i* 60);
    
            new_instance = instance_create_layer(instanceX, instanceY, layer, obj_stat);
            new_instance.stats_index = i; //First instance will have stats_index equalling 0, next will equal 1 etc
          }
       }else with obj_stat instance_destroy(); //if stat_screen is false, destroy any instances of obj_stat
    }

    Now inside obj_stat:

    Code:
    
    //Draw Gui
    var stat_value = a_stats[Stats_Info.Value, stats_index ]; //if a_stats is not global, this will cause an error so it needs to be either obj_controller.a_stats[Stats_Info.Value, stats_index ] OR global.a_stats[Stats_Info.Value, stats_index ]
    var stat_text = a_stats[Stats_Info.Name, stats_index ];
    
    //Now each instance should be drawing a different stat
    draw_text(x, y, stat_text + ": " + string(stat_value));
    
    
    I didn't put any code in to check for the mouse over the stat objects but if they dont have a sprite it wouldnt work anyway. If you did give them a sprite(whatever needs to be pressed to increase the value) then you can just use similar code to the mouse_check_button_pressed block that I put in the draw_gui code earlier in the post
     
    Last edited: Jan 10, 2020
  15. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    Is this valid?


    Code:
    enum Stats
    {
        Vitality,
        Intelligence,
        Strength,
        Dexterity,
        Defense,
        MAX
    }
    
    for ( var i =  0; i < Stats.MAX ; ++i)
    {
        Stats.Vitality = 0;
        Stats.Intelligence = 0;
        Stats.Strength = 0;
        Stats.Dexterity = 0;
        Stats.Defense = 0; 
    }
    
    or is it:

    Code:
    enum Stats
        {
            Vitality,
            Intelligence,
            Strength,
            Dexterity,
            Defense,
            MAX
        }
    for ( var i =  0; i < Stats.MAX ; ++i)
        {
            Stats = 0;
        }
     
    Last edited: Jan 10, 2020
  16. Rob

    Rob Member

    Joined:
    Jul 12, 2016
    Posts:
    765
    no it has to be something similar to what I posted eg

    Code:
    Stats[0] = 0;
    Stats[1] = 1;
    //array[enumerator.Strength]  = 0
    Stats[Stats.Strength] = 0;
    
    
    And now you can see why using a prefix is a good idea for enums and arrays as we have an enumerator AND an array BOTH called Stats. You can't reference an array like "Stats.strength" but it IS how you reference an enumerator. Forget about the enumerator for now if it's confusing you more.

    When you set up an enumerator, every entry is automatically assigned a number so

    Code:
    enum e_stats
    {
        Vitality,
        Intelligence,
        Strength,
        Dexterity,
        Defense,
        MAX
    }
    is the same as

    Code:
    enum e_stats
    {
        Vitality = 0,
        Intelligence = 1,
        Strength = 2,
        Dexterity = 3,
        Defense = 4,
        MAX = 5,
    }
    I would normally just use a for loop to initialise the array:

    Code:
    //Fill an array with 5 entries
    for (var i = 0; i < e_stats.MAX; i ++){
       global.a_stats[i] = 0;
    }
     
    Last edited: Jan 10, 2020
  17. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    Okay, I think I misunderstood how Enums work, looks like it is storing the position in the code:

    Code:
    enum e_stats
    {
       Vitality = 0,
       Intelligence = 1,
       Strength = 2,
       Dexterity = 3,
       Defense = 4,
       MAX = 5,
    }

    But, how would I manipulate the values that they represent?

    For example, if the player has 10 points into Vitality
    Vitality = 10.

    Is this where I need the 2D array then?

    Code:
    enum Stats_Info{
       Value,
       Name,
    }
    
    a_stats[Stats_Info.Value, Stats.Vitality] = 3;
    a_stats[Stats_Info.Name, Stats.Vitality] = "Vitality";

    If that is the case, then is this the more appropriate code?

    Code:
    //Fill an array with 5 entries
    for (var i = 0; i < Stats.MAX; i ++)
    {
       Stats[i] = 0;
    }
    
    enum Stats
        {
            Vitality,
            Intelligence,
            Strength,
            Dexterity,
            Defense,
            MAX
        }
       
    enum Stats_Info
        {
           Value,
           Name,
        }
    
    Stats[Stats_Info.Value, Stats.Vitality] = 0;
    Stats[Stats_Info.Name, Stats.Vitality] = "Vitality";
    Or would Stats.Vitality = 10 work?
     
  18. NightFrost

    NightFrost Member

    Joined:
    Jun 24, 2016
    Posts:
    2,090
    Your code is assigning the same name (Stats) to both the array and the enumerator. Call the enumerator as e_stats like Rob suggested, or something else that doesn't mix it up with the Stats array. The array is a row of boxes (or grid of boxes, in case of 2D array), the enumerators are helpers that can be used to select the correct box.
     
  19. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    Ah, I thought it was just sorting one array, not two arrays acting separately.

    So in essence, this is 3 Arrays working together?
    a_stats
    e_stats
    Stats_info

    So is this correct?

    Code:
    a_stats[5] = 0;
    
        enum e_stats
            {
                Vitality,
                Intelligence,
                Strength,
                Dexterity,
                Defense,
                MAX
            }
     
        enum Stats_Info
            {
               Value,
               Name,
            }
    
    a_stats[Stats_Info.Value, e_stats.Vitality] = 0;
    a_stats[Stats_Info.Name, e_stats.Vitality] = "Vitality";
    
    

    So I got it working this way:

    Code:
    globalvar Stats
    
    for(var a=0; a<5; a++)
        {
        Stats[a] = 0; //zero points
        }
    
    
    using 0-5 as the stat marks.

    Now I am working on the button press and I have this.

    var PosX = argument0;
    var PosY = argument1;
    var StatChoice = argument2;
    var redrawX = argument3;


    if (abs(device_mouse_x_to_gui(0) - PosX) <= 5 && abs(device_mouse_y_to_gui(0) - PosY) <= 5)
    {
    //Add a point to vitality with a mouse click
    if mouse_check_button_pressed(mb_left) && CharAbility >= 1
    {
    draw_sprite(spr_stats_button, 1, PosX, PosY) ///Green on click
    Stats[StatChoice] += 1; ///Add Stats
    CharAbility -= 1;
    }
    //This doesn't remove an ability point or even check if there are
    ///ability points to use but hopefully it's not a big leap for you from here
    } else exit;
    //Now we have the value of the stat and the name of the current stat to be drawn, draw it:
    draw_text(redrawX, PosY, + string(Stats[StatChoice])); /// Character stat;

    My issue now, is the mouse press, how do I get it on a specific marker, for this, it just registers a click anywhere rather than on the sprite.

    Update: got it working, reflecting in the above code.

    I am now going to test all the stats and see how it work.
     
    Last edited: Jan 10, 2020
  20. NightFrost

    NightFrost Member

    Joined:
    Jun 24, 2016
    Posts:
    2,090
    One array and two enumerators. Enumerators are, essentially, stand-ins for numbers as already mentioned. Because when you might have instead written:
    Code:
    a_stats[1, 2] = 10;
    
    and when a month later look at the code again, you say "what am I setting here? I have no idea! Something about stats, judging by the array name."

    But since with enumerators you instead have:
    Code:
    a_stats[Stats_Info.Value, e_stats.Strength] = 10;
    
    you'll instead nod "Hm, so I am setting value of Strength to ten here, I see." In other words, you don't have to memorize the meaning of all those numbers as you have clear labels to use instead. You don't even have to guess them thanks to autocomplete; just write the enumerator's root and the dot, and the autocomplete gives you a dropdown of suggestions with all the value labels.
     
  21. samspade

    samspade Member

    Joined:
    Feb 26, 2017
    Posts:
    2,230
    Unless you really need the name stored in the array, I wouldn't have one. I would keep it simple:

    Code:
    
    //set up enums
    enum e_stats {
       Vitality,
       Intelligence,
       Strength,
       Dexterity,
       Defense,
       MAX
    }
    
    //create a blank array
    global.stats_array = array_create(e_stats.MAX, 0);
    
    
    if you want the strings for some reason I would probably just make a static array for that. It would be extra work to create and maintain, but could then be referenced with the same enums and would be a lot easier to use, save, and reference than a 2D array.

    Code:
    
    global.stats_array_names = ["Vitality", "Intelligence", "Strength", "Dexterity", Defense"];
    
    //now you can reference things like this individually:
    global.stats_array[e_stats.Vitality];
    global.stats_array_names[e_stats.Vitality];
    
    
    The other problem I see in many of your examples is that while enums are great, especially for naming numbers like NightFrost mentioned, if you are looping through an array you wouldn't use them, you would instead use your loop variable (generally i):

    Code:
    
    var _stat, _stat_name;
    for (var i = 0; i < e_stats.MAX; i++) {
        _stat = global.stats_array[i];
        _stat_name= global.stats_array_names[i];
        ///use stat and stat name for example to draw them
    }
    
    
     
  22. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    I am not sure why the Enums arent working.

    I have it currently as:

    globalvar Stats
    for(var a=0; a<5; a++)
    {
    Stats[a] = 0; //zero points
    }

    If I were to do:

    enum e_stats {
    Vitality,
    Intelligence,
    Strength,
    Dexterity,
    Defense,
    MAX
    }

    To add an ability point would be:
    Stats[e_stats.Vitality] += 1;

    ???






    What do you mean loop through my array? I don't understand the problem.




    In other notes; as I was working through the code to add to the player's base stats, I received this error.

    Code:
    FATAL ERROR in
    action number 1
    of Create Event
    for object Obj_lifeform:
    
    trying to index a variable which is not an array
     at gml_Object_Obj_lifeform_Create_0 (line 1) - hp = 50 *(Stats[0]+0.15); ///  I want to increase HP by 15% per ability point
    ############################################################################################
    --------------------------------------------------------------------------------------------
    stack frame is
    gml_Object_Obj_lifeform_Create_0 (line 1)
    called from - gml_Object_Obj_Player_Create_0 (line 1) - event_inherited()
    
     
  23. TsukaYuriko

    TsukaYuriko Q&A Spawn Camper Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    1,925
    You're calling Obj_lifeform's Create event on line 1 (via event_inherited(). In it, you're referencing Stats. Stats does not exist at this point in time at all or is not an array.

    Please define "doesn't work" - what is your code supposed to do and what does it do instead?

    PS:
    globalvar is deprecated. Global variables are prefixed with "global.". in every place they are referenced - you should update your code accordingly.
     
  24. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    How do I make sure the array runs before these checks then?

    Doesn't this initialize the Stats?
    globalvar Stats
    for(var a=0; a<5; a++)
    {
    Stats[a] = 0; //zero points
    }


    It just doesn't work, gives me errors. I will try it again and post the error statements. After I get this current error working first though.

    I do not understand, doing global.Stats gives me an error.
    "unnecessary expression used as a statement."





    I am coming across an issue with this array.

    For example if I would like to Increase player hp by 10% per Vitality point its getting blown up.

    If I do this:
    max_hp *= (Stats[0]+.10)
    it doesn't work. Gets the error posted above
    If I do this when the player puts a point into vitality:

    draw_sprite(spr_stats_button, 1, PosX, PosY) ///Green on click
    Stats[StatChoice] += 1; ///Add Stats
    CharAbility -= 1;
    Obj_Player.max_hp += 10

    It adds 1020 HP to the player.
     

    Attached Files:

    Last edited: Jan 11, 2020
  25. TsukaYuriko

    TsukaYuriko Q&A Spawn Camper Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    1,925
    Initialize it in a separate room, preferably the first room of the game, before any other code has any chance to run.

    Please post full error messages and all involved code. A fragment of an error message does not provide us with the full context, which is required for us to be able to help you accurately.

    The statement "global.level;" on its own has no meaning. Therefore, an error is raised. Remove this statement.
    If you were trying to initialize this variable, you need to assign a value to it, like this:
    Code:
    global.level = 1;
     
  26. Nidoking

    Nidoking Member

    Joined:
    Nov 20, 2019
    Posts:
    107
    I feel the need to jump in here, in the middle of the troubleshooting, to get back to the original topic of learning how to use arrays, because there's one gigantic lesson that I feel needs to be added: This is not a good use for arrays. If you're just trying to learn how arrays work, that's fine, but these values are just linear values. In reality, you'd just set Player.Vitality = 10; and move on. Arrays would be used when you have a collection of values that need to be associated with indices and all serve the same purpose. For a better example, in my opinion, let's say that the player's vitality determines maximum health, but non-linearly. Perhaps, Vitality 1 gives max HP of 30, Vit 2 gives max HP 40, Vit 3 gives max HP 48, and so on. You don't want to have to write a formula to calculate that. In this case, you have a Vitality stat that is a single value, but you have a collection of max HP values that correspond to each Vitality value. You might use an array for this:
    MaxHPArray[1] = 30;
    MaxHPArray[2] = 40;
    MaxHPArray[3] = 48;

    and so on. Then, when you increase Vitality, you might do this:

    Player.MaxHP = MaxHPArray[Player.Vitality];

    The new Vitality value is your index into the array that tells the program what the max HP value should be. Every value in the array serves the same purpose, and corresponds to the index value.
     
    Joe Ellis and MaxLos like this.
  27. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39

    I am glad you posted it. I do think now this is the underlying issue of why the system won't work.
    As it stands, the feature works like this:
    Once a player levels, they get 1 stat point to put into their 1 of 5 stats.
    Now trying to get the Stats to influence the player stats is proving to be difficult.
    For example. 1 Stat in Vitality would increase health(10%), & stamina(15%) by a given percent.

    How I planned on doing it:

    With one stat point into vitality (Stat[0] should = 1)

    max_hp = 50 * (Stats[0]+0.1);
    hp = max_hp;


    But this is not working, giving this error:

    ___________________________________________
    ############################################################################################
    FATAL ERROR in
    action number 1
    of Create Event
    for object Obj_Player:

    trying to index a variable which is not an array
    at gml_Object_Obj_Player_Create_0 (line 30) - max_hp = 50 * (Stats[0]+0.1);
    ############################################################################################
    --------------------------------------------------------------------------------------------
    stack frame is
    gml_Object_Obj_Player_Create_0 (line 30)
     

    Attached Files:

  28. Yal

    Yal GMC Memer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    4,214
    Judging from the error message, it isn't set to 1 - it doesn't even exist! ("Trying to index a variable that isn't an array" errors also show up when the variable doesn't exist, regrettably, and usually that case is more likely than mixing array and non-array uses on the same variable.)

    From the look of it, you want Stats to be global, but it isn't (since there's no global. in front of its name). So it looks for a variable in the player object named Stats, which doesn't exist.
     
  29. Rob

    Rob Member

    Joined:
    Jul 12, 2016
    Posts:
    765
    [Edited as Yal already responded to the error msg - I'm also gonna bow out of this thread because I feel like I'm just confusing the guy now!]

    Stats[e_stats.Vitality] += 1; is the exact way to increase your vitality stat by 1, I would recommend using some kind of prefix before "Stats" like "a_stats" or "global.a_stats" so that you don't make mistakes trying to reference the wrong thing. "a" being short hand for "array" and "e" being shorthand for "enumerator".
     
  30. Yal

    Yal GMC Memer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    4,214
    This is known as Hungarian Notation, and pretty much universally disliked because it's redundant (the compiler knows the type, anyone that can find an assignment to the variable knows the type, and a good variable name conveys the type) and makes code harder to read, maintain and refactor. It's also very popular with Microsoft developers. There is probably some sort of connection.

    If you're gonna use prefixes, it's better to prefix by logical divisor instead - as in, some group name that clearly defines which things belong together. It also helps you easily find things, if you just know what subsystem they belong to - just search for that and pick the one you were looking for.
    [​IMG]
     
    Rob and TsukaYuriko like this.
  31. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    I was using globarvar. But I changed it to global.stats and it still is the same


    ___________________________________________
    ############################################################################################
    FATAL ERROR in
    action number 1
    of Create Event
    for object Obj_Player:

    trying to index a variable which is not an array
    at gml_Object_Obj_Player_Create_0 (line 30) - max_hp = 50 * (global.Stats[0]+0.1);
    ############################################################################################
    --------------------------------------------------------------------------------------------
    stack frame is
    gml_Object_Obj_Player_Create_0 (line 30)


    So it took a bit of maneuvering to complete.
    Basically had to move the array into the Obj_player to initialize instead of having its own object.
    I also got the stats system to work.
    In the script that updates the Abilitypoints, I have it update the stats as well.
    I have to test more than one stat to see if there are any issues.
    I have a feeling there will be issues in the future. For example, if the player dies and respawns, what will happen when the create event runs again and re-initializes the array.
     
    Last edited: Jan 11, 2020
  32. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    Could you elaborate a little on this?
     
  33. TailBit

    TailBit Member

    Joined:
    Oct 16, 2019
    Posts:
    141
    the reason for that is the order of operations, it tried to increase a number in the array before the array had been created ..

    I guess you had both objects in the room, but the order they were put in the room does matter .. the room editor should have a list that you can re-arrange their creation order so the one that defines the array comes before the player.

    usually I have my control object take care of player creation, with variables for room and the position to place him at.
     
  34. TsukaYuriko

    TsukaYuriko Q&A Spawn Camper Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    1,925
    To add to that - such problems regarding which code executes first when it's about initialization of variables can be easily avoided if you...
    ;)
     
  35. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    How do you do this?

    This actually introduced similar errors haha, I guess it will be a learning curve figuring out how to find which code should be initialized first. But knowing how to do this above(rearranging in editor) should help.
     
  36. TsukaYuriko

    TsukaYuriko Q&A Spawn Camper Forum Staff Moderator

    Joined:
    Apr 21, 2016
    Posts:
    1,925
    You can find the instance creation order under the layer properties tab when you have an instance layer selected. By default, this is docked to the left side of the room editor, the tab is titled "Properties", and the creation order tab is accessible via a button labeled "Instance Creation Order".
     
  37. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    1,106
    For the problem: "what will happen when the create event runs again and re-initializes the array"
    You need to change it so this doesn't happen when the player is created. If you're using arrays for building the stat upgrades and things, you need to make this structure when the game first starts ideally, and give every instance easy access to it by making it a global variable.

    Also I wanted to point out, for initializing an array of any length, you can just do: array[length] = 0, and if the array with a certain name already exists and you want to wipe it, just set "array" to 0, then do: array[length] = 0 and it will create a brand new array and assign it to the variable name
     
    IndianaBones and TsukaYuriko like this.
  38. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    I think I have everything working. I put it in a game initializing the object, the stats work and influence the player stats when selected.
    I have to try to implement a respawn and damage floating to see how much damage I'm doing and I think I should be good.
     
  39. Yal

    Yal GMC Memer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    4,214
    I thought the code screenshot was clear enough, but to explain it even more
    Controller State - ctst_SETUP, ctst_ANNOUNCE, etc.
    Battle action - battleaction_ATTACK, battleaction_ITEM, etc.

    I usually use shorter prefixes with mnemonics and abbreviations for stuff that's used in more places (since having those names be too long just wastes space and makes it harder to type out the constants) and longer, more descriptive names for stuff that's only used in one or two places (so that the user don't need to chase it down just to understand what it does, but can figure it out from the name alone).
     
  40. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    Question, when you are trying to save a game, how do you save an array?
     
  41. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    1,106
    You have to save the contents of it to a file and load it back into an array when loading, either with a text\ini file or a buffer. It's simple if all the values are numbers, you can just save each one as a separate entry, but if there are strings aswell it gets more complicated, you'd have to ask if the value "is_string(value)", before writing and also something that indicates which type it is before reading the value from the file.
    If you're using a text file you can get around this problem by loading each line\entry as a string, then you can simply convert strings holding numbers into real numbers by asking if the first character of the string is a digit or "-" followed by a digit
     
    Last edited: Jan 13, 2020
  42. Yal

    Yal GMC Memer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    4,214
    You don't have to save it to a buffer - I usually just loop through it and save every number on a separate line in the file (I use text files for everything). Depending on how serious I am, I either save the array size first, or just use a constant for both reading and writing.
     
  43. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    1,106
    Sorry yes I should've said either a buffer or text file. I'm gonna edit what I put
     
  44. Halas

    Halas Member

    Joined:
    Jan 4, 2020
    Posts:
    39
    What would the code look like to loop through an array collecting all its values?
     
  45. Nidoking

    Nidoking Member

    Joined:
    Nov 20, 2019
    Posts:
    107
    You'd use a for loop for that.
    Code:
    for (var i = 0; i < array_length_1D(the_array); i++)
    {
      // do something with the_array[i]
    }
     
  46. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    1,106
    Or a repeat loop:

    Code:
    var i = -1, val;
    repeat array_length_1D(array)
    {
    val = array[++i]
    //do something with val
    }
    Repeat loops are a bit faster than for & while loops, plus getting the length of the array every cycle of the loop will add extra time.
    If you still want to use a for or while loop it's better to get the length of the array once, put it in a var then compare with this, it increases the speed by about 10%, which can be a big difference with large arrays. eg.
    Code:
    //For loop:
    var l = array_length_1d(array), val;
    for (var i = 0; i < l; ++i)
    {val = array[i]}
    
    //While loop:
    var i = -1, l = array_length_1d(array) - 1, val;
    while (i < l)
    {val = array[++i]}
    
    //Repeat loop:
    var i = -1, val;
    repeat array_length_1d(array)
    {val = array[++i]}
     
  47. Yal

    Yal GMC Memer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    4,214
    AFAIK there's no evidence suggesting repeat loops are faster, but it's a plausible guess (since the for-loop counter is compiled instead of interpreted). Usually the loop body takes the vast majority of the execution time, though :p
    Also, there's a lot of times you want access to the loop counter, repeat loops won't give you that.

    Instead of executing an expensive operation (e.g. array_length_1d()) every iteration, execute it once and store the result in a variable. Bam, now you only need to do it once. (It obviously doesn't work if you expect the result to change over time, but then again, then you should be using a while/do-until loop instead of a for loop anyway!)
     
  48. Joe Ellis

    Joe Ellis Member

    Joined:
    Aug 30, 2016
    Posts:
    1,106
    It's not a guess, I tested it just before I wrote that, and you can access the loop counter just as easily with a repeat loop cus "i" is the counter.
    There's been a few other tests I've seen on here that all say repeat loops are the fastest. They are only very slightly faster though, they seem to be able to do around 1500 more reps in one frame than a for loop, but when both can do around 41000-43000 there isn't much difference relatively.

    -EDIT
    I ran two tests:

    Test1:
    Code:
    if ++num_steps < 3600
    {
    var start_time = get_timer(), reps = 0, val, l = 100;
    while get_timer() - start_time < 15000
    {
    //For:
    for (var i = 0; i < l; ++i)
    {val = 0}
    //Repeat:
    var i = 0;
    repeat l
    {
    ++i;
    val = 0;
    }
    ++reps
    }
    total_reps += reps
    }
    This measured how many times each loop could be called per step for 1 minute.
    (within 15000 microseconds, a full step is 16000 but you can't use up the entire frame time
    cus it's needed for drawing and things, so I use 15000 and it makes the fps_real just above 60)

    Results:

    10000 cycles in loop:

    Total in 1 minute:
    repeat: 24251 reps
    for: 22187 reps
    difference: 2064

    per step average:
    repeat: 6.73 reps
    for: 6.16 reps
    difference: 0.57 reps (5700 cycles more)

    1000 cycles:

    Total in 1 minute:
    repeat: 223089 reps
    for: 209360 reps
    difference: 13729

    per step average:
    repeat: 61.96 reps
    for: 58.15 reps
    difference: 3.81 reps (3810 cycles more)

    100 cycles:

    Total in 1 minute:
    repeat: 2202243 reps
    for: 2049085 reps
    difference: 153158

    per step average:
    repeat: 611.73 reps
    for: 569.19 reps
    difference: 42.54 reps (4254 cycles more)

    10 cycles:

    Total in 1 minute:
    repeat: 18161396 reps
    for: 17540926 reps
    difference: 620470

    per step average:
    repeat: 5044.83 reps
    for: 4872.47 reps
    difference: 172.36 reps (1724 cycles more)

    Test2:

    Code:
    if ++num_steps <= 3600
    {
    var l = 100, val, start_time, end_time;
    start_time = get_timer()
    
    //For loop:
    for (var i = 0; i < l; ++i)
    {val = 0}
    
    //Repeat loop:
    var i = 0;
    repeat l
    {
    ++i;
    val = 0;
    }
    
    end_time = get_timer()
    time_taken += end_time - start_time
    }
    This measured how much time in microseconds each loop took to run.

    Results:

    10000 cycles:

    Total in 1 minute:
    repeat: 12064541 ms
    for: 12322877 ms
    difference: 258336 ms

    per step average:
    repeat: 3351 ms
    for: 3423 ms
    difference: 72 ms

    1000 cycles:

    Total in 1 minute:
    repeat: 1441925 ms
    for: 1553714 ms
    difference: 10209 ms

    per step average:
    repeat: 401 ms
    for: 432 ms
    difference: 31 ms

    100 cycles:

    Total in 1 minute:
    repeat: 164447 ms
    for: 174656 ms
    difference: 10209 ms

    per step average:
    repeat: 46 ms
    for: 49 ms
    difference: 3 ms

    10 cycles:

    Total in 1 minute:
    repeat: 31536 ms
    for: 26820 ms
    difference: 10209 ms

    per step average:
    repeat: 8.76 ms
    for: 7.45 ms
    difference: 1.31 ms

    It shows that for loops actually take less time to complete if the length of the loop is low (10)
    So I need to test this further and see where the crossover point is where repeat becomes faster.
    It might be because repeat loops take longer to initialize, or that they cause less strain on the cpu,
    making the function take longer because the cpu is running slower.
    However I feel that test1 is a better test, because it measures how many times the loops can be run per step, and the more times results in a task being completed faster.
    But in summary, the difference is that miniscule that even a very long task would only take about quarter of a second longer if you use for loops, so you may aswell use whichever one you prefer.
     
    Last edited: Jan 14, 2020

Share This Page