diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..4607abd --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/../../../:\ai-project\.idea/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/ai-project.iml b/.idea/ai-project.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/ai-project.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d1e22ec --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..57157c8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/assets/apple.png b/assets/apple.png new file mode 100644 index 0000000..609435b Binary files /dev/null and b/assets/apple.png differ diff --git a/assets/map1.png b/assets/map1.png new file mode 100644 index 0000000..21920bf Binary files /dev/null and b/assets/map1.png differ diff --git a/assets/map2.png b/assets/map2.png new file mode 100644 index 0000000..90e2f7e Binary files /dev/null and b/assets/map2.png differ diff --git a/assets/map3.png b/assets/map3.png new file mode 100644 index 0000000..11cf3ae Binary files /dev/null and b/assets/map3.png differ diff --git a/assets/map4.png b/assets/map4.png new file mode 100644 index 0000000..1d49162 Binary files /dev/null and b/assets/map4.png differ diff --git a/assets/player.png b/assets/player.png new file mode 100644 index 0000000..3aebbc3 Binary files /dev/null and b/assets/player.png differ diff --git a/assets/stone.png b/assets/stone.png new file mode 100644 index 0000000..a3638f6 Binary files /dev/null and b/assets/stone.png differ diff --git a/assets/tree.png b/assets/tree.png new file mode 100644 index 0000000..78004b9 Binary files /dev/null and b/assets/tree.png differ diff --git a/assets/water.png b/assets/water.png new file mode 100644 index 0000000..d08257a Binary files /dev/null and b/assets/water.png differ diff --git a/assets/wood.png b/assets/wood.png new file mode 100644 index 0000000..993aee0 Binary files /dev/null and b/assets/wood.png differ diff --git a/survival/__init__.py b/survival/__init__.py new file mode 100644 index 0000000..0f7bd0d --- /dev/null +++ b/survival/__init__.py @@ -0,0 +1,44 @@ +import pygame + +from game.game_map import GameMap + +window_width = 1280 +window_height = 720 + + +def draw_game(): + game_map.draw(win) + pygame.display.update() + + +def update_game(pressed_keys): + game_map.update(pressed_keys) + pass + + +if __name__ == '__main__': + pygame.init() + + win = pygame.display.set_mode((window_width, window_height)) + pygame.display.set_caption("AI Project") + + clock = pygame.time.Clock() + + game_map = GameMap(int(window_width/32), int(window_height/32) + 1) + + run = True + + while run: + # Set the framerate + clock.tick(60) + + events = pygame.event.get() + + for event in events: + if event.type == pygame.QUIT: + run = False + + keys = pygame.key.get_pressed() + + draw_game() + update_game(keys) diff --git a/survival/camera.py b/survival/camera.py new file mode 100644 index 0000000..91f4129 --- /dev/null +++ b/survival/camera.py @@ -0,0 +1,16 @@ +from pygame.rect import Rect + + +class Camera: + def __init__(self, width, height): + self.camera = Rect(0, 0, width, height) + self.width = width + self.height = height + + def apply(self, game_object): + return game_object.get_rect().move(self.camera.topleft) + + def update(self, target_object): + x = -target_object.get_rect().x + int(self.width / 2) + y = -target_object.get_rect().y + int(self.height / 2) + self.camera = Rect(x, y, self.width, self.height) diff --git a/survival/game_map.py b/survival/game_map.py new file mode 100644 index 0000000..bea79ad --- /dev/null +++ b/survival/game_map.py @@ -0,0 +1,50 @@ +import os + +import pygame +from pygame.rect import Rect + +from game.player import Player +from game.quad_tree import QuadTree +from game.stone import Stone +from game.tile import Tile + + +class GameMap: + + def __init__(self, width, height): + self.width = width + self.height = height + self.game_objects = [] + self.player = Player() + self.game_objects.append(self.player) + self.tiles = [[Tile() for x in range(width)] for y in range(height)] + self.textures = [pygame.image.load(os.path.join('..', 'assets', 'map1.png')), + pygame.image.load(os.path.join('..', 'assets', 'map2.png')), + pygame.image.load(os.path.join('..', 'assets', 'map3.png')), + pygame.image.load(os.path.join('..', 'assets', 'map4.png'))] + self.game_objects.append(Stone([100, 200])) + self.quad_tree = QuadTree(0, Rect(0, 0, width * 32, height * 32)) + + def draw(self, window): + for y in range(self.height): + for x in range(self.width): + window.blit(self.textures[self.tiles[y][x].background_id], (x * 32, y * 32)) + + for game_object in self.game_objects: + game_object.draw(window) + + def update(self, pressed_keys): + self.quad_tree.clear() + + for game_object in self.game_objects: + self.quad_tree.insert(game_object) + + self.player.update(pressed_keys) + + for game_object in self.game_objects: + possible_colliders = [] + self.quad_tree.retrieve(possible_colliders, game_object) + for collider in possible_colliders: + if game_object.get_rect().colliderect(collider.get_rect()) and game_object != collider: + game_object.velocity = [0, 0] + game_object.pos = game_object.last_pos diff --git a/survival/game_object.py b/survival/game_object.py new file mode 100644 index 0000000..dd27093 --- /dev/null +++ b/survival/game_object.py @@ -0,0 +1,20 @@ +import pygame +from pygame.rect import Rect + + +class GameObject: + + def __init__(self, pos, texture): + self.pos = pos + self.last_pos = pos + self.texture = pygame.image.load(texture) + self.texture = pygame.transform.scale(self.texture, (64, 64)) + self.width = self.texture.get_width() + self.height = self.texture.get_height() + self.velocity = [0, 0] + + def draw(self, window): + window.blit(self.texture, self.pos) + + def get_rect(self): + return Rect(self.pos[0], self.pos[1], self.width, self.height) diff --git a/survival/player.py b/survival/player.py new file mode 100644 index 0000000..116a739 --- /dev/null +++ b/survival/player.py @@ -0,0 +1,34 @@ +import os + +import pygame + +from game.game_object import GameObject + + +class Player(GameObject): + def __init__(self): + super().__init__([0, 0], os.path.join('..', 'assets', 'player.png')) + + def draw(self, window): + super().draw(window) + + def update(self, pressed_keys): + if pressed_keys[pygame.K_LEFT]: + self.velocity[0] = -1 + elif pressed_keys[pygame.K_RIGHT]: + self.velocity[0] = 1 + else: + self.velocity[0] = 0 + + if pressed_keys[pygame.K_DOWN]: + self.velocity[1] = 1 + elif pressed_keys[pygame.K_UP]: + self.velocity[1] = -1 + else: + self.velocity[1] = 0 + + self.last_pos = [self.pos[0], self.pos[1]] + + self.pos[0] += self.velocity[0] + self.pos[1] += self.velocity[1] + diff --git a/survival/quad_tree.py b/survival/quad_tree.py new file mode 100644 index 0000000..44a35ce --- /dev/null +++ b/survival/quad_tree.py @@ -0,0 +1,116 @@ +import pygame +from pygame.rect import Rect + + +class QuadTree: + """ + How to use: + + 1) Create new quadtree: QuadTree(0, Rect(0, 0, width, height)) + 2) Every frame clear the quadtree and insert all objects into it. + 3) Get the possible colliders for each objects and check if they collide. + + for object in objects: + possible_colliders = [] + possible_colliders = quad_tree.retrieve(possible_colliders, object) + + for collider in possible_colliders: + # Check collision here + + """ + MAX_OBJECTS = 10 + MAX_LEVELS = 5 + + def __init__(self, level, bounds): + self.level = level + self.bounds = bounds + self.objects = [] + self.nodes = [] + + def clear(self): + """ + Clears the quadtree recursively. + """ + self.objects.clear() + + for node in self.nodes: + if node is not None: + node.clear() + + def split(self): + """ + Splits the node into 4 sub nodes. + """ + sub_width = int(self.bounds.width / 2) + sub_height = int(self.bounds.height / 2) + x = int(self.bounds.x) + y = int(self.bounds.y) + + self.nodes.append(QuadTree(self.level + 1, Rect(x + sub_width, y, sub_width, sub_height))) + self.nodes.append(QuadTree(self.level + 1, Rect(x, y, sub_width, sub_height))) + self.nodes.append(QuadTree(self.level + 1, Rect(x, y + sub_height, sub_width, sub_height))) + self.nodes.append(QuadTree(self.level + 1, Rect(x + sub_width, y + sub_height, sub_width, sub_height))) + + def get_index(self, rect): + """ + Checks which node the object belongs to. + """ + index = -1 + vertical_point = self.bounds.x + (self.bounds.width / 2) + horizontal_point = self.bounds.y + (self.bounds.height / 2) + + top_quadrant = rect.y < horizontal_point and rect.y + rect.height < horizontal_point + bot_quadrant = rect.y > horizontal_point + + if rect.x < vertical_point and rect.x + rect.width < vertical_point: + if top_quadrant: + index = 1 + elif bot_quadrant: + index = 2 + elif rect.x > vertical_point: + if top_quadrant: + index = 0 + elif bot_quadrant: + index = 3 + + return index + + def insert(self, game_object): + """ + Inserts given game object into the quadtree. + If objects count exceeds the limit the node is split and + all objects are added to their corresponding nodes. + """ + rect = Rect(game_object.pos[0], game_object.pos[1], game_object.width, game_object.height) + if len(self.nodes) > 0: + index = self.get_index(rect) + + if index != -1: + self.nodes[index].insert(rect) + return + + self.objects.append(game_object) + + if len(self.objects) > self.MAX_OBJECTS and self.level < self.MAX_LEVELS: + if len(self.nodes) == 0: + self.split() + + i = 0 + while i < len(self.objects): + index = self.get_index(self.objects[i]) + if index == -1: + i += 1 + else: + self.nodes[index].insert(self.objects.remove(i)) + + def retrieve(self, objects, game_object): + """ + Returns all objects that collide with given rectangle. + """ + rect = game_object.get_rect() + index = self.get_index(rect) + + if index != -1 and len(self.nodes) > 0: + self.nodes[index].retrieve(objects, rect) + + objects.extend(self.objects) diff --git a/survival/stone.py b/survival/stone.py new file mode 100644 index 0000000..66c66bb --- /dev/null +++ b/survival/stone.py @@ -0,0 +1,9 @@ +import os + +from game.game_object import GameObject + + +class Stone(GameObject): + + def __init__(self, pos): + super().__init__(pos, os.path.join('..', 'assets', 'stone.png')) diff --git a/survival/tile.py b/survival/tile.py new file mode 100644 index 0000000..bff4aab --- /dev/null +++ b/survival/tile.py @@ -0,0 +1,7 @@ +import pygame +from random import randrange + + +class Tile: + def __init__(self): + self.background_id = randrange(4)