def Kampf(self,spieler,gegner): """Startet einen Kampf zwischen Spieler und Gegner, bis einer der beiden keine Energie mehr hat Usereingaben sind moeglich wie Item oder Angriff""" aktion = "angriff" aktNr = 0 pos = [0,0] keyhandler = DirectObject() keyMap = {"angriff":0,"item":0} spieler = self.werteBerechnen(spieler) DirectObject.accept(keyhandler,"a", self.setKey, ["angriff",1]) DirectObject.accept(keyhandler,"i", self.setKey, ["item",1]) if (keyMap["angriff"]!=0): aktion = angriff if (keyMap["item"]!=0): aktion = item if spieler.energie <= 0: print "Spieler tot" self.active = False return [spieler,gegner] elif gegner.energie <= 0: print "Gegner besiegt" self.active = False return [spieler,gegner] print "Spieler greift an" self.eventhandleSpieler(aktion,spieler,gegner,aktNr,pos) aktion = "angriff" print "Gegner greift an" self.eventhandleGegner(spieler,gegner) self.anzeigen(spieler,gegner) self.active = True return [spieler,gegner]
def initPlayerAbilities(self): DO = DirectObject() DO.accept('1', self.fireAbility, [1]) DO.accept('2', self.fireAbility, [2]) DO.accept('3', self.fireAbility, [3]) DO.accept('4', self.fireAbility, [4]) self.abilityDict = {'offensive':1, 'defensive':1, 'evasive':1, 'area':1}
def finish(self, pos=(0,0,0), minWidth=1): """ adjust frame for uniform width with siblings adjust pos relative to parent or older sibs attach stateGraphics """ me = self.getPythonTag("extras") item = me.item scale = me.style['font-size'] kind=item['kind'] width, height, lineHeight, fram = getTextSize(item['txt'], me.style) width = max(width,minWidth) arrowhead=.5*lineHeight padding = me.style['padding'] padding = (padding[0]*scale ,padding[1]*scale ,padding[2]*scale ,padding[3]*scale ) bevel = me.style['bevel']*scale multiLineShim = 0 if '\n' in item['txt']: multiLineShim = .04 # might need to change this to fit font NodePath(self).setPos(pos) if kind=='separator': sh = me.style['height']*scale frame=( -padding[0], width+padding[1], -sh/2, sh/2) else: frame = (fram[0],max(fram[1],fram[0]+minWidth),fram[2],fram[3]) frame=( frame[0]-padding[0], frame[1]+padding[1], frame[2]-padding[2]-multiLineShim, frame[3]+padding[3]) stateGraphics = getBackgroundSet(self, frame, arrowhead) for state in STATES: self.clearStateDef(state) # in case we've been here befor self.instanceToStateDef(state, stateGraphics[state]) DO=DirectObject() if not self.clickAccepted: #JUSTIN'S INSERTED CODE DO.accept(self.getEnterEvent(), onEnter, [me.menuRoot.menuRootID, self]) DO.accept(self.getExitEvent(), onLeave, [me.menuRoot.menuRootID, self]) if kind in ('button', 'checkBox', 'radioBTN', 'checkAllBTN', 'unCheckAllBTN', 'titleBar', 'close'): if not self.clickAccepted: #JUSTIN'S INSERTED CODE DO.accept(self.getPressEvent(MouseButton.one()), onPress, [self]) DO.accept(self.getReleaseEvent(MouseButton.one()), onRelease, [self]) # so we can simulate 'click' with messenger.send('press'+id, ['simClk']): DO.accept('press'+item['id'], onPress, [self]) if not self.clickAccepted: #JUSTIN'S INSERTED CODE self.clickAccepted=True self.setActive(True) if kind == 'parent':# accomodate arrowheads self.setFrame(frame[0], frame[1]+arrowhead*2-bevel, frame[2], frame[3]) else: self.setFrame(frame)
def setupMouseCollision(self): self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0)) DO = DirectObject() DO.accept('mouse1', self.onMouseDown) DO.accept('mouse1-up', self.onMouseUp) self.collisionHandler = CollisionHandlerQueue() self.pickerCollNode = CollisionNode('mouseRay') self.pickerNodePath = camera.attachNewNode(self.pickerCollNode) self.pickerCollNode.setIntoCollideMask(BitMask32.allOff()) self.pickerCollNode.setFromCollideMask(BitMask32.bit(1)) self.pickerRay = CollisionRay() self.pickerCollNode.addSolid(self.pickerRay) base.cTrav.addCollider(self.pickerNodePath, self.collisionHandler) taskMgr.add(self.moveTask, 'moveTask') taskMgr.add(self.attackTask, 'attackTask')
def __init__(self, mainRef): print("Player instantiated") Unit.__init__(self) FSM.__init__(self, 'playerFSM') self._hudRef = None self._mainRef = mainRef self._enemyListRef = mainRef.enemyList self._ddaHandlerRef = mainRef.DDAHandler self._mapHandlerRef = mainRef.mapHandler self._stateHandlerRef = mainRef.stateHandler self._scenarioHandlerRef = mainRef.scenarioHandler self.playerNode = mainRef.mainNode.attachNewNode('playerNode') self.initPlayerAttributes() self.initPlayerModel() self.initPlayerCamera() self.initPlayerAbilities() self.initPlayerCollisionHandlers() self.initPlayerCollisionSolids() self.initPlayerDDA() # Start mouse picking and movement self.mouseHandler = utils.MouseHandler(self) # Start player update task playerUpdateTask = taskMgr.add(self.playerUpdate, 'playerUpdateTask') # Initialize player FSM state self.request('Idle') DO = DirectObject() DO.accept('shift-mouse1', self.onShiftDown) DO.accept('shift-up', self.onShiftUp) self._shiftButtonDown = False
def setupDebugHelp(APP): def toggleOobe(): """Switch between free camera (steering with the mouse) and the camera controled by the game""" APP.oobe() def explorer(): """activates the Panda3D halp tool to explore the render Nodepath""" APP.render.explore() def analyze(): APP.render.analyze() def toggleWireframe(): """Switch between wired model view and normal view""" base.toggleWireframe() from time import strftime def takeScreenshot(): # make a screenshot path = os.path.join(Settings.settingsPath, "screenshots") if not os.path.exists(path): os.makedirs(path) # create the filename with a name and the actual time fn = "Screenshot" + strftime("_%a_%d-%m-%Y_%H:%M:%S") + ".png" path = os.path.join(path, fn) APP.win.saveScreenshot(Filename.fromOsSpecific(path)) logging.info(str.format("take Screenshot in: {0}", path)) # create a DirectObject object to handle the key input by the user directobject = DirectObject() directobject.accept("f2", analyze) directobject.accept("f3", explorer) directobject.accept("f4", toggleWireframe) directobject.accept("f5", takeScreenshot) directobject.accept("f12", toggleOobe)
class DirectEntry(DirectFrame): """ DirectEntry(parent) - Create a DirectGuiWidget which responds to keyboard buttons """ directWtext = ConfigVariableBool('direct-wtext', 1) AllowCapNamePrefixes = ("Al", "Ap", "Ben", "De", "Del", "Della", "Delle", "Der", "Di", "Du", "El", "Fitz", "La", "Las", "Le", "Les", "Lo", "Los", "Mac", "St", "Te", "Ten", "Van", "Von", ) ForceCapNamePrefixes = ("D'", "DeLa", "Dell'", "L'", "M'", "Mc", "O'", ) def __init__(self, parent = None, **kw): # Inherits from DirectFrame # A Direct Frame can have: # - A background texture (pass in path to image, or Texture Card) # - A midground geometry item (pass in geometry) # - A foreground text Node (pass in text string or Onscreen Text) # For a direct entry: # Each button has 3 states (focus, noFocus, disabled) # The same image/geom/text can be used for all three states or each # state can have a different text/geom/image # State transitions happen automatically based upon mouse interaction optiondefs = ( # Define type of DirectGuiWidget ('pgFunc', PGEntry, None), ('numStates', 3, None), ('state', DGG.NORMAL, None), ('entryFont', None, DGG.INITOPT), ('width', 10, self.setup), ('numLines', 1, self.setup), ('focus', 0, self.setFocus), ('cursorKeys', 1, self.setCursorKeysActive), ('obscured', 0, self.setObscureMode), # Setting backgroundFocus allows the entry box to get keyboard # events that are not handled by other things (i.e. events that # fall through to the background): ('backgroundFocus', 0, self.setBackgroundFocus), # Text used for the PGEntry text node # NOTE: This overrides the DirectFrame text option ('initialText', '', DGG.INITOPT), # Command to be called on hitting Enter ('command', None, None), ('extraArgs', [], None), # Command to be called when enter is hit but we fail to submit ('failedCommand', None, None), ('failedExtraArgs',[], None), # commands to be called when focus is gained or lost ('focusInCommand', None, None), ('focusInExtraArgs', [], None), ('focusOutCommand', None, None), ('focusOutExtraArgs', [], None), # Sounds to be used for button events ('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound), ('clickSound', DGG.getDefaultClickSound(), self.setClickSound), ('autoCapitalize', 0, self.autoCapitalizeFunc), ('autoCapitalizeAllowPrefixes', DirectEntry.AllowCapNamePrefixes, None), ('autoCapitalizeForcePrefixes', DirectEntry.ForceCapNamePrefixes, None), ) # Merge keyword options with default options self.defineoptions(kw, optiondefs) # Initialize superclasses DirectFrame.__init__(self, parent) if self['entryFont'] == None: font = DGG.getDefaultFont() else: font = self['entryFont'] # Create Text Node Component self.onscreenText = self.createcomponent( 'text', (), None, OnscreenText, (), parent = hidden, # Pass in empty text to avoid extra work, since its really # The PGEntry which will use the TextNode to generate geometry text = '', align = TextNode.ALeft, font = font, scale = 1, # Don't get rid of the text node mayChange = 1) # We can get rid of the node path since we're just using the # onscreenText as an easy way to access a text node as a # component self.onscreenText.removeNode() # Bind command function self.bind(DGG.ACCEPT, self.commandFunc) self.bind(DGG.ACCEPTFAILED, self.failedCommandFunc) self.accept(self.guiItem.getFocusInEvent(), self.focusInCommandFunc) self.accept(self.guiItem.getFocusOutEvent(), self.focusOutCommandFunc) # listen for auto-capitalize events on a separate object to prevent # clashing with other parts of the system self._autoCapListener = DirectObject() # Call option initialization functions self.initialiseoptions(DirectEntry) if not hasattr(self, 'autoCapitalizeAllowPrefixes'): self.autoCapitalizeAllowPrefixes = DirectEntry.AllowCapNamePrefixes if not hasattr(self, 'autoCapitalizeForcePrefixes'): self.autoCapitalizeForcePrefixes = DirectEntry.ForceCapNamePrefixes # Update TextNodes for each state for i in range(self['numStates']): self.guiItem.setTextDef(i, self.onscreenText.textNode) # Now we should call setup() again to make sure it has the # right font def. self.setup() # Update initial text self.unicodeText = 0 if self['initialText']: self.enterText(self['initialText']) def destroy(self): self.ignoreAll() self._autoCapListener.ignoreAll() DirectFrame.destroy(self) def setup(self): self.guiItem.setupMinimal(self['width'], self['numLines']) def setFocus(self): PGEntry.setFocus(self.guiItem, self['focus']) def setCursorKeysActive(self): PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys']) def setObscureMode(self): PGEntry.setObscureMode(self.guiItem, self['obscured']) def setBackgroundFocus(self): PGEntry.setBackgroundFocus(self.guiItem, self['backgroundFocus']) def setRolloverSound(self): rolloverSound = self['rolloverSound'] if rolloverSound: self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound) else: self.guiItem.clearSound(DGG.ENTER + self.guiId) def setClickSound(self): clickSound = self['clickSound'] if clickSound: self.guiItem.setSound(DGG.ACCEPT + self.guiId, clickSound) else: self.guiItem.clearSound(DGG.ACCEPT + self.guiId) def commandFunc(self, event): if self['command']: # Pass any extra args to command apply(self['command'], [self.get()] + self['extraArgs']) def failedCommandFunc(self, event): if self['failedCommand']: # Pass any extra args apply(self['failedCommand'], [self.get()] + self['failedExtraArgs']) def autoCapitalizeFunc(self): if self['autoCapitalize']: self._autoCapListener.accept(self.guiItem.getTypeEvent(), self._handleTyping) self._autoCapListener.accept(self.guiItem.getEraseEvent(), self._handleErasing) else: self._autoCapListener.ignore(self.guiItem.getTypeEvent()) self._autoCapListener.ignore(self.guiItem.getEraseEvent()) def focusInCommandFunc(self): if self['focusInCommand']: apply(self['focusInCommand'], self['focusInExtraArgs']) if self['autoCapitalize']: self.accept(self.guiItem.getTypeEvent(), self._handleTyping) self.accept(self.guiItem.getEraseEvent(), self._handleErasing) def _handleTyping(self, guiEvent): self._autoCapitalize() def _handleErasing(self, guiEvent): self._autoCapitalize() def _autoCapitalize(self): name = self.get().decode('utf-8') # capitalize each word, allowing for things like McMutton capName = '' # track each individual word to detect prefixes like Mc wordSoFar = '' # track whether the previous character was part of a word or not wasNonWordChar = True for i in xrange(len(name)): character = name[i] # test to see if we are between words # - Count characters that can't be capitalized as a break between words # This assumes that string.lower and string.upper will return different # values for all unicode letters. # - Don't count apostrophes as a break between words if ((string.lower(character) == string.upper(character)) and (character != "'")): # we are between words wordSoFar = '' wasNonWordChar = True else: capitalize = False if wasNonWordChar: # first letter of a word, capitalize it unconditionally; capitalize = True elif (character == string.upper(character) and len(self.autoCapitalizeAllowPrefixes) and wordSoFar in self.autoCapitalizeAllowPrefixes): # first letter after one of the prefixes, allow it to be capitalized capitalize = True elif (len(self.autoCapitalizeForcePrefixes) and wordSoFar in self.autoCapitalizeForcePrefixes): # first letter after one of the force prefixes, force it to be capitalized capitalize = True if capitalize: # allow this letter to remain capitalized character = string.upper(character) else: character = string.lower(character) wordSoFar += character wasNonWordChar = False capName += character self.enterText(capName.encode('utf-8')) def focusOutCommandFunc(self): if self['focusOutCommand']: apply(self['focusOutCommand'], self['focusOutExtraArgs']) if self['autoCapitalize']: self.ignore(self.guiItem.getTypeEvent()) self.ignore(self.guiItem.getEraseEvent()) def set(self, text): """ Changes the text currently showing in the typable region; does not change the current cursor position. Also see enterText(). """ self.unicodeText = isinstance(text, types.UnicodeType) if self.unicodeText: self.guiItem.setWtext(text) else: self.guiItem.setText(text) def get(self, plain = False): """ Returns the text currently showing in the typable region. If plain is True, the returned text will not include any formatting characters like nested color-change codes. """ wantWide = self.unicodeText or self.guiItem.isWtext() if not self.directWtext.getValue(): # If the user has configured wide-text off, then always # return an 8-bit string. This will be encoded if # necessary, according to Panda's default encoding. wantWide = False if plain: if wantWide: return self.guiItem.getPlainWtext() else: return self.guiItem.getPlainText() else: if wantWide: return self.guiItem.getWtext() else: return self.guiItem.getText() def setCursorPosition(self, pos): if (pos < 0): self.guiItem.setCursorPosition(self.guiItem.getNumCharacters() + pos) else: self.guiItem.setCursorPosition(pos) def enterText(self, text): """ sets the entry's text, and moves the cursor to the end """ self.set(text) self.setCursorPosition(self.guiItem.getNumCharacters()) def getFont(self): return self.onscreenText.getFont() def getBounds(self, state = 0): # Compute the width and height for the entry itself, ignoring # geometry etc. tn = self.onscreenText.textNode mat = tn.getTransform() align = tn.getAlign() lineHeight = tn.getLineHeight() numLines = self['numLines'] width = self['width'] if align == TextNode.ALeft: left = 0.0 right = width elif align == TextNode.ACenter: left = -width / 2.0 right = width / 2.0 elif align == TextNode.ARight: left = -width right = 0.0 bottom = -0.3 * lineHeight - (lineHeight * (numLines - 1)) top = lineHeight self.ll.set(left, 0.0, bottom) self.ur.set(right, 0.0, top) self.ll = mat.xformPoint(Point3.rfu(left, 0.0, bottom)) self.ur = mat.xformPoint(Point3.rfu(right, 0.0, top)) vec_right = Vec3.right() vec_up = Vec3.up() left = (vec_right[0] * self.ll[0] + vec_right[1] * self.ll[1] + vec_right[2] * self.ll[2]) right = (vec_right[0] * self.ur[0] + vec_right[1] * self.ur[1] + vec_right[2] * self.ur[2]) bottom = (vec_up[0] * self.ll[0] + vec_up[1] * self.ll[1] + vec_up[2] * self.ll[2]) top = (vec_up[0] * self.ur[0] + vec_up[1] * self.ur[1] + vec_up[2] * self.ur[2]) self.ll = Point3(left, 0.0, bottom) self.ur = Point3(right, 0.0, top) # Scale bounds to give a pad around graphics. We also want to # scale around the border width. pad = self['pad'] borderWidth = self['borderWidth'] self.bounds = [self.ll[0] - pad[0] - borderWidth[0], self.ur[0] + pad[0] + borderWidth[0], self.ll[2] - pad[1] - borderWidth[1], self.ur[2] + pad[1] + borderWidth[1]] return self.bounds
# THE CREDITS TEXT ost = OnscreenText(parent=textParent, font=loader.loadFont('cmss12'), text='CREDITS\n\n'+'\n'.join([c*30 for c in string.ascii_letters]), fg=(1,1,1,1),scale=.065) ost.setP(-90) ost.flattenLight() ts=TextureStage('') textParent.setTexGen(ts,TexGenAttrib.MWorldPosition) textParent.setTexScale(ts,.5,.5) textParent.setTexOffset(ts,.5,.5) textParent.setTexture(ts,tex) b3=textParent.getTightBounds() dims=b3[1]-b3[0] textParent.posInterval(50,Point3(0,dims[1]+2,0),Point3(0,-1.05,0)).start() VPcam = Camera('vpcam') VPcam.copyLens(base.cam2d.node().getLens()) VPcamNP = dummyParent.attachNewNode(VPcam) VPcamNP.setP(-90) dr=base.win.makeDisplayRegion() dr.setCamera(VPcamNP) dr.setSort(1000) do=DirectObject() do.accept('space',cycleAlpha) do.accept('escape',sys.exit) globalClock.setMode(ClockObject.MNormal) run()
terrainNode = NodePath("terrNode") terrainNode.reparentTo(render) terrainNode.attachNewNode(squareGN) terrainNode.setX(-width/2) texGrass = loader.loadTexture("textures/envir-ground.jpg") terrainNode.setTexture(texGrass) cHandler.addInPattern("%(rays)ft-into-%(building)it") cHandler.addOutPattern("%(rays)ft-out-%(building)it") cHandler.addAgainPattern("ray_again_all%(""rays"")fh%(""building"")ih") DO=DirectObject() DO.accept('ray1-into-buildPlayer1', collideInBuilding) DO.accept('ray1-out-buildPlayer1', collideOutBuilding) DO.accept('ray_again_all', collideAgainstBuilds) pickingEnabledOject=None DO.accept('mouse1', mouseClick, ["down"]) DO.accept('mouse1-up', mouseClick, ["up"]) DO.accept("w",moveCam, ["up"]) DO.accept("s",moveCam, ["down"]) DO.accept("w-up",moveCam, ["stopY"]) DO.accept("s-up",moveCam, ["stopY"]) DO.accept("a",moveCam, ["left"]) DO.accept("d",moveCam, ["right"])
base.cam.setY(self.avatar.getY()-self.camdistY) if self.steer2d: base.cam.setX(self.avatar.getX()) base.cam.setZ(self.avatar.getZ()+self.camdistZ) base.cam.lookAt(self.avatar) return Task.cont #========================================================================= # Main #========================================================================= alight = AmbientLight('alight') alight.setColor((.3, .3, .3, 1)) alnp = render.attachNewNode(alight) render.setLight(alnp) dlight = DirectionalLight('dlight') lv=.6 dlight.setColor((lv,lv,lv, 1)) dlnp = render.attachNewNode(dlight) render.setLight(dlnp) dlnp.setPos(0,-20,35) dlnp.lookAt(0,0,0) DO=DirectObject() DO.accept('c', toggle_collisions) DO.accept('h', toggle_info) DO.accept('x', toggle_wire) DO.accept('escape',sys.exit)
class PhysicsManager: def __init__(self, root_nodepath, world): self.world = BulletWorld() self.world.setGravity((0, 0, -9.81)) self._timestep = 1 / world.tick_rate # # Seems that this does not function # on_contact_added = PythonCallbackObject(self._on_contact_added) # self.world.set_contact_added_callback(on_contact_added) # on_filter = PythonCallbackObject(self._filter_collision) # self.world.set_filter_callback(on_filter) self.listener = DirectObject() self.listener.accept('bullet-contact-added', self._on_contact_added) self.listener.accept('bullet-contact-destroyed', self._on_contact_removed) self.tracked_contacts = defaultdict(int) self.existing_collisions = set() # Debugging info debug_node = BulletDebugNode('Debug') debug_node.showWireframe(True) debug_node.showConstraints(True) debug_node.showBoundingBoxes(False) debug_node.showNormals(False) # Add to world self.debug_nodepath = root_nodepath.attachNewNode(debug_node) self.world.set_debug_node(debug_node) self.debug_nodepath.show() def _on_contact_removed(self, node_a, node_b): self.tracked_contacts[(node_a, node_b)] -= 1 def _on_contact_added(self, node_a, node_b): self.tracked_contacts[(node_a, node_b)] += 1 def _dispatch_collisions(self): # Dispatch collisions existing_collisions = self.existing_collisions for pair, contact_count in self.tracked_contacts.items(): # If is new collision if contact_count > 0 and pair not in existing_collisions: existing_collisions.add(pair) # Dispatch collision node_a, node_b = pair entity_a = node_a.get_python_tag("entity") entity_b = node_b.get_python_tag("entity") if not (entity_a and entity_b): continue contact_result = ContactResult(self.world, node_a, node_b) entity_a.messenger.send("collision_started", entity=entity_b, contacts=contact_result.contacts_a) entity_b.messenger.send("collision_started", entity=entity_a, contacts=contact_result.contacts_b) # Ended collision elif contact_count == 0 and pair in existing_collisions: existing_collisions.remove(pair) # Dispatch collision node_a, node_b = pair entity_a = node_a.get_python_tag("entity") entity_b = node_b.get_python_tag("entity") if not (entity_a and entity_b): continue entity_a.messenger.send("collision_stopped", entity_b) entity_b.messenger.send("collision_stopped", entity_a) def add_entity(self, entity, component): body = component.body self.world.attach_rigid_body(body) body.set_python_tag("entity", entity) def remove_entity(self, entity, component): body = component.body self.world.remove_rigid_body(body) body.clear_python_tag("entity") def tick(self): self.world.do_physics(self._timestep) self._dispatch_collisions() def resimulate_pawn(self, pawn): pass
class SCTerminal(SCElement): __module__ = __name__ def __init__(self, linkedEmote = None): SCElement.__init__(self) self.setLinkedEmote(linkedEmote) scGui = loader.loadModel(SCMenu.GuiModelName) self.emotionIcon = scGui.find('**/emotionIcon') self.setDisabled(False) self.__numCharges = -1 self._handleWhisperModeSV = StateVar(False) self._handleWhisperModeFC = None return def destroy(self): self._handleWhisperModeSV.set(False) if self._handleWhisperModeFC: self._handleWhisperModeFC.destroy() self._handleWhisperModeSV.destroy() SCElement.destroy(self) def privSetSettingsRef(self, settingsRef): SCElement.privSetSettingsRef(self, settingsRef) if self._handleWhisperModeFC is None: self._handleWhisperModeFC = FunctionCall(self._handleWhisperModeSVChanged, self._handleWhisperModeSV) self._handleWhisperModeFC.pushCurrentState() self._handleWhisperModeSV.set(self.settingsRef is not None and not self.isWhisperable()) return def _handleWhisperModeSVChanged(self, handleWhisperMode): if handleWhisperMode: self._wmcListener = DirectObject() self._wmcListener.accept(self.getEventName(SCWhisperModeChangeEvent), self._handleWhisperModeChange) elif hasattr(self, '_wmcListener'): self._wmcListener.ignoreAll() del self._wmcListener self.invalidate() def _handleWhisperModeChange(self, whisperMode): self.invalidate() def handleSelect(self): messenger.send(self.getEventName(SCTerminalSelectedEvent)) if self.hasLinkedEmote() and self.linkedEmoteEnabled(): messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent), [self.linkedEmote]) def isWhisperable(self): return True def getLinkedEmote(self): return self.linkedEmote def setLinkedEmote(self, linkedEmote): self.linkedEmote = linkedEmote self.invalidate() def hasLinkedEmote(self): return self.linkedEmote is not None def linkedEmoteEnabled(self): if Emote.globalEmote: return Emote.globalEmote.isEnabled(self.linkedEmote) def getCharges(self): return self.__numCharges def setCharges(self, nCharges): self.__numCharges = nCharges if nCharges is 0: self.setDisabled(True) def isDisabled(self): return self.__disabled or self.isWhispering() and not self.isWhisperable() def setDisabled(self, bDisabled): self.__disabled = bDisabled def onMouseClick(self, event): if not self.isDisabled(): SCElement.onMouseClick(self, event) self.handleSelect() def getMinDimensions(self): width, height = SCElement.getMinDimensions(self) if self.hasLinkedEmote(): width += 1.3 return (width, height) def finalize(self, dbArgs = {}): if not self.isDirty(): return args = {} if self.hasLinkedEmote(): self.lastEmoteIconColor = self.getEmoteIconColor() self.emotionIcon.setColorScale(*self.lastEmoteIconColor) args.update({'image': self.emotionIcon, 'image_pos': (self.width - 0.6, 0, -self.height * 0.5)}) if self.isDisabled(): args.update({'rolloverColor': (0, 0, 0, 0), 'pressedColor': (0, 0, 0, 0), 'rolloverSound': None, 'clickSound': None, 'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)}) args.update(dbArgs) SCElement.finalize(self, dbArgs=args) return def getEmoteIconColor(self): if self.linkedEmoteEnabled() and not self.isWhispering(): r, g, b = self.getColorScheme().getEmoteIconColor() else: r, g, b = self.getColorScheme().getEmoteIconDisabledColor() return (r, g, b, 1) def updateEmoteIcon(self): if hasattr(self, 'button'): self.lastEmoteIconColor = self.getEmoteIconColor() for i in range(self.button['numStates']): self.button['image%s_image' % i].setColorScale(*self.lastEmoteIconColor) else: self.invalidate() def enterVisible(self): SCElement.enterVisible(self) if hasattr(self, 'lastEmoteIconColor'): if self.getEmoteIconColor() != self.lastEmoteIconColor: self.invalidate() def handleWhisperModeChange(whisperMode, self = self): if self.hasLinkedEmote(): if self.isVisible() and not self.isWhispering(): self.updateEmoteIcon() self.accept(self.getEventName(SCWhisperModeChangeEvent), handleWhisperModeChange) def handleEmoteEnableStateChange(self = self): if self.hasLinkedEmote(): if self.isVisible() and not self.isWhispering(): self.updateEmoteIcon() if self.hasLinkedEmote(): if Emote.globalEmote: self.accept(Emote.globalEmote.EmoteEnableStateChanged, handleEmoteEnableStateChange) def exitVisible(self): SCElement.exitVisible(self) self.ignore(self.getEventName(SCWhisperModeChangeEvent)) if Emote.globalEmote: self.ignore(Emote.globalEmote.EmoteEnableStateChanged) def getDisplayText(self): if self.getCharges() is not -1: return self.text + ' (%s)' % self.getCharges() else: return self.text
text="CREDITS\n\n" + "\n".join([c * 30 for c in string.ascii_letters]), fg=(1, 1, 1, 1), scale=0.065, ) ost.setP(-90) ost.flattenLight() ts = TextureStage("") textParent.setTexGen(ts, TexGenAttrib.MWorldPosition) textParent.setTexScale(ts, 0.5, 0.5) textParent.setTexOffset(ts, 0.5, 0.5) textParent.setTexture(ts, tex) b3 = textParent.getTightBounds() dims = b3[1] - b3[0] textParent.posInterval(50, Point3(0, dims[1] + 2, 0), Point3(0, -1.05, 0)).loop() VPcam = Camera("vpcam") VPcam.copyLens(base.cam2d.node().getLens()) VPcamNP = dummyParent.attachNewNode(VPcam) VPcamNP.setP(-90) dr = base.win.makeDisplayRegion() dr.setCamera(VPcamNP) dr.setSort(1000) do = DirectObject() do.accept("space", cycleAlpha) do.accept("escape", sys.exit) globalClock.setMode(ClockObject.MNormal) run()
class PopupMouseWatcherRegion(MouseWatcherRegion): """ This is an ultra hacky class! The correct implementation of PopupMouseWatcherRegion cannot be done in Python. This also assumes that m_mouse_watcher is NametagGlobals::_mouse_watcher. """ class _Param: def __init__(self, outside=False): self.outside = outside def isOutside(self): return self.outside def getButton(self): return MouseButton.one() MOUSE_WATCHER_SETUP = False def __init__(self, obj, name, frame): MouseWatcherRegion.__init__(self, '%s-%s' % (name, id(self)), frame) self.obj = obj self.__inside = False if not self.MOUSE_WATCHER_SETUP: NametagGlobals._mouse_watcher.setEnterPattern('mouse-enter-%r') NametagGlobals._mouse_watcher.setLeavePattern('mouse-leave-%r') NametagGlobals._mouse_watcher.setButtonDownPattern( 'button-down-%r') NametagGlobals._mouse_watcher.setButtonUpPattern('button-up-%r') self.MOUSE_WATCHER_SETUP = True self.slaveObject = DirectObject() self.slaveObject.accept( self.__getEvent(NametagGlobals._mouse_watcher.getEnterPattern()), self.__mouseEnter) self.slaveObject.accept( self.__getEvent(NametagGlobals._mouse_watcher.getLeavePattern()), self.__mouseLeave) self.slaveObject.accept( self.__getEvent( NametagGlobals._mouse_watcher.getButtonDownPattern()), self.__buttonDown) self.slaveObject.accept( self.__getEvent( NametagGlobals._mouse_watcher.getButtonUpPattern()), self.__buttonUp) def __mouseEnter(self, region, extra): self.__inside = True self.obj.enterRegion(None) def __mouseLeave(self, region, extra): self.__inside = False self.obj.exitRegion(None) def __buttonDown(self, region, button): if button == 'mouse1': self.obj.press(PopupMouseWatcherRegion._Param()) def __buttonUp(self, region, button): if button == 'mouse1': self.obj.release(PopupMouseWatcherRegion._Param(not self.__inside)) def __getEvent(self, pattern): return pattern.replace('%r', self.getName())
#** This is a task function called each frame, where the collision ray position is syncronized with the mouse pointer position and, more important, we traverse our collision traverser to make its routines check the collisions. def rayupdate(task): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() # here the meat: differently from step 6, we do not call setFromLens to shoot our ray in 3d space from the camera lens, but rather we move the ray along the cursor position and shoot a straight ray from there along Y axis. This is because in orthographic view there is no perspective and therefore a ray is shoot parallel to its starting point pickerNP.setPos(render2d, mpos[0], 0, mpos[1]) # and here we run the traverse step, to check the collisions in our scene customCtrav.traverse(render2d) return task.cont #** to manage the collision and mouse button events DO = DirectObject() #** Let's manage the collision events: as you can see these are the events generated by the patterns defined above, a fixed string this time so no surprises here. DO.accept('ray-into-cards', collideInCard) DO.accept('ray-out-cards', collideOutCard) #** Storage for the object eventually below the mouse pointer pickingEnabledOject = None #** Here's how we interact with mouse clicks - see the mouseClick function above DO.accept('mouse1', mouseClick, ['down']) DO.accept('mouse1-up', mouseClick, ['up']) #** And here we start the task that continuously update the ray collider position and make traverse the collision traverser in 2d space taskMgr.add(rayupdate, "updatePicker") splash.destroy() run()
class Game(ShowBase): def __init__(self): ShowBase.__init__(self) #start the time self.globalTime = 0 self.nextEnemy = 1 #setup your collision event handlers, apparently needs a direct object self.do = DirectObject() self.do.accept('unit-into-unit', self.handleUnitIntoCollision) self.do.accept('unit-out-unit', self.handleUnitOutCollision) self.do.accept('unit-into-cube', self.handleCubeIntoCollision) self.do.accept('unit-into-wing', self.handleWingIntoCollision) self.do.accept('unit-into-bar', self.handleBarIntoCollision) #get window properties self.winProps = WindowProperties() #self.winProps.setFullscreen(True) self.winProps.setCursorHidden(True) base.win.requestProperties(self.winProps) self.winProps = base.win.getProperties() self.screenHeight = self.winProps.getYSize() #set up the control scheme self.controlScheme = ControlScheme(base.mouseWatcherNode, base.win, \ [LEFT, RIGHT, UP, DOWN, PAUSE, PULL, PUSH, SWITCH, QUIT]) #disable the automatic task to move the camera #(this does not actually disable the mouse) base.disableMouse() #declare null values for variables, fill them in later self.environment = None self.player = None #a node for holding all in-game units self.unitNodePath = NodePath('unit holder') self.unitNodePath.reparentTo(self.render) #object lists self.enemies = [] self.obstacles = [] self.projectiles = [] #list of enemies to be spawned self.eSpawnList = [] #not paused by default self.paused = False self.pauseWasPressed = False #variables for tracking time self.previousFrameTime = 0 #start the collision traverser traverser = CollisionTraverser() base.cTrav = traverser#run every frame self.cTrav = base.cTrav #set the check for units accidentally passing through level geometry self.cTrav.setRespectPrevTransform(True) #self.cTrav.showCollisions(self.render) #self.cTrav.showCollisions(self.unitNodePath)#show the collisions #load terrain and enemies #load the environment, it seems that an x value of zero, a y value of -50 puts the origin point relatively in the middle of the crater filename = PARAMS_PATH + "environment.txt" self.loadLevelGeom(filename) #load the enemies filename = PARAMS_PATH + "enemies.txt" self.loadLevelEnemies(filename) #lookup table for actors self.actors = {} #place the player in the environment self.player = Player(self.controlScheme, self.camera, self, 0, 0, 0) self.player.setName("player") self.player.setH(180) self.player.reparentTo(self.unitNodePath) self.player.nodePath = self.render.find("player") self.actors["player"] = self.player #add some lights topLight = DirectionalLight("top light") topLight.setColor(Vec4(0.5, 0.5, 0.5, 1)) topLight.setDirection(Vec3(0, -90, 0)) self.render.setLight(self.render.attachNewNode(topLight)) ambientLight = AmbientLight("ambient light") ambientLight.setColor(Vec4(0.5, 0.5, 0.5, 1)) self.render.setLight(self.render.attachNewNode(ambientLight)) #the distance the camera is from the player self.cameraHOffset = 45 self.cameraVOffset = 10 #add the collision sound self.collisionSound = self.loader.loadSfx(SFX_PATH + "collide.wav") #register the update task self.taskMgr.add(self.updateGame, "updateGame") #add targeting to the world self.setupTargeting() #seed the random number generator random.seed() # configure the entire GUI self.setupGUI() # configure the title screen self.setupTitleScreen() def setupGUI(self): GUIFont = loader.loadFont(FONTS_PATH + 'orbitron-medium.ttf') """ self.debugText = TextNode('debug') self.debugText.setText("") self.debugText.setAlign(TextNode.ALeft) self.debugText.setFont(GUIFont) dTextNodePath = aspect2d.attachNewNode(self.debugText) dTextNodePath.setScale(0.075) dTextNodePath.setPos(-1.2, 0, -0.9) """ #INTRO self.introText = TextNode('intro') self.introText.setText("") self.introText.setAlign(TextNode.ALeft) self.introText.setFont(GUIFont) self.introText.setCardColor(0.1, 0.1, 0.1, 0.6) self.introText.setCardAsMargin(0.5, 0.5, 0.5, 0.5) self.introText.setCardDecal(True) self.introText.setWordwrap(30.0) self.iTextNodePath = aspect2d.attachNewNode(self.introText) self.iTextNodePath.setScale(0.06) self.iTextNodePath.setPos(-1.2, 0, -0.7) #image is 171 x 323 self.leftMouseImage = OnscreenImage() self.leftMouseImage.setImage(GUI_PATH + "mouse-icon-left.png") self.leftMouseImage.setTransparency(1) self.leftMousePath = aspect2d.attachNewNode(self.leftMouseImage.node()) self.rightMouseImage = OnscreenImage() self.rightMouseImage.setImage(GUI_PATH + "mouse-icon-right.png") self.rightMouseImage.setTransparency(1) self.rightMousePath = aspect2d.attachNewNode(self.rightMouseImage.node()) self.middleMouseImage = OnscreenImage() self.middleMouseImage.setImage(GUI_PATH + "mouse-icon-middle.png") self.middleMouseImage.setTransparency(1) self.middleMousePath = aspect2d.attachNewNode(self.middleMouseImage.node()) self.leftMousePath.setScale(.105882, 0, .2) self.leftMousePath.setPos(1.1, 0, -0.75) self.rightMousePath.setScale(.105882, 0, .2) self.rightMousePath.setPos(1.1, 0, -0.75) self.middleMousePath.setScale(.105882, 0, .2) self.middleMousePath.setPos(1.1, 0, -0.75) self.leftMousePath.hide() self.rightMousePath.hide() self.middleMousePath.hide() #HUD #image is 365 x 187 self.attackModeImage = OnscreenImage() self.attackModeImage.setImage(GUI_PATH + "mode-area.png") self.attackModeImage.setTransparency(1) modeNodePath = aspect2d.attachNewNode(self.attackModeImage.node()) modeNodePath.setScale(.136631, 0, .07) modeNodePath.setPos(-1.13, 0, 0.88) self.energyBarImage = OnscreenImage() self.energyBarImage.setImage(GUI_PATH + "energy-bar-full.png") self.energyBarImage.setTransparency(1) eBarNodePath = aspect2d.attachNewNode(self.energyBarImage.node()) eBarNodePath.setScale(.400, 0, .0475) eBarNodePath.setPos(-0.66, 0, 0.88) self.healthBarImage = OnscreenImage() self.healthBarImage.setImage(GUI_PATH + "health-bar-full.png") self.healthBarImage.setTransparency(1) hBarNodePath = aspect2d.attachNewNode(self.healthBarImage.node()) hBarNodePath.setScale(.400, 0, .0475) hBarNodePath.setPos(0.85, 0, 0.88) def setupTitleScreen(self): self.titleScreenIsActive = True #image is 800 x 600 self.titleImage = OnscreenImage() self.titleImage.setImage(GUI_PATH + "title.png") titleNodePath = aspect2d.attachNewNode(self.titleImage.node()) titleNodePath.setScale(1.33333, 0, 1) titleNodePath.setPos(0, 0, 0) def updateTitleScreen(self): if (self.controlScheme.keyDown(PUSH) or self.controlScheme.keyDown(PULL) or self.controlScheme.keyDown(SWITCH)) and self.titleScreenIsActive: self.titleImage.hide() self.titleScreenIsActive = False elif self.titleScreenIsActive: self.titleImage.setImage(GUI_PATH + "title.png") self.globalTime=0 else: pass def calculateBarImage(self, level): if level == 0: level = "empty" elif level == 10: level = "full" elif level == 1: level = "1" elif level == 2: level = "2" elif level == 3: level = "3" elif level == 4: level = "4" elif level == 5: level = "5" elif level == 6: level = "6" elif level == 7: level = "7" elif level == 8: level = "8" elif level == 9: level = "9" return level def updateGUI(self): """ self.debugText.setText("Energy: "+str((100*self.player.energy/self.player.maxEnergy))+"%, Health: "+str((100*self.player.health/self.player.maxHealth))) """ self.updateIntro() if self.player.currentWeapon == AREA: modeImg = "mode-area" elif self.player.currentWeapon == NARROW: modeImg = "mode-narrow" energyLevel = self.calculateBarImage((100 * (self.player.energy / self.player.maxEnergy)) // 10) self.energyBarImage.setImage(GUI_PATH + "energy-bar-" + energyLevel + ".png") self.energyBarImage.setTransparency(1) self.attackModeImage.setImage(GUI_PATH + modeImg + ".png") self.attackModeImage.setTransparency(1) healthLevel = self.calculateBarImage((100 * (self.player.health / self.player.maxHealth)) // 10) self.healthBarImage.setImage(GUI_PATH + "health-bar-" + healthLevel + ".png") self.healthBarImage.setTransparency(1) def updateIntro(self): modifiedTime = self.globalTime/5 if self.introText is None: pass elif modifiedTime>=14300 and self.introText is not None: self.iTextNodePath.removeNode() self.introText = None elif modifiedTime<300: #0-300 self.introText.setText("Hey are you receiving?") elif modifiedTime<800: #300-800 self.introText.setText("We recently uncovered the location of an old cruiser wreck from the war.") elif modifiedTime<1500: #800-1500 self.introText.setText("You're gonna go in there and get the little bits of it that are still worth salvaging.") elif modifiedTime<2900: #1500-2900 self.introText.setText("Be aware though, this job may not be quite as simple as a grab-and-run. The coordinates have been leaked to some of our old enemies.") elif modifiedTime<4200: #2900-4200 self.introText.setText("To give you an edge, we've made a few upgrades to your electromagnetic grapple.") elif modifiedTime<5300: #4200-5300 self.introText.setText("Pull enemies inwards with the right mouse button.") self.leftMousePath.hide() self.rightMousePath.show() self.middleMousePath.hide() elif modifiedTime<6400: #5300-6400 self.introText.setText("Push enemies away with the left mouse button.") self.leftMousePath.show() self.rightMousePath.hide() self.middleMousePath.hide() elif modifiedTime<8500: #6400-8500 self.introText.setText("Toggle between your range and narrow electromagnet with the middle mouse button (or spacebar).") self.leftMousePath.hide() self.rightMousePath.hide() self.middleMousePath.show() elif modifiedTime<13300: #8500-13300 self.introText.setText("You can give a solid kick to anything straight in front of you or just push everything around you away. Try and throw 'em into each other or other wreckage to finish 'em off for good.") self.leftMousePath.hide() self.rightMousePath.hide() self.middleMousePath.hide() elif modifiedTime<14300: #13300-14300 self.introText.setText("After all, the more salvage left over, the better. Out.") def loadLevelGeom(self, filename): filename = os.path.abspath(filename) if not os.path.isfile(filename): print "FILE DOES NOT EXIST:" exit(1) #get the lines from the file textFileList = open(filename, 'r').readlines() if len(textFileList) < 1: print "FATAL ERROR READING FILE" exit(1) #now split each line into lists for num, val in enumerate(textFileList): val = val.rstrip('\n')#strip the newlines val = val.strip() textFileList[num] = val.split(TEXT_DELIMITER) obstacle = None for list in textFileList: #go through the list if list[0] == TERRAIN_OUTER:#do all the things for the terrain #choose the model modelVal = list[1] modelVal = (MODELS_PATH + modelVal) self.environment = self.loader.loadModel(modelVal) self.environment.reparentTo(self.render) #get the collision geometry for the environment #and move it up a bit to allow the character to float above the crater self.craterCollision = self.environment.find("**/craterCollisionPlane") self.craterCollision.setZ(self.environment.getZ() + 0.15) self.environment.setCollideMask(BitMask32.allOff()) self.craterCollision.setCollideMask(BitMask32(TERRAIN_RAY_MASK)) #self.environment.find( #.find("craterCollisionPlane").setZ(self.environment.getZ() + 10) #set scale scaleVal = list[2].split(',')#split by commas self.environment.setScale(float(scaleVal[0]), float(scaleVal[1]), float(scaleVal[2])) #set location locVal = list[3].split(',') self.environment.setPos(float(locVal[0]), float(locVal[1]), float(locVal[2]))#then we have our terrain #set rotation hprVal = list[4].split(',') self.environment.setHpr(float(hprVal[0]), float(hprVal[1]), float(hprVal[2])) elif list[0] == TERRAIN_CUBE: #choose the model modelVal = list[1] modelVal = (MODELS_PATH + modelVal) #load the model obstacle = self.loader.loadModel(modelVal) obstacle.reparentTo(render) #set scale scaleVal = list[2].split(',') obstacle.setScale(float(scaleVal[0]), float(scaleVal[1]), float(scaleVal[2])) #set location locVal = list[3].split(',') obstacle.setPos(float(locVal[0]), float(locVal[1]), float(locVal[2]))#the we have our object hprVal = list[4].split(',') obstacle.setHpr(float(hprVal[0]), float(hprVal[1]), float(hprVal[2])) #set up collisions unitCollision = obstacle.find("**/CubeBlock") unitCollision.node().setName("cube") obstacle.setCollideMask(BitMask32.allOff()) unitCollision.setCollideMask(BitMask32(PLAYER_ENEMY_OBJECTS)) self.obstacles.append(obstacle) elif list[0] == TERRAIN_WING: modelVal = list[1] modelVal = (MODELS_PATH + modelVal) #load the model obstacle = self.loader.loadModel(modelVal) obstacle.reparentTo(render) #set scale scaleVal = list[2].split(',') obstacle.setScale(float(scaleVal[0]), float(scaleVal[1]), float(scaleVal[2])) #set location locVal = list[3].split(',') obstacle.setPos(float(locVal[0]), float(locVal[1]), float(locVal[2]))#the we have our object hprVal = list[4].split(',') obstacle.setHpr(float(hprVal[0]), float(hprVal[1]), float(hprVal[2])) #set up collisions unitCollision = obstacle.find("**/wingCollider") unitCollision.node().setName("wing") obstacle.setCollideMask(BitMask32.allOff()) unitCollision.setCollideMask(BitMask32(PLAYER_ENEMY_OBJECTS)) self.obstacles.append(obstacle) elif list[0] == TERRAIN_BAR: modelVal = list[1] modelVal = (MODELS_PATH + modelVal) #load the model obstacle = self.loader.loadModel(modelVal) obstacle.reparentTo(render) #set scale scaleVal = list[2].split(',') obstacle.setScale(float(scaleVal[0]), float(scaleVal[1]), float(scaleVal[2])) #set location locVal = list[3].split(',') obstacle.setPos(float(locVal[0]), float(locVal[1]), float(locVal[2]))#the we have our object hprVal = list[4].split(',') obstacle.setHpr(float(hprVal[0]), float(hprVal[1]), float(hprVal[2])) #set up collisions unitCollision = obstacle.find("**/metalBarCollisionCube") unitCollision.node().setName("bar") obstacle.setCollideMask(BitMask32.allOff()) unitCollision.setCollideMask(BitMask32(PLAYER_ENEMY_OBJECTS)) elif list[0] == TERRAIN_SHARDS: modelVal = list[1] modelVal = (MODELS_PATH + modelVal) #load the model obstacle = self.loader.loadModel(modelVal) obstacle.reparentTo(render) #set scale scaleVal = list[2].split(',') obstacle.setScale(float(scaleVal[0]), float(scaleVal[1]), float(scaleVal[2])) #set location locVal = list[3].split(',') obstacle.setPos(float(locVal[0]), float(locVal[1]), float(locVal[2]))#the we have our object hprVal = list[4].split(',') obstacle.setHpr(float(hprVal[0]), float(hprVal[1]), float(hprVal[2])) #set up collisions unitCollision = obstacle.find("**/metalShardCollisionCube") unitCollision.node().setName("shard") obstacle.setCollideMask(BitMask32.allOff()) unitCollision.setCollideMask(BitMask32(PLAYER_ENEMY_OBJECTS)) else: print "FATAL ERROR READING FILE" exit(1) pass def loadLevelEnemies(self, filename): filename = os.path.abspath(filename) if not os.path.isfile(filename): print "FILE DOES NOT EXIST:" exit(1) #get the lines from the file and split them textFileList = open(filename, 'r').readlines() if len(textFileList) < 1: print "FATAL ERROR READING FILE" exit(1) for num, val in enumerate(textFileList): val = val.rstrip('\n')#strip the newlines val = val.strip() textFileList[num] = val.split(TEXT_DELIMITER) currwave = dict() currwave["time"] = None currwave["enemies"] = [] currEnem = dict() for val in textFileList: if val[0] == BEGIN_WAVE: currwave = dict() currwave["time"] = float(val[1])#set your time currwave["enemies"] = [] elif val[0] == RUSH_ENEMY: currEnem = dict() pos = [] pos = val[1].split(',')#get the three values for spawning, not floats currEnem["type"] = RUSH_ENEMY currEnem["xVal"] = float(pos[0]) currEnem["yVal"] = float(pos[1]) currEnem["zVal"] = float(pos[2]) currwave["enemies"].append(dict(currEnem))#copy elif val[0] == DRONE_ENEMY: currEnem = dict() pos = [] pos = val[1].split(',')#get the three values for spawning, not floats currEnem["type"] = DRONE_ENEMY currEnem["xVal"] = float(pos[0]) currEnem["yVal"] = float(pos[1]) currEnem["zVal"] = float(pos[2]) currwave["enemies"].append(dict(currEnem))#copy elif val[0] == SHOOTING_ENEMY: currEnem = dict() pos = [] pos = val[1].split(',')#get the three values for spawning, not floats currEnem["type"] = SHOOTING_ENEMY currEnem["xVal"] = float(pos[0]) currEnem["yVal"] = float(pos[1]) currEnem["zVal"] = float(pos[2]) currwave["enemies"].append(dict(currEnem))#copy elif val[0] == END_WAVE:#then we are done with that wave self.eSpawnList.append(dict(currwave))#copy else: pass#then something was stupid #now sort your waves with lowest time first self.eSpawnList.sort(key = lambda object: object["time"]) #and you're done def updateGame(self, task): if not self.titleScreenIsActive: self.globalTime = self.globalTime + task.time elapsedTime = task.time - self.previousFrameTime #base.resetPrevTransform(render)#for our high intensity collision detection if self.controlScheme.keyDown(QUIT): exit(0) if not self.paused and not self.titleScreenIsActive: time = elapsedTime while time > 0.02: self.updateGameComponents(0.02) time -= 0.02 self.updateGameComponents(time) self.cTrav.traverse(render) self.spawnEnemies()#globalTime is available if self.controlScheme.keyDown(PAUSE): if not self.pauseWasPressed: self.paused = not self.paused self.controlScheme.resetMouse() self.pauseWasPressed = True else: self.pauseWasPressed = False self.updateGUI() self.updateTitleScreen() self.previousFrameTime = task.time return task.cont def updateGameComponents(self, time): ''' Updates the state of the world. @precondition: The game isn't paused. ''' self.updateCamera(time) for enemy in self.enemies: enemy.update(time) for projectile in self.projectiles: projectile.update(time) #check for basic terrain collisions self.player.terrainCollisionCheck() self.player.update(time) for enemy in self.enemies: enemy.terrainCollisionCheck() for projectile in self.projectiles: projectile.terrainCollisionCheck() def spawnEnemies(self):#now we spawn our enemies while((len(self.eSpawnList) > 0) and self.eSpawnList[0]["time"] < self.globalTime): for val in self.eSpawnList[0]["enemies"]: if val["type"] == RUSH_ENEMY: #add an enemy tempEnemy = RushEnemy(self, val["xVal"], val["yVal"], val["zVal"]) self.configureEnemy(tempEnemy) elif val["type"] == DRONE_ENEMY: #add an enemy tempEnemy = DroneEnemy(self, self.player, val["xVal"], val["yVal"], val["zVal"]) self.configureEnemy(tempEnemy) elif val["type"] == SHOOTING_ENEMY: #add an enemy tempEnemy = ShootingEnemy(self, val["xVal"], val["yVal"], val["zVal"]) self.configureEnemy(tempEnemy) else: pass del self.eSpawnList[0]#del def configureEnemy(self, tempEnemy):#after making an enemy configure it for the game numString = str(self.nextEnemy) tempEnemy.setName("enemy" + numString) tempEnemy.reparentTo(self.unitNodePath) tempEnemy.nodePath = self.render.find("enemy1") self.actors["enemy" + numString] = tempEnemy self.nextEnemy = self.nextEnemy + 1 self.enemies.append(tempEnemy) def rotateCamera(self): if self.controlScheme.mouseX > self.winProps.getXSize(): self.camera.setH(-(self.winProps.getXSize() - 20) * 0.5) else: self.camera.setH(-self.controlScheme.mouseX * 0.5) def updateCamera(self, elapsedTime): #update the camera's heading based on the mouse's x position if self.controlScheme.recheckMouse(): self.camera.setH(-self.controlScheme.mouseX * 0.5) else: self.rotateCamera() #update the camera's pitch and vertical position based on the mouse's y position self.cameraVOffset = min(self.screenHeight, max(0, self.controlScheme.mouseY)) / self.screenHeight * 25 + 4 self.cameraHOffset = self.cameraVOffset * 0.8 + 30 self.camera.setP(atan2(-self.cameraVOffset * 0.7, self.cameraHOffset) \ * 180 / pi) #update the camera to point at the player self.camera.setPos(self.player.getX() + self.cameraHOffset * sin(self.camera.getH() * pi / 180), self.player.getY() - self.cameraHOffset * cos(self.camera.getH() * pi / 180), self.player.getZ() + 0.3 + self.cameraVOffset) def selectTarget(self): """Finds the closest shootable object and returns it""" #traverse all objects in render self.mPickerTraverser.traverse(self.unitNodePath) if (self.mCollisionQue.getNumEntries() > 0): self.mCollisionQue.sortEntries() for i in range(0, self.mCollisionQue.getNumEntries()): entry = self.mCollisionQue.getEntry(i) pickedObj = entry.getIntoNodePath() if not pickedObj.isEmpty(): name = pickedObj.getParent().getName() if name == "render": return None #if the object is shootable, set it as the target try: if self.actors[name].shootable: return self.actors[name] except: continue return None def setupTargeting(self): """Set up the collisions necessary to target enemies and other objects""" #Since we are using collision detection to do picking, we set it up #any other collision detection system with a traverser and a handler self.mPickerTraverser = CollisionTraverser() #Make a traverser #self.mPickerTraverser.showCollisions(self.unitNodePath) self.mCollisionQue = CollisionHandlerQueue() #create a collision solid ray to detect against self.mPickRay = CollisionRay() self.mPickRay.setOrigin(self.player.getPos(self.render)) self.mPickRay.setDirection(self.render.getRelativeVector(self.player, Vec3(0, 1, 0))) self.mPickRay2 = CollisionRay() self.mPickRay2.setOrigin(self.player.getPos(self.render)) self.mPickRay2.setDirection(self.render.getRelativeVector(self.player, Vec3(0.06, 1, 0))) self.mPickRay3 = CollisionRay() self.mPickRay3.setOrigin(self.player.getPos(self.render)) self.mPickRay3.setDirection(self.render.getRelativeVector(self.player, Vec3(-0.06, 1, 0))) self.mPickRay4 = CollisionRay() self.mPickRay4.setOrigin(self.player.getPos(self.render)) self.mPickRay4.setDirection(self.render.getRelativeVector(self.player, Vec3(0, 1, 0.06))) self.mPickRay5 = CollisionRay() self.mPickRay5.setOrigin(self.player.getPos(self.render)) self.mPickRay5.setDirection(self.render.getRelativeVector(self.player, Vec3(0, 1, -0.06))) #create our collison Node to hold the ray self.mPickNode = CollisionNode('pickRay') self.mPickNode.setIntoCollideMask(BitMask32.allOff()) self.mPickNode.addSolid(self.mPickRay) self.mPickNode2 = CollisionNode('pickRay2') self.mPickNode2.setIntoCollideMask(BitMask32.allOff()) self.mPickNode2.addSolid(self.mPickRay2) self.mPickNode3 = CollisionNode('pickRay3') self.mPickNode3.setIntoCollideMask(BitMask32.allOff()) self.mPickNode3.addSolid(self.mPickRay3) self.mPickNode4 = CollisionNode('pickRay4') self.mPickNode4.setIntoCollideMask(BitMask32.allOff()) self.mPickNode4.addSolid(self.mPickRay4) self.mPickNode5 = CollisionNode('pickRay5') self.mPickNode5.setIntoCollideMask(BitMask32.allOff()) self.mPickNode5.addSolid(self.mPickRay5) #Attach that node to the player since the ray will need to be positioned #relative to it, returns a new nodepath #well use the default geometry mask #this is inefficent but its for mouse picking only self.mPickNP = self.player.attachNewNode(self.mPickNode) self.mPickNP2 = self.player.attachNewNode(self.mPickNode2) self.mPickNP3 = self.player.attachNewNode(self.mPickNode3) self.mPickNP4 = self.player.attachNewNode(self.mPickNode4) self.mPickNP5 = self.player.attachNewNode(self.mPickNode5) #well use what panda calls the "from" node. This is really a silly convention #but from nodes are nodes that are active, while into nodes are usually passive environments #this isnt a hard rule, but following it usually reduces processing #Everything to be picked will use bit 1. This way if we were doing other #collision we could seperate it, we use bitmasks to determine what we check other objects against #if they dont have a bitmask for bit 1 well skip them! #self.mPickNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) self.mPickNode.setFromCollideMask(PLAYER_ENEMY_OBJECTS) self.mPickNode2.setFromCollideMask(PLAYER_ENEMY_OBJECTS) self.mPickNode3.setFromCollideMask(PLAYER_ENEMY_OBJECTS) self.mPickNode4.setFromCollideMask(PLAYER_ENEMY_OBJECTS) self.mPickNode5.setFromCollideMask(PLAYER_ENEMY_OBJECTS) #Register the ray as something that can cause collisions self.mPickerTraverser.addCollider(self.mPickNP, self.mCollisionQue) self.mPickerTraverser.addCollider(self.mPickNP2, self.mCollisionQue) self.mPickerTraverser.addCollider(self.mPickNP3, self.mCollisionQue) self.mPickerTraverser.addCollider(self.mPickNP4, self.mCollisionQue) self.mPickerTraverser.addCollider(self.mPickNP5, self.mCollisionQue) def handleUnitIntoCollision(self, entry): try: fromName = entry.getFromNodePath().getParent().getName() intoName = entry.getIntoNodePath().getParent().getName() Unit.collideWithUnit(self.actors[intoName], self.actors[fromName]) if (fromName == "player" or intoName == "player") and self.collisionSound.status() is not self.collisionSound.PLAYING: self.collisionSound.play() except: pass def handleUnitOutCollision(self, entry): pass def handleWingIntoCollision(self, entry): fromName = entry.getFromNodePath().getParent().getName() Unit.collideWithObstacle(self.actors[fromName]) if fromName == "player" and self.collisionSound.status() is not self.collisionSound.PLAYING: self.collisionSound.play() def handleCubeIntoCollision(self, entry): try: fromName = entry.getFromNodePath().getParent().getName() Unit.collideWithObstacle(self.actors[fromName]) if fromName == "player" and self.collisionSound.status() is not self.collisionSound.PLAYING: self.collisionSound.play() except: pass def handleBarIntoCollision(self, entry): fromName = entry.getFromNodePath().getParent().getName() Unit.collideWithObstacle(self.actors[fromName]) if fromName == "player" and self.collisionSound.status() is not self.collisionSound.PLAYING: self.collisionSound.play() def gameOver(self): pass
def mousePick(status): if pickingEnabled: if status == 'down': smileyModel.setScale(.9) if status == 'up': smileyModel.setScale(1.0) # Here as well we see something already seen in the previous steps related to collision events so I won't go into details. collisionHandler.addInPattern('%fn-into-%in') collisionHandler.addOutPattern('%fn-out-%in') #** Let's manage now the collision events: DO=DirectObject() # if you went from step3 and step4, here should not be mysteries for you DO.accept('mouseraycnode-into-smileycnode', collideEventIn) DO.accept('mouseraycnode-out-smileycnode', collideEventOut) #** This little but important variable will be used to know the picking state at any time - note that the picking is enabled ASA the mouse pointer goes above the ball and disabled when it leave. pickingEnabled=False #** This is how we interact with mouse clicks - see the mousePick function above for details DO.accept('mouse1', mousePick, ['down']) DO.accept('mouse1-up', mousePick, ['up']) #** And at last, we start the task that continuously update the ray collider position and orientation while we move the mouse pointer taskMgr.add(rayupdate, "updatePicker") splash.destroy() run()
class SCTerminal(SCElement): """ SCTerminal is the base class for all 'terminal' speedchat entities """ def __init__(self, linkedEmote=None): SCElement.__init__(self) self.setLinkedEmote(linkedEmote) scGui = loader.loadModel(SCMenu.GuiModelName) self.emotionIcon = scGui.find('**/emotionIcon') self.setDisabled(False) self.__numCharges = -1 # should we listen for whisper mode changes? self._handleWhisperModeSV = StateVar(False) # can't set this up until we're ready to have the handler func called self._handleWhisperModeFC = None def destroy(self): self._handleWhisperModeSV.set(False) if self._handleWhisperModeFC: self._handleWhisperModeFC.destroy() self._handleWhisperModeSV.destroy() SCElement.destroy(self) def privSetSettingsRef(self, settingsRef): SCElement.privSetSettingsRef(self, settingsRef) if self._handleWhisperModeFC is None: self._handleWhisperModeFC = FunctionCall( self._handleWhisperModeSVChanged, self._handleWhisperModeSV) self._handleWhisperModeFC.pushCurrentState() # if this terminal is not whisperable, we need to listen for whisper mode changes self._handleWhisperModeSV.set((self.settingsRef is not None) and (not self.isWhisperable())) def _handleWhisperModeSVChanged(self, handleWhisperMode): if handleWhisperMode: # this terminal can't be whispered. we need to reconstruct # our GUI element when the whisper mode changes # listen for that mode change # create a DirectObject to avoid conflicts with other parts of this # object that are listening for this event self._wmcListener = DirectObject() self._wmcListener.accept( self.getEventName(SCWhisperModeChangeEvent), self._handleWhisperModeChange) else: if hasattr(self, '_wmcListener'): # we no longer need to listen for whisper mode changes self._wmcListener.ignoreAll() del self._wmcListener # make sure our GUI element is appropriate self.invalidate() def _handleWhisperModeChange(self, whisperMode): # whisper mode changed, we need to change our GUI element self.invalidate() # the meat of SCTerminal; inheritors should override this # and perform the appropriate action def handleSelect(self): """ called when the user selects this node """ # send the generic 'something was selected' event messenger.send(self.getEventName(SCTerminalSelectedEvent)) # if we have a linked emote, and it isn't disabled, generate a msg if self.hasLinkedEmote() and self.linkedEmoteEnabled(): messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent), [self.linkedEmote]) def isWhisperable(self): # can this terminal be sent as a whisper message? return True # Some terminal nodes have an emote associated with them, which # should be invoked when the node is selected. def getLinkedEmote(self): return self.linkedEmote def setLinkedEmote(self, linkedEmote): self.linkedEmote = linkedEmote # TODO: we should make sure we're listening for emote # enable state changes if this is set while we're visible self.invalidate() def hasLinkedEmote(self): return (self.linkedEmote is not None) def linkedEmoteEnabled(self): if Emote.globalEmote: return Emote.globalEmote.isEnabled(self.linkedEmote) def getCharges(self): return self.__numCharges def setCharges(self, nCharges): self.__numCharges = nCharges if (nCharges == 0): self.setDisabled(True) # support for disabled terminals def isDisabled(self): return self.__disabled or (self.isWhispering() and not self.isWhisperable()) def setDisabled(self, bDisabled): # make the button 'unclickable' self.__disabled = bDisabled # from SCElement def onMouseClick(self, event): if not self.isDisabled(): SCElement.onMouseClick(self, event) self.handleSelect() def getMinDimensions(self): width, height = SCElement.getMinDimensions(self) if self.hasLinkedEmote(): # add space for the emotion icon width += 1.3 return width, height def finalize(self, dbArgs={}): """ catch this call and influence the appearance of our button """ if not self.isDirty(): return args = {} if self.hasLinkedEmote(): self.lastEmoteIconColor = self.getEmoteIconColor() self.emotionIcon.setColorScale(*self.lastEmoteIconColor) args.update({ 'image': self.emotionIcon, 'image_pos': (self.width - .6, 0, -self.height * .5), }) if self.isDisabled(): args.update({ 'rolloverColor': (0, 0, 0, 0), 'pressedColor': (0, 0, 0, 0), 'rolloverSound': None, 'clickSound': None, 'text_fg': self.getColorScheme().getTextDisabledColor() + (1, ), }) args.update(dbArgs) SCElement.finalize(self, dbArgs=args) def getEmoteIconColor(self): if self.linkedEmoteEnabled() and (not self.isWhispering()): r, g, b = self.getColorScheme().getEmoteIconColor() else: r, g, b = self.getColorScheme().getEmoteIconDisabledColor() return (r, g, b, 1) def updateEmoteIcon(self): if hasattr(self, 'button'): self.lastEmoteIconColor = self.getEmoteIconColor() for i in range(self.button['numStates']): self.button['image%s_image' % i].setColorScale(*self.lastEmoteIconColor) else: self.invalidate() # from SCObject def enterVisible(self): SCElement.enterVisible(self) # Check if the emote state has changed since the last time # we were finalized, and invalidate if it's different. if hasattr(self, 'lastEmoteIconColor'): if self.getEmoteIconColor() != self.lastEmoteIconColor: self.invalidate() # listen for whisper-mode changes def handleWhisperModeChange(whisperMode, self=self): if self.hasLinkedEmote(): # we are leaving or entering whisper mode; # the appearance of our emote icon needs to change # (no linked emotes on whispers) if self.isVisible() and not self.isWhispering(): self.updateEmoteIcon() self.accept(self.getEventName(SCWhisperModeChangeEvent), handleWhisperModeChange) # listen for emote-enable state changes def handleEmoteEnableStateChange(self=self): if self.hasLinkedEmote(): # emotions have just become enabled/disabled # update our emote icon # (no emotes when whispering) if self.isVisible() and not self.isWhispering(): self.updateEmoteIcon() if self.hasLinkedEmote(): if Emote.globalEmote: self.accept(Emote.globalEmote.EmoteEnableStateChanged, handleEmoteEnableStateChange) def exitVisible(self): SCElement.exitVisible(self) self.ignore(self.getEventName(SCWhisperModeChangeEvent)) if Emote.globalEmote: self.ignore(Emote.globalEmote.EmoteEnableStateChanged) def getDisplayText(self): if self.getCharges() != -1: return self.text + " (%s)" % self.getCharges() return self.text
# inside the scrollable window. # 0 : does not block mouse wheel events from being sent to all other objects. # You can scroll the window by holding down the modifier key # (defined below) while scrolling your wheel. modifier='control' # shift/control/alt ) # populate the list with alphabets and numbers for a in string.ascii_letters+string.digits: # extraArgs would be passed to command and contextMenu btnList.addItem(text='item '+(a+' ')*10, extraArgs=a, atIndex=None) btnList.setAutoFocus(1) # after adding those items, # enable auto view-focus on newly added item DO=DirectObject() # events for btnList DO.accept('h',btnList.hide) DO.accept('s',btnList.show) DO.accept('space',btnList.toggleVisibility) DO.accept('delete',btnList.destroy) DO.accept('enter',addNewItem) DO.accept('enter-repeat',addNewItem) DO.accept('insert',insertNewItemAtSelected) DO.accept('insert-repeat',insertNewItemAtSelected) DO.accept('control-insert',insertNewItemBelowSelected) DO.accept('control-insert-repeat',insertNewItemBelowSelected) DO.accept('caps_lock-repeat',disableRandomItem) DO.accept('tab-repeat',enableRandomItem) DO.accept('x-repeat',removeRandomItem) # events for the world DO.accept('wheel_up',teapot.setScale,[teapot,1.2]) DO.accept('wheel_down',teapot.setScale,[teapot,.8])
but then we should have used 2 accepts like this: DO.accept('ray1-again-smileys', collideAgainBalls) DO.accept('ray1-again-frowney', collideAgainBalls) instead of just one as we did below, and this don't hurt very much in this snippet cos' we got just 2 groups, but could be complicated if we need to use a lot more groups. Another big thing to note is that we could have been done all of this using the masking technique (see step4.py). """ #** to manage the collision events need Directobject, because the chaining between the CollisionHandlerEvent handler and our two functions seen above, happens with the accept() function of a DirectObject instance DO = DirectObject() #** Let's manage the collision events: as you can see in this rack of 4 accepts, these are the resulting events generated by the patterns defined above. Now it's up to you to understand the why an the how comparing these with those above. DO.accept('ray1-into-smiley', collideInSmiley) DO.accept('ray1-out-smiley', collideOutSmiley) DO.accept('ray1-into-frowney', collideInFrowney) DO.accept('ray1-out-frowney', collideOutFrowney) # the event here instead, will call the collideAgainBalls function handler while the mouse pointer keep over any ball, either of the smiley or the frowney groups - still, check above how we defined the pattern string to understand how we came to this. DO.accept('ray_again_all', collideAgainBalls) #** Storage for the object actually hovered by the mouse pointer pickingEnabledOject = None #** Here's how we interact with mouse clicks - see the mouseClick function above DO.accept('mouse1', mouseClick, ['down']) DO.accept('mouse1-up', mouseClick, ['up']) #** And at the end of all, we start the task that continuously update the ray collider position and orientation while we move the mouse pointer taskMgr.add(rayupdate, "updatePicker")
class SCTerminal(SCElement): def __init__(self, linkedEmote = None): SCElement.__init__(self) self.setLinkedEmote(linkedEmote) scGui = loader.loadModel(SCMenu.GuiModelName) self.emotionIcon = scGui.find('**/emotionIcon') self.setDisabled(False) self.__numCharges = -1 self._handleWhisperModeSV = StateVar(False) self._handleWhisperModeFC = None return def destroy(self): self._handleWhisperModeSV.set(False) if self._handleWhisperModeFC: self._handleWhisperModeFC.destroy() self._handleWhisperModeSV.destroy() SCElement.destroy(self) def privSetSettingsRef(self, settingsRef): SCElement.privSetSettingsRef(self, settingsRef) if self._handleWhisperModeFC is None: self._handleWhisperModeFC = FunctionCall(self._handleWhisperModeSVChanged, self._handleWhisperModeSV) self._handleWhisperModeFC.pushCurrentState() self._handleWhisperModeSV.set(self.settingsRef is not None and not self.isWhisperable()) return def _handleWhisperModeSVChanged(self, handleWhisperMode): if handleWhisperMode: self._wmcListener = DirectObject() self._wmcListener.accept(self.getEventName(SCWhisperModeChangeEvent), self._handleWhisperModeChange) elif hasattr(self, '_wmcListener'): self._wmcListener.ignoreAll() del self._wmcListener self.invalidate() def _handleWhisperModeChange(self, whisperMode): self.invalidate() def handleSelect(self): messenger.send(self.getEventName(SCTerminalSelectedEvent)) if self.hasLinkedEmote() and self.linkedEmoteEnabled(): messenger.send(self.getEventName(SCTerminalLinkedEmoteEvent), [self.linkedEmote]) def isWhisperable(self): return True def getLinkedEmote(self): return self.linkedEmote def setLinkedEmote(self, linkedEmote): self.linkedEmote = linkedEmote self.invalidate() def hasLinkedEmote(self): return self.linkedEmote is not None def linkedEmoteEnabled(self): if Emote.globalEmote: return Emote.globalEmote.isEnabled(self.linkedEmote) def getCharges(self): return self.__numCharges def setCharges(self, nCharges): self.__numCharges = nCharges if nCharges is 0: self.setDisabled(True) def isDisabled(self): return self.__disabled or self.isWhispering() and not self.isWhisperable() def setDisabled(self, bDisabled): self.__disabled = bDisabled def onMouseClick(self, event): if not self.isDisabled(): SCElement.onMouseClick(self, event) self.handleSelect() def getMinDimensions(self): width, height = SCElement.getMinDimensions(self) if self.hasLinkedEmote(): width += 1.3 return (width, height) def finalize(self, dbArgs = {}): if not self.isDirty(): return args = {} if self.hasLinkedEmote(): self.lastEmoteIconColor = self.getEmoteIconColor() self.emotionIcon.setColorScale(*self.lastEmoteIconColor) args.update({'image': self.emotionIcon, 'image_pos': (self.width - 0.6, 0, -self.height * 0.5)}) if self.isDisabled(): args.update({'rolloverColor': (0, 0, 0, 0), 'pressedColor': (0, 0, 0, 0), 'rolloverSound': None, 'clickSound': None, 'text_fg': self.getColorScheme().getTextDisabledColor() + (1,)}) args.update(dbArgs) SCElement.finalize(self, dbArgs=args) return def getEmoteIconColor(self): if self.linkedEmoteEnabled() and not self.isWhispering(): r, g, b = self.getColorScheme().getEmoteIconColor() else: r, g, b = self.getColorScheme().getEmoteIconDisabledColor() return (r, g, b, 1) def updateEmoteIcon(self): if hasattr(self, 'button'): self.lastEmoteIconColor = self.getEmoteIconColor() for i in xrange(self.button['numStates']): self.button['image%s_image' % i].setColorScale(*self.lastEmoteIconColor) else: self.invalidate() def enterVisible(self): SCElement.enterVisible(self) if hasattr(self, 'lastEmoteIconColor'): if self.getEmoteIconColor() != self.lastEmoteIconColor: self.invalidate() def handleWhisperModeChange(whisperMode, self = self): if self.hasLinkedEmote(): if self.isVisible() and not self.isWhispering(): self.updateEmoteIcon() self.accept(self.getEventName(SCWhisperModeChangeEvent), handleWhisperModeChange) def handleEmoteEnableStateChange(self = self): if self.hasLinkedEmote(): if self.isVisible() and not self.isWhispering(): self.updateEmoteIcon() if self.hasLinkedEmote(): if Emote.globalEmote: self.accept(Emote.globalEmote.EmoteEnableStateChanged, handleEmoteEnableStateChange) def exitVisible(self): SCElement.exitVisible(self) self.ignore(self.getEventName(SCWhisperModeChangeEvent)) if Emote.globalEmote: self.ignore(Emote.globalEmote.EmoteEnableStateChanged) def getDisplayText(self): if self.getCharges() is not -1: return self.text + ' (%s)' % self.getCharges() else: return self.text
colliderINTO = entry.getIntoNodePath().getParent() # we now may change the aspect of the two colliding objects colliderINTO.setColor(1, 1, 1, 1) colliderFROM.setScale(1.5) #... and this when they leave each other alone. def collideEventOut(entry): colliderFROM = entry.getFromNodePath().getParent() colliderINTO = entry.getIntoNodePath().getParent() colliderINTO.setColor(.4, .4, .4, 1) colliderFROM.setScale(1.0) #** Here's the definition of the patterns that should catch our collisions - note that just these two are enough to catch the 2 couple of combinations. collisionHandler.addInPattern('%fn-into-%in') collisionHandler.addOutPattern('%fn-out-%in') #** Let's manage the collision events: DO = DirectObject() # we should provide a couple of event strings for each couple of interacting objects (remember we made the smiley react against the whole heart and the frowney with the broken one). Compare these strings with the patterns above and if it is not enough to understand why it works, go back and dig again step2 and step3 until they're clear for you. # Note that we route the in and out events to the same functions for both couples but that is just to slim the code. DO.accept('collider_heart-into-smileycnode', collideEventIn) DO.accept('collider_heart-out-smileycnode', collideEventOut) # DO.accept('collider_brkheart-into-frowneycnode', collideEventIn) DO.accept('collider_brkheart-out-frowneycnode', collideEventOut) splash.destroy() run()
class ScreenManager(object): ''' Maintain all instances of screens within this class. Screen order is as follows: ScreenManager - render (root node) - modal node - individual screen nodes Each screen has its own node in the tree which is the parent of all objects drawn on that screen Changing screens is merely done by hiding the individual screen's parent node and show()ing the next screen's parent object node ''' def __init__(self, game): ''' Set up a 2d node and a 3d node for drawing 2d and 3d objects. All objects must be parented to their respective nodes in order to be rendered ''' self.game = game self._flipNode = render.attachNewNode("flip_node") self._flipNode2d = aspect2d.attachNewNode("flip_node2d") if base.displayFlipped: self._flipNode.setScale(-1, 1, 1) self._flipNode2d.setScale(-1, 1, 1) self._node = self._flipNode.attachNewNode(PandaNode("screen_manager")) self._2dnode = self._flipNode2d.attachNewNode( PandaNode("2dscreen_manager")) self.logger = logging.getLogger("ScreenManager") self.logger.info("Initializing Screen Manager") # Create a dictionary to hold our screens self.screens = {} self.modal_screens = {} # This shows the frame rate meter at the top right of the screen for diagnostic purposes base.setFrameRateMeter(False) self.modaltext = None self.modal_text_color = "" self.modal_text_blink_status = False self.obj = DirectObject() self.debug_text = OnscreenText("", 1, font=base.fontLoader.load('arial.ttf'), fg=(1, 1, 1, 1), pos=(1.2, -0.9), align=TextNode.ARight, scale=.05, mayChange=True, parent=self._2dnode) self.debug_text.hide() def loadScreens(self): ''' Load all screens into the 'screens' dictionary for quick access. ''' self.screens['boot'] = BootScreen(self) self.screens['svc_main_old'] = MainMenu(self) self.screens['svc_diagnostics'] = DiagnosticsMenu(self) self.screens['svc_update'] = UpdateCode(self) self.screens['svc_3d'] = ThreeDPlacement(self) self.screens['svc_main'] = MainMenu(self) self.screens['svc_flashers'] = Flashers(self) self.screens['svc_switches'] = Switches(self) self.screens['svc_log'] = Log(self) self.screens['stack'] = StackScreen(self) self.screens['attract1'] = PCCScreen(self) self.screens['attract2'] = DMIntroScreen(self) self.screens['attract3'] = HighScoreDisplay(self) self.screens['attract4'] = GameScoreDisplay(self) self.screens['attract5'] = ThanksDisplay(self) self.screens['blocks'] = BlocksScreen(self) self.screens['skillshot'] = SkillShotScreen(self) self.screens['score'] = ScoreScreen(self) self.screens['bonus'] = BonusScreen(self) self.screens['computer'] = ComputerScreen(self) self.screens['acmag'] = AcmagScreen(self) self.screens['match'] = MatchScreen(self) self.screens['wtsa'] = SanAngelesScreen(self) self.screens['carchase'] = CarChaseScreen(self) self.screens['extraball'] = ExtraBallScreen(self) self.screens['claw'] = ClawScreen(self) self.screens['simon_says'] = SimonSaysScreen(self) def getScreen(self, name): return self.screens[name] def registerEvents(self): self.logger.info("Registering event handlers") base.accept("update_score", self._onScoreUpdate) self.obj.accept("show_screen", self._thread_safe_showScreen) self.obj.accept("update_ball", self._onBallUpdate) def _onScoreUpdate(self, player, score): self.logger.info("onScoreUpdate triggered") self.screens['score'].update_score(player, score) def _onBallUpdate(self, ball): self.logger.info("onBallUpdate triggered") self.screens['score'].update_ball(ball) def showScreen(self, screen_name, hideOthers=True, hideScore=False): self.logger.info( "screenmanager::showScreen Thread: %s Screen: %s" % (str(threading.current_thread().getName()), screen_name)) #base.messenger.send("show_screen", [screen_name, hideOthers, hideScore]) #print base.display_queue base.display_queue.put_nowait( partial(self._thread_safe_showScreen, screen_name=screen_name, hideOthers=hideOthers, hideScore=hideScore)) def getShownScreens(self): shown_screens = [] for screen in self.screens: if not self.screens[screen].is_hidden(): shown_screens.append(screen) return shown_screens def _thread_safe_showScreen(self, screen_name, hideOthers=True, hideScore=False): ''' Show the given screen. If hideOthers is true, all other screens are hidden before showing the desired screen. ''' if not screen_name in self.screens: self.logger.warning("Tried to show screen %s, not in screen list", screen_name) if hideOthers: for screen in self.screens: if screen != 'score': self.screens[screen].hide() if hideScore: self.screens['score'].hide() # Show the given screen self.screens[screen_name].show() self.logger.info( "Showing screen %s Thread %s" % (screen_name, str(threading.current_thread().getName()))) def hideScreen(self, screen_name): self.logger.info("screenmanager::hideScreen %s" % (screen_name)) base.display_queue.put_nowait( partial(self._thread_safe_hideScreen, screen_name=screen_name)) def hideAllScreens(self): base.display_queue.put_nowait(partial( self._thread_safe_hideAllScreens)) def _thread_safe_hideAllScreens(self): for screen in self.screens: self.screens[screen].hide() def _thread_safe_hideScreen(self, screen_name): self.screens[screen_name].hide() def getNode(self): ''' Return the 3d node for this screen manager. This is useful for screens to obtain a node to parent objects to for drawing (done in the GameScreen constructor) ''' return self._node def get2dNode(self): ''' Return the 2d node for this screen manager. ''' return self._2dnode def showDebugMessage(self, message): base.taskMgr.remove('hide_debugmsg') self.debug_text.setText(message) self.debug_text.show() base.taskMgr.doMethodLater(10, self.hideDebugMessage, 'hide_debugmsg', extraArgs=[]) def hideDebugMessage(self): self.debug_text.hide() def showModalMessage(self, message, modal_name="", time=0, scale=0.2, font="arial.ttf", fg=(1, 1, 0, 1), bg=(0, 0, 0, 1), frame_color=(1, 1, 0, 1), animation="none", start_location=None, end_location=None, animation_duration=2, blink_speed=0, blink_color=(0, 0, 0, 1), frame_blink_color=None, frame_width=4, frame_margin=(0.1, 0.1, 0.1, 0.1)): #cardMaker.setFrame(-sizeX/2.0, sizeX/2.0, -sizeY/2.0, sizeY/2.0) if modal_name in self.modal_screens: self._hideModalMessage(modal_name) base.taskMgr.remove('hide_' + modal_name) if modal_name == "": modal_name = "modal_" + str(random.randint(1, 63463)) modal_object = {} modal_object['modaltext'] = TextNode('modal') modal_object['modaltext'].setText(message) modal_object['modal_text_color'] = fg modal_object['blink_color'] = blink_color modal_object['modaltext'].setFont(base.fontLoader.load(font)) modal_object['modaltext'].setTextColor(fg[0], fg[1], fg[2], fg[3]) modal_object['modaltext'].setAlign(TextNode.ACenter) modal_object['blink_status'] = False if frame_blink_color == None: frame_blink_color = frame_color # Set our text shadows #self.modaltext.setShadow(0.05, 0.05) #self.modaltext.setShadowColor(0, 0, 0, 1) # Set the border modal_object['modaltext'].setFrameColor(frame_color[0], frame_color[1], frame_color[2], frame_color[3]) modal_object['modaltext'].setFrameAsMargin(frame_margin[0], frame_margin[1], frame_margin[2], frame_margin[3]) modal_object['modaltext'].setFrameLineWidth(frame_width) # Set the background styling up modal_object['modaltext'].setCardColor(bg[0], bg[1], bg[2], bg[3]) modal_object['modaltext'].setCardAsMargin(0, 0, 0, 0) modal_object['modaltext'].setCardDecal(True) modal_object['frame_color'] = frame_color modal_object['frame_blink_color'] = frame_blink_color modal_object['modaltextNodePath'] = self._2dnode.attachNewNode( modal_object['modaltext'], 20) modal_object['modaltextNodePath'].setScale(scale) if start_location != None: modal_object['modaltextNodePath'].setPos(start_location) if animation == "easeIn" or animation == "up": interval = LerpPosInterval(modal_object['modaltextNodePath'], pos=end_location, startPos=start_location, blendType='easeIn', duration=animation_duration).start() if animation == "slide": seq = Sequence( LerpPosInterval(modal_object['modaltextNodePath'], pos=end_location, startPos=start_location, blendType='easeOut', duration=0.8), Wait(time - 1.6), LerpPosInterval(modal_object['modaltextNodePath'], pos=start_location, startPos=end_location, blendType='easeIn', duration=0.8)).start() self.modal_screens[modal_name] = modal_object # Set our timer so this message can expire if time is > 0 if time > 0: base.taskMgr.doMethodLater(time, self._hideModalMessage, 'hide_' + modal_name, extraArgs=[modal_name]) if blink_speed > 0: base.taskMgr.doMethodLater(blink_speed, self._blinkModalMessage, 'blink_' + modal_name, extraArgs=[modal_name]) def _blinkModalMessage(self, modal_name): self.modal_screens[modal_name][ 'blink_status'] = not self.modal_screens[modal_name]['blink_status'] if self.modal_screens[modal_name]['blink_status']: self.modal_screens[modal_name]['modaltext'].setTextColor( self.modal_screens[modal_name]['modal_text_color'][0], self.modal_screens[modal_name]['modal_text_color'][1], self.modal_screens[modal_name]['modal_text_color'][2], self.modal_screens[modal_name]['modal_text_color'][3]) self.modal_screens[modal_name]['modaltext'].setFrameColor( self.modal_screens[modal_name]['frame_color'][0], self.modal_screens[modal_name]['frame_color'][1], self.modal_screens[modal_name]['frame_color'][2], self.modal_screens[modal_name]['frame_color'][3]) else: self.modal_screens[modal_name]['modaltext'].setTextColor( self.modal_screens[modal_name]['blink_color'][0], self.modal_screens[modal_name]['blink_color'][1], self.modal_screens[modal_name]['blink_color'][2], self.modal_screens[modal_name]['blink_color'][3]) self.modal_screens[modal_name]['modaltext'].setFrameColor( self.modal_screens[modal_name]['frame_blink_color'][0], self.modal_screens[modal_name]['frame_blink_color'][1], self.modal_screens[modal_name]['frame_blink_color'][2], self.modal_screens[modal_name]['frame_blink_color'][3]) return Task.again def hideModalMessage(self, modal_name): if not modal_name in self.modal_screens: return self.modal_screens[modal_name]['modaltextNodePath'].removeNode() del self.modal_screens[modal_name] base.taskMgr.remove('blink_' + modal_name) def clearModalMessages(self): for modal_name in self.modal_screens.copy(): self.hideModalMessage(modal_name) def isModalBeingShown(self): return len(self.modal_screens) > 0 def _hideModalMessage(self, modal_name): self.hideModalMessage(modal_name)
class EnemyBike(Bike): def __init__(self, cTrav, cevent): #messenger.toggleVerbose() Bike.__init__(self, cTrav) self.bike.setPos(10, 0, 0) self.initAI() self.hp = 10 self.singleShot = base.loader.loadSfx('50Cal.mp3') self.shooting = 0 self.decshooting = True self.targeting = 0 self.dectargeting = True frombikemask = BitMask32(0x10) intobikemask = BitMask32.allOff() floormask = BitMask32(0x2) self.cevent = CollisionHandlerEvent() self.cevent.addInPattern('%fn-into-%in') self.cevent.addOutPattern('%fn-out-%in') self.bullettrace = self.bike.attachNewNode(CollisionNode('aimtrace')) self.bullettrace.node().addSolid(CollisionRay(0, 0, 0, 0, -1, .1)) self.bullettrace.node().setFromCollideMask(frombikemask) self.bullettrace.node().setIntoCollideMask(intobikemask) #self.bullettrace.show() #base.cTrav.addCollider(self.bullettrace, self.bike) base.cTrav.addCollider(self.bullettrace, self.cevent) self.gravtrace = self.bike.attachNewNode(CollisionNode('colNode')) self.gravtrace.node().addSolid(CollisionRay(0, 0, 0, 0, 0, -1)) self.gravtrace.node().setFromCollideMask(floormask) self.gravtrace.node().setIntoCollideMask(BitMask32.allOff()) #self.gravtrace.show() self.vistrace = self.bike.attachNewNode(CollisionNode('vistrace')) self.vistrace.node().addSolid(CollisionRay(0, 0, 0, 0, -1, .15)) #self.vistrace.node().addSolid(CollisionRay(0, 0, 0, 1, 1, 0)) #self.vistrace.node().addSolid(CollisionRay(0, 0, 0, -1, 1, 0)) self.vistrace.node().addSolid(CollisionRay(0, 0, 0, 1, -1, .15)) self.vistrace.node().addSolid(CollisionRay(0, 0, 0, -1, -1, .15)) self.vistrace.node().setFromCollideMask(frombikemask) self.vistrace.node().setIntoCollideMask(intobikemask) #self.vistrace.show() #base.cTrav.addCollider(self.vistrace, self.bike) base.cTrav.addCollider(self.vistrace, self.cevent) self.lifter = CollisionHandlerFloor() self.lifter.setMaxVelocity(9.8) base.cTrav.addCollider(self.gravtrace,self.lifter) self.lifter.addCollider(self.gravtrace, self.bike) self.do = DirectObject() self.do.accept('vistrace-into-p_bike_push', self.visIn) self.do.accept('vistrace-out-p_bike_push', self.visOut) self.do.accept('aimtrace-into-p_bike_push', self.aimIn) self.do.accept('aimtrace-out-p_bike_push', self.aimOut) self.prev = self.bike.getPos() def initAI(self): self.AIchar = AICharacter("Enemy Bike", self.bike, 100, 0.05, 100) self.AIbehaviors = self.AIchar.getAiBehaviors() self.AImode = 'scan' self.target = None """ l = loader.loadModel("shield.egg") l.reparentTo(render) l.setPos(70.0, 70.0, 0.0) l = loader.loadModel("shield.egg") l.reparentTo(render) l.setPos(70.0, -70.0, 0.0) l = loader.loadModel("shield.egg") l.reparentTo(render) l.setPos(-70.0, 70.0, 0.0) l = loader.loadModel("shield.egg") l.reparentTo(render) l.setPos(-70.0, -70.0, 0.0) """ def update(self): #self.shoot() if self.AImode == 'flee': if random.randint(1, 60) == 1: self.lightsToggle() if (self.bike.getPos() - self.prev).length() > 0.01: self.bike.loop("move") else: #self.bike.stop() pass self.prev = self.bike.getPos() if self.shooting > 0: self.shoot() if self.decshooting: self.shooting -= 1 if self.targeting > 0: if self.dectargeting: self.targeting -= 1 if self.targeting == 0: self.setMode('scan') if self.hp <= 1: self.setMode('flee') #print "shooting: " + str(self.shooting) + " (" + str(self.decshooting) + ")" #print "targeting: " + str(self.targeting) + " (" + str(self.dectargeting) + ")" def shoot(self): if self.shotClock >= 25: #create a bullet self.singleShot.play() #this may be the worst thing ever self.bike.setH((self.bike.getH() - 180.0) % 360.0) self.bullet.createBullet(self.bike) self.bike.setH((self.bike.getH() + 180.0) % 360.0) self.shotClock = 0 else: self.shotClock += 1 def setMode(self, mode): self.AImode = mode #self.AIbehaviors.removeAi("all") mag = 70.0 r1 = 10.0 r2 = 2.0 #self.AIbehaviors.flee(Vec3(mag, mag, 0.0), r1, r2, 1.0) #self.AIbehaviors.flee(Vec3(mag, -mag, 0.0), r1, r2, 1.0) #self.AIbehaviors.flee(Vec3(-mag, mag, 0.0), r1, r2, 1.0) #self.AIbehaviors.flee(Vec3(-mag, -mag, 0.0), r1, r2, 1.0) #print self.AImode if self.AImode == 'target': #self.AIchar.setMaxForce(100); self.AIbehaviors.wander(0.5, 0, 17, 0.25) self.AIbehaviors.pursue(self.target.dummy, 0.5) self.AIbehaviors.evade(self.target.dummy, 4.0, 2.5, 0.95) elif self.AImode == 'flee': #self.AIchar.setMaxForce(100); self.AIbehaviors.wander(1.0, 0, 17, 0.5) self.AIbehaviors.pursue(self.target.dummy, 0.0) self.AIbehaviors.evade(self.target.dummy, 3.0, 6.0, 1.0) elif self.AImode == 'scan': #self.AIchar.setMaxForce(100); self.AIbehaviors.wander(0.5, 0, 17, 0.35) self.AIbehaviors.pursue(self.target.dummy, 0.5) self.AIbehaviors.evade(self.target.dummy, 3.0, 2.5, 0.85) def aimIn(self, event): #print length(self.bike.getPos(), event.getFromNodePath().getPos()) #self.AImode = 'target' #print event.getFromNodePath().getParent().AImode #print event self.shooting = 60 self.decshoot = False def aimOut(self, event): #print event self.decshoot = True def visIn(self, event): #print self.physNode.getPos() #print (self.bike.getPos() - event.getIntoNodePath().getPos()).length() self.setMode('target') self.targeting = 5 self.dectargeting = False def visOut(self, event): self.dectargeting = True
# ** This is a task function called each frame, where the collision ray position is syncronized with the mouse pointer position and, more important, we traverse our collision traverser to make its routines check the collisions. def rayupdate(task): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() # here the meat: differently from step 6, we do not call setFromLens to shoot our ray in 3d space from the camera lens, but rather we move the ray along the cursor position and shoot a straight ray from there along Y axis. This is because in orthographic view there is no perspective and therefore a ray is shoot parallel to its starting point pickerNP.setPos(render2d, mpos[0], 0, mpos[1]) # and here we run the traverse step, to check the collisions in our scene customCtrav.traverse(render2d) return task.cont # ** to manage the collision and mouse button events DO = DirectObject() # ** Let's manage the collision events: as you can see these are the events generated by the patterns defined above, a fixed string this time so no surprises here. DO.accept("ray-into-cards", collideInCard) DO.accept("ray-out-cards", collideOutCard) # ** Storage for the object eventually below the mouse pointer pickingEnabledOject = None # ** Here's how we interact with mouse clicks - see the mouseClick function above DO.accept("mouse1", mouseClick, ["down"]) DO.accept("mouse1-up", mouseClick, ["up"]) # ** And here we start the task that continuously update the ray collider position and make traverse the collision traverser in 2d space taskMgr.add(rayupdate, "updatePicker") splash.destroy() run()
class Graphics_Data(): def __init__(self, golog, simplex, **kwargs): self.simplex = simplex self.golog = golog self.messenger = self.golog.base.messenger self.listener = DirectObject( ) # might let Graphics_Data Inherit from Direct_Object self.graphics_kwargs = dict( ) # keeping track of information needed to re-create graphics_data #things to clean self for deletion: self.node_list = [] self.listeners = [self.listener] if simplex.level == 0: self.NP = golog.render.attachNewNode(simplex.label) self.node_list.append(self.NP) self.NP.setTag( 'level', '0') # to tell mode_heads what type of simplex this is golog.sphere.instanceTo(self.NP) self.collision = self.NP.attachNewNode( CollisionNode('sphereColNode')) self.collision.node().addSolid(CollisionSphere(0, 0, 0, 1)) self.messenger_names = {'node': str(id(self.NP))} #detail parents self.parents = ( ) #need to give no parents for a unified update function self.parent_pos_convolution = lambda *x: golog.render.getPos( ) # function of parent's [node] positions to detail offset (0-simlex it's just render location) #listener for parental updates, pass arguemnts through extraKwargs to detail what kind of update to perform for parent in self.parents: self.listener.accept(self.parents.messenger_names['node'], self.update) #set position if 'pos' in kwargs.keys(): self.update({'pos': kwargs['pos']}) else: self.update({'pos': LPoint3f(0, 0, 0)}) elif simplex.level == 1: self.NP = golog.render.attachNewNode(simplex.label) self.node_list.append(self.NP) self.NP.setTag('level', '1') self.golog.cone.instanceTo(self.NP) self.collision = self.NP.attachNewNode( CollisionNode('coneColNode')) self.collision.node().addSolid(CollisionSphere(0, 0, 0, 1)) self.messenger_names = {'node': str(id(simplex))} self.graphics = (Rope(), Rope()) #two ropes :) for rope in self.graphics: self.node_list.append(rope) #set up parents self.parents = tuple(golog.Simplex_to_Graphics[face] for face in self.simplex.faces) def tuple_avg(tuples): b = LPoint3f(0, 0, 0) for a in tuples: b = b + a return b / len(tuples) self.parent_pos_convolution = lambda *x: tuple_avg( tuple(parent.NP.getPos() for parent in self.parents)) for parent in self.parents: self.listener.accept(parent.messenger_names['node'], self.update) if 'pos' in kwargs.keys(): if isinstance(kwargs['pos'], tuple): pos = LPoint3f(*kwargs['pos']) else: pos = kwargs['pos'] else: pos = LPoint3f(0, 0, 0) self.graphics_kwargs['pos'] = pos #create shitty control nodes for rope module (I know, this is not pretty) self.control_nodes = ( self.golog.render.attachNewNode(simplex.label + '_control_node0'), self.golog.render.attachNewNode(simplex.label + '_control_node1')) for node in self.control_nodes: self.node_list.append(node) control_listener = DirectObject() self.listeners.append(control_listener) def control_updator(*x): for i in [0, 1]: self.control_nodes[i].setPos(self.graphics_kwargs['pos'] + self.parents[i].NP.getPos()) control_updator() control_listener.accept(self.messenger_names['node'], control_updator) self.update({'pos': None}) #set up rope graphics self.graphics[0].setup(3, [(self.NP, (0, 0, 0)), (self.control_nodes[0], (0, 0, 0)), (self.parents[0].NP, (0, 0, 0))]) self.graphics[1].setup(3, [(self.parents[1].NP, (0, 0, 0)), (self.control_nodes[1], (0, 0, 0)), (self.NP, (0, 0, 0))]) for rope in self.graphics: rope.reparentTo(golog.render) #set up dictionary references self.golog.Simplex_to_Graphics[simplex] = self self.golog.Graphics_to_Simplex[self] = simplex self.golog.NP_to_Graphics[self.NP] = self #create an invisible textNode that can be shown if called text = TextNode(self.simplex.label + '_text_node') #? make this wrap if too long text.setText(self.simplex.label) text.setCardDecal(True) text.setCardColor(.5, 1, .5, 1) text.setCardAsMargin(0, 0, 0, 0) text.setTextColor(0, 0, 0, 1) self.textNP = self.NP.attachNewNode(text) #? make this update to always be in front of camera # - Either get it into 2d plane, or make a z axis that always faces the camera and attach to that self.textNP.setPos(0, -1, 0) self.textNP.show() self.node_list.append(self.textNP) #function that cleans up references and listeners for deletion #!!! THIS SHOULD NOT BE CALLED UNLESS DELETING UNDERLYING SIMPLEX AS WELL !!!# def _remove(self): #first should clean the nodes it supports (done through hcat) simplex = self.simplex supports = [*simplex.supports] for simp in supports: self.golog.Simplex_to_Graphics[simp]._remove() #delete listeners for listener in self.listeners: listener.ignoreAll() del listener #delete relevant nodes for node in self.node_list: node.removeNode() #delete dictionary references del self.golog.Simplex_to_Graphics[simplex] del self.golog.Graphics_to_Simplex[self] del self.golog.NP_to_Graphics[self.NP] #remove simplex from sSet self.golog.sSet.remove(simplex) #listener calls update with some data def update(self, kwargs): if 'pos' in kwargs: #if an offset is provided, change the graphics_kwargs, if not, leave them if kwargs['pos']: self.graphics_kwargs['pos'] = kwargs['pos'] newpos = self.parent_pos_convolution() + self.graphics_kwargs[ 'pos'] #set a new offset base on parent positions self.NP.setPos(newpos) self.messenger.send(self.messenger_names['node'], [{'pos': None}]) #supress attribute errors def __getattr__(self, attr): return None
class CollisionManager: def __init__(self): self.line_dir = NodePath() base.cTrav = CollisionTraverser() self.col_handler = CollisionHandlerEvent() picker_node = CollisionNode("mouseRayNode") pickerNPos = base.camera.attachNewNode(picker_node) self.pickerRay = CollisionRay() picker_node.addSolid(self.pickerRay) plane_node = CollisionNode("base_plane") plane = base.render.attachNewNode(plane_node) self.plane_col = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, 0))) picker_node.addSolid(self.pickerRay) picker_node.setTag("rays","mray") base.cTrav.addCollider(pickerNPos, self.col_handler) self.col_handler.addInPattern("%(rays)ft-into-%(type)it") self.col_handler.addOutPattern("%(rays)ft-out-%(type)it") self.col_handler.addAgainPattern("ray_again_all%(""rays"")fh%(""type"")ih") self.model = loader.loadModel("../models/chest.egg") self.model_node = NodePath("sdfafd") self.model.reparentTo(self.model_node) self.model_node.reparentTo(render) # # self.text_node = TextNode("battle_text") # self.text_node.setText("TEXTYTEXTYTEXTTEXT") # self.text_node_path = render.attachNewNode(self.text_node) # self.text_node_path.reparentTo(render) # self.text_node_path.setPos(0,0,4) # self.text_node_path.setHpr(0,0,0) # self.text_node_path.setScale(1) # #self.text_node_path.setTransparency(TransparencyAttrib.MAlpha) # self.text_node.setTextColor((1,1,1,1)) # self.text_node.setAlign(TextNode.ALeft) self.placement_ghost = EditorObjects.PlacementGhost(0,"tower",base.object_scale) z=0 self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, z)) taskMgr.add(self.ray_update, "updatePicker") taskMgr.add(self.get_mouse_plane_pos, "MousePositionOnPlane") taskMgr.add(self.task_mouse_press_check, "checkMousePress") self.input_init() self.pickable=None def input_init(self): self.DO=DirectObject() self.DO.accept('mouse1', self.mouse_click, ["1-down"]) self.DO.accept('mouse1-up', self.mouse_click, ["1-up"]) self.DO.accept('mouse3', self.mouse_click, ["3-down"]) self.DO.accept('0', self.placement_ghost.change_player, [0]) self.DO.accept('1', self.placement_ghost.change_player, [1]) self.DO.accept('2', self.placement_ghost.change_player, [2]) self.DO.accept('a', self.placement_ghost.change_type, ["army"]) self.DO.accept('t', self.placement_ghost.change_type, ["tower"]) self.DO.accept('control-s', base.xml_manager.save) self.DO.accept('mray-into-army', self.col_in_object) self.DO.accept('mray-out-army', self.col_out_object) self.DO.accept('mray-into-tower', self.col_in_object) self.DO.accept('mray-out-tower', self.col_out_object) self.DO.accept('ray_again_all', self.col_against_object) def col_against_object(self,entry): if entry.getIntoNodePath().getParent() != self.pickable: np_from=entry.getFromNodePath() np_into=entry.getIntoNodePath() self.selected_type = np_into.getTag("type") self.pickable = np_into.getParent() def col_in_object(self,entry): if base.state == "selecting": np_into=entry.getIntoNodePath() np_into.getParent().setColor(0.5,0.5,0.5,1) def col_out_object(self,entry): np_into=entry.getIntoNodePath() try: np_into.getParent().clearColor() except: print "ERROR CLEARING COLOUR" def get_mouse_plane_pos(self, task): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pos3d = Point3() nearPoint = Point3() farPoint = Point3() base.camLens.extrude(mpos, nearPoint, farPoint) if self.plane.intersectsLine(self.pos3d, render.getRelativePoint(camera, nearPoint), render.getRelativePoint(camera, farPoint)): #print "Mouse ray intersects ground plane at ", self.pos3d self.model_node.setPos(render, self.pos3d) self.placement_ghost.set_position(self.pos3d[0],self.pos3d[1]) return task.again def ray_update(self,task): if base.mouseWatcherNode.hasMouse(): mpos = base.mouseWatcherNode.getMouse() self.pickerRay.setFromLens(base.camNode, mpos.getX(),mpos.getY()) return task.cont def task_mouse_place(self,task): if base.mouseWatcherNode.isButtonDown(MouseButton.one()): self.placing_object = True self.place_pos = (self.anchor_x,self.anchor_y) self.line_dir.remove() ls = LineSegs() ls.move_to(self.anchor_x,self.anchor_y,1) ls.draw_to(self.model.getX(),self.model.getY(),1) node = ls.create() angle1 = math.atan2(self.anchor_y - self.anchor_y,self.anchor_x - self.anchor_x+50) angle2 = math.atan2(self.anchor_y - self.model.getY(),self.anchor_x - self.model.getY()); final_angle = angle1-angle2; self.model.setHpr(final_angle,0,0) self.line_dir = NodePath(node) self.line_dir.reparentTo(render) return task.again else: self.line_dir.hide() taskMgr.add(self.task_mouse_press_check, "checkMousePress") return task.done def task_mouse_press_check(self,task): if base.mouseWatcherNode.isButtonDown(MouseButton.one()): #if self.multi_select == True: self.anchor_x,self.anchor_y = self.model.getX(),self.model.getY() taskMgr.add(self.task_mouse_place, "multibox") return task.done return task.again def mouse_click(self,status): print base.state if status == "1-down": if base.state == "placement": if self.placement_ghost.get_type() == "tower": obj = self.placement_ghost.place("tower",self.model.getX(),self.model.getY()) elif self.placement_ghost.get_type() == "army": obj = self.placement_ghost.place("army",self.model.getX(),self.model.getY()) base.details_box.set_object(obj) base.change_state("modifying") elif base.state == "moving": obj = base.details_box.get_object() obj.set_position(self.pos3d[0],self.pos3d[1]) base.change_state("modifying") elif base.state == "selecting" and self.pickable != None: obj = base.get_obj_from_node(self.pickable) base.details_box.set_object(obj,"selecting") base.change_state("modifying") elif status == "1-up": print "Mouse",status elif status == "3-down": if base.state == "placement": base.change_state("selecting") elif base.state == "selecting": base.change_state("placement")
def __init__(self, golog, simplex, **kwargs): self.simplex = simplex self.golog = golog self.messenger = self.golog.base.messenger self.listener = DirectObject( ) # might let Graphics_Data Inherit from Direct_Object self.graphics_kwargs = dict( ) # keeping track of information needed to re-create graphics_data #things to clean self for deletion: self.node_list = [] self.listeners = [self.listener] if simplex.level == 0: self.NP = golog.render.attachNewNode(simplex.label) self.node_list.append(self.NP) self.NP.setTag( 'level', '0') # to tell mode_heads what type of simplex this is golog.sphere.instanceTo(self.NP) self.collision = self.NP.attachNewNode( CollisionNode('sphereColNode')) self.collision.node().addSolid(CollisionSphere(0, 0, 0, 1)) self.messenger_names = {'node': str(id(self.NP))} #detail parents self.parents = ( ) #need to give no parents for a unified update function self.parent_pos_convolution = lambda *x: golog.render.getPos( ) # function of parent's [node] positions to detail offset (0-simlex it's just render location) #listener for parental updates, pass arguemnts through extraKwargs to detail what kind of update to perform for parent in self.parents: self.listener.accept(self.parents.messenger_names['node'], self.update) #set position if 'pos' in kwargs.keys(): self.update({'pos': kwargs['pos']}) else: self.update({'pos': LPoint3f(0, 0, 0)}) elif simplex.level == 1: self.NP = golog.render.attachNewNode(simplex.label) self.node_list.append(self.NP) self.NP.setTag('level', '1') self.golog.cone.instanceTo(self.NP) self.collision = self.NP.attachNewNode( CollisionNode('coneColNode')) self.collision.node().addSolid(CollisionSphere(0, 0, 0, 1)) self.messenger_names = {'node': str(id(simplex))} self.graphics = (Rope(), Rope()) #two ropes :) for rope in self.graphics: self.node_list.append(rope) #set up parents self.parents = tuple(golog.Simplex_to_Graphics[face] for face in self.simplex.faces) def tuple_avg(tuples): b = LPoint3f(0, 0, 0) for a in tuples: b = b + a return b / len(tuples) self.parent_pos_convolution = lambda *x: tuple_avg( tuple(parent.NP.getPos() for parent in self.parents)) for parent in self.parents: self.listener.accept(parent.messenger_names['node'], self.update) if 'pos' in kwargs.keys(): if isinstance(kwargs['pos'], tuple): pos = LPoint3f(*kwargs['pos']) else: pos = kwargs['pos'] else: pos = LPoint3f(0, 0, 0) self.graphics_kwargs['pos'] = pos #create shitty control nodes for rope module (I know, this is not pretty) self.control_nodes = ( self.golog.render.attachNewNode(simplex.label + '_control_node0'), self.golog.render.attachNewNode(simplex.label + '_control_node1')) for node in self.control_nodes: self.node_list.append(node) control_listener = DirectObject() self.listeners.append(control_listener) def control_updator(*x): for i in [0, 1]: self.control_nodes[i].setPos(self.graphics_kwargs['pos'] + self.parents[i].NP.getPos()) control_updator() control_listener.accept(self.messenger_names['node'], control_updator) self.update({'pos': None}) #set up rope graphics self.graphics[0].setup(3, [(self.NP, (0, 0, 0)), (self.control_nodes[0], (0, 0, 0)), (self.parents[0].NP, (0, 0, 0))]) self.graphics[1].setup(3, [(self.parents[1].NP, (0, 0, 0)), (self.control_nodes[1], (0, 0, 0)), (self.NP, (0, 0, 0))]) for rope in self.graphics: rope.reparentTo(golog.render) #set up dictionary references self.golog.Simplex_to_Graphics[simplex] = self self.golog.Graphics_to_Simplex[self] = simplex self.golog.NP_to_Graphics[self.NP] = self #create an invisible textNode that can be shown if called text = TextNode(self.simplex.label + '_text_node') #? make this wrap if too long text.setText(self.simplex.label) text.setCardDecal(True) text.setCardColor(.5, 1, .5, 1) text.setCardAsMargin(0, 0, 0, 0) text.setTextColor(0, 0, 0, 1) self.textNP = self.NP.attachNewNode(text) #? make this update to always be in front of camera # - Either get it into 2d plane, or make a z axis that always faces the camera and attach to that self.textNP.setPos(0, -1, 0) self.textNP.show() self.node_list.append(self.textNP)
def setMode(self, n=0): if n != self.mode: self.mode = n if n == 0: base.mouseWatcherNode.setGeometry(self.dummyNP.node()) #self.crosshair2.detachNode() self.crosshair2.hide() self.crosshair.show() #self.crosshair.reparentTo(self.dummyNP) else: base.mouseWatcherNode.setGeometry(self.dummyNP2.node()) #self.crosshair.detachNode() self.crosshair2.show() self.crosshair.hide() #self.crosshair2.reparentTo(self.dummyNP2) def toggle(self): if self.mode == 0: self.setMode(1) else: self.setMode(0) if __name__ == "__main__": m = MouseCursor() DO = DirectObject() DO.accept("mouse3", m.toggle) DO.accept("escape", sys.exit) run()
class PandaPhysicsSystem(DelegateByNetmode, SignalListener): subclasses = {} def __init__(self): self.register_signals() self.world = BulletWorld() self.world.setGravity((0, 0, -9.81)) # # Seems that this does not function # on_contact_added = PythonCallbackObject(self._on_contact_added) # self.world.set_contact_added_callback(on_contact_added) # on_filter = PythonCallbackObject(self._filter_collision) # self.world.set_filter_callback(on_filter) self.listener = DirectObject() self.listener.accept('bullet-contact-added', self._on_contact_added) self.listener.accept('bullet-contact-destroyed', self._on_contact_removed) debug_node = BulletDebugNode('Debug') debug_node.showWireframe(True) debug_node.showConstraints(True) debug_node.showBoundingBoxes(False) debug_node.showNormals(False) self.debug_nodepath = render.attachNewNode(debug_node) self.world.set_debug_node(debug_node) self.tracked_contacts = defaultdict(int) self.existing_collisions = set() def _create_contacts_from_result(self, requesting_node, contact_result): """Return collision contacts between two nodes""" contacts = [] for contact in contact_result.get_contacts(): if contact.get_node0() == requesting_node: manifold = contact.get_manifold_point() position = manifold.get_position_world_on_a() normal = -manifold.get_normal_world_on_b() elif contact.get_node1() == requesting_node: manifold = contact.get_manifold_point() position = manifold.get_position_world_on_b() normal = manifold.get_normal_world_on_b() impulse = manifold.get_applied_impulse() contact_ = CollisionContact(position, normal, impulse) contacts.append(contact_) return contacts def _on_contact_removed(self, node_a, node_b): self.tracked_contacts[(node_a, node_b)] -= 1 def _on_contact_added(self, node_a, node_b): self.tracked_contacts[(node_a, node_b)] += 1 def _filter_collision(self, filter_data): filter_data.set_collide(True) @RegisterPhysicsNode.on_global def register_node(self, node): self.world.attachRigidBody(node) node.set_python_tag("world", self.world) @DeregisterPhysicsNode.on_global def deregister_node(self, node): self.world.removeRigidBody(node) node.clear_python_tag("world") def dispatch_collisions(self): # Dispatch collisions existing_collisions = self.existing_collisions for pair, contact_count in self.tracked_contacts.items(): if contact_count > 0 and pair not in existing_collisions: existing_collisions.add(pair) # Dispatch collision node_a, node_b = pair entity_a = entity_from_nodepath(node_a) entity_b = entity_from_nodepath(node_b) contact_result = None if entity_a is not None: def contact_getter(): nonlocal contact_result if contact_result is None: contact_result = self.world.contact_test_pair(node_a, node_b) return self._create_contacts_from_result(node_a, contact_result) collision_result = LazyCollisionResult(entity_b, CollisionState.started, contact_getter) CollisionSignal.invoke(collision_result, target=entity_a) if entity_b is not None: def contact_getter(): nonlocal contact_result if contact_result is None: contact_result = self.world.contact_test_pair(node_a, node_b) return self._create_contacts_from_result(node_b, contact_result) collision_result = LazyCollisionResult(entity_a, CollisionState.started, contact_getter) CollisionSignal.invoke(collision_result, target=entity_b) elif contact_count == 0 and pair in existing_collisions: existing_collisions.remove(pair) # Dispatch collision node_a, node_b = pair entity_a = entity_from_nodepath(node_a) entity_b = entity_from_nodepath(node_b) # Don't send contacts for ended collisions contact_getter = lambda: None if entity_a is not None: collision_result = LazyCollisionResult(entity_b, CollisionState.ended, contact_getter) CollisionSignal.invoke(collision_result, target=entity_a) if entity_b is not None: collision_result = LazyCollisionResult(entity_a, CollisionState.ended, contact_getter) CollisionSignal.invoke(collision_result, target=entity_b) def update(self, delta_time): self.world.doPhysics(delta_time) self.dispatch_collisions()
# **...but from now on things are about to change: these two functions are the handlers the collision handler will call as soon as two events will happens: the two objects touch each other, the two objects leave each other alone. Afterwards we'll see how to make this happen. # this is the function will be called while the two objects collide... def collideEventIn(entry): # lights on snipstuff.dlight.setColor((0.5, 0.5, 0.5, 1)) # ... and this when they leave each other alone. def collideEventOut(entry): # lights shut down snipstuff.dlight.setColor((0.0, 0.1, 0.0, 1)) # ** And here it is how we tell to our handler which are the two functions to call whenever a collision or the opposite event happens: # this method is for the INTO event, as is when the FROM object (heart) collide with the INTO one (smiley). You see that we set a pattern string with strange symbols inside - I'm not going to dig these details cos you'll find a wide explanation inside the panda3D manual at the chapter "Collision Handlers" - just know that that pattern will match a system event we'll define later, which the collision handler will call ASA the event occurs. collisionHandler.addInPattern("%fn-into-%in") # this is on the other hand the relative call for the OUT event, as is when the FROM object (heart) goes OUT the INTO oject (heart). collisionHandler.addOutPattern("%fn-out-%in") # ** To manage the collision events need Directobject, because the chaining between the CollisionHandlerEvent handler and our two functions seen above, happens with the accept() function of a DirectObject instance that define the event strings the task manager will call via the CollisionHandlerEvent stimulus. DO = DirectObject() # ** And at last here it is the event catching setup: we tell the panda3D event manager to call our function handlers whenever 'somebody' fires the strings 'collider_heart-into-smileycnode' or 'collider_heart-out-smileycnode' - this is to say that whenever the CollisionHandlerEvent object detect a collision, will send an event string to the engine formatted, as you can see, with first the name of the FROM collider (collider_heart precisely) the "-into-" or "-out-" string chunk and at the end the name of the INTO collider (smileycnode precisely). Note that '-into-' and '-out-' are just for our convenience, nobody oblige us to write them like that: we may call'em 'dummy' and 'foo' respectively but provided that they're specified also in the pattern declaration above in place of '-into-' and '-out-'. Now go back above and compare what said here with the pattern strings specified above with the addInPattern and addOutPattern declarations and with a little help of the panda3D manual you should understand all this knotted stuff. DO.accept("collider_heart-into-smileycnode", collideEventIn) DO.accept("collider_heart-out-smileycnode", collideEventOut) splash.destroy() run()
# we retrieve the two object nodepaths - note that we need to go back the nodes hierarchy because the getXXXNodePath methods returns just the collision geometry, that we know is parented to the very object nodepath we need to manage here colliderFROM = entry.getFromNodePath().getParent() colliderINTO = entry.getIntoNodePath().getParent() # we now may change the aspect of the two colliding objects colliderINTO.setColor(1,1,1,1) colliderFROM.setScale(1.5) #... and this when they leave each other alone. def collideEventOut(entry): colliderFROM = entry.getFromNodePath().getParent() colliderINTO = entry.getIntoNodePath().getParent() colliderINTO.setColor(.4, .4, .4, 1) colliderFROM.setScale(1.0) #** Here's the definition of the patterns that should catch our collisions - note that just these two are enough to catch the 2 couple of combinations. collisionHandler.addInPattern('%fn-into-%in') collisionHandler.addOutPattern('%fn-out-%in') #** Let's manage the collision events: DO=DirectObject() # we should provide a couple of event strings for each couple of interacting objects (remember we made the smiley react against the whole heart and the frowney with the broken one). Compare these strings with the patterns above and if it is not enough to understand why it works, go back and dig again step2 and step3 until they're clear for you. # Note that we route the in and out events to the same functions for both couples but that is just to slim the code. DO.accept('collider_heart-into-smileycnode', collideEventIn) DO.accept('collider_heart-out-smileycnode', collideEventOut) # DO.accept('collider_brkheart-into-frowneycnode', collideEventIn) DO.accept('collider_brkheart-out-frowneycnode', collideEventOut) splash.destroy() run()
def __init__(self): ShowBase.__init__(self) #Setup scene = BulletWorld() scene.setGravity(Vec3(0, 0, -9.81)) base.setBackgroundColor(0.6,0.9,0.9) fog = Fog("The Fog") fog.setColor(0.9,0.9,1.0) fog.setExpDensity(0.003) render.setFog(fog) #Lighting #Sun light sun = DirectionalLight("The Sun") sun_np = render.attachNewNode(sun) sun_np.setHpr(0,-60,0) render.setLight(sun_np) #Ambient light amb = AmbientLight("The Ambient Light") amb.setColor(VBase4(0.39,0.39,0.39, 1)) amb_np = render.attachNewNode(amb) render.setLight(amb_np) #Variables self.gear = 0 self.start = 0 self.Pbrake = 0 self.terrain_var = 1 self.time = 0 self.headlight_var = 0 self.RPM = 0 self.clutch = 0 self.carmaxspeed = 100 #KPH self.carmaxreversespeed = -40 #KPH self.steering = 0 #Functions def V1(): camera.setPos(0.25,-1.2,0.5) camera.setHpr(0,-13,0) def V2(): camera.setPos(0,-15,3) camera.setHpr(0,-10,0) def V3(): camera.setPos(0,0,9) camera.setHpr(0,-90,0) def up(): self.gear = self.gear -1 if self.gear < -1: self.gear = -1 def down(): self.gear = self.gear +1 if self.gear > 1: self.gear = 1 def start_function(): self.start = 1 self.start_sound.play() self.engine_idle_sound.play() self.RPM = 1000 def stop_function(): self.start = 0 self.engine_idle_sound.stop() def parkingbrake(): self.Pbrake = (self.Pbrake + 1) % 2 def rotate(): Car_np.setHpr(0, 0, 0) def horn(): self.horn_sound.play() def set_time(): if self.time == -1: sun.setColor(VBase4(0.4, 0.3, 0.3, 1)) base.setBackgroundColor(0.8,0.7,0.7) if self.time == 0: sun.setColor(VBase4(0.7, 0.7, 0.7, 1)) base.setBackgroundColor(0.6,0.9,0.9) if self.time == 1: sun.setColor(VBase4(0.2, 0.2, 0.2, 1)) base.setBackgroundColor(0.55,0.5,0.5) if self.time == 2: sun.setColor(VBase4(0.02, 0.02, 0.05, 1)) base.setBackgroundColor(0.3,0.3,0.3) if self.time == -2: self.time = -1 if self.time == 3: self.time = 2 def time_forward(): self.time = self.time + 1 def time_backward(): self.time = self.time -1 def set_terrain(): if self.terrain_var == 1: self.ground_model.setTexture(self.ground_tex, 1) self.ground_model.setScale(3) if self.terrain_var == 2: self.ground_model.setTexture(self.ground_tex2, 1) self.ground_model.setScale(3) if self.terrain_var == 3: self.ground_model.setTexture(self.ground_tex3, 1) self.ground_model.setScale(4) if self.terrain_var == 4: self.terrain_var = 1 if self.terrain_var == 0: self.terrain_var = 3 def next_terrain(): self.terrain_var = self.terrain_var + 1 def previous_terrain(): self.terrain_var = self.terrain_var - 1 def show_menu(): self.menu_win.show() self.a1.show() self.a2.show() self.a3.show() self.a4.show() self.t1.show() self.t2.show() self.ok.show() self.exit_button.show() def hide_menu(): self.menu_win.hide() self.a1.hide() self.a2.hide() self.a3.hide() self.a4.hide() self.ok.hide() self.t1.hide() self.t2.hide() self.exit_button.hide() def Menu(): self.menu_win = OnscreenImage(image = "Textures/menu.png", pos = (0.9,0,0), scale = (0.5)) self.menu_win.setTransparency(TransparencyAttrib.MAlpha) #The Arrow Buttons self.a1 = DirectButton(text = "<", scale = 0.2, pos = (0.55,0,0.25), command = previous_terrain) self.a2 = DirectButton(text = ">", scale = 0.2, pos = (1.15,0,0.25), command = next_terrain) self.a3 = DirectButton(text = "<", scale = 0.2, pos = (0.55,0,0.0), command = time_backward) self.a4 = DirectButton(text = ">", scale = 0.2, pos = (1.15,0,0.0), command = time_forward) #The Text self.t1 = OnscreenText(text = "Terrain", pos = (0.85,0.25,0), scale = 0.1, fg = (0.4,0.4,0.5,1)) self.t2 = OnscreenText(text = "Time", pos = (0.85,0,0), scale = 0.1, fg = (0.4,0.4,0.5,1)) #The Buttons self.ok = DirectButton(text = "Okay", scale = 0.11, pos = (0.87,0,-0.25), command = hide_menu) self.exit_button = DirectButton(text = "Quit", scale = 0.11, pos = (0.87,0,-0.42), command = sys.exit) Menu() def take_screenshot(): base.screenshot("Screenshot") def set_headlights(): if self.headlight_var == 1: Headlight1.setColor(VBase4(9.0,8.9,8.9,1)) Headlight2.setColor(VBase4(9.0,8.9,8.9,1)) if self.headlight_var == 0: Headlight1.setColor(VBase4(0,0,0,1)) Headlight2.setColor(VBase4(0,0,0,1)) def headlights(): self.headlight_var = (self.headlight_var + 1) % 2 def update_rpm(): #Simulate RPM if self.start == 1: if self.gear == 0: self.RPM = self.RPM - self.RPM / 400 else: self.RPM = self.RPM + self.carspeed / 9 self.RPM = self.RPM - self.RPM / 200 #Reset RPM to 0 when engine is off if self.start == 0: if self.RPM > 0.0: self.RPM = self.RPM - 40 if self.RPM < 10: self.RPM = 0.0 #Idle RPM power if self.start == 1: if self.RPM < 650: self.RPM = self.RPM + 4 if self.RPM < 600: self.clutch = 1 else: self.clutch = 0 #RPM limit if self.RPM > 6000: self.RPM = 6000 #Controls inputState.watchWithModifiers("F", "arrow_up") inputState.watchWithModifiers("B", "arrow_down") inputState.watchWithModifiers("L", "arrow_left") inputState.watchWithModifiers("R", "arrow_right") do = DirectObject() do.accept("escape", show_menu) do.accept("1", V1) do.accept("2", V2) do.accept("3", V3) do.accept("page_up", up) do.accept("page_down", down) do.accept("x-repeat", start_function) do.accept("x", stop_function) do.accept("p", parkingbrake) do.accept("backspace", rotate) do.accept("enter", horn) do.accept("f12", take_screenshot) do.accept("h", headlights) #The ground self.ground = BulletPlaneShape(Vec3(0, 0, 1,), 1) self.ground_node = BulletRigidBodyNode("The ground") self.ground_node.addShape(self.ground) self.ground_np = render.attachNewNode(self.ground_node) self.ground_np.setPos(0, 0, -2) scene.attachRigidBody(self.ground_node) self.ground_model = loader.loadModel("Models/plane.egg") self.ground_model.reparentTo(render) self.ground_model.setPos(0,0,-1) self.ground_model.setScale(3) self.ground_tex = loader.loadTexture("Textures/ground.png") self.ground_tex2 = loader.loadTexture("Textures/ground2.png") self.ground_tex3 = loader.loadTexture("Textures/ground3.png") self.ground_model.setTexture(self.ground_tex, 1) #The car Car_shape = BulletBoxShape(Vec3(1, 2.0, 1.0)) Car_node = BulletRigidBodyNode("The Car") Car_node.setMass(1200.0) Car_node.addShape(Car_shape) Car_np = render.attachNewNode(Car_node) Car_np.setPos(0,0,3) Car_np.setHpr(0,0,0) Car_np.node().setDeactivationEnabled(False) scene.attachRigidBody(Car_node) Car_model = loader.loadModel("Models/Car.egg") Car_model.reparentTo(Car_np) Car_tex = loader.loadTexture("Textures/Car1.png") Car_model.setTexture(Car_tex, 1) self.Car_sim = BulletVehicle(scene, Car_np.node()) self.Car_sim.setCoordinateSystem(ZUp) scene.attachVehicle(self.Car_sim) #The inside of the car Car_int = loader.loadModel("Models/inside.egg") Car_int.reparentTo(Car_np) Car_int_tex = loader.loadTexture("Textures/inside.png") Car_int.setTexture(Car_int_tex, 1) Car_int.setTransparency(TransparencyAttrib.MAlpha) #The steering wheel Sw = loader.loadModel("Models/Steering wheel.egg") Sw.reparentTo(Car_np) Sw.setPos(0.25,0,-0.025) #The first headlight Headlight1 = Spotlight("Headlight1") lens = PerspectiveLens() lens.setFov(180) Headlight1.setLens(lens) Headlight1np = render.attachNewNode(Headlight1) Headlight1np.reparentTo(Car_np) Headlight1np.setPos(-0.8,2.5,-0.5) Headlight1np.setP(-15) render.setLight(Headlight1np) #The second headlight Headlight2 = Spotlight("Headlight2") Headlight2.setLens(lens) Headlight2np = render.attachNewNode(Headlight2) Headlight2np.reparentTo(Car_np) Headlight2np.setPos(0.8,2.5,-0.5) Headlight2np.setP(-15) render.setLight(Headlight2np) #Sounds self.horn_sound = loader.loadSfx("Sounds/horn.ogg") self.start_sound = loader.loadSfx("Sounds/enginestart.ogg") self.engine_idle_sound = loader.loadSfx("Sounds/engineidle.ogg") self.engine_idle_sound.setLoop(True) self.accelerate_sound = loader.loadSfx("Sounds/enginethrottle.ogg") #Camera base.disableMouse() camera.reparentTo(Car_np) camera.setPos(0,-15,3) camera.setHpr(0,-10,0) #Wheel function def Wheel(pos, np, r, f): w = self.Car_sim.createWheel() w.setNode(np.node()) w.setChassisConnectionPointCs(pos) w.setFrontWheel(f) w.setWheelDirectionCs(Vec3(0, 0, -1)) w.setWheelAxleCs(Vec3(1, 0, 0)) w.setWheelRadius(r) w.setMaxSuspensionTravelCm(40) w.setSuspensionStiffness(120) w.setWheelsDampingRelaxation(2.3) w.setWheelsDampingCompression(4.4) w.setFrictionSlip(50) w.setRollInfluence(0.1) #Wheels w1_np = loader.loadModel("Models/Lwheel") w1_np.reparentTo(render) w1_np.setColorScale(0,6) Wheel(Point3(-1,1,-0.6), w1_np, 0.4, False) w2_np = loader.loadModel("Models/Rwheel") w2_np.reparentTo(render) w2_np.setColorScale(0,6) Wheel(Point3(-1.1,-1.2,-0.6), w2_np, 0.4, True) w3_np = loader.loadModel("Models/Lwheel") w3_np.reparentTo(render) w3_np.setColorScale(0,6) Wheel(Point3(1.1,-1,-0.6), w3_np, 0.4, True) w4_np = loader.loadModel("Models/Rwheel") w4_np.reparentTo(render) w4_np.setColorScale(0,6) Wheel(Point3(1,1,-0.6), w4_np, 0.4, False) #The engine and steering def processInput(dt): #Vehicle properties self.steeringClamp = 35.0 self.steeringIncrement = 70 engineForce = 0.0 brakeForce = 0.0 #Get the vehicle's current speed self.carspeed = self.Car_sim.getCurrentSpeedKmHour() #Engage clutch when in gear 0 if self.gear == 0: self.clutch = 1 #Slow the steering when at higher speeds self.steeringIncrement = self.steeringIncrement - self.carspeed / 1.5 #Reset the steering if not inputState.isSet("L") and not inputState.isSet("R"): if self.steering < 0.00: self.steering = self.steering + 0.6 if self.steering > 0.00: self.steering = self.steering - 0.6 if self.steering < 1.0 and self.steering > -1.0: self.steering = 0 #Slow the car down while it's moving if self.clutch == 0: brakeForce = brakeForce + self.carspeed / 5 else: brakeForce = brakeForce + self.carspeed / 15 #Forward if self.start == 1: if inputState.isSet("F"): self.RPM = self.RPM + 35 self.accelerate_sound.play() if self.clutch == 0: if self.gear == -1: if self.carspeed > self.carmaxreversespeed: engineForce = -self.RPM / 3 if self.gear == 1: if self.carspeed < self.carmaxspeed: engineForce = self.RPM / 1 #Brake if inputState.isSet("B"): engineForce = 0.0 brakeForce = 12.0 if self.gear != 0 and self.clutch == 0: self.RPM = self.RPM - 20 #Left if inputState.isSet("L"): if self.steering < 0.0: #This makes the steering reset at the correct speed when turning from right to left self.steering += dt * self.steeringIncrement + 0.6 self.steering = min(self.steering, self.steeringClamp) else: #Normal steering self.steering += dt * self.steeringIncrement self.steering = min(self.steering, self.steeringClamp) #Right if inputState.isSet("R"): if self.steering > 0.0: #This makes the steering reset at the correct speed when turning from left to right self.steering -= dt * self.steeringIncrement + 0.6 self.steering = max(self.steering, -self.steeringClamp) else: #Normal steering self.steering -= dt * self.steeringIncrement self.steering = max(self.steering, -self.steeringClamp) #Park if self.Pbrake == 1: brakeForce = 10.0 if self.gear != 0 and self. clutch == 0: self.RPM = self.RPM - 20 #Apply forces to wheels self.Car_sim.applyEngineForce(engineForce, 0); self.Car_sim.applyEngineForce(engineForce, 3); self.Car_sim.setBrake(brakeForce, 1); self.Car_sim.setBrake(brakeForce, 2); self.Car_sim.setSteeringValue(self.steering, 0); self.Car_sim.setSteeringValue(self.steering, 3); #Steering wheel Sw.setHpr(0,0,-self.steering*10) #The HUD self.gear_hud = OnscreenImage(image = "Textures/gear_hud.png", pos = (-1,0,-0.85), scale = (0.2)) self.gear_hud.setTransparency(TransparencyAttrib.MAlpha) self.gear2_hud = OnscreenImage(image = "Textures/gear2_hud.png", pos = (-1,0,-0.85), scale = (0.2)) self.gear2_hud.setTransparency(TransparencyAttrib.MAlpha) self.starter = OnscreenImage(image = "Textures/starter.png", pos = (-1.2,0,-0.85), scale = (0.15)) self.starter.setTransparency(TransparencyAttrib.MAlpha) self.park = OnscreenImage(image = "Textures/pbrake.png", pos = (-0.8,0,-0.85), scale = (0.1)) self.park.setTransparency(TransparencyAttrib.MAlpha) self.rev_counter = OnscreenImage(image = "Textures/dial.png", pos = (-1.6, 0.0, -0.70), scale = (0.6,0.6,0.4)) self.rev_counter.setTransparency(TransparencyAttrib.MAlpha) self.rev_needle = OnscreenImage(image = "Textures/needle.png", pos = (-1.6, 0.0, -0.70), scale = (0.5)) self.rev_needle.setTransparency(TransparencyAttrib.MAlpha) self.rev_text = OnscreenText(text = " ", pos = (-1.6, -0.90, 0), scale = 0.05) self.speedometer = OnscreenImage(image = "Textures/dial.png", pos = (-1.68, 0.0, -0.10), scale = (0.7,0.7,0.5)) self.speedometer.setTransparency(TransparencyAttrib.MAlpha) self.speedometer_needle = OnscreenImage(image = "Textures/needle.png", pos = (-1.68, 0.0, -0.10), scale = (0.5)) self.speedometer_needle.setTransparency(TransparencyAttrib.MAlpha) self.speedometer_text = OnscreenText(text = " ", pos = (-1.68, -0.35, 0), scale = 0.05) #Update the HUD def Update_HUD(): #Move gear selector if self.gear == -1: self.gear2_hud.setPos(-1,0,-0.785) if self.gear == 0: self.gear2_hud.setPos(-1,0,-0.85) if self.gear == 1: self.gear2_hud.setPos(-1,0,-0.91) #Rotate starter if self.start == 0: self.starter.setHpr(0,0,0) else: self.starter.setHpr(0,0,45) #Update the parking brake light if self.Pbrake == 1: self.park.setImage("Textures/pbrake2.png") self.park.setTransparency(TransparencyAttrib.MAlpha) else: self.park.setImage("Textures/pbrake.png") self.park.setTransparency(TransparencyAttrib.MAlpha) #Update the rev counter self.rev_needle.setR(self.RPM/22) rev_string = str(self.RPM)[:4] self.rev_text.setText(rev_string+" RPM") #Update the speedometer if self.carspeed > 0.0: self.speedometer_needle.setR(self.carspeed*2.5) if self.carspeed < 0.0: self.speedometer_needle.setR(-self.carspeed*2.5) speed_string = str(self.carspeed)[:3] self.speedometer_text.setText(speed_string+" KPH") #Update the program def update(task): dt = globalClock.getDt() processInput(dt) Update_HUD() set_time() set_terrain() set_headlights() update_rpm() scene.doPhysics(dt, 5, 1.0/180.0) return task.cont taskMgr.add(update, "Update")
class DirectEntry(DirectFrame): """ DirectEntry(parent) - Create a DirectGuiWidget which responds to keyboard buttons """ directWtext = ConfigVariableBool('direct-wtext', 1) AllowCapNamePrefixes = ( "Al", "Ap", "Ben", "De", "Del", "Della", "Delle", "Der", "Di", "Du", "El", "Fitz", "La", "Las", "Le", "Les", "Lo", "Los", "Mac", "St", "Te", "Ten", "Van", "Von", ) ForceCapNamePrefixes = ( "D'", "DeLa", "Dell'", "L'", "M'", "Mc", "O'", ) def __init__(self, parent=None, **kw): # Inherits from DirectFrame # A Direct Frame can have: # - A background texture (pass in path to image, or Texture Card) # - A midground geometry item (pass in geometry) # - A foreground text Node (pass in text string or Onscreen Text) # For a direct entry: # Each button has 3 states (focus, noFocus, disabled) # The same image/geom/text can be used for all three states or each # state can have a different text/geom/image # State transitions happen automatically based upon mouse interaction optiondefs = ( # Define type of DirectGuiWidget ('pgFunc', PGEntry, None), ('numStates', 3, None), ('state', DGG.NORMAL, None), ('entryFont', None, DGG.INITOPT), ('width', 10, self.updateWidth), ('numLines', 1, self.updateNumLines), ('focus', 0, self.setFocus), ('cursorKeys', 1, self.setCursorKeysActive), ('obscured', 0, self.setObscureMode), # Setting backgroundFocus allows the entry box to get keyboard # events that are not handled by other things (i.e. events that # fall through to the background): ('backgroundFocus', 0, self.setBackgroundFocus), # Text used for the PGEntry text node # NOTE: This overrides the DirectFrame text option ('initialText', '', DGG.INITOPT), # Enable or disable text overflow scrolling ('overflow', 0, self.setOverflowMode), # Command to be called on hitting Enter ('command', None, None), ('extraArgs', [], None), # Command to be called when enter is hit but we fail to submit ('failedCommand', None, None), ('failedExtraArgs', [], None), # commands to be called when focus is gained or lost ('focusInCommand', None, None), ('focusInExtraArgs', [], None), ('focusOutCommand', None, None), ('focusOutExtraArgs', [], None), # Sounds to be used for button events ('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound), ('clickSound', DGG.getDefaultClickSound(), self.setClickSound), ('autoCapitalize', 0, self.autoCapitalizeFunc), ('autoCapitalizeAllowPrefixes', DirectEntry.AllowCapNamePrefixes, None), ('autoCapitalizeForcePrefixes', DirectEntry.ForceCapNamePrefixes, None), ) # Merge keyword options with default options self.defineoptions(kw, optiondefs) # Initialize superclasses DirectFrame.__init__(self, parent) if self['entryFont'] == None: font = DGG.getDefaultFont() else: font = self['entryFont'] # Create Text Node Component self.onscreenText = self.createcomponent( 'text', (), None, OnscreenText, (), parent=ShowBaseGlobal.hidden, # Pass in empty text to avoid extra work, since its really # The PGEntry which will use the TextNode to generate geometry text='', align=TextNode.ALeft, font=font, scale=1, # Don't get rid of the text node mayChange=1) # We can get rid of the node path since we're just using the # onscreenText as an easy way to access a text node as a # component self.onscreenText.removeNode() # Bind command function self.bind(DGG.ACCEPT, self.commandFunc) self.bind(DGG.ACCEPTFAILED, self.failedCommandFunc) self.accept(self.guiItem.getFocusInEvent(), self.focusInCommandFunc) self.accept(self.guiItem.getFocusOutEvent(), self.focusOutCommandFunc) # listen for auto-capitalize events on a separate object to prevent # clashing with other parts of the system self._autoCapListener = DirectObject() # Call option initialization functions self.initialiseoptions(DirectEntry) if not hasattr(self, 'autoCapitalizeAllowPrefixes'): self.autoCapitalizeAllowPrefixes = DirectEntry.AllowCapNamePrefixes if not hasattr(self, 'autoCapitalizeForcePrefixes'): self.autoCapitalizeForcePrefixes = DirectEntry.ForceCapNamePrefixes # Update TextNodes for each state for i in range(self['numStates']): self.guiItem.setTextDef(i, self.onscreenText.textNode) # Now we should call setup() again to make sure it has the # right font def. self.setup() # Update initial text self.unicodeText = 0 if self['initialText']: self.enterText(self['initialText']) def destroy(self): self.ignoreAll() self._autoCapListener.ignoreAll() DirectFrame.destroy(self) def setup(self): self.guiItem.setupMinimal(self['width'], self['numLines']) def updateWidth(self): self.guiItem.setMaxWidth(self['width']) def updateNumLines(self): self.guiItem.setNumLines(self['numLines']) def setFocus(self): PGEntry.setFocus(self.guiItem, self['focus']) def setCursorKeysActive(self): PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys']) def setOverflowMode(self): PGEntry.set_overflow_mode(self.guiItem, self['overflow']) def setObscureMode(self): PGEntry.setObscureMode(self.guiItem, self['obscured']) def setBackgroundFocus(self): PGEntry.setBackgroundFocus(self.guiItem, self['backgroundFocus']) def setRolloverSound(self): rolloverSound = self['rolloverSound'] if rolloverSound: self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound) else: self.guiItem.clearSound(DGG.ENTER + self.guiId) def setClickSound(self): clickSound = self['clickSound'] if clickSound: self.guiItem.setSound(DGG.ACCEPT + self.guiId, clickSound) else: self.guiItem.clearSound(DGG.ACCEPT + self.guiId) def commandFunc(self, event): if self['command']: # Pass any extra args to command self['command'](*[self.get()] + self['extraArgs']) def failedCommandFunc(self, event): if self['failedCommand']: # Pass any extra args self['failedCommand'](*[self.get()] + self['failedExtraArgs']) def autoCapitalizeFunc(self): if self['autoCapitalize']: self._autoCapListener.accept(self.guiItem.getTypeEvent(), self._handleTyping) self._autoCapListener.accept(self.guiItem.getEraseEvent(), self._handleErasing) else: self._autoCapListener.ignore(self.guiItem.getTypeEvent()) self._autoCapListener.ignore(self.guiItem.getEraseEvent()) def focusInCommandFunc(self): if self['focusInCommand']: self['focusInCommand'](*self['focusInExtraArgs']) if self['autoCapitalize']: self.accept(self.guiItem.getTypeEvent(), self._handleTyping) self.accept(self.guiItem.getEraseEvent(), self._handleErasing) def _handleTyping(self, guiEvent): self._autoCapitalize() def _handleErasing(self, guiEvent): self._autoCapitalize() def _autoCapitalize(self): name = self.guiItem.getWtext() # capitalize each word, allowing for things like McMutton capName = u'' # track each individual word to detect prefixes like Mc wordSoFar = u'' # track whether the previous character was part of a word or not wasNonWordChar = True for i, character in enumerate(name): # test to see if we are between words # - Count characters that can't be capitalized as a break between words # This assumes that string.lower and string.upper will return different # values for all unicode letters. # - Don't count apostrophes as a break between words if character.lower() == character.upper() and character != u"'": # we are between words wordSoFar = u'' wasNonWordChar = True else: capitalize = False if wasNonWordChar: # first letter of a word, capitalize it unconditionally; capitalize = True elif (character == character.upper() and len(self.autoCapitalizeAllowPrefixes) and wordSoFar in self.autoCapitalizeAllowPrefixes): # first letter after one of the prefixes, allow it to be capitalized capitalize = True elif (len(self.autoCapitalizeForcePrefixes) and wordSoFar in self.autoCapitalizeForcePrefixes): # first letter after one of the force prefixes, force it to be capitalized capitalize = True if capitalize: # allow this letter to remain capitalized character = character.upper() else: character = character.lower() wordSoFar += character wasNonWordChar = False capName += character self.guiItem.setWtext(capName) self.guiItem.setCursorPosition(self.guiItem.getNumCharacters()) def focusOutCommandFunc(self): if self['focusOutCommand']: self['focusOutCommand'](*self['focusOutExtraArgs']) if self['autoCapitalize']: self.ignore(self.guiItem.getTypeEvent()) self.ignore(self.guiItem.getEraseEvent()) def set(self, text): """ Changes the text currently showing in the typable region; does not change the current cursor position. Also see enterText(). """ assert not isinstance(text, bytes) self.unicodeText = True self.guiItem.setWtext(text) def get(self, plain=False): """ Returns the text currently showing in the typable region. If plain is True, the returned text will not include any formatting characters like nested color-change codes. """ wantWide = self.unicodeText or self.guiItem.isWtext() if not self.directWtext.getValue(): # If the user has configured wide-text off, then always # return an 8-bit string. This will be encoded if # necessary, according to Panda's default encoding. wantWide = False if plain: if wantWide: return self.guiItem.getPlainWtext() else: return self.guiItem.getPlainText() else: if wantWide: return self.guiItem.getWtext() else: return self.guiItem.getText() def getCursorPosition(self): return self.guiItem.getCursorPosition() def setCursorPosition(self, pos): if (pos < 0): self.guiItem.setCursorPosition(self.guiItem.getNumCharacters() + pos) else: self.guiItem.setCursorPosition(pos) def getNumCharacters(self): return self.guiItem.getNumCharacters() def enterText(self, text): """ sets the entry's text, and moves the cursor to the end """ self.set(text) self.setCursorPosition(self.guiItem.getNumCharacters()) def getFont(self): return self.onscreenText.getFont() def getBounds(self, state=0): # Compute the width and height for the entry itself, ignoring # geometry etc. tn = self.onscreenText.textNode mat = tn.getTransform() align = tn.getAlign() lineHeight = tn.getLineHeight() numLines = self['numLines'] width = self['width'] if align == TextNode.ALeft: left = 0.0 right = width elif align == TextNode.ACenter: left = -width / 2.0 right = width / 2.0 elif align == TextNode.ARight: left = -width right = 0.0 bottom = -0.3 * lineHeight - (lineHeight * (numLines - 1)) top = lineHeight self.ll.set(left, 0.0, bottom) self.ur.set(right, 0.0, top) self.ll = mat.xformPoint(Point3.rfu(left, 0.0, bottom)) self.ur = mat.xformPoint(Point3.rfu(right, 0.0, top)) vec_right = Vec3.right() vec_up = Vec3.up() left = (vec_right[0] * self.ll[0] + vec_right[1] * self.ll[1] + vec_right[2] * self.ll[2]) right = (vec_right[0] * self.ur[0] + vec_right[1] * self.ur[1] + vec_right[2] * self.ur[2]) bottom = (vec_up[0] * self.ll[0] + vec_up[1] * self.ll[1] + vec_up[2] * self.ll[2]) top = (vec_up[0] * self.ur[0] + vec_up[1] * self.ur[1] + vec_up[2] * self.ur[2]) self.ll = Point3(left, 0.0, bottom) self.ur = Point3(right, 0.0, top) # Scale bounds to give a pad around graphics. We also want to # scale around the border width. pad = self['pad'] borderWidth = self['borderWidth'] self.bounds = [ self.ll[0] - pad[0] - borderWidth[0], self.ur[0] + pad[0] + borderWidth[0], self.ll[2] - pad[1] - borderWidth[1], self.ur[2] + pad[1] + borderWidth[1] ] return self.bounds
class PandaPhysicsSystem(DelegateByNetmode, SignalListener): subclasses = {} def __init__(self): self.register_signals() self.world = BulletWorld() self.world.setGravity((0, 0, -9.81)) # # Seems that this does not function # on_contact_added = PythonCallbackObject(self._on_contact_added) # self.world.set_contact_added_callback(on_contact_added) # on_filter = PythonCallbackObject(self._filter_collision) # self.world.set_filter_callback(on_filter) self.listener = DirectObject() self.listener.accept('bullet-contact-added', self._on_contact_added) self.listener.accept('bullet-contact-destroyed', self._on_contact_removed) debug_node = BulletDebugNode('Debug') debug_node.showWireframe(True) debug_node.showConstraints(True) debug_node.showBoundingBoxes(False) debug_node.showNormals(False) self.debug_nodepath = render.attachNewNode(debug_node) self.world.set_debug_node(debug_node) self.tracked_contacts = defaultdict(int) self.existing_collisions = set() def _create_contacts_from_result(self, requesting_node, contact_result): """Return collision contacts between two nodes""" contacts = [] for contact in contact_result.get_contacts(): if contact.get_node0() == requesting_node: manifold = contact.get_manifold_point() position = manifold.get_position_world_on_a() normal = -manifold.get_normal_world_on_b() elif contact.get_node1() == requesting_node: manifold = contact.get_manifold_point() position = manifold.get_position_world_on_b() normal = manifold.get_normal_world_on_b() impulse = manifold.get_applied_impulse() contact_ = CollisionContact(position, normal, impulse) contacts.append(contact_) return contacts def _on_contact_removed(self, node_a, node_b): self.tracked_contacts[(node_a, node_b)] -= 1 def _on_contact_added(self, node_a, node_b): self.tracked_contacts[(node_a, node_b)] += 1 def _filter_collision(self, filter_data): filter_data.set_collide(True) @RegisterPhysicsNode.on_global def register_node(self, node): self.world.attachRigidBody(node) node.set_python_tag("world", self.world) @DeregisterPhysicsNode.on_global def deregister_node(self, node): self.world.removeRigidBody(node) node.clear_python_tag("world") def dispatch_collisions(self): # Dispatch collisions existing_collisions = self.existing_collisions for pair, contact_count in self.tracked_contacts.items(): if contact_count > 0 and pair not in existing_collisions: existing_collisions.add(pair) # Dispatch collision node_a, node_b = pair entity_a = entity_from_nodepath(node_a) entity_b = entity_from_nodepath(node_b) contact_result = None if entity_a is not None: def contact_getter(): nonlocal contact_result if contact_result is None: contact_result = self.world.contact_test_pair( node_a, node_b) return self._create_contacts_from_result( node_a, contact_result) collision_result = LazyCollisionResult( entity_b, CollisionState.started, contact_getter) CollisionSignal.invoke(collision_result, target=entity_a) if entity_b is not None: def contact_getter(): nonlocal contact_result if contact_result is None: contact_result = self.world.contact_test_pair( node_a, node_b) return self._create_contacts_from_result( node_b, contact_result) collision_result = LazyCollisionResult( entity_a, CollisionState.started, contact_getter) CollisionSignal.invoke(collision_result, target=entity_b) elif contact_count == 0 and pair in existing_collisions: existing_collisions.remove(pair) # Dispatch collision node_a, node_b = pair entity_a = entity_from_nodepath(node_a) entity_b = entity_from_nodepath(node_b) # Don't send contacts for ended collisions contact_getter = lambda: None if entity_a is not None: collision_result = LazyCollisionResult( entity_b, CollisionState.ended, contact_getter) CollisionSignal.invoke(collision_result, target=entity_a) if entity_b is not None: collision_result = LazyCollisionResult( entity_a, CollisionState.ended, contact_getter) CollisionSignal.invoke(collision_result, target=entity_b) def update(self, delta_time): self.world.doPhysics(delta_time) self.dispatch_collisions()
if self.steer2d: base.cam.setX(self.avatar.getX()) base.cam.setZ(self.avatar.getZ() + self.camdistZ) base.cam.lookAt(self.avatar) return Task.cont #========================================================================= # Main #========================================================================= alight = AmbientLight('alight') alight.setColor((.3, .3, .3, 1)) alnp = render.attachNewNode(alight) render.setLight(alnp) dlight = DirectionalLight('dlight') lv = .6 dlight.setColor((lv, lv, lv, 1)) dlnp = render.attachNewNode(dlight) render.setLight(dlnp) dlnp.setPos(0, -20, 35) dlnp.lookAt(0, 0, 0) DO = DirectObject() DO.accept('c', toggle_collisions) DO.accept('h', toggle_info) DO.accept('x', toggle_wire) DO.accept('escape', sys.exit)
but then we should have used 2 accepts like this: DO.accept('ray1-again-smileys', collideAgainBalls) DO.accept('ray1-again-frowney', collideAgainBalls) instead of just one as we did below, and this don't hurt very much in this snippet cos' we got just 2 groups, but could be complicated if we need to use a lot more groups. Another big thing to note is that we could have been done all of this using the masking technique (see step4.py). """ #** to manage the collision events need Directobject, because the chaining between the CollisionHandlerEvent handler and our two functions seen above, happens with the accept() function of a DirectObject instance DO=DirectObject() #** Let's manage the collision events: as you can see in this rack of 4 accepts, these are the resulting events generated by the patterns defined above. Now it's up to you to understand the why an the how comparing these with those above. DO.accept('ray1-into-smiley', collideInSmiley) DO.accept('ray1-out-smiley', collideOutSmiley) DO.accept('ray1-into-frowney', collideInFrowney) DO.accept('ray1-out-frowney', collideOutFrowney) # the event here instead, will call the collideAgainBalls function handler while the mouse pointer keep over any ball, either of the smiley or the frowney groups - still, check above how we defined the pattern string to understand how we came to this. DO.accept('ray_again_all', collideAgainBalls) #** Storage for the object actually hovered by the mouse pointer pickingEnabledOject=None #** Here's how we interact with mouse clicks - see the mouseClick function above DO.accept('mouse1', mouseClick, ['down']) DO.accept('mouse1-up', mouseClick, ['up']) #** And at the end of all, we start the task that continuously update the ray collider position and orientation while we move the mouse pointer taskMgr.add(rayupdate, "updatePicker")
class ControlSettingsScreenHelper(object): COLOUR_VALID = (1, 1, 1, 1) COLOUR_SELECTED = (0.75, 0.75, 1, 1) COLOUR_INVALID = (1, 0.75, 0.75, 1) def __init__(self, app, scene, parent): self.app = app self.scene = scene self.parent = parent self.keymap = self.app.keymap self.do = DirectObject() self.saveButton = None self.finishButton = None self.pendingChanges = False # The KeyboadMapping provides a dict of keys to actions: this member # does the opposite (provides a dict to actions to keys) self.keys = {} self.selectedAction = None self.inputLookup = {} self.layout = [] def show(self): self.keys = dict((v, k) for k, v in self.keymap.actions.items()) for column in self.layout: for category in column: for action in category: if action in self.keys: self.inputLookup[action]['text'] = self.keys[action] self.inputLookup[action]['frameColor'] = self.COLOUR_VALID else: self.inputLookup[action]['frameColor'] = self.COLOUR_INVALID self.keys[action] = None def setup(self, node): colours = self.app.theme.colours TEXT_PROPERTIES = { 'parent': node, 'text_scale': 0.038, 'text_fg': colours.listboxButtons, 'text_align': TextNode.A_right, 'relief': None, } KEY_PROPERTIES = { 'parent': node, 'scale': 0.038, 'frameColor': self.COLOUR_VALID, 'frameSize': (-3.0, 3.0, -0.7, 0.7), 'text_align': TextNode.A_center, 'text_scale': 0.9, 'text_pos': (0, -0.18), 'relief': DGG.FLAT, 'textMayChange': True, 'command': self.actionSelected, } movement = [ACTION_JUMP, ACTION_DOWN, ACTION_LEFT, ACTION_RIGHT] menus = [ACTION_MAIN_MENU, ACTION_MORE_MENU] actions = [ ACTION_UPGRADE_MENU, ACTION_USE_UPGRADE, ACTION_ABANDON_UPGRADE, ACTION_EDIT_PLAYER_INFO, ACTION_READY, ACTION_PAUSE_GAME, ACTION_EMOTE, ] misc = [ACTION_CHAT, ACTION_FOLLOW] upgrades = [ upgradeClass.action for upgradeClass in sorted( allUpgrades, key=lambda upgradeClass: upgradeClass.order)] upgrades.append(ACTION_CLEAR_UPGRADE) display = [ ACTION_LEADERBOARD_TOGGLE, ACTION_HUD_TOGGLE, ACTION_TERMINAL_TOGGLE] actionNames = { ACTION_ABANDON_UPGRADE: 'Abandon upgrade', ACTION_UPGRADE_MENU: 'Select upgrade', ACTION_USE_UPGRADE: 'Activate upgrade', ACTION_EDIT_PLAYER_INFO: 'Change nick / hat', ACTION_CHAT: 'Chat', ACTION_DOWN: 'Drop down', ACTION_EMOTE: 'Emote', ACTION_FOLLOW: 'Auto pan (replay)', ACTION_JUMP: 'Jump', ACTION_LEADERBOARD_TOGGLE: 'Show leaderboard', ACTION_LEFT: 'Move left', ACTION_MAIN_MENU: 'Main menu', ACTION_MORE_MENU: 'Advanced', ACTION_CLEAR_UPGRADE: 'Deselect upgrade', ACTION_READY: 'Toggle ready', ACTION_PAUSE_GAME: 'Pause/resume', ACTION_RIGHT: 'Move right', ACTION_HUD_TOGGLE: 'Toggle HUD', ACTION_TERMINAL_TOGGLE: 'Toggle terminal', } actionNames.update((upgradeClass.action, upgradeClass.name) for upgradeClass in allUpgrades) # Organise the categories by column self.layout = [ [movement, menus], [actions, display], [upgrades, misc], ] xPos = -0.68 for column in self.layout: # Each column yPos = 0.30 for category in column: # Each category for action in category: # Each action # Draw action name (eg. Respawn) label = DirectLabel( text=actionNames[action], **TEXT_PROPERTIES ) align(label, right=xPos, midZ=yPos) # Create input box box = DirectButton( text='', extraArgs=[action], **KEY_PROPERTIES ) align(box, left=xPos + 0.03, midZ=yPos) self.inputLookup[action] = box yPos -= 0.07 # Between items yPos -= 0.08 # Between categories xPos += 0.65 # Between columns BUTTON_PROPERTIES = { 'scale': 0.04, 'frameSize': (-5.0, 5.0, -1.0, 1.5), 'parent': node, } self.restoreDefaultButton = DirectButton( text='Restore defaults', # scale=0.04, # parent=node, command=self.restoreDefaults, # text_align=TextNode.A_left, # pad=(0.5, 0.2) **BUTTON_PROPERTIES ) align(self.restoreDefaultButton, midX=0, z=-0.63) self.saveButton = DirectButton( text='Save', command=self.save, **BUTTON_PROPERTIES ) align(self.saveButton, left=-0.87, z=-0.63) self.saveButton.hide() self.finishButton = DirectButton( text='Back', command=self.cancelPressed, **BUTTON_PROPERTIES ) align(self.finishButton, right=0.87, z=-0.63) def cancelPressed(self): self.deselectAction() if self.pendingChanges: self.keymap.reset() self.show() self.hideSaveButton() else: self.parent.showMainButtons() def save(self): self.keymap.apply() self.keymap.save() self.hideSaveButton() def showSaveButton(self): self.pendingChanges = True self.saveButton.show() self.finishButton['text'] = 'Cancel' def hideSaveButton(self): self.pendingChanges = False self.saveButton.hide() self.finishButton['text'] = 'Back' def restoreDefaults(self): self.deselectAction() self.keymap.revertToDefault() self.show() self.showSaveButton() def deselectAction(self): if self.selectedAction: self.inputLookup[self.selectedAction]['frameColor'] = self.COLOUR_VALID self.selectedAction = None self.do.ignoreAll() def actionSelected(self, action): self.deselectAction() self.selectedAction = action button = self.inputLookup[action] button['frameColor'] = self.COLOUR_SELECTED log.debug('Changing key for %s' % action) self.app.panda.buttonThrowers[0].node().setButtonDownEvent('button') self.do.accept('button', self.keyPressed, [action]) def keyPressed(self, action, key): self.deselectAction() oldKey = self.keys[action] if oldKey == key: return # Remove the old key from the keymap if oldKey is not None: del self.keymap.actions[oldKey] self.inputLookup[action]['text'] = key # If there's a conflict, remove the conflicting action from the keymap if key in self.keymap.actions: secondAction = self.keymap.actions[key] log.debug('Overwriting conflicting key for %s' % secondAction) self.inputLookup[secondAction]['text'] = '' self.inputLookup[secondAction]['frameColor'] = self.COLOUR_INVALID self.keys[secondAction] = None # Update the keymap with the new key self.keys[action] = key self.keymap.actions[key] = action log.debug('New key for %s is %s' % (action, key)) self.showSaveButton()
class DirectEntry(DirectFrame): __module__ = __name__ directWtext = ConfigVariableBool('direct-wtext', 1) AllowCapNamePrefixes = ('Al', 'Ap', 'Ben', 'De', 'Del', 'Della', 'Delle', 'Der', 'Di', 'Du', 'El', 'Fitz', 'La', 'Las', 'Le', 'Les', 'Lo', 'Los', 'Mac', 'St', 'Te', 'Ten', 'Van', 'Von') ForceCapNamePrefixes = ("D'", 'DeLa', "Dell'", "L'", "M'", 'Mc', "O'") def __init__(self, parent = None, **kw): optiondefs = (('pgFunc', PGEntry, None), ('numStates', 3, None), ('state', DGG.NORMAL, None), ('entryFont', None, DGG.INITOPT), ('width', 10, self.setup), ('numLines', 1, self.setup), ('focus', 0, self.setFocus), ('cursorKeys', 1, self.setCursorKeysActive), ('obscured', 0, self.setObscureMode), ('backgroundFocus', 0, self.setBackgroundFocus), ('initialText', '', DGG.INITOPT), ('command', None, None), ('extraArgs', [], None), ('failedCommand', None, None), ('failedExtraArgs', [], None), ('focusInCommand', None, None), ('focusInExtraArgs', [], None), ('focusOutCommand', None, None), ('focusOutExtraArgs', [], None), ('rolloverSound', DGG.getDefaultRolloverSound(), self.setRolloverSound), ('clickSound', DGG.getDefaultClickSound(), self.setClickSound), ('autoCapitalize', 0, self.autoCapitalizeFunc), ('autoCapitalizeAllowPrefixes', DirectEntry.AllowCapNamePrefixes, None), ('autoCapitalizeForcePrefixes', DirectEntry.ForceCapNamePrefixes, None)) self.defineoptions(kw, optiondefs) DirectFrame.__init__(self, parent) if self['entryFont'] == None: font = DGG.getDefaultFont() else: font = self['entryFont'] self.onscreenText = self.createcomponent('text', (), None, OnscreenText, (), parent=hidden, text='', align=TextNode.ALeft, font=font, scale=1, mayChange=1) self.onscreenText.removeNode() self.bind(DGG.ACCEPT, self.commandFunc) self.bind(DGG.ACCEPTFAILED, self.failedCommandFunc) self.accept(self.guiItem.getFocusInEvent(), self.focusInCommandFunc) self.accept(self.guiItem.getFocusOutEvent(), self.focusOutCommandFunc) self._autoCapListener = DirectObject() self.initialiseoptions(DirectEntry) if not hasattr(self, 'autoCapitalizeAllowPrefixes'): self.autoCapitalizeAllowPrefixes = DirectEntry.AllowCapNamePrefixes if not hasattr(self, 'autoCapitalizeForcePrefixes'): self.autoCapitalizeForcePrefixes = DirectEntry.ForceCapNamePrefixes for i in range(self['numStates']): self.guiItem.setTextDef(i, self.onscreenText.textNode) self.setup() self.unicodeText = 0 if self['initialText']: self.enterText(self['initialText']) return None def destroy(self): self.ignoreAll() self._autoCapListener.ignoreAll() DirectFrame.destroy(self) def setup(self): self.guiItem.setupMinimal(self['width'], self['numLines']) def setFocus(self): PGEntry.setFocus(self.guiItem, self['focus']) def setCursorKeysActive(self): PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys']) def setObscureMode(self): PGEntry.setObscureMode(self.guiItem, self['obscured']) def setBackgroundFocus(self): PGEntry.setBackgroundFocus(self.guiItem, self['backgroundFocus']) def setRolloverSound(self): rolloverSound = self['rolloverSound'] if rolloverSound: self.guiItem.setSound(DGG.ENTER + self.guiId, rolloverSound) else: self.guiItem.clearSound(DGG.ENTER + self.guiId) def setClickSound(self): clickSound = self['clickSound'] if clickSound: self.guiItem.setSound(DGG.ACCEPT + self.guiId, clickSound) else: self.guiItem.clearSound(DGG.ACCEPT + self.guiId) def commandFunc(self, event): if self['command']: apply(self['command'], [self.get()] + self['extraArgs']) def failedCommandFunc(self, event): if self['failedCommand']: apply(self['failedCommand'], [self.get()] + self['failedExtraArgs']) def autoCapitalizeFunc(self): if self['autoCapitalize']: self._autoCapListener.accept(self.guiItem.getTypeEvent(), self._handleTyping) self._autoCapListener.accept(self.guiItem.getEraseEvent(), self._handleErasing) else: self._autoCapListener.ignore(self.guiItem.getTypeEvent()) self._autoCapListener.ignore(self.guiItem.getEraseEvent()) def focusInCommandFunc(self): if self['focusInCommand']: apply(self['focusInCommand'], self['focusInExtraArgs']) if self['autoCapitalize']: self.accept(self.guiItem.getTypeEvent(), self._handleTyping) self.accept(self.guiItem.getEraseEvent(), self._handleErasing) def _handleTyping(self, guiEvent): self._autoCapitalize() def _handleErasing(self, guiEvent): self._autoCapitalize() def _autoCapitalize(self): name = self.get().decode('utf-8') capName = '' wordSoFar = '' wasNonWordChar = True for i in xrange(len(name)): character = name[i] if string.lower(character) == string.upper(character) and character != "'": wordSoFar = '' wasNonWordChar = True else: capitalize = False if wasNonWordChar: capitalize = True elif character == string.upper(character) and len(self.autoCapitalizeAllowPrefixes) and wordSoFar in self.autoCapitalizeAllowPrefixes: capitalize = True elif len(self.autoCapitalizeForcePrefixes) and wordSoFar in self.autoCapitalizeForcePrefixes: capitalize = True if capitalize: character = string.upper(character) else: character = string.lower(character) wordSoFar += character wasNonWordChar = False capName += character self.enterText(capName.encode('utf-8')) def focusOutCommandFunc(self): if self['focusOutCommand']: apply(self['focusOutCommand'], self['focusOutExtraArgs']) if self['autoCapitalize']: self.ignore(self.guiItem.getTypeEvent()) self.ignore(self.guiItem.getEraseEvent()) def set(self, text): self.unicodeText = isinstance(text, types.UnicodeType) if self.unicodeText: self.guiItem.setWtext(text) else: self.guiItem.setText(text) def get(self, plain = False): if not self.unicodeText: wantWide = self.guiItem.isWtext() if not self.directWtext.getValue(): wantWide = False if plain: return wantWide and self.guiItem.getPlainWtext() else: return self.guiItem.getPlainText() elif wantWide: return self.guiItem.getWtext() else: return self.guiItem.getText() def setCursorPosition(self, pos): if pos < 0: self.guiItem.setCursorPosition(self.guiItem.getNumCharacters() + pos) else: self.guiItem.setCursorPosition(pos) def enterText(self, text): self.set(text) self.setCursorPosition(self.guiItem.getNumCharacters()) def getFont(self): return self.onscreenText.getFont() def getBounds(self, state = 0): tn = self.onscreenText.textNode mat = tn.getTransform() align = tn.getAlign() lineHeight = tn.getLineHeight() numLines = self['numLines'] width = self['width'] if align == TextNode.ALeft: left = 0.0 right = width elif align == TextNode.ACenter: left = -width / 2.0 right = width / 2.0 elif align == TextNode.ARight: left = -width right = 0.0 bottom = -0.3 * lineHeight - lineHeight * (numLines - 1) top = lineHeight self.ll.set(left, 0.0, bottom) self.ur.set(right, 0.0, top) self.ll = mat.xformPoint(self.ll) self.ur = mat.xformPoint(self.ur) pad = self['pad'] borderWidth = self['borderWidth'] self.bounds = [self.ll[0] - pad[0] - borderWidth[0], self.ur[0] + pad[0] + borderWidth[0], self.ll[2] - pad[1] - borderWidth[1], self.ur[2] + pad[1] + borderWidth[1]] return self.bounds
class PandaPhysicsSystem(DelegateByNetmode, SignalListener): subclasses = {} def __init__(self): self.register_signals() self.world = BulletWorld() self.world.setGravity((0, 0, -9.81)) # # Seems that this does not function # on_contact_added = PythonCallbackObject(self._on_contact_added) # self.world.set_contact_added_callback(on_contact_added) # on_filter = PythonCallbackObject(self._filter_collision) # self.world.set_filter_callback(on_filter) self.listener = DirectObject() self.listener.accept('bullet-contact-added', self._on_contact_added) self.listener.accept('bullet-contact-destroyed', self._on_contact_removed) debug_node = BulletDebugNode('Debug') debug_node.showWireframe(True) debug_node.showConstraints(True) debug_node.showBoundingBoxes(False) debug_node.showNormals(False) self.debug_nodepath = render.attachNewNode(debug_node) self.world.set_debug_node(debug_node) self.debug_nodepath.show() def _get_contacts(self, node): test = self.world.contact_test(node) contacts = [] for contact in test.get_contacts(): if contact.get_node0() == node: manifold = contact.get_manifold_point() position = manifold.get_position_world_on_a() normal = None elif contact.get_node1() == node: manifold = contact.get_manifold_point() position = manifold.get_position_world_on_b() normal = None else: continue impulse = manifold.get_applied_impulse() contact_ = CollisionContact(position, normal, impulse) contacts.append(contact_) return contacts def _on_contact_added(self, node_a, node_b): if node_a.has_python_tag("on_contact_added"): callback = node_a.get_python_tag("on_contact_added") contacts = self._get_contacts(node_a) callback(node_b, contacts) if node_b.has_python_tag("on_contact_added"): callback = node_b.get_python_tag("on_contact_added") contacts = self._get_contacts(node_b) callback(node_a, contacts) def _on_contact_removed(self, node_a, node_b): if node_a.has_python_tag("on_contact_removed"): callback = node_a.get_python_tag("on_contact_removed") callback(node_b) if node_b.has_python_tag("on_contact_removed"): callback = node_b.get_python_tag("on_contact_removed") callback(node_a) def _filter_collision(self, filter_data): filter_data.set_collide(True) @RegisterPhysicsNode.on_global def register_node(self, node): self.world.attachRigidBody(node) node.set_python_tag("world", self.world) @DeregisterPhysicsNode.on_global def deregister_node(self, node): self.world.removeRigidBody(node) node.clear_python_tag("world") def update(self, delta_time): self.world.doPhysics(delta_time)