So out of sheer boredom I decided to show you guys how I go about making an Action Sequence System (ASS, >_> eww.. I think we'll just call it ASEQ for now ) You could call it an automated command system or whatever floats your boat. >_> Now this is a rather simple thing to make (I've done it enough times, it works similar to the event commands anyway)
Now I'm gonna assume, you know what are variables, constants, loops, if, unless, case statements and the like. What you need to get started is knowledge on Arrays, and Modules (kinda optional)
Now before we start, a few things I need to tell you: I will place () after my methods, its a habit I have so I can have C programming habits also. For example: array.clone() Killing 2 birds with one stone O:, also I'll use spaces in my code, For example: draw_text( x, y, width, height, text, align ) I use # // for my comments >_> its a formating thing (mixed with C once again)
CODE
Project: Action Sequence System (ASEQ) Difficulty: Fairly Moderate Estimated Time: 30 minutes Uses: Lots of stuff EG. Used In: Melody, Takentai, Yggdrasil
Now our objective for today, is a basic system that has:
Print Wait
Now lets begin: Part1: Fast Start So lets get the basic layout down
Spoiler:
Create a new script in your editor somewhere (doesnt really matter, where gonna use nothing but the bare classes)
In this instance I decided to use a module (I dun want ma stuff all over tha place)
CODE
module ASEQ
# // Just a quick method for setting up the nessecary variables def self.start @list = [] end
# // Currently empty update method def self.update() end
end
ASEQ.start
loop do
Graphics.update ASEQ.update
end
Now, if you run your game, nothing will happen. You'll just have a black screen, with absolutely no action. (Boooooo we want action!!! >___>)
Part2: Creating the first commands
Spoiler:
Now lets get that wait command in:
CODE
module ASEQ
# // Just a quick method for setting up the nessecary variables def self.start @list = [] @wait_count = 0 end
# // Currently empty update method def self.update() @wait_count = [@wait_count-1, 0].max() update_actions() if @wait_count == 0 end
def self.update_actions() while !@list.empty?() # // Keep looping until the @list is empty act_set = @list.shift() # // shift removes the first element from an array and returns it action = act_set[0] parameters = act_set[1] case action.upcase when "WAIT" @wait_count = parameters[0] # // First parameter is the wait in frames end end end
end
ASEQ.start
loop do
Graphics.update ASEQ.update
end
Okay so a LOT happened there, lets explain each part:
In the start method, we initialized the @wait_count variable (setting it to 0)
CODE
def self.start @list = [] @wait_count = 0 end
Now in the update method stuff seems to be happening here
CODE
def self.update() @wait_count = [@wait_count-1, 0].max() update_actions() if @wait_count == 0 end
We subtract 1 from the @wait_count and set it back. @wait_count should never be less than 0, so if that ever happens set it back to 0
And now
CODE
update_actions() if @wait_count == 0
Pretty straight forward, if the @wait_count == 0 continue reading the action list
CODE
if @wait_count == 0 update_actions() end
Now here is the magical section
CODE
def self.update_actions() while !@list.empty?() # // Keep looping until the @list is empty act_set = @list.shift() # // shift removes the first element from an array and returns it action = act_set[0] parameters = act_set[1] case action.upcase when "WAIT" @wait_count = parameters[0] # // First parameter is the wait in frames end end end
A lot is happening here (well not really)
CODE
while !@list.empty?()
Equivalent to:
CODE
while @list.empty?() == false
OR
CODE
while not @list.empty?()
In short, stop looping if the list is empty.
BAM, shift function XD
CODE
act_set = @list.shift()
What shift does (as explained by my comment on the side) It removes the first element of the array and returns it. Thats like...
CODE
e = array[0] array.delete_at(0) return e
So yeah:
CODE
action = act_set[0] parameters = act_set[1]
Pretty straight forward, the first element of our newly shifted action_set is the action (or the name) The second element, is another array which are the parameters.
CODE
case action.upcase when "WAIT" @wait_count = parameters[0]
Case statement, >_> I dont think I need to explain whats happening here right
So unto adding the print command:
CODE
module ASEQ
# // Just a quick method for setting up the nessecary variables def self.start @list = [] @wait_count = 0 end
# // Currently empty update method def self.update() @wait_count = [@wait_count-1, 0].max() update_actions() if @wait_count == 0 end
def self.update_actions() while !@list.empty?() # // Keep looping until the @list is empty act_set = @list.shift() # // shift removes the first element from an array and returns it action = act_set[0] parameters = act_set[1] ret_code = -1 # // Return Code case action.upcase when "WAIT" @wait_count = parameters[0] # // First parameter is the wait in frames break when "PRINT" print parameters[0] # // First parameter is the text to print end end end
end
ASEQ.start
loop do
Graphics.update ASEQ.update
end
You may notice I've introduce the break command under the "WAIT" action, this is used to cancel the while loop so it can begin "waiting" You may also notice I have a ret_code (return code) I'll explain this later and its purpose.
Part3: When your action case gets too large
Spoiler:
Code Extract from Yggdrasil 1.5s Action Sequence script:
Spoiler:
CODE
when /(?:ATTACK|atk):[ ]*(.*)/i case $1.to_s.upcase when "LIGHT", "LGHT" @character.iex_ygg_physical_attack(1) when "MEDIUM", "MED", "NORMAL" @character.iex_ygg_physical_attack(0) when "HEAVY", "HEV" @character.iex_ygg_physical_attack(2) when /(?:CUSTOM|cust)[ ]*(.*)/i attack = YGG::ATTACKS[$1.to_s] @character.iex_ygg_execute_physical_attack(attack) end #==============================================================================# # ATTACK_AT (or) attack at (or) ATK_AT (or) atk at # ~Properties # : string, +/- dist, +/- sway #------------------------------------------------------------------------------# # This will use an attack(string) at (dist) infront and (sway) adjacent # EG: # "Attack At: string, dist, sway" # "Attack At: SpearLike1, 2, 1" #==============================================================================# when /(?:ATTACK_AT|attack at|atk_at|atk at):[ ]*(.*)[ ]*,[ ]*([\+\-]?\d+)[ ]*,[ ]*([\+\-]?\d+)/i coords = @character.iex_ygg_coord_calculate(@character.x, @character.y, $2.to_i, $3.to_i) attack = YGG::ATTACKS[$1.to_s] @character.iex_ygg_execute_attack_at(attack, coords[0], coords[1]) #==============================================================================# # ATTACK_XY (or) attack xy (or) ATK_XY (or) atk xy # ~Properties # : string, x, y #------------------------------------------------------------------------------# # This will use an attack(string) at (x)(y) on map # EG: # "Attack xy: string, x, y" # "Attack xy: SpearLike1, 22, 24" #==============================================================================# when /(?:ATTACK_XY|attack xy|atk_xy|atk xy):[ ]*(\d+)[ ]*,[ ]*(\d+)[ ]*,[ ]*(\d+)/i coords = [$2.to_i, $3.to_i] attack = YGG::ATTACKS[$1.to_s] @character.iex_ygg_execute_attack_at(attack, coords[0], coords[1])
Doesnt look all that neat does it:
^ Now if you find your self doing that, its time to break it down into other functions:
CODE
module ASEQ
# // Just a quick method for setting up the nessecary variables def self.start @list = [] @wait_count = 0 end
# // Currently empty update method def self.update() @wait_count = [@wait_count-1, 0].max() update_actions() if @wait_count == 0 end
def self.update_actions() while !@list.empty?() # // Keep looping until the @list is empty act_set = @list.shift() # // shift removes the first element from an array and returns it action = act_set[0] parameters = act_set[1] ret_code = -1 # // Return Code case action.upcase when "PRINT" ret_code = action_print( action, parameters ) when "WAIT" ret_code = action_wait( action, parameters ) end break if ret_code == 1 end end
def self.action_print( action, parameters ) print parameters[0] # // First parameter is the text to print return 0 end
def self.action_wait( action, parameters ) @wait_count = parameters[0] # // First parameter is the wait in frames return 1 end
end
ASEQ.start
loop do
Graphics.update ASEQ.update
end
In most cases the passing the action unto the function is useless, though there are some cases where you use 1 function that can carry out multiple similar actions Such as a wait function, that can take a value, or calculates one based on the name of the action (EG. "WAIT_FOR_ANIMATION")
Now to explain the ret_code (return code) This serves 2 main purposes, one is to tell the state of the last action (was it completed, does it need the loop to stop in order to continue) This also works for debugging, you know if the return code is -1 something didn't happen.
In this case, if the return code is 1 it breaks the loop.
Part4: First real test
Spoiler:
Now you have a basic functional system, but we need to actually test it to call it functional:
# // Just a quick method for setting up the nessecary variables def self.start @list = [] @wait_count = 0 end
# // Currently empty update method def self.update() @list = ACTION_LISTS["HelloWorld"].clone() if @list.empty?() # // Remove this when done @wait_count = [@wait_count-1, 0].max() update_actions() if @wait_count == 0 end
def self.update_actions() while !@list.empty?() # // Keep looping until the @list is empty act_set = @list.shift() # // shift removes the first element from an array and returns it action = act_set[0] parameters = act_set[1] ret_code = -1 # // Return Code case action.upcase when "PRINT" ret_code = action_print( action, parameters ) when "WAIT" ret_code = action_wait( action, parameters ) end break if ret_code == 1 end end
def self.action_print( action, parameters ) print parameters[0] # // First parameter is the text to print return 0 end
def self.action_wait( action, parameters ) @wait_count = parameters[0] # // First parameter is the wait in frames return 1 end
end
ASEQ.start
loop do
Graphics.update ASEQ.update
end
Now let me explain this:
CODE
@list = ACTION_LISTS["HelloWorld"].clone() if @list.empty?()
This line acts like a loop, once the list is empty, Now lemme explain why you CLONE the action_list(array) and not just assign it. shift is a destructive method, when you assign something your simply referencing(or pointing to it) it. Think of a ninja in broad daylight, your gonna point him out right, he get caught and sent to prison (for whatever reason) Now the clone method is like... his substitution, real ninja is untouched, and his sub gets sent off to prison.
Take a second and think about it.
shift == destructive pointer to said array Guess what happens:
while !@list.empty? print @list.shift end # => "Bad", "Stuff", "Gonna", "Happen" print MYARRAY # => "Bad", "Stuff", "Gonna", "Happen"
=w= Keep that in mind
You could save yourself all this crap though by simply using:
CODE
@list += ACTION_LISTS["HelloWorld"] if @list.empty?()
Personally I'm not entirely sure why I dont just do that instead D: *facepalms*
Now if you run your game, every 192 frames (3. something seconds) you will get window with "Hello World"
Congrats you have your basic action system. Now you can expand on this, and change it as much as you like, no credits required but would be appreciated.
# // Just a quick method for setting up the nessecary variables def self.start() @list = [] @wait_count = 0 end
# // def self.setup_action( name ) @list.clear(); @list += ACTION_LISTS[name] end
# // Currently empty update method def self.update() setup_action("HelloWorld") if @list.empty?() @wait_count = [@wait_count-1, 0].max() update_actions() if @wait_count == 0 end
def self.update_actions() while !@list.empty?() # // Keep looping until the @list is empty # // shift removes the first element from an array and returns it action, parameters = *@list.shift() ret_code = -1 # // Return Code case action.upcase() when "PRINT" ret_code = action_print( action, parameters ) when "WAIT" ret_code = action_wait( action, parameters ) end break if ret_code == 1 end end
def self.action_print( action, parameters ) print parameters[0] # // First parameter is the text to print return 0 end
def self.action_wait( action, parameters ) @wait_count = parameters[0] # // First parameter is the wait in frames return 1 end
end
ASEQ.start()
loop do
Graphics.update() ASEQ.update()
end
If you found this useful, or you have suggestions for future lessons, post here. (call it [Icy How Do You] cause I dun do lessons XD, :x even though I called this a lesson)
Bright and optimistic, your daemon reflects your sunny disposition. You are free-spirited and like to think outside the box. However, you can go from one extreme to another, sometimes extremely shy, or living in the limelight 24/7. Most people you meet like you, but you can sometimes be distant. You like to try new things, and do so frequently.
What Element Are You?
My Results:
Light
You're a bouncy, people-person who is an eternal optimist.
What sort of novel ending are you?
My Results:
Happy
You are the embodiment of a happy ending. To those around you, you are a paragon of happiness and delight, causing their days to be instantaneously brightened. You will forever remain a delight!
What Mystical creature are you?
My Results:
Dragon
You are very powerful, kind of mean and dark. You couldn't care less about others.<---That must be wrong!
What color are you???
My Results:
Rainbow!
u are every color in the world. you are sunny, calm, playful, and like hanging out with friends. keep doing what u do and u will succeed
What is your warrior name?
My Results:
Tigerstripe
You are Tigerstripe. Clan:ShadowClan Rank:Medicine Cat Pelt:Golden-brown with black stripes, and a white chest and front left paw. Scar on left eye. Eye color:Black Mother:Darkcloud Father:Whitepelt You are Tigerstripe, ShadowClan's medicine cat. Unlike most ShadowClan cats, who are normally up to something, you are a very sweet and generous she-cat. You have saved ShadowClan lives as well as cats from other clans. Even though medicine cats can't have kits, most toms seem to be incredibly fond of you.
What age are you on the inside?
My Results:
10 years old
You're finally mature enough to have a real conversation with someone but your attitude is kind of annoying...