## Tweens, building a particle emitter

This is the place to post any code that you want to share with the community. Only completed scripts should be posted here.
Note: posts here are not necessarily endorsed by the community, and may represent amateur or even bad practices.

### Tweens, building a particle emitter

First, the code:
Code: Select all
`class Tween(object):   def __init__(self, start=0, end=1, duration=10):      self.startpoint = start      self.endpoint = end      self.duration = duration      self.current = 0      @classmethod   def start(cls, point):      tw = cls(point)      return tw      def end(self, point):      self.endpoint = point      return self      def over(self, duration):      self.duration = duration      return self      def tick(self, amount):      self.current += amount      percent_complete = self.current / self.duration      if percent_complete >= 1.0:         end = self.endpoint         self.endpoint = None         return end      return ((self.endpoint - self.startpoint) * percent_complete) + self.startpoint      if __name__ == '__main__':   t = Tween.start(0).end(10).over(1000)   ticking = True   while ticking:      tick = t.tick(10)      ticking = tick      if tick:         print(tick)`

Tweening is used in animation to smoothly move something from one place to another. Here's a quick little script to help with that. A better example to help understand would be to think of an element moving across a webpage. The "start" param would be the element's original x-coordinate, and the "end" is what you want that x-coordinate to be when the animation is complete. "over", then, is the length of time it should take to get from start to end, probably in milliseconds. You then repeatedly tick the tweener, letting it know how much time has passed since the last time you ticked it, and it lets you know what the new value should be. Following our webpage example, you'd set the element's x-coordinate to whatever the return value of tick() was. The result, with a good 'over' and 'tick' setting, would appear to be an element that smoothly moves itself from one place to another.

In python, this sort of thing could very easily be used with a graphical package (such as pygame) to create animations. When I get some free time, I may do an example of it in pygame to demonstrate.
Last edited by nilamo on Tue Sep 13, 2016 6:19 pm, edited 1 time in total.
This forum is shutting down. Future discussion should take place at http://python-forum.io

nilamo

Posts: 87
Joined: Thu Jul 21, 2016 8:00 pm
Location: Michigan

### Re: Tweens, first look

Code: Select all
`class Tween(object):   def __init__(self, start=0, end=1, duration=10):      self.startpoint = start      self.endpoint = end      self.duration = duration      self.current = 0      self.complete = False      @classmethod   def start(cls, point):      tw = cls(point)      return tw      def end(self, point):      self.endpoint = point      return self      def over(self, duration):      self.duration = duration      return self      def tick(self, amount):      self.current += amount      percent_complete = self.current / self.duration      if percent_complete >= 1.0:         self.complete = True         return self.endpoint      return ((self.endpoint - self.startpoint) * percent_complete) + self.startpoint      if __name__ == '__main__':   import pygame   import time      WHITE = (255, 255, 255)   BLACK = (0, 0, 0)      # initialize pygame   pygame.init()   screen_size = (700, 500)   # create a window   screen = pygame.display.set_mode(screen_size)   pygame.display.set_caption("Tween Test")      # let's create a tweener   tween = Tween.start(100).end(screen_size[0]-100).over(1000)   bouncing_ball = pygame.Surface((10, 10))   bouncing_ball.fill(WHITE)      # clock is used to set a max fps   clock = pygame.time.Clock()   last_update = time.time()      running = True   while running:      for event in pygame.event.get():         if event.type == pygame.QUIT:            running = False            #clear the screen      screen.fill(BLACK)            # draw to the screen      tick = tween.tick(1000 * (time.time() - last_update))      screen.blit(bouncing_ball, (tick, 125))      if tween.complete:         # create a new tweener to move the dot back where it came from         # it'll go back and forth, like a bouncing ball         tween = Tween.start(tween.endpoint).end(tween.startpoint).over(tween.duration)      # flip() updates the screen to make our changes visible      pygame.display.flip()            last_update = time.time()      clock.tick(60)      pygame.quit()`

Here's an example using pygame. If you run it (and have pygame installed), you'll see a small box bounce back and forth from left to right, smoothly sliding across the screen.

Currently, only linear easing is implemented. Next up, I'll add more easings. Probably the default should be ease-in-sine (view a ton of them here: http://easings.net/). ease in sine is nice, because the movement starts slow, then picks up the pace afterward... like a car that needs a little bit of time to start moving (very few things in nature actually move linearly).

...pay no attention to my poor mouse-drawing skills.
Attachments
tween.PNG (5 KiB) Viewed 1095 times
This forum is shutting down. Future discussion should take place at http://python-forum.io

nilamo

Posts: 87
Joined: Thu Jul 21, 2016 8:00 pm
Location: Michigan

### Re: Tweens, first look

Math is hard, but here's some easings. Code is mostly stolen from here.
Code: Select all
`class Easing(object):   @staticmethod   def linear(start, end, timepoint, maxtime):      percent = timepoint / maxtime      return start + ((end - start) * percent)   @staticmethod   def in_out_cubic(start, end, timepoint, maxtime):      percent = timepoint / maxtime      ts = percent * percent      tc = ts * percent      return start + (end - start) * (-2*ts + 3*ts)         @staticmethod   def in_quintic(start, end, timepoint, maxtime):      percent = timepoint / maxtime      return start + (end - start) * (percent * percent * percent * percent)         @staticmethod   def in_cubic(start, end, timepoint, maxtime):      percent = timepoint / maxtime      return start + (end - start) * (percent * percent * percent)      class Tween(object):   def __init__(self, start=0, end=1, duration=10, easing=Easing.linear):      self.startpoint = start      self.endpoint = end      self.duration = duration      self.current = 0      self.complete = False      self.easing = easing      @classmethod   def start(cls, point):      tw = cls(point)      return tw      def end(self, point):      self.endpoint = point      return self      def over(self, duration):      self.duration = duration      return self      def via(self, easing):      self.easing = easing      return self      def tick(self, amount):      self.current += amount      if self.current >= self.duration:         self.complete = True         return self.endpoint      return self.easing(self.startpoint, self.endpoint, self.current, self.duration)            if __name__ == '__main__':   import pygame   import time      WHITE = (255, 255, 255)   BLACK = (0, 0, 0)      # initialize pygame   pygame.init()   screen_size = (700, 500)   # create a window   screen = pygame.display.set_mode(screen_size)   pygame.display.set_caption("Tween Test")      # let's create a tweener   tween = Tween.start(100).end(screen_size[0]-100).over(1000).via(Easing.in_quintic)   bouncing_ball = pygame.Surface((10, 10))   bouncing_ball.fill(WHITE)      # clock is used to set a max fps   clock = pygame.time.Clock()   last_update = time.time()      running = True   while running:      for event in pygame.event.get():         if event.type == pygame.QUIT:            running = False            #clear the screen      screen.fill(BLACK)            # draw to the screen      tick = tween.tick(1000 * (time.time() - last_update))      screen.blit(bouncing_ball, (tick, 125))      if tween.complete:         # create a new tweener to move the dot back where it came from         # it'll go back and forth, like a bouncing ball         easing = Easing.in_cubic if tween.easing == Easing.in_quintic else Easing.in_quintic         tween = Tween.start(tween.endpoint).end(tween.startpoint).over(tween.duration).via(easing)      # flip() updates the screen to make our changes visible      pygame.display.flip()            last_update = time.time()      clock.tick(60)      pygame.quit()`
This forum is shutting down. Future discussion should take place at http://python-forum.io

nilamo

Posts: 87
Joined: Thu Jul 21, 2016 8:00 pm
Location: Michigan

### Re: Tweens, building a particle emitter

So now that we've got easings, let's wrap the tweener controls into a controller class which can handle multiple tweens at once. Let's call it a particle. With a little setup code, the main loop is getting nice and slim and easy to understand. The next step is building an emitter, which creates multiple particles, sets their effects, and keeps track of all active particles.

