diff --git a/main.py b/main.py index 80a397c..feabd6b 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,8 @@ from map import * from player import * from raycasting import * from object_renderer import * +#from sprite_object import * +from object_handler import * class Game: def __init__(self): @@ -20,19 +22,30 @@ class Game: self.player = Player(self) self.object_renderer = ObjectRenderer(self) self.raycasting = RayCasting(self) + #self.static_sprites = SpriteObject(self) + #self.animated_sprites = AnimatedSprite(self) + self.object_handler = ObjectHandler(self) def update(self): self.player.update() self.raycasting.update() + #self.static_sprites.update() + #self.animated_sprites.update() + self.object_handler.update() pg.display.flip() self.delta_time = self.clock.tick(FPS) - pg.display.set_caption(f"FPS: {self.clock.get_fps() : 0.2f}") + if DEBUG_MODE: + pg.display.set_caption(f"Garbage Game - FPS: {self.clock.get_fps() : 0.2f} - Delta Time: {self.delta_time / 1000 : 0.2f}") + else: + pg.display.set_caption(f"Garbage Game") def draw(self): - #self.screen.fill('black') - self.object_renderer.draw() - #self.map.draw() - #self.player.draw() + if DEBUG_MODE: + self.screen.fill('black') + self.map.draw() + self.player.draw() + else: + self.object_renderer.draw() def check_events(self): for event in pg.event.get(): diff --git a/map.py b/map.py index d5d89c9..3e9b3cf 100644 --- a/map.py +++ b/map.py @@ -10,10 +10,10 @@ mini_map = [ [1, _, _, 3, 3, 3, 3, _, _, _, _, _, _, _, _, 1], [1, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 1], [1, _, _, _, 4, _, _, _, 4, _, _, _, _, _, _, 1], - [1, 1, 1, 3, 1, 3, 1, 1, 1, 3, _, _, 3, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 3, _, _, 3, 1, 1, 1], - [1, 1, 1, 1, 1, 1, 1, 1, 1, 3, _, _, 3, 1, 1, 1], - [1, 1, 3, 1, 1, 1, 1, 1, 1, 3, _, _, 3, 1, 1, 1], + [1, 1, 1, 3, 1, 3, _, 1, 1, 3, _, _, 3, 1, 1, 1], + [1, 1, 1, 1, _, _, _, 1, 1, 3, _, _, 3, 1, 1, 1], + [1, 1, 1, _, _, 1, 1, 1, 1, 3, _, _, 3, 1, 1, 1], + [1, 1, 3, _, 1, 1, 1, 1, 1, 3, _, _, 3, 1, 1, 1], [1, 4, _, _, _, _, _, _, _, _, _, _, _, _, _, 1], [3, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 1], [1, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 1], diff --git a/object_handler.py b/object_handler.py new file mode 100644 index 0000000..932f384 --- /dev/null +++ b/object_handler.py @@ -0,0 +1,28 @@ +from sprite_object import * + +class ObjectHandler: + def __init__(self, game): + self.game = game + self.sprite_list = [] + self.static_sprite_path = 'resources/sprites/static_sprites/' + self.animated_sprite_path = 'resources/sprites/animated_sprites/' + add_sprite = self.add_sprite + + # Sprite Map + add_sprite(SpriteObject(game)) + add_sprite(AnimatedSprite(game)) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(14.5, 5.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(14.5, 7.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(12.5, 7.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(9.5, 7.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(14.5, 12.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(9.5, 20.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(10.5, 20.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(3.5, 14.5))) + add_sprite(AnimatedSprite(game, path=self.animated_sprite_path + 'red_light/0.png', pos=(3.5, 18.5))) + + def update(self): + [sprite.update() for sprite in self.sprite_list] + + def add_sprite(self, sprite): + self.sprite_list.append(sprite) \ No newline at end of file diff --git a/object_renderer.py b/object_renderer.py index d39a00c..4f79bb0 100644 --- a/object_renderer.py +++ b/object_renderer.py @@ -22,7 +22,7 @@ class ObjectRenderer: pg.draw.rect(self.screen, FLOOR_COLOR, (0, HALF_HEIGHT, WIDTH, HALF_HEIGHT)) def render_game_objects(self): - list_objects = self.game.raycasting.objects_to_render + list_objects = sorted(self.game.raycasting.objects_to_render, key=lambda t: t[0], reverse=True) for depth, image, pos in list_objects: colour = [255 / (1 + depth ** 3.2 * 0.0002)] * 3 image.fill(colour, special_flags=pg.BLEND_MULT) @@ -39,6 +39,6 @@ class ObjectRenderer: 1: self.get_texture('resources/textures/1.png'), 2: self.get_texture('resources/textures/2.png'), 3: self.get_texture('resources/textures/3.png'), - 4: self.get_texture('resources/textures/4.jpg'), - 5: self.get_texture('resources/textures/1.jpg'), + 4: self.get_texture('resources/textures/4.png'), + 5: self.get_texture('resources/textures/1.png'), } \ No newline at end of file diff --git a/player.py b/player.py index 8c28dc3..640da36 100644 --- a/player.py +++ b/player.py @@ -6,12 +6,12 @@ class Player: def __init__(self, game): self.game = game self.x, self.y = PLAYER_POS - self.rot = PLAYER_ROT + self.angle = PLAYER_ROT def movement(self): # Initialize variables - sin_a = math.sin(self.rot) - cos_a = math.cos(self.rot) + sin_a = math.sin(self.angle) + cos_a = math.cos(self.angle) dx, dy = 0, 0 player_speed = PLAYER_SPEED * self.game.delta_time player_speed_sin = player_speed * sin_a @@ -35,12 +35,12 @@ class Player: # set player position self.check_collision(dx, dy) - # player rotation - if keys[pg.K_LEFT]: - self.rot -= PLAYER_ROT_SPEED * self.game.delta_time - if keys[pg.K_RIGHT]: - self.rot += PLAYER_ROT_SPEED * self.game.delta_time - self.rot %= 2 * math.tau # tau = 2 * pi + # player rotation with arrow keys + #if keys[pg.K_LEFT]: + # self.angle -= PLAYER_ROT_SPEED * self.game.delta_time + #if keys[pg.K_RIGHT]: + # self.angle += PLAYER_ROT_SPEED * self.game.delta_time + self.angle %= math.tau # tau = 2 * pi def check_wall(self, x, y): return (x, y) not in self.game.map.world_map @@ -54,8 +54,8 @@ class Player: def draw(self): pg.draw.line(self.game.screen, 'green', (self.x * 100, self.y * 100), - (self.x * 100 + WIDTH * math.cos(self.rot), - self.y * 100 + WIDTH * math.sin(self.rot)), 2) + (self.x * 100 + WIDTH * math.cos(self.angle), + self.y * 100 + WIDTH * math.sin(self.angle)), 2) pg.draw.circle(self.game.screen, 'red', (self.x * 100, self.y * 100), 15) def mouse_control(self): @@ -64,7 +64,7 @@ class Player: pg.mouse.set_pos([HALF_WIDTH, HALF_HEIGHT]) self.rel = pg.mouse.get_rel()[0] self.rel = max(-MOUSE_MAX_SPEED, min(MOUSE_MAX_SPEED, self.rel)) - self.rot += self.rel * MOUSE_SENSITIVITY * self.game.delta_time + self.angle += self.rel * MOUSE_SENSITIVITY * self.game.delta_time def update(self): self.movement() diff --git a/raycasting.py b/raycasting.py index 4f4431f..e3d2be8 100644 --- a/raycasting.py +++ b/raycasting.py @@ -38,7 +38,7 @@ class RayCasting(): texture_vert, texture_horz = 1, 1 - ray_angle = self.game.player.rot - HALF_FOV + 0.0001 # 0.0001 to avoid division by zero + ray_angle = self.game.player.angle - HALF_FOV + 0.0001 # 0.0001 to avoid division by zero for ray in range(NUM_RAYS): sin_a = math.sin(ray_angle) cos_a = math.cos(ray_angle) @@ -90,11 +90,13 @@ class RayCasting(): offset = (1 - x_horz) if sin_a > 0 else x_horz # Draw the ray for debugging - #pg.draw.line(self.game.screen, 'blue', (ox * 100, oy * 100), - # (100 * ox + 100 * depth * cos_a, 100 * oy + 100 * depth * sin_a), 1) + if DEBUG_MODE: + pg.draw.line(self.game.screen, 'blue', (ox * 100, oy * 100), + (100 * ox + 100 * depth * cos_a, 100 * oy + 100 * depth * sin_a), 1) # Remove fish-eye effect - depth *= math.cos(self.game.player.rot - ray_angle) + if FISH_EYE_FIX: + depth *= math.cos(self.game.player.angle - ray_angle) # Projection proj_height = SCREEN_DIST / (depth + 0.0001) # 0.0001 to avoid division by zero diff --git a/resources/sprites/animated_sprites/green_light/0.png b/resources/sprites/animated_sprites/green_light/0.png new file mode 100644 index 0000000..e9a335e Binary files /dev/null and b/resources/sprites/animated_sprites/green_light/0.png differ diff --git a/resources/sprites/animated_sprites/green_light/1.png b/resources/sprites/animated_sprites/green_light/1.png new file mode 100644 index 0000000..90af563 Binary files /dev/null and b/resources/sprites/animated_sprites/green_light/1.png differ diff --git a/resources/sprites/animated_sprites/green_light/3.png b/resources/sprites/animated_sprites/green_light/3.png new file mode 100644 index 0000000..84f08e1 Binary files /dev/null and b/resources/sprites/animated_sprites/green_light/3.png differ diff --git a/resources/sprites/animated_sprites/green_light/4.png b/resources/sprites/animated_sprites/green_light/4.png new file mode 100644 index 0000000..ab4e131 Binary files /dev/null and b/resources/sprites/animated_sprites/green_light/4.png differ diff --git a/resources/sprites/animated_sprites/red_light/0.png b/resources/sprites/animated_sprites/red_light/0.png new file mode 100644 index 0000000..f656b7b Binary files /dev/null and b/resources/sprites/animated_sprites/red_light/0.png differ diff --git a/resources/sprites/animated_sprites/red_light/1.png b/resources/sprites/animated_sprites/red_light/1.png new file mode 100644 index 0000000..4ce104d Binary files /dev/null and b/resources/sprites/animated_sprites/red_light/1.png differ diff --git a/resources/sprites/animated_sprites/red_light/2.png b/resources/sprites/animated_sprites/red_light/2.png new file mode 100644 index 0000000..e1abe9f Binary files /dev/null and b/resources/sprites/animated_sprites/red_light/2.png differ diff --git a/resources/sprites/animated_sprites/red_light/3.png b/resources/sprites/animated_sprites/red_light/3.png new file mode 100644 index 0000000..ebade06 Binary files /dev/null and b/resources/sprites/animated_sprites/red_light/3.png differ diff --git a/resources/sprites/static_sprites/candlebra.png b/resources/sprites/static_sprites/candlebra.png new file mode 100644 index 0000000..1145fcf Binary files /dev/null and b/resources/sprites/static_sprites/candlebra.png differ diff --git a/resources/textures/2.jpg b/resources/textures/2.jpg new file mode 100644 index 0000000..4576f2c Binary files /dev/null and b/resources/textures/2.jpg differ diff --git a/settings.py b/settings.py index 06e6c8f..b10ddd4 100644 --- a/settings.py +++ b/settings.py @@ -4,12 +4,12 @@ import math RES = WIDTH, HEIGHT = 1600, 900 HALF_WIDTH = WIDTH // 2 HALF_HEIGHT = HEIGHT // 2 -FPS = 0 # 0 = unlimited +FPS = 0 # 0 = unlimited -PLAYER_POS = 1.5, 5 # player position on the map +PLAYER_POS = 1.5, 5 # player position on the map PLAYER_ROT = 0 -PLAYER_SPEED = 0.004 -PLAYER_ROT_SPEED = 0.004 +PLAYER_SPEED = 0.002 # player speed +PLAYER_ROT_SPEED = 0.004 # player rotation speed PLAYER_SIZE_SCALE = 60 MOUSE_SENSITIVITY = 0.0003 @@ -30,4 +30,9 @@ SCREEN_DIST = HALF_WIDTH / math.tan(HALF_FOV) SCALE = WIDTH // NUM_RAYS TEXTURE_SIZE = 256 -HALF_TEXTURE_SIZE = TEXTURE_SIZE // 2 \ No newline at end of file +HALF_TEXTURE_SIZE = TEXTURE_SIZE // 2 + + +# DEBUG SETTINGS +DEBUG_MODE = False # show debug info +FISH_EYE_FIX = True # fix fish eye effect \ No newline at end of file diff --git a/sprite_object.py b/sprite_object.py new file mode 100644 index 0000000..5510e15 --- /dev/null +++ b/sprite_object.py @@ -0,0 +1,91 @@ +import pygame as pg +from settings import * +import os +from collections import deque + + +class SpriteObject: + def __init__(self, game, path='resources/sprites/static_sprites/candlebra.png', + pos=(10.5, 3.5), scale=0.7, shift=0.27): + self.game = game + self.player = game.player + self.x, self.y = pos + self.image = pg.image.load(path).convert_alpha() + self.IMAGE_WIDTH = self.image.get_width() + self.IMAGE_HALF_WIDTH = self.image.get_width() // 2 + self.IMAGE_RATIO = self.IMAGE_WIDTH / self.image.get_height() + self.dx, self.dy, self.theta, self.screen_x, self.dist, self.norm_dist = 0, 0, 0, 0, 1, 1 + self.sprite_half_width = 0 + self.SPRITE_SCALE = scale + self.SPRITE_HEIGHT_SHIFT = shift + + def get_sprite_projection(self): + proj = SCREEN_DIST / self.norm_dist * self.SPRITE_SCALE + proj_width, proj_height = proj * self.IMAGE_RATIO, proj + + image = pg.transform.scale(self.image, (proj_width, proj_height)) + + self.sprite_half_width = proj_width // 2 + height_shift = proj_height * self.SPRITE_HEIGHT_SHIFT + pos = self.screen_x - self.sprite_half_width, HALF_HEIGHT - \ + proj_height // 2 + height_shift + + self.game.raycasting.objects_to_render.append( + (self.norm_dist, image, pos)) + + def get_sprite(self): + dx = self.x - self.player.x + dy = self.y - self.player.y + self.dx, self.dy = dx, dy + self.theta = math.atan2(dy, dx) + + delta = self.theta - self.player.angle + if (dx > 0 and self.player.angle > math.pi) or (dx < 0 and dy < 0): + delta += math.tau + + delta_rays = delta / DELTA_ANGLE + self.screen_x = (HALF_NUM_RAYS + delta_rays) * SCALE + + self.dist = math.hypot(dx, dy) + self.norm_dist = self.dist * math.cos(delta) + if -self.IMAGE_HALF_WIDTH < self.screen_x < (WIDTH + self.IMAGE_HALF_WIDTH) and self.norm_dist > 0.5: + self.get_sprite_projection() + + def update(self): + self.get_sprite() + + +class AnimatedSprite(SpriteObject): + def __init__(self, game, path='resources/sprites/animated_sprites/green_light/0.png', + pos=(11.5, 3.5), scale=0.8, shift=0.15, animation_time=120): + super().__init__(game, path, pos, scale, shift) + self.animation_time = animation_time + self.path = path.rsplit('/', 1)[0] + self.images = self.get_images(self.path) + self.animation_time_prev = pg.time.get_ticks() + self.animation_trigger = False + + def update(self): + super().update() + self.check_animation_time() + self.animate(self.images) + + def animate(self, images): + if self.animation_trigger: + images.rotate(-1) + self.image = images[0] + + def check_animation_time(self): + self.animation_trigger = False + time_now = pg.time.get_ticks() + if time_now - self.animation_time_prev > self.animation_time: + self.animation_time_prev = time_now + self.animation_trigger = True + + def get_images(self, path): + images = deque() + for file_name in os.listdir(path): + if os.path.isfile(os.path.join(path, file_name)): + img = pg.image.load(path + '/' + file_name).convert_alpha() + images.append(img) + return images