Skip to content

Instantly share code, notes, and snippets.

@KokoseiJ
Created October 17, 2022 10:32
Show Gist options
  • Save KokoseiJ/389f9c6c8573a61ba771cd9275fce93d to your computer and use it in GitHub Desktop.
Save KokoseiJ/389f9c6c8573a61ba771cd9275fce93d to your computer and use it in GitHub Desktop.
Moving objects smoothly in pygame with decreasing acceleration rate
import pygame
import time
class SmoothMove:
def __init__(self, surface, orig, dest, duration):
self.surface = surface
self.orig = orig
self.dest = dest
self.duration = duration
self.starttime = None
self.y = 360
self.distance = dest - orig
self.start_speed = 2 * self.distance / duration
self.acceleration = -self.start_speed / duration
@property
def is_started(self):
return self.starttime is not None
@property
def elapsed_time(self):
if self.is_started:
return time.perf_counter() - self.starttime
else:
return None
@property
def is_running(self):
return self.is_started and not self.elapsed_time > self.duration
def start(self):
self.starttime = time.perf_counter()
def reset(self):
self.starttime = None
def get_pos(self):
return self._get_pos(self.elapsed_time)
def draw(self, surface):
return surface.blit(self.surface, (self.get_pos(), self.y))
def _get_pos(self, time_):
if not self.is_started:
cur_pos = self.orig
elif self.elapsed_time <= self.duration:
# s = v0t + 1/2at^2
cur_pos = (
self.orig
+ self.start_speed * time_
+ self.acceleration * time_**2 / 2
)
else:
cur_pos = self.dest
return cur_pos
class SmoothMoveXY:
def __init__(self, surface, orig, dest, duration):
self.surface = surface
self.orig = orig
self.dest = dest
self.duration = duration
self.starttime = None
self.distance_x = dest[0] - orig[0]
self.start_speed_x = 2 * self.distance_x / duration
self.acceleration_x = -self.start_speed_x / duration
self.distance_y = dest[1] - orig[1]
self.start_speed_y = 2 * self.distance_y / duration
self.acceleration_y = -self.start_speed_y / duration
@property
def is_started(self):
return self.starttime is not None
@property
def elapsed_time(self):
if self.is_started:
return time.perf_counter() - self.starttime
else:
return None
@property
def is_running(self):
return self.is_started and not self.elapsed_time > self.duration
def start(self):
self.starttime = time.perf_counter()
def reset(self):
self.starttime = None
def get_pos(self):
return self._get_pos(self.elapsed_time)
def draw(self, surface):
return surface.blit(self.surface, self.get_pos())
def _get_pos(self, time_):
if not self.is_started:
cur_pos = self.orig
elif self.elapsed_time <= self.duration:
# s = v0t + 1/2at^2
cur_pos_x = (
self.orig[0]
+ self.start_speed_x * time_
+ self.acceleration_x * time_**2 / 2
)
cur_pos_y = (
self.orig[1]
+ self.start_speed_y * time_
+ self.acceleration_y * time_**2 / 2
)
cur_pos = (cur_pos_x, cur_pos_y)
else:
cur_pos = self.dest
return cur_pos
pygame.init()
clock = pygame.time.Clock()
screen = pygame.display.set_mode((1280, 720))
surface = pygame.Surface((300, 300), pygame.SRCALPHA)
pygame.draw.circle(surface, "red", (150, 150), (150))
sm = SmoothMoveXY(surface, (0, 0), (1280 - 300, 720 - 300), 1)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
if sm.is_started and not sm.is_running:
sm.reset()
else:
sm.start()
screen.fill("gray")
sm.draw(screen)
pygame.display.flip()
clock.tick_busy_loop(60)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment