Jump to content


Photo

Instance creation.


  • Please log in to reply
25 replies to this topic

#1 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 01 September 2011 - 09:24 AM

Okay... while doing the HTML5 version, it's become apparent that there are some pretty serious issues with instance creation.

First, if you make one during the draw event, it's still drawn, but at the wrong depth for that frame - namely because it could be past the "sort" order it should have been drawn in, and they are all gathered to the end. Try this yourself. Create a sprite that's supposed to be UNDER the current one, and for a single frame it'll "flick" over the top of the one creating it. It's nasty.

If you create one during "processing", it can also have some nasty side effects, but I've also discovered that processing isn't done in "depth" order (which seems wrong to me), but object->instance->creation order. So when you make a new instance, it'll get stuck to the end of the list, and then processed in the same frame as well.

This all seems wrong to us. Really, we think if you create an instance, it shouldn't become active till the next game cycle/frame, that way they can cleanly be created and everything will happen in the correct order.

So. What we'd "like" to do, is start "remembering" creations, and creating them after everything else has processed, probably at the end of the frame. This could have a reasonably significant impact on some games. Many will use the "current" state, and you might lose that by the end of the frame.

The other thing we could do, is let you "create" things there and then (so the create event runs), but they only become "active" the next frame. So the instance is created, but no step/draw/alarm/key/anything event is processed until it becomes active the next game cycle.

What do you think?

Please try to stay on topic here.... It's an important call, and I will have to read everything, so try not to rant/ramble :whistle:

Thanks,
Mike
  • 0

#2 Nocturne

Nocturne

    Nocturne Games

  • Administrators
  • 25708 posts
  • Version:GM:Studio

Posted 01 September 2011 - 09:36 AM

I would think waiting untill the next step/cycle to create the object would be cleaner...


That said, some questions!

If you are running the create event only and then nothing until the next step, could that not cause timing issues? I mean, I create an instance and in that instances create event it creates another instance that sets an alarm to 1 step. So the alarm should run the next step, BUT if we are holding the newly created instance for the next cycle the alarm will take 2 steps to run... No?

If running the create event and waiting for the next step, would you be able to use the user_events in the create event or even event_perform so that another event happens in that instant too?

If I have (for example) an explosion object that creates 30 debris objects and they are all going to be held in "GM limbo" until the start of the next step, could this have a negative impact on performance? Especially if I need to create various instances of these objects in one step (multiple enemy deaths for example).

EDIT : tidied up the wording!

Edited by Nocturne, 01 September 2011 - 09:50 AM.

  • 0

U1FVsm3.png

40799.png


#3 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 01 September 2011 - 09:50 AM

I mean, I create an instance and in that instances create event it creates another instance that sets an alarm to 1 step...

This is the main reason I think executing ONLY the create event would probably be better. I think we would also allow you to execute events yourself by calling event_perform(), because this would allow you to "force" an update if you really wanted one.

But I think "automatic" processing of instances (step/draw/etc) should be delayed until the next "whole" frame.
  • 0

#4 Nocturne

Nocturne

    Nocturne Games

  • Administrators
  • 25708 posts
  • Version:GM:Studio

Posted 01 September 2011 - 09:56 AM

Hmm... In that case it would seem that running the create event and then "waiting" would be best, especially if you can still call the other events if needed. One other thing, if you do change this as you suggest, will you be changing the process order too, to a depth based system (as you mention)?
  • 0

U1FVsm3.png

40799.png


#5 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 01 September 2011 - 10:08 AM

The HTML5 one already does this, mainly because we think that's more "correct" anyway. As to the delphi runners. Not sure. Depends how "deep" that code goes.
  • 0

#6 GrixM

GrixM

    GMC Member

  • GMC Member
  • 424 posts
  • Version:GM:Studio

Posted 01 September 2011 - 10:20 AM

The other thing we could do, is let you "create" things there and then (so the create event runs), but they only become "active" the next frame. So the instance is created, but no step/draw/alarm/key/anything event is processed until it becomes active the next game cycle.


I would prefer this solution.
  • 0

#7 LegacyCrono

LegacyCrono

    GMC Member

  • GMC Member
  • 616 posts

Posted 01 September 2011 - 10:32 AM

Sounds good for me. If the Create event were called instantly I wouldn't see a problem. If it were triggered at the end of the frame it would be troublesome though, as I often change variables declared on Create right after calling instance_create.
  • 0

Posted Image


#8 TheMusicDork

TheMusicDork

    GMC Member

  • GMC Member
  • 305 posts

Posted 01 September 2011 - 10:39 AM

