Much better performance for the pygame viewport.

This commit is contained in:
Khopa 2018-10-30 20:00:14 +01:00
parent 7d70862e72
commit e10b853712
2 changed files with 96 additions and 154 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 247 KiB

View File

@ -21,23 +21,26 @@ class OverviewCanvas:
BLUE = (164, 164, 255) BLUE = (164, 164, 255)
WHITE = (255, 255, 255) WHITE = (255, 255, 255)
BLACK = (0, 0, 0) BLACK = (0, 0, 0)
BACKGROUND = pygame.Color(0, 64, 64)
def __init__(self, frame: Frame, parent, game: Game): def __init__(self, frame: Frame, parent, game: Game):
self.parent = parent self.parent = parent
self.game = game self.game = game
self.screen = None
# Pygame objects # Pygame objects
self.map = None self.map = None
self.screen = None
self.surface: pygame.Surface = None self.surface: pygame.Surface = None
self.thread: Thread = None self.thread: Thread = None
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
pygame.font.init() pygame.font.init()
self.font = pygame.font.SysFont("arial", 15) self.font = pygame.font.SysFont("arial", 15)
self.fontsmall = pygame.font.SysFont("arial", 10) self.fontsmall = pygame.font.SysFont("arial", 10)
self.rects = [] # Frontline are too heavy on performance to compute in realtime, so keep them in a cache
self.frontline_vector_cache = {}
# Map state # Map state
self.zoom = 1 self.zoom = 1
@ -50,14 +53,13 @@ class OverviewCanvas:
self.display_bases = BooleanVar(value=True) self.display_bases = BooleanVar(value=True)
self.display_road = BooleanVar(value=True) self.display_road = BooleanVar(value=True)
# TODO : dirty :(
parent.window.tk.protocol("<WM_DELETE_WINDOW>", self.on_close) parent.window.tk.protocol("<WM_DELETE_WINDOW>", self.on_close)
self.wrapper = Frame(frame, **STYLES["frame-wrapper"]) self.wrapper = Frame(frame, **STYLES["frame-wrapper"])
self.wrapper.grid(column=0, row=0, sticky=NSEW) # Adds grid self.wrapper.grid(column=0, row=0, sticky=NSEW) # Adds grid
self.wrapper.pack(side=LEFT) # packs window to the left self.wrapper.pack(side=LEFT) # packs window to the left
self.embed = Frame(self.wrapper, width=1200, height=600) # creates embed frame for pygame window self.embed = Frame(self.wrapper, width=1066, height=600) # creates embed frame for pygame window
self.embed.grid(column=0, row=0, sticky=NSEW) # Adds grid self.embed.grid(column=0, row=0, sticky=NSEW) # Adds grid
self.options = Frame(self.wrapper, **STYLES["frame-wrapper"]) self.options = Frame(self.wrapper, **STYLES["frame-wrapper"])
@ -70,19 +72,24 @@ class OverviewCanvas:
def build_map_options_panel(self): def build_map_options_panel(self):
col = 0 col = 0
Label(self.options, text="Ground targets", **STYLES["widget"]).grid(row=0, column=col, sticky=W) Label(self.options, text="Ground targets", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_ground_targets, **STYLES["radiobutton"]).grid(row=0, column=col + 1, sticky=E) Checkbutton(self.options, variable=self.display_ground_targets, **STYLES["radiobutton"]).grid(row=0,
column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS) Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
col += 3 col += 3
Label(self.options, text="Forces", **STYLES["widget"]).grid(row=0, column=col, sticky=W) Label(self.options, text="Forces", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_forces, **STYLES["radiobutton"]).grid(row=0, column=col + 1, sticky=E) Checkbutton(self.options, variable=self.display_forces, **STYLES["radiobutton"]).grid(row=0, column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS) Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
col += 3 col += 3
Label(self.options, text="Bases", **STYLES["widget"]).grid(row=0, column=col, sticky=W) Label(self.options, text="Bases", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_bases, **STYLES["radiobutton"]).grid(row=0, column=col + 1, sticky=E) Checkbutton(self.options, variable=self.display_bases, **STYLES["radiobutton"]).grid(row=0, column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS) Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
col += 4 col += 4
Label(self.options, text="Roads", **STYLES["widget"]).grid(row=0, column=col, sticky=W) Label(self.options, text="Roads", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_road, **STYLES["radiobutton"]).grid(row=0, column=col + 1, sticky=E) Checkbutton(self.options, variable=self.display_road, **STYLES["radiobutton"]).grid(row=0, column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS) Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
def on_close(self): def on_close(self):
@ -96,7 +103,7 @@ class OverviewCanvas:
if platform.system == "Windows": if platform.system == "Windows":
os.environ['SDL_VIDEODRIVER'] = 'windib' os.environ['SDL_VIDEODRIVER'] = 'windib'
self.screen = pygame.display.set_mode((1200, 600), pygame.DOUBLEBUF | pygame.HWSURFACE) self.screen = pygame.display.set_mode((1066, 600), pygame.DOUBLEBUF | pygame.HWSURFACE)
self.screen.fill(pygame.Color(0, 128, 128)) self.screen.fill(pygame.Color(0, 128, 128))
self.screen.set_alpha(None) self.screen.set_alpha(None)
@ -105,31 +112,36 @@ class OverviewCanvas:
self.icon_clr = pygame.image.load(os.path.join("resources", "ui", "cleared.png")) self.icon_clr = pygame.image.load(os.path.join("resources", "ui", "cleared.png"))
self.map = pygame.image.load(os.path.join("resources", self.game.theater.overview_image)).convert() self.map = pygame.image.load(os.path.join("resources", self.game.theater.overview_image)).convert()
pygame.draw.rect(self.map, self.BLACK, (0, 0, self.map.get_width(), self.map.get_height()), 10)
pygame.draw.rect(self.map, self.WHITE, (0, 0, self.map.get_width(), self.map.get_height()), 5)
self.surface = pygame.Surface((self.map.get_width(), self.map.get_height())) self.surface = pygame.Surface((self.map.get_width(), self.map.get_height()))
pygame.display.init() pygame.display.init()
pygame.display.update() pygame.display.update()
def init_sdl_thread(self): def init_sdl_thread(self):
# tk = Tk()
# tk.winfo_ismapped()
self.thread = Thread(target=self.sdl_thread) self.thread = Thread(target=self.sdl_thread)
self.thread.start() self.thread.start()
def sdl_thread(self): def sdl_thread(self):
self.redraw_required = True self.redraw_required = True
bg = pygame.Color(0, 64, 64) i = 0
while not self.exited: while not self.exited:
self.clock.tick(60)
self.draw()
i += 1
if i == 300:
self.frontline_vector_cache = {}
i = 300
def draw(self):
try: try:
self.parent.window.tk.winfo_ismapped() self.parent.window.tk.winfo_ismapped()
except: except:
self.exited = True self.exited = True
self.clock.tick(60)
right_down = False right_down = False
left_down = False left_down = False
@ -166,7 +178,7 @@ class OverviewCanvas:
if (self.redraw_required): if (self.redraw_required):
# Fill # Fill
self.screen.fill(bg) self.screen.fill(self.BACKGROUND)
# Surface # Surface
cursor_pos = pygame.mouse.get_pos() cursor_pos = pygame.mouse.get_pos()
@ -175,8 +187,9 @@ class OverviewCanvas:
self.draw_map(self.surface, cursor_pos, (left_down, right_down)); self.draw_map(self.surface, cursor_pos, (left_down, right_down));
# Scaling # Scaling
# scaled = pygame.transform.scale(self.surface, (int(self.surface.get_width() * self.zoom), int(self.surface.get_height() * self.zoom))) scaled = pygame.transform.scale(self.surface, (
self.screen.blit(self.surface, self.scroll) int(self.surface.get_width() * self.zoom), int(self.surface.get_height() * self.zoom)))
self.screen.blit(scaled, self.scroll)
pygame.display.flip() pygame.display.flip()
@ -210,29 +223,38 @@ class OverviewCanvas:
color = self.BLACK color = self.BLACK
pygame.draw.line(surface, color, coords, connected_coords, 4) pygame.draw.line(surface, color, coords, connected_coords, 4)
# self.canvas.create_line((coords[0], coords[1], connected_coords[0], connected_coords[1]), width=2, fill=color)
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp): if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
# Cache mechanism to avoid performing frontline vector computation on every loop
frontline = None
hash = str(cp.id) + "_" + str(connected_cp.id)
if hash in self.frontline_vector_cache:
frontline = self.frontline_vector_cache[hash]
else:
frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater) frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
self.frontline_vector_cache[hash] = frontline
if not frontline: if not frontline:
continue continue
frontline_pos, heading, distance = frontline frontline_pos, heading, distance = frontline
if distance < 10000: if distance < 10000:
frontline_pos = frontline_pos.point_from_heading(heading + 180, 5000) frontline_pos = frontline_pos.point_from_heading(heading + 180, 5000)
distance = 10000 distance = 10000
start_coords = self.transform_point(frontline_pos, treshold=10) start_coords = self.transform_point(frontline_pos, treshold=10)
end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60) end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance),
treshold=60)
# self.canvas.create_line((*start_coords, *end_coords), width=2, fill=color) pygame.draw.line(surface, color, start_coords, end_coords, 1)
pygame.draw.line(surface, color, start_coords, end_coords, 4)
if self.display_bases.get(): if self.display_bases.get():
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
coords = self.transform_point(cp.position) coords = self.transform_point(cp.position)
radius = 12 * math.pow(cp.importance, 1) radius = 12 * math.pow(cp.importance, 1)
radius_m = radius * cp.base.strength - 2; radius_m = radius * cp.base.strength - 2
if cp.captured: if cp.captured:
color = self._player_color() color = self._player_color()
@ -277,6 +299,10 @@ class OverviewCanvas:
if rect.collidepoint(mouse_pos): if rect.collidepoint(mouse_pos):
self.draw_ground_object_info(ground_object, (x, y), color, surface); self.draw_ground_object_info(ground_object, (x, y), color, surface);
def render_roads(self):
# Note : This is really slow. Viewport is much smoother when not rendering line.
pass
def draw_ground_object_info(self, ground_object: TheaterGroundObject, pos, color, surface: pygame.Surface): def draw_ground_object_info(self, ground_object: TheaterGroundObject, pos, color, surface: pygame.Surface):
lb = self.font.render("Type : " + ground_object.name_abbrev, False, color, self.BLACK); lb = self.font.render("Type : " + ground_object.name_abbrev, False, color, self.BLACK);
surface.blit(lb, (pos[0] + 18, pos[1])) surface.blit(lb, (pos[0] + 18, pos[1]))
@ -313,91 +339,7 @@ class OverviewCanvas:
return self.game.player == "USA" and self.RED or self.BLUE return self.game.player == "USA" and self.RED or self.BLUE
def update(self): def update(self):
self.draw()
self.screen.blit(self.map, (0, 0))
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 4:
self.zoom += 1
elif event.button == 5:
self.zoom -= 1
pygame.display.update()
"""
self.canvas.delete(ALL)
self.canvas.create_image((self.image.width()/2, self.image.height()/2), image=self.image)
for cp in self.game.theater.controlpoints:
for ground_object in cp.ground_objects:
x, y = self.transform_point(ground_object.position)
self.canvas.create_text(x,
y,
text=".",
fill="black" if ground_object.is_dead else self._enemy_color(),
font=("Helvetica", 18))
coords = self.transform_point(cp.position)
for connected_cp in cp.connected_points:
connected_coords = self.transform_point(connected_cp.position)
if connected_cp.captured != cp.captured:
color = self._enemy_color()
elif connected_cp.captured and cp.captured:
color = self._player_color()
else:
color = "black"
self.canvas.create_line((coords[0], coords[1], connected_coords[0], connected_coords[1]), width=2, fill=color)
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
if not frontline:
continue
frontline_pos, heading, distance = frontline
if distance < 10000:
frontline_pos = frontline_pos.point_from_heading(heading + 180, 5000)
distance = 10000
start_coords = self.transform_point(frontline_pos, treshold=10)
end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60)
self.canvas.create_line((*start_coords, *end_coords), width=2, fill=color)
for cp in self.game.theater.controlpoints:
coords = self.transform_point(cp.position)
arc_size = 16 * math.pow(cp.importance, 1)
extent = max(cp.base.strength * 180, 10)
start = (180 - extent) / 2
if cp.captured:
color = self._player_color()
else:
color = self._enemy_color()
cp_id = self.canvas.create_arc((coords[0] - arc_size/2, coords[1] - arc_size/2),
(coords[0] + arc_size/2, coords[1] + arc_size/2),
fill=color,
style=PIESLICE,
start=start,
extent=extent)
"""
"""
for r in cp.radials:
p = self.transform_point(cp.position.point_from_heading(r, 20000))
self.canvas.create_text(p[0], p[1], text="{}".format(r))
continue
"""
"""
self.canvas.tag_bind(cp_id, "<Button-1>", self.display(cp))
self.create_cp_title((coords[0] + arc_size/4, coords[1] + arc_size/4), cp)
units_title = "{}/{}/{}".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa)
self.canvas.create_text(coords[0]+1, coords[1] - arc_size / 1.5 +1, text=units_title, font=("Helvetica", 8), fill=color)
self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text=units_title, font=("Helvetica", 8), fill="white")
"""
def display(self, cp: ControlPoint): def display(self, cp: ControlPoint):
def action(_): def action(_):