├── .github └── workflows │ └── main.yml ├── .gitignore ├── AggiEngine ├── __init__.py ├── application.py ├── contactlistener.py ├── gameobject.py ├── gameobjecthandler.py ├── gamescreen.py ├── mainwindow.py ├── particles.py ├── sound.py ├── state.py ├── statemanager.py ├── tileloader.py └── uimanager.py ├── LICENSE ├── README.md ├── docs ├── application.html ├── contactlistener.html ├── gameobject.html ├── gameobjecthandler.html ├── gamescreen.html ├── index.html ├── mainwindow.html ├── particles.html ├── sound.html ├── state.html ├── statemanager.html ├── tileloader.html └── uimanager.html ├── setup.cfg └── setup.py /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | 28 | # Runs a set of commands using the runners shell 29 | - name: Updates the documentation folder 30 | run: | 31 | rm -r ./docs/* 32 | sudo apt-get install libasound2-dev 33 | pip install PySide2 34 | pip install PyOpenGL 35 | pip install Box2D 36 | pip install pytmx 37 | pip install pillow 38 | pip install numpy 39 | pip install simpleaudio 40 | pip install pdoc3 41 | pdoc3 --html --output-dir ./docs ./AggiEngine 42 | cd ./docs 43 | mv AggiEngine/* . 44 | rm -r ./AggiEngine 45 | git config --local user.name "action" 46 | git add . 47 | git commit -m "Auto updating docs" 48 | - name: Push changes # push the output folder to your repo 49 | uses: ad-m/github-push-action@master 50 | with: 51 | github_token: ${{ secrets.GITHUB_TOKEN }} 52 | force: true 53 | # Deploy the site 54 | - name: Deploy 55 | uses: peaceiris/actions-gh-pages@v3 56 | with: 57 | github_token: ${{ secrets.GITHUB_TOKEN }} 58 | publish_dir: ./docs 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ -------------------------------------------------------------------------------- /AggiEngine/__init__.py: -------------------------------------------------------------------------------- 1 | from AggiEngine.gamescreen import GameScreen 2 | from AggiEngine.state import State 3 | from AggiEngine.statemanager import StateManager 4 | from AggiEngine.uimanager import UiManager 5 | from AggiEngine.gameobject import GameObject 6 | from AggiEngine.mainwindow import MainWindow 7 | from AggiEngine.particles import Particles 8 | from AggiEngine.application import Application 9 | from AggiEngine.sound import Sound 10 | from AggiEngine.tileloader import TileMap 11 | -------------------------------------------------------------------------------- /AggiEngine/application.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from PySide2.QtWidgets import QApplication 4 | 5 | from . import State 6 | from .mainwindow import MainWindow 7 | 8 | 9 | class Application(QApplication): 10 | """ 11 | This is the core class of an AggiEngine application. 12 | """ 13 | 14 | def __init__(self, state: State, screenFps: Optional[float] = 120, fixedFps: Optional[float] = 60, 15 | args: Optional[list] = None) -> None: 16 | """ 17 | Creates and initializes QWidgets for the application to start. 18 | ``state:`` The initial state to launch the Application with 19 | ``args:`` System arguments passed in 20 | ``config:`` Set application parameters 21 | """ 22 | if args: 23 | super(Application, self).__init__(args) 24 | else: 25 | super(Application, self).__init__() 26 | 27 | self.window = MainWindow(self, state, screenFps, fixedFps) 28 | 29 | def run(self) -> None: 30 | """ 31 | Execute the application, this will start the passed State 32 | """ 33 | self.window.start() 34 | self.window.show() 35 | self.exec_() 36 | -------------------------------------------------------------------------------- /AggiEngine/contactlistener.py: -------------------------------------------------------------------------------- 1 | from Box2D import b2ContactListener, b2Contact, b2Manifold, b2Vec2 2 | 3 | 4 | class ContactListener(b2ContactListener): 5 | """ 6 | Receives contact information from Box2D and passes it along to the GameObjects in the collision. 7 | """ 8 | 9 | def __init__(self): 10 | b2ContactListener.__init__(self) 11 | 12 | def BeginContact(self, contact: b2Contact) -> None: 13 | """ 14 | Called when two Box2D bodies collide and calls their beginContact(otherBody) methods 15 | ``contact:`` The contact event 16 | """ 17 | bodyA = contact.fixtureA.body 18 | bodyB = contact.fixtureB.body 19 | bodyA.userData.beginContact(bodyB) 20 | bodyB.userData.beginContact(bodyA) 21 | 22 | def EndContact(self, contact: b2Contact) -> None: 23 | """ 24 | Called when two Box2D bodies collide and calls their endContact(otherBody) methods 25 | ``contact:`` The contact event 26 | """ 27 | bodyA = contact.fixtureA.body 28 | bodyB = contact.fixtureB.body 29 | bodyA.userData.endContact(bodyB) 30 | bodyB.userData.endContact(bodyA) 31 | 32 | def PreSolve(self, contact: b2Contact, manifold: b2Manifold) -> None: 33 | """ 34 | Called before Box2D handles a contact while the bodies are still overlapping 35 | """ 36 | bodyA = contact.fixtureA.body 37 | bodyB = contact.fixtureB.body 38 | bodyA.userData.preSolve(bodyB, manifold) 39 | bodyB.userData.preSolve(bodyA, manifold) 40 | 41 | def PostSolve(self, contact: b2Contact, impulse: b2Vec2) -> None: 42 | """ 43 | Called after Box2D handles a contact while the bodies are no longer overlapping 44 | """ 45 | bodyA = contact.fixtureA.body 46 | bodyB = contact.fixtureB.body 47 | bodyA.userData.postSolve(bodyB, impulse) 48 | bodyB.userData.postSolve(bodyA, impulse) 49 | -------------------------------------------------------------------------------- /AggiEngine/gameobject.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | import PySide2 3 | from Box2D import b2Contact, b2Vec2, b2Manifold 4 | 5 | 6 | class GameObject: 7 | 8 | def __init__(self, gameObjectHandler: Optional[object] = None): 9 | self.body = None 10 | self.active = True 11 | self.window = None 12 | self.vertices = [[0, 0], [0.1, 0], [0.1, 0.1], [0, 0.1]] 13 | self.color = [1, 1, 1, 1] 14 | self.position = [0, 0] 15 | self.rotation = 0 16 | self.gameObjectHandler = gameObjectHandler 17 | self.textureID = -1 18 | self.height = 1 19 | self.width = 1 20 | 21 | def getWidth(self) -> int: 22 | ''' 23 | Returns the width of the GameObject. 24 | ''' 25 | return self.width * 2 26 | 27 | def getHeight(self) -> int: 28 | ''' 29 | Returns the height. 30 | ''' 31 | return self.height * 2 32 | 33 | def setWidth(self, width: int) -> None: 34 | ''' 35 | Sets the GameObject's width. 36 | ''' 37 | self.width = width / 2 38 | 39 | def setHeight(self, height: int) -> None: 40 | ''' 41 | Sets the GameObject's height. 42 | ''' 43 | self.height = height / 2 44 | 45 | def start(self) -> None: 46 | ''' 47 | Called when the GameObject is created. 48 | ''' 49 | pass 50 | 51 | def update(self) -> None: 52 | ''' 53 | Called everytime the screen is rendered. 54 | ''' 55 | pass 56 | 57 | def fixedUpdate(self) -> None: 58 | ''' 59 | Called everytime Box2D finishes a physics update. 60 | ''' 61 | pass 62 | 63 | def exit(self) -> None: 64 | ''' 65 | Called when the GameObject is destroyed. 66 | ''' 67 | pass 68 | 69 | def keyPressed(self, event: PySide2.QtGui.QKeyEvent) -> None: 70 | ''' 71 | Called when a key is pushed down. 72 | ''' 73 | pass 74 | 75 | def keyReleased(self, event: PySide2.QtGui.QKeyEvent) -> None: 76 | ''' 77 | Called when a key is released. 78 | ''' 79 | pass 80 | 81 | def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) -> None: 82 | ''' 83 | Called when the cursor moves. 84 | ''' 85 | pass 86 | 87 | def mousePressed(self, event: PySide2.QtGui.QMouseEvent) -> None: 88 | ''' 89 | Called when a mouse button is pressed. 90 | ''' 91 | pass 92 | 93 | def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) -> None: 94 | ''' 95 | Called when a mouse button is released. 96 | ''' 97 | pass 98 | 99 | def beginContact(self, body: object) -> None: 100 | ''' 101 | Called whenever a GameObject's object first makes contact with another body. 102 | ''' 103 | pass 104 | 105 | def endContact(self, body: object) -> None: 106 | ''' 107 | Called whenever a GameObject's object stops making contact with another. 108 | ''' 109 | pass 110 | 111 | def preSolve(self, contact: b2Contact, manifold: b2Manifold) -> None: 112 | ''' 113 | Called whenever a GameObject's body's shape is overlapping another. 114 | Gravity and other forces will trigger this even if the body hasn't stopped making contact. 115 | ''' 116 | pass 117 | 118 | def postSolve(self, contact: b2Contact, impulse: b2Vec2) -> None: 119 | ''' 120 | Called whenever a GameObject's body's shape is not longer overlapping another. 121 | Gravity and other forces will trigger this even if the body hasn't stopped making contact. 122 | ''' 123 | pass 124 | -------------------------------------------------------------------------------- /AggiEngine/gameobjecthandler.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import PySide2 4 | from AggiEngine.contactlistener import ContactListener 5 | import math 6 | 7 | from Box2D import * 8 | 9 | 10 | class GameObjectHandler: 11 | 12 | def __init__(self, window, scale: Optional[float] = 16): 13 | self.timing = 1 / window.targetFixedFPS 14 | self.window = window 15 | 16 | self.world = Box2D.b2World(gravity=(0, -9.8)) # create instance of box 2d world 17 | self.world.contactListener = ContactListener() 18 | 19 | self.scale = scale # scaling parameter, is equal to pixels to meter 20 | self.gameObjects = [] # game object list 21 | self.removeList = [] 22 | 23 | def setScale(self, scale: float) -> None: 24 | ''' 25 | Set the pixels per meter scale for TMX, OpenGL, and Box2D calculations 26 | ''' 27 | self.scale = scale 28 | 29 | def update(self) -> None: 30 | ''' 31 | Calls update on all game objects, variable timing rate. Triggered back screen draws. 32 | ''' 33 | for gameObject in self.gameObjects: 34 | if gameObject.active: 35 | gameObject.update() 36 | 37 | def fixedUpdate(self) -> None: 38 | ''' 39 | Updates the Box2D world then game objects on a fixed time interval. 40 | ''' 41 | self.world.Step(self.timing, 6, 2) 42 | self.world.ClearForces() 43 | 44 | newRenderInfoList = [] 45 | for gameObject in self.gameObjects: 46 | info = [gameObject.textureID] 47 | 48 | if gameObject.textureID == -1: 49 | if len(gameObject.color) < 4: 50 | gameObject.color.append(1) 51 | info += [gameObject.vertices, gameObject.color] 52 | else: 53 | info += [gameObject.width, gameObject.height] 54 | 55 | if gameObject.active: 56 | gameObject.fixedUpdate() 57 | if gameObject.body is not None: 58 | gameObject.position = gameObject.body.position / self.scale 59 | gameObject.rotation = gameObject.body.angle 60 | info += [gameObject.position, math.degrees(gameObject.rotation)] 61 | newRenderInfoList.append(info) 62 | if self.window.gameScreen is not None: 63 | self.window.gameScreen.renderInfoList = newRenderInfoList 64 | 65 | for gameObject in self.removeList: 66 | self.gameObjects.remove(gameObject) 67 | self.world.DestroyBody(gameObject.body) 68 | self.removeList = [] 69 | 70 | def add(self, gameObject: object, bodyDef: Optional[b2BodyDef] = None, 71 | bodyFixtureDef: Optional[b2FixtureDef] = None, 72 | color: list = None) -> None: 73 | ''' 74 | Creates a game object. 75 | ''' 76 | self.gameObjects.append(gameObject) # adds game object to list of game objects 77 | gameObject.gameObjectHandler = self 78 | 79 | if bodyDef: 80 | body = self.world.CreateBody(bodyDef) 81 | body.CreateFixture(bodyFixtureDef) 82 | gameObject.body = body 83 | gameObject.body.userData = gameObject 84 | 85 | gameObject.body.userData = gameObject 86 | 87 | if type(bodyFixtureDef.shape) is b2PolygonShape: 88 | gameObject.vertices.clear() 89 | for vertex in bodyFixtureDef.shape.vertices: 90 | gameObject.vertices.append([vertex[0] / self.scale, vertex[1] / self.scale]) 91 | elif type(bodyFixtureDef.shape) is b2CircleShape: 92 | vertices = [] 93 | for i in range(0, 30): 94 | rad = (2 * math.pi * i) / 30 95 | r = bodyFixtureDef.shape.radius / self.scale 96 | vertices.append([(r * math.cos(rad) - (bodyFixtureDef.shape.pos[0] / self.scale)), 97 | (r * math.sin(rad) - (bodyFixtureDef.shape.pos[1] / self.scale))]) 98 | gameObject.vertices = vertices 99 | 100 | if color is None: 101 | gameObject.color = [1, 1, 1, 1] 102 | else: 103 | gameObject.color = color 104 | gameObject.position = body.position / self.scale 105 | 106 | gameObject.window = self.window 107 | gameObject.start() 108 | 109 | def getGameObject(self, typeOf: type): 110 | ''' 111 | Returns the first instance of the game object with the type passed 112 | ''' 113 | for gameObject in self.gameObjects: 114 | if isinstance(gameObject, typeOf): 115 | return gameObject 116 | 117 | def getGameObjects(self, typeOf: type) -> object: 118 | ''' 119 | Returns all instances of game objects with the type passed 120 | ''' 121 | gameObjects = [] 122 | for gameObject in self.gameObjects: 123 | if isinstance(gameObject, typeOf): 124 | gameObjects.append(gameObject) 125 | return gameObjects 126 | 127 | def removeGameObject(self, toRemove: object) -> None: 128 | ''' 129 | Removes the gameObject passed 130 | ''' 131 | self.removeList.append(toRemove) 132 | 133 | def removeGameObjects(self, typeOf: type) -> None: 134 | ''' 135 | Removes all game objects with the type passed 136 | ''' 137 | for gameObject in self.gameObjects: 138 | if isinstance(gameObject, typeOf): 139 | self.removeList.append(gameObject) 140 | 141 | def exit(self) -> None: 142 | ''' 143 | Trigger exit on all game objects, called when state exits 144 | ''' 145 | for gameObject in self.gameObjects: 146 | gameObject.exit() 147 | 148 | def keyPressed(self, event: PySide2.QtGui.QKeyEvent) -> None: 149 | ''' 150 | Trigger keyPressed on all game objects, called when a key is pressed 151 | ''' 152 | for gameObject in self.gameObjects: 153 | gameObject.keyPressed(event) 154 | 155 | def keyReleased(self, event: PySide2.QtGui.QKeyEvent) -> None: 156 | ''' 157 | Trigger keyReleased on all game objects, called when a key is released 158 | ''' 159 | for gameObject in self.gameObjects: 160 | gameObject.keyReleased(event) 161 | 162 | def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) -> None: 163 | ''' 164 | Trigger mouseMoved on all game objects, called when the mouse is moved 165 | ''' 166 | for gameObject in self.gameObjects: 167 | gameObject.mouseMoved(event) 168 | 169 | def mousePressed(self, event: PySide2.QtGui.QMouseEvent) -> None: 170 | ''' 171 | Trigger mousePressed on all game objects, called when a mouse key is pressed 172 | ''' 173 | for gameObject in self.gameObjects: 174 | gameObject.mousePressed(event) 175 | 176 | def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) -> None: 177 | ''' 178 | Trigger mouseReleased on all game objects, called when a mouse key is released 179 | ''' 180 | for gameObject in self.gameObjects: 181 | gameObject.mouseReleased(event) 182 | -------------------------------------------------------------------------------- /AggiEngine/gamescreen.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Callable 2 | 3 | from OpenGL.GL import * 4 | import numpy 5 | from PIL import Image 6 | from PIL import ImageOps 7 | from PySide2.QtWidgets import QWidget 8 | from PySide2.QtWidgets import QOpenGLWidget 9 | 10 | 11 | class GameScreen(QOpenGLWidget): 12 | 13 | def __init__(self, parent: QWidget): 14 | ''' 15 | Subclass of the QOpenGLWidget, this is promoted in Qt Designer so that 16 | we can draw to the widget. 17 | ``parent:`` The widget that the this held under usually MainWindow 18 | ''' 19 | super(GameScreen, self).__init__(parent=parent) 20 | 21 | self.cameraPosition = [0, 0] 22 | self.cameraScale = 1 23 | self.renderInfoList = [] 24 | self.bgColor = [0, 0, 0] 25 | 26 | def initializeGL(self) -> None: 27 | ''' 28 | Sets up OpenGL for rendering 29 | ''' 30 | glEnable(GL_BLEND) 31 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 32 | glClearColor(self.bgColor[0], self.bgColor[1], self.bgColor[2], 1) 33 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 34 | glEnable(GL_TEXTURE_2D) 35 | 36 | def paintGL(self) -> None: 37 | ''' 38 | Draws all game objects rect and texture id given by game object handler 39 | ''' 40 | 41 | glClearColor(self.bgColor[0], self.bgColor[1], self.bgColor[2], 1) 42 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 43 | glColor3f(1, 1, 1) 44 | glLoadIdentity() 45 | glScalef(self.cameraScale, self.cameraScale, 0) 46 | glTranslatef(-self.cameraPosition[0], -self.cameraPosition[1], 0) 47 | for renderInfo in self.renderInfoList: 48 | 49 | if renderInfo[0] == -1: 50 | glPushMatrix() 51 | glTranslatef(renderInfo[3][0], renderInfo[3][1], 0) 52 | glRotatef(renderInfo[4], 0, 0, 1) 53 | glColor4f(renderInfo[2][0], renderInfo[2][1], renderInfo[2][2], renderInfo[2][3]) 54 | glPolygonMode(GL_FRONT, GL_FILL) 55 | glBegin(GL_POLYGON) 56 | for vertex in renderInfo[1]: 57 | glVertex2f(vertex[0], vertex[1]) 58 | glEnd() 59 | glPopMatrix() 60 | else: 61 | glPushMatrix() 62 | glTranslatef(renderInfo[3][0], renderInfo[3][1], 0) 63 | glRotatef(renderInfo[4], 0, 0, 1) 64 | glBindTexture(GL_TEXTURE_2D, renderInfo[0]) 65 | glBegin(GL_QUADS) 66 | glTexCoord2f(0, 0) 67 | glVertex2f(renderInfo[1], renderInfo[2]) 68 | glTexCoord2f(1, 0) 69 | glVertex2f(-renderInfo[1], renderInfo[2]) 70 | glTexCoord2f(1, 1) 71 | glVertex2f(-renderInfo[1], -renderInfo[2]) 72 | glTexCoord2f(0, 1) 73 | glVertex2f(renderInfo[1], -renderInfo[2]) 74 | glEnd() 75 | glBindTexture(GL_TEXTURE_2D, 0) 76 | glPopMatrix() 77 | 78 | def resizeGL(self, w, h) -> None: 79 | ''' 80 | Resize event recalculates values to maintain the same image. 81 | ''' 82 | glViewport(0, 0, w, h) 83 | glMatrixMode(GL_PROJECTION) 84 | glLoadIdentity() 85 | aspect = w / h 86 | glOrtho(aspect, -aspect, -1.0, 1.0, 1.0, -1.0) 87 | glMatrixMode(GL_MODELVIEW) 88 | glLoadIdentity() 89 | 90 | def loadTexture(self, imageData: [int], width: int, height: int) -> int: 91 | ''' 92 | Loads the texture to the render device. 93 | Returns it's texture id. 94 | ''' 95 | textureID = glGenTextures(1) 96 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4) 97 | glBindTexture(GL_TEXTURE_2D, textureID) 98 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) 99 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) 100 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) 101 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) 102 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData) 103 | return textureID 104 | 105 | def loadImageTexture(self, fileName: str) -> int: 106 | ''' 107 | Loads an image from a path and loads it to the render device. 108 | Returns the texture id of the image. 109 | ''' 110 | image = Image.open(fileName) 111 | 112 | # Add alpha if the base image doesn't have one 113 | if not (fileName.split(".")[-1] == "png"): 114 | image.putalpha(256) 115 | 116 | # Making image data for OpenGL 117 | imageData = numpy.array(list(image.getdata()), numpy.uint8) 118 | return self.loadTexture(imageData, image.width, image.height) 119 | 120 | def image_loader(self, filename: str, colorkey, **kwargs) -> Callable: 121 | ''' 122 | The image loader used by pytmx so that all images are sent to the render devices memory for quick drawing. 123 | ''' 124 | image = Image.open(filename) 125 | transparent = filename.split(".")[-1] == "png" 126 | 127 | def extract_image(rect: Optional[list] = None, flags=None) -> int: 128 | # Cropping the image to the necessary size 129 | if rect: 130 | crop = image.crop((rect[0], rect[1], rect[0] + rect[2], rect[1] + rect[3])) 131 | else: 132 | crop = image 133 | 134 | if flags: 135 | # Handling the flags 136 | if flags.flipped_horizontally: 137 | crop = ImageOps.mirror(crop) 138 | if flags.flipped_vertically: 139 | crop = ImageOps.flip(crop) 140 | 141 | # Add alpha if the base image doesn't have one 142 | if not transparent: 143 | crop.putalpha(256) 144 | # Making image data for OpenGL 145 | imageData = numpy.array(list(crop.getdata()), numpy.uint8) 146 | return self.loadTexture(imageData, crop.width, crop.height), crop.width, crop.height 147 | 148 | return extract_image 149 | 150 | -------------------------------------------------------------------------------- /AggiEngine/mainwindow.py: -------------------------------------------------------------------------------- 1 | import time 2 | from typing import Optional 3 | 4 | import PySide2 5 | from PySide2.QtCore import QTimer, Qt 6 | from PySide2.QtWidgets import QMainWindow, QWidget 7 | 8 | from .uimanager import UiManager 9 | from .statemanager import StateManager 10 | from .state import State 11 | from .gamescreen import GameScreen 12 | 13 | 14 | class MainWindow(QMainWindow): 15 | 16 | def __init__(self, app, state: State, screenFPS: int, fixedFPS: int, parent: Optional[QWidget] = None): 17 | ''' 18 | The MainWindow is created by the application, here we handle ui/scenes and send out updates. 19 | ``state:`` The current state we want to start with. 20 | ``parent:`` The widget this held under, None by default because this is top widget 21 | ''' 22 | QMainWindow.__init__(self, parent) 23 | self.app = app # the application created 24 | self.gameScreen = None # where all graphics are drawn 25 | self.uiManager = UiManager(self, customWidgets=[GameScreen]) # Loads widgets into window from a file 26 | self.stateManager = StateManager(self, state) # Manages state updates and transitions 27 | 28 | self.updateFPSTimer = QTimer(self) # Used to manage frame timings 29 | self.updateFPSTimer.timeout.connect(self.__calculateFPS) 30 | 31 | self.fixedFps = 0 # Calculated FPS 32 | self.screenFps = 0 33 | 34 | self.fixedFrames = 0 # Accumulated Frames 35 | self.screenFrames = 0 36 | 37 | self.targetFixedFPS = fixedFPS 38 | self.targetUpdateFPS = screenFPS 39 | self.fixedTiming = 1 / self.targetFixedFPS 40 | self.screenTiming = 1 / self.targetUpdateFPS 41 | self.fixedNeeded = fixedFPS 42 | self.screenNeeded = screenFPS 43 | self.lastTime = 0 44 | self.setMouseTracking(True) 45 | 46 | self.uiManager.keepWidgets = self.children() 47 | 48 | def start(self) -> None: 49 | ''' 50 | Called when application.run() is executed. Starts the actual engine. 51 | ''' 52 | self.stateManager.initializeState() # start the state 53 | self.updateFPSTimer.start(100) 54 | 55 | def closeEvent(self, event: PySide2.QtGui.QCloseEvent) -> None: 56 | ''' 57 | Called when the window is closed 58 | ''' 59 | print("Window closed.") 60 | self.updateFPSTimer.stop() 61 | self.stateManager.exit() 62 | 63 | def __calculateFPS(self) -> None: 64 | ''' 65 | Averages FPS and adjust frame timings to meet the targets 66 | ''' 67 | 68 | self.fixedFps = self.fixedFrames / (time.perf_counter() - self.lastTime) 69 | self.fixedFrames = 0 70 | 71 | self.screenFps = self.screenFrames / (time.perf_counter() - self.lastTime) 72 | self.screenFrames = 0 73 | 74 | if -0.5 < (self.targetFixedFPS - self.fixedFps) / self.targetFixedFPS < 0.5 and self.fixedNeeded > 30: 75 | self.fixedNeeded += (self.targetFixedFPS - self.fixedFps) * 0.25 76 | self.fixedTiming = 1 / self.fixedNeeded 77 | 78 | if -0.5 < (self.screenNeeded - self.screenFps) / self.targetUpdateFPS < 0.5 and self.screenNeeded > 30: 79 | self.screenNeeded += (self.targetUpdateFPS - self.screenFps) * 0.15 80 | self.screenTiming = 1 / self.screenNeeded 81 | 82 | self.lastTime = time.perf_counter() 83 | 84 | def resizeEvent(self, event: PySide2.QtGui.QResizeEvent) -> None: 85 | ''' 86 | Triggered when the screen is resized and passes down the event. 87 | ''' 88 | if self.gameScreen: 89 | self.gameScreen.setGeometry(0, 0, self.width(), self.height()) 90 | 91 | def keyPressEvent(self, event: PySide2.QtGui.QKeyEvent) -> None: 92 | ''' 93 | Triggered when a key is pressed and passes down the event. 94 | ''' 95 | self.stateManager.keyPressed(event) 96 | 97 | def keyReleaseEvent(self, event: PySide2.QtGui.QKeyEvent) -> None: 98 | ''' 99 | Triggered when a key is released and passes down the event. 100 | ''' 101 | self.stateManager.keyReleased(event) 102 | 103 | def mouseMoveEvent(self, event: PySide2.QtGui.QMouseEvent) -> None: 104 | ''' 105 | Triggered when the mouse is moved and passes down the event. 106 | ''' 107 | self.stateManager.mouseMoved(event) 108 | 109 | def mousePressEvent(self, event: PySide2.QtGui.QMouseEvent) -> None: 110 | ''' 111 | Triggered when a mouse key is pressed and passes down the event. 112 | ''' 113 | self.stateManager.mousePressed(event) 114 | 115 | def mouseReleaseEvent(self, event: PySide2.QtGui.QMouseEvent) -> None: 116 | ''' 117 | Triggered when a mouse key is released and passes down the event. 118 | ''' 119 | self.stateManager.mouseReleased(event) 120 | 121 | def waitForLoad(self) -> None: 122 | ''' 123 | Makes state manager wait until the current state finishes loading widgets. 124 | ''' 125 | QTimer(self).singleShot(0, self.stateManager.start) 126 | -------------------------------------------------------------------------------- /AggiEngine/particles.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import numpy as np 4 | 5 | from AggiEngine import GameScreen 6 | 7 | 8 | class Particles: 9 | 10 | def __init__(self, gameScreen: GameScreen, startColor: Optional[list] = None, 11 | endColor: Optional[list] = None, shape: Optional[list] = None, rate=0.5, 12 | count=100, endSize=0.01, sizeDecrease=0.95, colorFade=0.05): 13 | if not endColor: 14 | self.endColor = [1, 1, 1, 1] 15 | else: 16 | self.endColor = endColor 17 | 18 | if not startColor: 19 | self.startColor = [1, 1, 1, 1] 20 | else: 21 | self.startColor = startColor 22 | 23 | if not shape: 24 | self.shape = [[0, 0], [0.05, 0], [0.05, -0.05], [0, -0.05]] 25 | else: 26 | self.shape = shape 27 | 28 | self.gameScreen = gameScreen 29 | self.particles = [] 30 | self.rate = rate 31 | self.count = count 32 | self.endSize = endSize 33 | self.sizeDecrease = sizeDecrease 34 | self.colorFade = colorFade 35 | self.time = 0 36 | 37 | def update(self) -> None: 38 | ''' 39 | Updates the particles, color, size and deletes old particles. 40 | ''' 41 | toRemove = [] 42 | for particle in self.particles: 43 | if abs(particle[1][0][0] - particle[1][1][0]) < self.endSize: 44 | toRemove.append(particle) 45 | 46 | shape = [] 47 | for vert in particle[1]: 48 | shape.append([vert[0] * self.sizeDecrease, vert[1] * self.sizeDecrease]) 49 | particle[1] = shape 50 | particle[2] = self.getColor(particle[-1]) 51 | particle[-1] -= self.colorFade 52 | self.gameScreen.renderInfoList.append(particle) 53 | 54 | for particle in toRemove: 55 | self.particles.remove(particle) 56 | 57 | self.time += self.rate 58 | 59 | def emit(self, position: list) -> None: 60 | ''' 61 | Appends are new particle to the particle list. 62 | ''' 63 | 64 | if self.time > 1: 65 | if len(self.particles) < self.count: 66 | self.particles.append([-1, self.shape, self.getColor(0), position, 0, 0]) 67 | self.time = 0 68 | 69 | def getColor(self, amount: float) -> list: 70 | ''' 71 | Interpolates the start and end color values and returns the value. 72 | ''' 73 | 74 | r = abs(self.startColor[0] - self.endColor[0]) * amount + self.startColor[0] 75 | g = abs(self.startColor[1] - self.endColor[1]) * amount + self.startColor[1] 76 | b = abs(self.startColor[2] - self.endColor[2]) * amount + self.startColor[2] 77 | a = abs(self.startColor[3] - self.endColor[3]) * amount + self.startColor[3] 78 | 79 | return [max(r, self.endColor[0]), max(g, self.endColor[1]), max(b, self.endColor[2]), max(a, self.endColor[3])] 80 | -------------------------------------------------------------------------------- /AggiEngine/sound.py: -------------------------------------------------------------------------------- 1 | import simpleaudio as sa 2 | from typing import Any 3 | 4 | 5 | class Sound: 6 | 7 | def __init__(self) -> None: 8 | self.sounds = dict() 9 | 10 | def load(self, key: Any, filestr: str) -> None: 11 | """ 12 | loads a wav file for future playback 13 | ``key:`` A key you will use to access the audio playback 14 | ``filestr:`` The file path for a wav file to play 15 | """ 16 | self.sounds[key] = sa.WaveObject.from_wave_file(filestr) 17 | 18 | def play(self, key: Any) -> sa.PlayObject: 19 | """ 20 | Plays your sound 21 | ``key:`` The key used when loading the sound 22 | """ 23 | return self.sounds[key].play() 24 | 25 | def stop_all(self) -> None: 26 | """ 27 | Stops all sounds from playing 28 | """ 29 | sa.stop_all() 30 | -------------------------------------------------------------------------------- /AggiEngine/state.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import PySide2 4 | from .gameobjecthandler import GameObjectHandler 5 | from .tileloader import TileMap 6 | 7 | 8 | class State: 9 | 10 | def __init__(self, ui_path: Optional[str] = None, window: Optional[object] = None): 11 | ''' 12 | Initialized class variables. 13 | ``ui_path:`` The path to the .ui file for the State to display 14 | ``window:`` The main window class for the whole application 15 | ''' 16 | self.ui_path = ui_path 17 | self.window = window 18 | self.gameObjectHandler = None 19 | self.active = True 20 | 21 | def loadMap(self, filePath: str) -> None: 22 | ''' 23 | Loads the TMX file and creates the contained game objects 24 | ``filePath:`` The path to the .tmx file to load game objects 25 | ''' 26 | TileMap(filePath, self.gameObjectHandler, self.window.gameScreen) 27 | 28 | def loadUi(self) -> None: 29 | ''' 30 | Loads the widgets in the .ui file 31 | ''' 32 | if self.ui_path is not None: 33 | self.window.uiManager.loadWidgets(self.ui_path, True) 34 | 35 | def startGOH(self) -> None: 36 | ''' 37 | Starts the game object handler 38 | ''' 39 | self.gameObjectHandler = GameObjectHandler(self.window) 40 | 41 | def updateGOH(self) -> None: 42 | ''' 43 | Updates the game object handler after a screen draw 44 | ''' 45 | self.gameObjectHandler.update() 46 | 47 | def fixedUpdateGOH(self) -> None: 48 | ''' 49 | Updates the game object handler after a physics update 50 | ''' 51 | self.gameObjectHandler.fixedUpdate() 52 | 53 | def exitGOH(self) -> None: 54 | ''' 55 | Stops the game object handler 56 | ''' 57 | self.gameObjectHandler.exit() 58 | 59 | def start(self) -> None: 60 | ''' 61 | Triggered when the State is first activated 62 | ''' 63 | pass 64 | 65 | def update(self) -> None: 66 | ''' 67 | Triggered after a screen draw 68 | ''' 69 | pass 70 | 71 | def fixedUpdate(self) -> None: 72 | ''' 73 | Triggered after a physics update 74 | ''' 75 | pass 76 | 77 | def exit(self) -> None: 78 | ''' 79 | Triggered when the State stops 80 | ''' 81 | pass 82 | 83 | def keyPressed(self, event: PySide2.QtGui.QKeyEvent) -> None: 84 | ''' 85 | Triggered when a key is pressed down 86 | ``event:`` The key event contains the key(s) pressed 87 | ''' 88 | pass 89 | 90 | def keyReleased(self, event: PySide2.QtGui.QKeyEvent) -> None: 91 | ''' 92 | Triggered when a key is released 93 | ``event:`` The key event contains the key(s) released 94 | ''' 95 | pass 96 | 97 | def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) -> None: 98 | ''' 99 | Triggered when the mouse is moved 100 | ``event:`` The mouse event contains the new mouse positions 101 | ''' 102 | pass 103 | 104 | def mousePressed(self, event: PySide2.QtGui.QMouseEvent) -> None: 105 | ''' 106 | Triggered when mouse buttons are pressed 107 | ``event:`` The mouse event contains the mouse buttons pressed 108 | ''' 109 | pass 110 | 111 | def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) -> None: 112 | ''' 113 | Triggered when mouse buttons are released 114 | ``event:`` The mouse event contains the mouse buttons released 115 | ''' 116 | pass 117 | -------------------------------------------------------------------------------- /AggiEngine/statemanager.py: -------------------------------------------------------------------------------- 1 | from typing import Callable 2 | 3 | from PySide2.QtCore import QRunnable, Slot, QThreadPool 4 | import PySide2 5 | from .state import State 6 | from .gamescreen import GameScreen 7 | 8 | import time 9 | 10 | 11 | class Physics(QRunnable): 12 | 13 | def __init__(self, fixedUpdate: Callable, state: State): 14 | ''' 15 | Runs all physics related events on a separate loop and thread. 16 | ``fixedUpdate:`` State Managers fixedUpdate function 17 | ``state:`` The current state loaded 18 | ''' 19 | QRunnable.__init__(self) 20 | self.fixedUpdate = fixedUpdate 21 | self.window = state.window 22 | self.state = state 23 | self.setAutoDelete(False) 24 | 25 | @Slot() # QtCore.Slot 26 | def run(self) -> None: 27 | ''' 28 | Starts the physics loop 29 | ''' 30 | while self.state.active: 31 | start = time.perf_counter() 32 | self.fixedUpdate() 33 | self.window.fixedFrames += 1 34 | wait = self.window.fixedTiming - (time.perf_counter() - start) 35 | time.sleep(wait if wait > 0 else 0) 36 | 37 | 38 | class Rendering(QRunnable): 39 | 40 | def __init__(self, update: Callable, state: State): 41 | ''' 42 | Runs all rendering related events on a separate loop and thread. 43 | ``update:`` State Managers update function 44 | ``state:`` The current state loaded 45 | ''' 46 | QRunnable.__init__(self) 47 | self.update = update 48 | self.window = state.window 49 | self.state = state 50 | self.setAutoDelete(False) 51 | 52 | @Slot() # QtCore.Slot 53 | def run(self) -> None: 54 | ''' 55 | Starts the rendering loop 56 | ''' 57 | while self.state.active: 58 | start = time.perf_counter() 59 | self.update() 60 | if self.window.gameScreen: 61 | self.window.gameScreen.update() 62 | self.window.screenFrames += 1 63 | wait = self.window.screenTiming - (time.perf_counter() - start) 64 | time.sleep(wait if wait > 0 else 0) 65 | 66 | 67 | class StateManager: 68 | 69 | def __init__(self, window: object, state: State): 70 | ''' 71 | ``window:`` The main window class on the application 72 | ``state:`` The initial state to be displayed 73 | ''' 74 | self.window = window 75 | self.currentState = state 76 | self.threadPool = QThreadPool() 77 | 78 | def changeState(self, state: State) -> None: 79 | ''' 80 | Switch states 81 | ``state:`` The next state to show 82 | ''' 83 | self.currentState.active = False 84 | self.currentState.exitGOH() 85 | self.currentState.exit() 86 | self.currentState = state 87 | self.initializeState() 88 | 89 | def update(self) -> None: 90 | ''' 91 | Triggered after a frame draw 92 | ''' 93 | self.currentState.updateGOH() 94 | self.currentState.update() 95 | 96 | def fixedUpdate(self) -> None: 97 | ''' 98 | Triggered after a physics update 99 | ''' 100 | self.currentState.fixedUpdateGOH() 101 | self.currentState.fixedUpdate() 102 | 103 | def initializeState(self) -> None: 104 | ''' 105 | Prepares State for execution 106 | ''' 107 | self.currentState.window = self.window # Give the state a reference to the window 108 | self.currentState.loadUi() # Load the states UI 109 | self.window.waitForLoad() 110 | 111 | def start(self) -> None: 112 | ''' 113 | Starts game loops and starts the State 114 | ''' 115 | self.window.gameScreen = self.window.findChild(GameScreen) 116 | self.currentState.startGOH() 117 | self.currentState.start() # Start the state 118 | self.threadPool.start(Physics(self.fixedUpdate, self.currentState)) 119 | self.threadPool.start(Rendering(self.update, self.currentState)) 120 | 121 | def exit(self) -> None: 122 | ''' 123 | Stops the State 124 | ''' 125 | self.currentState.active = False 126 | self.currentState.exitGOH() 127 | self.currentState.exit() 128 | 129 | def keyPressed(self, event: PySide2.QtGui.QKeyEvent) -> None: 130 | ''' 131 | Triggered when a key is pressed down 132 | ``event:`` The key event contains the key(s) pressed 133 | ''' 134 | self.currentState.keyPressed(event) 135 | self.currentState.gameObjectHandler.keyPressed(event) 136 | 137 | def keyReleased(self, event: PySide2.QtGui.QKeyEvent) -> None: 138 | ''' 139 | Triggered when a key is released 140 | ``event:`` The key event contains the key(s) released 141 | ''' 142 | self.currentState.keyReleased(event) 143 | self.currentState.gameObjectHandler.keyReleased(event) 144 | 145 | def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) -> None: 146 | ''' 147 | Triggered when the mouse is moved 148 | ``event:`` The mouse event contains the new mouse positions 149 | ''' 150 | self.currentState.mouseMoved(event) 151 | self.currentState.gameObjectHandler.mouseMoved(event) 152 | 153 | def mousePressed(self, event: PySide2.QtGui.QMouseEvent) -> None: 154 | ''' 155 | Triggered when mouse buttons are pressed 156 | ``event:`` The mouse event contains the mouse buttons pressed 157 | ''' 158 | self.currentState.mousePressed(event) 159 | self.currentState.gameObjectHandler.mousePressed(event) 160 | 161 | def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) -> None: 162 | ''' 163 | Triggered when mouse buttons are released 164 | ``event:`` The mouse event contains the mouse buttons released 165 | ''' 166 | self.currentState.mouseReleased(event) 167 | self.currentState.gameObjectHandler.mouseReleased(event) 168 | -------------------------------------------------------------------------------- /AggiEngine/tileloader.py: -------------------------------------------------------------------------------- 1 | import pytmx 2 | import Box2D 3 | import importlib 4 | from PIL import ImageColor 5 | from .gameobject import GameObject 6 | 7 | 8 | class TileMap: 9 | 10 | def __init__(self, tmxFile, gameObjectHandler, gameScreen): 11 | tiled_map = pytmx.TiledMap(tmxFile, image_loader=gameScreen.image_loader) 12 | if tiled_map.background_color: 13 | color = ImageColor.getcolor(tiled_map.background_color, 'RGB') 14 | gameScreen.bgColor = [color[0] / 255, color[1] / 255, color[2] / 255] 15 | 16 | hitboxes = {} 17 | objects = tiled_map.objects 18 | 19 | # Looks for objects and gets its vertices 20 | for obj in objects: 21 | pairPoints = [] 22 | for pair in obj.points: 23 | pairPoints.append( 24 | (-(pair[0] - obj.x) / gameObjectHandler.scale, -(pair[1] - obj.y) / gameObjectHandler.scale)) 25 | hitboxes[obj.name] = pairPoints 26 | 27 | for layer in tiled_map: 28 | # Checks for layers with tiles 29 | if isinstance(layer, pytmx.TiledTileLayer): 30 | for x, y, image in layer.tiles(): 31 | # Gets class name and gets class name definition 32 | className = layer.properties.get('class', 'GameObject') 33 | 34 | if className != 'GameObject': 35 | className = getattr(importlib.import_module('__main__'), className) 36 | else: 37 | className = GameObject 38 | # Sets up Texture 39 | gm = className() 40 | gm.textureID = image[0] 41 | 42 | gm.setWidth(image[1] / gameObjectHandler.scale ** 2) 43 | gm.setHeight(image[2] / gameObjectHandler.scale ** 2) 44 | 45 | # Sets up Color 46 | color = layer.properties.get('color', None) 47 | if color: 48 | color = ImageColor.getcolor(color, 'RGBA') 49 | color = [color[2] / 255, color[1] / 255, color[0] / 255, color[3] / 255] 50 | print(color) 51 | 52 | # Sets up Hitbox 53 | verticesName = layer.properties.get('hitboxName', None) 54 | 55 | # Sets up Body Type 56 | bodyType = layer.properties.get('bodyType', 'none') 57 | if bodyType != 'none': 58 | bodyDef = Box2D.b2BodyDef() 59 | if bodyType == 'dynamic': 60 | bodyDef.type = Box2D.b2_dynamicBody 61 | elif bodyType == 'static': 62 | bodyDef.type = Box2D.b2_staticBody 63 | else: 64 | bodyDef.type = Box2D.b2_kinematicBody 65 | bodyDef.linearDamping = layer.properties.get('linearDamping', 0) 66 | bodyDef.angularDamping = layer.properties.get('angularDamping', 0) 67 | bodyDef.fixedRotation = layer.properties.get('fixedRotation', False) 68 | 69 | # Linear Vel (Broken) 70 | linearVel = layer.properties.get('linearVel', None) 71 | if linearVel: 72 | linearVel = [float(x) for x in linearVel.split(',')] 73 | bodyDef.linearVelocity = (linearVel[0], linearVel[1]) 74 | 75 | # Sets up body Fixture 76 | if verticesName: 77 | bodyFixtureDef = Box2D.b2FixtureDef( 78 | shape=Box2D.b2PolygonShape(vertices=hitboxes[verticesName])) 79 | else: 80 | bodyFixtureDef = Box2D.b2FixtureDef(shape=Box2D.b2PolygonShape( 81 | box=((image[1] / gameObjectHandler.scale) / 2, 82 | (image[2] / gameObjectHandler.scale) / 2))) 83 | bodyFixtureDef.density = layer.properties.get('density', 1) 84 | bodyFixtureDef.friction = layer.properties.get('friction', 0) 85 | bodyFixtureDef.restitution = layer.properties.get('restitution', 0) 86 | # Sets up Position 87 | bodyDef.position = ( 88 | -(x * tiled_map.tilewidth) / gameObjectHandler.scale, 89 | -(y * tiled_map.tileheight) / gameObjectHandler.scale) 90 | # adds box 91 | if color: 92 | gm.textureID = -1 93 | gameObjectHandler.add(gm, bodyDef, bodyFixtureDef, color) 94 | else: 95 | gameObjectHandler.add(gm, bodyDef, bodyFixtureDef) 96 | else: 97 | gm.position = (-x / gameObjectHandler.scale, -y / gameObjectHandler.scale) 98 | if color: 99 | gm.textureID = -1 100 | vertices = hitboxes[verticesName] 101 | gm.vertices = [ 102 | (v[0] / gameObjectHandler.scale, v[1] / gameObjectHandler.scale) for v in vertices] 103 | gameObjectHandler.add(gm, color=color) 104 | else: 105 | gameObjectHandler.add(gm) 106 | -------------------------------------------------------------------------------- /AggiEngine/uimanager.py: -------------------------------------------------------------------------------- 1 | from PySide2.QtCore import QMetaObject 2 | from PySide2.QtUiTools import QUiLoader 3 | from PySide2.QtWidgets import QMainWindow 4 | 5 | 6 | class UiManager(QUiLoader): 7 | 8 | def __init__(self, window, customWidgets=None): 9 | QUiLoader.__init__(self) 10 | self.window = window 11 | 12 | # Register all the custom widgets so that they can be created later 13 | self.customWidgets = customWidgets 14 | if customWidgets is not None: 15 | for wid in customWidgets: 16 | self.registerCustomWidget(wid) 17 | 18 | self.keepWidgets = [] 19 | 20 | def createWidget(self, class_name, parent=None, name=''): 21 | """ 22 | Overrides QUiLoader to createWidget in current window rather than a new one 23 | ``class_name:`` The class we want to create 24 | ``parent:`` The parent widget 25 | ``name:`` The name of the widget we'll create 26 | """ 27 | 28 | if class_name is QMainWindow.__name__: 29 | return self.window 30 | 31 | if parent is None and self.window: 32 | return self.window 33 | else: 34 | if class_name in self.availableWidgets(): 35 | widget = QUiLoader.createWidget(self, class_name, parent, name) 36 | widget.show() 37 | else: 38 | try: 39 | widget = self.customWidgets[class_name](parent) 40 | except (TypeError, KeyError) as e: 41 | raise Exception(class_name, 'was not found are you sure it was promoted?') 42 | 43 | if self.window: 44 | setattr(self.window, name, widget) 45 | 46 | return widget 47 | 48 | def loadWidgets(self, ui_file, deleteCurrent=False): 49 | """ 50 | Loads the current ui_file and if wanted deleted old widgets 51 | ``ui_file:`` The file path to load 52 | ``deleteCurrent:`` Remove old widgets 53 | """ 54 | 55 | if len(self.window.children()) > 0 and deleteCurrent: 56 | for i in range(0, len(self.window.children())): 57 | if not(self.window.children()[i] in self.keepWidgets): 58 | self.window.children()[i].deleteLater() 59 | 60 | widgets = self.load(ui_file) # load widgets 61 | QMetaObject.connectSlotsByName(widgets) 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 bradylangdale 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://img.shields.io/github/actions/workflow/status/aggie-coding-club/AggiEngine/.github/workflows/main.yml?branch=master)](https://github.com/aggie-coding-club/AggiEngine/actions) 2 | [![AggiEngine on PyPI](https://img.shields.io/pypi/v/AggiEngine.svg?color=blue&style=for-the-badge)](https://pypi.org/project/AggiEngine) 3 | [![package downloads](https://img.shields.io/pypi/dm/AggiEngine.svg?color=skyblue&style=for-the-badge)](https://pypi.org/project/AggiEngine) 4 | 5 | # AggiEngine 6 | AggiEngine is a 2D game engine, designed for making game development easier. AggiEngine provides GUI, physics, state management and more... 7 | 8 | ## Documentation 9 | [Check out the docs here.](https://aggie-coding-club.github.io/AggiEngine/index.html) 10 | 11 | ## Easy Installation 12 | 13 | ```bash 14 | $ pip install AggiEngine 15 | ``` 16 | 17 | ## Manual Installation 18 | ```bash 19 | $ pip install PySide2 20 | $ pip install PyOpenGL 21 | $ pip install Box2D 22 | $ pip install pytmx 23 | $ pip install pillow 24 | $ pip install numpy 25 | $ pip install simpleaudio 26 | ``` 27 | 28 | ## Usage 29 | 30 | ```python 31 | import AggiEngine as ag 32 | 33 | 34 | class ExampleState(ag.State): 35 | 36 | def __init__(self, ui_path): 37 | ag.State.__init__(self, ui_path) 38 | 39 | def start(self): 40 | self.loadMap('example_map.tmx') 41 | 42 | def update(self): 43 | print("Updated!") 44 | 45 | state = ExampleState("example.ui") 46 | app = ag.Application(state) 47 | app.run() 48 | ``` 49 | 50 | ## Issues 51 | Feel free to report any [issues](https://github.com/aggie-coding-club/AggiEngine/issues) you may find. 52 | Also if there is a feature you would like to add feel free to make a pull request! 53 | -------------------------------------------------------------------------------- /docs/application.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.application API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.application

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
from typing import Optional
 30 | 
 31 | from PySide2.QtWidgets import QApplication
 32 | 
 33 | from . import State
 34 | from .mainwindow import MainWindow
 35 | 
 36 | 
 37 | class Application(QApplication):
 38 |     """
 39 |     This is the core class of an AggiEngine application.
 40 |     """
 41 | 
 42 |     def __init__(self, state: State, screenFps: Optional[float] = 120, fixedFps: Optional[float] = 60,
 43 |                  args: Optional[list] = None) -> None:
 44 |         """
 45 |         Creates and initializes QWidgets for the application to start.
 46 |         ``state:`` The initial state to launch the Application with    
 47 |         ``args:`` System arguments passed in    
 48 |         ``config:`` Set application parameters    
 49 |         """
 50 |         if args:
 51 |             super(Application, self).__init__(args)
 52 |         else:
 53 |             super(Application, self).__init__()
 54 | 
 55 |         self.window = MainWindow(self, state, screenFps, fixedFps)
 56 | 
 57 |     def run(self) -> None:
 58 |         """
 59 |         Execute the application, this will start the passed State
 60 |         """
 61 |         self.window.start()
 62 |         self.window.show()
 63 |         self.exec_()
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |

Classes

74 |
75 |
76 | class Application 77 | (state: State, screenFps: typing.Union[float, NoneType] = 120, fixedFps: typing.Union[float, NoneType] = 60, args: typing.Union[list, NoneType] = None) 78 |
79 |
80 |

This is the core class of an AggiEngine application.

81 |

Creates and initializes QWidgets for the application to start. 82 | state: The initial state to launch the Application with 83 |
84 | args: System arguments passed in 85 |
86 | config: Set application parameters

87 |
88 | 89 | Expand source code 90 | 91 |
class Application(QApplication):
 92 |     """
 93 |     This is the core class of an AggiEngine application.
 94 |     """
 95 | 
 96 |     def __init__(self, state: State, screenFps: Optional[float] = 120, fixedFps: Optional[float] = 60,
 97 |                  args: Optional[list] = None) -> None:
 98 |         """
 99 |         Creates and initializes QWidgets for the application to start.
100 |         ``state:`` The initial state to launch the Application with    
101 |         ``args:`` System arguments passed in    
102 |         ``config:`` Set application parameters    
103 |         """
104 |         if args:
105 |             super(Application, self).__init__(args)
106 |         else:
107 |             super(Application, self).__init__()
108 | 
109 |         self.window = MainWindow(self, state, screenFps, fixedFps)
110 | 
111 |     def run(self) -> None:
112 |         """
113 |         Execute the application, this will start the passed State
114 |         """
115 |         self.window.start()
116 |         self.window.show()
117 |         self.exec_()
118 |
119 |

Ancestors

120 |
    121 |
  • PySide2.QtWidgets.QApplication
  • 122 |
  • PySide2.QtGui.QGuiApplication
  • 123 |
  • PySide2.QtCore.QCoreApplication
  • 124 |
  • PySide2.QtCore.QObject
  • 125 |
  • Shiboken.Object
  • 126 |
127 |

Class variables

128 |
129 |
var staticMetaObject
130 |
131 |
132 |
133 |
134 |

Methods

135 |
136 |
137 | def run(self) ‑> NoneType 138 |
139 |
140 |

Execute the application, this will start the passed State

141 |
142 | 143 | Expand source code 144 | 145 |
def run(self) -> None:
146 |     """
147 |     Execute the application, this will start the passed State
148 |     """
149 |     self.window.start()
150 |     self.window.show()
151 |     self.exec_()
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | 183 |
184 | 187 | 188 | -------------------------------------------------------------------------------- /docs/contactlistener.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.contactlistener API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.contactlistener

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
from Box2D import b2ContactListener, b2Contact, b2Manifold, b2Vec2
 30 | 
 31 | 
 32 | class ContactListener(b2ContactListener):
 33 |     """
 34 |     Receives contact information from Box2D and passes it along to the GameObjects in the collision.
 35 |     """
 36 | 
 37 |     def __init__(self):
 38 |         b2ContactListener.__init__(self)
 39 | 
 40 |     def BeginContact(self, contact: b2Contact) -> None:
 41 |         """
 42 |         Called when two Box2D bodies collide and calls their beginContact(otherBody) methods
 43 |         ``contact:`` The contact event
 44 |         """
 45 |         bodyA = contact.fixtureA.body
 46 |         bodyB = contact.fixtureB.body
 47 |         bodyA.userData.beginContact(bodyB)
 48 |         bodyB.userData.beginContact(bodyA)
 49 | 
 50 |     def EndContact(self, contact: b2Contact) -> None:
 51 |         """
 52 |         Called when two Box2D bodies collide and calls their endContact(otherBody) methods
 53 |         ``contact:`` The contact event
 54 |         """
 55 |         bodyA = contact.fixtureA.body
 56 |         bodyB = contact.fixtureB.body
 57 |         bodyA.userData.endContact(bodyB)
 58 |         bodyB.userData.endContact(bodyA)
 59 | 
 60 |     def PreSolve(self, contact: b2Contact, manifold: b2Manifold) -> None:
 61 |         """
 62 |         Called before Box2D handles a contact while the bodies are still overlapping
 63 |         """
 64 |         bodyA = contact.fixtureA.body
 65 |         bodyB = contact.fixtureB.body
 66 |         bodyA.userData.preSolve(bodyB, manifold)
 67 |         bodyB.userData.preSolve(bodyA, manifold)
 68 | 
 69 |     def PostSolve(self, contact: b2Contact, impulse: b2Vec2) -> None:
 70 |         """
 71 |         Called after Box2D handles a contact while the bodies are no longer overlapping
 72 |         """
 73 |         bodyA = contact.fixtureA.body
 74 |         bodyB = contact.fixtureB.body
 75 |         bodyA.userData.postSolve(bodyB, impulse)
 76 |         bodyB.userData.postSolve(bodyA, impulse)
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |

