├── .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 | [](https://github.com/aggie-coding-club/AggiEngine/actions)
2 | [](https://pypi.org/project/AggiEngine)
3 | [](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 |
68 |
70 |
72 |
73 |
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 |
81 |
83 |
85 |
86 |
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 |
94 |
96 |
98 |
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 |
158 |
160 |
162 |
163 |
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 |
112 |
114 |
116 |
117 |
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 |
62 |
64 |
66 |
67 |
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 |
149 |
151 |
153 |
154 |
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 |
138 |
140 |
142 |
143 |
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 |
94 |
96 |
98 |
99 |
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 | )
--------------------------------------------------------------------------------