This article refers to "screen objects" or "animated objects". For other uses of the term "object", see the Object disambiguation page.
Objects, also known as screen objects or animated objects, provide the animation in AGI games. They can represent various things such as characters, background animations, or still props. Each object has a view assigned to it, and objects can be placed anywhere on the screen and moved around.
Object 0, commonly referred to as "ego", is special. This is the object that the player moves around using the arrow keys.
Before you can use a screen object, it must be initialized. Initialization involves the following steps:
- Animate the object. This is done using the animate.obj command. This tells the interpreter that you want to use this object. There are a limited number of objects that the interpreter can use at any one time. This is set by a certain byte in the OBJECT file. WinAGI allows you to easily edit the maximum number of screen objects. The more objects you use the slower the interpreter will run, but today's computer have less of a problem with this.
- Assign a view to the object. This is done using the set.view command. Each object must have a view assigned to it, and this is the view that is displayed on the screen wherever the object is. One view can be assigned to multiple screen objects. You must have the view loaded into memory before you assign it, using the load.view command. It is possible to assign the same view to more than one object. This can be used to create 'herds' of objects. The alligators in the King's Quest I moat are a good example.
- Position the object on screen. The default values for the object's coordinates are (0,0). Set the initial position of the object with the position or position.v commands.
- Draw the object. This is done using the draw command. The object is not visible on the screen until this command is issued.If you later need to remove it from the screen, use the erase command. When drawing objects, the object's location (set by position or position.v) is validated using a method called 'shuffling' to ensure the object is not drawn outside the playable area, on a control line or on another object (if the object is not ignoring other objects).
The following example displays an animation of VIEW.004 at (80, 120) on the screen, using object 2:
animate.obj(o2); load.view(4); set.view(o2, 4); position(o2, 80, 120); draw(o2);
The animation of an object is called "cycling." This is the successive display of several cels from the same loop in order. When you initialize an object, it cycles the cels in the first loop (loop 0) by default. To change the loop number, use the set.loop command. To change the speed of cycling, use the cycle.time command. If you want to display a single cel without animation, set the loop number and cel number you want to display using the set.loop and set.cel commands and then use the stop.cycling command (you can start cycling again by using the start.cycling command).
When animating, objects aren’t updated on screen until the start of the next interpreter cycle (or until the force.update command is issued), so whatever changes you make to an object’s position or appearance do not take affect till then.
Normally when an object is moving around, its loop number is determined by its direction:
|0 (not moving)||Not changed|
|1 (up)||3 (if loops 2 and 3 exist; otherwise not changed)|
|5 (down)||2 (if loops 2 and 3 exist; otherwise not changed)|
|6 (down-left)||1 (if loop 1 exists; otherwise loop 0)|
|7 (left)||1 (if loop 1 exists; otherwise loop 0)|
|8 (up-left)||1 (if loop 1 exists; otherwise loop 0)|
The direction is only chosen automatically if there are less than 5 loops in the view assigned to the object.
There are four modes of cycling available to an object:
- Forward Cycle
- In the forward cycle mode, the cels are displayed in increasing numerical order, i.e., cel 0, then cel 1, etc. After the last cel is reached, the interpreter goes back to cel 0 and starts over. The view will continue to cycle indefinitely. This is the default mode. Use the normal.cycle command to switch to this mode.
- Reverse Cycle
- In the reverse cycle mode, the cels are displayed in decreasing numerical order, i.e., cel X, cel X-1, etc. (where X is last cel). After the first cel is reached, the interpreter goes back to cel X and starts over. The view will continue to cycle indefinitely. Use the reverse.cycle command to switch to this mode.
- Forward to End
- In this mode, the cels cycle in the forward direction, but once the last cel is shown, cycling stops. Use the end.of.loop command to switch to this mode.
- Reverse to Beginning
- In this mode, the cels cycle in the reverse direction, but once the first cel is shown, cycling stops. Use the reverse.loop command to switch to this mode.
When positioning objects, you need to remember that the 'anchor point' for objects is the lower left corner of the current cel. Be sure to adjust the coordinates passed to positioning commands to allow for the height and width of your object.
After the object has been animated, it is possible to reposition the object. However, do NOT use the position and position.v commands. For objects that are already visible on the screen, use the reposition, reposition.to and reposition.to.v commands instead.
The repositioning commands will erase the object from its current location before redrawing it in its new location; the position commands do not.
The reposition.to and reposition.to.v commands are similar to the position and position.v commands. You can pass a set of new coordinates for the object and it repositions itself during the next interpreter cycle update.
The reposition command is slightly different; it takes as arguments horizontal and vertical offsets from the object's current position, instead of absolute values. The argument values are treated as signed 8 bit numbers, so negative offsets can be achieved. More details are available in the remarks section of the reposition command topic.
When objects are positioned on the screen, AGI validates the position to ensure an object is placed within the playing area (not overlapping any edge, or the horizon, if not ignoring it), is not on a control line, and does not collide with another object.
The validation technique used by AGI is known as 'shuffling'. When called by AGI to validate an object position, the shuffling function first checks to see if the object's position is valid. If it is (it's in the playing area, not on a control line, and does not collide with another object), the shuffling function returns without modifying the object's position. If it is not, it begins moving the object around the starting point in a counter-clockwise spiral until it finds a point that is valid. The object is then placed at this position.
- NOTE: Because of the way AGI validates that an object does not collide with another object, repositioning an object can sometimes yield unexpected results. If the new position of an object is below (Y value is higher) another object, and their baselines overlap, AGI considers this to be an object collision, and will shuffle the object until it finds a valid position.
Interacting with object positions
During game play, it may be important to be aware of objects' positions. The get.posn command is used to determine the current coordinates of an object. These values can then be used to take actions based on the object's position.
There are also four test commands that can be used to analyze an object's position. The posn, right.posn, center.posn and obj.in.box commands all compare an object's position to a specified rectangular area, and return TRUE if the object is inside the box.
The difference between the commands is how they determine what 'in the box' means. The posn command uses the anchor (bottom-left) point in the test; the center.posn and right.posn use the bottom-center point, and bottom-right point, respectively. The obj.in.box command checks that the entire bottom line is within the area.
These commands are very useful to be able to trigger certain events if an object approaches a given area of the screen. For example, if ego gets close enough to a door, you might want to make the door automatically open.
Each object on screen has a direction it is moving in. This is a numerical value from 0 to 8:
When an object is moving normally (and has a direction greater than 0), it will continue moving until it is told to stop, move in another direction, or runs into an obstacle (such as a control line, a block, or another object).
Each incremental change in the object's position as a result of moving is called a step. The object’s step size (the number of pixels moved each step) defaults to one, but can be changed with the step.size command. The step time (frequency of the steps) can be set with the step.time command.
Moving Non-ego Objects
For objects other than ego, the object's direction can be changed using the set.dir command. The get.dir command will allow store an object's direction in a variable. As discussed above in the Cycling Objects section, changing direction may result in an automatic change in the current loop as well.
There are four movement modes:
- normal movement
- object responds to the set dir command; will move in a straight line until told to stop, or hits an obstacle. This is default mode for the object
- move to a point
- object will move toward a point; interpeter will attempt to determine best direction. If obstacles encountered, it will try to get around; need to test carefully when using move command, to ensure the object can actually get where you want it to go.
- follow ego
- The object will move toward the current ego position. If ego moves, the object adjusts its path accordingly.
- The object will wander aimlessly about the screen. The interpeter randomly chooses directions and distances. If obstacles encountered, the object will choose another random direction to continue wandering.
In addition to setting the movement mode, movement must be enabled before the object will actually move. Objects are initialized with movement enabled by default. Use the stop.motion command to stop movement. Use the start.motion command to enable movement. Note that once movement is enabled, the object will begin movement on the next interpreter cycle based on its current movement mode.
Moving the ego Object
The ego object (object 0) is different from other objects. The ego object can operate in two control modes, player control and program control.
Use the program.control command to switch ego to program control. While in program control, ego essentially behaves as any other object. The cycle modes, movement modes, and positioning commands work as they do for other objects.
The exception to this is the set.dir command; it has no effect on the ego object. To programmatically set ego's direction, change the value of reserved variable v6. The get.dir command does work with the ego object, but it is just as easy to check the value of v6 instead.
Use the player.control command to switch back to player control. (When the interpreter starts, player control is the default.) Switching to player contol also switches the ego object back to normal movement mode.
The follow.ego command is ignored when used on the ego object (for obvious reasons).
There are six different types of obstacles that can restrict an object’s movement:
- Control lines on the priority screen
- Blocks set by the block command
- Land or water, if the object is restricted to either
- The horizon
- The screen edges
- Other objects
When checking for obstacles, none of pixels on the object's baseline (bottom row of pixels) are allowed to come in contact with an obstacle that the object is observing.
Control lines on the priority screen
On the priority screen, the first four colors (0-3) are used for control lines. The first two colors correspond to the unconditional and conditional control lines. No object is ever allowed to be on parts of the screen with a priority of 0 (unconditional control line). Objects that are observing blocks are not allowed to be on parts of the screen with a priority of 1 (conditional control line). The ignore.blocks=== command allows objects to pass over conditional control line pixels. The observe.blocks command restores normal behaviour.
Note that if an object's step size is large enough, it is possible for the object to 'jump' over a control line.
You can set up an invisible rectangular block on the screen using the block command. The interpreter will not allow objects to cross the borders of the block, unless they have been issued the ignore.blocks command. Use the observe.block command on objects that are ignoring blocks to begin observing the block again. Use the unblock command to remove the block. Unlike control lines, objects cannot 'jump' over blocks, no matter how big the step size.
Land and Water
Pixels on the priority screen with a priority of 3 are considered "water". Other areas are considered "land". The default behaviour for objects is no restrictions; the object can be on land or water. The terms water and land are for labeling purposes only; priority 3 areas could be used in a game to represent any area where the programmer wants to restrict or prohibit movement, such as ice, lava, sand, etc.
With the object.on.water command, an object is restricted to pixels with priority of 3. The object will not be allowed to move if any part of its baseline would be on non-water pixels.
The object.on.land command is just the opposite. It restricts the objects to non-water areas.
To remove all restrictions, use the object.on.anything command.
Whenever ego’s baseline is completely on water, reserved flag f0 is set.
The horizon is an invisible horizontal line, usually near the top of the screen. Its default value is 36, but this can be adjusted with the set.horizon command.
Objects cannot 'jump' over the horizon, no matter how big the object's step size.
Objects are not allowed to go past the screen edge. If the ego object reaches the screen edge, reserved variable v2 is assigned a code to indicate which edge.
If an object other than ego reaches an edge, its index number is stored in reserved variable v4 and reserved variable v5 is assigned the edge code.
In addition to preventing baselines of two objects from touching, AGI also checks to see if objects have 'jumped' over each other, but ONLY in the vertical, or Y, direction.
The actual function that checks for object collision first compares the X components of the objects' baselines. If the baselines don't overlap (i.e. one object's baseline has X values that are less than all X values of the other object), then there is no collision.
If the objects X coordinates do overlap, AGI then checks if the Y values are identical. if they are, then AGI treats it as a collision. If not, a final check is made comparing the two objects' previous Y values. If the Y values have swapped (meaning they probably 'jumped' over each other) then AGI treats it as a collision.
When a collision is detected, the object being tested is moved back to its previous position.
To simulate a 3-D environment, AGI assigns all objects a priority. The background picture includes a separate priority screen that is used in conjunction with objects' priorities to create the illusion of 3-D.
Objects get assigned a priority automatically based on their vertical position. Objects with a higher priority are displayed on top of objects with a lower priority. Different parts of a picture can also be given different priorities. So something in the far distance would have a priority of 4 (everything else would go on top of this) and something right at the front of the screen would have a priority of 15 (nothing will go on top of this except objects with a priority of 15).
When two or more objects overlap, the one with the highest priority is drawn on top. If they have the same priority, the object with the higher index number is drawn on top.
To set an object’s priority so it will remain constant wherever the objects is on screen, use the set.priority or set.priority.v commands. To change it back so the priority is dependent on the object’s position, use the release.priority command.
An object’s priority can be determined using the get.priority command.