Classes

87 |
88 |
89 | class ContactListener 90 |
91 |
92 |

Receives contact information from Box2D and passes it along to the GameObjects in the collision.

93 |
94 | 95 | Expand source code 96 | 97 |
class ContactListener(b2ContactListener):
 98 |     """
 99 |     Receives contact information from Box2D and passes it along to the GameObjects in the collision.
100 |     """
101 | 
102 |     def __init__(self):
103 |         b2ContactListener.__init__(self)
104 | 
105 |     def BeginContact(self, contact: b2Contact) -> None:
106 |         """
107 |         Called when two Box2D bodies collide and calls their beginContact(otherBody) methods
108 |         ``contact:`` The contact event
109 |         """
110 |         bodyA = contact.fixtureA.body
111 |         bodyB = contact.fixtureB.body
112 |         bodyA.userData.beginContact(bodyB)
113 |         bodyB.userData.beginContact(bodyA)
114 | 
115 |     def EndContact(self, contact: b2Contact) -> None:
116 |         """
117 |         Called when two Box2D bodies collide and calls their endContact(otherBody) methods
118 |         ``contact:`` The contact event
119 |         """
120 |         bodyA = contact.fixtureA.body
121 |         bodyB = contact.fixtureB.body
122 |         bodyA.userData.endContact(bodyB)
123 |         bodyB.userData.endContact(bodyA)
124 | 
125 |     def PreSolve(self, contact: b2Contact, manifold: b2Manifold) -> None:
126 |         """
127 |         Called before Box2D handles a contact while the bodies are still overlapping
128 |         """
129 |         bodyA = contact.fixtureA.body
130 |         bodyB = contact.fixtureB.body
131 |         bodyA.userData.preSolve(bodyB, manifold)
132 |         bodyB.userData.preSolve(bodyA, manifold)
133 | 
134 |     def PostSolve(self, contact: b2Contact, impulse: b2Vec2) -> None:
135 |         """
136 |         Called after Box2D handles a contact while the bodies are no longer overlapping
137 |         """
138 |         bodyA = contact.fixtureA.body
139 |         bodyB = contact.fixtureB.body
140 |         bodyA.userData.postSolve(bodyB, impulse)
141 |         bodyB.userData.postSolve(bodyA, impulse)
142 |
143 |

Ancestors

144 |
    145 |
  • Box2D.Box2D.b2ContactListener
  • 146 |
147 |

Methods

148 |
149 |
150 | def BeginContact(self, contact: Box2D.Box2D.b2Contact) ‑> NoneType 151 |
152 |
153 |

Called when two Box2D bodies collide and calls their beginContact(otherBody) methods 154 | contact: The contact event

155 |
156 | 157 | Expand source code 158 | 159 |
def BeginContact(self, contact: b2Contact) -> None:
160 |     """
161 |     Called when two Box2D bodies collide and calls their beginContact(otherBody) methods
162 |     ``contact:`` The contact event
163 |     """
164 |     bodyA = contact.fixtureA.body
165 |     bodyB = contact.fixtureB.body
166 |     bodyA.userData.beginContact(bodyB)
167 |     bodyB.userData.beginContact(bodyA)
168 |
169 |
170 |
171 | def EndContact(self, contact: Box2D.Box2D.b2Contact) ‑> NoneType 172 |
173 |
174 |

Called when two Box2D bodies collide and calls their endContact(otherBody) methods 175 | contact: The contact event

176 |
177 | 178 | Expand source code 179 | 180 |
def EndContact(self, contact: b2Contact) -> None:
181 |     """
182 |     Called when two Box2D bodies collide and calls their endContact(otherBody) methods
183 |     ``contact:`` The contact event
184 |     """
185 |     bodyA = contact.fixtureA.body
186 |     bodyB = contact.fixtureB.body
187 |     bodyA.userData.endContact(bodyB)
188 |     bodyB.userData.endContact(bodyA)
189 |
190 |
191 |
192 | def PostSolve(self, contact: Box2D.Box2D.b2Contact, impulse: Box2D.Box2D.b2Vec2) ‑> NoneType 193 |
194 |
195 |

Called after Box2D handles a contact while the bodies are no longer overlapping

196 |
197 | 198 | Expand source code 199 | 200 |
def PostSolve(self, contact: b2Contact, impulse: b2Vec2) -> None:
201 |     """
202 |     Called after Box2D handles a contact while the bodies are no longer overlapping
203 |     """
204 |     bodyA = contact.fixtureA.body
205 |     bodyB = contact.fixtureB.body
206 |     bodyA.userData.postSolve(bodyB, impulse)
207 |     bodyB.userData.postSolve(bodyA, impulse)
208 |
209 |
210 |
211 | def PreSolve(self, contact: Box2D.Box2D.b2Contact, manifold: Box2D.Box2D.b2Manifold) ‑> NoneType 212 |
213 |
214 |

Called before Box2D handles a contact while the bodies are still overlapping

215 |
216 | 217 | Expand source code 218 | 219 |
def PreSolve(self, contact: b2Contact, manifold: b2Manifold) -> None:
220 |     """
221 |     Called before Box2D handles a contact while the bodies are still overlapping
222 |     """
223 |     bodyA = contact.fixtureA.body
224 |     bodyB = contact.fixtureB.body
225 |     bodyA.userData.preSolve(bodyB, manifold)
226 |     bodyB.userData.preSolve(bodyA, manifold)
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | 260 |
261 | 264 | 265 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Package AggiEngine

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
from AggiEngine.gamescreen import GameScreen
 30 | from AggiEngine.state import State
 31 | from AggiEngine.statemanager import StateManager
 32 | from AggiEngine.uimanager import UiManager
 33 | from AggiEngine.gameobject import GameObject
 34 | from AggiEngine.mainwindow import MainWindow
 35 | from AggiEngine.particles import Particles
 36 | from AggiEngine.application import Application
 37 | from AggiEngine.sound import Sound
 38 | from AggiEngine.tileloader import TileMap
39 |
40 |
41 |
42 |

Sub-modules

43 |
44 |
AggiEngine.application
45 |
46 |
47 |
48 |
AggiEngine.contactlistener
49 |
50 |
51 |
52 |
AggiEngine.gameobject
53 |
54 |
55 |
56 |
AggiEngine.gameobjecthandler
57 |
58 |
59 |
60 |
AggiEngine.gamescreen
61 |
62 |
63 |
64 |
AggiEngine.mainwindow
65 |
66 |
67 |
68 |
AggiEngine.particles
69 |
70 |
71 |
72 |
AggiEngine.sound
73 |
74 |
75 |
76 |
AggiEngine.state
77 |
78 |
79 |
80 |
AggiEngine.statemanager
81 |
82 |
83 |
84 |
AggiEngine.tileloader
85 |
86 |
87 |
88 |
AggiEngine.uimanager
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | 125 |
126 | 129 | 130 | -------------------------------------------------------------------------------- /docs/mainwindow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.mainwindow API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.mainwindow

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
import time
 30 | from typing import Optional
 31 | 
 32 | import PySide2
 33 | from PySide2.QtCore import QTimer, Qt
 34 | from PySide2.QtWidgets import QMainWindow, QWidget
 35 | 
 36 | from .uimanager import UiManager
 37 | from .statemanager import StateManager
 38 | from .state import State
 39 | from .gamescreen import GameScreen
 40 | 
 41 | 
 42 | class MainWindow(QMainWindow):
 43 | 
 44 |     def __init__(self, app, state: State, screenFPS: int, fixedFPS: int, parent: Optional[QWidget] = None):
 45 |         '''
 46 |         The MainWindow is created by the application, here we handle ui/scenes and send out updates.
 47 |         ``state:`` The current state we want to start with.  
 48 |         ``parent:`` The widget this held under, None by default because this is top widget  
 49 |         '''
 50 |         QMainWindow.__init__(self, parent)
 51 |         self.app = app  # the application created
 52 |         self.gameScreen = None  # where all graphics are drawn
 53 |         self.uiManager = UiManager(self, customWidgets=[GameScreen])  # Loads widgets into window from a file
 54 |         self.stateManager = StateManager(self, state)  # Manages state updates and transitions
 55 | 
 56 |         self.updateFPSTimer = QTimer(self)  # Used to manage frame timings
 57 |         self.updateFPSTimer.timeout.connect(self.__calculateFPS)
 58 | 
 59 |         self.fixedFps = 0  # Calculated FPS
 60 |         self.screenFps = 0
 61 | 
 62 |         self.fixedFrames = 0  # Accumulated Frames
 63 |         self.screenFrames = 0
 64 | 
 65 |         self.targetFixedFPS = fixedFPS
 66 |         self.targetUpdateFPS = screenFPS
 67 |         self.fixedTiming = 1 / self.targetFixedFPS
 68 |         self.screenTiming = 1 / self.targetUpdateFPS
 69 |         self.fixedNeeded = fixedFPS
 70 |         self.screenNeeded = screenFPS
 71 |         self.lastTime = 0
 72 |         self.setMouseTracking(True)
 73 | 
 74 |         self.uiManager.keepWidgets = self.children()
 75 | 
 76 |     def start(self) -> None:
 77 |         '''
 78 |         Called when application.run() is executed. Starts the actual engine.
 79 |         '''
 80 |         self.stateManager.initializeState()  # start the state
 81 |         self.updateFPSTimer.start(100)
 82 | 
 83 |     def closeEvent(self, event: PySide2.QtGui.QCloseEvent) -> None:
 84 |         '''
 85 |         Called when the window is closed
 86 |         '''
 87 |         print("Window closed.")
 88 |         self.updateFPSTimer.stop()
 89 |         self.stateManager.exit()
 90 | 
 91 |     def __calculateFPS(self) -> None:
 92 |         '''
 93 |         Averages FPS and adjust frame timings to meet the targets
 94 |         '''
 95 | 
 96 |         self.fixedFps = self.fixedFrames / (time.perf_counter() - self.lastTime)
 97 |         self.fixedFrames = 0
 98 | 
 99 |         self.screenFps = self.screenFrames / (time.perf_counter() - self.lastTime)
100 |         self.screenFrames = 0
101 | 
102 |         if -0.5 < (self.targetFixedFPS - self.fixedFps) / self.targetFixedFPS < 0.5 and self.fixedNeeded > 30:
103 |             self.fixedNeeded += (self.targetFixedFPS - self.fixedFps) * 0.25
104 |             self.fixedTiming = 1 / self.fixedNeeded
105 | 
106 |         if -0.5 < (self.screenNeeded - self.screenFps) / self.targetUpdateFPS < 0.5 and self.screenNeeded > 30:
107 |             self.screenNeeded += (self.targetUpdateFPS - self.screenFps) * 0.15
108 |             self.screenTiming = 1 / self.screenNeeded
109 | 
110 |         self.lastTime = time.perf_counter()
111 | 
112 |     def resizeEvent(self, event: PySide2.QtGui.QResizeEvent) -> None:
113 |         '''
114 |         Triggered when the screen is resized and passes down the event.
115 |         '''
116 |         if self.gameScreen:
117 |             self.gameScreen.setGeometry(0, 0, self.width(), self.height())
118 | 
119 |     def keyPressEvent(self, event: PySide2.QtGui.QKeyEvent) -> None:
120 |         '''
121 |         Triggered when a key is pressed and passes down the event.
122 |         '''
123 |         self.stateManager.keyPressed(event)
124 | 
125 |     def keyReleaseEvent(self, event: PySide2.QtGui.QKeyEvent) -> None:
126 |         '''
127 |         Triggered when a key is released and passes down the event.
128 |         '''
129 |         self.stateManager.keyReleased(event)
130 | 
131 |     def mouseMoveEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
132 |         '''
133 |         Triggered when the mouse is moved and passes down the event.
134 |         '''
135 |         self.stateManager.mouseMoved(event)
136 | 
137 |     def mousePressEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
138 |         '''
139 |         Triggered when a mouse key is pressed and passes down the event.
140 |         '''
141 |         self.stateManager.mousePressed(event)
142 | 
143 |     def mouseReleaseEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
144 |         '''
145 |         Triggered when a mouse key is released and passes down the event.
146 |         '''
147 |         self.stateManager.mouseReleased(event)
148 | 
149 |     def waitForLoad(self) -> None:
150 |         '''
151 |         Makes state manager wait until the current state finishes loading widgets.
152 |         '''
153 |         QTimer(self).singleShot(0, self.stateManager.start)
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |

Classes

164 |
165 |
166 | class MainWindow 167 | (app, state: State, screenFPS: int, fixedFPS: int, parent: typing.Union[PySide2.QtWidgets.QWidget, NoneType] = None) 168 |
169 |
170 |

QMainWindow(self, parent: typing.Union[PySide2.QtWidgets.QWidget, NoneType] = None, flags: PySide2.QtCore.Qt.WindowFlags = Default(Qt.WindowFlags)) -> None

171 |

The MainWindow is created by the application, here we handle ui/scenes and send out updates. 172 | state: The current state we want to start with.
173 | parent: The widget this held under, None by default because this is top widget

174 |
175 | 176 | Expand source code 177 | 178 |
class MainWindow(QMainWindow):
179 | 
180 |     def __init__(self, app, state: State, screenFPS: int, fixedFPS: int, parent: Optional[QWidget] = None):
181 |         '''
182 |         The MainWindow is created by the application, here we handle ui/scenes and send out updates.
183 |         ``state:`` The current state we want to start with.  
184 |         ``parent:`` The widget this held under, None by default because this is top widget  
185 |         '''
186 |         QMainWindow.__init__(self, parent)
187 |         self.app = app  # the application created
188 |         self.gameScreen = None  # where all graphics are drawn
189 |         self.uiManager = UiManager(self, customWidgets=[GameScreen])  # Loads widgets into window from a file
190 |         self.stateManager = StateManager(self, state)  # Manages state updates and transitions
191 | 
192 |         self.updateFPSTimer = QTimer(self)  # Used to manage frame timings
193 |         self.updateFPSTimer.timeout.connect(self.__calculateFPS)
194 | 
195 |         self.fixedFps = 0  # Calculated FPS
196 |         self.screenFps = 0
197 | 
198 |         self.fixedFrames = 0  # Accumulated Frames
199 |         self.screenFrames = 0
200 | 
201 |         self.targetFixedFPS = fixedFPS
202 |         self.targetUpdateFPS = screenFPS
203 |         self.fixedTiming = 1 / self.targetFixedFPS
204 |         self.screenTiming = 1 / self.targetUpdateFPS
205 |         self.fixedNeeded = fixedFPS
206 |         self.screenNeeded = screenFPS
207 |         self.lastTime = 0
208 |         self.setMouseTracking(True)
209 | 
210 |         self.uiManager.keepWidgets = self.children()
211 | 
212 |     def start(self) -> None:
213 |         '''
214 |         Called when application.run() is executed. Starts the actual engine.
215 |         '''
216 |         self.stateManager.initializeState()  # start the state
217 |         self.updateFPSTimer.start(100)
218 | 
219 |     def closeEvent(self, event: PySide2.QtGui.QCloseEvent) -> None:
220 |         '''
221 |         Called when the window is closed
222 |         '''
223 |         print("Window closed.")
224 |         self.updateFPSTimer.stop()
225 |         self.stateManager.exit()
226 | 
227 |     def __calculateFPS(self) -> None:
228 |         '''
229 |         Averages FPS and adjust frame timings to meet the targets
230 |         '''
231 | 
232 |         self.fixedFps = self.fixedFrames / (time.perf_counter() - self.lastTime)
233 |         self.fixedFrames = 0
234 | 
235 |         self.screenFps = self.screenFrames / (time.perf_counter() - self.lastTime)
236 |         self.screenFrames = 0
237 | 
238 |         if -0.5 < (self.targetFixedFPS - self.fixedFps) / self.targetFixedFPS < 0.5 and self.fixedNeeded > 30:
239 |             self.fixedNeeded += (self.targetFixedFPS - self.fixedFps) * 0.25
240 |             self.fixedTiming = 1 / self.fixedNeeded
241 | 
242 |         if -0.5 < (self.screenNeeded - self.screenFps) / self.targetUpdateFPS < 0.5 and self.screenNeeded > 30:
243 |             self.screenNeeded += (self.targetUpdateFPS - self.screenFps) * 0.15
244 |             self.screenTiming = 1 / self.screenNeeded
245 | 
246 |         self.lastTime = time.perf_counter()
247 | 
248 |     def resizeEvent(self, event: PySide2.QtGui.QResizeEvent) -> None:
249 |         '''
250 |         Triggered when the screen is resized and passes down the event.
251 |         '''
252 |         if self.gameScreen:
253 |             self.gameScreen.setGeometry(0, 0, self.width(), self.height())
254 | 
255 |     def keyPressEvent(self, event: PySide2.QtGui.QKeyEvent) -> None:
256 |         '''
257 |         Triggered when a key is pressed and passes down the event.
258 |         '''
259 |         self.stateManager.keyPressed(event)
260 | 
261 |     def keyReleaseEvent(self, event: PySide2.QtGui.QKeyEvent) -> None:
262 |         '''
263 |         Triggered when a key is released and passes down the event.
264 |         '''
265 |         self.stateManager.keyReleased(event)
266 | 
267 |     def mouseMoveEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
268 |         '''
269 |         Triggered when the mouse is moved and passes down the event.
270 |         '''
271 |         self.stateManager.mouseMoved(event)
272 | 
273 |     def mousePressEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
274 |         '''
275 |         Triggered when a mouse key is pressed and passes down the event.
276 |         '''
277 |         self.stateManager.mousePressed(event)
278 | 
279 |     def mouseReleaseEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
280 |         '''
281 |         Triggered when a mouse key is released and passes down the event.
282 |         '''
283 |         self.stateManager.mouseReleased(event)
284 | 
285 |     def waitForLoad(self) -> None:
286 |         '''
287 |         Makes state manager wait until the current state finishes loading widgets.
288 |         '''
289 |         QTimer(self).singleShot(0, self.stateManager.start)
290 |
291 |

Ancestors

292 |
    293 |
  • PySide2.QtWidgets.QMainWindow
  • 294 |
  • PySide2.QtWidgets.QWidget
  • 295 |
  • PySide2.QtCore.QObject
  • 296 |
  • PySide2.QtGui.QPaintDevice
  • 297 |
  • Shiboken.Object
  • 298 |
299 |

Class variables

300 |
301 |
var staticMetaObject
302 |
303 |
304 |
305 |
306 |

Methods

307 |
308 |
309 | def closeEvent(self, event: PySide2.QtGui.QCloseEvent) ‑> NoneType 310 |
311 |
312 |

Called when the window is closed

313 |
314 | 315 | Expand source code 316 | 317 |
def closeEvent(self, event: PySide2.QtGui.QCloseEvent) -> None:
318 |     '''
319 |     Called when the window is closed
320 |     '''
321 |     print("Window closed.")
322 |     self.updateFPSTimer.stop()
323 |     self.stateManager.exit()
324 |
325 |
326 |
327 | def keyPressEvent(self, event: PySide2.QtGui.QKeyEvent) ‑> NoneType 328 |
329 |
330 |

Triggered when a key is pressed and passes down the event.

331 |
332 | 333 | Expand source code 334 | 335 |
def keyPressEvent(self, event: PySide2.QtGui.QKeyEvent) -> None:
336 |     '''
337 |     Triggered when a key is pressed and passes down the event.
338 |     '''
339 |     self.stateManager.keyPressed(event)
340 |
341 |
342 |
343 | def keyReleaseEvent(self, event: PySide2.QtGui.QKeyEvent) ‑> NoneType 344 |
345 |
346 |

Triggered when a key is released and passes down the event.

347 |
348 | 349 | Expand source code 350 | 351 |
def keyReleaseEvent(self, event: PySide2.QtGui.QKeyEvent) -> None:
352 |     '''
353 |     Triggered when a key is released and passes down the event.
354 |     '''
355 |     self.stateManager.keyReleased(event)
356 |
357 |
358 |
359 | def mouseMoveEvent(self, event: PySide2.QtGui.QMouseEvent) ‑> NoneType 360 |
361 |
362 |

Triggered when the mouse is moved and passes down the event.

363 |
364 | 365 | Expand source code 366 | 367 |
def mouseMoveEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
368 |     '''
369 |     Triggered when the mouse is moved and passes down the event.
370 |     '''
371 |     self.stateManager.mouseMoved(event)
372 |
373 |
374 |
375 | def mousePressEvent(self, event: PySide2.QtGui.QMouseEvent) ‑> NoneType 376 |
377 |
378 |

Triggered when a mouse key is pressed and passes down the event.

379 |
380 | 381 | Expand source code 382 | 383 |
def mousePressEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
384 |     '''
385 |     Triggered when a mouse key is pressed and passes down the event.
386 |     '''
387 |     self.stateManager.mousePressed(event)
388 |
389 |
390 |
391 | def mouseReleaseEvent(self, event: PySide2.QtGui.QMouseEvent) ‑> NoneType 392 |
393 |
394 |

Triggered when a mouse key is released and passes down the event.

395 |
396 | 397 | Expand source code 398 | 399 |
def mouseReleaseEvent(self, event: PySide2.QtGui.QMouseEvent) -> None:
400 |     '''
401 |     Triggered when a mouse key is released and passes down the event.
402 |     '''
403 |     self.stateManager.mouseReleased(event)
404 |
405 |
406 |
407 | def resizeEvent(self, event: PySide2.QtGui.QResizeEvent) ‑> NoneType 408 |
409 |
410 |

Triggered when the screen is resized and passes down the event.

411 |
412 | 413 | Expand source code 414 | 415 |
def resizeEvent(self, event: PySide2.QtGui.QResizeEvent) -> None:
416 |     '''
417 |     Triggered when the screen is resized and passes down the event.
418 |     '''
419 |     if self.gameScreen:
420 |         self.gameScreen.setGeometry(0, 0, self.width(), self.height())
421 |
422 |
423 |
424 | def start(self) ‑> NoneType 425 |
426 |
427 |

Called when application.run() is executed. Starts the actual engine.

428 |
429 | 430 | Expand source code 431 | 432 |
def start(self) -> None:
433 |     '''
434 |     Called when application.run() is executed. Starts the actual engine.
435 |     '''
436 |     self.stateManager.initializeState()  # start the state
437 |     self.updateFPSTimer.start(100)
438 |
439 |
440 |
441 | def waitForLoad(self) ‑> NoneType 442 |
443 |
444 |

Makes state manager wait until the current state finishes loading widgets.

445 |
446 | 447 | Expand source code 448 | 449 |
def waitForLoad(self) -> None:
450 |     '''
451 |     Makes state manager wait until the current state finishes loading widgets.
452 |     '''
453 |     QTimer(self).singleShot(0, self.stateManager.start)
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 | 493 |
494 | 497 | 498 | -------------------------------------------------------------------------------- /docs/particles.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.particles API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.particles

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
from typing import Optional
 30 | 
 31 | import numpy as np
 32 | 
 33 | from AggiEngine import GameScreen
 34 | 
 35 | 
 36 | class Particles:
 37 | 
 38 |     def __init__(self, gameScreen: GameScreen, startColor: Optional[list] = None,
 39 |                  endColor: Optional[list] = None, shape: Optional[list] = None, rate=0.5,
 40 |                  count=100, endSize=0.01, sizeDecrease=0.95, colorFade=0.05):
 41 |         if not endColor:
 42 |             self.endColor = [1, 1, 1, 1]
 43 |         else:
 44 |             self.endColor = endColor
 45 | 
 46 |         if not startColor:
 47 |             self.startColor = [1, 1, 1, 1]
 48 |         else:
 49 |             self.startColor = startColor
 50 | 
 51 |         if not shape:
 52 |             self.shape = [[0, 0], [0.05, 0], [0.05, -0.05], [0, -0.05]]
 53 |         else:
 54 |             self.shape = shape
 55 | 
 56 |         self.gameScreen = gameScreen
 57 |         self.particles = []
 58 |         self.rate = rate
 59 |         self.count = count
 60 |         self.endSize = endSize
 61 |         self.sizeDecrease = sizeDecrease
 62 |         self.colorFade = colorFade
 63 |         self.time = 0
 64 | 
 65 |     def update(self) -> None:
 66 |         '''
 67 |         Updates the particles, color, size and  deletes old particles.
 68 |         '''
 69 |         toRemove = []
 70 |         for particle in self.particles:
 71 |             if abs(particle[1][0][0] - particle[1][1][0]) < self.endSize:
 72 |                 toRemove.append(particle)
 73 | 
 74 |             shape = []
 75 |             for vert in particle[1]:
 76 |                 shape.append([vert[0] * self.sizeDecrease, vert[1] * self.sizeDecrease])
 77 |             particle[1] = shape
 78 |             particle[2] = self.getColor(particle[-1])
 79 |             particle[-1] -= self.colorFade
 80 |             self.gameScreen.renderInfoList.append(particle)
 81 | 
 82 |         for particle in toRemove:
 83 |             self.particles.remove(particle)
 84 | 
 85 |         self.time += self.rate
 86 | 
 87 |     def emit(self, position: list) -> None:
 88 |         '''
 89 |         Appends are new particle to the particle list.
 90 |         '''
 91 |         
 92 |         if self.time > 1:
 93 |             if len(self.particles) < self.count:
 94 |                 self.particles.append([-1, self.shape, self.getColor(0), position, 0, 0])
 95 |             self.time = 0
 96 | 
 97 |     def getColor(self, amount: float) -> list:
 98 |         '''
 99 |         Interpolates the start and end color values and returns the value.
100 |         '''
101 |         
102 |         r = abs(self.startColor[0] - self.endColor[0]) * amount + self.startColor[0]
103 |         g = abs(self.startColor[1] - self.endColor[1]) * amount + self.startColor[1]
104 |         b = abs(self.startColor[2] - self.endColor[2]) * amount + self.startColor[2]
105 |         a = abs(self.startColor[3] - self.endColor[3]) * amount + self.startColor[3]
106 |         
107 |         return [max(r, self.endColor[0]), max(g, self.endColor[1]), max(b, self.endColor[2]), max(a, self.endColor[3])]
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |

Classes

118 |
119 |
120 | class Particles 121 | (gameScreen: GameScreen, startColor: typing.Union[list, NoneType] = None, endColor: typing.Union[list, NoneType] = None, shape: typing.Union[list, NoneType] = None, rate=0.5, count=100, endSize=0.01, sizeDecrease=0.95, colorFade=0.05) 122 |
123 |
124 |
125 |
126 | 127 | Expand source code 128 | 129 |
class Particles:
130 | 
131 |     def __init__(self, gameScreen: GameScreen, startColor: Optional[list] = None,
132 |                  endColor: Optional[list] = None, shape: Optional[list] = None, rate=0.5,
133 |                  count=100, endSize=0.01, sizeDecrease=0.95, colorFade=0.05):
134 |         if not endColor:
135 |             self.endColor = [1, 1, 1, 1]
136 |         else:
137 |             self.endColor = endColor
138 | 
139 |         if not startColor:
140 |             self.startColor = [1, 1, 1, 1]
141 |         else:
142 |             self.startColor = startColor
143 | 
144 |         if not shape:
145 |             self.shape = [[0, 0], [0.05, 0], [0.05, -0.05], [0, -0.05]]
146 |         else:
147 |             self.shape = shape
148 | 
149 |         self.gameScreen = gameScreen
150 |         self.particles = []
151 |         self.rate = rate
152 |         self.count = count
153 |         self.endSize = endSize
154 |         self.sizeDecrease = sizeDecrease
155 |         self.colorFade = colorFade
156 |         self.time = 0
157 | 
158 |     def update(self) -> None:
159 |         '''
160 |         Updates the particles, color, size and  deletes old particles.
161 |         '''
162 |         toRemove = []
163 |         for particle in self.particles:
164 |             if abs(particle[1][0][0] - particle[1][1][0]) < self.endSize:
165 |                 toRemove.append(particle)
166 | 
167 |             shape = []
168 |             for vert in particle[1]:
169 |                 shape.append([vert[0] * self.sizeDecrease, vert[1] * self.sizeDecrease])
170 |             particle[1] = shape
171 |             particle[2] = self.getColor(particle[-1])
172 |             particle[-1] -= self.colorFade
173 |             self.gameScreen.renderInfoList.append(particle)
174 | 
175 |         for particle in toRemove:
176 |             self.particles.remove(particle)
177 | 
178 |         self.time += self.rate
179 | 
180 |     def emit(self, position: list) -> None:
181 |         '''
182 |         Appends are new particle to the particle list.
183 |         '''
184 |         
185 |         if self.time > 1:
186 |             if len(self.particles) < self.count:
187 |                 self.particles.append([-1, self.shape, self.getColor(0), position, 0, 0])
188 |             self.time = 0
189 | 
190 |     def getColor(self, amount: float) -> list:
191 |         '''
192 |         Interpolates the start and end color values and returns the value.
193 |         '''
194 |         
195 |         r = abs(self.startColor[0] - self.endColor[0]) * amount + self.startColor[0]
196 |         g = abs(self.startColor[1] - self.endColor[1]) * amount + self.startColor[1]
197 |         b = abs(self.startColor[2] - self.endColor[2]) * amount + self.startColor[2]
198 |         a = abs(self.startColor[3] - self.endColor[3]) * amount + self.startColor[3]
199 |         
200 |         return [max(r, self.endColor[0]), max(g, self.endColor[1]), max(b, self.endColor[2]), max(a, self.endColor[3])]
201 |
202 |

Methods

203 |
204 |
205 | def emit(self, position: list) ‑> NoneType 206 |
207 |
208 |

Appends are new particle to the particle list.

209 |
210 | 211 | Expand source code 212 | 213 |
def emit(self, position: list) -> None:
214 |     '''
215 |     Appends are new particle to the particle list.
216 |     '''
217 |     
218 |     if self.time > 1:
219 |         if len(self.particles) < self.count:
220 |             self.particles.append([-1, self.shape, self.getColor(0), position, 0, 0])
221 |         self.time = 0
222 |
223 |
224 |
225 | def getColor(self, amount: float) ‑> list 226 |
227 |
228 |

Interpolates the start and end color values and returns the value.

229 |
230 | 231 | Expand source code 232 | 233 |
def getColor(self, amount: float) -> list:
234 |     '''
235 |     Interpolates the start and end color values and returns the value.
236 |     '''
237 |     
238 |     r = abs(self.startColor[0] - self.endColor[0]) * amount + self.startColor[0]
239 |     g = abs(self.startColor[1] - self.endColor[1]) * amount + self.startColor[1]
240 |     b = abs(self.startColor[2] - self.endColor[2]) * amount + self.startColor[2]
241 |     a = abs(self.startColor[3] - self.endColor[3]) * amount + self.startColor[3]
242 |     
243 |     return [max(r, self.endColor[0]), max(g, self.endColor[1]), max(b, self.endColor[2]), max(a, self.endColor[3])]
244 |
245 |
246 |
247 | def update(self) ‑> NoneType 248 |
249 |
250 |

Updates the particles, color, size and 251 | deletes old particles.

252 |
253 | 254 | Expand source code 255 | 256 |
def update(self) -> None:
257 |     '''
258 |     Updates the particles, color, size and  deletes old particles.
259 |     '''
260 |     toRemove = []
261 |     for particle in self.particles:
262 |         if abs(particle[1][0][0] - particle[1][1][0]) < self.endSize:
263 |             toRemove.append(particle)
264 | 
265 |         shape = []
266 |         for vert in particle[1]:
267 |             shape.append([vert[0] * self.sizeDecrease, vert[1] * self.sizeDecrease])
268 |         particle[1] = shape
269 |         particle[2] = self.getColor(particle[-1])
270 |         particle[-1] -= self.colorFade
271 |         self.gameScreen.renderInfoList.append(particle)
272 | 
273 |     for particle in toRemove:
274 |         self.particles.remove(particle)
275 | 
276 |     self.time += self.rate
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 | 309 |
310 | 313 | 314 | -------------------------------------------------------------------------------- /docs/sound.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.sound API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.sound

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
import simpleaudio as sa
 30 | from typing import Any
 31 | 
 32 | 
 33 | class Sound:
 34 | 
 35 |     def __init__(self) -> None:
 36 |         self.sounds = dict()
 37 |     
 38 |     def load(self, key: Any, filestr: str) -> None:
 39 |         """
 40 |         loads a wav file for future playback  
 41 |         ``key:`` A key you will use to access the audio playback  
 42 |         ``filestr:`` The file path for a wav file to play  
 43 |         """
 44 |         self.sounds[key] = sa.WaveObject.from_wave_file(filestr)
 45 |     
 46 |     def play(self, key: Any) -> sa.PlayObject:
 47 |         """
 48 |         Plays your sound
 49 |         ``key:`` The key used when loading the sound  
 50 |         """
 51 |         return self.sounds[key].play()
 52 |     
 53 |     def stop_all(self) -> None:
 54 |         """
 55 |         Stops all sounds from playing
 56 |         """
 57 |         sa.stop_all()
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |

Classes

68 |
69 |
70 | class Sound 71 |
72 |
73 |
74 |
75 | 76 | Expand source code 77 | 78 |
class Sound:
 79 | 
 80 |     def __init__(self) -> None:
 81 |         self.sounds = dict()
 82 |     
 83 |     def load(self, key: Any, filestr: str) -> None:
 84 |         """
 85 |         loads a wav file for future playback  
 86 |         ``key:`` A key you will use to access the audio playback  
 87 |         ``filestr:`` The file path for a wav file to play  
 88 |         """
 89 |         self.sounds[key] = sa.WaveObject.from_wave_file(filestr)
 90 |     
 91 |     def play(self, key: Any) -> sa.PlayObject:
 92 |         """
 93 |         Plays your sound
 94 |         ``key:`` The key used when loading the sound  
 95 |         """
 96 |         return self.sounds[key].play()
 97 |     
 98 |     def stop_all(self) -> None:
 99 |         """
100 |         Stops all sounds from playing
101 |         """
102 |         sa.stop_all()
103 |
104 |

Methods

105 |
106 |
107 | def load(self, key: typing.Any, filestr: str) ‑> NoneType 108 |
109 |
110 |

loads a wav file for future playback
111 | key: A key you will use to access the audio playback
112 | filestr: The file path for a wav file to play

113 |
114 | 115 | Expand source code 116 | 117 |
def load(self, key: Any, filestr: str) -> None:
118 |     """
119 |     loads a wav file for future playback  
120 |     ``key:`` A key you will use to access the audio playback  
121 |     ``filestr:`` The file path for a wav file to play  
122 |     """
123 |     self.sounds[key] = sa.WaveObject.from_wave_file(filestr)
124 |
125 |
126 |
127 | def play(self, key: typing.Any) ‑> simpleaudio.shiny.PlayObject 128 |
129 |
130 |

Plays your sound 131 | key: The key used when loading the sound

132 |
133 | 134 | Expand source code 135 | 136 |
def play(self, key: Any) -> sa.PlayObject:
137 |     """
138 |     Plays your sound
139 |     ``key:`` The key used when loading the sound  
140 |     """
141 |     return self.sounds[key].play()
142 |
143 |
144 |
145 | def stop_all(self) ‑> NoneType 146 |
147 |
148 |

Stops all sounds from playing

149 |
150 | 151 | Expand source code 152 | 153 |
def stop_all(self) -> None:
154 |     """
155 |     Stops all sounds from playing
156 |     """
157 |     sa.stop_all()
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | 190 |
191 | 194 | 195 | -------------------------------------------------------------------------------- /docs/state.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.state API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.state

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
from typing import Optional
 30 | 
 31 | import PySide2
 32 | from .gameobjecthandler import GameObjectHandler
 33 | from .tileloader import TileMap
 34 | 
 35 | 
 36 | class State:
 37 | 
 38 |     def __init__(self, ui_path: Optional[str] = None, window: Optional[object] = None):
 39 |         '''
 40 |         Initialized class variables.
 41 |         ``ui_path:`` The path to the .ui file for the State to display
 42 |         ``window:`` The main window class for the whole application
 43 |         '''
 44 |         self.ui_path = ui_path
 45 |         self.window = window
 46 |         self.gameObjectHandler = None
 47 |         self.active = True
 48 | 
 49 |     def loadMap(self, filePath: str) -> None:
 50 |         '''
 51 |         Loads the TMX file and creates the contained game objects
 52 |         ``filePath:`` The path to the .tmx file to load game objects
 53 |         '''
 54 |         TileMap(filePath, self.gameObjectHandler, self.window.gameScreen)
 55 | 
 56 |     def loadUi(self) -> None:
 57 |         '''
 58 |         Loads the widgets in the .ui file
 59 |         '''
 60 |         if self.ui_path is not None:
 61 |             self.window.uiManager.loadWidgets(self.ui_path, True)
 62 | 
 63 |     def startGOH(self) -> None:
 64 |         '''
 65 |         Starts the game object handler
 66 |         '''
 67 |         self.gameObjectHandler = GameObjectHandler(self.window)
 68 | 
 69 |     def updateGOH(self) -> None:
 70 |         '''
 71 |         Updates the game object handler after a screen draw
 72 |         '''
 73 |         self.gameObjectHandler.update()
 74 | 
 75 |     def fixedUpdateGOH(self) -> None:
 76 |         '''
 77 |         Updates the game object handler after a physics update
 78 |         '''
 79 |         self.gameObjectHandler.fixedUpdate()
 80 | 
 81 |     def exitGOH(self) -> None:
 82 |         '''
 83 |         Stops the game object handler
 84 |         '''
 85 |         self.gameObjectHandler.exit()
 86 | 
 87 |     def start(self) -> None:
 88 |         '''
 89 |         Triggered when the State is first activated
 90 |         '''
 91 |         pass
 92 | 
 93 |     def update(self) -> None:
 94 |         '''
 95 |         Triggered after a screen draw
 96 |         '''
 97 |         pass
 98 | 
 99 |     def fixedUpdate(self) -> None:
