Skip to content
Snippets Groups Projects
Commit d6c3dd6e authored by Fabian Heinrich's avatar Fabian Heinrich
Browse files

Changed world coordinate frame to unit length * number of counters in the...

Changed world coordinate frame to unit length * number of counters in the layout. World size is determined by kitchen layout
parent 625a3f15
No related branches found
No related tags found
1 merge request!25Resolve "Positions based on grid not pixels"
Pipeline #42506 failed
counter_side_length: 40
world_width: 800
world_height: 600
plates:
clean_plates: 3
dirty_plates: 2
......
_________________
_#QU#TNLB#_______
_#_______M_______
_#_______#_______
_W_______________
_#__A__A_________
_C_______________
_C_______#_______
_#_______X_______
_#P#S+#S+#_______
_________________
#QU#TNLB#
#_______M
#_______#
W________
#__A__A__
C________
C_______#
#_______X
#P#S+#S+#
radius: 0.4
move_dist: 5
move_dist: 0.1
interaction_range: 1.6
\ No newline at end of file
......@@ -82,7 +82,7 @@ class Environment:
with open(env_config_path, "r") as file:
environment_config = yaml.safe_load(file)
self.layout_path: Path = layout_path
self.counter_side_length = environment_config["counter_side_length"]
# self.counter_side_length = 1 # -> this changed! is 1 now
self.item_info_path: Path = item_info_path
self.item_info = self.load_item_info()
......@@ -118,6 +118,9 @@ class Environment:
"+": SinkAddon,
}
self.kitchen_height: int = 0
self.kitchen_width: int = 0
(
self.counters,
self.designated_player_positions,
......@@ -128,9 +131,6 @@ class Environment:
self.score: int = 0
self.world_width: int = environment_config["world_width"]
self.world_height: int = environment_config["world_height"]
def load_item_info(self) -> dict[str, ItemInfo]:
with open(self.item_info_path, "r") as file:
item_lookup = yaml.safe_load(file)
......@@ -156,16 +156,20 @@ class Environment:
Args:
layout_file: Path to the layout file.
"""
current_y: float = self.counter_side_length / 2
current_y: float = 1.5
counters: list[Counter] = []
designated_player_positions: list[npt.NDArray] = []
free_positions: list[npt.NDArray] = []
self.kitchen_width = 0
with open(layout_file, "r") as layout_file:
lines = layout_file.readlines()
self.kitchen_height = len(lines)
for line in lines:
line = line.replace("\n", "").replace(" ", "") # remove newline char
current_x = self.counter_side_length / 2
current_x = 1.5
for character in line:
character = character.capitalize()
pos = np.array([current_x, current_y])
......@@ -180,8 +184,13 @@ class Environment:
)
elif counter_class == "Free":
free_positions.append(np.array([current_x, current_y]))
current_x += self.counter_side_length
current_y += self.counter_side_length
current_x += 1
if current_x > self.kitchen_width:
self.kitchen_width = current_x
current_y += 1
self.kitchen_height = int(self.kitchen_height + 1.5)
self.kitchen_width = int(self.kitchen_width + 0.5)
return counters, designated_player_positions, free_positions
......@@ -331,9 +340,7 @@ class Environment:
other_players = filter(lambda p: p.name != player.name, self.players.values())
def collide(p):
return np.linalg.norm(player.pos - p.pos) <= (
player.radius * self.counter_side_length
) + (p.radius * self.counter_side_length)
return np.linalg.norm(player.pos - p.pos) <= (player.radius) + (p.radius)
return list(filter(collide, other_players))
......@@ -388,16 +395,15 @@ class Environment:
Returns: True if player and counter overlap, False if not.
"""
size = self.counter_side_length
cx, cy = player.pos
dx = max(np.abs(cx - counter.pos[0]) - size / 2, 0)
dy = max(np.abs(cy - counter.pos[1]) - size / 2, 0)
dx = max(np.abs(cx - counter.pos[0]) - 1 / 2, 0)
dy = max(np.abs(cy - counter.pos[1]) - 1 / 2, 0)
distance = np.linalg.norm([dx, dy])
return distance < (player.radius * self.counter_side_length)
return distance < (player.radius)
def add_player(self, player_name: str, pos: npt.NDArray = None):
log.debug(f"Add player {player_name} to the game")
player = Player(player_name, self.counter_side_length, pos)
player = Player(player_name, pos)
self.players[player.name] = player
if player.pos is None:
if len(self.designated_player_positions) > 0:
......@@ -420,12 +426,9 @@ class Environment:
Returns: True if the player touches the world bounds, False if not.
"""
collisions_lower = any(
(player.pos - (player.radius * self.counter_side_length)) < 0
)
collisions_lower = any((player.pos - (player.radius)) < 0)
collisions_upper = any(
(player.pos + (player.radius * self.counter_side_length))
> [self.world_width, self.world_height]
(player.pos + (player.radius)) > [self.kitchen_width, self.kitchen_height]
)
return collisions_lower or collisions_upper
......@@ -467,9 +470,7 @@ class Environment:
case Sink(pos=pos):
assert len(sink_addons) > 0, "No SinkAddon but normal Sink"
closest_addon = self.get_closest(pos, sink_addons)
assert self.counter_side_length - (
self.counter_side_length * 0.05
) <= np.linalg.norm(
assert 1 - (1 * 0.05) <= np.linalg.norm(
closest_addon.pos - pos
), f"No SinkAddon connected to Sink at pos {pos}"
counter.set_addon(closest_addon)
......
......@@ -24,7 +24,6 @@ class Player:
def __init__(
self,
name: str,
grid_size: int,
pos: Optional[npt.NDArray[float]] = None,
):
self.name: str = name
......@@ -42,7 +41,6 @@ class Player:
self.player_config = yaml.safe_load(file)
self.radius: float = self.player_config["radius"]
self.grid_size: int = grid_size
self.move_dist: int = self.player_config["move_dist"]
self.interaction_range: int = self.player_config["interaction_range"]
self.facing_direction: npt.NDArray[float] = np.array([0, 1])
......@@ -85,9 +83,7 @@ class Player:
self.update_facing_point()
def update_facing_point(self):
self.facing_point = self.pos + (
self.facing_direction * self.radius * self.grid_size * 0.5
)
self.facing_point = self.pos + (self.facing_direction * self.radius * 0.5)
def can_reach(self, counter: Counter):
"""Checks whether the player can reach the counter in question. Simple check if the distance is not larger
......@@ -99,9 +95,7 @@ class Player:
Returns: True if the counter is in range of the player, False if not.
"""
return np.linalg.norm(counter.pos - self.facing_point) <= (
self.interaction_range * self.grid_size
)
return np.linalg.norm(counter.pos - self.facing_point) <= self.interaction_range
def pick_action(self, counter: Counter):
"""Performs the pickup-action with the counter. Handles the logic of what the player is currently holding,
......
......@@ -89,10 +89,10 @@ class PyGameGUI:
self.screen = None
self.FPS = 60
self.simulator = simulator
self.counter_size = self.simulator.env.counter_side_length
self.grid_size = 40
self.window_width, self.window_height = (
simulator.env.world_width,
simulator.env.world_height,
simulator.env.kitchen_width * self.grid_size,
simulator.env.kitchen_height * self.grid_size,
)
self.player_names = player_names
......@@ -183,7 +183,7 @@ class PyGameGUI:
def draw_background(self):
"""Visualizes a game background."""
block_size = self.counter_size // 2 # Set the size of the grid block
block_size = self.grid_size // 2 # Set the size of the grid block
for x in range(0, self.window_width, block_size):
for y in range(0, self.window_height, block_size):
rect = pygame.Rect(x, y, block_size, block_size)
......@@ -219,19 +219,20 @@ class PyGameGUI:
state: The game state returned by the environment.
"""
for p_idx, player in enumerate(state["players"].values()):
pos = player.pos * self.grid_size
if USE_PLAYER_COOK_SPRITES:
img_path = self.visualization_config["Cook"]["parts"][0]["path"]
rel_x, rel_y = player.facing_direction
angle = -np.rad2deg(math.atan2(rel_y, rel_x)) + 90
size = (
self.visualization_config["Cook"]["parts"][0]["size"]
* self.counter_size
* self.grid_size
)
self.draw_image(img_path, size, player.pos, angle)
self.draw_image(img_path, size, pos, angle)
else:
pos = player.pos
size = player.radius * self.counter_size
size = player.radius * self.grid_size
color1 = self.player_colors[p_idx]
color2 = colors["white"]
......@@ -239,21 +240,20 @@ class PyGameGUI:
pygame.draw.circle(self.screen, BLUE, pos, size, width=1)
pygame.draw.circle(self.screen, colors[color1], pos, size // 2)
pos = player.pos
facing = player.facing_direction
pygame.draw.polygon(
self.screen,
BLUE,
(
(
pos[0] + (facing[1] * 0.1 * self.counter_size),
pos[1] - (facing[0] * 0.1 * self.counter_size),
pos[0] + (facing[1] * 0.1 * self.grid_size),
pos[1] - (facing[0] * 0.1 * self.grid_size),
),
(
pos[0] - (facing[1] * 0.1 * self.counter_size),
pos[1] + (facing[0] * 0.1 * self.counter_size),
pos[0] - (facing[1] * 0.1 * self.grid_size),
pos[1] + (facing[0] * 0.1 * self.grid_size),
),
player.pos + (facing * 0.5 * self.counter_size),
pos + (facing * 0.5 * self.grid_size),
),
)
......@@ -261,11 +261,13 @@ class PyGameGUI:
pygame.draw.circle(
self.screen,
BLUE,
player.facing_point,
player.interaction_range * self.counter_size,
player.facing_point * self.grid_size,
player.interaction_range * self.grid_size,
width=1,
)
pygame.draw.circle(self.screen, colors["red1"], player.facing_point, 4)
pygame.draw.circle(
self.screen, colors["red1"], player.facing_point * self.grid_size, 4
)
if player.holding is not None:
holding_item_pos = player.pos + (20 * player.facing_direction)
......@@ -273,14 +275,15 @@ class PyGameGUI:
if player.current_nearest_counter:
counter: Counter = player.current_nearest_counter
pos = counter.pos * self.grid_size
pygame.draw.rect(
self.screen,
colors[self.player_colors[p_idx]],
rect=pygame.Rect(
counter.pos[0] - (self.counter_size // 2),
counter.pos[1] - (self.counter_size // 2),
self.counter_size,
self.counter_size,
pos[0] - (self.grid_size // 2),
pos[1] - (self.grid_size // 2),
self.grid_size,
self.grid_size,
),
width=2,
)
......@@ -295,40 +298,44 @@ class PyGameGUI:
parts: The visual parts to draw.
scale: Rescale the item by this factor.
"""
for part in parts:
part_type = part["type"]
if part_type == "image":
self.draw_image(
parts[0]["path"], parts[0]["size"] * scale * self.counter_size, pos
)
elif part_type == "rect":
height = part["height"] * self.counter_size
width = part["width"] * self.counter_size
color = part["color"]
if "center_offset" in part:
dx, dy = np.array(part["center_offset"]) * self.counter_size
rect = pygame.Rect(pos[0] + dx, pos[1] + dy, height, width)
pygame.draw.rect(self.screen, color, rect)
else:
rect = pygame.Rect(
pos[0] - (height / 2),
pos[1] - (width / 2),
height,
width,
match part_type:
case "image":
self.draw_image(
parts[0]["path"],
parts[0]["size"] * scale * self.grid_size,
pos,
)
pygame.draw.rect(self.screen, color, rect)
elif part_type == "circle":
radius = part["radius"] * self.counter_size
color = colors[part["color"]]
if "center_offset" in part:
pygame.draw.circle(
self.screen,
color,
pos + np.array(part["center_offset"]),
radius,
)
else:
pygame.draw.circle(self.screen, color, pos, radius)
case "rect":
height = part["height"] * self.grid_size
width = part["width"] * self.grid_size
color = part["color"]
if "center_offset" in part:
dx, dy = np.array(part["center_offset"]) * self.grid_size
rect = pygame.Rect(pos[0] + dx, pos[1] + dy, height, width)
pygame.draw.rect(self.screen, color, rect)
else:
rect = pygame.Rect(
pos[0] - (height / 2),
pos[1] - (width / 2),
height,
width,
)
pygame.draw.rect(self.screen, color, rect)
case "circle":
radius = part["radius"] * self.grid_size
color = colors[part["color"]]
if "center_offset" in part:
pygame.draw.circle(
self.screen,
color,
(pos * self.grid_size) + np.array(part["center_offset"]),
radius,
)
else:
pygame.draw.circle(self.screen, color, pos, radius)
def draw_item(self, pos: npt.NDArray[float], item: Item, scale: float = 1.0):
"""Visualization of an item at the specified position. On a counter or in the hands of the player.
......@@ -365,11 +372,12 @@ class PyGameGUI:
def draw_progress_bar(self, pos, current, needed):
"""Visualize progress of progressing item as a green bar under the item."""
if current != 0:
bar_height = self.counter_size * 0.2
progress_width = (current / needed) * self.counter_size
pos = pos * self.grid_size
bar_height = self.grid_size * 0.2
progress_width = (current / needed) * self.grid_size
progress_bar = pygame.Rect(
pos[0] - (self.counter_size / 2),
pos[1] - (self.counter_size / 2) + self.counter_size - bar_height,
pos[0] - (self.grid_size / 2),
pos[1] - (self.grid_size / 2) + self.grid_size - bar_height,
progress_width,
bar_height,
)
......@@ -384,14 +392,14 @@ class PyGameGUI:
counter: The counter to visualize.
"""
self.draw_thing(counter.pos, self.visualization_config["Counter"]["parts"])
pos = counter.pos * self.grid_size
self.draw_thing(pos, self.visualization_config["Counter"]["parts"])
if str(counter) in self.visualization_config:
self.draw_thing(
counter.pos, self.visualization_config[str(counter)]["parts"]
)
self.draw_thing(pos, self.visualization_config[str(counter)]["parts"])
else:
self.draw_thing(
counter.pos,
pos,
self.visualization_config[counter.__class__.__name__]["parts"],
)
......@@ -400,12 +408,10 @@ class PyGameGUI:
if isinstance(counter.occupied_by, (list, deque)):
with self.simulator.env.lock:
for i, o in enumerate(counter.occupied_by):
self.draw_item(
np.abs([counter.pos[0], counter.pos[1] - (i * 3)]), o
)
self.draw_item(np.abs([pos[0], pos[1] - (i * 3)]), o)
# All other items:
else:
self.draw_item(counter.pos, counter.occupied_by)
self.draw_item(pos, counter.occupied_by)
def draw_counters(self, state):
"""Visualizes the counters in the environment.
......@@ -416,7 +422,9 @@ class PyGameGUI:
for counter in state["counters"]:
self.draw_counter(counter)
if SHOW_COUNTER_CENTERS:
pygame.draw.circle(self.screen, colors["green1"], counter.pos, 3)
pygame.draw.circle(
self.screen, colors["green1"], counter.pos * self.grid_size, 3
)
def draw(self, state):
"""Main visualization function.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment