gui.py 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import cocos
  2. from cocos.actions import ScaleBy, Repeat, Reverse, RotateBy
  3. from cocos.director import director
  4. from cocos.layer import ScrollableLayer, Layer
  5. from cocos.sprite import Sprite
  6. from pyglet.window import key as wkey
  7. from random import randint
  8. from sandbox.life_game.simulation import CellDieEvent, Cell, InvertCellStateBehaviour, \
  9. EmptyPositionWithLotOfCellAroundEvent
  10. from sandbox.life_game.simulation import CellBornEvent
  11. from synergine2.gui import Gui
  12. from synergine2.terminals import TerminalPackage
  13. from synergine2.terminals import Terminal
  14. cell_scale = ScaleBy(1.1, duration=0.25)
  15. cell_rotate = RotateBy(360, duration=30)
  16. flash_flash = ScaleBy(8, duration=0.5)
  17. flash_rotate = RotateBy(360, duration=6)
  18. class GridManager(object):
  19. def __init__(
  20. self,
  21. layer: Layer,
  22. square_width: int,
  23. border: int=0,
  24. ):
  25. self.layer = layer
  26. self.square_width = square_width
  27. self.border = border
  28. @property
  29. def final_width(self):
  30. return self.square_width + self.border
  31. def scale_sprite(self, sprite: Sprite):
  32. sprite.scale_x = self.final_width / sprite.image.width
  33. sprite.scale_y = self.final_width / sprite.image.height
  34. def position_sprite(self, sprite: Sprite, grid_position):
  35. grid_x = grid_position[0]
  36. grid_y = grid_position[1]
  37. sprite.position = grid_x * self.final_width, grid_y * self.final_width
  38. def get_window_position(self, grid_position_x, grid_position_y):
  39. grid_x = grid_position_x
  40. grid_y = grid_position_y
  41. return grid_x * self.final_width, grid_y * self.final_width
  42. def get_grid_position(self, window_x, window_y, z=0) -> tuple:
  43. window_size = director.get_window_size()
  44. window_center_x = window_size[0] // 2
  45. window_center_y = window_size[1] // 2
  46. window_relative_x = window_x - window_center_x
  47. window_relative_y = window_y - window_center_y
  48. real_width = self.final_width * self.layer.scale
  49. return int(window_relative_x // real_width),\
  50. int(window_relative_y // real_width),\
  51. z
  52. class Cells(Layer):
  53. def __init__(self):
  54. super().__init__()
  55. self.cells = {}
  56. self.flashs = []
  57. self.grid_manager = GridManager(self, 32, border=2)
  58. def born(self, grid_position):
  59. if grid_position in self.cells:
  60. return # cell can be added by gui
  61. cell = Sprite('resources/cells_l.png')
  62. cell.rotation = randint(0, 360)
  63. self.grid_manager.scale_sprite(cell)
  64. self.grid_manager.position_sprite(cell, grid_position)
  65. cell.do(Repeat(cell_scale + Reverse(cell_scale)))
  66. cell.do(Repeat(cell_rotate + Reverse(cell_rotate)))
  67. self.cells[grid_position] = cell
  68. self.add(cell)
  69. def die(self, grid_position):
  70. try:
  71. self.remove(self.cells[grid_position])
  72. del self.cells[grid_position]
  73. except KeyError:
  74. pass # Cell can be removed by gui
  75. def flash(self, position):
  76. flash = Sprite('resources/flash.png')
  77. flash.opacity = 40
  78. flash.scale = 0.1
  79. flash.rotation = randint(0, 360)
  80. flash.do(flash_flash + Reverse(flash_flash))
  81. flash.do(Repeat(flash_rotate + Reverse(flash_rotate)))
  82. self.grid_manager.position_sprite(flash, position)
  83. self.flashs.append(flash)
  84. self.add(flash)
  85. class MainLayer(ScrollableLayer):
  86. is_event_handler = True
  87. def __init__(self, terminal: Terminal):
  88. super().__init__()
  89. self.terminal = terminal
  90. self.scroll_step = 100
  91. self.grid_manager = GridManager(self, 32, border=2)
  92. self.background = Sprite('resources/banner-1711735_640.jpg')
  93. self.background.position = 0, 0
  94. self.background.opacity = 70
  95. self.background.scale = 5
  96. self.add(self.background, z=1)
  97. self.cells = Cells()
  98. self.add(self.cells)
  99. self.cross = Sprite('resources/cross31x31.png')
  100. self.cross.position = 0, 0
  101. self.cross.opacity = 50
  102. self.add(self.cross)
  103. # Set scene center on center of screen
  104. window_size = director.get_window_size()
  105. self.position = window_size[0] // 2, window_size[1] // 2
  106. def on_key_press(self, key, modifiers):
  107. if key == wkey.LEFT:
  108. self.position = (self.position[0] + self.scroll_step, self.position[1])
  109. if key == wkey.RIGHT:
  110. self.position = (self.position[0] - self.scroll_step, self.position[1])
  111. if key == wkey.UP:
  112. self.position = (self.position[0], self.position[1] - self.scroll_step)
  113. if key == wkey.DOWN:
  114. self.position = (self.position[0], self.position[1] + self.scroll_step)
  115. if key == wkey.A:
  116. if self.scale >= 0.3:
  117. self.scale -= 0.2
  118. if key == wkey.Z:
  119. if self.scale <= 4:
  120. self.scale += 0.2
  121. def on_mouse_press(self, x, y, buttons, modifiers):
  122. x, y = director.get_virtual_coordinates(x, y)
  123. grid_position = self.grid_manager.get_grid_position(x, y)
  124. self.terminal.send(TerminalPackage(
  125. simulation_actions=[(InvertCellStateBehaviour, {'position': grid_position})],
  126. ))
  127. def on_mouse_motion(self, x, y, dx, dy):
  128. x, y = director.get_virtual_coordinates(x, y)
  129. grid_position = self.grid_manager.get_grid_position(x, y)
  130. window_position = self.grid_manager.get_window_position(grid_position[0], grid_position[1])
  131. self.cross.position = window_position
  132. class LifeGameGui(Gui):
  133. def __init__(
  134. self,
  135. terminal: Terminal,
  136. read_queue_interval: float=1 / 60.0,
  137. ):
  138. super().__init__(terminal, read_queue_interval)
  139. self.main_layer = MainLayer(terminal=self.terminal)
  140. self.main_scene = cocos.scene.Scene(self.main_layer)
  141. self.positions = {}
  142. self.terminal.register_event_handler(CellDieEvent, self.on_cell_die)
  143. self.terminal.register_event_handler(CellBornEvent, self.on_cell_born)
  144. self.terminal.register_event_handler(
  145. EmptyPositionWithLotOfCellAroundEvent,
  146. self.on_empty_cell_with_lot_of_cell_around,
  147. )
  148. def get_main_scene(self):
  149. return self.main_scene
  150. def before_received(self, package: TerminalPackage):
  151. if package.subjects: # It's thirst package
  152. for subject in package.subjects:
  153. if isinstance(subject, Cell):
  154. self.positions[subject.id] = subject.position
  155. self.main_layer.cells.born(subject.position)
  156. for flash in self.main_layer.cells.flashs[:]:
  157. self.main_layer.cells.flashs.remove(flash)
  158. self.main_layer.cells.remove(flash)
  159. def on_cell_die(self, event: CellDieEvent):
  160. try:
  161. self.main_layer.cells.die(self.positions[event.subject_id])
  162. except KeyError:
  163. pass
  164. def on_cell_born(self, event: CellBornEvent):
  165. if event.subject_id not in self.terminal.subjects:
  166. return
  167. subject = self.terminal.subjects.get(event.subject_id)
  168. self.positions[event.subject_id] = subject.position
  169. self.main_layer.cells.born(subject.position)
  170. def on_empty_cell_with_lot_of_cell_around(self, event: EmptyPositionWithLotOfCellAroundEvent):
  171. self.main_layer.cells.flash(event.position)