devpace 1.0.0__tar.gz

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.
Files changed (35) hide show
  1. devpace-1.0.0/.gitignore +3 -0
  2. devpace-1.0.0/LICENSE +19 -0
  3. devpace-1.0.0/PKG-INFO +0 -0
  4. devpace-1.0.0/README.md +0 -0
  5. devpace-1.0.0/pyproject.toml +23 -0
  6. devpace-1.0.0/src/devPace/GlobalDebugger.py +53 -0
  7. devpace-1.0.0/src/devPace/__init__.py +39 -0
  8. devpace-1.0.0/src/devPace/_net.py +55 -0
  9. devpace-1.0.0/src/devPace/assets/animations.py +111 -0
  10. devpace-1.0.0/src/devPace/assets/cache.py +91 -0
  11. devpace-1.0.0/src/devPace/assets/images.py +48 -0
  12. devpace-1.0.0/src/devPace/assets/text.py +7 -0
  13. devpace-1.0.0/src/devPace/assets/tilemap.py +88 -0
  14. devpace-1.0.0/src/devPace/basics/camera.py +32 -0
  15. devpace-1.0.0/src/devPace/basics/input.py +135 -0
  16. devpace-1.0.0/src/devPace/basics/shapes.py +74 -0
  17. devpace-1.0.0/src/devPace/helpers/_tilemapEditor.py +340 -0
  18. devpace-1.0.0/src/devPace/helpers/_tilemap_files.py +154 -0
  19. devpace-1.0.0/src/devPace/helpers/utils.py +9 -0
  20. devpace-1.0.0/src/devPace/main.py +184 -0
  21. devpace-1.0.0/src/devPace/physics/KinematicPlatform.py +87 -0
  22. devpace-1.0.0/src/devPace/physics/StateManager.py +87 -0
  23. devpace-1.0.0/src/devPace/physics/collisions.py +40 -0
  24. devpace-1.0.0/src/devPace/physics/dynamicBody.py +135 -0
  25. devpace-1.0.0/src/devPace/tracker.txt +13 -0
  26. devpace-1.0.0/tests/assets/PL #3 Species(Grass & flowers).png +0 -0
  27. devpace-1.0.0/tests/assets/TileSet/PL #1 TileSet(Ground).png +0 -0
  28. devpace-1.0.0/tests/assets/TileSet/PL #1 TileSet(Lava Animation).png +0 -0
  29. devpace-1.0.0/tests/assets/TileSet/PL #1 TileSet(Water Animation).png +0 -0
  30. devpace-1.0.0/tests/assets/levelTiles.png +0 -0
  31. devpace-1.0.0/tests/assets/test.png +0 -0
  32. devpace-1.0.0/tests/game.json +1 -0
  33. devpace-1.0.0/tests/platformergame.py +75 -0
  34. devpace-1.0.0/tests/props.json +1 -0
  35. devpace-1.0.0/tests/test.json +1 -0
@@ -0,0 +1,3 @@
1
+ .vscode/
2
+ __pycache__/
3
+ *.pyc
devpace-1.0.0/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
devpace-1.0.0/PKG-INFO ADDED
Binary file
Binary file
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["hatchling >= 1.26"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "devpace"
7
+ version = "1.0.0"
8
+ authors = [
9
+ { name="Sam Fertig", email="sfertig007@gmail.com" },
10
+ ]
11
+ description = "A growing game framework for pygame developement"
12
+ readme = "README.md"
13
+ requires-python = ">=3.9"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Operating System :: OS Independent",
17
+ ]
18
+ license = "MIT"
19
+ license-files = ["LICEN[CS]E*"]
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/sfertig/GameEngine"
23
+ Issues = "https://github.com/sfertig/GameEngine/issues"
@@ -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()
@@ -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
+ ]
@@ -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
+
@@ -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
+