On Tue, Jul 6, 2010 at 9:31 AM, claudio canepa <[email protected]> wrote:
>
> I want to discuss to changes in actions code.
> I specifically want Richard's feedback, because is related to code checked
> in by him, but if someone else has an opinion please chime in.
>
> 1) I want to remove actions.instant_actions.DoAction
> Rationale:
> I think it was introduced to deal with limitations in the old (0.3.0)
> sequence operator, like the DoAction docstring tells:
>
> "...Useful if you want to sequence actions of infinite duration."
I'll admit I actually don't understand what DoAction's use-case is. I
fixed the docstring as best I could because it was broken, as far as I
could tell :-)
> The cocos 0.4 sequence operator was upgraded and now directly supports
> sequencing when one of the actions can have potential infinite duration.
>
> Thus, the example in the docstring:
>
> action = Repeat( dance )
> sprite.do( go_home + DoAction( action ) )
>
> can be written
> action = Repeat( dance )
> sprite.do( go_home + Repeat( dance ) )
>
> If you want an example with stock cocos,
>
> rotate = RotateBy(360, 1 )
> sprite.do( MoveTo(home, 2) + Repeat(rotate))
>
> Thus, DoAction duplicates existing functionality.
> And I think it is more clear 'go_home + Repeat( dance)' than 'go_home +
> DoAction( Repeat(dance) )'
>
> As it was only recently added in trunk, and never made to release, we don't
> need to preserve for compatibility reasons.
Yeah, I'm not sure I see a point to DoAction either.
> 2) I want to remove actions.interval_actions.Mover
LOL! I didn't even know that was there!
I like the idea of a standard Mover, but I don't like that
implementation, for different reasons. For what it's worth I've just
implemented this mover:
class Move(actions.Action):
"""Move the target based on parameters on the target.
For movement the parameters are::
target.position = (x, y)
target.velocity = (dx, dy)
target.acceleration = (ddx, ddy) = (0, 0)
target.gravity = 0
And rotation::
target.rotation
target.dr
target.ddr
"""
def step(self, dt):
x, y = self.target.position
dx, dy = self.target.velocity
ddx, ddy = getattr(self.target, 'acceleration', (0, 0))
gravity = getattr(self.target, 'gravity', 0)
dx += ddx * dt
dy += (ddy + gravity) * dt
self.target.velocity = (dx, dy)
x += dx * dt
y += dy * dt
self.target.position = (x, y)
dr = getattr(self.target, 'dr', 0)
ddr = getattr(self.target, 'ddr', 0)
if dr or ddr:
dr = self.target.dr = dr + ddr * dt
if dr:
self.target.rotation += dr * dt
class WrappedMove(Move):
"""Move the target but wrap position when it hits certain bounds.
Wrap occurs outside of 0 < x < width and 0 < y < height taking into
account the dimenstions of the target.
"""
def init(self, width, height):
"""Init method.
:Parameters:
`width` : integer
The width to wrap position at.
`height` : integer
The height to wrap position at.
"""
self.width, self.height = width, height
def step(self, dt):
super(WrappedMove, self).step(dt)
x, y = self.target.position
w, h = self.target.width, self.target.height
# XXX assumes center anchor
if x > self.width + w/2:
x -= self.width + w
elif x < 0 - w/2:
x += self.width + w
if y > self.height + h/2:
y -= self.height + h
elif y < 0 - h/2:
y += self.height + h
self.target.position = (x, y)
class BoundedMove(Move):
"""Move the target but limit position when it hits certain bounds.
Position is bounded to 0 < x < width and 0 < y < height taking into
account the dimenstions of the target.
"""
def init(self, width, height):
"""Init method.
:Parameters:
`width` : integer
The width to bound position at.
`height` : integer
The height to bound position at.
"""
self.width, self.height = width, height
def step(self, dt):
super(BoundedMove, self).step(dt)
x, y = self.target.position
w, h = self.target.width, self.target.height
# XXX assumes center anchor
if x > self.width - w/2:
x = self.width - w/2
elif x < w/2:
x = w/2
if y > self.height - h/2:
y = self.height - h/2
elif y < h/2:
y = h/2
self.target.position = (x, y)
This is what I'll be using in the tutorial I'm presenting at
EuroPython. It seems nicer to me for a couple of reasons:
- the movement control vars are on the sprite (well, cocosnode) being
controlled instead of the action, and
- the movement control is more flexible, allowing acceleration and
movement along a different vector to the rotation.
Note the base class is actions.Action.
I'd like to put this into the cocos library as cocos/actions/move_actions.py
> Rationale:
>
> The intentions for this code seems to be:
> - allow external code to get access to the performing action
> - control from external code some actions parameters
> - move over a parabolic trajectory, have heading in consideration
>
> The first point is achieved by cheating __deepcopy__ to return the action
> itself, not a copy
> But deepcopy is central to the current actions implementation:
> i. used in all operators, and in particular Loop and Repeat will fail
> with an action cheating as such
> ii. it allows code like
> action = MoveBy( (100, 0), 3)
> node1.do(action)
> node2.do(action)
> to perform as expected, both nodes moving left by 100
> An action cheating deepcopy will broke that
I don't believe it makes sense to Loop or Repeat a Mover. Nor does it
make sense to have multiple objects sharing the same Mover instance.
> An there is an alternative way to allow external code to control action's
> parameters without touching deepcopy:
> cocosnode.do returns the action that will perform the changes, so the
> external code can do:
> action = ActionNotCheatingDeepcopy()
> worker_action = node.do(action)
> external code operates over worker_action
>
> (Your code was intended to do:
> mover = Mover()
> external code operates over mover
> )
>
> I suspect that controlled actions (when external code changes actions
> parameters in the action life) opens a whole new can of worms, like really
> not working good with operators.
Indeed.
> It can make sense at the app level, where the app context made clear there
> are special actions, not intended to be operated, not to be performed by two
> nodes, other restrictions my apply. And even at the app level I would not
> use the deepcopy cheat: the alternative reminds what is the normal idiom to
> refer to the performing action (like I need to know if I want to do
> node.remove_action : node.remove_action(worker_action) works )
>
> To summarize:
> 1) the deepcopy trick clashes with basic asumptions about actions
> behavior
> 2) the normal way to acces the performing action is ignored
> 3) controlled actions are unchartered territory; to play fair with the
> base actions we will need a base class for actions even more basic that
> Actions, and see which operators can be allowed ( top of my head spawn is
> plausible, but needs other implementation, Loop and Repeat seems less
> plausible, and very tricky to implement)
> 4) it is normal for users to look the lib code to write his own custom
> classes, and this will be steering them in the wrong direction in most of
> cases
>
> Should this action be retained in some form, it must be Action subclass, not
> IntervalAction, and some warning added about why is a special case and not a
> typical action.
Agreed, like I've done with my new Move class.
> I attach the example in test that uses Mover, modified to not use the
> deepcopy cheat (drop in the test dir to have resources)
>
> I you are ok, Richard, I can perform the relevant changes
I think it's reasonable to lose Mover as it does pose some problems as
you've outlined and also I find it less useful than it could be :-)
Richard
--
You received this message because you are subscribed to the Google Groups
"cocos2d discuss" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/cocos-discuss?hl=en.