gui.py 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. # coding: utf-8
  2. import weakref
  3. import pyglet
  4. from pyglet.window import mouse
  5. import cocos
  6. from cocos import collision_model
  7. from cocos import euclid
  8. from cocos.director import director
  9. from cocos.layer import Layer
  10. from cocos.layer import ScrollableLayer
  11. from cocos.sprite import Sprite
  12. from synergine2.config import Config
  13. from synergine2.log import SynergineLogger
  14. from synergine2.terminals import Terminal
  15. from synergine2.terminals import TerminalPackage
  16. from synergine2_cocos2d.actor import Actor
  17. from synergine2_cocos2d.layer import LayerManager
  18. from synergine2_cocos2d.middleware import TMXMiddleware
  19. class GridManager(object):
  20. def __init__(
  21. self,
  22. layer: Layer,
  23. square_width: int,
  24. border: int=0,
  25. ):
  26. self.layer = layer
  27. self.square_width = square_width
  28. self.border = border
  29. @property
  30. def final_width(self):
  31. return self.square_width + self.border
  32. def scale_sprite(self, sprite: Sprite):
  33. sprite.scale_x = self.final_width / sprite.image.width
  34. sprite.scale_y = self.final_width / sprite.image.height
  35. def position_sprite(self, sprite: Sprite, grid_position):
  36. grid_x = grid_position[0]
  37. grid_y = grid_position[1]
  38. sprite.position = grid_x * self.final_width, grid_y * self.final_width
  39. def get_window_position(self, grid_position_x, grid_position_y):
  40. grid_x = grid_position_x
  41. grid_y = grid_position_y
  42. return grid_x * self.final_width, grid_y * self.final_width
  43. def get_grid_position(self, window_x, window_y, z=0) -> tuple:
  44. window_size = director.get_window_size()
  45. window_center_x = window_size[0] // 2
  46. window_center_y = window_size[1] // 2
  47. window_relative_x = window_x - window_center_x
  48. window_relative_y = window_y - window_center_y
  49. real_width = self.final_width * self.layer.scale
  50. return int(window_relative_x // real_width),\
  51. int(window_relative_y // real_width),\
  52. z
  53. class GridLayerMixin(object):
  54. def __init__(self, *args, **kwargs):
  55. square_width = kwargs.pop('square_width', 32)
  56. square_border = kwargs.pop('square_border', 2)
  57. self.grid_manager = GridManager(
  58. self,
  59. square_width=square_width,
  60. border=square_border,
  61. )
  62. super().__init__(*args, **kwargs)
  63. class MinMaxRect(cocos.cocosnode.CocosNode):
  64. def __init__(self, layer_manager: LayerManager):
  65. super(MinMaxRect, self).__init__()
  66. self.layer_manager = layer_manager
  67. self.color3 = (20, 20, 20)
  68. self.vertexes = [(0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]
  69. self.visible = False
  70. def adjust_from_w_minmax(self, wminx, wmaxx, wminy, wmaxy):
  71. # asumes world to screen preserves order
  72. sminx, sminy = self.layer_manager.scrolling_manager.world_to_screen(wminx, wminy)
  73. smaxx, smaxy = self.layer_manager.scrolling_manager.world_to_screen(wmaxx, wmaxy)
  74. self.vertexes = [(sminx, sminy), (sminx, smaxy), (smaxx, smaxy), (smaxx, sminy)]
  75. def draw(self):
  76. if not self.visible:
  77. return
  78. pyglet.gl.glLineWidth(1) # deprecated
  79. pyglet.gl.glColor3ub(*self.color3)
  80. pyglet.gl.glBegin(pyglet.gl.GL_LINE_STRIP)
  81. for v in self.vertexes:
  82. pyglet.gl.glVertex2f(*v)
  83. pyglet.gl.glVertex2f(*self.vertexes[0])
  84. pyglet.gl.glEnd()
  85. # rectangle
  86. pyglet.gl.glColor4f(0, 0, 0, 0.5)
  87. pyglet.gl.glBegin(pyglet.gl.GL_QUADS)
  88. pyglet.gl.glVertex3f(self.vertexes[0][0], self.vertexes[0][1], 0)
  89. pyglet.gl.glVertex3f(self.vertexes[1][0], self.vertexes[1][1], 0)
  90. pyglet.gl.glVertex3f(self.vertexes[2][0], self.vertexes[2][1], 0)
  91. pyglet.gl.glVertex3f(self.vertexes[3][0], self.vertexes[3][1], 0)
  92. pyglet.gl.glEnd()
  93. def set_vertexes_from_minmax(self, minx, maxx, miny, maxy):
  94. self.vertexes = [(minx, miny), (minx, maxy), (maxx, maxy), (maxx, miny)]
  95. class EditLayer(cocos.layer.Layer):
  96. is_event_handler = True
  97. def __init__(
  98. self,
  99. config: Config,
  100. logger: SynergineLogger,
  101. layer_manager: LayerManager,
  102. worldview,
  103. bindings=None,
  104. fastness=None,
  105. autoscroll_border=10,
  106. autoscroll_fastness=None,
  107. wheel_multiplier=None,
  108. zoom_min=None,
  109. zoom_max=None,
  110. zoom_fastness=None,
  111. mod_modify_selection=None,
  112. mod_restricted_mov=None,
  113. ):
  114. super(EditLayer, self).__init__()
  115. self.config = config
  116. self.logger = logger
  117. self.layer_manager = layer_manager
  118. self.bindings = bindings
  119. buttons = {}
  120. modifiers = {}
  121. for k in bindings:
  122. buttons[bindings[k]] = 0
  123. modifiers[bindings[k]] = 0
  124. self.buttons = buttons
  125. self.modifiers = modifiers
  126. self.fastness = fastness
  127. self.autoscroll_border = autoscroll_border
  128. self.autoscroll_fastness = autoscroll_fastness
  129. self.wheel_multiplier = wheel_multiplier
  130. self.zoom_min = zoom_min
  131. self.zoom_max = zoom_max
  132. self.zoom_fastness = zoom_fastness
  133. self.mod_modify_selection = mod_modify_selection
  134. self.mod_restricted_mov = mod_restricted_mov
  135. self.weak_scroller = weakref.ref(self.layer_manager.scrolling_manager)
  136. self.weak_worldview = weakref.ref(worldview)
  137. self.wwidth = worldview.width
  138. self.wheight = worldview.height
  139. self.autoscrolling = False
  140. self.drag_selecting = False
  141. self.drag_moving = False
  142. self.restricted_mov = False
  143. self.wheel = 0
  144. self.dragging = False
  145. self.keyscrolling = False
  146. self.keyscrolling_descriptor = (0, 0)
  147. self.wdrag_start_point = (0, 0)
  148. self.elastic_box = None # type: MinMaxRect
  149. self.elastic_box_wminmax = 0, 0, 0, 0
  150. self.selection = {}
  151. self.screen_mouse = (0, 0)
  152. self.world_mouse = (0, 0)
  153. self.sleft = None
  154. self.sright = None
  155. self.sbottom = None
  156. self.s_top = None
  157. # opers that change cshape must ensure it goes to False,
  158. # selection opers must ensure it goes to True
  159. self.selection_in_collman = True
  160. # TODO: Hardcoded here, should be obtained from level properties or calc
  161. # from available actors or current actors in worldview
  162. gsize = 32 * 1.25
  163. self.collision_manager = collision_model.CollisionManagerGrid(
  164. -gsize,
  165. self.wwidth + gsize,
  166. -gsize,
  167. self.wheight + gsize,
  168. gsize,
  169. gsize,
  170. )
  171. self.schedule(self.update)
  172. self.selectable_actors = []
  173. def set_selectable(self, actor: Actor) -> None:
  174. self.selectable_actors.append(actor)
  175. self.collision_manager.add(actor)
  176. def unset_selectable(self, actor: Actor) -> None:
  177. self.selectable_actors.remove(actor)
  178. self.collision_manager.remove_tricky(actor)
  179. def draw(self, *args, **kwargs) -> None:
  180. for actor in self.selectable_actors:
  181. if actor.need_update_cshape:
  182. if self.collision_manager.knows(actor):
  183. self.collision_manager.remove_tricky(actor)
  184. actor.update_cshape()
  185. self.collision_manager.add(actor)
  186. actor.need_update_cshape = False
  187. def on_enter(self):
  188. super(EditLayer, self).on_enter()
  189. scene = self.get_ancestor(cocos.scene.Scene)
  190. if self.elastic_box is None:
  191. self.elastic_box = MinMaxRect(self.layer_manager)
  192. scene.add(self.elastic_box, z=10)
  193. def update(self, dt):
  194. mx = self.buttons['right'] - self.buttons['left']
  195. my = self.buttons['up'] - self.buttons['down']
  196. dz = self.buttons['zoomin'] - self.buttons['zoomout']
  197. # scroll
  198. if self.autoscrolling:
  199. self.update_autoscroll(dt)
  200. else:
  201. # care for keyscrolling
  202. new_keyscrolling = ((len(self.selection) == 0) and
  203. (mx != 0 or my != 0))
  204. new_keyscrolling_descriptor = (mx, my)
  205. if ((new_keyscrolling != self.keyscrolling) or
  206. (new_keyscrolling_descriptor != self.keyscrolling_descriptor)):
  207. self.keyscrolling = new_keyscrolling
  208. self.keyscrolling_descriptor = new_keyscrolling_descriptor
  209. fastness = 1.0
  210. if mx != 0 and my != 0:
  211. fastness *= 0.707106 # 1/sqrt(2)
  212. self.autoscrolling_sdelta = (0.5 * fastness * mx, 0.5 * fastness * my)
  213. if self.keyscrolling:
  214. self.update_autoscroll(dt)
  215. # selection move
  216. if self.drag_moving:
  217. # update positions
  218. wx, wy = self.world_mouse
  219. dx = wx - self.wdrag_start_point[0]
  220. dy = wy - self.wdrag_start_point[1]
  221. if self.restricted_mov:
  222. if abs(dy) > abs(dx):
  223. dx = 0
  224. else:
  225. dy = 0
  226. dpos = euclid.Vector2(dx, dy)
  227. for actor in self.selection:
  228. old_pos = self.selection[actor].center
  229. new_pos = old_pos + dpos
  230. # TODO: clamp new_pos so actor into world boundaries ?
  231. actor.update_position(new_pos)
  232. scroller = self.weak_scroller()
  233. # zoom
  234. zoom_change = (dz != 0 or self.wheel != 0)
  235. if zoom_change:
  236. if self.mouse_into_world():
  237. wzoom_center = self.world_mouse
  238. szoom_center = self.screen_mouse
  239. else:
  240. # decay to scroller unadorned
  241. wzoom_center = None
  242. if self.wheel != 0:
  243. dt_dz = 0.01666666 * self.wheel
  244. self.wheel = 0
  245. else:
  246. dt_dz = dt * dz
  247. zoom = scroller.scale + dt_dz * self.zoom_fastness
  248. if zoom < self.zoom_min:
  249. zoom = self.zoom_min
  250. elif zoom > self.zoom_max:
  251. zoom = self.zoom_max
  252. scroller.scale = zoom
  253. if wzoom_center is not None:
  254. # postprocess toward 'world point under mouse the same before
  255. # and after zoom' ; other restrictions may prevent fully comply
  256. wx1, wy1 = self.layer_manager.scrolling_manager.screen_to_world(*szoom_center)
  257. fx = scroller.restricted_fx + (wzoom_center[0] - wx1)
  258. fy = scroller.restricted_fy + (wzoom_center[1] - wy1)
  259. scroller.set_focus(fx, fy)
  260. def update_mouse_position(self, sx, sy):
  261. self.screen_mouse = sx, sy
  262. self.world_mouse = self.layer_manager.scrolling_manager.screen_to_world(sx, sy)
  263. # handle autoscroll
  264. border = self.autoscroll_border
  265. if border is not None:
  266. # sleft and companions includes the border
  267. scroller = self.weak_scroller()
  268. self.update_view_bounds()
  269. sdx = 0.0
  270. if sx < self.sleft:
  271. sdx = sx - self.sleft
  272. elif sx > self.sright:
  273. sdx = sx - self.sright
  274. sdy = 0.0
  275. if sy < self.sbottom:
  276. sdy = sy - self.sbottom
  277. elif sy > self.s_top:
  278. sdy = sy - self.s_top
  279. self.autoscrolling = sdx != 0.0 or sdy != 0.0
  280. if self.autoscrolling:
  281. self.autoscrolling_sdelta = (sdx / border, sdy / border)
  282. def update_autoscroll(self, dt):
  283. fraction_sdx, fraction_sdy = self.autoscrolling_sdelta
  284. scroller = self.weak_scroller()
  285. worldview = self.weak_worldview()
  286. f = self.autoscroll_fastness
  287. wdx = (fraction_sdx * f * dt) / scroller.scale / worldview.scale
  288. wdy = (fraction_sdy * f * dt) / scroller.scale / worldview.scale
  289. # ask scroller to try scroll (wdx, wdy)
  290. fx = scroller.restricted_fx + wdx
  291. fy = scroller.restricted_fy + wdy
  292. scroller.set_focus(fx, fy)
  293. self.world_mouse = self.layer_manager.scrolling_manager.screen_to_world(*self.screen_mouse)
  294. self.adjust_elastic_box()
  295. # self.update_view_bounds()
  296. def update_view_bounds(self):
  297. scroller = self.weak_scroller()
  298. scx, scy = self.layer_manager.scrolling_manager.world_to_screen(
  299. scroller.restricted_fx,
  300. scroller.restricted_fy,
  301. )
  302. hw = scroller.view_w / 2.0
  303. hh = scroller.view_h / 2.0
  304. border = self.autoscroll_border
  305. self.sleft = scx - hw + border
  306. self.sright = scx + hw - border
  307. self.sbottom = scy - hh + border
  308. self.s_top = scy + hh - border
  309. def mouse_into_world(self):
  310. worldview = self.weak_worldview()
  311. # TODO: allow lower limits != 0 ?
  312. return ((0 <= self.world_mouse[0] <= worldview.width) and
  313. (0 <= self.world_mouse[1] <= worldview.height))
  314. def on_key_press(self, k, m):
  315. binds = self.bindings
  316. if k in binds:
  317. self.buttons[binds[k]] = 1
  318. self.modifiers[binds[k]] = 1
  319. return True
  320. return False
  321. def on_key_release(self, k, m):
  322. binds = self.bindings
  323. if k in binds:
  324. self.buttons[binds[k]] = 0
  325. self.modifiers[binds[k]] = 0
  326. return True
  327. return False
  328. def on_mouse_motion(self, sx, sy, dx, dy):
  329. self.update_mouse_position(sx, sy)
  330. def on_mouse_leave(self, sx, sy):
  331. self.autoscrolling = False
  332. def on_mouse_press(self, x, y, buttons, modifiers):
  333. if self.logger.is_debug:
  334. rx, ry = self.layer_manager.scrolling_manager.screen_to_world(x, y)
  335. self.logger.debug('GUI click: x: {}, y: {}, rx: {}, ry: {}'.format(x, y, rx, ry))
  336. def on_mouse_release(self, sx, sy, button, modifiers):
  337. # should we handle here mod_restricted_mov ?
  338. wx, wy = self.layer_manager.scrolling_manager.screen_to_world(sx, sy)
  339. modify_selection = modifiers & self.mod_modify_selection
  340. if self.dragging:
  341. # ignore all buttons except left button
  342. if button != mouse.LEFT:
  343. return
  344. if self.drag_selecting:
  345. self.end_drag_selection(wx, wy, modify_selection)
  346. elif self.drag_moving:
  347. self.end_drag_move(wx, wy)
  348. self.dragging = False
  349. else:
  350. if button == mouse.LEFT:
  351. self.end_click_selection(wx, wy, modify_selection)
  352. def end_click_selection(self, wx, wy, modify_selection):
  353. under_mouse_unique = self.single_actor_from_mouse()
  354. if modify_selection:
  355. # toggle selected status for unique
  356. if under_mouse_unique in self.selection:
  357. self.selection_remove(under_mouse_unique)
  358. elif under_mouse_unique is not None:
  359. self.selection_add(under_mouse_unique)
  360. else:
  361. # new_selected becomes the current selected
  362. self.selection.clear()
  363. if under_mouse_unique is not None:
  364. self.selection_add(under_mouse_unique)
  365. def selection_add(self, actor):
  366. self.selection[actor] = actor.cshape.copy()
  367. def selection_remove(self, actor):
  368. del self.selection[actor]
  369. def end_drag_selection(self, wx, wy, modify_selection):
  370. new_selection = self.collision_manager.objs_into_box(*self.elastic_box_wminmax)
  371. if not modify_selection:
  372. # new_selected becomes the current selected
  373. self.selection.clear()
  374. for actor in new_selection:
  375. self.selection_add(actor)
  376. self.elastic_box.visible = False
  377. self.drag_selecting = False
  378. def on_mouse_drag(self, sx, sy, dx, dy, buttons, modifiers):
  379. # TODO: inhibir esta llamada si estamos fuera de la client area / viewport
  380. self.update_mouse_position(sx, sy)
  381. if not buttons & mouse.LEFT:
  382. # ignore except for left-btn-drag
  383. return
  384. if not self.dragging:
  385. print("begin drag")
  386. self.begin_drag()
  387. return
  388. if self.drag_selecting:
  389. # update elastic box
  390. self.adjust_elastic_box()
  391. elif self.drag_moving:
  392. self.restricted_mov = (modifiers & self.mod_restricted_mov)
  393. def adjust_elastic_box(self):
  394. # when elastic_box visible this method needs to be called any time
  395. # world_mouse changes or screen_to_world results changes (scroll, etc)
  396. wx0, wy0 = self.wdrag_start_point
  397. wx1, wy1 = self.world_mouse
  398. wminx = min(wx0, wx1)
  399. wmaxx = max(wx0, wx1)
  400. wminy = min(wy0, wy1)
  401. wmaxy = max(wy0, wy1)
  402. self.elastic_box_wminmax = wminx, wmaxx, wminy, wmaxy
  403. self.elastic_box.adjust_from_w_minmax(*self.elastic_box_wminmax)
  404. def begin_drag(self):
  405. self.dragging = True
  406. self.wdrag_start_point = self.world_mouse
  407. under_mouse_unique = self.single_actor_from_mouse()
  408. if under_mouse_unique is None:
  409. # begin drag selection
  410. self.drag_selecting = True
  411. self.adjust_elastic_box()
  412. self.elastic_box.visible = True
  413. print("begin drag selection: drag_selecting, drag_moving",
  414. self.drag_selecting, self.drag_moving)
  415. else:
  416. # want drag move
  417. if under_mouse_unique in self.selection:
  418. # want to move current selection
  419. pass
  420. else:
  421. # change selection before moving
  422. self.selection.clear()
  423. self.selection_add(under_mouse_unique)
  424. self.begin_drag_move()
  425. def begin_drag_move(self):
  426. # begin drag move
  427. self.drag_moving = True
  428. # how-to update collman: remove/add vs clear/add all
  429. # when total number of actors is low anyone will be fine,
  430. # with high numbers, most probably we move only a small fraction
  431. # For simplicity I choose remove/add, albeit a hybrid aproach
  432. # can be implemented later
  433. self.set_selection_in_collman(False)
  434. # print "begin drag: drag_selecting, drag_moving", self.drag_selecting, self.drag_moving
  435. def end_drag_move(self, wx, wy):
  436. self.set_selection_in_collman(True)
  437. for actor in self.selection:
  438. self.selection[actor] = actor.cshape.copy()
  439. self.drag_moving = False
  440. def single_actor_from_mouse(self):
  441. under_mouse = self.collision_manager.objs_touching_point(*self.world_mouse)
  442. if len(under_mouse) == 0:
  443. return None
  444. # return the one with the center most near to mouse, if tie then
  445. # an arbitrary in the tie
  446. nearest = None
  447. near_d = None
  448. p = euclid.Vector2(*self.world_mouse)
  449. for actor in under_mouse:
  450. d = (actor.cshape.center - p).magnitude_squared()
  451. if nearest is None or (d < near_d):
  452. nearest = actor
  453. near_d = d
  454. return nearest
  455. def set_selection_in_collman(self, bool_value):
  456. if self.selection_in_collman == bool_value:
  457. return
  458. self.selection_in_collman = bool_value
  459. if bool_value:
  460. for actor in self.selection:
  461. self.collision_manager.add(actor)
  462. else:
  463. for actor in self.selection:
  464. self.collision_manager.remove_tricky(actor)
  465. def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
  466. # TODO: check if mouse over scroller viewport?
  467. self.wheel += scroll_y * self.wheel_multiplier
  468. class MainLayer(ScrollableLayer):
  469. is_event_handler = True
  470. def __init__(
  471. self,
  472. layer_manager: LayerManager,
  473. width: int,
  474. height: int,
  475. scroll_step: int=100,
  476. ) -> None:
  477. super().__init__()
  478. self.layer_manager = layer_manager
  479. self.scroll_step = scroll_step
  480. self.grid_manager = GridManager(self, 32, border=2)
  481. self.width = width
  482. self.height = height
  483. self.px_width = width
  484. self.px_height = height
  485. class Gui(object):
  486. def __init__(
  487. self,
  488. config: Config,
  489. logger: SynergineLogger,
  490. terminal: Terminal,
  491. read_queue_interval: float= 1/60.0,
  492. ):
  493. self.config = config
  494. self.logger = logger
  495. self._read_queue_interval = read_queue_interval
  496. self.terminal = terminal
  497. self.cycle_duration = self.config.core.cycle_duration
  498. cocos.director.director.init(
  499. width=640,
  500. height=480,
  501. vsync=True,
  502. resizable=True,
  503. )
  504. # Enable blending
  505. pyglet.gl.glEnable(pyglet.gl.GL_BLEND)
  506. pyglet.gl.glBlendFunc(pyglet.gl.GL_SRC_ALPHA, pyglet.gl.GL_ONE_MINUS_SRC_ALPHA)
  507. # Enable transparency
  508. pyglet.gl.glEnable(pyglet.gl.GL_ALPHA_TEST)
  509. pyglet.gl.glAlphaFunc(pyglet.gl.GL_GREATER, .1)
  510. def run(self):
  511. self.before_run()
  512. pyglet.clock.schedule_interval(
  513. lambda *_, **__: self.terminal.read(),
  514. self._read_queue_interval,
  515. )
  516. cocos.director.director.run(self.get_main_scene())
  517. def before_run(self) -> None:
  518. pass
  519. def get_main_scene(self) -> cocos.cocosnode.CocosNode:
  520. raise NotImplementedError()
  521. def before_received(self, package: TerminalPackage):
  522. pass
  523. def after_received(self, package: TerminalPackage):
  524. pass
  525. class TMXGui(Gui):
  526. def __init__(
  527. self,
  528. config: Config,
  529. logger: SynergineLogger,
  530. terminal: Terminal,
  531. read_queue_interval: float = 1 / 60.0,
  532. map_dir_path: str=None,
  533. ):
  534. assert map_dir_path
  535. super(TMXGui, self).__init__(
  536. config,
  537. logger,
  538. terminal,
  539. read_queue_interval,
  540. )
  541. self.map_dir_path = map_dir_path
  542. self.layer_manager = LayerManager(
  543. self.config,
  544. self.logger,
  545. middleware=TMXMiddleware(
  546. self.config,
  547. self.logger,
  548. self.map_dir_path,
  549. ),
  550. )
  551. self.layer_manager.init()
  552. self.layer_manager.center()
  553. def get_main_scene(self) -> cocos.cocosnode.CocosNode:
  554. return self.layer_manager.main_scene