What's the Difference: Objects and Instances

FrostyCat

Member
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 templates that exist on the resource tree. Objects are types.
  • Instances are products that exist in rooms. Instances are things.

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

What you can and can't do

The difference between instances and objects means certain operations are forbidden on one or the other. You must keep these in mind at all times.

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.

Objects do not have variables. As a result, there is no static scope in GML. If you need static-scope constants or variables, use macros (constants), enums (constants) or global variables (variables).

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. DO NOT use self. 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).

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 self. 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: other is always equal to -2 and will lose context outside the event. 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. Note that these functions return noone upon not finding a collision. Always account for this special case whenever you handle collisions this way.
GML:
var inst_enemy = instance_place(x, y, obj_enemy);
if (inst_enemy != noone) {
    inst_enemy.hp -= 10;
}
Created or copied instance. instance_create() (GMS 1.x and legacy) / instance_create_layer() and instance_create_depth() (GMS 2.x) and instance_copy() return the ID of the created or copied instance. NEVER use instance_nearest() to establish this relationship --- something else could be closer by.
GML:
var inst_bullet = instance_create_layer(x, y, layer, 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() (GMS 1.x and legacy) / instance_create_layer() or instance_create_depth() (GMS 2.x) 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.
GML:
my_pet = instance_create_layer(x+32, y, layer, 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
 

immortalx

Member
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