iPhone App Directory
RPG Maker VX
 
Gallery Members Search Calendar Help


Welcome Guest ( Log In | Register )

Collapse

>Announcement

Keep an eye out for the upcoming 3rd Quarter Contest!
 
Reply to this topicStart new topic
> [Lesson]Create a Basic Action Sequence System, Or something like that..
IceDragon
post Aug 17 2011, 09:01 PM
Post #1


I demand cookies.
Group Icon


Type: Coder
Alignment: Neutral Good




Scriptign Lessonz With Icy

Lesson01:Create a Basic Action Sequence System

Introduction

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 dry.gif )
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


CODE
@wait_count = [@wait_count-1, 0].max()


Here is whats happening:
CODE
@wait_count = @wait_count - 1
@wait_count = 0 if @wait_count < 0

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:
CODE
module ASEQ
  
  # // Setup an empty hash
  ACTION_LISTS = {}
  
  ACTION_LISTS["HelloWorld"] = [
    ["PRINT", ["Hello World"]],
    ["WAIT", [192]],
  ]
  
  # // 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:
CODE
MYARRAY = ["Bad", "Stuff", "Gonna", "Happen"]
@list = MYARRAY

while !@list.empty?
print @list.shift
end
# => "Bad", "Stuff", "Gonna", "Happen"
print MYARRAY
# => *But nothing happened*


Now we gonna clone said array
CODE
MYARRAY = ["Bad", "Stuff", "Gonna", "Happen"]
@list = MYARRAY.clone

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.



Final Code (Dont just copy this >_>)

Spoiler:

CODE
module ASEQ
  
  # // Setup an empty hash
  ACTION_LISTS = {}
  
  ACTION_LISTS["HelloWorld"] = [
    ["PRINT", ["Hello World"]],
    ["WAIT", [192]],
  ]
  
  # // 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)


--------------------
Spoiler:





To be Hexa-fied


Spoiler:
Other/Reminders--
Support your cookie loving dragon!

CODE
[url="http://www.rpgmakervx.net/index.php?showuser=52960"][img]http://i56.tinypic.com/2d1pfmw.png[/img][/url]


http://www.rpgmakervx.net/index.php?showto...amp;#entry48215 HA AUTOTILES
Pine's Birthday is the 2th of July

WORK ON YOUR TIME MEMORY @___@
Go to the top of the page
 
+Quote Post
   
Toothless the Ni...
post Nov 26 2011, 10:25 PM
Post #2



Group Icon


Type: Undisclosed
Alignment: Unaligned




Nice. Not gonna try it, because it isn't something I would do really.

How do you do basic battle modifications like stringing skills together to make a large attack?


--------------------
Shorten your signature or it gets deleted.
Spoiler:
I support:
Spoiler:


Free to click on and will help me alot!


http://www.otakuzone.com/registration/inde...DN8Um94YXNYSUlJ
Please go there!And register!

Spoiler:
What is your daemon's shape?

My Results:


Your daemon is a Blue Morpho Butterfly!

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...












The RMVX Wars.Destroyed rmvx,Click here for RMBO!!!


My adopteds!




Awesome Site:


Go to the top of the page
 
+Quote Post
   

Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 

Lo-Fi Version Time is now: 30th August 2014 - 01:19 AM

RPGMakerVX.net is an Privacy Policy, Legal.