100 |         '''
101 |         Triggered after a physics update
102 |         '''
103 |         pass
104 | 
105 |     def exit(self) -> None:
106 |         '''
107 |         Triggered when the State stops
108 |         '''
109 |         pass
110 | 
111 |     def keyPressed(self, event: PySide2.QtGui.QKeyEvent) -> None:
112 |         '''
113 |         Triggered when a key is pressed down
114 |         ``event:`` The key event contains the key(s) pressed
115 |         '''
116 |         pass
117 | 
118 |     def keyReleased(self, event: PySide2.QtGui.QKeyEvent) -> None:
119 |         '''
120 |         Triggered when a key is released
121 |         ``event:`` The key event contains the key(s) released
122 |         '''
123 |         pass
124 | 
125 |     def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) -> None:
126 |         '''
127 |         Triggered when the mouse is moved
128 |         ``event:`` The mouse event contains the new mouse positions
129 |         '''
130 |         pass
131 | 
132 |     def mousePressed(self, event: PySide2.QtGui.QMouseEvent) -> None:
133 |         '''
134 |         Triggered when mouse buttons are pressed
135 |         ``event:`` The mouse event contains the mouse buttons pressed
136 |         '''
137 |         pass
138 | 
139 |     def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) -> None:
140 |         '''
141 |         Triggered when mouse buttons are released
142 |         ``event:`` The mouse event contains the mouse buttons released
143 |         '''
144 |         pass
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |

Classes

155 |
156 |
157 | class State 158 | (ui_path: typing.Union[str, NoneType] = None, window: typing.Union[object, NoneType] = None) 159 |
160 |
161 |

Initialized class variables. 162 | ui_path: The path to the .ui file for the State to display 163 | window: The main window class for the whole application

164 |
165 | 166 | Expand source code 167 | 168 |
class State:
169 | 
170 |     def __init__(self, ui_path: Optional[str] = None, window: Optional[object] = None):
171 |         '''
172 |         Initialized class variables.
173 |         ``ui_path:`` The path to the .ui file for the State to display
174 |         ``window:`` The main window class for the whole application
175 |         '''
176 |         self.ui_path = ui_path
177 |         self.window = window
178 |         self.gameObjectHandler = None
179 |         self.active = True
180 | 
181 |     def loadMap(self, filePath: str) -> None:
182 |         '''
183 |         Loads the TMX file and creates the contained game objects
184 |         ``filePath:`` The path to the .tmx file to load game objects
185 |         '''
186 |         TileMap(filePath, self.gameObjectHandler, self.window.gameScreen)
187 | 
188 |     def loadUi(self) -> None:
189 |         '''
190 |         Loads the widgets in the .ui file
191 |         '''
192 |         if self.ui_path is not None:
193 |             self.window.uiManager.loadWidgets(self.ui_path, True)
194 | 
195 |     def startGOH(self) -> None:
196 |         '''
197 |         Starts the game object handler
198 |         '''
199 |         self.gameObjectHandler = GameObjectHandler(self.window)
200 | 
201 |     def updateGOH(self) -> None:
202 |         '''
203 |         Updates the game object handler after a screen draw
204 |         '''
205 |         self.gameObjectHandler.update()
206 | 
207 |     def fixedUpdateGOH(self) -> None:
208 |         '''
209 |         Updates the game object handler after a physics update
210 |         '''
211 |         self.gameObjectHandler.fixedUpdate()
212 | 
213 |     def exitGOH(self) -> None:
214 |         '''
215 |         Stops the game object handler
216 |         '''
217 |         self.gameObjectHandler.exit()
218 | 
219 |     def start(self) -> None:
220 |         '''
221 |         Triggered when the State is first activated
222 |         '''
223 |         pass
224 | 
225 |     def update(self) -> None:
226 |         '''
227 |         Triggered after a screen draw
228 |         '''
229 |         pass
230 | 
231 |     def fixedUpdate(self) -> None:
232 |         '''
233 |         Triggered after a physics update
234 |         '''
235 |         pass
236 | 
237 |     def exit(self) -> None:
238 |         '''
239 |         Triggered when the State stops
240 |         '''
241 |         pass
242 | 
243 |     def keyPressed(self, event: PySide2.QtGui.QKeyEvent) -> None:
244 |         '''
245 |         Triggered when a key is pressed down
246 |         ``event:`` The key event contains the key(s) pressed
247 |         '''
248 |         pass
249 | 
250 |     def keyReleased(self, event: PySide2.QtGui.QKeyEvent) -> None:
251 |         '''
252 |         Triggered when a key is released
253 |         ``event:`` The key event contains the key(s) released
254 |         '''
255 |         pass
256 | 
257 |     def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) -> None:
258 |         '''
259 |         Triggered when the mouse is moved
260 |         ``event:`` The mouse event contains the new mouse positions
261 |         '''
262 |         pass
263 | 
264 |     def mousePressed(self, event: PySide2.QtGui.QMouseEvent) -> None:
265 |         '''
266 |         Triggered when mouse buttons are pressed
267 |         ``event:`` The mouse event contains the mouse buttons pressed
268 |         '''
269 |         pass
270 | 
271 |     def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) -> None:
272 |         '''
273 |         Triggered when mouse buttons are released
274 |         ``event:`` The mouse event contains the mouse buttons released
275 |         '''
276 |         pass
277 |
278 |

Methods

279 |
280 |
281 | def exit(self) ‑> NoneType 282 |
283 |
284 |

Triggered when the State stops

285 |
286 | 287 | Expand source code 288 | 289 |
def exit(self) -> None:
290 |     '''
291 |     Triggered when the State stops
292 |     '''
293 |     pass
294 |
295 |
296 |
297 | def exitGOH(self) ‑> NoneType 298 |
299 |
300 |

Stops the game object handler

301 |
302 | 303 | Expand source code 304 | 305 |
def exitGOH(self) -> None:
306 |     '''
307 |     Stops the game object handler
308 |     '''
309 |     self.gameObjectHandler.exit()
310 |
311 |
312 |
313 | def fixedUpdate(self) ‑> NoneType 314 |
315 |
316 |

Triggered after a physics update

317 |
318 | 319 | Expand source code 320 | 321 |
def fixedUpdate(self) -> None:
322 |     '''
323 |     Triggered after a physics update
324 |     '''
325 |     pass
326 |
327 |
328 |
329 | def fixedUpdateGOH(self) ‑> NoneType 330 |
331 |
332 |

Updates the game object handler after a physics update

333 |
334 | 335 | Expand source code 336 | 337 |
def fixedUpdateGOH(self) -> None:
338 |     '''
339 |     Updates the game object handler after a physics update
340 |     '''
341 |     self.gameObjectHandler.fixedUpdate()
342 |
343 |
344 |
345 | def keyPressed(self, event: PySide2.QtGui.QKeyEvent) ‑> NoneType 346 |
347 |
348 |

Triggered when a key is pressed down 349 | event: The key event contains the key(s) pressed

350 |
351 | 352 | Expand source code 353 | 354 |
def keyPressed(self, event: PySide2.QtGui.QKeyEvent) -> None:
355 |     '''
356 |     Triggered when a key is pressed down
357 |     ``event:`` The key event contains the key(s) pressed
358 |     '''
359 |     pass
360 |
361 |
362 |
363 | def keyReleased(self, event: PySide2.QtGui.QKeyEvent) ‑> NoneType 364 |
365 |
366 |

Triggered when a key is released 367 | event: The key event contains the key(s) released

368 |
369 | 370 | Expand source code 371 | 372 |
def keyReleased(self, event: PySide2.QtGui.QKeyEvent) -> None:
373 |     '''
374 |     Triggered when a key is released
375 |     ``event:`` The key event contains the key(s) released
376 |     '''
377 |     pass
378 |
379 |
380 |
381 | def loadMap(self, filePath: str) ‑> NoneType 382 |
383 |
384 |

Loads the TMX file and creates the contained game objects 385 | filePath: The path to the .tmx file to load game objects

386 |
387 | 388 | Expand source code 389 | 390 |
def loadMap(self, filePath: str) -> None:
391 |     '''
392 |     Loads the TMX file and creates the contained game objects
393 |     ``filePath:`` The path to the .tmx file to load game objects
394 |     '''
395 |     TileMap(filePath, self.gameObjectHandler, self.window.gameScreen)
396 |
397 |
398 |
399 | def loadUi(self) ‑> NoneType 400 |
401 |
402 |

Loads the widgets in the .ui file

403 |
404 | 405 | Expand source code 406 | 407 |
def loadUi(self) -> None:
408 |     '''
409 |     Loads the widgets in the .ui file
410 |     '''
411 |     if self.ui_path is not None:
412 |         self.window.uiManager.loadWidgets(self.ui_path, True)
413 |
414 |
415 |
416 | def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) ‑> NoneType 417 |
418 |
419 |

Triggered when the mouse is moved 420 | event: The mouse event contains the new mouse positions

421 |
422 | 423 | Expand source code 424 | 425 |
def mouseMoved(self, event: PySide2.QtGui.QMouseEvent) -> None:
426 |     '''
427 |     Triggered when the mouse is moved
428 |     ``event:`` The mouse event contains the new mouse positions
429 |     '''
430 |     pass
431 |
432 |
433 |
434 | def mousePressed(self, event: PySide2.QtGui.QMouseEvent) ‑> NoneType 435 |
436 |
437 |

Triggered when mouse buttons are pressed 438 | event: The mouse event contains the mouse buttons pressed

439 |
440 | 441 | Expand source code 442 | 443 |
def mousePressed(self, event: PySide2.QtGui.QMouseEvent) -> None:
444 |     '''
445 |     Triggered when mouse buttons are pressed
446 |     ``event:`` The mouse event contains the mouse buttons pressed
447 |     '''
448 |     pass
449 |
450 |
451 |
452 | def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) ‑> NoneType 453 |
454 |
455 |

Triggered when mouse buttons are released 456 | event: The mouse event contains the mouse buttons released

457 |
458 | 459 | Expand source code 460 | 461 |
def mouseReleased(self, event: PySide2.QtGui.QMouseEvent) -> None:
462 |     '''
463 |     Triggered when mouse buttons are released
464 |     ``event:`` The mouse event contains the mouse buttons released
465 |     '''
466 |     pass
467 |
468 |
469 |
470 | def start(self) ‑> NoneType 471 |
472 |
473 |

Triggered when the State is first activated

474 |
475 | 476 | Expand source code 477 | 478 |
def start(self) -> None:
479 |     '''
480 |     Triggered when the State is first activated
481 |     '''
482 |     pass
483 |
484 |
485 |
486 | def startGOH(self) ‑> NoneType 487 |
488 |
489 |

Starts the game object handler

490 |
491 | 492 | Expand source code 493 | 494 |
def startGOH(self) -> None:
495 |     '''
496 |     Starts the game object handler
497 |     '''
498 |     self.gameObjectHandler = GameObjectHandler(self.window)
499 |
500 |
501 |
502 | def update(self) ‑> NoneType 503 |
504 |
505 |

Triggered after a screen draw

506 |
507 | 508 | Expand source code 509 | 510 |
def update(self) -> None:
511 |     '''
512 |     Triggered after a screen draw
513 |     '''
514 |     pass
515 |
516 |
517 |
518 | def updateGOH(self) ‑> NoneType 519 |
520 |
521 |

Updates the game object handler after a screen draw

522 |
523 | 524 | Expand source code 525 | 526 |
def updateGOH(self) -> None:
527 |     '''
528 |     Updates the game object handler after a screen draw
529 |     '''
530 |     self.gameObjectHandler.update()
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 | 575 |
576 | 579 | 580 | -------------------------------------------------------------------------------- /docs/tileloader.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.tileloader API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.tileloader

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
import pytmx
 30 | import Box2D
 31 | import importlib
 32 | from PIL import ImageColor
 33 | from .gameobject import GameObject
 34 | 
 35 | 
 36 | class TileMap:
 37 | 
 38 |     def __init__(self, tmxFile, gameObjectHandler, gameScreen):
 39 |         tiled_map = pytmx.TiledMap(tmxFile, image_loader=gameScreen.image_loader)
 40 |         if tiled_map.background_color:
 41 |             color = ImageColor.getcolor(tiled_map.background_color, 'RGB')
 42 |             gameScreen.bgColor = [color[0] / 255, color[1] / 255, color[2] / 255]
 43 | 
 44 |         hitboxes = {}
 45 |         objects = tiled_map.objects
 46 | 
 47 |         # Looks for objects and gets its vertices
 48 |         for obj in objects:
 49 |             pairPoints = []
 50 |             for pair in obj.points:
 51 |                 pairPoints.append(
 52 |                     (-(pair[0] - obj.x) / gameObjectHandler.scale, -(pair[1] - obj.y) / gameObjectHandler.scale))
 53 |             hitboxes[obj.name] = pairPoints
 54 | 
 55 |         for layer in tiled_map:
 56 |             # Checks for layers with tiles
 57 |             if isinstance(layer, pytmx.TiledTileLayer):
 58 |                 for x, y, image in layer.tiles():
 59 |                     # Gets class name and gets class name definition
 60 |                     className = layer.properties.get('class', 'GameObject')
 61 | 
 62 |                     if className != 'GameObject':
 63 |                         className = getattr(importlib.import_module('__main__'), className)
 64 |                     else:
 65 |                         className = GameObject
 66 |                     # Sets up Texture
 67 |                     gm = className()
 68 |                     gm.textureID = image[0]
 69 | 
 70 |                     gm.setWidth(image[1] / gameObjectHandler.scale ** 2)
 71 |                     gm.setHeight(image[2] / gameObjectHandler.scale ** 2)
 72 | 
 73 |                     # Sets up Color
 74 |                     color = layer.properties.get('color', None)
 75 |                     if color:
 76 |                         color = ImageColor.getcolor(color, 'RGBA')
 77 |                         color = [color[2] / 255, color[1] / 255, color[0] / 255, color[3] / 255]
 78 |                         print(color)
 79 | 
 80 |                     # Sets up Hitbox
 81 |                     verticesName = layer.properties.get('hitboxName', None)
 82 | 
 83 |                     # Sets up Body Type
 84 |                     bodyType = layer.properties.get('bodyType', 'none')
 85 |                     if bodyType != 'none':
 86 |                         bodyDef = Box2D.b2BodyDef()
 87 |                         if bodyType == 'dynamic':
 88 |                             bodyDef.type = Box2D.b2_dynamicBody
 89 |                         elif bodyType == 'static':
 90 |                             bodyDef.type = Box2D.b2_staticBody
 91 |                         else:
 92 |                             bodyDef.type = Box2D.b2_kinematicBody
 93 |                         bodyDef.linearDamping = layer.properties.get('linearDamping', 0)
 94 |                         bodyDef.angularDamping = layer.properties.get('angularDamping', 0)
 95 |                         bodyDef.fixedRotation = layer.properties.get('fixedRotation', False)
 96 | 
 97 |                         # Linear Vel (Broken)
 98 |                         linearVel = layer.properties.get('linearVel', None)
 99 |                         if linearVel:
