To implement a **context menu on right-click for agents** in your Pygame simulation, you can follow these steps:
---
## ✅ Overview
- Detect **right-click** on an agent.
- Display a **context menu** with options like:
- "Inspect"
- "Move Agent"
- "Assign Task"
- "Reset Energy"
- Handle **menu item selection** via mouse clicks.
This is a **custom context menu**, as Pygame does not have built-in GUI components like Tkinter or PyQt.
---
## ✅ Features in This Implementation
- Right-click on an agent → show a small menu near the mouse.
- Click on a menu item → perform an action.
- Menu disappears after selection or click elsewhere.
---
## 🧱 Full Pygame Code with Context Menu
```python
import pygame
import random
import math
# Pygame initialization
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mini Agent Simulation - Right-click Menu")
font = pygame.font.SysFont("Arial", 14)
menu_font = pygame.font.SysFont("Arial", 16)
info_font = pygame.font.SysFont("Arial", 16, bold=True)
clock = pygame.time.Clock()
# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (100, 100, 255)
GREEN = (100, 255, 100)
RED = (255, 100, 100)
HIGHLIGHT = (255, 200, 0)
MENU_BG = (240, 240, 240)
MENU_BORDER = (100, 100, 100)
selected_agent = None
show_menu = False
menu_pos = (0, 0)
class Agent:
def __init__(self, name, world, pos):
self.name = name
self.age = random.randint(18, 60)
self.job = random.choice(["Farmer", "Builder", "Teacher", "Doctor"])
self.home = None
self.location = "Town Center"
self.relationships = {}
self.energy = 100
self.world = world
self.pos = pos # (x, y)
self.color = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
def work(self):
self.energy -= 10
def rest(self):
self.energy += 20
def socialize(self):
other_agent = random.choice([a for a in self.world.agents if a != self and a.energy > 0])
self.relationships[other_agent.name] = self.relationships.get(other_agent.name, 0) + 1
def build(self, structure_type):
self.world.add_structure(structure_type, self.name, self.pos)
self.energy -= 15
def decide_action(self):
if self.energy < 30:
self.rest()
elif random.random() < 0.3:
self.socialize()
elif random.random() < 0.2:
self.build(random.choice(["House", "School", "Clinic"]))
else:
self.work()
def draw(self, screen):
pygame.draw.circle(screen, self.color, self.pos, 15)
name_text = font.render(self.name, True, BLACK)
screen.blit(name_text, (self.pos[0] - 20, self.pos[1] - 30))
energy_text = font.render(f"{self.energy}", True, RED)
screen.blit(energy_text, (self.pos[0] - 5, self.pos[1] + 20))
class World:
def __init__(self):
self.agents = []
self.structures = []
def add_agent(self, agent):
self.agents.append(agent)
def add_structure(self, structure_type, built_by, pos):
self.structures.append({
"type": structure_type,
"built_by": built_by,
"pos": (pos[0], pos[1] + 50)
})
def draw(self, screen, font):
for s in self.structures:
rect = pygame.Rect(s["pos"][0], s["pos"][1], 40, 40)
pygame.draw.rect(screen, GREEN, rect)
label = font.render(s["type"], True, BLACK)
screen.blit(label, (s["pos"][0] - 10, s["pos"][1] - 20))
# Initialize World
world = World()
# Create Agents
names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
positions = [(100, 100), (200, 150), (300, 100), (400, 200), (500, 150), (600, 100)]
agents = [Agent(name, world, pos) for name, pos in zip(names, positions)]
for a in agents:
world.add_agent(a)
# Menu options
menu_items = ["Inspect", "Reset Energy", "Assign Task"]
# Simulation Loop
running = True
days = 30
day = 0
def draw_context_menu(mouse_pos):
menu_width = 150
menu_height = len(menu_items) * 30
menu_rect = pygame.Rect(mouse_pos[0], mouse_pos[1], menu_width, menu_height)
pygame.draw.rect(screen, MENU_BG, menu_rect)
pygame.draw.rect(screen, MENU_BORDER, menu_rect, 2)
for i, item in enumerate(menu_items):
item_rect = pygame.Rect(mouse_pos[0], mouse_pos[1] + i * 30, menu_width, 30)
text = menu_font.render(item, True, BLACK)
screen.blit(text, (item_rect.x + 10, item_rect.y + 5))
return menu_rect
while running and day < days:
screen.fill(WHITE)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
# Left-click: select agent
if event.button == 1:
clicked_agent = None
for agent in agents:
distance = math.hypot(agent.pos[0] - mouse_pos[0], agent.pos[1] - mouse_pos[1])
if distance < 20:
clicked_agent = agent
break
global selected_agent
selected_agent = clicked_agent
show_menu = False
# Right-click: show context menu
elif event.button == 3:
clicked_agent = None
for agent in agents:
distance = math.hypot(agent.pos[0] - mouse_pos[0], agent.pos[1] - mouse_pos[1])
if distance < 20:
clicked_agent = agent
break
if clicked_agent:
selected_agent = clicked_agent
menu_pos = mouse_pos
show_menu = True
elif event.type == pygame.MOUSEBUTTONUP and show_menu:
if event.button == 1:
# Check if clicked on a menu item
menu_rect = pygame.Rect(menu_pos[0], menu_pos[1], 150, len(menu_items) * 30)
if menu_rect.collidepoint(event.pos):
index = (event.pos[1] - menu_pos[1]) // 30
if 0 <= index < len(menu_items):
action = menu_items[index]
if action == "Reset Energy":
selected_agent.energy = 100
elif action == "Assign Task":
selected_agent.build("House")
elif action == "Inspect":
print(f"Inspecting {selected_agent.name}")
show_menu = False
# Simulate daily actions
for agent in agents:
agent.decide_action()
if agent == selected_agent:
pygame.draw.circle(screen, HIGHLIGHT, agent.pos, 20, 3)
agent.draw(screen)
world.draw(screen, font)
# Draw selected agent info
if selected_agent:
info_y = 10
screen.blit(info_font.render("Selected Agent:", True, BLACK), (10, info_y))
info_y += 20
screen.blit(font.render(f"Name: {selected_agent.name}", True, BLACK), (10, info_y))
info_y += 20
screen.blit(font.render(f"Job: {selected_agent.job}", True, BLACK), (10, info_y))
info_y += 20
screen.blit(font.render(f"Age: {selected_agent.age}", True, BLACK), (10, info_y))
info_y += 20
screen.blit(font.render(f"Energy: {selected_agent.energy}", True, RED), (10, info_y))
# Draw context menu
if show_menu:
draw_context_menu(menu_pos)
pygame.display.flip()
clock.tick(1) # 1 frame per second
day += 1
pygame.quit()
```
---
## 🔍 Code Explanation
- **Mouse Button Detection**:
- Left-click: selects an agent or deselects.
- Right-click: shows a context menu if the click is on an agent.
- **Menu Drawing**:
- `draw_context_menu()` draws a small rectangle with clickable menu items.
- **Menu Interaction**:
- On mouse-up, checks if the click is within the menu.
- Executes the corresponding action:
- Reset energy.
- Assign task (e.g., build a house).
- Inspect (prints to console).
---
## ✅ Possible Improvements
- Add icons or images to menu items.
- Support keyboard navigation.
- Add a "Cancel" or "Close" option.
- Make the menu follow the mouse if it goes off-screen.
---
###