class ShipCollision: def __init__(self, ship): self.ship = ship self.setup_collision() self.queue = CollisionHandlerQueue() self.traverser = CollisionTraverser('Collision Traverser') self.traverser.showCollisions(render) self.traverser.add_collider(self.target_nodepath, self.queue) base.taskMgr.add(self.collide, "Collision Task") def setup_collision(self): self.target = CollisionSphere(0, 0, 0, 0.5) self.target_node = CollisionNode('collision_ship') self.target_node.setFromCollideMask(ENEMIES) self.target_node.setIntoCollideMask(ALLIES) self.target_nodepath = self.ship.model.attach_new_node(self.target_node) self.target_nodepath.node().addSolid(self.target) #self.target_nodepath.show() def collide(self, task): self.traverser.traverse(render) for entry in self.queue.get_entries(): #print("Ship:") #print(entry) self.ship.model.cleanup() self.ship.model.removeNode() return task.cont
class RayThatCollidesWithScene: def __init__(self): self.hitters = [self.setup_collision_ray(offset, bitmask) for offset, bitmask in [ (-3, BM_LEFT), (3, BM_RIGHT), ]] self.queue = CollisionHandlerQueue() self.traverser = CollisionTraverser('Collision Traverser') self.traverser.showCollisions(base.render) for ray in self.hitters: self.traverser.add_collider(ray, self.queue) base.taskMgr.add(self.collide, "Collision Task") def setup_collision_ray(self, offset, bitmask): # Hitter. Do note that not every combination of object works, # there is a table for that in the manual. hitter = CollisionRay(0, 0, 0, 0, 1, 0) hitter_node = CollisionNode('collision_hitter') hitter_node.setFromCollideMask(bitmask) hitter_nodepath = base.render.attach_new_node(hitter_node) hitter_nodepath.node().addSolid(hitter) hitter_nodepath.set_pos(offset, -2, 0) hitter_nodepath.show() return hitter_nodepath def collide(self, task): self.traverser.traverse(render) for entry in self.queue.get_entries(): print(entry) return task.cont
class EntityCollision: def __init__(self, entity): self.entity = entity self.setup_collision() self.queue = CollisionHandlerQueue() self.traverser = CollisionTraverser('Collision Traverser') self.traverser.showCollisions(render) self.traverser.add_collider(self.target_nodepath, self.queue) base.taskMgr.add(self.collide, "Collision Task") def setup_collision(self): self.target = CollisionSphere(0, 0, 0, 1) self.target_node = CollisionNode('collision_entity') self.target_node.setFromCollideMask(ALLIES) # unused self.target_node.setIntoCollideMask(ENEMIES) self.target_nodepath = self.entity.model.attach_new_node(self.target_node) self.target_nodepath.node().addSolid(self.target) self.target_nodepath.show() def collide(self, task): self.traverser.traverse(render) for entry in self.queue.get_entries(): # print("Entity:") pos = entry.getSurfacePoint(self.entity.model) pos_x = pos[0] pos_z = pos[2] self.entity.spawn_particles(pos_x, pos_z) self.entity.life -= 1 return task.cont
class ShipCollision: def __init__(self, game): self.game = game self.setup_collision() self.queue = CollisionHandlerQueue() self.traverser = CollisionTraverser('Collision Traverser') self.traverser.showCollisions(render) self.traverser.add_collider(self.target_nodepath, self.queue) base.taskMgr.add(self.collide, "Collision Task") def setup_collision(self): self.target = CollisionSphere(0, 0, 0, 0.5) self.target_node = CollisionNode('collision_ship') self.target_node.setFromCollideMask(ENEMIES) self.target_node.setIntoCollideMask(ALLIES) self.target_nodepath = self.game.ship.model.attach_new_node( self.target_node) self.target_nodepath.node().addSolid(self.target) self.target_nodepath.show() def collide(self, task): self.traverser.traverse(render) for entry in self.queue.get_entries(): print("Ship:") print(entry) return task.cont
def test_collision_traverser_pickle(): from direct.stdpy.pickle import dumps, loads handler = CollisionHandlerQueue() collider1 = NodePath(CollisionNode("collider1")) collider2 = NodePath(CollisionNode("collider2")) trav = CollisionTraverser("test123") trav.respect_prev_transform = True trav.add_collider(collider1, handler) trav.add_collider(collider2, handler) trav = loads(dumps(trav, -1)) assert trav.respect_prev_transform is True assert trav.name == "test123" assert trav.get_num_colliders() == 2 collider1 = trav.get_collider(0) collider2 = trav.get_collider(1) assert collider1.name == "collider1" assert collider2.name == "collider2" # Two colliders must still be the same object; this only works with our own # version of the pickle module, in direct.stdpy.pickle. assert trav.get_handler(collider1) == trav.get_handler(collider2)
class RayThatCollidesWithScene: def __init__(self): self.hitters = [ self.setup_collision_ray(offset, bitmask) for offset, bitmask in [ (-3, BM_LEFT), (3, BM_RIGHT), ] ] self.queue = CollisionHandlerQueue() self.traverser = CollisionTraverser('Collision Traverser') self.traverser.showCollisions(base.render) for ray in self.hitters: self.traverser.add_collider(ray, self.queue) base.taskMgr.add(self.collide, "Collision Task") def setup_collision_ray(self, offset, bitmask): # Hitter. Do note that not every combination of object works, # there is a table for that in the manual. hitter = CollisionRay(0, 0, 0, 0, 1, 0) hitter_node = CollisionNode('collision_hitter') hitter_node.setFromCollideMask(bitmask) hitter_nodepath = base.render.attach_new_node(hitter_node) hitter_nodepath.node().addSolid(hitter) hitter_nodepath.set_pos(offset, -2, 0) hitter_nodepath.show() return hitter_nodepath def collide(self, task): self.traverser.traverse(render) for entry in self.queue.get_entries(): print(entry) return task.cont
class BulletCollision: def __init__(self, bullet): self.bullet = bullet self.setup_collision() self.queue = CollisionHandlerQueue() self.traverser = CollisionTraverser('Collision Traverser') self.traverser.showCollisions(render) self.traverser.add_collider(self.target_nodepath, self.queue) base.taskMgr.add(self.collide, "Collision Task") def setup_collision(self): self.target = CollisionSphere(0, 0, 0, 0.1) self.target_node = CollisionNode('collision_bullet') self.target_node.setFromCollideMask(ENEMIES) self.target_node.setIntoCollideMask(ALLIES) self.target_nodepath = self.bullet.model.attach_new_node( self.target_node) self.target_nodepath.node().addSolid(self.target) self.target_nodepath.show() def collide(self, task): self.traverser.traverse(render) for entry in self.queue.get_entries(): #print("Bullet:") #print(entry) self.bullet.model.removeNode() return task.cont
class EntityCollision: def __init__(self, entity): self.entity = entity self.setup_collision() self.queue = CollisionHandlerQueue() self.traverser = CollisionTraverser('Collision Traverser') self.traverser.showCollisions(render) self.traverser.add_collider(self.target_nodepath, self.queue) base.taskMgr.add(self.collide, "Collision Task") def setup_collision(self): self.target = CollisionSphere(0, 0, 0, 1) self.target_node = CollisionNode('collision_entity') self.target_node.setFromCollideMask(ALLIES) # unused self.target_node.setIntoCollideMask(ENEMIES) self.target_nodepath = self.entity.model.attach_new_node( self.target_node) self.target_nodepath.node().addSolid(self.target) self.target_nodepath.show() def collide(self, task): self.traverser.traverse(render) for entry in self.queue.get_entries(): # print("Entity:") pos = entry.getSurfacePoint(self.entity.model) pos_x = pos[0] pos_z = pos[2] self.entity.spawn_particles(pos_x, pos_z) self.entity.life -= 1 return task.cont
def make_collision(solid_from, solid_into): node_from = CollisionNode("from") node_from.add_solid(solid_from) node_into = CollisionNode("into") node_into.add_solid(solid_into) root = NodePath("root") trav = CollisionTraverser() queue = CollisionHandlerQueue() np_from = root.attach_new_node(node_from) np_into = root.attach_new_node(node_into) trav.add_collider(np_from, queue) trav.traverse(root) entry = None for e in queue.get_entries(): if e.get_into() == solid_into: entry = e return (entry, np_from, np_into)
def make_collision(solid_from, solid_into): node_from = CollisionNode("from") node_from.add_solid(solid_from) node_into = CollisionNode("into") node_into.add_solid(solid_into) root = NodePath("root") trav = CollisionTraverser() queue = CollisionHandlerQueue() np_from = root.attach_new_node(node_from) np_into = root.attach_new_node(node_into) trav.add_collider(np_from, queue) trav.traverse(root) entry = None for e in queue.get_entries(): if e.get_into() == solid_into: entry = e return (entry, np_from, np_into)
class Cursor(DirectObject): """Cursor object for the UI.""" parent: NodePath mouse_np: NodePath actor: Actor last_position: Point moved: bool pointed_at: Optional[NodePath] def __init__(self, parent: NodePath): super().__init__() self.parent = parent self.mouse_np = p3d.camera.attach_new_node(PandaNode('mouse')) self.mouse_np.set_y(p3d.lens.get_near()) picker_node = CollisionNode('mouse_ray') picker_np = p3d.camera.attach_new_node(picker_node) self._picker_ray = CollisionRay() picker_node.add_solid(self._picker_ray) self._collision_handler = CollisionHandlerQueue() self._traverser = CollisionTraverser('mouse_traverser') self._traverser.add_collider(picker_np, self._collision_handler) self.actor = Actor( resource_filename('tsim', 'data/models/cursor'), {'spin': resource_filename('tsim', 'data/models/cursor-spin')}) self.actor.loop('spin') self.actor.reparent_to(parent) self.actor.set_pos(0.0, 0.0, 0.0) self.actor.set_shader_off() self._position = Point(0.0, 0.0) self.last_position = self._position self.moved = False self.pointed_at = None self._tool: Optional[Tool] = None self._register_events() @property def position(self) -> Point: """Get the cursor position.""" return self._position @position.setter def position(self, value: Union[Point, Iterable]): if not isinstance(value, Point): value = Point(*islice(value, 2)) self.actor.set_x(value.x) self.actor.set_y(value.y) self._position = value @property def tool(self) -> Optional[Tool]: """Get current tool.""" return self._tool @tool.setter def tool(self, value: Tool): if self._tool is not None: self._tool.cleanup() self.ignore_all() self._register_events() self._tool = value if value is not None: for key in INPUT.keys_for('tool_1'): self.accept(key, self._tool.on_button1_press) self.accept(f'{key}-up', self._tool.on_button1_release) for key in INPUT.keys_for('tool_2'): self.accept(key, self._tool.on_button2_press) self.accept(f'{key}-up', self._tool.on_button2_release) for key in INPUT.keys_for('tool_3'): self.accept(key, self._tool.on_button3_press) self.accept(f'{key}-up', self._tool.on_button3_release) self.accept('cursor_move', self._tool.on_cursor_move) def update(self): """Update callback.""" self.actor.set_scale(p3d.camera.get_z()**0.6 / 10) self.moved = False if p3d.mouse_watcher.has_mouse(): mouse_x, mouse_y = p3d.mouse_watcher.get_mouse() self._picker_ray.set_from_lens(p3d.cam_node, mouse_x, mouse_y) self._traverser.traverse(p3d.render) if self._collision_handler.get_num_entries(): self._collision_handler.sort_entries() node_path = ( self._collision_handler.get_entry(0).get_into_node_path()) self.position = node_path.get_pos(p3d.render) self.actor.set_z(2.0) self.pointed_at = node_path else: self.pointed_at = None film = p3d.lens.get_film_size() * 0.5 self.mouse_np.set_x(mouse_x * film.x) self.mouse_np.set_y(p3d.lens.get_focal_length()) self.mouse_np.set_z(mouse_y * film.y) self.last_position = self._position mouse_pos = self.mouse_np.get_pos(self.parent) cam_pos = p3d.camera.get_pos(self.parent) mouse_vec = mouse_pos - cam_pos if mouse_vec.z < 0.0: scale = -mouse_pos.z / mouse_vec.z self.actor.set_pos(mouse_pos + mouse_vec * scale) self.position = self.actor.get_pos() if self._position != self.last_position: self.moved = True p3d.messenger.send('cursor_move') if self._tool is not None: self._tool.on_update() def _on_simulation_step(self, dt: Duration): if self._tool is not None: self._tool.on_simulation_step(dt) def _register_events(self): def set_tool(tool: Type[Tool]): self.tool = tool(self) log.info('[%s] Changing tool to %s', __name__, tool.__name__) for tool in TOOLS: try: self.accept(tool.KEY, partial(set_tool, tool)) except AttributeError: log.warning('[%s] No KEY set for tool %s', __name__, tool.__name__) self.accept('simulation_step', self._on_simulation_step)
class MouseOverOnEntity(System): entity_filters = { 'mouseoverable': [Proxy('model'), MouseOverable], 'mouseoverable_geometry': [Proxy('geometry'), MouseOverableGeometry], 'camera': [Camera, Input, MouseOveringCamera], } proxies = { 'model': ProxyType(Model, 'node'), 'geometry': ProxyType(Geometry, 'node'), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.traverser = CollisionTraverser() self.queue = CollisionHandlerQueue() self.picker_ray = CollisionRay() self.picker_node = CollisionNode('mouse ray') self.picker_node.add_solid(self.picker_ray) self.picker_node.set_from_collide_mask(MOUSEOVER_MASK) self.picker_node.set_into_collide_mask(0x0) self.picker_node_path = NodePath(self.picker_node) self.traverser.add_collider(self.picker_node_path, self.queue) def enter_filter_mouseoverable(self, entity): model_proxy = self.proxies['model'] model_node = model_proxy.field(entity) mouseoverable = entity[MouseOverable] into_node = CollisionNode('wecs_mouseoverable') into_node.add_solid(mouseoverable.solid) into_node.set_from_collide_mask(0x0) into_node.set_into_collide_mask(mouseoverable.mask) into_node_path = model_node.attach_new_node(into_node) into_node_path.set_python_tag('wecs_mouseoverable', entity._uid) def exit_filter_mouseoverable(self, entity): # FIXME: Undo all the other stuff that accumulated! entity[MouseOverable].solid.detach_node() def enter_filter_mouseoverable_geometry(self, entity): into_node = self.proxies['geometry'].field(entity) old_mask = into_node.get_collide_mask() new_mask = old_mask | entity[MouseOverableGeometry].mask into_node.set_collide_mask(new_mask) into_node.find('**/+GeomNode').set_python_tag('wecs_mouseoverable', entity._uid) def update(self, entities_by_filter): for entity in entities_by_filter['camera']: mouse_overing = entity[MouseOveringCamera] camera = entity[Camera] input = entity[Input] # Reset overed entity to None mouse_overing.entity = None mouse_overing.collision_entry = None requested = 'mouse_over' in input.contexts has_mouse = base.mouseWatcherNode.has_mouse() if requested and has_mouse: # Attach and align testing ray, and run collisions self.picker_node_path.reparent_to(camera.camera) mpos = base.mouseWatcherNode.get_mouse() self.picker_ray.set_from_lens( base.camNode, mpos.getX(), mpos.getY(), ) self.traverser.traverse(camera.camera.get_top()) # Remember reference to mouseovered entity, if any if self.queue.get_num_entries() > 0: self.queue.sort_entries() entry = self.queue.get_entry(0) picked_node = entry.get_into_node_path() picked_uid = picked_node.get_python_tag( 'wecs_mouseoverable') mouse_overing.entity = picked_uid mouse_overing.collision_entry = entry
class Player(): def __init__(self, world): self.world = world self.root = self.world.root.attach_new_node("player") self.root_target = self.world.root.attach_new_node("player_target") self.pivot = self.root.attach_new_node("player_pivot") base.camera.reparent_to(self.pivot) base.camera.set_z(1.7) base.cam.node().get_lens().set_fov(90) self.traverser = CollisionTraverser() self.ray = setup_ray( self.pivot, self.traverser, self.world.mask, # ray ends well below feet to register downward slopes (0, 0, 1), (0, 0, -1)) self.xyh_inertia = Vec3(0, 0, 0) h_acc = ConfigVariableDouble('mouse-accelleration', 0.1).get_value() self.xyh_acceleration = Vec3(0.8, 0.8, h_acc) self.friction = 0.15 self.torque = 0.5 self.last_up = Vec3(0, 0, 1) # Collider for portals csphere = CollisionSphere(0, 0, 1.25, 1.5) cnode = CollisionNode('player') cnode.add_solid(csphere) cnode.set_from_collide_mask(0x2) cnode.set_into_collide_mask(CollideMask.all_off()) self.collider = self.root.attach_new_node(cnode) self.event_handler = CollisionHandlerEvent() self.event_handler.add_in_pattern('into-%in') self.traverser.add_collider(self.collider, self.event_handler) self.collider.show() self.teleported = False base.input.set_mouse_relativity(True) def handle_input(self): dt = globalClock.get_dt() directional_keys = (["strafe_right", "strafe_left"], ["forward", "backward"]) for k, keys in enumerate(directional_keys): if base.input.buttons[keys[0]]: self.xyh_inertia[k] += self.xyh_acceleration[k] * dt elif base.input.buttons[keys[1]]: self.xyh_inertia[k] -= self.xyh_acceleration[k] * dt self.xyh_inertia[k] /= 1 + self.friction if base.mouseWatcherNode.hasMouse(): h = base.input.mouse_movement.x * self.xyh_acceleration[2] * dt self.pivot.set_h(self.pivot, h) p = base.input.mouse_movement.y * self.xyh_acceleration[2] * dt base.cam.set_p(base.cam, p) base.cam.set_p(max(-70, min(base.cam.get_p(), 70))) #base.cam.set_r(0) def ray_to_destination(self, xonly=0): if xonly: self.ray["node"].set_x(self.pivot, self.xyh_inertia[1] * xonly) self.ray["node"].set_y(self.pivot, 0) else: self.ray["node"].set_x(self.pivot, self.xyh_inertia[0]) self.ray["node"].set_y(self.pivot, self.xyh_inertia[1]) def move_to_ray(self): self.ray["handler"].sort_entries() #closest_entry = list(self.ray["handler"].entries)[0] closest_entry = None delta = float('inf') dot = -1.0 for c in list(self.ray["handler"].entries): if closest_entry is None: closest_entry = c delta = c.get_surface_point(self.root).length() dot = self.last_up.dot(c.get_surface_normal(render)) continue if c.get_surface_point(self.root).length() < delta: if self.last_up.dot(c.get_surface_normal(render)) > dot: closest_entry = c dot = self.last_up.dot(c.get_surface_normal(render)) delta = c.get_surface_point(self.root).length() collision_point = closest_entry.get_surface_point(render) collision_normal = closest_entry.get_surface_normal(render) self.last_up = collision_normal # take heed of the ray ending well below feet #collision_point.set_z(max(0,collision_point.z)) self.root.set_pos(render, collision_point) self.root_target.set_pos(render, collision_point) self.root_target.heads_up(render, collision_point, collision_normal) def update(self): if self.teleported: self.xyh_inertia = Vec3(0, self.xyh_acceleration[1], 0) self.teleported = False dt = globalClock.get_dt() self.handle_input() if self.xyh_inertia.length() > 0: self.ray_to_destination() self.traverser.traverse(render) if self.ray["handler"].get_num_entries() > 0: self.move_to_ray() else: self.ray_to_destination(-1) self.traverser.traverse(render) if self.ray["handler"].get_num_entries() > 0: self.move_to_ray() else: self.ray_to_destination(1) self.traverser.traverse(render) if self.ray["handler"].get_num_entries() > 0: self.move_to_ray() self.ray["node"].set_y(self.pivot, 0) current_quat = self.root.get_quat() target_quat = self.root_target.get_quat() current_quat.normalize() target_quat.normalize() if current_quat.is_same_direction(target_quat): self.root.set_quat(target_quat) return self.root.set_quat(current_quat + (target_quat - current_quat) * (ROTATION_SPEED * dt))
class Labrintth(ShowBase): def __init__(self): #from panda3d bump mapping demo # Configure the parallax mapping settings (these are just the defaults) loadPrcFileData("", "parallax-mapping-samples 3\n" "parallax-mapping-scale 0.1") # Initialize the ShowBase class from which we inherit, which will # create a window and set up everything we need for rendering into it. ShowBase.__init__(self) #titles from panda3d bumb mapping demo # Check video card capabilities. if not self.win.getGsg().getSupportsBasicShaders(): addTitle("Bump Mapping: " "Video driver reports that Cg shaders are not supported.") return self.playerHealth = 10 self.loseHealth = True brickTexture = loader.loadTexture("models/brick-c.jpg") self.lstWalls = makeMaze() self.ptGrid = makePointGrid() #self.camera.setPos(self.ptGrid[len(self.ptGrid)-1][0]) self.focus = LVector3(0,1000,30) for row in range(len(self.ptGrid)): for col in range(len(self.ptGrid[row])): for n in range(2): #wall model made by 'TheCreator', https://free3d.com/3d-model/brick-wall-51172.html self.wall = loader.loadModel("models/walls") self.wall.setTexture(brickTexture) self.wall.setScale(25.3) self.wall.setPos(self.ptGrid[row][col]) self.wallC = self.wall.find("**/collideWall") self.wallC.node().setIntoCollideMask(BitMask32.bit(0)) self.wallC.show() #right of cell if n == 0: if self.lstWalls[row][col][n] == 1: self.wall.setX(self.wall, 1.05) self.wall.setZ(5) self.wall.reparentTo(render) #down of cell else: if self.lstWalls[row][col][n] == 1: self.wall.setHpr(90,0,0) self.wall.setX(self.wall, -3) self.wall.setZ(5) self.wall.reparentTo(render) self.spacing = self.ptGrid[0][1][0] - self.ptGrid[0][0][0] self.exit = loader.loadModel('models/walls') self.exit.setScale(25.3) self.exit.setColorScale(0.6,0.6,1,1) self.exitCoord, wall = generateDoor(self.ptGrid, self.lstWalls) self.exitPos = self.ptGrid[self.exitCoord[0]][self.exitCoord[1]] self.exit.setPos(self.exitPos) for n in range(2): if n == 0: if wall[n] == 2: self.exit.setX(self.exit, 1.15) self.exit.setZ(5) #down of cell else: if wall[n] == 2: self.exit.setHpr(90,0,0) self.exit.setX(self.exit, -3.2) self.exit.setZ(5) self.exit.reparentTo(render) self.exit.detachNode() numPpl = 15 #make first pos minotaur pplLst = [] pplLst = generatePosLst(pplLst, numPpl, self.ptGrid, self.spacing) targetPos = pplLst[random.randint(1, len(pplLst)-1)] print('target:',targetPos) #ball model from panda3D ball in maze demo num = 0 for pos in range(1, len(pplLst)): num += 1 #if the person in the list is the minotaur's target, set it's #name to self.target if pplLst[pos] == targetPos: self.target = loader.loadModel("models/alfred") self.target.setPos(pplLst[pos]) self.target.setScale(5) self.target.reparentTo(render) self.targetK = self.target.attachNewNode(CollisionNode('targetDie')) self.targetK.node().addSolid(CollisionSphere(0, 0, 5, 6)) self.targetK.node().setIntoCollideMask(BitMask32.bit(2)) #self.targetK.show() self.targetS = self.target.attachNewNode(CollisionNode('targetSave')) self.targetS.node().addSolid(CollisionSphere(0, 0, 5, 6)) self.targetS.node().setIntoCollideMask(BitMask32.bit(1)) #self.targetS.show() #otherwise, keep it as a self.ppl else: self.ppl = loader.loadModel("models/alfred") self.ppl.setPos(pplLst[pos]) self.ppl.setScale(5) #put a tag on each person so we can tell them apart self.ppl.setTag('personNum', str(num)) self.ppl.reparentTo(render) #make it so that the people can be collision detected self.pplC = self.ppl.attachNewNode(CollisionNode('pplCollision')) self.pplC.node().addSolid(CollisionSphere(0, 0, 5, 7)) self.pplC.node().setIntoCollideMask(BitMask32.bit(1)) self.pplC.setTag('personCNum', str(num)) #self.pplC.show() #first index in pplLst = minotar minotaurPos = pplLst[0] #Minotaur 3d model by 'Clint Bellanger' https://opengameart.org/content/minotaur self.minotaur = Actor("models/TheMinotaur") self.minotaur.setPos(minotaurPos) self.minotaur.setScale(25) self.minotaur.reparentTo(render) minotaurTexture = loader.loadTexture("models/catfur.jpg") self.minotaur.setTexture(minotaurTexture) self.minC = self.minotaur.attachNewNode(CollisionNode('minCollision')) self.minC.node().addSolid(CollisionSphere(0 , 0, 1, 0.5)) self.minC.node().setFromCollideMask(BitMask32.bit(2)) #self.minC.node().setIntoCollideMask(BitMask32.bit(1)) #self.minC.show() self.minBat = self.minotaur.attachNewNode(CollisionNode('battle')) self.minBat.node().addSolid(CollisionSphere(0, 0, 1, 4.3)) self.minBat.node().setIntoCollideMask(BitMask32.bit(1)) self.minBat.show() self.minHit = self.minotaur.attachNewNode(CollisionNode('hit')) self.minHit.node().addSolid(CollisionSphere(0, 0, 1.5, 0.4)) self.minHit.node().setIntoCollideMask(BitMask32.bit(3)) self.minAtk = self.minotaur.attachNewNode(CollisionNode('attack')) self.minAtk.node().addSolid(CollisionSphere(0, 0, 1.85, 0.4)) self.minAtk.node().setIntoCollideMask(BitMask32.bit(4)) self.minAtk.show() self.cTrav = CollisionTraverser() self.cHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.minC, self.cHandler) self.cTrav.addCollider(self.minAtk, self.cHandler) self.minotaurObj = Minotaur(self.minotaur.getPos()) #find the path the minotaur takes to kill the person self.killPath = self.minotaurObj.findPerson(minotaurPos, targetPos, self.ptGrid, self.lstWalls, self.spacing) self.minSpeed = 1 self.sleepDelay = 13 self.killFlag = True self.killDone = False self.killPause = False self.killPaused = False self.hit = False self.hurtTime = 0 self.minDied = False self.minTimeDied = 0 self.attackTrig = False self.warningTrig = True taskMgr.add(self.minotaurKilling, "minotaurKilling") taskMgr.add(self.minotaurDie, 'minotaurDied') taskMgr.add(self.pauseKill, 'pauseKill') self.saved = 0 self.addSaved = False self.saveTrig = False taskMgr.add(self.displaySaved, "saved") #collisions #create a ball for collisions self.camBall = loader.loadModel("models/ball") self.camBall.setPos(self.spacing/2, self.spacing/2, -1) self.camBall.setScale(10) #self.camBall.setColorScale(0, 1, 0.7, 1) self.camBall.reparentTo(render) self.playerPos = (0,0,0) self.camBallC = self.camBall.find("**/ball") #self.camBallC = self.camBall.attachNewNode(CollisionNode('camCollision')) #self.camBallC.node().addSolid(CollisionSphere(0, 0, 0, 1.2)) self.camBallC.node().setFromCollideMask(BitMask32.bit(0)) self.pusher = CollisionHandlerPusher() self.pusher.addCollider(self.camBallC, self.camBall) self.cTrav.addCollider(self.camBallC, self.pusher) self.camBallC.setPos(0,0.0002,0) self.camC = self.camera.attachNewNode(CollisionNode('cameraCollision')) self.camC.node().addSolid(CollisionSphere(0, 0, 0, 0.01)) self.camC.node().setFromCollideMask(BitMask32.bit(1)) self.camC.node().setIntoCollideMask(BitMask32.bit(4)) self.camC.show() self.cTrav.addCollider(self.camC, self.cHandler) taskMgr.add(self.disappear, "disappear") #sting-sword model created by KangaroOz 3D, from: https://www.turbosquid.com/FullPreview/Index.cfm/ID/1125944 self.sword = loader.loadModel('models/sting-sword') self.sword.wrtReparentTo(self.camBall) self.sword.detachNode() self.sword.setY(1) self.sword.setX(0.5) self.sword.setZ(0.2) self.sword.setScale(0.07) self.swordC = self.sword.attachNewNode(CollisionNode('stab')) self.swordC.node().addSolid(CollisionSphere(0, 25, 0, 2)) self.swordC.node().setFromCollideMask(BitMask32.bit(3)) #self.swordC.show() self.attacked = False self.stabbing = False self.cTrav.add_collider(self.swordC, self.cHandler) swordTexture = loader.loadTexture("models/Sting_Emissive.png") self.sword.setTexture(swordTexture) self.stabDelay = 0 self.redBarL = loader.loadModel('models/walls') self.redBarL.setScale(3) self.redBarL.setColorScale(250,0,0,1) self.redBarL.setPos(38.5, 60,5) self.redBarL.wrtReparentTo(self.camBall) self.redBarL.detachNode() self.redBarR = loader.loadModel('models/walls') self.redBarR.setScale(3) self.redBarR.setColorScale(250,0,0,1) self.redBarR.setPos(55.7,60,5) self.redBarR.wrtReparentTo(self.camBall) self.redBarR.detachNode() self.hurtTimeP = 0 #make list of controls self.keyMap = { "left": 0, "right": 0, "forward": 0, "backward": 0, "cam-left": 0,\ "cam-right": 0, "stab":0, "map": 0, "instrMode":0, "gameMode":0} # Make the mouse invisible, turn off normal mouse controls self.disableMouse() props = WindowProperties() props.setCursorHidden(True) # Set the current viewing target self.heading = 0 self.pitch = 0 self.last = 0 self.startMode = True self.startTrig = True self.startDestroy = True self.instrMode = False self.instrTrig = True self.instrShown = False self.instrDestroy = True self.gameMode = False self.bkGround = loader.loadModel('models/walls') self.bkGround.reparentTo(self.camBall) self.bkGround.setColorScale(0,0.5,0.55,1) self.bkGround.setPos(-1,3,0) taskMgr.add(self.showStart, "start-screen") taskMgr.add(self.controlCamera, "camera-task") self.accept("escape", sys.exit, [0]) self.accept("o", self.setKey, ["instrMode", True]) self.accept("p", self.setKey, ["gameMode", True]) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["backward", True]) self.accept("w", self.setKey, ["stab", True]) self.accept("s", self.setKey, ["down", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["backward", False]) self.accept("w-up", self.setKey, ["stab", False]) self.accept("s-up", self.setKey, ["down", False]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("d", self.setKey, ["cam-right", True]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("d-up", self.setKey, ["cam-right", False]) self.accept("space", self.setKey, ["map", True]) self.accept("space-up", self.setKey, ["map", False]) def setKey(self, key, value): self.keyMap[key] = value def startKill(self): try: self.target.setColorScale(250,0,0,1) except: pass intervalL = self.intervalLst() self.kill = Sequence(intervalL[0], name = 'kill person') for i in range(1, len(intervalL)): self.kill.append(intervalL[i]) self.kill.start() def pauseKill(self, task): print(self.killPaused, self.killPause) if self.gameMode == True: if self.killDone == True: self.kill.finish() if self.killPaused == False and self.killPause == True: print('hmmmmmmmmmmmm') self.kill.pause() self.killPaused = True if self.killPaused == True and self.killPause == False: self.kill.resume() self.killPaused = False return Task.cont def intervalLst(self): intervals = [] for i in range(len(self.killPath)): intervals += [self.minotaur.posInterval(self.minSpeed, Point3(self.killPath[i]))] return intervals def stab(self, task): timer = globalClock.getFrameTime() speed = 0.2 stabIn = LerpPosInterval(self.sword, speed, Point3(0,4,0.2)) stabOut = LerpPosInterval(self.sword, speed, Point3(0.5,1,0.2)) stab = Sequence(stabIn, stabOut, name = 'stab') stab.start() self.stabbing = True if self.hit == True: self.minotaurObj.hit() self.hurtTime = globalClock.getFrameTime() self.hurtShow(task) self.hit = False self.stabbing = False def hurtShow(self, task): if self.hit == True: timer = globalClock.getFrameTime() if timer - self.hurtTime < 2: self.minotaur.setColorScale(180,0,0,1) return Task.cont def attack(self, task): if self.attackTrig == True: speed = 1 turnSpeed = 0.5 attackPos, distance, direction = self.minotaurObj.attack(self.camera.getPos(), self.minotaur.getPos(), \ self.ptGrid, self.spacing, self.lstWalls) origPt = self.minotaur.getPos() if attackPos != None: attack = LerpPosInterval(self.minotaur, speed, Point3(attackPos)) turnToPlayer = LerpHprInterval(self.minotaur, turnSpeed, self.minotaurObj.facePlayer(direction)) if distance == 'short': retract = LerpPosInterval(self.minotaur, speed, origPt) attackSeq = Sequence(turnToPlayer, attack, retract, name = 'attack') attackSeq.start() else: attackSeq = Sequence(turnToPlayer, attack, name = 'attack') attackSeq.start() self.attackTrig = False def attackShow(self, task): if self.attacked == True: timer = globalClock.getFrameTime() if timer - self.hurtTimeP < 2: self.redBarL.reparentTo(self.camBall) self.redBarR.reparentTo(self.camBall) if self.loseHealth == True: self.playerHealth -= 10 print(self.playerHealth) self.loseHealth = False return Task.cont def displaySaved(self, task): if self.gameMode == True: if self.saved == 0 and self.saveTrig == False: msg = 'Saved: ' + str(self.saved) self.displSaved = OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, shadow=(0, 0, 0, 1),parent=base.a2dTopLeft, align=TextNode.ALeft, pos=(0.05, -1.9)) self.saveTrig = True if self.addSaved == True and self.saveTrig == True and self.saved > 0: print('here') self.displSaved.destroy() msg = 'Saved: ' + str(self.saved) self.displSaved = OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=.05, shadow=(0, 0, 0, 1),parent=base.a2dTopLeft, align=TextNode.ALeft, pos=(0.05, -1.9)) self.addSaved = False return Task.cont def minotaurKilling(self, task): if self.gameMode == True: #timer for minotaur sleeping timer = globalClock.getFrameTime() timeLeft = self.sleepDelay - int(timer) #put up the instructions before the minotaur wakes up if timeLeft >= 0: if self.warningTrig == True: if timeLeft == 1: msg = "You have 1 second to find the Minotaur before he wakes up!" elif timeLeft == 0: msg = "He's awake!" else: msg = "You have " + str(timeLeft) + " seconds to find the Minotaur before he wakes up!" self.warning = OnscreenText(text=msg, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.a2dTopLeft, align=TextNode.ALeft, pos=(0.05, -0.08)) self.displayWarning = timer self.warningTrig = False if timer - self.displayWarning > 0.92: self.warning.destroy() self.warningTrig = True if timeLeft < 0: self.warning.destroy() #wake the minotaur up after time ends if timeLeft < 0 and self.killFlag == True: self.startKill() self.killFlag = False #make sure minotaur is always the right color if timer - self.hurtTime > 2: self.minotaur.setColorScaleOff() if timer - self.hurtTimeP > 2: self.redBarL.detachNode() self.redBarR.detachNode() if self.minDied == True: if timer - self.minTimeDied > 20: self.minotaur.detachNode() self.exit.reparentTo(render) self.showRopes() if timer - self.hurtTimeP > 2: self.loseHealth = True return Task.cont def minotaurDie(self, task): if self.minotaurObj.health <= 0 and self.minDied == False: dieSpeed = 3 die = LerpHprInterval(self.minotaur, dieSpeed, (self.camera.getX(), 90, 5)) die.start() self.minDied = True self.minTimeDied = globalClock.getDt() return Task.cont def showropes(self): ropeHeight = 30 lastRope = findExitPos(self.exitPos, self.ptGrid, self.spacing) playerPos = findPlayerPos(self.camera.getPos(), self.spacing) playerPos = (playerPos[0], playerPos[1], ropeHeight) ropePath, ropeDir = findExit(playerPos, lastRope, self.ptGrid, self.lstWalls, self.spacing) lastDir = findDir(self.exitCoord, self.ptGrid) if ropeDir != None: ropeDir += [lastDir] #gold texture found at https://www.google.com/url?sa=i&source=images&cd=&cad=r\ #ja&uact=8&ved=2ahUKEwjw76Lbv4rfAhVpvFkKHY3JDPYQjRx6BAgBEAU&url=http%3A%2F\ #%2Fmetal.graphics%2Fgraphic%2Fbrushed%2Fgold-texture%2F&psig=AOvVaw1q3Yu\ #xuuGvetU4fhA1cIqh&ust=1544161415078294 goldTexture = loader.loadTexture("models/gold.jpg") if ropePath != None: #rope model by Robert J. Smith, https://www.cgtrader.com/free-3d-models/industrial/tool/knot-rope for pos in range(len(ropePath)): rope = loader.loadModel("models/rope1") rope.setPos(ropePath[pos]) rope.setScale(3.6) rope.setZ(-5) if pos < len(ropeDir): if ropeDir[pos] == 'e': rope.setHpr(76,0,0) rope.setX(rope.getX() + 75) rope.setY(rope.getY() + 5) if ropeDir[pos] == 'n': rope.setHpr(166,0,0) rope.setY(rope.getY() + 65) if ropeDir[pos] == 'w': rope.setHpr(256,0,0) rope.setX(rope.getX() - 60) rope.setY(rope.getY() - 9) if ropeDir[pos] == 's': rope.setHpr(-14,0,0) rope.setY(rope.getY() - 70) rope.setX(rope.getX() + 15) rope.setTexture(goldTexture) rope.reparentTo(render) def lostGame(self): lose = loader.loadModel('models/walls') lose.reparentTo(self.camBall) lose.setColorScale(0.1,0.1,0.1,1) lose.setPos(-1,3,0) def showStart(self, task): startMsg = "Welcome to Daedalus's Creation." startMsg2 = "You have to kill the minotaur, navegate the labyrinth, and save the others."+"You have 30 seconds to find the Minotaur before he wakes up and starts killing your people." startMsg3 = "Good luck doing what no man has done before." startMsg4 = "Press 'o' for instructions on how to play." if self.startMode == True and self.startTrig == True: self.start = OnscreenText(text=startMsg, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, pos = (0,0.5), align=TextNode.ACenter, wordwrap= 15) self.start2 = OnscreenText(text=startMsg2, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,0.2), wordwrap= 20) self.start3 = OnscreenText(text=startMsg3, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,-0.2), wordwrap= 25) self.start4 = OnscreenText(text=startMsg4, style=1, fg=(1, 1, 1, 1), scale=0.1, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,-0.5), wordwrap= 25) self.startTrig = False if self.startMode == False and self.startDestroy == True: self.start.destroy() self.start2.destroy() self.start3.destroy() self.start4.destroy() self.startDestroy = False task.remove() return Task.cont def showInstr(self): instrMsg = "Press the right and left arrow keys to move right and left." instrMsg1 = "Press the up and down arrow keys to move forwards and backwards." instrMsg2 = "Press the 'a' key to turn the camera left." instrMsg3 = "Press the 'd' key to turn the camera right." instrMsg4 = "Press the space key for a map of the labyrinth" instrMsg5 = "Press the 'w' key to attack." instrMsg6 = "Press the 'p' key to start the game." if self.instrMode == True and self.instrTrig == True: self.instr = OnscreenText(text=instrMsg, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, pos = (0,0.7), align=TextNode.ACenter, wordwrap= 30) self.instr1 = OnscreenText(text=instrMsg1, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, pos = (0,0.5), align=TextNode.ACenter, wordwrap= 20) self.instr2 = OnscreenText(text=instrMsg2, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,0.2), wordwrap= 20) self.instr3 = OnscreenText(text=instrMsg3, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,0), wordwrap= 25) self.instr4 = OnscreenText(text=instrMsg4, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,-0.2), wordwrap= 25) self.instr5 = OnscreenText(text=instrMsg5, style=1, fg=(1, 1, 1, 1), scale=0.08, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,-0.4), wordwrap= 25) self.instr6 = OnscreenText(text=instrMsg6, style=1, fg=(1, 1, 1, 1), scale=0.1, shadow=(0, 0, 0, 1),parent=base.aspect2d, align=TextNode.ACenter, pos = (0,-0.6), wordwrap= 25) self.instrTrig = False self.instrShown = True def endInstr(self): if self.instrMode == False and self.instrDestroy == True and self.instrShown == True: self.instr.destroy() self.instr1.destroy() self.instr2.destroy() self.instr3.destroy() self.instr4.destroy() self.instr5.destroy() self.instr6.destroy() self.instrDestroy = False #make the person disappear when it's touched def disappear(self,task): for i in range(self.cHandler.getNumEntries()): entry = self.cHandler.getEntry(i) fromNode = entry.getFromNodePath() intoNode = entry.getIntoNodePath() intoName = entry.getIntoNode().getName() fromName = entry.getFromNode().getName() #if the collision was with a person if (intoName == 'pplCollision' and fromName == 'cameraCollision'): num = intoNode.getTag("personCNum") tag = '**/=personNum=' + num #finds the person player collided into and removes them person = render.find(tag) person.removeNode() #add one to saved counter self.saved += 1 self.addSaved = True self.displSaved.destroy() return Task.cont if intoName == 'targetSave' and fromName == 'cameraCollision': print('saved!') self.target.removeNode() self.saved += 1 self.addSaved = True return Task.cont if intoName == 'targetDie' and fromName == 'minCollision': print('ahhhhhhhhh!') self.target.removeNode() self.killDone = True return Task.cont #if minotaur gets stabbed by player if intoName == 'hit' and fromName == 'stab': if self.stabbing == True: self.hit = True return Task.cont if intoName == 'cameraCollision' and fromName == 'attack': print('attacked!!!!!') self.attacked = True self.hurtTimeP = globalClock.getFrameTime() self.attackShow(task) if self.playerHealth < 0: self.lostGame() return Task.cont if (intoName == 'battle' and fromName == 'cameraCollision' and self.killFlag == False) \ or (intoName == 'minCollision' and fromName == 'cameraCollision' and self.killFlag == False): print('pause') self.killPause = True return Task.cont else: print('hereeeee') self.killPause = False print('killPause', self.killPause) return Task.cont #structure from panda3d bump mapping demo def controlCamera(self, task): dt = globalClock.getDt() self.camera.setHpr(self.heading, self.pitch, 0) self.camBall.setHpr(self.heading, self.pitch, 0) dir = self.camera.getMat().getRow3(1) camPos = (self.camBall.getPos()[0], self.camBall.getPos()[1], 6) self.camera.setPos(camPos) if self.keyMap["instrMode"] == True: self.startMode = False self.instrMode = True self.showInstr() if self.keyMap["gameMode"] == True: self.instrMode = False self.gameMode = True self.bkGround.detachNode() self.endInstr() if self.gameMode == True: #move camera right and left if self.keyMap["left"]: self.camBall.setX(self.camBall, -10*dt) if self.keyMap["right"]: self.camBall.setX(self.camBall, +10*dt) if self.keyMap["forward"]: self.camBall.setY(self.camBall, +10*dt) if self.keyMap["backward"]: self.camBall.setY(self.camBall, -10*dt) #rotate camera if self.keyMap["cam-right"]: self.heading = self.heading - 100*dt if self.keyMap["cam-left"]: self.heading = self.heading + 100*dt self.focus = self.camera.getPos() + (dir * 5) self.last = task.time if self.keyMap['stab']: timer = globalClock.getFrameTime() if timer - self.stabDelay > 0.5: self.stab(task) self.stabDelay = timer self.attack(task) self.attackTrig = True #show map if self.keyMap["map"] == True: center = (len(self.ptGrid))/2*self.spacing self.camera.setPos(center,center, 1.8*len(self.ptGrid)*self.spacing) self.camera.lookAt(center, center, 0) self.sword.detachNode() if self.killFlag == False and self.killPause == True: self.kill.pause() self.camBall.reparentTo(render) self.camBall.setScale(20) self.camBall.setColorScale(0, 0.5, 1, 1) if self.keyMap["map"] == False and self.startMode == False and self.instrMode == False: self.sword.reparentTo(self.camBall) self.camBall.setColorScaleOff() self.camBall.setScale(10) return Task.cont
class RoamingRalphDemo(ShowBase): def __init__(self): # Set up the window, camera, etc. ShowBase.__init__(self) #self.setupCD() # Set the background color to black # self.win.setClearColor((0.6, 0.6, 1.0, 1.0)) # self.fog = Fog('myFog') # self.fog.setColor(0, 0, 0) # self.fog.setExpDensity(.05) # render.setFog(self.fog) # This is used to store which keys are currently pressed. self.keyMap = { "left": 0, "right": 0, "forward": 0, "cam-left": 0, "cam-right": 0, "c":0, "back":0, "space":0} # Post the instructions #self.title = addTitle( # "Panda3D Tutorial: Roaming Ralph (Walking on Uneven Terrain)") self.inst1 = addInstructions(0.06, "[ESC]: Quit") self.inst2 = addInstructions(0.12, "[Left Arrow]: Rotate Ralph Left") self.inst3 = addInstructions(0.18, "[Right Arrow]: Rotate Ralph Right") self.inst4 = addInstructions(0.24, "[Up Arrow]: Run Ralph Forward") #self.inst6 = addInstructions(0.30, "[A]: Rotate Camera Left") #self.inst7 = addInstructions(0.36, "[S]: Rotate Camera Right") # Set up the environment # # This environment model contains collision meshes. If you look # in the egg file, you will see the following: # # <Collide> { Polyset keep descend } # # This tag causes the following mesh to be converted to a collision # mesh -- a mesh which is optimized for collision, not rendering. # It also keeps the original mesh, so there are now two copies --- # one optimized for rendering, one for collisions. self.environ = loader.loadModel("models/world") #self.environ.reparentTo(render) self.room = loader.loadModel("models/room2.egg") self.room.reparentTo(render) #self.room.setScale(.1) self.room.setPos(0,0,-5) self.room.setShaderAuto() #self.room.writeBamFile("myRoom1.bam") #self.room.setColor(1,.3,.3,1) self.room2 = loader.loadModel("models/abstractroom2") self.room2.reparentTo(render) self.room2.setScale(.1) self.room2.setPos(-12,0,0) # Create the main character, Ralph #ralphStartPos = LVecBase3F(0,0,0) #self.room.find("**/start_point").getPos() self.ralph = Actor("models/ralph", {"run": "models/ralph-run", "walk": "models/ralph-walk"}) self.ralph.reparentTo(render) self.ralph.setScale(.2) self.ralph.setPos(0,0,0) #cs = CollisionSphere(0, 0, 0, 1) #cnodePath = self.ralph.attachNewNode(CollisionNode('cnode')) #cnodePath.node().addSolid(cs) #cnodePath.node().setPos(0,0,0) #cnodePath.show() self.gianteye = loader.loadModel("models/gianteye") self.gianteye.reparentTo(render) self.gianteye.setScale(.1) self.gianteye.setPos(10,10,0) #self.bluefinal = loader.loadModel("models/chrysalis") #self.bluefinal.reparentTo(render) #self.bluefinal.setScale(.1) #self.bluefinal.setPos(7,7,0) #self.blue = loader.loadModel("models/blue1") #self.blue.reparentTo(render) #self.blue.setScale(.1) #self.blue.setPos(10,5,0) self.chik = loader.loadModel("models/chik") self.chik.reparentTo(render) self.chik.setScale(.1) self.chik.setPos(3,13,0) self.pawn = loader.loadModel("pawn") self.pawn.reparentTo(render) self.pawn.setPos(0,0,0) self.shot = loader.loadModel("models/icosphere.egg") self.shot.reparentTo(render) self.shot.setScale(.5) self.shot.setPos(0,0,1) self.shot.setColor(1,.3,.3,1) self.myShot = loader.loadModel("models/icosphere.egg") #self.myShot.reparentTo(render) self.myShot.setScale(.1) self.myShot.setPos(0,0,1) self.myShotVec = LVector3(0,0,0) self.lightpivot3 = render.attachNewNode("lightpivot3") self.lightpivot3.setPos(0, 0, 0) self.lightpivot3.hprInterval(10, LPoint3(0, 0, 0)).loop() plight3 = PointLight('plight2') plight3.setColor((0, .3,0, 1)) plight3.setAttenuation(LVector3(0.7, 0.05, 0)) plnp3 = self.lightpivot3.attachNewNode(plight3) plnp3.setPos(0, 0, 0) self.room2.setLight(plnp3) self.room.setLight(plnp3) sphere3 = loader.loadModel("models/icosphere") sphere3.reparentTo(plnp3) sphere3.setScale(0.1) sphere3.setColor((0,1,0,1)) # Create a floater object, which floats 2 units above ralph. We # use this as a target for the camera to look at. self.floater = NodePath(PandaNode("floater")) self.floater.reparentTo(self.ralph) self.floater.setZ(8.0) # Accept the control keys for movement and rotation self.accept("escape", sys.exit) self.accept("arrow_left", self.setKey, ["left", True]) self.accept("arrow_right", self.setKey, ["right", True]) self.accept("arrow_up", self.setKey, ["forward", True]) self.accept("arrow_down", self.setKey, ["back", True]) self.accept("a", self.setKey, ["cam-left", True]) self.accept("s", self.setKey, ["cam-right", True]) self.accept("arrow_left-up", self.setKey, ["left", False]) self.accept("arrow_right-up", self.setKey, ["right", False]) self.accept("arrow_up-up", self.setKey, ["forward", False]) self.accept("arrow_down-up", self.setKey, ["back", False]) self.accept("a-up", self.setKey, ["cam-left", False]) self.accept("s-up", self.setKey, ["cam-right", False]) self.accept("space", self.setKey, ["space", True]) self.accept("space-up", self.setKey, ["space", False]) self.accept("c",self.setKey,["c",True]) self.accept("c-up",self.setKey,["c",False]) taskMgr.add(self.move, "moveTask") # Game state variables self.isMoving = False self.jumping = False self.vz = 0 # Set up the camera self.disableMouse() self.camera.setPos(self.ralph.getX(), self.ralph.getY() + 7, 3) self.camLens.setFov(60) # We will detect the height of the terrain by creating a collision # ray and casting it downward toward the terrain. One ray will # start above ralph's head, and the other will start above the camera. # A ray may hit the terrain, or it may hit a rock or a tree. If it # hits the terrain, we can detect the height. If it hits anything # else, we rule that the move is illegal. def setupCollision(self): cs = CollisionSphere(0,0,2,1) cnodePath = self.ralph.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(cs) cnodePath.show() #for o in self.OBS: #ct = CollisionTube(0,0,0, 0,0,1, 0.5) #cn = o.attachNewNode(CollisionNode('ocnode')) #cn.node().addSolid(ct) #cn.show() eyecs = CollisionSphere(0,0,4,5) cnodePath = self.gianteye.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(eyecs) cnodePath.show() eyecs = CollisionSphere(0,0,4,2) cnodePath = self.chik.attachNewNode(CollisionNode('cnode')) cnodePath.node().addSolid(eyecs) cnodePath.show() pusher = CollisionHandlerPusher() pusher.addCollider(cnodePath, self.player) self.cTrav = CollisionTraverser() self.cTrav.add_collider(cnodePath,pusher) self.cTrav.showCollisions(render) self.walls = self.room2.find("**/wall_collide") #self.walls.node().setIntoCollideMask(BitMask32.bit(0)) self.cTrav = CollisionTraverser() self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 9) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(CollideMask.bit(0)) self.ralphGroundCol.setIntoCollideMask(CollideMask.allOff()) self.ralphGroundColNp = self.ralph.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) self.camGroundRay = CollisionRay() self.camGroundRay.setOrigin(0, 0, 9) self.camGroundRay.setDirection(0, 0, -1) self.camGroundCol = CollisionNode('camRay') self.camGroundCol.addSolid(self.camGroundRay) self.camGroundCol.setFromCollideMask(CollideMask.bit(0)) self.camGroundCol.setIntoCollideMask(CollideMask.allOff()) self.camGroundColNp = self.camera.attachNewNode(self.camGroundCol) self.camGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.camGroundColNp, self.camGroundHandler) self.sphere = CollisionSphere(0,0,4,2) self.sphere2 = CollisionSphere(0,0,2,2) self.cnodePath = self.ralph.attachNewNode((CollisionNode('cnode'))) self.cnodePath.node().addSolid(self.sphere) self.cnodePath.node().addSolid(self.sphere2) self.cnodePath.show() self.pusher = CollisionHandlerPusher() self.pusher.addCollider(self.cnodePath, self.ralph) self.cTrav.add_collider(self.cnodePath, self.pusher) self.eyecs = CollisionSphere(0,0,22,25) self.cnodePath1 = self.gianteye.attachNewNode(CollisionNode('cnode')) self.cnodePath1.node().addSolid(self.eyecs) self.cnodePath1.show() self.pusher1 = CollisionHandlerPusher() self.pusher1.addCollider(self.cnodePath1, self.gianteye) self.cTrav.add_collider(self.cnodePath1, self.pusher1) self.cTrav.showCollisions(render) self.eyeGroundRay = CollisionRay() self.eyeGroundRay.setOrigin(0, 0, 9) self.eyeGroundRay.setDirection(0, 0, -1) self.eyeGroundCol = CollisionNode('eyeRay') self.eyeGroundCol.addSolid(self.eyeGroundRay) self.eyeGroundCol.setFromCollideMask(CollideMask.bit(0)) self.eyeGroundCol.setIntoCollideMask(CollideMask.allOff()) self.eyeGroundColNp = self.gianteye.attachNewNode(self.eyeGroundCol) self.eyeGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.eyeGroundColNp, self.eyeGroundHandler) self.chikcs = CollisionSphere(0,0,11,20) self.cnodePath2 = self.chik.attachNewNode(CollisionNode('cnode')) self.cnodePath2.node().addSolid(self.chikcs) self.cnodePath2.show() self.pusher2 = CollisionHandlerPusher() self.pusher2.addCollider(self.cnodePath, self.chik) self.cTrav.add_collider(self.cnodePath, self.pusher2) self.cTrav.showCollisions(render) self.chikGroundRay = CollisionRay() self.chikGroundRay.setOrigin(0, 0, 9) self.chikGroundRay.setDirection(0, 0, -1) self.chikGroundCol = CollisionNode('chikRay') self.chikGroundCol.addSolid(self.chikGroundRay) self.chikGroundCol.setFromCollideMask(CollideMask.bit(0)) self.chikGroundCol.setIntoCollideMask(CollideMask.allOff()) self.chikGroundColNp = self.chik.attachNewNode(self.chikGroundCol) self.chikGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.chikGroundColNp, self.chikGroundHandler) # Uncomment this line to see the collision rays self.ralphGroundColNp.show() self.camGroundColNp.show() #self.ralphroom1ColNp.show() # Uncomment this line to show a visual representation of the # collisions occuring #self.cTrav.showCollisions(render) # Create some lighting ambientLight = AmbientLight("ambientLight") ambientLight.setColor((.3, .3, .3, .4)) ambientLight2 = AmbientLight("ambientLight2") ambientLight2.setColor((1, 1, 1, 10)) directionalLight = DirectionalLight("directionalLight") directionalLight.setDirection((0, 0, -2)) directionalLight.setColor((1, 1, 1, 1)) directionalLight.setSpecularColor((1, 1, 1, 1)) render.setLight(render.attachNewNode(ambientLight)) #self.environ.setLight(self.environ.attachNewNode(ambientLight2)) render.setLight(render.attachNewNode(directionalLight)) # Add a light to the scene. self.lightpivot = render.attachNewNode("lightpivot") self.lightpivot.setPos(0, 0, 1.6) self.lightpivot.hprInterval(20, LPoint3(360, 0, 0)).loop() plight = PointLight('plight') plight.setColor((.7, .3, 0, 1)) plight.setAttenuation(LVector3(0.7, 0.05, 0)) plnp = self.lightpivot.attachNewNode(plight) plnp.setPos(5, 0, 0) self.room.setLight(plnp) sphere = loader.loadModel("models/icosphere") sphere.reparentTo(plnp) sphere.setScale(0.1) sphere.setColor((1,1,0,1)) self.lightpivot2 = render.attachNewNode("lightpivot") self.lightpivot2.setPos(-16, 0, 1.6) self.lightpivot2.hprInterval(20, LPoint3(360, 0, 0)).loop() plight2 = PointLight('plight2') plight2.setColor((0, .4,.8, 1)) plight2.setAttenuation(LVector3(0.7, 0.05, 0)) plnp2 = self.lightpivot2.attachNewNode(plight2) plnp2.setPos(5, 0, 0) self.room2.setLight(plnp2) sphere2 = loader.loadModel("models/icosphere") sphere2.reparentTo(plnp2) sphere2.setScale(0.2) sphere2.setColor((0,0,1,1)) self.vec = LVector3(0,1,0) # Records the state of the arrow keys def setKey(self, key, value): self.keyMap[key] = value # Accepts arrow keys to move either the player or the menu cursor, # Also deals with grid checking and collision detection def move(self, task): # Get the time that elapsed since last frame. We multiply this with # the desired speed in order to find out with which distance to move # in order to achieve that desired speed. dt = globalClock.getDt() # If the camera-left key is pressed, move camera left. # If the camera-right key is pressed, move camera right. if self.keyMap["cam-left"]: self.camera.setZ(self.camera, -20 * dt) if self.keyMap["cam-right"]: self.camera.setZ(self.camera, +20 * dt) # save ralph's initial position so that we can restore it, # in case he falls off the map or runs into something. startpos = self.ralph.getPos() # If a move-key is pressed, move ralph in the specified direction. if self.keyMap["left"]: self.ralph.setH(self.ralph.getH() + 150 * dt) #self.floater.setH(self.floater.getH() + 300 * dt) self.camera.setX(self.camera, +15.5 * dt) if self.keyMap["right"]: self.ralph.setH(self.ralph.getH() - 150 * dt) self.camera.setX(self.camera, -15.5 * dt) if self.keyMap["forward"]: self.ralph.setY(self.ralph, -35 * dt) if self.keyMap["back"]: self.ralph.setY(self.ralph, +35 * dt) if self.keyMap["c"]: if self.jumping is False: #self.ralph.setH(self.ralph.getH() + 300 * dt) #self.ralph.setZ(self.ralph.getZ() + 100 * dt) self.jumping = True self.vz = 7 if self.keyMap["space"]: self.lightpivot3.setPos(self.ralph.getPos()) self.lightpivot3.setZ(self.ralph.getZ() + .5) self.lightpivot3.setX(self.ralph.getX() - .25) #self.myShot.setHpr(self.ralph.getHpr()) #parent node = NodePath("tmp") node.setHpr(self.ralph.getHpr()) vec = render.getRelativeVector(node,(0,-1,0)) self.myShotVec = vec self.lightpivot3.setPos(self.lightpivot3.getPos() + self.myShotVec * dt * 15 ) if self.jumping is True: self.vz = self.vz - 16* dt self.ralph.setZ(self.ralph.getZ() + self.vz * dt ) entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if len(entries) > 0 : if self.ralph.getZ() < 0:#entries[0].getSurfacePoint(render).getZ(): #self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) self.ralph.setZ(0) self.jumping = False # If ralph is moving, loop the run animation. # If he is standing still, stop the animation. if self.keyMap["forward"] or self.keyMap["left"] or self.keyMap["right"] or self.keyMap["c"] or self.keyMap["forward"] or self.keyMap["back"]: if self.isMoving is False: self.ralph.loop("run") self.isMoving = True else: if self.isMoving: self.ralph.stop() self.ralph.pose("walk", 5) self.isMoving = False node = NodePath("tmp") node.setHpr(self.ralph.getHpr()) vec = render.getRelativeVector(node,(1,0,0)) #self.ralph.setPos(self.ralph.getPos() + vec * dt * 20) node = NodePath("tmp") #self.pawn.getH() node.setHpr(self.pawn.getHpr()) vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.shot.setPos(self.shot.getPos() + self.vec * dt * 10 ) if self.shot.getY() < -15 or self.shot.getY() > 15 or self.shot.getX() < -15 or self.shot.getX() > 15: self.shot.setPos(self.pawn.getPos() + (0,0,0)) self.vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) self.vec = render.getRelativeVector(node,(random.random() * -0.8,random.random() + 1,0)) # If the camera is too far from ralph, move it closer. # If the camera is too close to ralph, move it farther. camvec = self.ralph.getPos() - self.camera.getPos() #camvec.setZ(self.camera.getZ()) camdist = camvec.length() x = self.camera.getZ() camvec.normalize() if camdist > 6.0: self.camera.setPos(self.camera.getPos() + camvec * (camdist - 6)) camdist = 10.0 #self.camera.setZ(self.camera, x) if camdist < 6.0: self.camera.setPos(self.camera.getPos() - camvec * (6 - camdist)) camdist = 5.0 #self.camera.setZ(self.camera, x) # Normally, we would have to call traverse() to check for collisions. # However, the class ShowBase that we inherit from has a task to do # this for us, if we assign a CollisionTraverser to self.cTrav. #self.cTrav.traverse(render) # Adjust ralph's Z coordinate. If ralph's ray hit terrain, # update his Z. If it hit anything else, or didn't hit anything, put # him back where he was last frame. entries = list(self.ralphGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) if self.jumping == False: if len(entries) > 0:# and entries[0].getIntoNode().getName() == "terrain": #self.ralph.setZ(entries[0].getSurfacePoint(render).getZ()) pass else: self.ralph.setPos(startpos) # Keep the camera at one foot above the terrain, # or two feet above ralph, whichever is greater. entries = list(self.camGroundHandler.getEntries()) entries.sort(key=lambda x: x.getSurfacePoint(render).getZ()) #if len(entries) > 0 and entries[0].getIntoNode().getName() == "ground": #self.camera.setZ(entries[0].getSurfacePoint(render).getZ() + 1.5) if self.camera.getZ() < self.ralph.getZ() + 1 or self.camera.getZ() > self.ralph.getZ() + 1: self.camera.setZ(self.ralph.getZ() + 1) #self.camera.setZ(self.ralph.getZ() + 1.5) #self.camera.setP(self.camera, 130) # The camera should look in ralph's direction, # but it should also try to stay horizontal, so look at # a floater which hovers above ralph's head. self.camera.lookAt(self.floater) return task.cont
class Picker(object): ''' A class for picking (Panda3d) objects. ''' def __init__(self, app, render, camera, mouseWatcher, pickKeyOn, pickKeyOff, collideMask, pickableTag="pickable"): self.render = render self.mouseWatcher = mouseWatcher.node() self.camera = camera self.camLens = camera.node().get_lens() self.collideMask = collideMask self.pickableTag = pickableTag self.taskMgr = app.task_mgr # setup event callback for picking body self.pickKeyOn = pickKeyOn self.pickKeyOff = pickKeyOff app.accept(self.pickKeyOn, self._pickBody, [self.pickKeyOn]) app.accept(self.pickKeyOff, self._pickBody, [self.pickKeyOff]) # collision data self.collideMask = collideMask self.cTrav = CollisionTraverser() self.collisionHandler = CollisionHandlerQueue() self.pickerRay = CollisionRay() pickerNode = CollisionNode("Utilities.pickerNode") node = NodePath("PhysicsNode") node.reparentTo(render) anp = node.attachNewNode(pickerNode) base.physicsMgr.attachPhysicalNode(pickerNode) pickerNode.add_solid(self.pickerRay) pickerNode.set_from_collide_mask(self.collideMask) pickerNode.set_into_collide_mask(BitMask32.all_off()) #pickerNode.node().getPhysicsObject().setMass(10) self.cTrav.add_collider(self.render.attach_new_node(pickerNode), self.collisionHandler) # service data self.pickedBody = None self.oldPickingDist = 0.0 self.deltaDist = 0.0 self.dragging = False self.updateTask = None def _pickBody(self, event): # handle body picking if event == self.pickKeyOn: # check mouse position if self.mouseWatcher.has_mouse(): # Get to and from pos in camera coordinates pMouse = self.mouseWatcher.get_mouse() # pFrom = LPoint3f() pTo = LPoint3f() if self.camLens.extrude(pMouse, pFrom, pTo): # Transform to global coordinates rayFromWorld = self.render.get_relative_point( self.camera, pFrom) rayToWorld = self.render.get_relative_point( self.camera, pTo) # cast a ray to detect a body # traverse downward starting at rayOrigin self.pickerRay.set_direction(rayToWorld - rayFromWorld) self.pickerRay.set_origin(rayFromWorld) self.cTrav.traverse(self.render) if self.collisionHandler.get_num_entries() > 0: self.collisionHandler.sort_entries() entry0 = self.collisionHandler.get_entry(0) hitPos = entry0.get_surface_point(self.render) # get the first parent with name pickedObject = entry0.get_into_node_path() while not pickedObject.has_tag(self.pickableTag): pickedObject = pickedObject.getParent() if not pickedObject: return if pickedObject == self.render: return # self.pickedBody = pickedObject self.oldPickingDist = (hitPos - rayFromWorld).length() self.deltaDist = ( self.pickedBody.get_pos(self.render) - hitPos) print(self.pickedBody.get_name(), hitPos) if not self.dragging: self.dragging = True # create the task for updating picked body motion self.updateTask = self.taskMgr.add( self._movePickedBody, "_movePickedBody") # set sort/priority self.updateTask.set_sort(0) self.updateTask.set_priority(0) else: if self.dragging: # remove pick body motion update task self.taskMgr.remove("_movePickedBody") self.updateTask = None self.dragging = False self.pickedBody = None def _movePickedBody(self, task): # handle picked body if any if self.pickedBody and self.dragging: # check mouse position if self.mouseWatcher.has_mouse(): # Get to and from pos in camera coordinates pMouse = self.mouseWatcher.get_mouse() # pFrom = LPoint3f() pTo = LPoint3f() if self.camLens.extrude(pMouse, pFrom, pTo): # Transform to global coordinates rayFromWorld = self.render.get_relative_point( self.camera, pFrom) rayToWorld = self.render.get_relative_point( self.camera, pTo) # keep it at the same picking distance direction = (rayToWorld - rayFromWorld).normalized() direction *= self.oldPickingDist self.pickedBody.set_pos( self.render, rayFromWorld + direction + self.deltaDist) #self.pickedBody.reparentTo(np) #self.pickedBody.setMass(10.0) # return task.cont
class Character: def __init__(self, name, model): self.name = name self.node = NodePath(name + "_character") self.node.reparent_to(render) self.model = model self.model.reparent_to(self.node) self.traverser = CollisionTraverser() self.item_ray = self.ray("item", base.itemmask, point_b=(0, 1, 1)) self.movement = Vec3(0, 0, 0) self.speed = 0.85 self.keys = "" def ray(self, name, bitmask, point_a=(0, 0, 1), point_b=(0, 0, 0)): shape = CollisionSegment(point_a, point_b) col = CollisionNode(self.node.getName() + "-ray-" + name) col.add_solid(shape) col.set_from_collide_mask(bitmask) col.set_into_collide_mask(CollideMask.all_off()) col_node = self.node.attach_new_node(col) handler = CollisionHandlerQueue() self.traverser.add_collider(col_node, handler) return { "collider": col, "shape": shape, "handler": handler, "node": col_node } def sphere(self, name, bitmask, pos=(0, 0, 1), radius=0.2): col = CollisionNode(self.node.getName() + "-sphere-" + name) shape = CollisionSphere(pos, radius) col.add_solid(shape) col.set_from_collide_mask(bitmask) col.set_into_collide_mask(CollideMask.allOff()) col_node = self.node.attachNewNode(col) handler = CollisionHandlerPusher() handler.add_collider(col_node, self.node) self.traverser.add_collider(col_node, handler) return { "collider": col, "shape": shape, "handler": handler, "node": col_node } def fall(self): if self.fall_ray["handler"].get_num_entries() > 0: self.fall_ray["handler"].sort_entries() closest_entry = list(self.fall_ray["handler"].entries)[0] collision_position = closest_entry.get_surface_point(render) self.node.set_z(collision_position.get_z()) self.movement.z = 0 else: self.movement.z -= base.dt def open_doors(self): if self.item_ray["handler"].get_num_entries() > 0: closest_entry = list(self.item_ray["handler"].entries)[0] item = closest_entry.get_into_node_path() if item.name[0] == "d": door = base.map.doors[item.name] if not door.lock or door.lock in self.keys: door.open = True
class MapPicker(): __name: Final[str] __base: Final[ShowBase] __data: Final[NDArray[(Any, Any, Any), np.uint8]] # collision data __ctrav: Final[CollisionTraverser] __cqueue: Final[CollisionHandlerQueue] __cn: Final[CollisionNode] __cnp: Final[NodePath] # picker data __pn: Final[CollisionNode] __pnp: Final[NodePath] __pray: Final[CollisionRay] # constants COLLIDE_MASK: Final[BitMask32] = BitMask32.bit(1) def __init__(self, services: Services, base: ShowBase, map_data: MapData, name: Optional[str] = None): self.__services = services self.__services.ev_manager.register_listener(self) self.__base = base self.__name = name if name is not None else (map_data.name + "_picker") self.__map = map_data self.__data = map_data.data # collision traverser & queue self.__ctrav = CollisionTraverser(self.name + '_ctrav') self.__cqueue = CollisionHandlerQueue() # collision boxes self.__cn = CollisionNode(self.name + '_cn') self.__cn.set_collide_mask(MapPicker.COLLIDE_MASK) self.__cnp = self.__map.root.attach_new_node(self.__cn) self.__ctrav.add_collider(self.__cnp, self.__cqueue) self.__points = [] z_offset = 1 if self.__map.dim == 3 else self.__map.depth for idx in np.ndindex(self.__data.shape): if bool(self.__data[idx] & MapData.TRAVERSABLE_MASK): p = Point(*idx) self.__points.append(p) idx = self.__cn.add_solid(CollisionBox(idx, Point3(p.x+1, p.y+1, p.z-z_offset))) assert idx == (len(self.__points) - 1) # mouse picker self.__pn = CollisionNode(self.name + '_pray') self.__pnp = self.__base.cam.attach_new_node(self.__pn) self.__pn.set_from_collide_mask(MapPicker.COLLIDE_MASK) self.__pray = CollisionRay() self.__pn.add_solid(self.__pray) self.__ctrav.add_collider(self.__pnp, self.__cqueue) # debug -> shows collision ray / impact point # self.__ctrav.show_collisions(self.__map.root) @property def name(self) -> str: return self.__name @property def pos(self): # check if we have access to the mouse if not self.__base.mouseWatcherNode.hasMouse(): return None # get the mouse position mpos = self.__base.mouseWatcherNode.get_mouse() # set the position of the ray based on the mouse position self.__pray.set_from_lens(self.__base.camNode, mpos.getX(), mpos.getY()) # find collisions self.__ctrav.traverse(self.__map.root) # if we have hit something sort the hits so that the closest is first if self.__cqueue.get_num_entries() == 0: return None self.__cqueue.sort_entries() # compute & return logical cube position x, y, z = self.__cqueue.get_entry(0).getSurfacePoint(self.__map.root) x, y, z = [max(math.floor(x), 0), max(math.floor(y), 0), max(math.ceil(z), 0)] if x == len(self.__data): x -= 1 if y == len(self.__data[x]): y -= 1 if z == len(self.__data[x][y]): z -= 1 return Point(x, y, z) def notify(self, event: Event) -> None: if isinstance(event, MapUpdateEvent): z_offset = 1 if self.__map.dim == 3 else self.__map.depth for p in event.updated_cells: if p.n_dim == 2: p = Point(*p, 0) if bool(self.__data[p.values] & MapData.TRAVERSABLE_MASK): self.__points.append(p) idx = self.__cn.add_solid(CollisionBox(p.values, Point3(p.x+1, p.y+1, p.z-z_offset))) assert idx == (len(self.__points) - 1) else: try: i = self.__points.index(p) except ValueError: continue self.__cn.remove_solid(i) self.__points.pop(i) def destroy(self) -> None: self.__cqueue.clearEntries() self.__ctrav.clear_colliders() self.__cnp.remove_node() self.__pnp.remove_node()