# LabelSprite rotate_by problem browsing

• posted
0

Hello everyone,

Newbie question : I am trying to create an animation of a label moving like a pendulum. I tried using a SpriteLabel rotating around an arbitrary point using Action.rotate_by, but it always rotates around the lower left corner of the screen (0, 0) and not around the top center, is there any way around this ?
Here is the exact code I am using:

from scene import *
from math import *

class MyScene(Scene):

def setup(self):
self.background_color = 'black'
self.anchor_point = (0.5, 0.0)
self.timelabel = LabelNode(position=self.size / 2, text='time')
self.timelabel.rotation = -pi / 8.0
self.run_action(Action.repeat(Action.sequence(Action.rotate_by(pi / 4.0, 1, TIMING_EASE_IN_OUT), Action.rotate_by(-pi / 4.0, 1, TIMING_EASE_IN_OUT), 0), 0))

run(MyScene(), PORTRAIT)

Cheers
Andre

posted
1

Is this close to the behavior you want?

from scene import *
from math import *

class MyScene(Scene):

def setup(self):
self.background_color = 'black'
self.time_anchor = Node(position=(self.size.w/2, 0), parent=self)
self.timelabel = LabelNode(position=(0, self.size.h/2), text='time')
rotate_action = Action.repeat(Action.sequence(Action.rotate_to(pi / 4.0, 1, TIMING_EASE_IN_OUT), Action.rotate_to(-pi / 4.0, 1, TIMING_EASE_IN_OUT), 0), 0)
self.time_anchor.run_action(rotate_action)

run(MyScene(), PORTRAIT)

Things I've changed:

• Added time_label to a container Node (time_anchor), so it can be rotated around an arbitrary point without rotating the whole scene (your approach of rotating the scene itself becomes problematic when you want to add more things to the scene; also, the anchor_point attribute is ignored for scenes)
• Changed rotate_by to rotate_to -- this is just a guess, but I think the resulting effect may be closer to what you're after.

• posted
0

That is exactly what I wanted omz, thanks alot!

Cheers
Andre

• posted
0

Hi @omz,

is there a way to include a dynamic variable in an Action?

I was thinking of using the variable self.angle in the Action like this:

from itertools import cycle
from scene import *
from math import *

class MyScene(Scene):

def setup(self):
self.background_color = 'black'
self.time_anchor = Node(position=(self.size.w/2, 0), parent=self)
self.timelabel = LabelNode(position=(0, self.size.h/2), text='time')
self.angles = cycle([pi/4, -pi/4])
rotate_action = Action.repeat(Action.rotate_to(self.angles.next(), 1, TIMING_EASE_IN_OUT), 0)
self.time_anchor.run_action(rotate_action)

run(MyScene(), PORTRAIT)

This does not seem to work it stays at the same angle all the time, is there any way around it?

Cheers
Andre

• posted
1

One way to do is to write your own 'Action.repeat".. In the following code, the angles get changed when you touch. May be others could suggest better ways to this.

from scene import *
from math import *

class MyScene(Scene):
def repeat(self):
self.rotate_action = Action.sequence(
Action.rotate_to(self.angles[0], 1, TIMING_EASE_IN_OUT),
Action.rotate_to(self.angles[1], 1, TIMING_EASE_IN_OUT),
Action.call(self.repeat), 0)
self.time_anchor.run_action(self.rotate_action)

def setup(self):
self.background_color = 'black'
self.time_anchor = Node(position=(self.size.w/2, 0), parent=self)
self.timelabel = LabelNode(position=(0, self.size.h/2), text='time')
self.angles = (pi/8, -pi/8)
self.rotate_action = Action.sequence(
Action.rotate_to(self.angles[0], 1, TIMING_EASE_IN_OUT),
Action.rotate_to(self.angles[1], 1, TIMING_EASE_IN_OUT),
Action.call(self.repeat), 0)
self.time_anchor.run_action(self.rotate_action)
self.toggle = True

def touch_began(self, touch):
if self.toggle:
self.angles = (pi/4, -pi/4)
else:
self.angles = (pi/8, -pi/8)
self.toggle = not self.toggle

run(MyScene(), PORTRAIT)

• posted
1

Try this code, you can easily define the fps (frame per second), you work in degrees and you can define min/max, and you can have more interaction than with run_action:

from scene import *
from math import *

class MyScene(Scene):
def setup(self):
self.background_color = 'black'
self.timelabel = LabelNode(position=self.size/2, text='time')
self.ang = -45
self.delta = 1
def update(self):
self.ang = self.ang + self.delta
if abs(self.ang) > 45:
self.delta = -self.delta
self.ang = self.ang + self.delta
r = self.size[1]/2
self.timelabel.position = (x,y)

run(MyScene(), PORTRAIT,frame_interval=1)

• posted
1

You can change the first line of the update function to the following code if you want TIMING_EASE_IN_OUT like effect

self.ang = self.ang + self.delta*cos(radians(2*self.ang)) +.05*self.delta

Note that it assumes the maximum angle is 45. If it is different, you need to change 2*self.ang to appropriate expression.

• posted
0

Thank you @abcabc,
creating a new action is great, do I need to do a self.time_anchor.remove_all_actions ()before?
Cheers
Andre

• posted
0

Thanks @cvp,

I was trying to learn automatic actions so I had not considered your approach, I guess I was being lazy :-)

Cheers
Andre

• posted
0

No problem, I had never used scene thus I'm always happy to try and thus to learn something. Good luck

• posted
1

I think that it is not needed since the previous action will be complete by that time. I assume that "run_action" call spawns a thread and
the Action.call does not wait for the thread to complete and it returns immediately. I hope that run_action call overheads are not much.I do not know the internal implementation details and may be omz can answer this.

• posted
0

"run_action" may not be spawning a thread and it may just create action related data structures. which will be updated every frame. Anyway other experts ( I am just learning) could help you on this.

• posted
0

Well, it's running just fine!
Thanks to all!
Cheers
Andre

Internal error.

Oops! Looks like something went wrong!