Pages: [1] 2 3
  Print  
Author Topic: Some common GML issues, and dealing with them in Enigma?  (Read 5039 times)
Offline (Male) polygone
Posted on: December 21, 2010, 02:44:15 PM

Contributor
Location: England
Joined: Mar 2009
Posts: 810

View Profile
These are some common big issue I find with GML, I am wondering how/if they can be dealt with in Enigma.

1. Referencing multiple instances

Often you want to be reference or loop through a set of multiple instances, such as when using instance_nearest or instance_position. Is there functionality in Enigma which allows you to easily do this?


2. Referencing resources as strings

People often find themselves wanting to reference a resource index from it's corresponding string, being lazy they don't wish to map all the object indexes to strings manually, so is there a way this can be done easily in Enigma?

3. Exiting Code

Certain functions like game_end and some others do not exit code instantly but only when the current event has finished executing, I presume this behaviour is mimicked in Enigma. A problem however arises when you wish to exit in a script for example because in GM you can only exit the current script and not the entire event being executed. Is there a way for Enigma to deal with this?

4. get_real

A get_real() function asking the user to enter a real value is a very commonly wanted feature.

5. Pausing The Game

Very common feature which considering how commonly it's wanted isn't really as easy as it should be to implement in GML. Maybe a nicer system could be worked out for Enigma?

6. Multiple collisions issue

This is rather common, a user adds some code to a collision event like increasing the score or something but undesirably GM winds up increasing their score every step that the collision is occurring. A way for users to handle this nicely would be useful, I think a collision enter event and a collision exit event will suffice.

7. Timers

Another very common problem. Someone for example wants to draw some text for 4 second when they press a key. This isn't really nice to do in GML it involves creating an alarm and such then drawing from a variable in the draw event. You cannot simply do it from key press event of whatever. Now I don't know exactly how you could achieve such a functionality in Enigma but if you could work a out some nice mechanism for it, it would be rather useful for beginners.
« Last Edit: December 21, 2010, 05:26:48 PM by polygone » Logged
I honestly don't know wtf I'm talking about but hopefully I can muddle my way through.
Offline (Unknown gender) TheExDeus
Reply #1 Posted on: December 21, 2010, 03:03:32 PM

Developer
Joined: Apr 2008
Posts: 1919

