devpace 1.0.0__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.
@@ -0,0 +1,53 @@
1
+ def GlobalDebugger(log_queue):
2
+ """
3
+ Runs in an isolated process. Must completely rebuild its
4
+ own window drivers and pump events immediately to become visible.
5
+ """
6
+ import pygame # Import inside the process to ensure clean namespace isolation
7
+
8
+ # RULE 1: Fresh initialization of the sub-process multimedia context
9
+ pygame.init()
10
+
11
+ # RULE 3: Use traditional display modes for sub-processes to ensure OS hook compatibility
12
+ # Set window size (e.g., 400x600)
13
+ screen = pygame.display.set_mode((400, 600))
14
+ pygame.display.set_caption("Engine Debug Log")
15
+
16
+ font = pygame.font.Font(None, 22)
17
+ clock = pygame.time.Clock()
18
+ displayed_logs = ["--- Debugger Online ---"]
19
+
20
+ running = True
21
+ while running:
22
+ # RULE 2: Pump events immediately so Windows displays the window framework
23
+ for event in pygame.event.get():
24
+ if event.type == pygame.QUIT:
25
+ running = False
26
+
27
+ # Drain the communication tube
28
+ while not log_queue.empty():
29
+ try:
30
+ msg = log_queue.get(block=False)
31
+ displayed_logs.append(msg)
32
+ if len(displayed_logs) > 25:
33
+ displayed_logs.pop(1)
34
+ except:
35
+ break
36
+
37
+ # Draw the logs onto the screen
38
+ screen.fill((15, 15, 15))
39
+
40
+ y = 15
41
+ for log in displayed_logs:
42
+ color = (255, 80, 80) if "Error" in log else (220, 220, 220)
43
+ txt = font.render(log, True, color)
44
+ screen.blit(txt, (15, y))
45
+ y += 22
46
+
47
+ # Push the frame buffer onto the screen monitor
48
+ pygame.display.flip()
49
+
50
+ # Keep frame times light to optimize host CPU execution
51
+ clock.tick(30)
52
+
53
+ pygame.quit()
devPace/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ """
2
+ Pygame Engine
3
+ """
4
+
5
+
6
+ __version__ = "1.0.0"
7
+ __author__ = "Sam Fertig"
8
+
9
+ #____imports____
10
+ from .main import Engine, Scene
11
+ from .basics.camera import Camera
12
+ from .basics.input import Keys, FastMovement
13
+ from .basics.shapes import Rect, Circle
14
+ from .assets.cache import Assets
15
+ from .assets.images import Image
16
+ from .physics.dynamicBody import DynamicBody
17
+ from .physics.KinematicPlatform import KinematicPlatform
18
+ from .assets.tilemap import Tilemap, TILEMAP_COLLISION_GEN
19
+ from .assets.animations import AnimationManager
20
+ from .physics.StateManager import StateManager, AutoStateManager
21
+
22
+ __all__ = [
23
+ "Engine",
24
+ "Scene",
25
+ "Camera",
26
+ "Rect",
27
+ "Keys",
28
+ "FastMovement",
29
+ "Circle",
30
+ "Assets",
31
+ "Image",
32
+ "DynamicBody",
33
+ "KinematicPlatform",
34
+ "Tilemap",
35
+ "TILEMAP_COLLISION_GEN",
36
+ "AnimationManager",
37
+ "StateManager",
38
+ "AutoStateManager"
39
+ ]
devPace/_net.py ADDED
@@ -0,0 +1,55 @@
1
+ import pygame
2
+
3
+ class _global_:
4
+ def __init__(self):
5
+ #screen info
6
+ self.screen: pygame.Surface = None
7
+ self.display: pygame.Surface = None
8
+ self.ScreenDim: tuple = (800, 600)
9
+ self.title: str = "Pygame Window"
10
+
11
+ self.FW = 0.0 #diference used for resizing
12
+ self.FH = 0.0
13
+
14
+ #basics
15
+ self.cam = None
16
+ self.clock: pygame.time.Clock = None
17
+ self.bg: str = "black"
18
+ self.dt: float = 0.0
19
+ self.events: list[pygame.event.Event] = []
20
+
21
+ #assets
22
+ self.assets = None
23
+
24
+ #objects
25
+ self.objects = {}
26
+
27
+ #collisions
28
+ self.collisions = []
29
+ self.all_collision_layers = None
30
+
31
+ #scenes
32
+ self.current_scene = None
33
+ self.scenes = {}
34
+ self.last_scene = None
35
+
36
+ #tilemaps
37
+
38
+ self.tilemaps = {} #tilemaps: dict[int, list[tilemap]]
39
+
40
+ def _add_tilemap(self, layer, map):
41
+ if layer not in self.tilemaps:
42
+ self.tilemaps[layer] = []
43
+ self.tilemaps[layer].append(map)
44
+
45
+ def add_object(self, layer: str, obj):
46
+ if layer not in self.objects:
47
+ self.objects[layer] = []
48
+ self.objects[layer].append(obj)
49
+
50
+ def remove_object(self, layer: str, obj):
51
+ if layer in self.objects:
52
+ self.objects[layer].remove(obj)
53
+
54
+ Global = _global_()
55
+
@@ -0,0 +1,111 @@
1
+ import pygame
2
+ from .._net import Global
3
+ from .images import Image
4
+ #from .cache import Assets
5
+ from ..helpers.utils import change_layer
6
+ from ..physics.collisions import CollisionRect
7
+
8
+ def splitImage(image, width, height, frames):
9
+ images = []
10
+ #split image into frames
11
+ for y in range(image.get_height()//height):
12
+ for x in range(image.get_width()//width):
13
+ images.append(image.subsurface(pygame.Rect(x*width, y*height, width, height)))
14
+ new_images = []
15
+ #extract each frame
16
+
17
+ #if all
18
+ if frames == "_ALL": return images
19
+ #else return range
20
+ for i in range(frames[0], frames[1]+1):
21
+ new_images.append(images[i])
22
+ return new_images
23
+
24
+ class Animation:
25
+ def __init__(self, name, image, frames, width=16, height=16, x=0, y=0, loop=True, speed=0.3, layer=3, show=True, enableCollision=False):
26
+ self.name = name
27
+ self.frames = splitImage(image, width, height, frames)
28
+ self.frame = 0
29
+ self.loop = loop
30
+ self.speed = speed
31
+ self.layer = layer
32
+ self.show = show
33
+ self.time = 0.0
34
+ self.x = x
35
+ self.y = y
36
+
37
+ Global.add_object(layer, self)
38
+
39
+ self.collision = None
40
+ if enableCollision:
41
+ self.collision = CollisionRect(x, y, width, height, [layer])
42
+ self.collision.layers = [layer]
43
+
44
+ def change_layer(self, new_layer):
45
+ change_layer(self, new_layer, self.layer)
46
+ self.layer = new_layer
47
+
48
+ def update(self):
49
+ if self.collision is not None:
50
+ self.collision.x = self.x
51
+ self.collision.y = self.y
52
+ #update using Global.dt
53
+ self.time+=Global.dt
54
+ if self.time >= self.speed:
55
+ self.time = 0.0
56
+ self.frame+=1
57
+ if self.frame > len(self.frames)-1:
58
+ if self.loop:
59
+ self.frame = 0
60
+
61
+ def enableCollision(self):
62
+ self.collision = CollisionRect(self.x, self.y, self.image.get_width(), self.image.get_height(), [self.layer])
63
+
64
+ def get_frame(self):
65
+ return self.frames[self.frame]
66
+
67
+ def render(self):
68
+ if self.show:
69
+ Global.screen.blit(self.frames[self.frame], (self.x-Global.cam.x, self.y-Global.cam.y))
70
+
71
+ class AnimationManager:
72
+ def __init__(self, name, layer=3, enable_collisions=False, animations={}, current_animation=None):
73
+ self.name = name
74
+ self.layer = layer
75
+ Global.add_object(layer, self)
76
+ self.animations: dict[str, Animation] = animations
77
+ self.current_animation = current_animation
78
+
79
+ #remove animations from main update loop
80
+ for animation in self.animations.values():
81
+ Global.remove_object(animation.layer, animation)
82
+
83
+ self.collision = None
84
+ if enable_collisions:
85
+ self.collision = CollisionRect(0, 0, 0, 0, [layer])
86
+ self.collision.layers = [layer]
87
+
88
+ def change_layer(self, new_layer):
89
+ change_layer(self, new_layer, self.layer)
90
+ self.layer = new_layer
91
+
92
+ def add_animation(self, name, animation):
93
+ self.animations[name] = animation
94
+ def remove_animation(self, name):
95
+ self.animations.pop(name)
96
+ def play_animation(self, name):
97
+ self.current_animation = name
98
+
99
+ def update(self):
100
+ if self.collision is not None:
101
+ self.collision.x = self.x
102
+ self.collision.y = self.y
103
+ if self.current_animation is None: return
104
+ self.animations[self.current_animation].update()
105
+ self.animations[self.current_animation].x = self.x
106
+ self.animations[self.current_animation].y = self.y
107
+
108
+
109
+ def render(self):
110
+ if self.current_animation is None: return
111
+ self.animations[self.current_animation].render()
@@ -0,0 +1,91 @@
1
+ import pygame
2
+ #from .._net import Global
3
+ from .animations import Animation
4
+ class _Cache:
5
+ def __init__(self):
6
+ self.images: dict[str, pygame.surface.Surface] = {}
7
+ self.animations: dict[str, Animation] = {}
8
+ self.tilesets: dict[str, dict[tuple[int, int], pygame.surface.Surface]] = {}
9
+ self._tileset_cache_data: dict[str, tuple[int, int]] = {}
10
+ self.fonts: dict[str, pygame.font.Font] = {}
11
+
12
+ def clear_all(self, _confirm=False):
13
+ if not _confirm: return
14
+ self.images.clear()
15
+ self.animations.clear()
16
+ self.tilesets.clear()
17
+ self._tileset_cache_data.clear()
18
+ self.fonts.clear()
19
+
20
+ #########################
21
+ #CREATION
22
+ #########################
23
+
24
+ def new_image(self, path, name, colorkey=None, scale=1.0) -> bool:
25
+ if name in self.images:
26
+ return False
27
+ self.images[name] = pygame.image.load(path).convert_alpha()
28
+ if colorkey is not None:
29
+ self.images[name].set_colorkey(colorkey)
30
+ if scale != 1.0:
31
+ self.images[name] = pygame.transform.scale(self.images[name], (int(self.images[name].get_width()*scale), int(self.images[name].get_height()*scale)))
32
+ return True
33
+
34
+ def new_animation(self, image, name, frames, width=16, height=16, x=0, y=0, loop=True, speed=0.3, layer=3, show=True) -> bool:
35
+ if name in self.animations:
36
+ return False
37
+ self.animations[name] = Animation(name, image, frames, width, height, x, y, loop, speed, layer, show)
38
+ return True
39
+
40
+ def new_tileset(self, path, name, width=16, height=16, scale=2.0, colorkey=None) -> bool:
41
+ image = pygame.image.load(path).convert_alpha()
42
+ if colorkey is not None:
43
+ image.set_colorkey(colorkey)
44
+ if scale != 1.0:
45
+ image = pygame.transform.scale(image, (int(image.get_width()*scale), int(image.get_height()*scale)))
46
+ data, cache = load_tileset(image, width*scale, height*scale)
47
+ self.tilesets[name] = data
48
+ self._tileset_cache_data[name] = cache
49
+
50
+ ########################
51
+ #REMOVING
52
+ ########################
53
+
54
+ def remove_image(self, name):
55
+ if name in self.images:
56
+ del self.images[name]
57
+
58
+ def remove_animation(self, name):
59
+ if name in self.animations:
60
+ del self.animations[name]
61
+
62
+ def remove_tileset(self, name):
63
+ if name in self.tilesets:
64
+ del self.tilesets[name]
65
+
66
+ ########################
67
+ #GETTERS
68
+ ########################
69
+
70
+ def get_image(self, name, copy=False) -> pygame.surface.Surface:
71
+ if copy: return self.images[name].copy()
72
+ else: return self.images[name]
73
+
74
+ def get_animation(self, name) -> Animation:
75
+ return self.animations[name]
76
+
77
+ def get_tileset(self, name) -> pygame.surface.Surface:
78
+ return self.tilesets[name]
79
+
80
+ Assets = _Cache()
81
+
82
+ def load_tileset(image, width, height):
83
+ tileset = {}
84
+ w, h = image.get_size()
85
+ w = int(w/width)
86
+ h = int(h/height)
87
+ for y in range(int(image.get_height()//height)):
88
+ for x in range(int(image.get_width()//width)):
89
+ tileset[(x, y)] = image.subsurface(pygame.Rect(x*width, y*height, width, height))
90
+ return tileset, (w, h)
91
+
@@ -0,0 +1,48 @@
1
+ import pygame
2
+
3
+ from .._net import Global
4
+ #from .cache import Assets
5
+ from ..helpers.utils import change_layer
6
+ from ..physics.collisions import CollisionRect
7
+
8
+
9
+
10
+ class Image:
11
+ def __init__(self, image, colorkey=None, x=0, y=0, scale=1.0, show=True, layer=3, enableCollision=False):
12
+ self.image = image
13
+ if scale != 1.0:
14
+ self.image = pygame.transform.scale(self.image, (int(self.image.get_width()*scale), int(self.image.get_height()*scale)))
15
+ self.colorkey = colorkey
16
+ self.x = x
17
+ self.y = y
18
+ self.show = show
19
+ self.layer = layer
20
+ self.scale = scale
21
+
22
+ self.collision: CollisionRect = None
23
+ if enableCollision:
24
+ self.collision = CollisionRect(x, y, image.get_width(), image.get_height(), [layer])
25
+
26
+ Global.add_object(layer, self)
27
+
28
+ def change_layer(self, new_layer):
29
+ change_layer(self, new_layer, self.layer)
30
+ self.layer = new_layer
31
+
32
+ def rescale(self, scale):
33
+ self.image = pygame.transform.scale(self.image, (int(self.image.get_width()*scale), int(self.image.get_height()*scale)))
34
+ self.scale = scale
35
+
36
+ def enableCollision(self):
37
+ self.collision = CollisionRect(self.x, self.y, self.image.get_width(), self.image.get_height(), [self.layer])
38
+
39
+ def update(self):
40
+ if self.collision is not None:
41
+ self.collision.x = self.x
42
+ self.collision.y = self.y
43
+
44
+ def render(self, x=None, y=None):
45
+ if self.show:
46
+ x, y = x or self.x, y or self.y
47
+ Global.screen.blit(self.image, (x-Global.cam.x, y-Global.cam.y))
48
+
devPace/assets/text.py ADDED
@@ -0,0 +1,7 @@
1
+ import pygame
2
+ from .._net import Global
3
+ from .cache import Assets
4
+
5
+ class Text:
6
+ def __init__(self, text, x, y, size, color, font=None, bold=False, italic=False, underline=False):
7
+ pass
@@ -0,0 +1,88 @@
1
+ import pygame
2
+ import os
3
+ from .._net import Global
4
+ from ..helpers.utils import change_layer
5
+ from ..basics.camera import Camera
6
+ from ..basics.input import Keys
7
+ from ..physics.collisions import CollisionRect
8
+
9
+ from ..helpers._tilemapEditor import _TileMapEditor
10
+ from ..helpers._tilemap_files import __saveTileMap_json__, __loadTileMap_json__, gen_collision_shapes
11
+
12
+ #tilemap collision types
13
+ COL_FULL = 1
14
+ COL_HALF_TOP = 2
15
+ COL_HALF_BOTTOM = 3
16
+ COL_HALF_LEFT = 4
17
+ COL_HALF_RIGHT = 5
18
+
19
+ #collision gen vars
20
+ TILEMAP_COLLISION_GEN = 25
21
+ SEARCH_TIME = TILEMAP_COLLISION_GEN
22
+
23
+ class Tilemap:
24
+ def __init__(self, name, tileset, show=True, layer=3, dataFile=None, collisionLayers=None):
25
+ self.name = name
26
+ self.tileset: dict[tuple[int, int], pygame.surface.Surface] = tileset
27
+ self.width = tileset[0, 0].get_width()
28
+ self.height = tileset[0, 0].get_height()
29
+ self.show = show
30
+ self.layer = layer
31
+
32
+ if collisionLayers is not None: self.collisionLayers = collisionLayers
33
+ else: self.collisionLayers = [layer]
34
+
35
+ self.dataFile = dataFile
36
+
37
+ self.tiles: dict[tuple[int, int ], tuple[int, int]] = {}
38
+ self.collisions = []
39
+ self.collDef: dict[tuple[int, int], int] = {}
40
+
41
+ Global.add_object(layer, self)
42
+
43
+ #fallback for hardcoded tilemaps (no loading from file)
44
+ self.__gen_self_collision_shapes()
45
+
46
+ Global._add_tilemap(layer, self)
47
+
48
+ def change_layer(self, new_layer):
49
+ change_layer(self, new_layer, self.layer)
50
+ self.layer = new_layer
51
+
52
+
53
+ def __gen_self_collision_shapes(self):
54
+ gen_collision_shapes(self, COL_FULL, COL_HALF_TOP, COL_HALF_BOTTOM, COL_HALF_LEFT, COL_HALF_RIGHT, SEARCH_TIME)
55
+
56
+ def manual_save_json(self, path=None):
57
+ if path and self.dataFile == None: return
58
+ if path is None: path = self.dataFile
59
+ __saveTileMap_json__(path, self.tiles, self.collDef)
60
+ def manual_load_json(self, path):
61
+ if path and self.dataFile == None: return
62
+ if path is None: path = self.dataFile
63
+ self.tiles, self.collDef = __loadTileMap_json__(path)
64
+ self.__gen_self_collision_shapes()
65
+
66
+ def activateEditor(self, exit_key=Keys.escape, _pin=(0, 0)):
67
+ x, y = pygame.display.get_window_position()
68
+ _TileMapEditor(x, y, Global.display.get_width(), Global.display.get_height(), self, exit_key, _pin).run()
69
+ self.manual_save_json()
70
+ self.__gen_self_collision_shapes()
71
+
72
+ def render(self):
73
+ if not self.show: return
74
+ for pos, tile in self.tiles.items():
75
+ tx = pos[0] * self.width - Global.cam.x
76
+ ty = pos[1] * self.height - Global.cam.y
77
+ if tx > -self.width and tx < Global.screen.get_width()+self.width and ty > -self.height and ty < Global.screen.get_height()+self.height:
78
+ image = self.tileset[tile]
79
+ Global.screen.blit(image, (tx, ty))
80
+
81
+ def tile_beside(pos, _pos):
82
+ if _pos[0] == pos[0]+1: return True
83
+ elif _pos[0] == pos[0]-1: return True
84
+ return False
85
+ def tile_above(pos, _pos):
86
+ if _pos[1] == pos[1]+1: return True
87
+ elif _pos[1] == pos[1]-1: return True
88
+ return False
@@ -0,0 +1,32 @@
1
+ import pygame
2
+ from .._net import Global
3
+
4
+ class Camera:
5
+ def __init__(self, x=0, y=0, vx=0.0, vy=0.0, zoom=1.0):
6
+ self.x = x
7
+ self.y = y
8
+ self.vx = vx
9
+ self.vy = vy
10
+ self.zoom = zoom
11
+
12
+ self.follow_target = None
13
+ self.offset = None
14
+ self.speed = 0.0
15
+
16
+
17
+ if Global.cam is None:
18
+ Global.cam = self
19
+
20
+ def update(self):
21
+ self.x+=self.vx*Global.dt
22
+ self.y+=self.vy*Global.dt
23
+
24
+ if self.follow_target is not None:
25
+ self.x = (self.x+(self.follow_target.x+self.offset[0]-self.x)*self.speed)
26
+ self.y = (self.y+(self.follow_target.y+self.offset[1]-self.y)*self.speed)
27
+
28
+ def set_follow_target(self, target, speed=0.2):
29
+ self.follow_target = target
30
+ self.speed = speed
31
+ self.offset = (self.x - target.x, self.y - target.y)
32
+
@@ -0,0 +1,135 @@
1
+ import pygame
2
+
3
+ from .._net import Global
4
+
5
+
6
+ class _keys:
7
+ def __init__(self):
8
+
9
+ #alphabet
10
+ self.a = pygame.K_a
11
+ self.b = pygame.K_b
12
+ self.c = pygame.K_c
13
+ self.d = pygame.K_d
14
+ self.e = pygame.K_e
15
+ self.f = pygame.K_f
16
+ self.g = pygame.K_g
17
+ self.h = pygame.K_h
18
+ self.i = pygame.K_i
19
+ self.j = pygame.K_j
20
+ self.k = pygame.K_k
21
+ self.l = pygame.K_l
22
+ self.m = pygame.K_m
23
+ self.n = pygame.K_n
24
+ self.o = pygame.K_o
25
+ self.p = pygame.K_p
26
+ self.q = pygame.K_q
27
+ self.r = pygame.K_r
28
+ self.s = pygame.K_s
29
+ self.t = pygame.K_t
30
+ self.u = pygame.K_u
31
+ self.v = pygame.K_v
32
+ self.w = pygame.K_w
33
+ self.x = pygame.K_x
34
+ self.y = pygame.K_y
35
+ self.z = pygame.K_z
36
+
37
+ #numbers
38
+ self.n0 = pygame.K_0
39
+ self.n1 = pygame.K_1
40
+ self.n2 = pygame.K_2
41
+ self.n3 = pygame.K_3
42
+ self.n4 = pygame.K_4
43
+ self.n5 = pygame.K_5
44
+ self.n6 = pygame.K_6
45
+ self.n7 = pygame.K_7
46
+ self.n8 = pygame.K_8
47
+ self.n9 = pygame.K_9
48
+
49
+ #special keys
50
+ self.enter = pygame.K_RETURN
51
+ self.escape = pygame.K_ESCAPE
52
+ self.space = pygame.K_SPACE
53
+ self.backspace = pygame.K_BACKSPACE
54
+ self.tab = pygame.K_TAB
55
+ self.shift = pygame.K_LSHIFT
56
+
57
+ #arrow keys
58
+ self.up = pygame.K_UP
59
+ self.down = pygame.K_DOWN
60
+ self.left = pygame.K_LEFT
61
+ self.right = pygame.K_RIGHT
62
+
63
+
64
+ #mouse
65
+ self.mouse_x, self.mouse_y = 0, 0
66
+
67
+ def is_pressed(self, keys):
68
+ if type(keys) != list: keys = [keys]
69
+ if Global.events is None: return False
70
+ for event in Global.events:
71
+ if event.type == pygame.KEYDOWN:
72
+ if event.key in keys: return True
73
+ return False
74
+
75
+ def is_held(self, key):
76
+ keys = pygame.key.get_pressed()
77
+ return keys[key]
78
+
79
+ def update(self): self.mouse_x, self.mouse_y = pygame.mouse.get_pos()
80
+
81
+ Keys = _keys()
82
+
83
+ class FastMovement:
84
+ def __init__(self, obj, speed, reset_x=True, reset_y=True, WSAD=True, ARROWS=True, move_x=True, move_y=True, jump=False, jumpForce=0.0, jumpKeys=[Keys.space]):
85
+ self.obj = obj
86
+ self.speed = speed
87
+ self.reset_x = reset_x
88
+ self.reset_y = reset_y
89
+
90
+ self.jump = jump
91
+ self.jumpForce = jumpForce
92
+ self.jumpKeys = jumpKeys
93
+
94
+ self.WSAD = WSAD
95
+ self.ARROWS = ARROWS
96
+
97
+ self.move_x = move_x
98
+ self.move_y = move_y
99
+
100
+ if hasattr(self.obj, 'vx') and hasattr(self.obj, 'vy'): self.safe = True
101
+ else: self.safe = False
102
+ if hasattr(self.obj, 'is_on_floor'): self.safeJump = True
103
+ else: self.safeJump = False
104
+
105
+ Global.add_object(0, self)
106
+
107
+ def update(self):
108
+ if not self.safe: return
109
+
110
+ if self.reset_x: self.obj.vx = 0.0
111
+ if self.reset_y: self.obj.vy = 0.0
112
+
113
+ if self.WSAD:
114
+ if self.move_x:
115
+ if Keys.is_held(Keys.a): self.obj.vx = -self.speed
116
+ if Keys.is_held(Keys.d): self.obj.vx = self.speed
117
+ if self.move_y:
118
+ if Keys.is_held(Keys.w): self.obj.vy = -self.speed
119
+ if Keys.is_held(Keys.s): self.obj.vy = self.speed
120
+
121
+ if self.safeJump and self.jump:
122
+ if Keys.is_pressed(self.jumpKeys) and self.obj.is_on_floor:
123
+ self.obj.vy = -self.jumpForce
124
+
125
+ if self.ARROWS:
126
+ if self.move_x:
127
+ if Keys.is_held(Keys.left): self.obj.vx = -self.speed
128
+ if Keys.is_held(Keys.right): self.obj.vx = self.speed
129
+ if self.move_y:
130
+ if Keys.is_held(Keys.up): self.obj.vy = -self.speed
131
+ if Keys.is_held(Keys.down): self.obj.vy = self.speed
132
+
133
+ if self.safeJump and self.jump:
134
+ if Keys.is_pressed(self.jumpKeys) and self.obj.is_on_floor: self.obj.vy = -self.jumpForce
135
+