initial commit #1
8
.idea/.gitignore
vendored
Normal file
@ -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/
|
8
.idea/ai-project.iml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
.idea/inspectionProfiles/profiles_settings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
4
.idea/misc.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/ai-project.iml" filepath="$PROJECT_DIR$/.idea/ai-project.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
BIN
assets/apple.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/map1.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/map2.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/map3.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
assets/map4.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/player.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
assets/stone.png
Normal file
After Width: | Height: | Size: 151 B |
BIN
assets/tree.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/water.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
assets/wood.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
44
survival/__init__.py
Normal file
@ -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)
|
16
survival/camera.py
Normal file
@ -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)
|
50
survival/game_map.py
Normal file
@ -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
|
20
survival/game_object.py
Normal file
@ -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)
|
34
survival/player.py
Normal file
@ -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]
|
||||||
|
|
116
survival/quad_tree.py
Normal file
@ -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)
|
9
survival/stone.py
Normal file
@ -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'))
|
7
survival/tile.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import pygame
|
||||||
|
from random import randrange
|
||||||
|
|
||||||
|
|
||||||
|
class Tile:
|
||||||
|
def __init__(self):
|
||||||
|
self.background_id = randrange(4)
|