Skip to content

Instantly share code, notes, and snippets.

@f0ursqu4r3
Last active October 26, 2021 17:22
Show Gist options
  • Save f0ursqu4r3/4b3eb21ccaf711e1af086cd40232f6f9 to your computer and use it in GitHub Desktop.
Save f0ursqu4r3/4b3eb21ccaf711e1af086cd40232f6f9 to your computer and use it in GitHub Desktop.
pygame radial menu
import pygame
from pygame import Vector2
class RadialMenu:
def __init__(self, pos, radius=None, items=[], item_size=24, item_padding=0, speed=.05, callback=None, start_open=False):
self.pos = Vector2(pos)
self.radius = radius
self.item_size = item_size
self.item_padding = item_padding
self.items = items
self.surf = None
self.center = Vector2()
self.speed = speed
self.progress = 0
self.state = 'opening' if start_open else 'closed'
self.callback = callback
def process_click_event(self):
if self.selected:
if callable(self.callback):
self.callback(self.selected)
else:
print(self.selected)
self.state = 'closing'
def is_open(self):
return not self.state == 'closed'
def set_pos(self, pos):
self.pos = Vector2(pos)
self.state = 'opening'
def update(self, mpos, dt):
if not self.speed:
self._radius = self.radius
if self.state == 'closed':
return
length = len(self.items)
mouse_pos = mpos-self.pos+self.center
self.selected = None
if length == 0:
return
elif length == 1:
self.surf = pygame.Surface(
Vector2(self.item_size+8),
pygame.SRCALPHA
)
self.center = Vector2(self.surf.get_size())/2
color = (200,200,200,100)
line = 1
if self.center.distance_to(mouse_pos) < self.item_size/2:
color = (0,200,0)
line = 0
pygame.draw.circle(self.surf, color, self.center, self.item_size/2, line)
else:
radius = self._get_radius(length, dt)
self.surf = pygame.Surface(
Vector2((radius+self.item_size)*2),
pygame.SRCALPHA
)
self.center = Vector2(self.surf.get_size())/2
spacing_angle = 360/length
half_spacing_angle = spacing_angle/2
for i, item in enumerate(self.items):
current_angle = spacing_angle*i
item_angle = Vector2(0, -1).rotate(current_angle).normalize()
color = (200,200,200,100)
line = 1
angle_to_mouse = abs((mouse_pos-self.center).angle_to(item_angle))
dist_to_mouse = self.center.distance_to(mouse_pos)
if angle_to_mouse < half_spacing_angle and dist_to_mouse <= radius + self.item_size:
self.selected = item
color = (0,200,0)
line = 0
size = self.item_size/2
if self.speed:
size = self.remap(self.progress, 0, self.speed, 0, size)
pygame.draw.circle(self.surf, color, self.center + (item_angle * radius), size, line)
def _get_radius(self, length, dt):
radius = self.radius or max(self.item_size, ((self.item_size + self.item_padding)/6) * length)
if not self.speed:
return radius
else:
if self.state == 'opening':
if self.progress < self.speed:
self.progress = min(self.speed, self.progress + dt)
else:
self.state = 'open'
elif self.state == 'closing':
if self.progress > 0:
self.progress = max(0, self.progress - dt)
else:
self.state = 'closed'
return self.remap(self.progress, 0, self.speed, 0, radius)
def remap(self, old_val, old_min, old_max, new_min, new_max):
old_range = (old_max - old_min)
new_range = (new_max - new_min)
return (((old_val - old_min) * new_range) / old_range) + new_min
def draw(self, surface):
if self.state == 'closed':
return
surface.blit(self.surf, self.pos-self.center)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment