GML [1.4.9999] need help applying variables from other object

woods

Member
local multiplayer split screen space shooter..
i am using a handful of ship objects that the player can collide with to select that ship in the pre-game room. the players takes on the properties of one of the ships and moves to the starting area. when both players are in the starting area, we can goto room next.. and start the game.

everything is working well with one exception: rate of fire does not update to the player


a little bit of info for debugging
controller object draw event
Code:
if (room == rm_start)
{
//P1
if (view_current == 0)
    {
    draw_text_transformed(view_xview[0], view_yview[0], "HP " + string(obj_P1.HP),4,4,0); // draw P1 HP
    draw_text_transformed(view_xview[0], view_yview[0]+64, "FIRE " + string(obj_P1.countdown),3,3,0); // draw P1 ready to fire
    draw_text_transformed(view_xview[0], view_yview[0]+96, "turn " + string(obj_P1.turn_spd),3,3,0); // draw P1 turn spd
    draw_text_transformed(view_xview[0], view_yview[0]+128, "thrust " + string(obj_P1.thrust),3,3,0); // draw P1 thrust
    draw_text_transformed(view_xview[0], view_yview[0]+160, "max spd " + string(obj_P1.spd),3,3,0); // draw P1 max speed 
    }

player 1 create event
Code:
/// initialize vars

image_angle = 0;
image_index = spr_P1; // default sprit no ship
friction = 0.01 // gotta slow down in space too
can_shoot = true; // to slow down rate of fire
HP = 10;
countdown = room_speed*1; // timer for shoot
turn_spd = 1; // rotate image_angle
thrust = 0.02; // must be higher than friction to move
spd = 2; // max speed clamp
player 1 step event - select ship
code repeats from ship 0 to 7
Code:
/// select ship

if (place_meeting(x, y, obj_ship_select_0))
{
    sprite_index = spr_ship_0;
    HP = obj_ship_select_0.HP;
    countdown = obj_ship_select_0.countdown;
    turn_spd = obj_ship_select_0.turn_spd;
    thrust = obj_ship_select_0.thrust;
    spd = obj_ship_select_0.spd;
    image_xscale = 0.5
    image_yscale = 0.5
    
}

if (place_meeting(x, y, obj_ship_select_1))
{
    sprite_index = spr_ship_1;
    HP = obj_ship_select_1.HP;
    countdown = obj_ship_select_1.countdown;
    turn_spd = obj_ship_select_1.turn_spd;
    thrust = obj_ship_select_1.thrust;
    spd = obj_ship_select_1.spd;
    image_xscale = 0.5
    image_yscale = 0.5
}


if (place_meeting(x, y, obj_ship_select_2))
{
    sprite_index = spr_ship_2;
    HP = obj_ship_select_2.HP;
    countdown = obj_ship_select_2.countdown;
    turn_spd = obj_ship_select_2.turn_spd;
    thrust = obj_ship_select_2.thrust;
    spd = obj_ship_select_2.spd;
    image_xscale = 0.5
    image_yscale = 0.5
}

player 1 step event -shooting

Code:
/// shoot

if (keyboard_check(vk_space) && can_shoot == true) 
{ 
   countdown = room_speed*1; // Set shot_timer to 1 second
   can_shoot = false;
   instance_create(x,y,obj_bullet_P1_1);
}
if (countdown > -1) 
{
   countdown -= 1;

}
if (countdown == 0) {
   can_shoot = true;
   // Reset can_use_skill back to true after shot_timer hits 0
}

the countdown variable changes in the debug text in the top corner.. but the rate of fire stays the same..
tested with P1 and P2 using different ships with different countdown timers..


i understand there is a difference between instance variable and local variable (most likely my issue)
this is a gray area for me on how to use/change them properly

i see that i am declaring countdown at the top of the script in the shoot code.. ive tried changing that up a few different ways... with poor results.

how do i change the countdown timer to use the "countdown" from the selection ship like the rest of the variables?
 
I wrote up a big thing showing you how to restructure your code better, but then I decided to delete it and just fix your single problem, if you want to see what I was originally going to show you, just ask.

What value do you want countdown to be? You literally have it set to one value, room_speed*1. Countdown is 1 second. Is it meant to be 1 second? Is it showing something different from 1 second? What is the actual problem that is occurring?
 

woods

Member
in the player create event i set my stats.
in my ships in the selection room i have better stats.. different for each ship, but better than just the pilot.
when my player collides with a ship, it takes on the properties of that ship.. overwriting the values set in the player create.
when both players are satisfied with their loadout we move to the playroom and start the game.


the default pilot has countdown of room_speed*1
a big slow ship has room_speed*2
a light quick fighter ship has room_speed *.25


the issue is: the "countdown" isnt getting carried over to the player, from the ship the player selects.. everything else does tho.


edit:
changing "countdown" to "obj_P1.countdown" fixed the issue... dam im slow sometimes.. or overthink things too much
edit edit: well.. it made it not chrash.. but the countdown rate of fire isnt working still ;/




I wrote up a big thing showing you how to restructure your code better .... i am ALWAYS down for learning ;o)
 
Last edited:
Ok, so then the problem is that you're thinking of what countdown is incorrectly...What you're calling countdown should actually be countdown_max (or whatever you might want to think of it as). It's the value that the timer should be set to when you want it to start counting down, not the actual timer itself. The simple fix is to create another variable in the player ship, call it countdown_current or something. Then do this:
Code:
if (keyboard_check(vk_space) && can_shoot == true)
{
   countdown_current = countdown; // Countdown is that value that you want to start counting from, not the actual value that is counting down
   can_shoot = false;
   instance_create(x,y,obj_bullet_P1_1);
}
if (countdown_current > -1)
{
   countdown_current -= 1;

}
if (countdown_current == 0) {
   can_shoot = true;
   // Reset can_use_skill back to true after shot_timer hits 0
}
Also, here's what I was originally going to type. Put your learning hat on:
Firstly, there's no local variables in your code at all, there's only instance variables, lol, so that couldn't be your issue. The way you have gone about setting up this whole thing is very long, with lots of duplicated code where there shouldn't be and you are trying to pass variables back and forth between objects when you should be using a data structure and simply reading and writing to that: Firstly, create a parent object (I'll call it obj_ship_select_parent) and make each obj_ship_select_0, obj_ship_select_1, etc, all a child of that parent object. Then we are going to create some data structures at the start of your game: a ds_list, which will hold two ds_maps, one for each ship (note, this code should NOT be in your ship object, it should be in some controller object that runs once at the start of the game):
Code:
global.player_stats = ds_list_create(); // Create the list to hold each players stats

var player_1_map = ds_map_create(); // Create a map for the first player (also note, the var here means that this is a local variable)
player_1_map[? "sprite index"] = spr_ship_1; // Here, we add an entry to the map, under the key "sprite index"
player_1_map[? "HP"] = 100;
player_1_map[? "countdown"] = room_speed*1;
player_1_map[? "turn spd"] = 1;
player_1_map[? "thrust"] = 0.02;
player_1_map[? "spd"] = 2;
ds_list_add(global.player_stats,player_1_map); // Now we add that map to the list
ds_list_mark_as_map(global.player_stats,0); // And mark the position it has been added to as a map

// Repeat for player 2 except in slot 1, instead of slot 0
var player_2_map = ds_map_create();
player_2_map[? "sprite index"] = spr_ship_2;
player_2_map[? "HP"] = 100;
player_2_map[? "countdown"] = room_speed*1;
player_2_map[? "turn spd"] = 1;
player_2_map[? "thrust"] = 0.02;
player_2_map[? "spd"] = 2;
ds_list_add(global.player_stats,player_2_map);
ds_list_mark_as_map(global.player_stats,1);
Now we have a nice data structure that we can use to store player stats and access from anywhere, without worrying about instances transferring variables or whatever. So, how do we use this data structure. Firstly, let's create a script called set_player_stats():
Code:
///@function set_player_stats(player_no);
///@description This script uses player_no to pick a slot in global.player_stats and then uses the ds_map held in it to set the instance variables of the calling instance to the appropriate values
///@param {int} player_no The player number we want to get the data from

var no = argument0; // Grab the number that we sent to the script
if (no >= ds_list_size(global.player_stats)) { // This is just error checking
   show_debug_message("Player_no provided to read_player_stats script was larger than the player_stats script");
   return;
}

stats = global.player_stats[| no]; // We store the map at position "no" from global.player_stats into an instance variable called stats

// Now we set some data
sprite_index = stats[? "sprite index"]; // We simply use the keys we set before to set the appropriate variables to the values held in the entries the keys correspond to
HP = stats[? "HP"];
countdown = stats[? "countdown"]; // timer for shoot
turn_spd = stats[? "turn spd"]; // rotate image_angle
thrust = stats[? "thrust"]; // must be higher than friction to move
spd = stats[? "spd"]; // max speed clamp
Now lets get the Create Event for player 1 rolling:
Code:
/// initialize vars

// Put 0 for player one and 1 for player two into the script
set_player_stats(0);

// These are your variables that don't need to be set via the ds_map
image_angle = 0;
// image_index = spr_P1; (I believe you've incorrectly used image_index on this line, instead of sprite_index
friction = 0.01 // gotta slow down in space too
can_shoot = true; // to slow down rate of fire
What this does is every time your ship is created, it reads the data it needs from the data structure we set up. This means that all you do is change that data structure and your ship will inherit your changes. The Create Event for player 2 is the exact same code (if I this were me, I would be using a single object for both players to cut down on code duplication further, but we'll stick with two separate objects for now) except for one change, we put 1 into the set_player_stats script instead of 0. All the rest of the code is the same.

Now how do we make changes when the player selects the ship? Like this:
Code:
var ship_hit = instance_place(x,y,obj_ship_select_parent); // Using the parent object for the ship select means that we literally only need one block of code, instead of repeats for each type of ship

if (instance_exists(ship_hit)) { // If instance_place returns a valid instance
    // In the Create Event of each ship, we made it so that stats refers to the map holding all the data, this means that we can also set the data using stats, so we'll do that below
    stats[? "sprite index"] = ship_hit.sprite_index;
    stats[? "HP"] = ship_hit.HP;
    stats[? "countdown"] = ship_hit.countdown;
    stats[? "turn_spd"] = ship_hit.turn_spd;
    stats[? "thrust"] = ship_hit.thrust;
    stats[? "spd"] = ship_hit.spd;
    image_xscale = 0.5
    image_yscale = 0.5
    set_player_stats(0); // 0 for player one and 1 for player two
}
I think that's all. It's not 100% absolutely necessary to make all these changes for what you're doing, but it is very important to get used to dealing with data structures if you're looking to make games. Passing data back and forth between instances really only works well if your project is extremely small. If you start making anything bigger, you'll run into all sorts of problems that can easily be solved by separating the "action" from the "data" and using data structures to deal with data and "action code" (such as movement, jumping, etc) to only deal with the actual actions. Personally, I haven't worked on a project in the last god knows how many years that I didn't use a data structure. Have a read through what I've written and try to get your head around what is happening. Copy and paste the code into GMS (even if it's just a blank project) and middle click any functions I've used that you're having trouble understanding and read the manual entry, repeatedly, until it starts to make sense. Try experimenting with small-scale proof of concepts. For instance, can you loop through a list containing strings and draw all the strings on the screen? Can you toggle a map entry between true and false by pressing a key? Etc, just small things, but things that involve interfacing with data structures. Once you get your head around them, you'll be kicking yourself for not having learnt them sooner.
 

woods

Member
first off.... thank you
for explaining this to me in a way i can grasp it... using my half completed project makes it more familiar and less overwhelming ;o)
the few times ive looked at data structures list and maps... ive gotten very muddled very quick.

second *brews a fresh pot off coffee*
i think im gonna need a bigger hat

laying this side by side with how ive done this the long hard hacknslash way, i can see how things work alot easier..
it may take me a while for all of this to sink in.. but the lightbulb is flickering.
 
Yeah, I found it hard to take the abstract explanations and apply them concretely in a project when I was learning them. It just takes a lot of mucking around with them and experimentation. Using the debugger is a big thing as well, you can right click on watched variables or variables under the Variables tab and select View As > ds_map, ds_list, etc, which lets you view what is actually in the data structure as you play, which is invaluable for learning what is going on under the hood of your game and spotting when the wrong thing is being stored, then you can start to track down why the wrong thing is being stored.

Basically, any data structure really boils down to a list like structure or a grid like structure (like a spreadsheet). I mean internally they aren't all represented that way, but you can visualise them as that. So a map can be looked at as a grid with 2 columns and as many rows as you want, where the first column of each row is the "key" and the second column is the data you are storing under that key. When you ask for data under a specific key, the game looks down column 1, finds the row that contains the key and then returns the data in column 2 from that row. Like a spreadsheet basically (again, internally, this is not accurate, but visually in your head it's good enough). Lists are literally just like a shopping list.

I think the biggest element of confusion comes in when storing data structures within data structures like we did in my post above. When you create the map and then store it in a list, you're just storing a "reference" to that map (the reference is going to be a number). Then when later on you read the list position and store it in a variable (my_var = my_list[| 0]), you are just copying that reference number into my_var. When you use that variable in conjunction with the ? accessor (my_var[? "my key"]), you're telling GMS it's a map, so it'll look under it's list of maps and find the one that corresponds to the number stored in my_var and then it'll look in that map for the key "my key" and return what's stored under that key. It's convoluted, but if you bang your head against the wall enough, it'll start making sense.
 

woods

Member
ive got ALOT to learn ...
hell.. the only 'debugging' ive done is throwing in some show_text here and there.. i know there is sooo much more.. just havnt tapped into it yet. (baby steps)

i understand the spreadsheet concept.. my problem comes with getting lost referencing this and that and the cross checking to the right data.. i get lost easily.

i can tell now, from where i was a year ago..... i am retaining alot more information. and things are getting easier to muck thru.

ive got a long way to go tho ;o)
 
You're in good company getting lost with this stuff, hahaha, it takes a lot of mental effort and training to be able to fluidly understand what is going on with computer science. Most bugs, professional programmers included, come from someones mental model of what they think is happening being different to what is actually happening (i.e. they're getting lost, but not realising it). It gets easier with time and practice though. Don't be afraid to experiment in throw away projects. Some of my biggest "aha now I understand" moments came from experimenting with things I found confusing in a separate project. Mess around with the debugger, press the buttons in it just to see what they do, you won't break anything. It is literally your most useful tool as a programmer, the swiss army knife of bug-squashing. Once you've got a proper handle on the debugger, there's usually very few problems that will make you come crawling back to the forums for help and you'll have a much deeper understanding of what is actually going on, which'll help your actual programming as well.
 

woods

Member
aye.. i find myself experimenting often... usually ending up with semi functional messes like this one here ;o)

im not afraid to use my head for a hammer.. so long as i can learn from it
 
Top