VertexEngine 1.2rc4__4-py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
VertexEngine/engine.py ADDED
@@ -0,0 +1,70 @@
1
+ from PyQt6.QtWidgets import QWidget
2
+ from PyQt6.QtGui import QImage, QPainter
3
+ from PyQt6.QtCore import QTimer, Qt
4
+ import pygame
5
+ from .scenes import SceneManager
6
+ pygame.init()
7
+
8
+ class GameEngine(QWidget):
9
+ def __init__(self, width=800, height=600, fps=60):
10
+ super().__init__()
11
+ self.width = width
12
+ self.height = height
13
+ self.fps = fps
14
+
15
+ # Qt key tracking
16
+ self.keys_down = set()
17
+
18
+ # pygame surface
19
+ self.screen = pygame.Surface((self.width, self.height))
20
+
21
+ # Scene manager
22
+ self.scene_manager = SceneManager()
23
+
24
+ # Timer
25
+ self.timer = QTimer(self)
26
+ self.timer.timeout.connect(self.update_frame)
27
+ self.timer.start(1000 // self.fps)
28
+
29
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
30
+
31
+ # ---------------------- RENDER ----------------------
32
+
33
+ def paintEvent(self, event):
34
+ self.screen.fill((50, 50, 100))
35
+
36
+ self.scene_manager.draw(self.screen)
37
+
38
+ raw = pygame.image.tostring(self.screen, "RGBA")
39
+ img = QImage(
40
+ raw,
41
+ self.width,
42
+ self.height,
43
+ QImage.Format.Format_RGBA8888
44
+ )
45
+
46
+ painter = QPainter(self)
47
+ painter.drawImage(0, 0, img)
48
+
49
+ def resizeEvent(self, event):
50
+ size = event.size()
51
+ self.width = size.width()
52
+ self.height = size.height()
53
+ self.screen = pygame.Surface((self.width, self.height))
54
+
55
+ # ---------------------- UPDATE ----------------------
56
+
57
+ def update_frame(self):
58
+ if not self.hasFocus():
59
+ self.keys_down.clear()
60
+
61
+ self.scene_manager.update()
62
+ self.update() # triggers paintEvent
63
+
64
+ # ---------------------- INPUT ----------------------
65
+
66
+ def keyPressEvent(self, event):
67
+ self.keys_down.add(event.key())
68
+
69
+ def keyReleaseEvent(self, event):
70
+ self.keys_down.discard(event.key())
@@ -0,0 +1,59 @@
1
+ from PyQt6.QtWidgets import QApplication
2
+ from engine import GameEngine
3
+ import pygame
4
+ from scenes import Scene
5
+ from assets import AssetManager
6
+ from audio import AudioManager
7
+ import sys
8
+ class MyScene(Scene):
9
+ def __init__(self, engine):
10
+ super().__init__(engine)
11
+ self.assets = AssetManager()
12
+ self.assets.load_image("ball", "ball.png")
13
+ self.x = 100
14
+ self.y = 100
15
+ self.speed = 5
16
+ self.width = engine.width
17
+ self.height = engine.height
18
+
19
+ def update(self):
20
+ # Process pygame events
21
+ for event in pygame.event.get():
22
+ if event.type == pygame.KEYDOWN:
23
+ if event.key == pygame.K_LEFT:
24
+ self.x -= self.speed
25
+ elif event.key == pygame.K_RIGHT:
26
+ self.x += self.speed
27
+ elif event.key == pygame.K_UP:
28
+ self.y -= self.speed
29
+ elif event.key == pygame.K_DOWN:
30
+ self.y += self.speed
31
+
32
+ # Clamp the ball inside the engine window
33
+ self.x = max(0, min(self.x, self.width - 50))
34
+ self.y = max(0, min(self.y, self.height - 50))
35
+
36
+ def draw(self, surface):
37
+ surface.fill((0, 0, 0)) # clear screen
38
+ ball = self.assets.get_image("ball")
39
+ if ball:
40
+ ball_scaled = pygame.transform.scale(ball, (55, 50))
41
+ surface.blit(ball_scaled, (self.x, self.y))
42
+ else:
43
+ pygame.draw.circle(surface, (255, 0, 0), (self.x, self.y), 25)
44
+
45
+ app = QApplication(sys.argv) # ✅ must be first
46
+
47
+ engine = GameEngine()
48
+ engine.resize(800, 600)
49
+
50
+ # Create scene and add to manager
51
+ my_scene = MyScene(engine)
52
+ engine.scene_manager.add_scene("main", my_scene)
53
+ engine.scene_manager.switch_to("main") # make it the active scene
54
+
55
+ # Show the engine
56
+ engine.show()
57
+
58
+ # Start the PyQt6 event loop
59
+ sys.exit(app.exec())
@@ -0,0 +1,233 @@
1
+ import sys
2
+ import random
3
+ from PyQt6.QtWidgets import QApplication
4
+ from PyQt6.QtCore import Qt
5
+
6
+ from VertexEngine.engine import GameEngine
7
+ from VertexEngine.scenes import Scene
8
+ from VertexEngine import VertexScreen
9
+ from VertexEngine.InputSystem.KeyInputs import Input
10
+
11
+ # =====================
12
+ # CONFIG
13
+ # =====================
14
+ VIRTUAL_WIDTH = 800
15
+ VIRTUAL_HEIGHT = 450
16
+
17
+
18
+ #class ScreenScaler:
19
+ # def __init__(self, engine):
20
+ # self.engine = engine
21
+ # @property
22
+ # def scale(self):
23
+ # return min(
24
+ # self.engine.width / VIRTUAL_WIDTH,
25
+ # self.engine.height / VIRTUAL_HEIGHT,
26
+ # )
27
+ # @property
28
+ # def offset_x(self):
29
+ # return int((self.engine.width - VIRTUAL_WIDTH * self.scale) / 2)
30
+ # @property
31
+ # def offset_y(self):
32
+ # return int((self.engine.height - VIRTUAL_HEIGHT * self.scale) / 2)
33
+ # def rect(self, x, y, w, h):
34
+ # s = self.scale
35
+ # return (
36
+ # int(x * s) + self.offset_x,
37
+ # int(y * s) + self.offset_y,
38
+ # int(w * s),
39
+ # int(h * s),
40
+ # )
41
+ # def pos(self, x, y):
42
+ # s = self.scale
43
+ # return (
44
+ # int(x * s) + self.offset_x,
45
+ # int(y * s) + self.offset_y,
46
+ # )
47
+ class ScreenScaler:
48
+ def __init__(self, engine):
49
+ self.engine = engine
50
+
51
+ @property
52
+ def scale(self):
53
+ return min(
54
+ self.engine.width / VIRTUAL_WIDTH,
55
+ self.engine.height / VIRTUAL_HEIGHT,
56
+ )
57
+
58
+ # 👇 NO CENTERING
59
+ @property
60
+ def offset_x(self):
61
+ return 0
62
+
63
+ @property
64
+ def offset_y(self):
65
+ return 0
66
+
67
+ def rect(self, x, y, w, h):
68
+ s = self.scale
69
+ return (
70
+ int(x * s),
71
+ int(y * s),
72
+ int(w * s),
73
+ int(h * s),
74
+ )
75
+
76
+ def pos(self, x, y):
77
+ s = self.scale
78
+ return int(x * s), int(y * s)
79
+ # =====================
80
+ # GAME OBJECTS
81
+ # =====================
82
+ class Player:
83
+ def __init__(self):
84
+ self.x = 230
85
+ self.y = 300
86
+ self.w = 40
87
+ self.h = 40
88
+ self.vel_y = 0
89
+ self.on_ground = True
90
+
91
+ def update(self):
92
+ speed = 5
93
+
94
+ if Input.is_pressed("a") or Input.is_pressed("left"):
95
+ self.x -= speed
96
+ if Input.is_pressed("d") or Input.is_pressed("right"):
97
+ self.x += speed
98
+
99
+ if self.x > VIRTUAL_WIDTH:
100
+ self.x = 0
101
+
102
+ if self.x < 0:
103
+ self.x = VIRTUAL_WIDTH
104
+
105
+ if self.on_ground and Input.is_just_pressed("space"):
106
+ self.vel_y = -15
107
+ self.on_ground = False
108
+
109
+ self.vel_y += 1
110
+ self.y += self.vel_y
111
+
112
+ if self.y >= 300:
113
+ self.y = 300
114
+ self.vel_y = 0
115
+ self.on_ground = True
116
+ print(self.x)
117
+
118
+ class Block:
119
+ def __init__(self):
120
+ self.size = 40
121
+ self.x = random.randint(0, VIRTUAL_WIDTH - self.size)
122
+ self.y = -self.size
123
+ self.speed = random.randint(4, 8)
124
+
125
+ def update(self):
126
+ self.y += self.speed
127
+
128
+
129
+ # =====================
130
+ # SCENE
131
+ # =====================
132
+ class MainScene(Scene):
133
+ def __init__(self, engine):
134
+ super().__init__(engine)
135
+ self.scaler = ScreenScaler(engine)
136
+
137
+ self.player = Player()
138
+ self.blocks = []
139
+ self.dead = False
140
+
141
+ def update(self):
142
+ if not self.dead:
143
+ self.player.update()
144
+
145
+ if random.random() < 0.03:
146
+ self.blocks.append(Block())
147
+
148
+ for b in self.blocks:
149
+ b.update()
150
+ if (
151
+ self.player.x < b.x + b.size and
152
+ self.player.x + self.player.w > b.x and
153
+ self.player.y < b.y + b.size and
154
+ self.player.y + self.player.h > b.y
155
+ ):
156
+ self.dead = True
157
+
158
+ self.blocks = [b for b in self.blocks if b.y < VIRTUAL_HEIGHT]
159
+
160
+ Input.input_update()
161
+
162
+ def draw(self, surface):
163
+ s = self.scaler
164
+
165
+ # background
166
+ VertexScreen.Draw.rect(
167
+ VertexScreen.Draw,
168
+ surface,
169
+ (15, 15, 15),
170
+ s.rect(0, 0, VIRTUAL_WIDTH, VIRTUAL_HEIGHT),
171
+ )
172
+
173
+ # player
174
+ VertexScreen.Draw.rect(
175
+ VertexScreen.Draw,
176
+ surface,
177
+ (0, 255, 255),
178
+ s.rect(self.player.x, self.player.y, self.player.w, self.player.h),
179
+ )
180
+
181
+ # blocks
182
+ for b in self.blocks:
183
+ VertexScreen.Draw.rect(
184
+ VertexScreen.Draw,
185
+ surface,
186
+ (255, 0, 0),
187
+ s.rect(b.x, b.y, b.size, b.size),
188
+ )
189
+
190
+ # death text
191
+ if self.dead:
192
+ deathfont = VertexScreen.Font(None, 24)
193
+ deathfont.draw(
194
+ surface,
195
+ "DOGPILED",
196
+ s.pos(VIRTUAL_WIDTH // 2, VIRTUAL_HEIGHT // 2)
197
+ )
198
+
199
+
200
+ # =====================
201
+ # ENGINE (Qt INPUT HERE)
202
+ # =====================
203
+ class DogpileEngine(GameEngine):
204
+ def __init__(self, **kwargs):
205
+ super().__init__(**kwargs)
206
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
207
+
208
+ def keyPressEvent(self, event):
209
+ Input.key_down(event.key())
210
+
211
+ def keyReleaseEvent(self, event):
212
+ Input.key_up(event.key())
213
+
214
+
215
+ # =====================
216
+ # RUN
217
+ # =====================
218
+ if __name__ == "__main__":
219
+ app = QApplication(sys.argv)
220
+
221
+ engine = DogpileEngine(
222
+ fps=60,
223
+ width=1000,
224
+ height=800,
225
+ )
226
+ engine.setWindowTitle("Dogpile Dodger (VertexEngine)")
227
+ engine.setMinimumSize(VIRTUAL_WIDTH, VIRTUAL_HEIGHT)
228
+ engine.show()
229
+
230
+ scene = MainScene(engine)
231
+ engine.scene_manager.set_scene(scene)
232
+
233
+ sys.exit(app.exec())
@@ -0,0 +1,205 @@
1
+ """
2
+ This is a simple single scene platformer, made in the Vertex Game Engine.
3
+
4
+ This teaches you:
5
+ Basics of Asset rendering
6
+ Scenes
7
+ Game Engine
8
+ rendering to a screen
9
+ handle input
10
+ dictionaries
11
+ physics
12
+ update and drawing
13
+ """
14
+
15
+ from VertexEngine.engine import GameEngine
16
+ from VertexEngine.scenes import Scene
17
+ from VertexEngine import VertexScreen
18
+ from PyQt6.QtWidgets import QApplication
19
+ from PyQt6.QtCore import Qt
20
+ import sys
21
+ from VertexEngine.audio import AudioManager
22
+ import pygame
23
+ import os
24
+ # 🔽 WORLD SCALE
25
+ SCALE = 0.5
26
+
27
+ # 🔽 Physics (scaled)
28
+ GRAVITY = 1.2 * SCALE
29
+ JUMP_FORCE = -22 * SCALE
30
+ MOVE_SPEED = 8 * SCALE
31
+ GROUND_Y = 300 * SCALE
32
+ maindir = os.path.split(os.path.abspath(__file__))[0]
33
+ coin_img = pygame.image.load(maindir + "/data/coin.png")
34
+ coin_img = pygame.transform.scale(
35
+ coin_img,
36
+ (32 * SCALE, 32 * SCALE)
37
+ )
38
+
39
+ class Main(Scene):
40
+ def __init__(self, engine):
41
+ super().__init__(engine)
42
+ self.world_offset_y = engine.height // 2
43
+ self.score = 0
44
+ self.coins = [
45
+ {"x": 200, "y": -35 * SCALE, "collected": False},
46
+ {"x": 50, "y": 80 * SCALE, "collected": False},
47
+ {"x": 380, "y": -300 * SCALE, "collected": False},
48
+ ]
49
+ self.platforms = [
50
+ (200, 1 * SCALE, 200 * SCALE, 20 * SCALE),
51
+ (360, -200 * SCALE, 200 * SCALE, 20 * SCALE),
52
+ (0, 120 * SCALE, 220 * SCALE, 20 * SCALE),
53
+ (350, 200 * SCALE, 180 * SCALE, 20 * SCALE),
54
+ ]
55
+
56
+ # 🔽 Player (scaled)
57
+ self.w = int(80 * SCALE)
58
+ self.h = int(80 * SCALE)
59
+
60
+ self.x = 0
61
+ self.y = 0
62
+
63
+ self.vx = 0
64
+ self.vy = 0
65
+ self.on_ground = False
66
+
67
+ self.keys = set()
68
+
69
+ # --- INPUT ---
70
+ def keyPressEvent(self, event):
71
+ self.keys.add(event.key())
72
+
73
+ def keyReleaseEvent(self, event):
74
+ self.keys.discard(event.key())
75
+
76
+ # --- UPDATE ---
77
+ def update(self):
78
+ self.vx = 0
79
+
80
+ if Qt.Key.Key_A in self.keys:
81
+ self.vx = -MOVE_SPEED
82
+ if Qt.Key.Key_D in self.keys:
83
+ self.vx = MOVE_SPEED
84
+
85
+ if Qt.Key.Key_Space in self.keys and self.on_ground:
86
+ self.vy = JUMP_FORCE
87
+ self.on_ground = False
88
+
89
+ # Gravity
90
+ self.vy += GRAVITY
91
+
92
+ # Apply movement
93
+ self.x += self.vx
94
+ self.y += self.vy
95
+
96
+ self.on_ground = False
97
+
98
+ for coin in self.coins:
99
+ if coin["collected"]:
100
+ continue
101
+
102
+ cx = coin["x"]
103
+ cy = coin["y"]
104
+ cw = coin_img.get_width()
105
+ ch = coin_img.get_height()
106
+
107
+ if (
108
+ self.x < cx + cw and
109
+ self.x + self.w > cx and
110
+ self.y < cy + ch and
111
+ self.y + self.h > cy
112
+ ):
113
+ coin["collected"] = True
114
+ self.score += 1
115
+ print("Coin collected! Score:", self.score)
116
+
117
+
118
+ # --- PLATFORM COLLISIONS ---
119
+ for px, py, pw, ph in self.platforms:
120
+ if (
121
+ self.vy >= 0 and
122
+ self.x + self.w > px and
123
+ self.x < px + pw and
124
+ self.y + self.h > py and
125
+ self.y + self.h < py + ph + self.vy + 1
126
+ ):
127
+ self.y = py - self.h
128
+ self.vy = 0
129
+ self.on_ground = True
130
+
131
+ # --- GROUND COLLISION ---
132
+ if self.y + self.h >= GROUND_Y:
133
+ self.y = GROUND_Y - self.h
134
+ self.vy = 0
135
+ self.on_ground = True
136
+
137
+
138
+ # --- DRAW ---
139
+ def draw(self, surface):
140
+ oy = self.world_offset_y # visual offset
141
+
142
+ # Background
143
+ VertexScreen.Draw.rect(
144
+ VertexScreen.Draw,
145
+ surface,
146
+ (30, 30, 30),
147
+ (0, 0, self.engine.width, self.engine.height)
148
+ )
149
+
150
+ # Ground (shifted DOWN)
151
+ VertexScreen.Draw.rect(
152
+ VertexScreen.Draw,
153
+ surface,
154
+ (50, 200, 50),
155
+ (-2000 * SCALE, GROUND_Y + oy, 4000 * SCALE, 200 * SCALE)
156
+ )
157
+
158
+ # Player (shifted DOWN)
159
+ VertexScreen.Draw.rect(
160
+ VertexScreen.Draw,
161
+ surface,
162
+ (255, 100, 100),
163
+ (self.x, self.y + oy, self.w, self.h)
164
+ )
165
+
166
+ # Coins
167
+ for coin in self.coins:
168
+ if coin["collected"]:
169
+ continue
170
+
171
+ surface.blit(
172
+ coin_img,
173
+ (coin["x"], coin["y"] + oy)
174
+ )
175
+
176
+ # Platforms
177
+ for px, py, pw, ph in self.platforms:
178
+ VertexScreen.Draw.rect(
179
+ VertexScreen.Draw,
180
+ surface,
181
+ (100, 180, 255),
182
+ (px, py + oy, pw, ph)
183
+ )
184
+
185
+ if __name__ == "__main__":
186
+ app = QApplication(sys.argv)
187
+
188
+ engine = GameEngine(
189
+ fps=60,
190
+ width=1920,
191
+ height=1080,
192
+ )
193
+
194
+ engine.setWindowTitle("Vertex Platformer (Scaled World)")
195
+
196
+ engine.show()
197
+
198
+ main = Main(engine)
199
+ engine.scene_manager.add_scene("main", main)
200
+ engine.scene_manager.switch_to("main")
201
+
202
+ engine.keyPressEvent = main.keyPressEvent
203
+ engine.keyReleaseEvent = main.keyReleaseEvent
204
+
205
+ app.exec()
@@ -0,0 +1,135 @@
1
+ import sys
2
+ import random
3
+ import pygame
4
+
5
+ from PyQt6.QtWidgets import QApplication
6
+ from PyQt6.QtCore import Qt
7
+
8
+ from VertexEngine.engine import GameEngine
9
+ from VertexEngine.scenes import Scene
10
+
11
+ # =============================
12
+ # CONFIG
13
+ # =============================
14
+ WIDTH = 800
15
+ HEIGHT = 600
16
+ TILE = 30
17
+ FPS = 60
18
+ GRID_W = WIDTH // TILE
19
+ GRID_H = HEIGHT // TILE
20
+
21
+ # =============================
22
+ # SNAKE SCENE
23
+ # =============================
24
+ class SnakeScene(Scene):
25
+ def __init__(self, engine):
26
+ super().__init__(engine)
27
+
28
+ self.snake = [(10, 10)]
29
+ self.direction = (1, 0)
30
+ self.next_direction = self.direction
31
+
32
+ self.food = self.spawn_food()
33
+ self.dead = False
34
+
35
+ self.tick = 0
36
+ self.speed = 8 # lower = faster
37
+
38
+ def spawn_food(self):
39
+ return (
40
+ random.randrange(GRID_W),
41
+ random.randrange(GRID_H)
42
+ )
43
+
44
+ def set_direction(self, d):
45
+ # Prevent instant reverse
46
+ if (-d[0], -d[1]) != self.direction:
47
+ self.next_direction = d
48
+
49
+ def update(self):
50
+ if self.dead:
51
+ return
52
+
53
+ self.tick += 1
54
+ if self.tick < self.speed:
55
+ return
56
+ self.tick = 0
57
+
58
+ self.direction = self.next_direction
59
+
60
+ hx, hy = self.snake[0]
61
+ dx, dy = self.direction
62
+ new_head = (hx + dx, hy + dy)
63
+
64
+ # Death checks
65
+ if (
66
+ new_head in self.snake or
67
+ new_head[0] < 0 or new_head[1] < 0 or
68
+ new_head[0] >= WIDTH // TILE or
69
+ new_head[1] >= HEIGHT // TILE
70
+ ):
71
+ print("💀 GAME OVER")
72
+ self.dead = True
73
+ return
74
+
75
+ self.snake.insert(0, new_head)
76
+
77
+ if new_head == self.food:
78
+ self.food = self.spawn_food()
79
+ else:
80
+ self.snake.pop()
81
+
82
+ def draw(self, screen):
83
+ screen.fill((0, 0, 0))
84
+
85
+ for x, y in self.snake:
86
+ pygame.draw.rect(
87
+ screen,
88
+ (0, 255, 0),
89
+ (x * TILE, y * TILE, TILE, TILE)
90
+ )
91
+
92
+ fx, fy = self.food
93
+ pygame.draw.rect(
94
+ screen,
95
+ (255, 0, 0),
96
+ (fx * TILE, fy * TILE, TILE, TILE)
97
+ )
98
+
99
+ # =============================
100
+ # BOOTSTRAP (QT FIRST OR DIE)
101
+ # =============================
102
+ if __name__ == "__main__":
103
+ app = QApplication(sys.argv)
104
+
105
+ engine = GameEngine(
106
+ width=WIDTH,
107
+ height=HEIGHT,
108
+ fps=FPS
109
+ )
110
+
111
+ engine.setWindowTitle("Snake 🐍 — RC Survivor Edition")
112
+ engine.show()
113
+
114
+ scene = SnakeScene(engine)
115
+ engine.scene_manager.add_scene("main", scene)
116
+ engine.scene_manager.switch_to("main")
117
+
118
+ # =============================
119
+ # 🔑 QT INPUT — THIS IS THE FIX
120
+ # =============================
121
+ def engine_key_press(event):
122
+ key = event.key()
123
+
124
+ if key == Qt.Key.Key_Up:
125
+ scene.set_direction((0, -1))
126
+ elif key == Qt.Key.Key_Down:
127
+ scene.set_direction((0, 1))
128
+ elif key == Qt.Key.Key_Left:
129
+ scene.set_direction((-1, 0))
130
+ elif key == Qt.Key.Key_Right:
131
+ scene.set_direction((1, 0))
132
+
133
+ engine.keyPressEvent = engine_key_press
134
+
135
+ app.exec()