Add Objects
23
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():
|
||||
|
|
8
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],
|
||||
|
|
28
object_handler.py
Normal file
|
@ -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)
|
|
@ -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'),
|
||||
}
|
24
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()
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
resources/sprites/animated_sprites/green_light/0.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
resources/sprites/animated_sprites/green_light/1.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
resources/sprites/animated_sprites/green_light/3.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
resources/sprites/animated_sprites/green_light/4.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
resources/sprites/animated_sprites/red_light/0.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/sprites/animated_sprites/red_light/1.png
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
resources/sprites/animated_sprites/red_light/2.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/sprites/animated_sprites/red_light/3.png
Normal file
After Width: | Height: | Size: 64 KiB |
BIN
resources/sprites/static_sprites/candlebra.png
Normal file
After Width: | Height: | Size: 243 KiB |
BIN
resources/textures/2.jpg
Normal file
After Width: | Height: | Size: 276 KiB |
13
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
|
||||
|
@ -31,3 +31,8 @@ SCALE = WIDTH // NUM_RAYS
|
|||
|
||||
TEXTURE_SIZE = 256
|
||||
HALF_TEXTURE_SIZE = TEXTURE_SIZE // 2
|
||||
|
||||
|
||||
# DEBUG SETTINGS
|
||||
DEBUG_MODE = False # show debug info
|
||||
FISH_EYE_FIX = True # fix fish eye effect
|
91
sprite_object.py
Normal file
|
@ -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
|