What's the Difference: Objects and Instances

FrostyCat

Redemption Seeker
What's the difference: Objects and Instances

GM Version: N/A
Target Platform: All
Download: N/A
Links: N/A

Introduction

At least a dozen times a week, I see questions on the Q&A section that have to do with people not distinguishing objects from instances. This topic aims to explain the difference between them and the do's/dont's for handling them.

Difference between objects and instances

The Manual said:
To make a game in GameMaker: Studio you will need to first define objects and add them to the resource tree. An object can contain a number of different Events and in each event you can use either GML code or D'n'D to define behaviours for that object. However, this object is not placed into the game, but rather acts as a template for instances which are placed into rooms. An instance is basically a copy of the object that is placed in the room of your game and so, in this way, you would create one wall object and then place 100 instances of it into the room to create your game world.
In other words:
  • Objects are types. They are templates that exist on the resource tree.
  • Instances are things. They are individual products that exist in rooms.

Using a physical analogy, "Human" and "Dog" are objects, but "Alice", "Bob", "Fido" and "Lassie" are instances.

What you can and can't do

"Object" and "Instance" are NOT interchangeable terms in GML. As a consequence, you must keep the following in mind at all times:

NEVER access a single instance by object ID if multiple instances of the object exist. This includes attempts to reference or set object.variable (which is inconsistent across exports) and using with (object) to apply actions to it (this encompasses all instances of the object instead of just the one you want). Verbally, "Dog's colour" makes sense with one dog, but not with multiple dogs.

Corollary: NEVER set or use an instance's own variables with object.variable. An instance's own variables can be referenced as-is without dot prefixes. If multiple instances of the object exists, you might end up setting the value for all instances or for some other instance (depending on the export).

Instances do not have parents. If you need to get an instance's object identity, use object_index. object_get_parent() and object_is_ancestor() can then be used with the object ID to determine the object identity's relationship to other objects. Also remember that it is STRICTLY FORBIDDEN to change an object's property while an instance of it is active, especially its parent.

Corollary: NEVER set or reference instance properties using object_*() functions. For example, to change an instance's sprite, you must set sprite_index instead of using object_set_sprite(). Also, NEVER attempt to use object_add() to create an instance, and NEVER attempt to use object_exists() to check the existence of something in a room.

Objects do not have variables. As a result, there is no static scope in the C++/C#/Java sense in GML. If you need constants or variables scoped like that, use macros (constants), enums (constants) or global variables (variables).

Finding the right instance

As long as multiple instances of an object exists, referencing with an object ID alone will cease to work, so you must learn to find the one instance ID you want to work with. Here are some common situations.

An instance's own variables: Type out the variable name as-is, without any dot-prefixes. DO NOT use the object ID.
GML:
x += 16;
Collision events (the ones from the event selector, NOT collision checks in the Step event): other refers to the colliding instance in this event only. IMPORTANT NOTE: If you need the actual instance ID later, you need to use other.id instead.
GML:
other.hp -= 5;
GML:
last_collided_enemy = other.id;
Collision-checking functions: instance_place() and instance_position() are the instance-ID-oriented analogues of place_meeting() and position_meeting(). Functions that start with collision_ but don't end in _list all return instance IDs. Save that instance ID into a variable, then use that as the subject to work with. DO remember to check that it is not noone before acting on it. DO NOT refer to this instance as other.
GML:
var inst_enemy = instance_place(x, y, obj_enemy);
if (inst_enemy != noone) {
    inst_enemy.hp -= 10;
}
Created or copied instance. instance_create_layer() and instance_create_depth() (GM 2022+ and GMS 2.x), instance_create() (GMS 1.x and legacy), and instance_copy() return the ID of the created or copied instance. To set variables in the created instance, use the options struct if you are on 2022.5+, or use the returned instance ID as the subject if you are on an older version or want to apply actions to it. NEVER use instance_nearest() to establish this relationship --- something else could be closer by.

Modern GML (the bullet's new direction is available from the Create event and onwards):
GML:
instance_create_layer(x, y, layer, obj_bullet, {
    direction: direction,
    speed: 5,
});
Legacy GML (the bullet's new direction is only available AFTER the Create event):
GML:
var inst_bullet = instance_create(x, y, obj_bullet);
inst_bullet.direction = direction;
inst_bullet.speed = 5;
Subsidiary instance(s). Same as created or copied instance. If the subsidiary instance needs to reference its creator, the creator should assign its instance ID to an instance variable in the subsidiary instance. Conversely, if the creator needs to reference its subsidiary (or subsidiaries), it must store the return value of instance_create_layer() or instance_create_depth() (GM 2022+ and GMS S 2.x) / instance_create() (GMS 1.x and legacy) immediately. NEVER use instance_nearest() to establish or maintain this relationship --- being the closest does NOT imply being the most relevant, especially in close quarters.

Modern GML (owner is accessible from the Create event and onwards)
GML:
my_pet = instance_create_layer(x+32, y, layer, obj_pet, {
    owner: id,
});
Legacy GML (owner is only accessible AFTER the Create event):
GML:
my_pet = instance_create(x+32, y, obj_pet);
my_pet.owner = id;
If in doubt about whether a function will help you find the right instance, use the Index tab in the Manual to look for its entry. It will tell you whether it returns an instance ID or something else.

Looping through instances

The with block is GML's main tool for looping instances of the same object or of objects in the same lineage. To learn more about it, see the following articles:

Another common approach is to store instance IDs in arrays, lists or grids, then iterate through them using for loops. This is the most efficient when the same set of instances is repeatedly queried.

Here are some important notes on instance looping:

Avoid loops based on instance_find(). There is a hidden linear-time lookup in instance_find(), compounding this with a loop makes it quadratic-time. A with block or pre-computed array can do this in genuine linear time. See this post for evidence.

Avoid using instance placements as your data model in non-action games. Repeated collision checks are much slower than array lookups, and instance placements cannot be readily cloned for saving or use with AI simulations.

A with block in the Draw event does NOT change the drawing depth. If you need to draw at a different depth, change the depth ahead of time. If you need drawing subroutines to happen at a different depth, create instances of worker objects at those depths to do the drawing for you.
 
Last edited:
R

rjenman

Guest
thanks, I was aware of the difference but there is clearly a whole lot of more detailed implications to coding due to the difference and just due to the way code works that I didn't know
 
I

immortalx

Guest
Thanks @FrostyCat
I wish I've studied the basics more before actually making a game (but it's never too late) and your post seems like the cornerstone of the basics.
I just discovered this post and I'm glad I did, for it certainly cleared things up about objects and instances.
 
Top