A particle emitter (since we're now talking about that), is something which is used to create various effects. Effects such as fog, fire, clouds, the flash+smoke of gun fire, or an explosion. Included here are the bare bones (the only thing a particle can do is update it's own x-position), but we'll add more later. This will start getting exciting once particles can have dynamic x/y positions, colors, alpha, and size properties.
Code: Select all
`class Easing(object):   @staticmethod   def linear(start, end, timepoint, maxtime):      percent = timepoint / maxtime      return start + ((end - start) * percent)   @staticmethod   def in_out_cubic(start, end, timepoint, maxtime):      percent = timepoint / maxtime      ts = percent * percent      tc = ts * percent      return start + (end - start) * (-2*ts + 3*ts)         @staticmethod   def in_quintic(start, end, timepoint, maxtime):      percent = timepoint / maxtime      return start + (end - start) * (percent * percent * percent * percent)         @staticmethod   def in_cubic(start, end, timepoint, maxtime):      percent = timepoint / maxtime      return start + (end - start) * (percent * percent * percent)      class Tween(object):   def __init__(self, start=0, end=1, duration=10, easing=Easing.in_cubic):      self.startpoint = start      self.endpoint = end      self.duration = duration      self.current = 0      self.complete = False      self.easing = easing      @classmethod   def start(cls, point):      tw = cls(point)      return tw      def end(self, point):      self.endpoint = point      return self      def over(self, duration):      self.duration = duration      return self      def via(self, easing):      self.easing = easing      return self      def tick(self, amount):      self.current += amount      if self.current >= self.duration:         self.complete = True         return self.endpoint      return self.easing(self.startpoint, self.endpoint, self.current, self.duration)class Particle(object):   def __init__(self, screen, pos=(0,0), size=(1, 1), rgb=(0,0,0)):      self.screen = screen      self.surface = pygame.Surface(size)      self.rgb = rgb      self.surface.fill(self.rgb)      self.start_time = None      self.last_update = None      self.complete = False      self.pos = pos      self.alpha = 255      self.effects = []      def add_effect(self, starttime, effect, tweener):      self.effects.append([starttime, effect, tweener])      def start(self):      self.start_time = time.time()      self.last_update = time.time()      self.screen.blit(self.surface, self.pos)      self.complete = False         def update(self):      last = self.last_update      self.last_update = time.time()      since_last = 1000 * (self.last_update - last)      since_start = 1000 * (self.last_update - self.start_time)            active_effects = []      for effect in self.effects:         if effect[0] <= since_start:            effect[1](self, effect[2].tick(since_last))            if not effect[2].complete:               active_effects.append(effect)         else:            active_effects.append(effect)      self.effects = active_effects            self.surface.fill(self.rgb)      self.screen.blit(self.surface, self.pos)      self.complete = 0 == len(active_effects)         def setXPos(self, new_x):      self.pos = (new_x, self.pos[1])      if __name__ == '__main__':   import pygame   import time      WHITE = (255, 255, 255)   BLACK = (0, 0, 0)      # initialize pygame   pygame.init()   screen_size = (700, 500)      # create a window   screen = pygame.display.set_mode(screen_size)   pygame.display.set_caption("Particle Test")      # clock is used to set a max fps   clock = pygame.time.Clock()      particle = Particle(screen, (200, 400), (10, 10), WHITE)   particle.add_effect(2000, lambda p, val: p.setXPos(val), Tween.start(200).end(600).over(3000))   particle.add_effect(5000, lambda p, val: p.setXPos(val), Tween.start(600).end(100).over(3000))   particle.start()      running = True   while running:      for event in pygame.event.get():         if event.type == pygame.QUIT:            running = False            #clear the screen      screen.fill(BLACK)            # draw to the screen      if particle:         particle.update()         if particle.complete:            particle = None      # flip() updates the screen to make our changes visible      pygame.display.flip()            last_update = time.time()      clock.tick(60)      pygame.quit()`
This forum is shutting down. Future discussion should take place at http://python-forum.io

nilamo

Posts: 87
Joined: Thu Jul 21, 2016 8:00 pm
Location: Michigan

### Re: Tweens, building a particle emitter

This series will be continued on the new forums...
STAY TUNED, SPORTSFANS
This forum is shutting down. Future discussion should take place at http://python-forum.io

nilamo

Posts: 87
Joined: Thu Jul 21, 2016 8:00 pm
Location: Michigan