Volnaiskra
  • Spryke
  • Volblog
  • downloads
  • Presskit
  • About

What's the Scoop on the Scope - all about scoping in Clickteam Fusion 2.5

9/6/2017

11 Comments

 
I recently started putting together a large article about various good practices Clickteam Fusion 2.5 developers should be aware of. A 'Top 30 tips' type of thing, aimed primarily at beginner and intermediate users. One of those tips was: Learn how Fusion scopes.

But as soon as I waded into this topic, it quickly became apparent that it required its own blog post. Its own very long blog post. Well, here's that very long blog post. (Stay tuned for the article with the other 29 or so tips - that's probably coming in the next weeks). 

Picture
​If you're not familiar with the word "scope" in the context of Clickteam Fusion, don't worry - you're almost certainly familiar with the concept. One of the great things about Fusion is that it frequently scopes things automatically, in a way that is intuitive to the user....most of the time. 

The basics of scoping in Fusion are very simple and easy to grasp. But as will soon become apparent, it becomes pretty byzantine the deeper you dig, with all sorts of unintuitive rules and weird exceptions that will sooner or later generate frustrating bugs in your projects, unless you familiarise yourself with their hidden idiosyncracies. But fear not. At the end of this post, I summarise it all into some simple guidelines. 

Scoping in Clickteam Fusion 2.5 - The Basics

OK, let's dive in. In this first example, the simple action set angle of pear to 45 results in all pears being tilted. Naturally.
Picture
Example basics #1 ​
However, in the next example, the exact same action has only managed to rotate two pears. That's because the preceding condition (Y position of pear > 100) has scoped those two pears. ​In other words, Fusion has singled out those pears which have a Y value greater than 100px (ie. are visually lower down, and closer to the bottom of the screen, than y100). Thus, the subsequent action, because it takes place in the same event, now only concerns itself with those pears. I'm probably not telling you anything new here.
Picture
Example basics #2 ​
​In the next example, notice that this scoping is cumulative. Both conditions have combined to scope just one pear (the only pear that happens to be both lower than 100px on the Y axis and further right than 100px on the X axis). And both actions have honoured that scope (only the scoped pear was tilted, and only the scoped pear was alpha-faded).
Picture
Example basics #3

Scope on the left

S​o far so good, right? In most situations, scoping is as straight-forward as this. However, there are certain situations where Fusion's idiosyncrasies can make scoping less intuitive. One case is when two objects are compared. Look at the following two examples:
Picture
Example left #1
Mathematically, both above events appear to be identical. In each case we're talking about pears which are lower than apples. Yet only the pears in the first example were successfully scoped (the second example applied the tilting indiscriminately to all pears).

​This is because when you compare two objects in a condition, Fusion generally only scopes the leftmost object. If this seems weird, then it may help to remember that a condition in Fusion is essentially an IF statement.

​Let's replace the apple with the number 100 again, and look at these two events again:
Picture
Example left #2
The first statement, essentially, reads as follows: 
If the Y position of a pear is greater than 100, then rotate the pear.
 

The second statement reads: 
If 100 is less than the Y position of a pear, then rotate the pear. 

While that second statement isn't exactly broken, it's kind of....odd, right? The focus of the sentence seems now to be the number 100, so it doesn't feel entirely right to assume that we're talking about any specific pears. It's a bit like, instead of saying.....

Fred went to the the third drawer, and got himself a spoon

.....I instead said.....
 
The drawer was the third from the top, and Fred got himself a spoon

There's something a bit off about the second statement, right? You can still infer that Fred's spoon came from the same drawer that was the third from the top, but it's not 100% clear. And since computers have a much lower tolerance for ambiguity than humans do, you can imagine that a computer would struggle to make that inference.

Hopefully this will help you get an intuitive sense of why Fusion only scopes objects from the left. Regardless, as long as you remain aware of this idiosyncrasy, it's not likely to cause you many problems.

Scoping and overlap

There are some exceptions to the 'scope on the left' rule. One such exception occurs, thankfully, when we test for overlap.

As the following example shows, when you test for overlapping between two objects, both objects are scoped, regardless whether they're on the left or right. Just as well - testing for overlaps would be near-pointless if it didn't result in both specific objects being scoped! The same is true when testing for collision, by the way. 
Picture
Example overlap #1

Scoping and newly created objects

​Creating new objects also affects scoping. When a new object is created, all subsequent actions in that event will be scoped to the newly created object(s).

Notice in the next example how only the 2 newly created pears are tilted and faded, while the original 5 pears are untouched.
Picture
​Example newlyCreated #1

Now notice below how the same event - but with the actions reordered - achieves a very different result. Because the fading and tilting actions appear at the beginning, before anything is scoped, they apply to all pears. So all 5 original pears are modified. The 2 new ones are not modified because they only get created after the fading and tilting actions have taken place:
Picture
​Example newlyCreated #2

So far so good. But now let's shuffle these actions around a little:
Picture
​Example newlyCreated #3

Hmmm. Shouldn't one of those new pears (the one that was created earlier, at x50 y100) now be faded? Perhaps the second newly created pear is stuffing things up somehow? Let's remove it:
Picture
​Example newlyCreated #4
 
​Nope. We get essentially the same result. Both the fading and the tilting get applied to all previously existing pears, and not to the new pear (even though the fading is supposedly applied after it's created).

Perhaps newly created objects only affect scope when they are the topmost action? Let's try something:
Picture
​Example newlyCreated #5

Nope. In the above example (exactly the same as the preceding one, except that the tilting action is applied to apples instead of pears) the create new pear action does indeed affect scope. Only the newly created pear is faded. This all seems pretty inconsistent and weird. Who knows how exactly it works under the hood? But I can start to cobble together a hypothesis:

Perhaps, the first time an object is encountered in an action, its scoping is cemented for the rest of the event (including any scoping inherited from conditions, naturally). 

The above hypothesis appears to agree with everything we've seen so far. It explains that when a pear is first encountered in Example newlyCreated #5 it cements the scope to that 1 pear (only that pear is faded).

​And it explains that since the first pear action encountered in Example newlyCreated #4  applies to all 5 pre-existing pears, the scoping is cemented to those 5 pears (which is why fading is applied later to those 5, but not to the 6th that was created a moment after the scoping took place).

It also explains the next example. Because the first action applies to the 5 pre-existing pears, the third action does too. Thus, the newly created pear is neither destroyed nor faded. So far this hypothesis is looking good.
Picture
​Example newlyCreated #6

However, before I get too excited, I need to come clean and admit that, if you hadn't noticed already, the hypothesis doesn't actually fit with #1 newlycreated, where not 1 but 2 actions affect scope (both created pears are successfully faded and tilted).

My hypothesis also doesn't explain this:
Picture
Example newlyCreated #7
​The above example is identical to the previous one, but for the extra condition (Y position of pear >100). Yet while the newly created pear was spared in the previous example, it's destroyed in this one. Why? The first action here is clearly honouring the scope from the condition, since it destroys those pears which are lower than y=100 while leaving the top 3 intact.

So why does it also destroy the newly created pear? The newly created pear was neither part of the original scope (it didn't even yet exist when the scope was determined), nor does it qualify for that scope once it's created (since its Y position is less than 100).

Sadly, the best conclusion that I'm able to muster is that Clickteam Fusion is kind of like the English language: dazzling in its overall utilitarian simplicity, yet knotted with arcane rules below the surface that present seemingly even more exceptions than there are rules. People might think they know exactly how it works, but no fully does. Yet somehow we all get by anyway.

To try and summarise this section then: Creating objects at the beginning of an event automatically scopes those objects. This is highly useful. There may also be times when you want to create an object lower down in the action list - but do so at your peril, as it may produce unforseen consequences. 

Scoping and fastloops

Scoping can also be unintuitive when fastloops are involved. In the next example, the insertion of the fastloop has broken the scoping of the pears, resulting in all the pears being rotated, rather than just the ones that are Y>100. 
Picture
Example fastloops #1

​In a way, this makes sense, since the fastloop is wedged between the pear condition and the pear action. It shoots us out of event #1 and into event #2, thereby disrupting the hermetic nature of the event. We can perhaps forgive it for forgetting event #1's initial scoping once it eventually returns from the fastloop in #2.

So what happens when we move the Start loop "fade apple" 1 times to the end of the action list? 
Picture
Example fastloops #2

​Well, nothing. It's no longer wedged in between the pear condition and the pear action, and there doesn't seem to be any intuitive reason why the fastloop should continue to break the pear's scope. Yet it does. It would seem that the mere presence of a fastloop call anywhere in event #1 breaks the scoping of the pears. They are all tilted, rather than just the Y>100 ones. ​

It gets weirder though.

This time, look what happens if we modify the  fastloop to include a pear in it:
Picture
Example fastloops #3

​The fastloop successfully fades all the pears, but when it comes back to event #1, not only does it still not honour the Y>100 condition, but now it brings with it its own seemingly senseless scoping that no one asked for (the lower-right pear). In my view, this behaviour is no longer just a quirk but a definite flaw.

You might wonder what, if anything, was so special about that lower-right pear that gave it priveleged scoped status. The answer is that it happened to be the last pear that I created (in the frame editor). So at least it's a predictable flaw. But it does hint at some potentially useful functionality. Observe the next example:​
Picture
Example fastloops #4
​As before, the fastloop causes Fusion to dishonour the Y>100 scoping. And as before, the fastloop imprints its own scoping on the event. But this time, that scoping has a somewhat satisfying logic to it: we specially created two pears in our fastloop, and upon returning to event #1, it is precisely those two pears that are scoped. I can imagine some good uses for that.

But don't get too excited...
Picture
Example fastloops #5

​​This time, all we've done is add some extra actions to the fastloop, and suddenly our two newly created pears are no longer scoped. We are back to just the most recently created pear being scoped. I don't know about you, but this sort of behaviour seems far too flaky and unpredictable for me to ever rely on it. My advice is to exercise caution and avoid mixing fastloops and scoping. If in doubt, call the fastloop in a separate event.

Scoping and forEach loops

ForEach loops have their own scoping idiosyncrasies that are often different from those of fastloops. For one thing, a forEach loop's mere presence doesn't break scope (or at least, not in the same way that a fastloop's does).

​Below is a recreation of Example fastloops #1 from above, but using a forEach loop instead of a fastloop. Notice how the apple forEach loop doesn't interfere with the successful pear scoping. 
Picture
Example forEach #1

​​Furthermore, watch what happens when we call a forEach loop on pears instead of apples, below. The scoping from event #1 is actually remembered in event #2 (only the Y>100 pears are faded)! This gives you the ability to call a forEach loop on only a specific subset of objects, which is useful in various situations (eg. run a forEach loop that makes enemies shoot at the player...but only those which are close to the player).
Picture
Example forEach #2

​Just to be clear, this is an ability that is unique to forEach loops. The exact same scenario using a fastloop causes scoping to be broken (all pears are faded, plus only the most recently created one is tilted):
Picture
Example forEach #3

​So far, forEach loops seem like the good guys. Their mere presence doesn't seem to break scoping, and they obediently inherit scoping from their parent conditions. And of course, scoping is one of the main purposes of a forEach loop in the first place, as the very name "for each" implies: the forEach loop iterates through each instance of an object, one at a time, scoping each in turn as it progresses. Each time, all actions inside the forEach loop will be performed in the context of one specific, scoped object.

But alas, forEach loops also carry their own set of weird scoping problems.

In the next example, we create two new pears next to the apples. Then, we ask for the pears to be tilted. Intuitively, we might expect one of two scenarios: (a) that scoping will carry over from the forEach loop and only the 2 new pears will be tilted, or (b) that it won't, and all 7 pears will be tilted. We probably wouldn't expect the actual outcome, where all but the new pears were tilted. Strangely, event #1 behaves as if the order of its actions were reversed.
Picture
Example forEach #4

​Incidentally, replacing the forEach loops with a fastloop, in an otherwise identical scenario, produces the exact opposite result. Now, only the new pears are tilted. 
Picture
Example forEach #5

​While a fastloop that returns to its parent event can bring with it new scoping, a forEach loop seems to arrive late for the party; it's as if any new objects it created don't quite make it back to the parent event. This can be seen explicitly in the next example:
Picture
Example forEach #6

In the above example, we start off with 5 pears (created in the Frame Editor). We then shoot off via a forEach loop to create 5 more. When that's done, we come back, and ask Fusion to tell us how many pears there are in total. Fusion declares confidently that there are 5, when we can see with our own eyes that there are 10. 

We already know by now that creating new objects can do some wacky things to scoping. So let's see what happens if we try a similar scenario to the one above, but without the create new object bit:
Picture
Example forEach #7 (thanks to schrodinger from the Clickteam forums for discovering this one)

​Even now we have the same problem. The number 1 was added to Global Value A 5 times (since there are 5 pears, our forEach loop looped 5 times). Yet somehow, when we display this value afterwards, it is as if time has rewinded, and it's still 0.

Could it be that forEach loops are simply executed last, after all other actions? Even though the forEach loop appears to be the topmost action, is it secretly being bumped down in the queue? Upon further scrutiny, it appears that this may be what is happening. Have a look at this:
Picture
Example forEach #8

The fact that the pears in the example above end up tilted proves that the forEach loop did indeed run. Yet it was only allowed to run if at least one of the pears was ripe (ie. we changed its alterable value "ripe" from 0 to 1). But the pears only became ripe after the forEach loop had finished! 

There are only two possible explanations for this that I can see. Either forEach loops have discovered the secret of time travel, or they are always executed last, no matter where they appear in the action list. The former explanation is the more compelling one. But we must keep an open mind and at least consider the possibility that Fusion routinely delays forEach loops until all other actions are completed. 

Here's another example. Notice that we create a pear at the precise location of the apple. Yet when all is said and done, the pear ends up nowhere near the apple. That's because we moved the apple (but before the pear was supposed to have been created). Again, it seems as if Fusion has, without telling us, shuffled the forEach loop to the bottom of the action queue. 
Picture
Example forEach #9

However, things are not this clear-cut (which by now probably shouldn't be surprising). This next example proves that although forEach loops often seem to be executed last, they aren't always executed last:
Picture
Example forEach #10

Had the forEach loop run in the above example, the application would have immediately ended. But as you can see, the application is alive and well. We can deduce from this that the order of events played out exactly as we would expect from looking at the events (for once).

First, Fusion ran a loop for each pear - but since there were no pears yet, it ran the forEach loop zero times, so it never had to see the end the application action. We can assume that only afterwards was a pear created (since the existence of a pear should have caused the forEach loop to execute). In this case at least, the forEach loop seems to have executed at the start of the action list, not at the end. 

These last few examples weren't technically about scoping, but I think these idiosyncrasies are useful to know about, since they can, and do, affect scoping in certain circumstances (as we saw in Example forEach #4, for instance).

Manual scoping

OK, so we've investigated the different ways in which Fusion automatically scopes objects (or doesn't). If you pay attention to the basic principles and steer away from the problem areas we identified, you'll find Fusion's automatic scoping to be both useful and easy to work with.

But there will still be times when Fusion won't automatically do the scoping that you need, and will require a helping hand from you. This will be the case when you need scoping to persist over multiple events (Fusion's autoscoping  is predominantly limited to singular events).

Below, we have 4 pears. On top of each pear, we've also placed a worm:
Picture
Example manual #1

Now, let's say that we want to rotate those pears whenever we press spacebar; we'll do it via a forEach loop. When we rotate the pears, the worms are left behind:
Picture
Example manual #2

​We can tell Fusion to rotate the worms the same amount as the pears, but that won't work properly. Each time we tell Fusion to rotate a pear, it does them one by one, because of the forEach loop. But when we tell it to rotate a worm, we haven't specified which worm we're talking about (ie. we haven't scoped the worms), so it rotates all of them. As a result, the worms are all rotated together, 4 times, and they end up facing the same way:
Picture
Example manual #3
​
So we need a way to tell Fusion which worm we're talking about. In particular, we need some way to 'marry' each pear/worm pair. There are a number of ways to do this, though they all work on the same principle. Some people use spread values, for instance. My preferred method is to used fixed values, like so: 
Picture
Example manual #4

​A fixed value is a unique number that Fusion automatically assigns to every object in the scene. It typically looks something like this: 374928. But the beauty of this method is that you don't need to actually know , or care, what the fixed value is. The point is that every object in your game already has its own unique little identification number, without you needing to do anything, which makes these fixed values perfect for manual scoping. OK, let's break down this example step by step:

EVENT 1: We launch a forEach loop for the pears. This will only run once (at start of frame). 

EVENT 2: This is the forEach loop. In the first action, we create a worm at the pear. Fusion knows exactly which pear we're talking about, because the forEach loop is carefully going through each pear one by one, scoping an individual pear each time. At this point, we are in a unique position: Fusion has scoped a single instance of a pear (because of the forEach loop) and also a single instance of a worm (the one that was just created). But not just any pear and worm, of course: this pear and worm are located at the same physical X+Y location.

So while Fusion has both these objects scoped, we must take advantage of the situation and quickly find a way to slap some sort of permanent label on our duo, forever 'marrying' this particular worm with this particular pear. We do this in the second action, by assigning an alterable value (that I've chosen to call "ID") to the worm, and giving it the same value as the fixed value of the pear.

​As discussed earlier, each object has a totally unique fixed value that can be used to tell it apart from all other objects. So by copying the pear's fixed value to the worm's "ID" (alterable value), we've effectively formed a link between them that we'll be able to use at any time in the future to locate the happy couple. 

EVENT 4: Whenever spacebar is pressed, launch another forEach loop that will rotate the pears.

EVENT 5: The first condition (the forEach loop one) scopes the pear. Fusion now has one single pear in mind. The second condition scopes the worm, because it forces Fusion to find only the worm that has an "ID" (alterable value) that exactly matches the fixed value of a pear (which pear? the pear that was scoped in the preceding condition, of course!)

By the time we get to the actions, Fusion has successfully scoped one pear/worm couple. So when we ask Fusion to rotate the pear, it rotates just one. Then when we ask it to rotate a worm by the same amount, it knows to only rotate the worm that's 'married' to the pear. 

This technique doesn't just apply to pairs (nor just to pears, har har). It can be used to combine and scope any number of items. Here, we add a cowboy hat to the mix:
Picture
Example manual #5

Scoping - Rules of Thumb

Well, if you got through all of the above, then you know that Fusion's autoscoping can become one crazy rabbit hole. But that doesn't mean scoping needs to be intimidating or confusing. By simply remembering a few basic points, you'll be able to steer clear of 99% of Fusion autoscoping's weirdness, and reap the benefits of its intuitive, pleasant side. Here are the key points to be mindful of (as I see them, at least):

  1. Scoping is usually done for the leftmost object in a condition
  2. Testing for overlaps/collisions scopes both objects
  3. Compare two general values never scopes anything
  4. Fastloops will usually break an event's scoping. If in doubt, place fastloops in a separate event.
  5. ForEach loops inherit scoping from their parent conditions. Make use of this to call more narrowly defined forEach loops.
  6. Try to create new objects at the top of your action list (otherwise you may get scoping oddities)
  7. Try to place forEach loops at the bottom of your action list to prevent weird surprises (since Fusion usually appears to shift them there at runtime)


That's (not) all, folks!

This is the first such Clickteam Fusion article that I've posted, and I've no idea how many people will read it, and whether they'll find it useful. So please let me know what you thought. Tell me what you liked, and what you didn't like, either in the comments below, or at the Clickteam Fusion forums.

Stay tuned for the larger article that this post was originally meant to belong to. To be notified when new posts arrive, sign up to my newsletter. 
11 Comments
schrodinger link
12/6/2017 06:39:03 am

Very well laid post on a key subject, must-read for any dedicated Fusion user!

Reply
V.D.Sanctis
12/6/2017 09:13:44 am

Very useful article, I wish you published this a few months ago, when I was going crazy about scoping!
Now everything makes (a little) more sense, thanks!

Reply
Ls2
13/6/2017 12:25:26 am

Good article, Volnaiskra! Scope in Fusion was always a problem for me. I hope that I can handle this problem now.

Reply
Qyburn13
13/6/2017 09:07:10 pm

Really interesting read. Please keep them coming. There aren't enough high quality Fusion articles so really great to see.

Reply
SaTsu
28/6/2017 05:39:13 pm

I really learned about it. Thanks for your article and those examples.

Reply
Daniel Vaske
21/8/2017 10:00:17 pm

Thank you a lot for that great article. I had a lot of problems with that topic and had no idea what was going on. I always found a workaround but this gives me a better inside. Most guides focus on what works and do not show those weird side effects. Thus I always thought it was my fault when i hit one of those.

Reply
Zugzwang
17/4/2018 02:54:17 am

Hi.


My impression with Calculate Text Rect object is that it's break the Scoping if you are change object's position (tried with Active System Box) in a Loop.

It changed all Actve System Boxes even when I utilize an Alterable Value especifing each Id.

Reply
Zugzwang
17/4/2018 03:04:45 am

Nevermind. It was my fault.

Reply
Gurglak
29/7/2018 05:53:49 am

Thank you for taking the time to test this and explain it in a clear manner.

Reply
defisym
13/4/2019 12:51:03 pm

Hello volnaiskra!
I have read this article a long time ago, and it helps a lot.
but I was curious cause I used to use fastloops and they never break my scope.
After two years, recently I find out why.
if you scope down to a specific object, fastloop won't break scope, e.g. only one object's Y>100, or scope by a unique ID, or only one instance in the frame.
Only there are more than one object left in the scope, fastloop will break it.

Reply
DFK Northern Ireland link
30/1/2021 05:46:00 am

Great post thanks.

Reply



Leave a Reply.

    Author

    Picture
    I'm Dave Bleja. I quit my career to make one the most immersive, deeply crafted platformers of this generation. 

    Spryke is the story of a dimension-hopping cyberfish who discovers the joys of dry land!

    Stay in the loop

    Sign up for newsletter
    Follow @Volnaiskra

    RSS Feed

get updates

Follow the progress of our original game Spryke, along with Skyrim mods and more, on the social media platform of your choice





© 2015 Volnaiskra
  • Spryke
  • Volblog
  • downloads
  • Presskit
  • About