Creating your own actions is pretty easy. You should familiarize yourself with this concepts, because actions are very powerful and can be combined with another actions to create more actions.
For example, there is the Blink action. It is implemented by subclassing IntervalAction, but you could actually do something like:
def Blink(times, duration): return ( Hide() + Delay(duration/(times*2)) + Show() + Delay(duration/(times*2)) ) * times
All actions work on a target. Its their callers responsibility to set the target to the correct element. This allows the user to instantiate an action and then apply the same action to various different elements. All cocosnodes can be a target for an action.
You will not know who the target is when __init__ or init is called, but you will when start is called. If you are making an action that takes more actions as parameters, it is your responsibility to:
- set the target
- call the start method
- call the stop method
You can also override the __reversed__ method. In this method you have to construct and return an action that would be the reversed version of the action you are doing. For example, in Show() we return Hide() as its reverse:
class Show( InstantAction ): "<snip>" def __reversed__(self): return Hide()
Instant actions are actions that will take no time to execute. For example, Hide() sets the target visibility to False.
It is very easy to create an action using the CallFuncS action as a decorator:
@CallFuncS def make_visible( sp ): sp.do( Show() ) self.sprite.do( make_visible )
please note that make_visible will not be a regular function that you can call, it will be an action. So you can compose it like any other action.
Thats it.
For example, this is a minimal implementation of SetOpacity:
class SetOpacity( InstantAction ): def init(self, opacity): self.opacity = opacity def start(self): self.target.opacity = self.opacity
Interval actions is where the fun is. With this actions you can specify transformations that take a finite time. For example, MoveBy(how_much, duration).
So its in update that you do your magic. For example, if you want to fade something out, you can write something like:
class FadeOut( IntervalAction ): def init( self, duration ): self.duration = duration def update( self, t ): self.target.opacity = 255 * (1-t) def __reversed__(self): return FadeIn( self.duration )
The trick is that whoever is running your action will interpolate the values of t so that you get called with t==1 when your duration is up. This does not mean that duration seconds have elapsed, but it usually does. If someone wants to make your action go twice as fast, they can feed you updates at a different rate and you should not care.
Also, this allows us to change the interpolation method. We usually use linear interpolation, but AccelDeccel, for example, uses a sigmoid function so that is goes slower at the ends.
These are IntervalAction actions, but instead of modifying normal attributes like rotation, position, scale, they modify the grid attribute.
Let's see in detail how to build a basic non-tiled-grid action:
class Shaky3D( Grid3DAction):
Shaky3D is a subclass of Grid3DAction, so we are building a non-tiled action. If we want to create a tiled action, we need to subclass from the TiledGrid3DAction class:
def init( self, randrange=6, *args, **kw ): ''' :Parameters: `randrange` : int Number that will be used in random.randrange( -randrange, randrange) to do the effect ''' super(Shaky3D,self).init(*args,**kw) #: random range of the shaky effect self.randrange = randrange
Our class receives the randrange parameter, so we save it, and we call init of our super class:
def update( self, t ): for i in xrange(0, self.grid.x+1): for j in xrange(0, self.grid.y+1): x,y,z = self.get_original_vertex(i,j) x += random.randrange( -self.randrange, self.randrange+1 ) y += random.randrange( -self.randrange, self.randrange+1 ) z += random.randrange( -self.randrange, self.randrange+1 ) self.set_vertex( i,j, (x,y,z) )
Like any other IntervalAction action the update method is going to be called once per frame. So, our Shaky3D effect will modify the x,``y`` and z coordinate by a random number that is calculated by the random.randrange function.
The get_original_vertex method returns the original coordinates of the vertex x and y, while the get_vertex method returns the current coordinates of the vertex x and y.
XXX: Explain how to build a Tiled Action XXX: How to use: get_original_tile and get_tile