I agree with the previous posts.
It should run the creation code the exact moment the instance is created. (i.e. with (instance_create(0,0,Obj){i=0} would still work even if the create event of the object was i=1)
Then it should start updating for the created instance on the next frame.
  • 0

|Storyline| 30%

|Graphics| 5%

|Sounds| 50%

|Awesome| 100%


#9 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 01 September 2011 - 10:50 AM

....as I often change variables declared on Create right after calling instance_create.

(i.e. with (instance_create(0,0,Obj){i=0} would still work.....


Yes, I agree. You should still be able to "use" the instance, it's just automatic system events wouldn't be fired till the next frame. (a little faffy, but vital I think)
  • 0

#10 paul23

paul23

    GMC Member

  • Global Moderators
  • 4147 posts
  • Version:GM:Studio

Posted 01 September 2011 - 11:16 AM

Wait a minute: this means that instances can't be used as containers anymore?


I'd say this "might" be good. But then if and only if there's a "structure" resource too which can act as data container (with simple variables as data members).


Things like creating linked lists are now most clean done by having instances: as this allows actual naming of variables and just seems "correct". (Being influenced heavily by OO programming languages).

objLinkedList
Member variables: FirstNode, lastNode
"Member methods": GetFirstNode(), GetLastNode(), InsertNode(N, data), RemoveNode etc etc

objNode
member variables: NextNode, PrevNode, Value
"Member methods": GetNextNode(), GetPreviousNode() etc etc

Where member methods are considered to be called from within a "with" statement:
list = CreateLinkedList()
with (list) {
    InsertNode(0,"hello");
    InsertNode(1,"world");
    with (GetFirstNode()) {
        return GetNextNode().Value;
    }
}

Now I hear already people saying "But use lists". - Well no, in such a trivial case list might be sufficient. But consider something more advanced, ie my mathematical expression parser. For only 1 simple expression to parse more than 100 instances are "created" (and many destroyed immediatelly after parsing). If I had to use lists there I would've gained a headache with memory management long ago already, and probably never completed it. Using lists-of-lists-of-lists-of-lists is not fun.
  • 0

#11 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 01 September 2011 - 11:24 AM

No, you can. You can still "access" them manually. It's just the events won't be triggered until the next frame.

This is going by the "create" is executed, but no automatic events are thrown method. In retrospect, this is the only one you can do as (Nocturne rightly said), if you did a create inside a create, it would stagger over many frames, and that's just wrong.
  • 0

#12 paul23

paul23

    GMC Member

  • Global Moderators
  • 4147 posts
  • Version:GM:Studio

Posted 01 September 2011 - 11:39 AM

Hmm that's "something" I missed then - but the destroy event should also be possible to "activate" (maybe make the destroyed instance go into limbo state, where it still exists but is automatically destroyed at the end of the step?). Else you still can't use instances for "internal" scripts - where you execute a script during an event and you wish the script to cleanup itself too.


Funny I was just about to write a tutorial about using instances this way (As I personally experienced my productivity grow a lot when I started doing this), I'll see how this turns out.
  • 0

#13 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 01 September 2011 - 12:43 PM

I'll just point out... creating and destroying instances is pretty slow. You should only do it when you really need to, not when you just need a return "object". You can easily kill performance if you do this....

Oh... and if you call instance_destroy(), the event is called directly.
  • 0

#14 Manuel777

Manuel777

    InvaderGames

  • GMC Member
  • 3578 posts
  • Version:GM:Studio

Posted 01 September 2011 - 02:35 PM

The other thing we could do, is let you "create" things there and then (so the create event runs), but they only become "active" the next frame. So the instance is created, but no step/draw/alarm/key/anything event is processed until it becomes active the next game cycle.

I'll go for that one.. not much more to say..
  • 0

@MEtchegaray7

gplussignature.png

May this signature be frozen on the old GMC until the end of times. Or YoYo takes it down.


#15 Smarty

Smarty

    GMC Member

  • GMC Elder
  • 7522 posts
  • Version:GM:Studio

Posted 01 September 2011 - 06:45 PM

First, if you make one during the draw event, it's still drawn, but at the wrong depth for that frame - namely because it could be past the "sort" order it should have been drawn in, and they are all gathered to the end. Try this yourself. Create a sprite that's supposed to be UNDER the current one, and for a single frame it'll "flick" over the top of the one creating it. It's nasty.

Yes, but... It's also rather evident that depth ordering has already happened once the draw event is executed. Shouldn't we argue that the draw event isn't the best place to create new instances?

I'd suggest to emphasize that the draw event should be used to do exactly what it says - draw stuff. You're pretty darn late to figure out in your draw event that you still need to create instances. Other events also have commands that you rather shouldn't use in them (drawing on surfaces for example shouldn't happen in the draw event, and the Create event is a good bad place to create instances of the same or inherited objects if you want to nuke your game into orbit).

If you create one during "processing", it can also have some nasty side effects, but I've also discovered that processing isn't done in "depth" order (which seems wrong to me), but object->instance->creation order. So when you make a new instance, it'll get stuck to the end of the list, and then processed in the same frame as well.

Actually it makes sense to me - especially if you wish to "chain" instances and have them assume that earlier created instances have been handled already. Practical example: consider a caterpillar where each next segment instance is dependent upon the previous having updated its status, because it needs to follow it. It's very easy to reference the previous instance and to use its new position as long as you know they have been executed in order.

So. What we'd "like" to do, is start "remembering" creations, and creating them after everything else has processed, probably at the end of the frame. This could have a reasonably significant impact on some games. Many will use the "current" state, and you might lose that by the end of the frame.

Tricky. Consider creating a player instance in code somewhere, and in the same step have a check to see if there's any player instance left and end the game if there isn't. In the current state it doesn't exist, while one has been created just shortly ago. Will it end immediately? I can see loads of games run into problems because of this.

The other thing we could do, is let you "create" things there and then (so the create event runs), but they only become "active" the next frame. So the instance is created, but no step/draw/alarm/key/anything event is processed until it becomes active the next game cycle.

What about collision events of other instances with the created instance? Are these ignored too? Also, I can imagine one would like to create an instance to process collision events on the spot, and deal with these on the spot (meaning, in the same step).
  • 0

#16 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 01 September 2011 - 08:13 PM

See, this is the problem. Your assuming everything will just work in the order it's supposed to, but that's impossible. If you have an object at depth 10, and create an object at depth -100, then it can be processed+drawn after the creator. However, if you have one at 0 and create one at 10... then your stuffed. your now executing out of order - or your instance just doesn't get processed at all.

What I'm proposing is that the instances are all created and are semi-active; that is the data can be used, you can call events on them yourself, and things like with() statements would all "just work" as normal. But when the system does its "step" processing for things like step_begin, step, draw,alarms etc... none of these will be processed. This keeps it "consistent", which is some thing it just isn't at the moment.

actually... You may still get issues while using with() statements as well. If you did with(objtype) and inside there you created another one, you "might" not get that one, depending on how the list is processed. There's so many issues where "new" items modify current working lists, that it's not only unpredictable, but may cause a crash in some extreme cases. You can solve a lot of these by "copying" the working array, but that sucks as it's a major performance hog. I suspect iterating over a WITH(*) list will be fine, as objects are generally appended to the END of the list, and as with() statements aren't depth ordered, you'll get away with it.

Also...in your centipede example, even now you would have to do this with extreme care, making use of step and step_end so that you could be sure all previous "body" parts had been processed. Processing order of instances is odd, it runs through object types, and the instances they hold, then onto the next object type and so on. This means it's probably not working in the order you think it is, and that makes life tricky.

What I want to do is process objects by depth order. So while this makes objects at the same depth an unknown order, it does at least mean you have the ability to SET the order yourself. Currently processing and drawing orders are completely different, which is another issue I think.

What we have also been considering is adding a new event; and "create_active" one. This would allow you to get an event on "activation" of the object (i.e. when the object actually starts being processed the next game frame). You could then use this to setup things where normally you'd have had to set a timer to 1 (which I hate!).

Oh... and instance "numbers" are going away. This already happens in the HTML5 version. In the room editor you now use "names" or constants for instances, and they are renumbered dynamically meaning you can no longer just increment and decrement a number and expect to find the next/previous instance. Going forward, IDs will probably be "random" numbers rather than indexes. Going forward even more... this means that when developers just think of them as handles, we can dynamically allocate them, and possibly even use pointers directly - but that's currently impossible as everyone just "plays" with them.

Lastly... as to collisions. There would be no collisions with it until the next frame, which actually is fine... as no one will SEE it until the next frame as well!

Now.. yes. Some games (possibly quite a few), will have issues with this. But this is not a reason to just "go with what we have". It's seriously flawed, and while we could "lock off" commands in events (so no create in draw, and no draw in step etc..), this would inhibit new users ability to learn, and that's something we don't really want to do. When your learning, you have to be able to write bad code, because that's the easiest to write, and so it's the easiest way to learn the language. This is particularly true when your teaching yourself. So... I'm with you, draw in draw, and process in process. We would love to. But can't and won't.


Oh roll on the next version of GM, where we can redesign some of the basics... :verymad:
  • 2

#17 paul23

paul23

    GMC Member

  • Global Moderators
  • 4147 posts
  • Version:GM:Studio

Posted 01 September 2011 - 08:58 PM

hmm the only thing that I see it will break is those custom (temporary) "collision shapes" then: sometimes you wish to check if objects are within a certain "shape". So you temporary create an instance with the sprite as mask and check for collisions against that instance. Now you "just" have to keep a temporary background instance if you wish to use this feature.


Spoiler

  • 0

#18 NakedPaulToast

NakedPaulToast

    GM Studio/Mac/Win

  • GMC Member
  • 8808 posts
  • Version:GM:Studio

Posted 01 September 2011 - 09:03 PM

Oh roll on the next version of GM, where we can redesign some of the basics...



I'm finding this statement to me one of the most encouraging statements I've seen of late.

I like the idea of redesigning GameMaker from the ground up. You guys have learned a lot about GM (Windows, Mac, HTML5 and Mobile), there are now prolific game creators withing the halls of YYGs who contribute ideas from a usability point of view. Redesigning with considerations for multi-platform and performance instead of retrofitting is what is needed.

While backwards compatibility is always nice, it's also an inhibitor.
  • 0

If the Bible truly is inspired by God, you would think that somebody as omnipotent and all-knowing would have known to get his message out using TCP instead of UDP.

 


#19 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 02 September 2011 - 07:41 AM

While backwards compatibility is always nice, it's also an inhibitor.

Yeah... the big problem is that we use older games to test things and make sure it's all running properly, and at somepoint, we just have to decide; "nope....that's going to be broken". But it's a nasty thing to have to do. :(

hmm the only thing that I see it will break is those custom (temporary) "collision shapes" then: sometimes you wish to check if objects are within a certain "shape". So you temporary create an instance with the sprite as mask and check for collisions against that instance. Now you "just" have to keep a temporary background instance if you wish to use this feature.

Not at all (although I would never do that as it's pretty slow), you can manually call collision checks on an instance. It's only the automatic stuff that'll not run. And actually... if your making an instance specifically to test collisions, isn't that what your going to do anyway? Or would you normally have waited for the system collision check to happen?
  • 0

#20 Bleed

Bleed

    Chevalier

  • GMC Member
  • 763 posts
  • Version:GM:Studio

Posted 02 September 2011 - 09:54 AM

Yeah... the big problem is that we use older games to test things and make sure it's all running properly, and at somepoint, we just have to decide; "nope....that's going to be broken". But it's a nasty thing to have to do. :(



Just a quick question: Can't prof. Mark Overmars inform you why it was built that way?...just so you'd avoid running into problems later on?
But, personally i think it's an awesome idea since one frame/cycle, even if "cut out", wont make such a big difference and if it helps you sort things out in a timely and orderly fashion, even better.
Yet, is it at all possible that depth (just the depth) isn't taken into consideration the first frame anyway and just comes into play after the first frame? Possibly just present the first frame for initialization purposes?

Edited by Bleed, 02 September 2011 - 10:46 AM.

  • 0

rlztjp_zpsoqffaixe.jpg


#21 chance

chance

    GMC Member

  • Global Moderators
  • 8762 posts
  • Version:GM:Studio

Posted 03 September 2011 - 01:06 PM

Just a quick question: Can't prof. Mark Overmars inform you why it was built that way?...just so you'd avoid running into problems later on?

At this point, I'm confident that Mike, Russell et. al. know more about these subtleties than Mark does. Not to sound critical of Mark, but I doubt there was a particular reason for the depth flipping issue Mike identified. Probably just an over-sight. Or something Mark decided to ignore, since the DRAW event isn't a good place to use instance_create anyway.

Mike and Russell are responsible for future generations of GameMaker now, so they should decide these issues. In any event, either of Mike's alternatives seems like an improvement over the current situation. I certainly like the idea of processing by "depth" order -- not by object->instance->creation order.
  • 0

#22 kburkhart84

kburkhart84

    Firehammer Games

  • GMC Member
  • 2026 posts
  • Version:GM:Studio

Posted 04 September 2011 - 12:10 AM

I agree that objects should not be fully processed until the next frame, except for the create event, per Mike's idea. You still could use the object for anything, and this would make more sense in the long run.

About processing order being the same as the depth order, either one I think it should be the same. Or two, (probably not feasible) you could have a separate "process depth" value, which you could use to force the non-drawing events to be ordered with. This probably isn't worth the time it would take to implement optimistically, so you are probably better off like you say it is in the HTML5 runner, with processing and drawing using the same depth value and order.
  • 0

My KBInput system is now on the marketplace here.  It wraps up nice and tight GMStudio's input system into a few function calls making a user configurable input system that works the same regardless of what inputs the player has chosen including keyboard, mouse buttons, and gamepad/joysticks using DInput/XInput.  The support forum topic for it is here.


#23 paul23

paul23

    GMC Member

  • Global Moderators
  • 4147 posts
  • Version:GM:Studio

Posted 05 September 2011 - 06:40 AM

I doubt using "depth" for the order of instance events (apart from drawing) is good. - I DO think allowing events to be manually order is good, more features is more better for me :P - it is just that I don't think reusing depth would be the best way.

Currently it is very clear how depth works: you can change it up to the drawing event, where changing the depth has no effect anymore. So I often change it during a "controller's" end step event.
Using the depth for every event I would be unsure when it is "safe to edit" - or would it just take effect a full step later? In that case I wonder if drawing wouldn't "lag behind" the rest.


I'd suggest using an array for this:
depth[ev_draw, 0] = X
depth[ev_other, ev_user0] = Y
depth[ev_create, 0] = Z

Though maybe that would make things too difficult?
  • 0

#24 Smarty

Smarty

    GMC Member

  • GMC Elder
  • 7522 posts
  • Version:GM:Studio

Posted 05 September 2011 - 09:22 AM

What I want to do is process objects by depth order. So while this makes objects at the same depth an unknown order, it does at least mean you have the ability to SET the order yourself. Currently processing and drawing orders are completely different, which is another issue I think.

OK. What I'm curious about is this: in what order are instances at the same depth going to be drawn? Can we be confident that they are drawn in creation order, like they used to be?

I'm asking this because I'm working on a game where I create instances in code in such a manner that the shadow border is nicely overlapped by instances to the right and bottom of other instances. If any instance is destroyed, the underneath shadows of nearby instances automatically appears (top-left is the first instance, bottom-right the last, creation order is left-to-right first, top-to-bottom second).

I know I can nicely solve this by giving each instance its own depth, but the reason I rely on the same-depth approach is that I've noticed a significant performance drop if unique depths are used. Right now, depth ordering is much faster when instances have been created in (reverse) draw order in the first place (because of this, I've applied scripts that pick up the tiles and instances as they were put in the room editor, destroying them and recreating them in reverse depth order).

Oh... and instance "numbers" are going away. This already happens in the HTML5 version. In the room editor you now use "names" or constants for instances, and they are renumbered dynamically meaning you can no longer just increment and decrement a number and expect to find the next/previous instance. Going forward, IDs will probably be "random" numbers rather than indexes. Going forward even more... this means that when developers just think of them as handles, we can dynamically allocate them, and possibly even use pointers directly - but that's currently impossible as everyone just "plays" with them.

No objections whatsoever. I hope this is going to happen for all resources. This would increase performance as well.

Now.. yes. Some games (possibly quite a few), will have issues with this. But this is not a reason to just "go with what we have".

Never suggested that you should... I was just prodding at the proposal to identify where it would fall over for some users.
  • 0

#25 Mike.Dailly

Mike.Dailly

    Evil YoYo Games Employee

  • Administrators
  • 5277 posts
  • Version:GM:Studio

Posted 05 September 2011 - 09:58 AM

OK. What I'm curious about is this: in what order are instances at the same depth going to be drawn? Can we be confident that they are drawn in creation order, like they used to be?

Nope. Anything at the same "depth" will have a random order, although we'll make sure that order is consistent so it doesn't flicker. If you want something at a lower depth, then set it like that.

Currently the slowdown probably comes from the annoying sort that's done each time the scene is drawn. This will be going away (the C++ and HTML5 versions don't have it), and that speeds things up considerably. We're using an ordered list rather than what GM8.1 does, which is sorting it all the time.

Once this happens, it'll be up to you whether you upgrade to the next version or not. Chances are your better finishing your game on the current one, and starting a new game on the newest version. But that's up to you.
  • 0

#26 Smarty

Smarty

    GMC Member

  • GMC Elder
  • 7522 posts
  • Version:GM:Studio

Posted 05 September 2011 - 10:36 AM

Once this happens, it'll be up to you whether you upgrade to the next version or not. Chances are your better finishing your game on the current one, and starting a new game on the newest version. But that's up to you.

OK, thanks. Since I'll be aiming for the mobile platform eventually, I gather I had better change course anyway.

Back on topic - now that we know what you planned with instance creation, how is it going to work out with toggling backgrounds, creating particles and adding tiles? Are they going to become 'active' (and visible) in the next step as well?
  • 0