- Title: AI Command Stack
- Description: Smarten up your AI by giving them a set of instructions to perform tasks
- GM Version:
:GM81: - Registered: Yes/No
- File Type: .zip
- File Size: 700K
- File Link: http://www.host-a.ne...ommandstack.zip
- File Link: GM8.1: http://www.host-a.ne...mmandstack.gm81
- Required Extensions: None
- Required DLLs: None
I would like my AI to go some place, hunt down enemies and then come back and if there is an enemy along the way, I would like the AI to hunt him down but if a boss or too many enemies are around, I want the AI to run to a safe place....
How many such post have you seen... Is it possible? Yes. Is it easy? No. But the command stack makes it relatively easy.
I want to click a place on the screen and want my player to go there. I want him to follow everywhere I clicked. But I want him to defend himself along the way.
Is it hard to do? Not very but the command stack makes it dead easy.
HERE IS A SIMPLE SCRIPT SWITCHING SETUP WHICH IS MUCH LESS HARD TO GRASP
It's the foundation of this command stack system. I strongly recommend you try this simpler setup first because jumping from a case based AI to this command stack based AI may be too much of a jump for some. This is the in-between step. Hope it helps...
How it works:
The Command Stack uses the GM ds_list and ds_stack and the ability to call a script by reference to add in a sequence of commands for an AI to perform.
The Command Stack executes the script at the top of the command stack until the script is done. When the script is done, is it popped out of the stack and the next script in the stack is executed.
What does this mean?
It means you can code relatively small bits of command scripts to do very specific actions instead of writing a huge decision making script in the step event and loose yourself in all the local and global variables needed to remember where you are. That means less code is executed and the code is more easily created and fixed.
It means your AI becomes task oriented instead of state oriented.
It means your AI can change the AI behavior at anytime with little concern on the state it is currently in.
It means you can write very intelligent AIs with ease.
Here is an example of a command stack. A simplified example.
CommandStackPush PatrolToXY hunting for enemy along the way
The stack look likes this now
<instack>PatrolToXY <executing>
Now, PatrolToXY will push another script in the stack if it finds an enemy along the way AttackEnemy
Now the stack looks like this
<instack>AttackEnemy <executing>
<instack>PatrolToXY <on hold>
Now you hit the enemy and need to back up. Now the stack looks like
<instack>BackUp<executing>
<instack>AttackEnemy<on hold>
<instack>PatrolToXY <on hold>
Backing up is done so the attack resumes
<instack>AttackEnemy <executing>
<instack>PatrolToXY <on hold>
The attack is over so the patrolling resumes
<instack>PatrolToXY <executing>
When the stack is empty,
<instack><empty>
you add another set of instructions to the stack to make it do something else.
I also added a CommandList.
The command list is a sequence of pending tasks to perform once the command stack is empty. When the stack is empty, the first command list is pushed in the stack and executed. This allows to define a whole sequence of actions without affecting the current behavior (urgent tasks). Since adding a command on the stack stops the current script, the list is required to add a series of commands to be executed in order. That way, the stack will fill and empty up between each element in the command list, It's hard to word right.
Here is a sample patrolling command list:
<inlist>PatrolToXY 0.0
<inlist>PatrolToXY 100,100
<inlist>PatrolToXY 1000,1000
When the system starts, it will push the fist list item into the stack so the stack will be
<instack>PatrolToXY 0,0 <executing>
And the list is now
<inlist>PatrolToXY 100,100
<inlist>PatrolToXY 1000,1000
Your AI encounters an enemy so now the stack is
<instack>AttackEnemy <executing>
<instack>PatrolToXY 0,0 <on hold>
The list is still
<inlist>PatrolToXY 100,100
<inlist>PatrolToXY 1000,1000
The attack is over, the stack is popped
<instack>PatrolToXY 0,0 <executing>
The patrol point is done the stack is popped
<instack><empty>
so now the next list item is pushed
<instack>PatrolToXY 100,100 <executing>
And the list is now
<inlist>PatrolToXY 1000,1000
While the AI is attacking and moving, you can add another item in the list. Adding it to the stack would cancel the attack
Lets say we add another patrol point to 50,50 so now the list is
<inlist>PatrolToXY 1000,1000
<inlist>PatrolToXY 50,50
Simplified, Imagine the list is the things you need to do, in order, during the day.
And the stack is the things you need to do right now. If another more urgent thing happens, it goes on top of the stack and is removed when it's handled.
So, what goes in the stack and list?
Scripts (with their arguments) go in the stack or list, very specific scripts that follow a few simple rules
A script returns 1 when done -1 when done and urge the other script to continue right away and 0 when the script is not done.
This allows for the script to be called until it's done (such as for a moving scripts) or a script to run and quit right away.
A script receives a data object to stuff variables into for use in the execution. Each script has its own data object.
A script is called for notification and for execution. The top of each script has a decision handling code to determine why the script is called. The script is called when pushed into the stack where you can initialize variables, It's called when popped out of the stack where you free your data (some data like ds_lists and paths need to be freed if you create them). Finally, the scrip is called for execution. You can tell if it's the fist execution or the subsequent ones from the flag given.
The arguments passed to the CommandStackPush are actually stuffed in the data object. This was done to save on the number of stacks created. Since you get a data object to stuff your variables in, I copied over the arguments into the data object variables named arg1,arg2,�€�,arg15.
Some rules about adding command scripts:
Make your scripts as independent as possible.
Try to rely on passed arguments (you have 15 to work with) and not on global or instance variables. I broke this rule for the tank example scripts.
Since it's possible you can have the same script many times in the same stack, you should rely only on passed arguments and the data you stuffed in the data object as the other script may overwrite global variables.
Though you can still use instance variables to pass on information. Nothing stops you so be careful.
If you can make script generic enough, you will be able to share them with other people.
The scripts push other scripts in the stack. For example, the PatrollToXY will push the AttackEnemy script in the stack, passing on related arguments.
The demo has 3 rooms, you have to move the room to the top of the room list in GM to see it in action. The tanks are from our game (The Tank Game by Tiby312 and I)
I also include 2 path functions to generate a bunch of move commands based on a path. The good thing is, after you added your path to the command list, you can delete it right away if it is no longer needed or reuse it. The functions are smart enough to take in any kind of move scripts as long as x,y,speed are the first, second and third arguments in the command script. All other arguments are passed along.
That's all I can think of at the moment. Please give some feedback. And please see if you can create some cool scripts to help others control their AIs.
Information about object: cmdDataObj
Sprite: <no sprite>
Solid: false
Visible: false
Depth: 0
Persistent: true
Parent: <no parent>
Mask: <same as sprite>
Create Event:
execute code:
action = 0;
arg1 = 0;
arg2 = 0;
arg3 = 0;
arg4 = 0;
arg5 = 0;
arg6 = 0;
arg7 = 0;
arg8 = 0;
arg9 = 0;
arg10 = 0;
arg11 = 0;
arg12 = 0;
arg13 = 0;
arg14 = 0;
arg15 = 0;
arg13 = 0;
arg14 = 0;
arg15 = 0;
I just realized you should probably set the persistent flag to true for the cmdDataObj for the system to work properly in case you make your AI persistent... I have updated the code.
Also, you must be careful if you use instance_deactivate in your game.
You should reactivate the cmdDataObj(s) every time you instance_deactivate(all)
Press N for next room
Set cmdDataObj to visible to see it in action
Use the merge feature to add all required components to your game.
Tweaks to your movement system will be required for the system to work properly so merge early.
If you have trouble setting it up, you may PM me.
Example Game using the command stack
Desktop Deathmatch
Search Tags
- GMCTUTORIAL
- GMCENGINE
- GMCAI











