gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Game Design > Special Effects Scripting Language

#155887 - sgeos - Mon May 05, 2008 7:40 am

I was speaking with a friend about scripting, and the topic of a "special effects scripting language" came up. I've implemented something like this before using time based transitions that modify a host object. That is actually how the movement in this game was done. (Card flips, dealing cards, help window, win/lose messages... everything.)

The goal this time is to come up with a general purpose special effects language. A stack based approach follows. Each controller has an update stack and a general stack. The the general stack runs until it is out of tasks. The update stack loops until the general stack has finished doing its job.

Code:
# comment

# function definition
funtionName(pParamA, pParamB, pParamC)
{
}

# global constants
CONSTANT

# set the object to modify
controller.host = object
controller.host = Object.new(params)

# set effect using initial and final states
controller.transition_type(frames=1).parameterA(initial_state, final_state).parameterB(initial_state, final_state)

# set effect using final states (transition from current value to final value)
controller.transition_type(frames=1).parameterC(final_state).parameterD(final_state)

# mix the above just for kicks
controller.transition_type(frames=1).parameterE(initial_state, final_state).parameterF(final_state)

# transition types
controller.linear(frames=1) # current_frame / max_frame
controller.cos(frames=1)    # 1 - cos( PI * current_frame / max_frame ); radians; reluctant
controller.sin(frames=1)    # sin( PI * current_frame / max_frame ); radians; eager
controller.s(frames=1)      # cos to midpoint, sin from midpoint; slow, fast, slow
controller.z(frames=1)      # sin to midpoint, cos from midpoint; fast, slow, fast

# push effect list onto stack (controller stops when it reaches the end of the stack)
controller.push(repeat=1)

# push effects list onto update stack (controller repeats update stack from the start when it reaches the end)
controller.update(repeat=1)

# clear stack in question
controller.clear.push
controller.clear.update

# do nothing for a number of frames
controller.wait(frames)

# repeat entire stack in question a number of times
controller.repeat(times=1).push
controller.repeat(times=1).update

# special output
# flashing, shaking and tinting the background also belong here
controller.sfx(sound_effect_id)       # NULL clears currently playing effect
controller.bgm(background_music_id)   # NULL restores previous BGM
controller.value(value)               # show a value at the host's location
controller.text(text)                 # show text at the host's location
controller.message(message, *params)  # print to the message window

# example animation
# animation time ... 210 frames
# source throws a blade at target, blade spins on target and
# returns a little short of source, but source moves in to compensate
flyingBlade(pSource, pTarget, pDamage)
{
  # initialize effect stacks
  blade.host   = Image.new(GFX_BLADE_SMALL)
  sourceX.host = pSource  # doing something fancy, so two stacks are needed
  sourceY.host = pSource  # see above
  target.host  = pTarget
 
  # blade spin - rotate 360 degrees every 30 frames for the entire effect
  # slowest point is 270 degrees (facing left); spin counter clockwise
  blade.s(30).angle(270,-90).update
 
  # source and target wait
  sourceX.wait(150).push
  sourceY.wait(180).push
  target.wait(90).push

  # blade flies at target; makes swish sound
  blade.sfx(SFX_SWISH)
  blade.linear(90).scale(0,100).alpha(0,100)
  blade.cos(90).x(pSource.x, pTarget.x)
  blade.sin(90).y(pSource.y, pTarget.y)
  blade.push

  # blade hits target; makes grinding noise
  blade.sfx(SFX_GRIND).value(pDamage).wait(30).push  # spinning is automatic

  # target shakes
  pTarget.clear.push
  pTarget.linear.x( 2).push
  pTarget.linear.x(-2).push
  pTarget.repeat(15)

  # blade returns to source; makes zing sound
  gfxA.sfx(SFX_ZING)
  gfxA.linear(60).scale(100,0).alpha(100,0)
  gfxA.cos(60).x(pTarget.x, pSource.x - 30)
  gfxA.sin(60).y(pTarget.y, pSource.y)
  gfxA.push

  # source moves to catch blade
  pSourceX.s(30).x(-30).push

  # blade makes a click sound when caught
  gfxA.sfx(SFX_CLICK).push

  # source returns to position
  pSourceX.z(30).x(30).push
  pSourceY.sin(15).y( 15).push  # U-shape down
  pSourceY.cos(15).y(-15).push  # U-shape up

  # cleanup
  blade.host.delete
}

Like any good scripting language, the goal is to have something that is easy for the scripter to use. There are two end goals for such a scripting language- A) replace the special effects programmer with special effects scripters, B) make the life easier for the special effects programmer if it is decided that scripters are unnecessary afterall. I'm looking for comments and constructive criticism.

-Brendan

EDIT / NOTES TO SELF:
A) Need a way to dynamically staple one object to another VS just reading the value one time. Having an orb circle a critter as the critter moves is Good Stuff. Need to be able to both in any case. Afterall, sometimes you want to throw an object at a critter and then move the critter out of the way.
B) I didn't like how the shake was done when I was writing it, but that somehow seems correct.
C) On the return, X uses non-linear timing, and Y does two transitions in the time X does one. I don't see how to make this work without more than one controller for the same object.