GMS 2.3+ Newbie seeks initial game object help

Hello all.

I am making a platformer with RPG elements. I have the platformer basics in place, but now its time to make some decisions about how to store player stats and inventory. I want to be able to do the following:

-Pick up items that will affect player stats
-Player stats increase on level up


I am looking for some general suggestions on how to store variable like this in the best way so I don't have to rework a ton of stuff later. I am currently just creating variables like Hp, Exp and GunDelay in the player object. Also I need some kind of initial game object that has a menu and title screen.

Can yall give me some general advice on how to set things up in a smart way? thanks in advance.
 

Rob

Member
You'll benefit from using an array/list/grid for your stats so that you can use loops to run through them.
Lists/grids have their own read/write functions too for saving/loading.
If you pair that up with enumerators then you won't lose any readability in your code.
You can use a .csv that stores info about the things you can pick up eg what stat does it buff etc.
When you pick up an item, if it knows what entry in the csv it is, you can easily get the relevant data (as you can use load_csv at the start of the game to convert the csv into a grid).
If your items are very unique when compared to each other, just make an array instead and store a script for each item that is to be run when it's picked up/used
 
Last edited:
You'll benefit from using an array/list/grid for your stats so that you can use loops to run through them.
Lists/grids have their own read/write functions too for saving/loading.
If you pair that up with enumerators then you won't lose any readability in your code.
You can use a .csv that stores info about the things you can pick up eg what stat does it buff etc.
When you pick up an item, if it knows what entry in the csv it is, you can easily get the relevant data (as you can use load_csv at the start of the game to convert the csv into a grid).
If your items are very unique when compared to each other, just make an array instead and store a script for each item that is to be run when it's picked up/used
Thanks a lot for the replay. Would you mind giving me an example with the Enumerators providing readability, I dont quite understand that part.
 

Nidoking

Member
Simple. Tell me how much sense you make of this:
GML:
player.stats[0] = 100;
player.stats[1] = 5;
player.stats[2] = 7;
Doesn't tell you much, does it? Which one of those is Attack? Or is it Strength? Which one do I use for random item generation? Do I even have a Magic stat? Now look at this:
GML:
player.stats[PLAYERSTAT.HEALTH] = 100;
player.stats[PLAYERSTAT.ATTACK] = 5;
player.stats[PLAYERSTAT.DEFENSE] = 7;
Isn't it easier to tell which stat does what now?
 

TsukaYuriko

☄️
Forum Staff
Moderator
Speaking of things that are more human-readable: Structs are ideal for this kind of key-value pair mapping.

To adapt the example above...
GML:
player.stats =
{
    hp : 100,
    attack : 5,
    defense : 7
};

show_debug_message(player.stats.attack);
It doesn't really get any easier than this.
 
Hey thanks a ton for explaining that. I think I will go with an array, my brain seems to be able to keep that one straight. Can you suggest on where to store the array? Some sort of persistent game controller object?

Since this has been so helpful, may as well keep it going. I am looking to have the player object have 3 abilities on separate cooldowns. Items can affect these cooldowns as well as player level. I am sure there are a number of ways to set this up, but again I would like to plan a good system early on with advice from pros instead of just throwing something together that ends up needing to be replaced later. Thanks again yall.
 

NightFrost

Member
The most complex bit of the ability cooldown thing is "items can affect cooldowns" because besides a system for managing cooldowns, you also need a system for managing inventory.

If three abilities is all there is, and there's no ability management, switching active ones in and out, you could get away with just having three structs, one for each ability. Besides housing other ability data they would also contain data for calculating cooldown times. First, I'd have the unmodified cooldown time noted there. Since level affects the timer, I'd have another entry for how much it changes per level. Yet another entry would have the current value, which is the base value modified by all relevant things.

For items, there'd be another struct. It could be as simple as just having boolean values for carrying / not carrying the item, if they have no own stats.

The final piece would be a function that calculates current cooldown. It takes the base value, modifies it by level, then loops through the item list and upon finding correct item, modifies the value some more. The final value is then saved back to the ability struct as current cooldown. You call this function every time player level changes, an item is gained, or an item is lost. You don't waste unnecessary lines of code figuring whether you need to recalculate or not. You just always recalculate.
 
Ok I went with data structs because that was much easier for me to work with. But I have a small issue:

pstats {

HPMAX : 50,
HP : pstats.HPMAX,
}

The above code gives me a variable not set before being read error for HP. If I manually set HP : 50, the code works fine. This isnt really an issue but I dont understand why I cant initialize HP : pstats.HPMAX.

Any ideas?
 

TsukaYuriko

☄️
Forum Staff
Moderator
Because you are in the process of declaring pstats, so you can not access it. It doesn't exist yet.

This works:
GML:
pstats = {};
pstats.HPMAX = 50;
pstats.HP = pstats.HPMAX;
... because the first line declares it and, thus, every subsequent line can access it.
 

FrostyCat

