Skip to content

Instantly share code, notes, and snippets.

@bojidar-bg
Created July 25, 2024 09:34
Show Gist options
  • Save bojidar-bg/f58eb518d460d82be7bf6fab9c105bf4 to your computer and use it in GitHub Desktop.
Save bojidar-bg/f58eb518d460d82be7bf6fab9c105bf4 to your computer and use it in GitHub Desktop.
Procedurally-animated worm in Python
# A simple program which shows a procedurally-animated worm that can be moved around by clicking on the screen.
# References: https://mcsp.wartburg.edu/zelle/python/graphics/graphics/graphref.html, https://tkdocs.com/shipman/index-2.html
from random import random
from math import sqrt
from graphics import * # https://mcsp.wartburg.edu/zelle/python/graphics.py
class FollowerCircle(Circle):
"""
Creates a circle that follows another circle around at a set distance.
Make sure to call update() every time the original circle is moved in order to move the follower as well.
"""
def __init__(self, targetCircle, radius, targetDistance):
super().__init__(targetCircle.getCenter(), radius)
self.targetCircle = targetCircle
self.targetDistance = targetDistance
def getTargetCircle(self):
return self.targetCircle
def getTargetDistance(self):
return self.targetDistance
def update(self):
targetPos = self.targetCircle.getCenter()
myPos = self.getCenter()
diff = targetPos.getX() - myPos.getX(), targetPos.getY() - myPos.getY()
distance = sqrt(diff[0] ** 2 + diff[1] ** 2)
if distance > 0.01:
diffNorm = diff[0] / distance, diff[1] / distance
diffTarget = (
diffNorm[0] * self.targetDistance,
diffNorm[1] * self.targetDistance,
)
else:
diffTarget = 0, -self.targetDistance
self.move(diff[0] - diffTarget[0], diff[1] - diffTarget[1])
class TrailCircle(Circle):
"""
Creates a circle that trails behind another circle.
It will take the other's circle last pos every frame with a moveChance chance.
Make sure to call update() every time the original circle is moved in order to move the trail as well.
"""
def __init__(self, targetCircle, moveChance):
super().__init__(targetCircle.getCenter(), targetCircle.getRadius())
self.targetCircle = targetCircle
self.lastTargetPos = self.targetCircle.getCenter()
self.moveChance = moveChance
def getTargetCircle(self):
return self.targetCircle
def getMoveChance(self):
return self.moveChance
def update(self):
if random() < self.moveChance:
myPos = self.getCenter()
self.move(
self.lastTargetPos.getX() - myPos.getX(),
self.lastTargetPos.getY() - myPos.getY(),
)
self.lastTargetPos = self.targetCircle.getCenter()
def main():
win = GraphWin("Worm", 500, 500, autoflush=False)
# HACK: Make graphics window resizable
win.master.resizable(width=True, height=True)
backgroundColor = [100, 160, 30]
win.setBackground(color_rgb(*backgroundColor))
trails = []
def makeTrails(forCircle):
trail = forCircle
for j in range(2, 0, -1):
trail = TrailCircle(trail, 0.6)
trailColor = color_rgb(
*[((255 - c) * (j) // 5) + c for c in backgroundColor]
)
trail.setFill(trailColor)
trail.setOutline(trailColor)
trails.append(trail)
c = Circle(Point(100, 100), 20)
c.setFill(color_rgb(255, 200, 255))
follower = c
# followers = [(follower := FollowerCircle(follower, i, i * 0.75)) for i in range(20, 0, -1)]
followers = []
for i in range(20, 0, -1):
follower = FollowerCircle(follower, i, i * 0.75)
color = int((i + 20) / 40 * 255)
follower.setFill(color_rgb(color, color * 2 // 3, color))
followers.append(follower)
if i < 15:
makeTrails(follower)
for trail in reversed(trails):
trail.draw(win)
for follower in reversed(followers):
follower.draw(win)
c.draw(win)
framerate = 30 # s^-1
speed = 120 # px/s
update()
mousePos = Point(400, 400)
while win.isOpen():
# HACK: Make graphics window resizable
size = win.master.geometry().split("+")[0].split("x")
win.config(width=size[0], height=size[1])
newMousePos = win.checkMouse()
if newMousePos != None:
mousePos = newMousePos
centerPos = c.getCenter()
diff = mousePos.getX() - centerPos.getX(), mousePos.getY() - centerPos.getY()
distance = sqrt(diff[0] ** 2 + diff[1] ** 2)
if distance > speed / framerate:
diffNorm = diff[0] / distance, diff[1] / distance
c.move(diffNorm[0] * speed / framerate, diffNorm[1] * speed / framerate)
for f in followers:
f.update()
for t in trails:
t.update()
update(framerate)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment