Translations

Know another language? Translate this tutorial!

Stats

3.6K visitors
16.1K page views

Understanding picking with respect to Families

Favourite 51 favourites
Tutorial written by cacotigonOriginally published on 10th, May 2013 - 2 revisions

Before continuing to read this tutorial, the reader should be fairly comfortable with IID, UID, the differences between an object type and an instance of an object, SOL and picking. To provide a brief terminology refresher:

UID
A unique ID that is assigned at runtime for the lifetime of an object (effectively equivalent to a GUID), regardless of whether that object is destroyed, the game will not re-use that UID.

IID
Index of an object within its own object type (this applies to families as well)

ObjectType vs Objects
This is a one-to-many relationship. If you have an ObjectType called RocketShip, you could put N number of that object on your layout.

SOL
The Selected Object List is the list of objects that have been filtered via the use of conditional Events. For example, if you have an Event which states EnemyShip Life < 10, then the SOL for EnemyShip will be restricted to EnemyShip objects with Life less than 10.

Why is it important to be able to distinguish between two picked objects that belong to the same Family in a single event? Why can't I just make On Collision events for each type of object in my game?

It's important because it helps with scalability. I think an example might be helpful here, let's say that you have 50 monsters in a game, ogres, trolls, imps, etc. All these objects belong to a family called Monsters. This family has several instance variables, one of which is a numerical variable called Power. Additionally, you have two other variables, Function_WinsBattle and Function_LosesBattle. These are string variables which store the names of functions (please see the Function plugin help for usage) that are specific to the type of monster. Each function takes a UID as a parameter so that they know what monster they should be modifying. Finally, all Monsters have an Experience level which may go up after defeating a monster.

So our Monster Family Variables List looks like this:


In this example, we've defined two general Win and Loss functions which are by default the functions for every monster. However, you might choose to override these on a per monster basis. You could have a function called "ImpWinsBattle" and "ImpLosesBattle" which do different things then "TrollLosesBattle" and "TrollWinsBattle". This allows you to setup customization per monster. By grouping everything in a family, it allows generalization and makes your script more flexible.

When a monster collides with another monster, you want to compare their power and run WinningMonster.Function_WinsBattle and LosingMonster.Function_LosesBattle for each monster respectively.

If you have 50 different monsters, you don't want to have fifty different On Collision routines, it's inefficient and it's not maintainable. (For more details from an optimization perspective, see the addendum to this article.) By putting them in a single family, we can shorten our Collision detection to a single event:


Now, here's the crux of the issue, the SOL for Monsters is two monsters of random type, yet you need to be able to distinguish between each Monster at this point in order to compare their Power levels and call the correct function for each one. So how do we do this? Strictly dealing with Family on Family collisions, there are a number of different approaches. I will discuss the pros and cons of each.

Solution #1: Can I just create two identical families?

For the above scenario we described, you would create two Families, Monsters_1 and Monsters_2 and put all the monsters in both of them. So your Projects folder would look like this:


Then you would just call:


While this solves the problem of being able to access both objects distinctly in the same descending subevent tree, you lose access to behaviors and instance variables that belong to Monsters_1 when referring to the Monsters_2 collision instance, in this case, Monsters_2 doesn't have the family variable Power.

Solution #2: Can I save off the IID's of each object after doing a Pick nth instance on each, and then use them through the parathentical operators ( ) ?

The answer is sort of but ONLY IF you are picking via Object and not Family. Since this entire tutorial is based on leveraging Families, we don't really get into this.

Why won't it work for Families?

Because an IID has no context within a Family, an IID always refers to the specific object's index within its own Object Type.

Even if you could assume that those IIDs would stay consistent through the lifetime of the function, it still wouldn't work as the IID would actually be the child object's IID, not the IID in context of the Family. To put it in simpler terms, let's say you had two Object Types in the game Box and Circle. On the layout, you have a single instance of each one, so one Box and one Circle. Internally, the Box has an IID of 0 since IID is a index to the set of objects of the same type. So can you guess what Circle's IID is? It's also 0.

Now let's assume you have an On Family Collides with Family Event between a Box and a Circle. When you use two subevents, Pick 0th Family, Pick 1th Family and save off the IID values into temporary variables for the Circle and Square, they're both equal to 0. Referring to Family.IID is going to get the IID for that specific object within its type.

This is rather abstract, so I've attached a Capx example to demonstrate why this doesn't work.

Solution #3: Saving the relevant information from each object into local variables

This involves making use of the "System Pick nth instance" event. In this approach, you pick each one in a separate subevent and save off the information you need to local variables. So what information do we need? Remember that we need to be able to compare their Power variables, and run the winner's Function_WinsBattle and conversely run the loser's Function_LosesBattle and also pass in the UID for the winner and loser to each one respectively.

Let's see what this would look like:


As you can see, we've created a set of local variables for all information we need. We save off that information in individual pick subevents. After this we have power comparisons and we call the appropriate function.

Now, let's define our GeneralWin and GeneralLoss functions.