Member
The Manual entry on structs said:
In the above code the struct member variables "x" and "y" are being set to the values held in the instance variables "x" and "y", since the right-hand of the colon ":" refers to the instance that is defining the struct. It is worth noting that this means you cannot use struct member variables as part of the definition of other variables within the struct when it is being created. For example, the following would give you an error:
GML:
mystruct = {
a : 10,
b : 10,
c : a + b
     }
 
Thanks again that works great. Another random question: I have a sprite with several loops withing the same sprite. Some of the loops are set to "Ping pong mode" others are regular loops. I have just been breaking these types of files up into separate sprites for each loop, is there a way to use a single sprite and call certain loops out of it?
 

TsukaYuriko

☄️
Forum Staff
Moderator
Before you do anything else, note that the "ping pong mode" is for preview only. It will not work in-game.

You can certainly unify multiple sprites into one, as long as you then manually manage all of the functionality you lose by doing so, such as mapping the correct sub-images yourself when it is time to draw something included in that sprite. You can refer to individual sub-images of a sprite via the subimg argument found in virtually all sprite functions.
 
Never ending string of questions continues. I want to have an ability where I press a button and the player dashes in the facing direction and a small vertical boost too. I wont bother sharing my failed attempts at this. Can yall just let me know a good way to do this?

To jump I check for key pressed in the step event and if it finds it: VSP = -7 which jumps the player upwards.
 

woods

Member
raw simplest way to do a dash..
check the direction of the player
check for keypress for dash
change player speed in checked direction

but that depends alot on HOW you move your player ;o)
 
if pstats.cooldown1 == 0 and key_a1
{
pstats.VSP = -5;
x += (60 * sign(pstats.HSP));
alarm[0] = 60;
invincible = true;
pstats.cooldown1 = true;

}
This is as close as I can get. It kind of works but it looks really strange. First the player goes up in the Y direction, then the X part happens. I was hoping for an arc.

ie Combat Roll type of manuever. The other issue is when a player is standing still I get no forward roll.
 
Last edited:

woods

Member
take a look at length_dir x/y functions, and maybe point_direction..

as far as standing still, you are checking player's speed.. you need to check player's direction instead ;o)
 

NightFrost

Member
If you want an operation that adjusts something over time, you have to write the code to perform it across multiple steps. You're wanting to create an operation where player position is adjusted across 60 game steps (apparently), so you have to add code that runs for that 60 steps. But not run when it has not been allowed to, which is before the combat roll has been initiated, and after the roll has finished in 60 steps. A fairly typical method for this is called State Machine, which means dividing the code into blocks and running a block only if a state setting is correct. So in this context your state machine would have a combat roll state, which continually adjusts player position in desired manner and probably disallows any player controls, until a time counter has run out, after which the state is switched to something else (an idle state most likely).
 
@NightFrost I think I follow you. Here is a related question:

I am using enum states to define the behavior of my enemies. state = states.attack, states.idle etc. etc...

I want to adjust vsp when the object changes from the state "Wander" to the state "Alert". Basically I want ti to do a little hop when it gets alerted. Seems like this should be easy but I cant figure out where to put it. Here is the code:

GML:
//Wander
else if(state == states.wander)
{
    #region Wander State
   
    //behavior
    counter += 1;
   
    if (place_meeting(x+hsp,y,obj_wall)) hsp = -hsp
   
    //Transition triggers
    if(counter >= room_speed * 3){
        var change = choose (0,1);
        switch change{
            case 0: state=states.idle;
            case 1: counter = 0;
                    hsp = choose (-1,1)
        }
       
    if collision_circle(x,y,300,HERO,false,false)
    {
        state = states.alert;  
    }
   
    //Set sprite
    sprite_index = spr_assault_walk;
   
   
}
    #endregion
}
 
Thanks, it was getting late when I posted that question. Brain not firing great probably lol.

Moving on to particles:

I would like to have a very simple particle effect when oBullet hits oEnemy. I am picturing 1-5 white pixels emitting from the place meeting at random speeds and falling towards the ground. Is this possible using the basic GML functions or will I need to use 3rd party particle effect engine?
 

TsukaYuriko

☄️
Forum Staff
Moderator
All of that is possible with the default particles.

Also, you may want to open a new topic about issues unrelated to the one named in the title of this topic. This avoids confusion.
 

GMWolf

aka fel666
Some advice of items modifying stats: (sorry if it's been mentioned already).

Rather than keep a single variable that you change whenever you gain a new item, only keep the base stat and calculate the items effects whenever it's needed.

The reason being that when you start equipping and un-equipping items, changing the stats in the variable can get quite complex, especially if the order the effects are applied in affects the final number (multipliers and adders).

For instance, your base HP might be 10.
You equip a armour which gives you + 5, and a spell which gives you ×2.
(10 + 5) × 2 = 30.
If now you unequip the armour (-5) you get 25.

But if you only had the ×2 spell you total HP would only be 20, not 25.

So what I suggest is only keeping the base stat (and modifying it when you level up).
Then whenever you need to check a stat, call a script "get_hp()" or "get_strength()" etc which looks at all the equipment and applies them in the correct order.

This means no matter what happens your modifiers will always be consistent.
 
Top