View Profile
Quote
1. Referencing multiple instances
And how exactly the return value would look like? If you just thought something like instance_nearest_nth (which would return n'th nearest instance) then that needs just a separate function.

Quote
2. Referencing resources as strings
As string? Like "sprite_player" would return a sprite index? But then how would you write "sprite_player"? If you thought something like sprite_return("sprite_player"), then I think that could be possible, but in the same way as variable_set or execute_code.

Quote
3. Exiting Code
What do you mean with "entire code"? Do you mean the event? If so, then I guess it could be cool if you could stop the rest of the event to be executed. But to exit from code in any place you can just type "exit;".
Logged
Offline (Male) polygone
Reply #2 Posted on: December 21, 2010, 03:56:33 PM

Contributor
Location: England
Joined: Mar 2009
Posts: 810

View Profile
Quote
And how exactly the return value would look like? If you just thought something like instance_nearest_nth (which would return n'th nearest instance) then that needs just a separate function.
I don't know, I don't have all the answers, I was wondering if Josh had thought about it. The problem with nth type functions is that they will be a lot slower to cycle through instances. The way I see to do it is by return a list of some sort then you could then cycle through the list somehow or use the with() command perhaps

Quote
As string? Like "sprite_player" would return a sprite index? But then how would you write "sprite_player"? If you thought something like sprite_return("sprite_player"), then I think that could be possible, but in the same way as variable_set or execute_code.
Yes like that, again I don't have all the answers. Although thinking about it some more maybe this naturally wont be such an issue in enigma since it will I presume have better resource cycling functions then GM I imagine you could write a function easily to loop through a resource and map it yourself. This is a common thing that 'newbies' wish to do though so maybe Enigma could automatically map resource indexes to string values then Enigma could actually call reference resources from string values. As horrible as this sounds it might be worth considering...

Quote
What do you mean with "entire code"? Do you mean the event? If so, then I guess it could be cool if you could stop the rest of the event to be executed. But to exit from code in any place you can just type "exit;".
Yes I meant event, edited the post to include that.
« Last Edit: December 21, 2010, 07:58:20 PM by polygone » Logged
I honestly don't know wtf I'm talking about but hopefully I can muddle my way through.
Offline (Unknown gender) TheExDeus
Reply #3 Posted on: December 21, 2010, 04:22:21 PM

Developer
Joined: Apr 2008
Posts: 1919

View Profile
Quote
The problem with nth type functions is that they will be a lot slower to cycle through instances.
Well you will always need to cycle trough all the instances. Its the only way to actually get the distance of thous instances (unless the room is divided in some regions and only instances in specific regions are checked etc.).

Quote
2. The function is able to cycle through instances itself
Well C++ is possible to return multiple values, and automatically populating an array or some ds could also be possible.

Quote
Yes like that, again I don't have all the answers.
No I just wanted to understand what you actually meant. For example:
Code: (C) [Select]
sprite_index="spr_player"; //this would not be good, because you can't actually return "spr_player" in this case. So context sensitive parsing could do this while compiling, but not real time.
sprite_index=sprite_get_string("spr_player"); //this on the other hand could be cool. Especially when some simple loops are necessary
Logged
Offline (Male) polygone
Reply #4 Posted on: December 21, 2010, 04:42:09 PM

Contributor
Location: England
Joined: Mar 2009
Posts: 810

View Profile
Quote
Well you will always need to cycle trough all the instances. Its the only way to actually get the distance of thous instances (unless the room is divided in some regions and only instances in specific regions are checked etc.).
But with using nth you will need to cycle through the instance multiple times, every time it is called which is much slower. Unlike if you return a list of instance which only requires cycling through once, which is why I think the first method is probably a better choice.

Quote
No I just wanted to understand what you actually meant.
I wasn't fixed on a method, I think they are both could actually be done. The first:

Code: (EDL) [Select]
sprite_index = "spr_player";
instance_create(x, y, "obj_player");
Is much easier to use and more naturally to beginners to use, but is a lot less 'coder' friendly. I believe this could be implemented if Enigma automatically maps resource indexes to corresponding string indexes. Then for all instance functionality it could check if a real is given, if so just return the index as normal or if a string is given then lookup from a map and return the corresponding index. From a coder perspective I do not particularly like this myself but it would make life easier for beginners who do not understand the difference between strings and indexes, many beginners try to do this in Game Maker.

Code: (EDL) [Select]
sprite_index = sprite_get_index("spr_player");
instance_create(x, y, object_get_index("obj_player"));
These _get_index functions returning the index from a string name could be added to compliment the object_get_name() and hopefully _get_name() functions for all the resources. This is much more coder friendly but would still require Enigma mapping all the resource indexes to corresponding string names.

Or maybe both could be implemented? I personally would never use either method because I think it's bad programming but I'm just thinking what might entice beginners to the program.
« Last Edit: December 21, 2010, 08:12:26 PM by polygone » Logged
I honestly don't know wtf I'm talking about but hopefully I can muddle my way through.
Offline (Male) Josh @ Dreamland
Reply #5 Posted on: December 21, 2010, 08:03:22 PM

Prince of all Goldfish
Developer
Location: Ohio, United States
Joined: Feb 2008
Posts: 2953

View Profile Email
Trying to sort through you two's bickering is frustrating, so I'll just reply to everything I see without linking the posts together.

First, Poly's original post:


1. Referencing multiple instances

In Game Maker, the only way to do this is multiple inheritance. Most worthwhile scripting languages today offer multiple inheritance, but this isn't compatible with GM's system at face value due to ambiguity in event inheritance. I am still abstracting ideas on how to resolve this, but some of them depend on Ism. I've actually been thinking about this myself for my own purposes, and this is what I have so far:
  • Introduce weighted multiple inheritance. The objects are traversed from least important to most important, each overriding the code of the previous one so any conflicts are resolved by taking from the most important. (In any case, the current instance is given highest priority). In the case of empty objects used as groups, all instances of all objects inheriting from that group object are identifiable by the ID of that group object.
  • A separate "tag list" deal for grouping. Like objects, only with no wasted space in the object table (or what have you).
  • Varied object types. Game Maker games have a lot of controller instances and parent objects for the sole purpose of grouping. Being able to add an object of each kind could improve efficiency and organization in general, and also solve this problem (you reminded me of it). This strikes me as the ideal solution, but depends GREATLY on IsmAvatar, who may not be easily sold on the idea, as well as on a new format for ENIGMA games, which will eventually be necessary but is not yet implemented.
2. Referencing resources as strings

I've never thought of this. It could be implemented but is not in any plan thus far. As you may be aware, maps are much easier to use in ENIGMA, but if you feel this is beneficial, file a suggestion in the tracker and I'll see about implementing it.

3. Exiting Code

I've been chewing on this issue for tears. Presently, scripts and DND are homogeneous; one exit means all exit. And, game_end() is instantaneous at the moment because I've not standardized an exit method. I've been waiting for someone to complain about this behavior before I modified it; suggestions are always welcome. I may define "exit" as goto <label generated at end of this script> and leave "return" as an omnipotent breaker. I Intended to do this about the time I index the code for quick position lookup on C++ compile error.

4. get_real

Already implemented in GTK interface; will be implemented in WinAPI when the widget APIs are finished and made available.

5. Pausing The Game

In the beginning, when the earth was young, young me wanted a pause feature that would put that kind of functionality in the hands of the user. Back then, it was intended that ENIGMA would have its own interface, and I would have a special resource to implement draw code for the pause screen. I haven't even presented the idea to Ism, because I have enough things on my wish list for her. Do you suppose it will require more than a single script executed each paused step? And should it paused immediately, or just after the frame? (You mentioned this point in another issue on this list).

6. Multiple collisions issue

Good suggestion! When I was new to Game Maker, I had this problem. I wanted to play a sound when crashing into an instance. An abs(hspeed)>0 check wouldn't work, because I just set the hspeed every time you pressed an arrow. So I ended up just checking that the sound wasn't playing. Your example's an even better one, though. The problem hasn't occurred to me in a while, and I never thought up a solution in ENIGMA. I'm a bit tired to think one up now, but I'll put some thought into it over the next while and tell you if I come up with something. My first impulse is a flag called "collision_fresh" which is set to TRUE or FALSE according to whether or not a key exists in a 2D map with the IDs of both instances. I have no idea the implications of the efficiency or simplicity of use, so I'll have to think about it further.

7. Timers
Rusky got me thinking about this problem not long ago. His solution didn't strike me as practical because I didn't see how to implement it (especially in ENIGMA), but the idea is an instantiable event (his proposal involved closures). Presumably this could be resolved by having easily forged first-class functions, but with the current state of both C++ and GML this just doesn't seem practical in full scale. They're illegal inline in C++, and var doesn't allow storing anything but real and string. What I may end up doing is allowing in-place declaration of alarms, which would mean no less bloat, but an easier interface. Again when the earth was young, I was thinking it'd be neat to have a when() {} statement, but I hadn't the foggeiest how to implement it. I may as well use the same mechanism to do so.
Or I can simply implement when() to mimic for(), and it could be used like this:
Code: (EDL) [Select]
when (i=0; i==10; i++) {
  //Alarm code
}
But that's kind of ugly. What do you think? My original idea was just when(condition), but I'm not sure the typical user is smart enough to say timer_explode = 0; when (timer_explode++ == 10) explode();



Upon reading the rest of the replies, they seem to have resolved themselves or have already been answered above. I don't have an API to iterate instances other than rooms, which you can do in GML like so:
Code: (EDL) [Select]
for(r = room_first; r != -1; r=room_next(r)) map[room_get_name(r)] = r; I could add such an API for all resources, if you like.
« Last Edit: December 21, 2010, 08:10:22 PM by Josh @ Dreamland » Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Male) polygone
Reply #6 Posted on: December 22, 2010, 07:27:29 AM

Contributor
Location: England
Joined: Mar 2009
Posts: 810

View Profile
Quote
Upon reading the rest of the replies, they seem to have resolved themselves or have already been answered above. I don't have an API to iterate instances other than rooms, which you can do in GML like so:
Code: (EDL) [Select]
for(r = room_first; r != -1; r=room_next(r)) map[room_get_name(r)] = r; I could add such an API for all resources, if you like.
I think it would be obviously useful to add an API for all resources, I've seen this wanted many of times in GM. It's just nice to have the consistency anyway. I presumed this would be something you would naturally add.

Quote
1. Referencing multiple instances
I've actually been thinking about this myself for my own purposes, and this is what I have so far:
... <snip>
Interesting ideas, maybe you could start a new topic to discuss some of these concepts further. As they could lead to some changes I think they are perhaps best discussed earlier rather than later.

Quote
2. Referencing resources as strings

I've never thought of this. It could be implemented but is not in any plan thus far. As you may be aware, maps are much easier to use in ENIGMA, but if you feel this is beneficial, file a suggestion in the tracker and I'll see about implementing it.
Like I said I'm not personally a fan of the process but I think it's little things like this which really add up in appealing to beginners. I'll put it in the tracker if that's the best place for it.

Quote
3. Exiting Code

I've been chewing on this issue for tears. Presently, scripts and DND are homogeneous; one exit means all exit. And, game_end() is instantaneous at the moment because I've not standardized an exit method. I've been waiting for someone to complain about this behavior before I modified it; suggestions are always welcome. I may define "exit" as goto <label generated at end of this script> and leave "return" as an omnipotent breaker. I Intended to do this about the time I index the code for quick position lookup on C++ compile error.
So the current behaviour doesn't actually match GM's but I presume you plan on changing it to do so. A little idea I thought of, which isn't exactly 'nice' but rather convenient for usage would be to have an optional argument for those functions. For example:

Code: (EDL) [Select]
game_end(inst)If it was plain called with no argument as game_end() it would execute as inst being false so would behaviour exactly like GM's but if wanted you could could call game_end(true) to end the game instantaneously. I'm not sure how this idea fits in with how Enigma currently works but I think the same concept could also be used for other functions, like instance_destroy(). It's a lot more naturally for beginners to be able to specify and argument to that function to destroy a specific object, I see this all the time. So I was thinking it could possibly be done in the same way as previously suggested:

Code: (EDL) [Select]
instance_destroy(obj)When called plain with no argument as instance_destroy() it would destroy the current instant exactly like GM's but if wanted you could call game_end(obj) to specify a particular instance to destroy.

Quote
5. Pausing The Game

In the beginning, when the earth was young, young me wanted a pause feature that would put that kind of functionality in the hands of the user. Back then, it was intended that ENIGMA would have its own interface, and I would have a special resource to implement draw code for the pause screen. I haven't even presented the idea to Ism, because I have enough things on my wish list for her. Do you suppose it will require more than a single script executed each paused step? And should it paused immediately, or just after the frame? (You mentioned this point in another issue on this list).
I'm not actually sure it would work very well having it's own interface, it sounds like a lot of work to implement as well which is never good for Ism.

I was thinking before of perhaps a separate set of functions for pausing, like something similar to that of the high-score functions. So it's functionality is not fully in the hands of the user but more generalised. This would be easy for most people if they want plain pause menus but then some pause menus also need to interact with the game itself which would be more difficult to do (maybe script_execute could be utilised by the user for this?).

I don't know really. It's not a particularly nice thing to implement but then it's also currently not very nice for the user to implement either which is why it would be rather useful if something could be worked out. Surely some other game creation programs use a pause system. It might be worth looking around to see how some of them have implemented it.

Quote
7. Timers
... <snip>
I'm going to think about this some more and get back to you. Again it's one of those things that isn't exactly very nice to implement but I'm sure something neat could be worked out.
« Last Edit: December 22, 2010, 03:13:57 PM by polygone » Logged
I honestly don't know wtf I'm talking about but hopefully I can muddle my way through.
Offline (Unknown gender) TheExDeus
Reply #7 Posted on: December 22, 2010, 08:02:56 AM

Developer
Joined: Apr 2008
Posts: 1919

View Profile
Quote
5. Pausing The Game
I usually just use room_persistent and move to another room. Very easy to implement, cool effects possible (like drawing the frame to a global surface and then blur it in the background in the pause room) and works great.

Quote
I've been chewing on this issue for tears.
Don't cry. Everything will be o..... *died*
Logged
Offline (Unknown gender) MrGriggs
Reply #8 Posted on: December 22, 2010, 08:29:07 AM

Member
Joined: Dec 2010
Posts: 128

View Profile Email
Code: [Select]
when(i,==10,mytimer) {
if Done = true;{
Dostuff();
}
{

Script When
Code: [Select]
if argument2.firstran == false {
timer = string(argument3)
firstran = true;
//First ran code
argument2.Done = false;
argument0 = 0;
}


if (argument0 !argument1) {
argument0 +=1;
}
if (argument0 argument1) {
argument2.Done = true;
}







Would something like this not work for a timer?
« Last Edit: December 22, 2010, 11:25:26 AM by MrGriggs » Logged
Offline (Male) polygone
Reply #9 Posted on: December 22, 2010, 10:57:33 AM

Contributor
Location: England
Joined: Mar 2009
Posts: 810

View Profile
OK I have thought about things some more and I think come up with a good setup. Adding these features would be hugely beneficial, allowing things to be done much more simply and I believe actually more efficiently than is currently possible. The reason I see it could be more efficient is I presume once executed these checks could be completely removed internally, whereas a user cannot do this themselves with a standard alarm or step check, the alarm or check will always remain even if it's not being used any more.

Timer Event

I think you should add a separate event for timers, which is executed every step until the count hits 0. Just like alarms, eg:

Code: (EDL) [Select]
timer[0] = 4*room_speed;
Timer 0 Event:
Code: (EDL) [Select]
draw_text(x, y, "some text");That would be the way of solving my original problem. (Except you wouldn't be able to draw in the event but you get the idea...).

Do Step Statement

The when statement suggestion I think should definitely be added also, it really is a more powerful system. I don't think it's best suited like a for statement though, more like a do, until statement and I think it should be renamed from when to something else I'm just using do_step for now but it will obviously need to be changed from that also. I'm seeing something like this:

Code: (EDL) [Select]
i = 0;
do_step
{
  draw_text(x, y, "some text");  //will draw some text every step
  i++;
}
until (i == 4*room_speed)  //until i is equal to 4*room_speed
When the until statement has been met the code will be removed from the game.

Alarm Statement

You could maybe also do a similar statement with alarms, I think the keyword when is more suited to this actually:

Code: (EDL) [Select]
i = false;
when (i == true)
{
  show_message("hello");  //will show the message when i is set true
}
Basically it would set a dynamic alarm, when it has been executed once the code can again be removed from the game I presume.

You could then do standard alarm functionality by combining the two. ie:

Code: (EDL) [Select]
i = 4*room_speed;
do_step
{
  i--;
}
until (i == 0)

when (i == 0)
{
  show_message("hi");  //will show the message in 4 seconds.
}
« Last Edit: December 22, 2010, 03:19:24 PM by polygone » Logged
I honestly don't know wtf I'm talking about but hopefully I can muddle my way through.
Offline (Male) Rusky
Reply #10 Posted on: December 22, 2010, 11:03:26 AM

Resident Troll
Joined: Feb 2008
Posts: 961
MSN Messenger - rpjohnst@gmail.com
View Profile WWW Email
GM already has a way to iterate over all instances:
Code: (GML) [Select]
for (i = 0; i < instance_count; i += 1) {
    if (instance_id[i].object_index == bomb_obj && distance_to_object(instance_id[i]) < 100)
        instance_destroy()
}
Logged
Offline (Male) polygone
Reply #11 Posted on: December 22, 2010, 11:12:57 AM

Contributor
Location: England
Joined: Mar 2009
Posts: 810

View Profile
That code is also a lot slower than if it was done internally. And it's long winded to write. Looping through instance_position like that is even slower.
« Last Edit: December 22, 2010, 11:15:53 AM by polygone » Logged
I honestly don't know wtf I'm talking about but hopefully I can muddle my way through.
Offline (Unknown gender) freezway
Reply #12 Posted on: December 22, 2010, 04:22:59 PM

Member
Joined: Dec 2009
Posts: 220

View Profile
Are we gunna duplicate the create event happening before game start event bug?
Logged
if you drop a cat with buttered toast strapped to its back, which side lands down?
joshdreamland: our languages are based on the idea that it's going to end up FUBAR
/kick retep998
Offline (Female) IsmAvatar
Reply #13 Posted on: December 22, 2010, 06:48:48 PM

LateralGM Developer
LGM Developer
Location: Pennsylvania/USA
Joined: Apr 2008
Posts: 891

View Profile Email
freezway: This behavior is defined in events.res, I believe, which is a text file that you may freely modify. It seems like ENIGMA currently has game start occur first, then room start, and then create. This could be simply reordered as you desire.
Logged
Offline (Male) Josh @ Dreamland
Reply #14 Posted on: December 22, 2010, 07:00:19 PM

Prince of all Goldfish
Developer
Location: Ohio, United States
Joined: Feb 2008
Posts: 2953

View Profile Email
Polygone:
game_end(bool now = false): Good idea.
instance_destroy(obj): Already done. Since R3, and made public in R4.

Quote
I'm not actually sure it would work very well having it's own interface, it sounds like a lot of work to implement as well which is never good for Ism.

I was thinking before of perhaps a separate set of functions for pausing, similar to the high-score functions maybe. So it's functionality is not fully in the hands of the user but more generalised. This would be easier for plain pause menus and for most people to use but then a lot of pause menus also need to interact with the game itself which would be more difficult to do (maybe script_execute could be utilised though?).
Actually, it'd be as simple as another setting script like the four she offers now under "ENIGMA Settings." Each of those is supposed to be game-specific, and Definitions scripts are supposed to be instantiable. ENIGMA will, at that point, let you set code to be executed before game start, after game end, and (with my proposed addition) during game pause.

I think you should add a separate event for timers, which is executed every step until the count hits 0. Just like alarms really...
I see no distinction between your proposal and additional alarms. We could easily make alarms unlimited.

The when statement suggestion I think should definitely be added also, it really is a more powerful system. I don't think it's best suited like a for statement though, more like a do, until statement and I think it should be renamed from when to something else
when(){} and do_step{}  are two entirely different mechanisms, as I'm sure you realize. They are the step-based equivalents to if() and while(), respectively: if() and when() are performed only once (when the condition is finally met), where do_step() and while() are performed throughout the duration of a condition (or its negation). In ENIGMA, no distinction is made between while !() and until()... that may work to our advantage. How do you feel about when(){} and step(){}[until()]?
Code: (EDL) [Select]
// Destroy the next gate we come near. It'll probably create effects in its destroy event.
when (distance_to_object(instance_nearest(x,y,gate))<32)
{
  instance_destroy(instance_nearest(x,y,gate));
}

// Implement an alarm using only one local variable
local int do_explode = 0;
when (++do_explode == 30)
{
   // Alarm code
}

// Emit particles until not dizzy
local int dizzy = 100;
step {
   instance_create(x,y,dizzy_spiral);
   dizzy--;
} until not dizzy

Rusky:
...Sometimes I think you don't know what with() does...

Code: (GML) [Select]
for (i = 0; i < instance_count; i += 1) {
    if (instance_id[i].object_index == bomb_obj && distance_to_object(instance_id[i]) < 100)
        instance_destroy()
}

Is apparently the same as, but much, much less efficient than
Code: (GML) [Select]
with (bomb_obj) if (distance_to_object(instance_id[i]) < 100)
        instance_destroy()

Freezway/Ism:
The create event in GML is the equivalent of a constructor in C++. By logical dynamic, the create event must be the first event an instance can perform. Events.res has no control over the create event; it only lists it with an integer mapping to Mark's specification.
Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Pages: [1] 2 3
  Print