100 |                             linearVel = [float(x) for x in linearVel.split(',')]
101 |                             bodyDef.linearVelocity = (linearVel[0], linearVel[1])
102 | 
103 |                         # Sets up body Fixture
104 |                         if verticesName:
105 |                             bodyFixtureDef = Box2D.b2FixtureDef(
106 |                                 shape=Box2D.b2PolygonShape(vertices=hitboxes[verticesName]))
107 |                         else:
108 |                             bodyFixtureDef = Box2D.b2FixtureDef(shape=Box2D.b2PolygonShape(
109 |                                 box=((image[1] / gameObjectHandler.scale) / 2,
110 |                                      (image[2] / gameObjectHandler.scale) / 2)))
111 |                         bodyFixtureDef.density = layer.properties.get('density', 1)
112 |                         bodyFixtureDef.friction = layer.properties.get('friction', 0)
113 |                         bodyFixtureDef.restitution = layer.properties.get('restitution', 0)
114 |                         # Sets up Position
115 |                         bodyDef.position = (
116 |                             -(x * tiled_map.tilewidth) / gameObjectHandler.scale,
117 |                             -(y * tiled_map.tileheight) / gameObjectHandler.scale)
118 |                         # adds box
119 |                         if color:
120 |                             gm.textureID = -1
121 |                             gameObjectHandler.add(gm, bodyDef, bodyFixtureDef, color)
122 |                         else:
123 |                             gameObjectHandler.add(gm, bodyDef, bodyFixtureDef)
124 |                     else:
125 |                         gm.position = (-x / gameObjectHandler.scale, -y / gameObjectHandler.scale)
126 |                         if color:
127 |                             gm.textureID = -1
128 |                             vertices = hitboxes[verticesName]
129 |                             gm.vertices = [
130 |                                 (v[0] / gameObjectHandler.scale, v[1] / gameObjectHandler.scale) for v in vertices]
131 |                             gameObjectHandler.add(gm, color=color)
132 |                         else:
133 |                             gameObjectHandler.add(gm)
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |

Classes

144 |
145 |
146 | class TileMap 147 | (tmxFile, gameObjectHandler, gameScreen) 148 |
149 |
150 |
151 |
152 | 153 | Expand source code 154 | 155 |
class TileMap:
156 | 
157 |     def __init__(self, tmxFile, gameObjectHandler, gameScreen):
158 |         tiled_map = pytmx.TiledMap(tmxFile, image_loader=gameScreen.image_loader)
159 |         if tiled_map.background_color:
160 |             color = ImageColor.getcolor(tiled_map.background_color, 'RGB')
161 |             gameScreen.bgColor = [color[0] / 255, color[1] / 255, color[2] / 255]
162 | 
163 |         hitboxes = {}
164 |         objects = tiled_map.objects
165 | 
166 |         # Looks for objects and gets its vertices
167 |         for obj in objects:
168 |             pairPoints = []
169 |             for pair in obj.points:
170 |                 pairPoints.append(
171 |                     (-(pair[0] - obj.x) / gameObjectHandler.scale, -(pair[1] - obj.y) / gameObjectHandler.scale))
172 |             hitboxes[obj.name] = pairPoints
173 | 
174 |         for layer in tiled_map:
175 |             # Checks for layers with tiles
176 |             if isinstance(layer, pytmx.TiledTileLayer):
177 |                 for x, y, image in layer.tiles():
178 |                     # Gets class name and gets class name definition
179 |                     className = layer.properties.get('class', 'GameObject')
180 | 
181 |                     if className != 'GameObject':
182 |                         className = getattr(importlib.import_module('__main__'), className)
183 |                     else:
184 |                         className = GameObject
185 |                     # Sets up Texture
186 |                     gm = className()
187 |                     gm.textureID = image[0]
188 | 
189 |                     gm.setWidth(image[1] / gameObjectHandler.scale ** 2)
190 |                     gm.setHeight(image[2] / gameObjectHandler.scale ** 2)
191 | 
192 |                     # Sets up Color
193 |                     color = layer.properties.get('color', None)
194 |                     if color:
195 |                         color = ImageColor.getcolor(color, 'RGBA')
196 |                         color = [color[2] / 255, color[1] / 255, color[0] / 255, color[3] / 255]
197 |                         print(color)
198 | 
199 |                     # Sets up Hitbox
200 |                     verticesName = layer.properties.get('hitboxName', None)
201 | 
202 |                     # Sets up Body Type
203 |                     bodyType = layer.properties.get('bodyType', 'none')
204 |                     if bodyType != 'none':
205 |                         bodyDef = Box2D.b2BodyDef()
206 |                         if bodyType == 'dynamic':
207 |                             bodyDef.type = Box2D.b2_dynamicBody
208 |                         elif bodyType == 'static':
209 |                             bodyDef.type = Box2D.b2_staticBody
210 |                         else:
211 |                             bodyDef.type = Box2D.b2_kinematicBody
212 |                         bodyDef.linearDamping = layer.properties.get('linearDamping', 0)
213 |                         bodyDef.angularDamping = layer.properties.get('angularDamping', 0)
214 |                         bodyDef.fixedRotation = layer.properties.get('fixedRotation', False)
215 | 
216 |                         # Linear Vel (Broken)
217 |                         linearVel = layer.properties.get('linearVel', None)
218 |                         if linearVel:
219 |                             linearVel = [float(x) for x in linearVel.split(',')]
220 |                             bodyDef.linearVelocity = (linearVel[0], linearVel[1])
221 | 
222 |                         # Sets up body Fixture
223 |                         if verticesName:
224 |                             bodyFixtureDef = Box2D.b2FixtureDef(
225 |                                 shape=Box2D.b2PolygonShape(vertices=hitboxes[verticesName]))
226 |                         else:
227 |                             bodyFixtureDef = Box2D.b2FixtureDef(shape=Box2D.b2PolygonShape(
228 |                                 box=((image[1] / gameObjectHandler.scale) / 2,
229 |                                      (image[2] / gameObjectHandler.scale) / 2)))
230 |                         bodyFixtureDef.density = layer.properties.get('density', 1)
231 |                         bodyFixtureDef.friction = layer.properties.get('friction', 0)
232 |                         bodyFixtureDef.restitution = layer.properties.get('restitution', 0)
233 |                         # Sets up Position
234 |                         bodyDef.position = (
235 |                             -(x * tiled_map.tilewidth) / gameObjectHandler.scale,
236 |                             -(y * tiled_map.tileheight) / gameObjectHandler.scale)
237 |                         # adds box
238 |                         if color:
239 |                             gm.textureID = -1
240 |                             gameObjectHandler.add(gm, bodyDef, bodyFixtureDef, color)
241 |                         else:
242 |                             gameObjectHandler.add(gm, bodyDef, bodyFixtureDef)
243 |                     else:
244 |                         gm.position = (-x / gameObjectHandler.scale, -y / gameObjectHandler.scale)
245 |                         if color:
246 |                             gm.textureID = -1
247 |                             vertices = hitboxes[verticesName]
248 |                             gm.vertices = [
249 |                                 (v[0] / gameObjectHandler.scale, v[1] / gameObjectHandler.scale) for v in vertices]
250 |                             gameObjectHandler.add(gm, color=color)
251 |                         else:
252 |                             gameObjectHandler.add(gm)
253 |
254 |
255 |
256 |
257 |
258 | 278 |
279 | 282 | 283 | -------------------------------------------------------------------------------- /docs/uimanager.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | AggiEngine.uimanager API documentation 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

Module AggiEngine.uimanager

23 |
24 |
25 |
26 | 27 | Expand source code 28 | 29 |
from PySide2.QtCore import QMetaObject
 30 | from PySide2.QtUiTools import QUiLoader
 31 | from PySide2.QtWidgets import QMainWindow
 32 | 
 33 | 
 34 | class UiManager(QUiLoader):
 35 | 
 36 |     def __init__(self, window, customWidgets=None):
 37 |         QUiLoader.__init__(self)
 38 |         self.window = window
 39 | 
 40 |         # Register all the custom widgets so that they can be created later
 41 |         self.customWidgets = customWidgets
 42 |         if customWidgets is not None:
 43 |             for wid in customWidgets:
 44 |                 self.registerCustomWidget(wid)
 45 | 
 46 |         self.keepWidgets = []
 47 | 
 48 |     def createWidget(self, class_name, parent=None, name=''):
 49 |         """
 50 |         Overrides QUiLoader to createWidget in current window rather than a new one
 51 |         ``class_name:`` The class we want to create  
 52 |         ``parent:`` The parent widget  
 53 |         ``name:`` The name of the widget we'll create  
 54 |         """
 55 | 
 56 |         if class_name is QMainWindow.__name__:
 57 |             return self.window
 58 | 
 59 |         if parent is None and self.window:
 60 |             return self.window
 61 |         else:
 62 |             if class_name in self.availableWidgets():
 63 |                 widget = QUiLoader.createWidget(self, class_name, parent, name)
 64 |                 widget.show()
 65 |             else:
 66 |                 try:
 67 |                     widget = self.customWidgets[class_name](parent)
 68 |                 except (TypeError, KeyError) as e:
 69 |                     raise Exception(class_name, 'was not found are you sure it was promoted?')
 70 | 
 71 |             if self.window:
 72 |                 setattr(self.window, name, widget)
 73 | 
 74 |             return widget
 75 | 
 76 |     def loadWidgets(self, ui_file, deleteCurrent=False):
 77 |         """
 78 |         Loads the current ui_file and if wanted deleted old widgets
 79 |         ``ui_file:`` The file path to load  
 80 |         ``deleteCurrent:`` Remove old widgets  
 81 |         """
 82 | 
 83 |         if len(self.window.children()) > 0 and deleteCurrent:
 84 |             for i in range(0, len(self.window.children())):
 85 |                 if not(self.window.children()[i] in self.keepWidgets):
 86 |                     self.window.children()[i].deleteLater()
 87 | 
 88 |         widgets = self.load(ui_file)  # load widgets
 89 |         QMetaObject.connectSlotsByName(widgets)
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |

Classes

100 |
101 |
102 | class UiManager 103 | (window, customWidgets=None) 104 |
105 |
106 |

QUiLoader(self, parent: typing.Union[PySide2.QtCore.QObject, NoneType] = None) -> None

107 |
108 | 109 | Expand source code 110 | 111 |
class UiManager(QUiLoader):
112 | 
113 |     def __init__(self, window, customWidgets=None):
114 |         QUiLoader.__init__(self)
115 |         self.window = window
116 | 
117 |         # Register all the custom widgets so that they can be created later
118 |         self.customWidgets = customWidgets
119 |         if customWidgets is not None:
120 |             for wid in customWidgets:
121 |                 self.registerCustomWidget(wid)
122 | 
123 |         self.keepWidgets = []
124 | 
125 |     def createWidget(self, class_name, parent=None, name=''):
126 |         """
127 |         Overrides QUiLoader to createWidget in current window rather than a new one
128 |         ``class_name:`` The class we want to create  
129 |         ``parent:`` The parent widget  
130 |         ``name:`` The name of the widget we'll create  
131 |         """
132 | 
133 |         if class_name is QMainWindow.__name__:
134 |             return self.window
135 | 
136 |         if parent is None and self.window:
137 |             return self.window
138 |         else:
139 |             if class_name in self.availableWidgets():
140 |                 widget = QUiLoader.createWidget(self, class_name, parent, name)
141 |                 widget.show()
142 |             else:
143 |                 try:
144 |                     widget = self.customWidgets[class_name](parent)
145 |                 except (TypeError, KeyError) as e:
146 |                     raise Exception(class_name, 'was not found are you sure it was promoted?')
147 | 
148 |             if self.window:
149 |                 setattr(self.window, name, widget)
150 | 
151 |             return widget
152 | 
153 |     def loadWidgets(self, ui_file, deleteCurrent=False):
154 |         """
155 |         Loads the current ui_file and if wanted deleted old widgets
156 |         ``ui_file:`` The file path to load  
157 |         ``deleteCurrent:`` Remove old widgets  
158 |         """
159 | 
160 |         if len(self.window.children()) > 0 and deleteCurrent:
161 |             for i in range(0, len(self.window.children())):
162 |                 if not(self.window.children()[i] in self.keepWidgets):
163 |                     self.window.children()[i].deleteLater()
164 | 
165 |         widgets = self.load(ui_file)  # load widgets
166 |         QMetaObject.connectSlotsByName(widgets)
167 |
168 |

Ancestors

169 |
    170 |
  • PySide2.QtUiTools.QUiLoader
  • 171 |
  • PySide2.QtCore.QObject
  • 172 |
  • Shiboken.Object
  • 173 |
174 |

Class variables

175 |
176 |
var staticMetaObject
177 |
178 |
179 |
180 |
181 |

Methods

182 |
183 |
184 | def createWidget(self, class_name, parent=None, name='') 185 |
186 |
187 |

Overrides QUiLoader to createWidget in current window rather than a new one 188 | class_name: The class we want to create
189 | parent: The parent widget
190 | name: The name of the widget we'll create

191 |
192 | 193 | Expand source code 194 | 195 |
def createWidget(self, class_name, parent=None, name=''):
196 |     """
197 |     Overrides QUiLoader to createWidget in current window rather than a new one
198 |     ``class_name:`` The class we want to create  
199 |     ``parent:`` The parent widget  
200 |     ``name:`` The name of the widget we'll create  
201 |     """
202 | 
203 |     if class_name is QMainWindow.__name__:
204 |         return self.window
205 | 
206 |     if parent is None and self.window:
207 |         return self.window
208 |     else:
209 |         if class_name in self.availableWidgets():
210 |             widget = QUiLoader.createWidget(self, class_name, parent, name)
211 |             widget.show()
212 |         else:
213 |             try:
214 |                 widget = self.customWidgets[class_name](parent)
215 |             except (TypeError, KeyError) as e:
216 |                 raise Exception(class_name, 'was not found are you sure it was promoted?')
217 | 
218 |         if self.window:
219 |             setattr(self.window, name, widget)
220 | 
221 |         return widget
222 |
223 |
224 |
225 | def loadWidgets(self, ui_file, deleteCurrent=False) 226 |
227 |
228 |

Loads the current ui_file and if wanted deleted old widgets 229 | ui_file: The file path to load
230 | deleteCurrent: Remove old widgets

231 |
232 | 233 | Expand source code 234 | 235 |
def loadWidgets(self, ui_file, deleteCurrent=False):
236 |     """
237 |     Loads the current ui_file and if wanted deleted old widgets
238 |     ``ui_file:`` The file path to load  
239 |     ``deleteCurrent:`` Remove old widgets  
240 |     """
241 | 
242 |     if len(self.window.children()) > 0 and deleteCurrent:
243 |         for i in range(0, len(self.window.children())):
244 |             if not(self.window.children()[i] in self.keepWidgets):
245 |                 self.window.children()[i].deleteLater()
246 | 
247 |     widgets = self.load(ui_file)  # load widgets
248 |     QMetaObject.connectSlotsByName(widgets)
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 | 281 |
282 | 285 | 286 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # Inside of setup.cfg 2 | [metadata] 3 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | setup( 3 | name = 'AggiEngine', 4 | packages = ['AggiEngine'], 5 | version = '0.1', 6 | license='MIT', 7 | description = 'A game engine written in python', 8 | author = 'Brady Langdale, Nurivan Gomez', 9 | author_email = 'bradylangdale@tamu.edu, nurivan@tamu.edu', 10 | url = 'https://github.com/aggie-coding-club/AggiEngine/', 11 | download_url = 'https://github.com/aggie-coding-club/AggiEngine/archive/refs/tags/0.1.tar.gz', 12 | keywords = ['Python', 'Game Engine'], 13 | install_requires=[ 14 | 'PySide2', 15 | 'PyOpenGL', 16 | 'Box2D', 17 | 'pytmx', 18 | 'pillow', 19 | 'numpy', 20 | 'simpleaudio' 21 | ], 22 | classifiers=[ 23 | 'Development Status :: 3 - Alpha', 24 | 'Intended Audience :: Developers', 25 | 'Topic :: Software Development :: Build Tools', 26 | 'License :: OSI Approved :: MIT License', 27 | 'Programming Language :: Python :: 3', 28 | 'Programming Language :: Python :: 3.4', 29 | 'Programming Language :: Python :: 3.5', 30 | 'Programming Language :: Python :: 3.6', 31 | 'Programming Language :: Python :: 3.7', 32 | 'Programming Language :: Python :: 3.8', 33 | ], 34 | ) --------------------------------------------------------------------------------