Note that in each Function, it also does a simultaneous pick operation based on the UID value that we pass in from the Monster collision event routine. Since we passed the UID value of the specific Monster, we can pick using standard function parameter notation Function.Param(0).

Okay, let's say at this point you want to have a slightly different behavior for the troll if he loses. In our project asset list, we just change the Function_LosesBattle for Troll object to "TrollLosesBattle", and then we define the function in our event sheet:


All of this is vastly simplified for purposes of explanation but I'm sure you can see the power of combining Families and Functions.

Thanks for reading and feel free to leave some comments and/or questions if you have any.

-- Cacotigon

Optimization

There is another very important reason to take every effort to reduce the number of collision-related events, and that is performance. Construct's engine does not cache collision detection. What this means is that any time you do an Overlap or Collision detection event, it will recheck all objects related to that event even if you might have compared some of the objects before.

Let's take an example:
You have a family called BoxFamily with three object types, GreenBox, PinkBox, and YellowBox. On the layout, you have one of each object.

In order to be able to distinguish between the two objects involved in the collision, you might assume you can just write three collision checks from object to family, like so:


Every game cycle, it would execute the following:
GreenBox On Collision with Family - Two separate collision checks: X-Y and X-Z
PinkBox On Collision with Family - Two separate collision checks: Y-X and Y-Z
YellowBox On Collision with Family - Two separate collision checks: Z-X and Z-Y

Making for a total of 6 collision checks.

Not let's see what would happen if we used a single Family to Family collision event, as in the following:


Every game cycle, it would run the following check:
Collision Check: X-Y (the same as Y-X)
Collision Check: X-Z (the same as Z-X)
Collision Check: Y-Z (the same as X-Y)

Allowing us to half the number of collision checks! For a concrete example of this kind of performance gain in action, see the attached Benchmarking Capx. Click the buttons to switch between the types of collision detection, and hold the right-mouse button down in the layout to increase the number of boxes.

Unlock your full gamedev potential

Upgrade to the Personal Edition of Construct 2, it has way more features and won't holding back from making money and using your full creativity like the free edition does. It's a one off payment and all Construct 2 editor updates are free for life!

View deals

Plus, it's got a lot of additional features that will help you save time and make more impressive games!

Congratulations on finishing this tutorial!

Did you learn a lot from it? Share it now with your friends!

Share and Copy this Tutorial

You are free to copy, distribute, transmit and adapt this work with correct attribution. Click for more info.

Comments

6
ChrisAlgoo 4,004 rep

This was great! Thanks very much.

Friday, May 10, 2013 at 9:39:42 PM
1
Lordshiva1948 41.0k rep

yes this gr8 nice one. Thank you

Saturday, May 11, 2013 at 8:34:53 AM
1
AbelaNET 14.8k rep

Super, Thanks :)

Saturday, May 11, 2013 at 11:25:21 AM
1
Martijnbroeckie 1,510 rep

Thank you very much!

Thursday, May 30, 2013 at 8:21:23 AM
0
chrisriis 1,920 rep

Nice! I just have one question. The "on collision with monsters" event will be insanely long if you had e.g. 20 different monsters. You'd have to use like 80 local variables in your case if you wanted 20 different monsters, and then a whole bunch of sub-events to do logic for each and every monster. Isn't there a cleaner way? Perhaps you could use a loop somehow.

Friday, May 31, 2013 at 11:23:37 AM
1
alextro 23.4k rep

Ah so that's the reason why my attempt to retrieve family IID not work the way I expected to. And yeah collision detection methods have a huge performance issue in there. Worth reading indeed.

Tuesday, August 20, 2013 at 3:01:05 PM
1
Nitestalker 453 rep

Thank you for this, Your tutorial has helped me significantly

Friday, February 14, 2014 at 2:45:02 PM
0
jeeba 1,265 rep

Aweesome dude, this make game developing so much better. An answer to @chrisriis , not really, every time it's called, the event has their own state, so internally they will be 20 call to that event with their own local variables. Now the trick here is the use of functions, saved as a string in the monsters sprite.

With this aproach you will have just one "collision with monster" event and 20 functions that is called when the respective monster is hit!. So much clean.

I will have to rework my game using this aproach but it will save me a lot of problems in the future. Thanks again cacotigon

Sunday, May 04, 2014 at 2:07:38 PM
0
Merlandese 4,458 rep

This is a good tutorial. I especially like the optimization tip at the end. Thanks! :)

Thursday, May 05, 2016 at 5:01:28 PM
0
KoolEcky 16.3k rep

@ cacotigon. "Understanding picking with respect to Families" is full of excellent insights. Thanks very much.

Wednesday, November 02, 2016 at 9:23:49 PM
0
mumu64 5,682 rep

Thanks! This tutorial solved a problem and gave me more insight in the use of families.

Friday, May 05, 2017 at 10:27:33 PM
0
wiverson 1,330 rep

The best way per Scirra is use the "Pick nth instance" sub event. See...

construct3.ideas.aha.io/ideas/C3-I-227

Thursday, August 17, 2017 at 10:36:04 PM

Leave a comment

Everyone is welcome to leave their thoughts! Register a new account or login.