├── Animation ├── house.png ├── my_game.py ├── player.py ├── poppy_sheet.json ├── poppy_sheet.png └── spritesheet.py ├── Camera System ├── camera.py ├── house_full.png ├── main_loop.py ├── player.py ├── poppy_sheet.json ├── poppy_sheet.png └── spritesheet.py ├── CustomControls ├── RetroFont.ttf ├── controls.py ├── main.py └── util.py ├── DeepSeekPython ├── Pokemon Data.xlsx ├── deepseek_tutorial.ipynb └── yt comments.csv ├── Framerate Independence └── demo.py ├── Game States ├── assets │ ├── font │ │ └── PressStart2P-vaV7.ttf │ ├── map │ │ ├── cursor.aseprite │ │ ├── cursor.png │ │ ├── grass.png │ │ ├── menu.aseprite │ │ └── menu.png │ └── sprites │ │ └── player │ │ ├── player_back1.png │ │ ├── player_back2.png │ │ ├── player_back3.png │ │ ├── player_back4.png │ │ ├── player_front1.png │ │ ├── player_front2.png │ │ ├── player_front3.png │ │ ├── player_front4.png │ │ ├── player_left1.png │ │ ├── player_left2.png │ │ ├── player_left3.png │ │ ├── player_left4.png │ │ ├── player_right1.png │ │ ├── player_right2.png │ │ ├── player_right3.png │ │ └── player_right4.png ├── game.py └── states │ ├── game_world.py │ ├── party.py │ ├── pause_menu.py │ ├── state.py │ └── title.py ├── Menu System ├── game.py ├── main.py └── menu.py ├── PS4 Controller ├── ps4_keys.json └── test.py ├── Physics ├── main.py ├── player.py ├── spritesheet.json ├── spritesheet.png ├── spritesheet.py ├── test_level.csv └── tiles.py ├── PyRacer ├── Assets │ ├── images │ │ ├── GO.png │ │ ├── PressStart2P-vaV7.ttf │ │ ├── Streetlight1.png │ │ ├── Streetlight2.png │ │ ├── Streetlight3.png │ │ ├── background.png │ │ └── car.png │ └── sounds │ │ ├── go.wav │ │ ├── light.wav │ │ └── racing_song.ogg ├── car.py ├── game.py └── map.py ├── README.md ├── RockPaperScissors ├── art.py └── main.py ├── Switch Sales and Reviews Scrape ├── Switch Sales and Reviews.csv ├── Switch Sales and Reviews.xlsx └── switch_scrape.ipynb ├── TicTacToe ├── helpers.py └── tictactoe.py ├── Tile Collisions ├── main.py ├── player.py ├── spritesheet.json ├── spritesheet.png ├── spritesheet.py ├── test_level.csv └── tiles.py ├── Tilemap ├── main.py ├── spritesheet.json ├── spritesheet.png ├── spritesheet.py ├── test_level.csv └── tiles.py ├── readme images └── logo.gif └── spritesheet ├── spritesheet.py ├── test_game.py ├── trainer_sheet.json ├── trainer_sheet.png ├── trainer_sheet_two.json └── trainer_sheet_two.png /Animation/house.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Animation/house.png -------------------------------------------------------------------------------- /Animation/my_game.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from player import Player 3 | ################################# LOAD UP A BASIC WINDOW AND CLOCK ################################# 4 | pygame.init() 5 | DISPLAY_W, DISPLAY_H = 480, 270 6 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 7 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 8 | running = True 9 | clock = pygame.time.Clock() 10 | house = pygame.image.load('house.png').convert() 11 | ################################# LOAD PLAYER ################################### 12 | cat = Player() 13 | ################################# GAME LOOP ########################## 14 | while running: 15 | clock.tick(60) 16 | ################################# CHECK PLAYER INPUT ################################# 17 | for event in pygame.event.get(): 18 | if event.type == pygame.QUIT: 19 | running = False 20 | if event.type == pygame.KEYDOWN: 21 | if event.key == pygame.K_LEFT: 22 | cat.LEFT_KEY, cat.FACING_LEFT = True, True 23 | elif event.key == pygame.K_RIGHT: 24 | cat.RIGHT_KEY, cat.FACING_LEFT = True, False 25 | if event.type == pygame.KEYUP: 26 | if event.key == pygame.K_LEFT: 27 | cat.LEFT_KEY = False 28 | elif event.key == pygame.K_RIGHT: 29 | cat.RIGHT_KEY = False 30 | 31 | ################################# UPDATE/ Animate SPRITE ################################# 32 | cat.update() 33 | ################################# UPDATE WINDOW AND DISPLAY ################################# 34 | #canvas.fill((255,255,255)) 35 | canvas.blit(house, (0,0)) 36 | cat.draw(canvas) 37 | window.blit(canvas, (0,0)) 38 | pygame.display.update() 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Animation/player.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from spritesheet import Spritesheet 3 | 4 | class Player(pygame.sprite.Sprite): 5 | def __init__(self): 6 | pygame.sprite.Sprite.__init__(self) 7 | self.LEFT_KEY, self.RIGHT_KEY, self.FACING_LEFT = False, False, False 8 | self.load_frames() 9 | self.rect = self.idle_frames_left[0].get_rect() 10 | self.rect.midbottom = (240, 244) 11 | self.current_frame = 0 12 | self.last_updated = 0 13 | self.velocity = 0 14 | self.state = 'idle' 15 | self.current_image = self.idle_frames_left[0] 16 | 17 | def draw(self, display): 18 | display.blit(self.current_image, self.rect) 19 | 20 | def update(self): 21 | self.velocity = 0 22 | if self.LEFT_KEY: 23 | self.velocity = -2 24 | elif self.RIGHT_KEY: 25 | self.velocity = 2 26 | self.rect.x += self.velocity 27 | self.set_state() 28 | self.animate() 29 | 30 | def set_state(self): 31 | self.state = ' idle' 32 | if self.velocity > 0: 33 | self.state = 'moving right' 34 | elif self.velocity < 0: 35 | self.state = 'moving left' 36 | 37 | def animate(self): 38 | now = pygame.time.get_ticks() 39 | if self.state == ' idle': 40 | if now - self.last_updated > 200: 41 | self.last_updated = now 42 | self.current_frame = (self.current_frame + 1) % len(self.idle_frames_left) 43 | if self.FACING_LEFT: 44 | self.current_image = self.idle_frames_left[self.current_frame] 45 | elif not self.FACING_LEFT: 46 | self.current_image = self.idle_frames_right[self.current_frame] 47 | else: 48 | if now - self.last_updated > 100: 49 | self.last_updated = now 50 | self.current_frame = (self.current_frame + 1) % len(self.walking_frames_left) 51 | if self.state == 'moving left': 52 | self.current_image = self.walking_frames_left[self.current_frame] 53 | elif self.state == 'moving right': 54 | self.current_image = self.walking_frames_right[self.current_frame] 55 | 56 | 57 | 58 | def load_frames(self): 59 | my_spritesheet = Spritesheet('poppy_sheet.png') 60 | #pygame.image.load('MY_IMAGE_NAME.png').convert() 61 | self.idle_frames_left = [my_spritesheet.parse_sprite("poppy_idle1.png"), 62 | my_spritesheet.parse_sprite("poppy_idle2.png")] 63 | self.walking_frames_left = [my_spritesheet.parse_sprite("poppywalk1.png"), my_spritesheet.parse_sprite("poppywalk2.png"), 64 | my_spritesheet.parse_sprite("poppywalk3.png"), my_spritesheet.parse_sprite("poppywalk4.png"), 65 | my_spritesheet.parse_sprite("poppywalk5.png"), my_spritesheet.parse_sprite("poppywalk6.png"), 66 | my_spritesheet.parse_sprite("poppywalk7.png"), my_spritesheet.parse_sprite("poppywalk8.png")] 67 | self.idle_frames_right = [] 68 | for frame in self.idle_frames_left: 69 | self.idle_frames_right.append( pygame.transform.flip(frame,True, False) ) 70 | self.walking_frames_right = [] 71 | for frame in self.walking_frames_left: 72 | self.walking_frames_right.append(pygame.transform.flip(frame, True, False)) 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Animation/poppy_sheet.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "poppy_idle1.png": 4 | { 5 | "frame": {"x":62,"y":164,"w":62,"h":41}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 9 | "sourceSize": {"w":62,"h":41} 10 | }, 11 | "poppy_idle2.png": 12 | { 13 | "frame": {"x":62,"y":123,"w":62,"h":41}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 17 | "sourceSize": {"w":62,"h":41} 18 | }, 19 | "poppywalk1.png": 20 | { 21 | "frame": {"x":62,"y":164,"w":62,"h":41}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 25 | "sourceSize": {"w":62,"h":41} 26 | }, 27 | "poppywalk2.png": 28 | { 29 | "frame": {"x":62,"y":82,"w":62,"h":41}, 30 | "rotated": false, 31 | "trimmed": false, 32 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 33 | "sourceSize": {"w":62,"h":41} 34 | }, 35 | "poppywalk3.png": 36 | { 37 | "frame": {"x":62,"y":41,"w":62,"h":41}, 38 | "rotated": false, 39 | "trimmed": false, 40 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 41 | "sourceSize": {"w":62,"h":41} 42 | }, 43 | "poppywalk4.png": 44 | { 45 | "frame": {"x":0,"y":164,"w":62,"h":41}, 46 | "rotated": false, 47 | "trimmed": false, 48 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 49 | "sourceSize": {"w":62,"h":41} 50 | }, 51 | "poppywalk5.png": 52 | { 53 | "frame": {"x":0,"y":123,"w":62,"h":41}, 54 | "rotated": false, 55 | "trimmed": false, 56 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 57 | "sourceSize": {"w":62,"h":41} 58 | }, 59 | "poppywalk6.png": 60 | { 61 | "frame": {"x":0,"y":82,"w":62,"h":41}, 62 | "rotated": false, 63 | "trimmed": false, 64 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 65 | "sourceSize": {"w":62,"h":41} 66 | }, 67 | "poppywalk7.png": 68 | { 69 | "frame": {"x":62,"y":0,"w":62,"h":41}, 70 | "rotated": false, 71 | "trimmed": false, 72 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 73 | "sourceSize": {"w":62,"h":41} 74 | }, 75 | "poppywalk8.png": 76 | { 77 | "frame": {"x":0,"y":41,"w":62,"h":41}, 78 | "rotated": false, 79 | "trimmed": false, 80 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 81 | "sourceSize": {"w":62,"h":41} 82 | }, 83 | "poppywalk9.png": 84 | { 85 | "frame": {"x":0,"y":0,"w":62,"h":41}, 86 | "rotated": false, 87 | "trimmed": false, 88 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 89 | "sourceSize": {"w":62,"h":41} 90 | }}, 91 | "meta": { 92 | "app": "https://www.codeandweb.com/texturepacker", 93 | "version": "1.0", 94 | "image": "poppy_sheet.png", 95 | "format": "RGBA8888", 96 | "size": {"w":124,"h":205}, 97 | "scale": "1", 98 | "smartupdate": "$TexturePacker:SmartUpdate:25e08f38bf8a8a83daef62aa8fe0ad77:4fd91fce2665d1137edcb402e855150a:cd212b300f119d0fbb83f7e46ceafbe8$" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Animation/poppy_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Animation/poppy_sheet.png -------------------------------------------------------------------------------- /Animation/spritesheet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import json 3 | 4 | 5 | class Spritesheet: 6 | def __init__(self, filename): 7 | self.filename = filename 8 | self.sprite_sheet = pygame.image.load(filename).convert() 9 | self.meta_data = self.filename.replace('png', 'json') 10 | with open(self.meta_data) as f: 11 | self.data = json.load(f) 12 | f.close() 13 | 14 | 15 | 16 | def get_sprite(self, x, y, w, h): 17 | sprite = pygame.Surface((w, h)) 18 | sprite.set_colorkey((0,0,0)) 19 | sprite.blit(self.sprite_sheet,(0, 0),(x, y, w, h)) 20 | return sprite 21 | 22 | def parse_sprite(self, name): 23 | sprite = self.data['frames'][name]['frame'] 24 | x, y, w, h = sprite["x"], sprite["y"], sprite["w"], sprite["h"] 25 | image = self.get_sprite(x, y, w, h) 26 | return image 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Camera System/camera.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | vec = pygame.math.Vector2 3 | from abc import ABC, abstractmethod 4 | 5 | class Camera: 6 | def __init__(self, player): 7 | self.player = player 8 | self.offset = vec(0, 0) 9 | self.offset_float = vec(0, 0) 10 | self.DISPLAY_W, self.DISPLAY_H = 480, 270 11 | self.CONST = vec(-self.DISPLAY_W / 2 + player.rect.w / 2, -self.player.ground_y + 20) 12 | 13 | def setmethod(self, method): 14 | self.method = method 15 | 16 | def scroll(self): 17 | self.method.scroll() 18 | 19 | class CamScroll(ABC): 20 | def __init__(self, camera,player): 21 | self.camera = camera 22 | self.player = player 23 | 24 | @abstractmethod 25 | def scroll(self): 26 | pass 27 | 28 | class Follow(CamScroll): 29 | def __init__(self, camera, player): 30 | CamScroll.__init__(self, camera, player) 31 | 32 | def scroll(self): 33 | self.camera.offset_float.x += (self.player.rect.x - self.camera.offset_float.x + self.camera.CONST.x) 34 | self.camera.offset_float.y += (self.player.rect.y - self.camera.offset_float.y + self.camera.CONST.y) 35 | self.camera.offset.x, self.camera.offset.y = int(self.camera.offset_float.x), int(self.camera.offset_float.y) 36 | 37 | class Border(CamScroll): 38 | def __init__(self, camera, player): 39 | CamScroll.__init__(self, camera, player) 40 | 41 | def scroll(self): 42 | self.camera.offset_float.x += (self.player.rect.x - self.camera.offset_float.x + self.camera.CONST.x) 43 | self.camera.offset_float.y += (self.player.rect.y - self.camera.offset_float.y + self.camera.CONST.y) 44 | self.camera.offset.x, self.camera.offset.y = int(self.camera.offset_float.x), int(self.camera.offset_float.y) 45 | self.camera.offset.x = max(self.player.left_border, self.camera.offset.x) 46 | self.camera.offset.x = min(self.camera.offset.x, self.player.right_border - self.camera.DISPLAY_W) 47 | 48 | class Auto(CamScroll): 49 | def __init__(self,camera,player): 50 | CamScroll.__init__(self,camera,player) 51 | 52 | def scroll(self): 53 | self.camera.offset.x += 1 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Camera System/house_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Camera System/house_full.png -------------------------------------------------------------------------------- /Camera System/main_loop.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from player import Player 3 | from camera import * 4 | 5 | ################################# LOAD UP A BASIC WINDOW AND CLOCK ################################# 6 | pygame.init() 7 | DISPLAY_W, DISPLAY_H = 480, 270 8 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 9 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 10 | running = True 11 | clock = pygame.time.Clock() 12 | house = pygame.image.load('house_full.png').convert() 13 | 14 | ################################# LOAD PLAYER AND CAMERA################################### 15 | cat = Player() 16 | camera = Camera(cat) 17 | follow = Follow(camera,cat) 18 | border = Border(camera,cat) 19 | auto = Auto(camera,cat) 20 | camera.setmethod(follow) 21 | ################################# GAME LOOP ########################## 22 | while running: 23 | clock.tick(60) 24 | ################################# CHECK PLAYER INPUT ################################# 25 | for event in pygame.event.get(): 26 | if event.type == pygame.QUIT: 27 | running = False 28 | if event.type == pygame.KEYDOWN: 29 | if event.key == pygame.K_LEFT: 30 | cat.LEFT_KEY, cat.FACING_LEFT = True, True 31 | elif event.key == pygame.K_RIGHT: 32 | cat.RIGHT_KEY, cat.FACING_LEFT = True, False 33 | elif event.key == pygame.K_1: 34 | camera.setmethod(follow) 35 | elif event.key == pygame.K_2: 36 | camera.setmethod(auto) 37 | elif event.key == pygame.K_3: 38 | camera.setmethod(border) 39 | if event.type == pygame.KEYUP: 40 | if event.key == pygame.K_LEFT: 41 | cat.LEFT_KEY = False 42 | elif event.key == pygame.K_RIGHT: 43 | cat.RIGHT_KEY = False 44 | 45 | ################################# UPDATE/ Animate SPRITE ################################# 46 | cat.update() 47 | camera.scroll() 48 | ################################# UPDATE WINDOW AND DISPLAY ################################# 49 | canvas.blit(house, (0 - camera.offset.x, 0 - camera.offset.y)) 50 | canvas.blit(cat.current_image,(cat.rect.x - camera.offset.x, cat.rect.y - camera.offset.y)) 51 | window.blit(canvas, (0,0)) 52 | pygame.display.update() 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Camera System/player.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from spritesheet import Spritesheet 3 | 4 | class Player(pygame.sprite.Sprite): 5 | def __init__(self): 6 | pygame.sprite.Sprite.__init__(self) 7 | self.LEFT_KEY, self.RIGHT_KEY, self.FACING_LEFT = False, False, False 8 | self.load_frames() 9 | self.rect = self.idle_frames_left[0].get_rect() 10 | self.rect.midbottom = (570, 244) 11 | self.current_frame = 0 12 | self.last_updated = 0 13 | self.velocity = 0 14 | self.state = 'idle' 15 | self.current_image = self.idle_frames_left[0] 16 | self.left_border, self.right_border = 250, 1150 17 | self.ground_y = 224 18 | self.box = pygame.Rect(self.rect.x, self.rect.y, self.rect.w * 2, self.rect.h) 19 | self.box.center = self.rect.center 20 | self.passed = False 21 | 22 | def draw(self, display): 23 | display.blit(self.current_image, self.rect) 24 | 25 | def update(self): 26 | self.velocity = 0 27 | if self.LEFT_KEY: 28 | self.velocity = -2 29 | elif self.RIGHT_KEY: 30 | self.velocity = 2 31 | self.rect.x += self.velocity 32 | if self.velocity == 0 and self.passed: 33 | self.passed = False 34 | self.box.center = self.rect.center 35 | if self.rect.x > 1150 - self.rect.w: 36 | self.rect.x = 1150 - self.rect.w 37 | elif self.rect.x < 250: 38 | self.rect.x = 250 39 | self.set_state() 40 | self.animate() 41 | if self.rect.left > self.box.left and self.rect.right < self.box.right : 42 | pass 43 | else: 44 | self.passed = True 45 | if self.rect.left > self.box.left : 46 | self.box.left += self.velocity 47 | elif self.rect.right < self.box.right : 48 | self.box.left += self.velocity 49 | 50 | 51 | def set_state(self): 52 | self.state = ' idle' 53 | if self.velocity > 0: 54 | self.state = 'moving right' 55 | elif self.velocity < 0: 56 | self.state = 'moving left' 57 | 58 | def animate(self): 59 | now = pygame.time.get_ticks() 60 | if self.state == ' idle': 61 | if now - self.last_updated > 200: 62 | self.last_updated = now 63 | self.current_frame = (self.current_frame + 1) % len(self.idle_frames_left) 64 | if self.FACING_LEFT: 65 | self.current_image = self.idle_frames_left[self.current_frame] 66 | elif not self.FACING_LEFT: 67 | self.current_image = self.idle_frames_right[self.current_frame] 68 | else: 69 | if now - self.last_updated > 100: 70 | self.last_updated = now 71 | self.current_frame = (self.current_frame + 1) % len(self.walking_frames_left) 72 | if self.state == 'moving left': 73 | self.current_image = self.walking_frames_left[self.current_frame] 74 | elif self.state == 'moving right': 75 | self.current_image = self.walking_frames_right[self.current_frame] 76 | 77 | 78 | 79 | def load_frames(self): 80 | my_spritesheet = Spritesheet('poppy_sheet.png') 81 | #pygame.image.load('MY_IMAGE_NAME.png').convert() 82 | self.idle_frames_left = [my_spritesheet.parse_sprite("poppy_idle1.png"), 83 | my_spritesheet.parse_sprite("poppy_idle2.png")] 84 | self.walking_frames_left = [my_spritesheet.parse_sprite("poppywalk1.png"), my_spritesheet.parse_sprite("poppywalk2.png"), 85 | my_spritesheet.parse_sprite("poppywalk3.png"), my_spritesheet.parse_sprite("poppywalk4.png"), 86 | my_spritesheet.parse_sprite("poppywalk5.png"), my_spritesheet.parse_sprite("poppywalk6.png"), 87 | my_spritesheet.parse_sprite("poppywalk7.png"), my_spritesheet.parse_sprite("poppywalk8.png")] 88 | self.idle_frames_right = [] 89 | for frame in self.idle_frames_left: 90 | self.idle_frames_right.append( pygame.transform.flip(frame,True, False) ) 91 | self.walking_frames_right = [] 92 | for frame in self.walking_frames_left: 93 | self.walking_frames_right.append(pygame.transform.flip(frame, True, False)) 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Camera System/poppy_sheet.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "poppy_idle1.png": 4 | { 5 | "frame": {"x":62,"y":164,"w":62,"h":41}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 9 | "sourceSize": {"w":62,"h":41} 10 | }, 11 | "poppy_idle2.png": 12 | { 13 | "frame": {"x":62,"y":123,"w":62,"h":41}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 17 | "sourceSize": {"w":62,"h":41} 18 | }, 19 | "poppywalk1.png": 20 | { 21 | "frame": {"x":62,"y":164,"w":62,"h":41}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 25 | "sourceSize": {"w":62,"h":41} 26 | }, 27 | "poppywalk2.png": 28 | { 29 | "frame": {"x":62,"y":82,"w":62,"h":41}, 30 | "rotated": false, 31 | "trimmed": false, 32 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 33 | "sourceSize": {"w":62,"h":41} 34 | }, 35 | "poppywalk3.png": 36 | { 37 | "frame": {"x":62,"y":41,"w":62,"h":41}, 38 | "rotated": false, 39 | "trimmed": false, 40 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 41 | "sourceSize": {"w":62,"h":41} 42 | }, 43 | "poppywalk4.png": 44 | { 45 | "frame": {"x":0,"y":164,"w":62,"h":41}, 46 | "rotated": false, 47 | "trimmed": false, 48 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 49 | "sourceSize": {"w":62,"h":41} 50 | }, 51 | "poppywalk5.png": 52 | { 53 | "frame": {"x":0,"y":123,"w":62,"h":41}, 54 | "rotated": false, 55 | "trimmed": false, 56 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 57 | "sourceSize": {"w":62,"h":41} 58 | }, 59 | "poppywalk6.png": 60 | { 61 | "frame": {"x":0,"y":82,"w":62,"h":41}, 62 | "rotated": false, 63 | "trimmed": false, 64 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 65 | "sourceSize": {"w":62,"h":41} 66 | }, 67 | "poppywalk7.png": 68 | { 69 | "frame": {"x":62,"y":0,"w":62,"h":41}, 70 | "rotated": false, 71 | "trimmed": false, 72 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 73 | "sourceSize": {"w":62,"h":41} 74 | }, 75 | "poppywalk8.png": 76 | { 77 | "frame": {"x":0,"y":41,"w":62,"h":41}, 78 | "rotated": false, 79 | "trimmed": false, 80 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 81 | "sourceSize": {"w":62,"h":41} 82 | }, 83 | "poppywalk9.png": 84 | { 85 | "frame": {"x":0,"y":0,"w":62,"h":41}, 86 | "rotated": false, 87 | "trimmed": false, 88 | "spriteSourceSize": {"x":0,"y":0,"w":62,"h":41}, 89 | "sourceSize": {"w":62,"h":41} 90 | }}, 91 | "meta": { 92 | "app": "https://www.codeandweb.com/texturepacker", 93 | "version": "1.0", 94 | "image": "poppy_sheet.png", 95 | "format": "RGBA8888", 96 | "size": {"w":124,"h":205}, 97 | "scale": "1", 98 | "smartupdate": "$TexturePacker:SmartUpdate:25e08f38bf8a8a83daef62aa8fe0ad77:4fd91fce2665d1137edcb402e855150a:cd212b300f119d0fbb83f7e46ceafbe8$" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Camera System/poppy_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Camera System/poppy_sheet.png -------------------------------------------------------------------------------- /Camera System/spritesheet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import json 3 | 4 | 5 | class Spritesheet: 6 | def __init__(self, filename): 7 | self.filename = filename 8 | self.sprite_sheet = pygame.image.load(filename).convert() 9 | self.meta_data = self.filename.replace('png', 'json') 10 | with open(self.meta_data) as f: 11 | self.data = json.load(f) 12 | f.close() 13 | 14 | 15 | 16 | def get_sprite(self, x, y, w, h): 17 | sprite = pygame.Surface((w, h)) 18 | sprite.set_colorkey((0,0,0)) 19 | sprite.blit(self.sprite_sheet,(0, 0),(x, y, w, h)) 20 | return sprite 21 | 22 | def parse_sprite(self, name): 23 | sprite = self.data['frames'][name]['frame'] 24 | x, y, w, h = sprite["x"], sprite["y"], sprite["w"], sprite["h"] 25 | image = self.get_sprite(x, y, w, h) 26 | return image 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /CustomControls/RetroFont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/CustomControls/RetroFont.ttf -------------------------------------------------------------------------------- /CustomControls/controls.py: -------------------------------------------------------------------------------- 1 | import pygame, sys 2 | from util import write_save 3 | 4 | class Controls_Handler(): 5 | def __init__(self, save): 6 | self.save_file = save 7 | self.curr_block = save["current_profile"] 8 | self.controls = self.save_file["controls"][str(self.curr_block)] 9 | self.setup() 10 | 11 | def update(self, actions): 12 | if self.selected: self.set_new_control() 13 | else: self.navigate_menu(actions) 14 | 15 | def render(self, surface): 16 | self.draw_text(surface, "Control Profile " + str(self.curr_block+1) , 20, pygame.Color((0,0,0)), 480 / 2, 270/8) 17 | self.display_controls(surface, self.save_file["controls"][str(self.curr_block)]) 18 | if self.curr_block == self.save_file["current_profile"]: self.draw_text(surface, "*" , 20, pygame.Color((0,0,0)), 20, 20) 19 | 20 | def navigate_menu(self, actions): 21 | # Move the cursor up and down 22 | if actions["Down"]: self.curr_index = (self.curr_index + 1) % (len(self.save_file["controls"][str(self.curr_block)]) + 1) 23 | if actions["Up"]: self.curr_index = (self.curr_index - 1) % (len(self.save_file["controls"][str(self.curr_block)]) + 1) 24 | # Switch between profiles 25 | if actions["Left"]: self.curr_block = (self.curr_block -1) % len(self.save_file["controls"]) 26 | if actions["Right"]: self.curr_block = (self.curr_block +1) % len(self.save_file["controls"]) 27 | # Handle Selection 28 | if actions["Action1"] or actions["Start"]: 29 | # Set the current profile to be the main one 30 | if self.cursor_dict[self.curr_index] == "Set": 31 | self.controls = self.save_file["controls"][str(self.curr_block)] 32 | self.save_file["current_profile"] = self.curr_block 33 | write_save(self.save_file) 34 | else: 35 | self.selected = True 36 | 37 | def set_new_control(self): 38 | selected_control = self.cursor_dict[self.curr_index] 39 | done = False 40 | while not done: 41 | for event in pygame.event.get(): 42 | if event.type == pygame.QUIT: 43 | done = True 44 | pygame.quit() 45 | sys.exit() 46 | elif event.type == pygame.KEYDOWN: 47 | if event.key == pygame.K_ESCAPE: 48 | done = True 49 | pygame.quit() 50 | sys.exit() 51 | elif event.key not in self.save_file["controls"][str(self.curr_block)].values(): 52 | self.save_file["controls"][str(self.curr_block)][selected_control] = event.key 53 | write_save(self.save_file) 54 | self.selected = False 55 | done = True 56 | 57 | def display_controls(self,surface, controls): 58 | color = (255,13,5) if self.selected else (255,250,239) 59 | pygame.draw.rect(surface, color, (80 , 270/4 - 10 + (self.curr_index*30), 320, 20) ) 60 | i = 0 61 | for control in controls: 62 | self.draw_text(surface, control + ' - ' + pygame.key.name(controls[control]),20, 63 | pygame.Color((0,0,0)), 480 / 2, 270/4 + i) 64 | i += 30 65 | self.draw_text(surface, "Set Current Profile",20, pygame.Color((0,0,0)), 480 / 2, 270/4 + i) 66 | 67 | def setup(self): 68 | self.selected = False 69 | self.font = pygame.font.Font("RetroFont.ttf", 20) 70 | self.cursor_dict = {} 71 | self.curr_index = 0 72 | i = 0 73 | for control in self.controls: 74 | self.cursor_dict[i] = control 75 | i += 1 76 | self.cursor_dict[i] = "Set" 77 | 78 | def draw_text(self,surface, text, size, color, x, y): 79 | text_surface = self.font.render(text, True, color, size) 80 | text_surface.set_colorkey((0,0,0)) 81 | text_rect = text_surface.get_rect() 82 | text_rect.center = (x, y) 83 | surface.blit(text_surface, text_rect) -------------------------------------------------------------------------------- /CustomControls/main.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from util import load_save, reset_keys 3 | from controls import Controls_Handler 4 | 5 | 6 | ################################# LOAD UP A BASIC WINDOW ################################# 7 | pygame.init() 8 | DISPLAY_W, DISPLAY_H = 480, 270 9 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 10 | window = pygame.display.set_mode(((DISPLAY_W * 2,DISPLAY_H * 2))) 11 | running = True 12 | 13 | actions = {"Left": False, "Right": False, "Up": False, "Down": False, "Start": False, "Action1": False} 14 | 15 | ################################ LOAD THE CURRENT SAVE FILE ################################# 16 | save = load_save() 17 | control_handler = Controls_Handler(save) 18 | ################################# GAME LOOP ########################## 19 | while running: 20 | ################################# CHECK PLAYER INPUT ################################# 21 | for event in pygame.event.get(): 22 | if event.type == pygame.QUIT: 23 | running = False 24 | if event.type == pygame.KEYDOWN: 25 | if event.key == pygame.K_ESCAPE: 26 | running = False 27 | if event.key == control_handler.controls['Left']: 28 | actions['Left'] = True 29 | if event.key == control_handler.controls['Right']: 30 | actions['Right'] = True 31 | if event.key == control_handler.controls['Up']: 32 | actions['Up'] = True 33 | if event.key == control_handler.controls['Down']: 34 | actions['Down'] = True 35 | if event.key == control_handler.controls['Start']: 36 | actions['Start'] = True 37 | if event.key == control_handler.controls['Action1']: 38 | actions['Action1'] = True 39 | 40 | if event.type == pygame.KEYUP: 41 | if event.key == control_handler.controls['Left']: 42 | actions['Left'] = False 43 | if event.key == control_handler.controls['Right']: 44 | actions['Right'] = False 45 | if event.key == control_handler.controls['Up']: 46 | actions['Up'] = False 47 | if event.key == control_handler.controls['Down']: 48 | actions['Down'] = False 49 | if event.key == control_handler.controls['Start']: 50 | actions['Start'] = False 51 | if event.key == control_handler.controls['Action1']: 52 | actions['Action1'] = False 53 | 54 | ################################# UPDATE THE GAME ################################# 55 | control_handler.update(actions) 56 | ################################# RENDER WINDOW AND DISPLAY ################################# 57 | canvas.fill((135, 206, 235)) 58 | control_handler.render(canvas) 59 | window.blit(pygame.transform.scale(canvas, (DISPLAY_W * 2,DISPLAY_H * 2) ), (0,0)) 60 | pygame.display.update() 61 | reset_keys(actions) -------------------------------------------------------------------------------- /CustomControls/util.py: -------------------------------------------------------------------------------- 1 | import os, json, pygame 2 | 3 | def load_existing_save(savefile): 4 | with open(os.path.join(savefile), 'r+') as file: 5 | controls = json.load(file) 6 | return controls 7 | 8 | def write_save(data): 9 | with open(os.path.join(os.getcwd(),'save.json'), 'w') as file: 10 | json.dump(data, file) 11 | 12 | def load_save(): 13 | try: 14 | # Save is loaded 15 | save = load_existing_save('save.json') 16 | except: 17 | # No save file, so create one 18 | save = create_save() 19 | write_save(save) 20 | return save 21 | 22 | 23 | def create_save(): 24 | new_save = { 25 | "controls":{ 26 | "0" :{"Left": pygame.K_a, "Right": pygame.K_d, "Up": pygame.K_w, "Down": pygame.K_s, 27 | "Start": pygame.K_RETURN, "Action1": pygame.K_SPACE}, 28 | "1" :{"Left": pygame.K_a, "Right": pygame.K_d, "Up": pygame.K_w, "Down": pygame.K_s, 29 | "Start": pygame.K_RETURN, "Action1": pygame.K_SPACE} 30 | }, 31 | "current_profile": 0 32 | } 33 | 34 | return new_save 35 | 36 | def reset_keys(actions): 37 | for action in actions: 38 | actions[action] = False 39 | return actions -------------------------------------------------------------------------------- /DeepSeekPython/Pokemon Data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/DeepSeekPython/Pokemon Data.xlsx -------------------------------------------------------------------------------- /DeepSeekPython/deepseek_tutorial.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# Install a pip package in the current Jupyter kernel\n", 10 | "# or do it using pip from the command prompt\n", 11 | "import sys\n", 12 | "!{sys.executable} -m pip install ollama" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "from ollama import chat\n", 22 | "from ollama import ChatResponse\n", 23 | "\n", 24 | "response: ChatResponse = chat(model='deepseek-r1', messages=[\n", 25 | " {\n", 26 | " 'role': 'user',\n", 27 | " 'content': 'Why is the sky blue?',\n", 28 | " },\n", 29 | "])\n", 30 | "print(response['message']['content'])\n", 31 | "# or access fields directly from the response object\n", 32 | "print(response.message.content)" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "metadata": {}, 39 | "outputs": [], 40 | "source": [ 41 | "import re\n", 42 | "def ask_deepseek(input_content, system_prompt, deep_think = True, print_log = True):\n", 43 | " response: ChatResponse = chat(model='deepseek-r1', messages=[\n", 44 | " {'role' : 'system', 'content' : system_prompt},\n", 45 | " {'role': 'user','content': input_content}\n", 46 | " ])\n", 47 | " response_text = response['message']['content']\n", 48 | " if print_log: print(response_text)\n", 49 | " # Extract everything inside ... - this is the Deep Think\n", 50 | " think_texts = re.findall(r'(.*?)', response_text, flags=re.DOTALL)\n", 51 | " # Join extracted sections (optional, if multiple sections exist)\n", 52 | " think_texts = \"\\n\\n\".join(think_texts).strip()\n", 53 | " # Exclude the Deep Think, and return the response\n", 54 | " clean_response= re.sub(r'.*?', '', response_text, flags=re.DOTALL).strip()\n", 55 | "\n", 56 | " # Return either the context, or a tuple with the context and deep think\n", 57 | " return clean_response if not deep_think else (clean_response, think_texts)" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "import pandas as pd\n", 67 | "youtube_comments_df = pd.read_csv(r\"YOUR FILE PATH HERE\").head(10)\n", 68 | "youtube_comments_df" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "system_prompt_sentiment = '''You will be provided with a youtube comment. Please rate the Youtube comment on a \n", 78 | "scale of -1 (very negative) to 1 (very positive) where 0 indicates neutral sentiment. Report the scores in increments of 0.1.\n", 79 | "Please only answer with the sentiment score and do not include any other word or explanation before or after the sentiment score.\n", 80 | "Do not include the '+' symbol in the output if the score is positive, but do inlcude the '-' symbol if the score is negative.\n", 81 | "Try your best to look out for humour and sarcasm. Some users may write comments that seem negative on the surface but\n", 82 | "are meant to be endearing. The distinction may not be that easy without the context of the video, but do your best!\n", 83 | "'''\n", 84 | "\n", 85 | "# Ask deepseek to score each comment and place the result in a new column\n", 86 | "#youtube_comments_df['LLM_SENTIMENT'] = youtube_comments_df['TEXT'].apply(lambda comment : ask_deepseek(comment, system_prompt_sentiment, deep_think=False))\n", 87 | "\n", 88 | "# Ask deepseek to score each comment, include the logic, and then place them both in a column\n", 89 | "youtube_comments_df[['LLM_SENTIMENT', 'LLM_DEEP_THINK']] = youtube_comments_df['TEXT'].apply(lambda comment : ask_deepseek(comment, system_prompt_sentiment)).apply(pd.Series)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "youtube_comments_df" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "pokemon_df = pd.read_excel(r\"YOUR FILE PATH HERE\")\n", 108 | "pokemon_df = pokemon_df.head(9)\n", 109 | "pokemon_df\n", 110 | "\n", 111 | "pokemon_df['LLM_INPUT'] = pokemon_df.to_dict(orient='records')\n", 112 | "pokemon_df['LLM_INPUT'] = pokemon_df['LLM_INPUT'].astype(str)\n", 113 | "pokemon_df['LLM_INPUT'][0]" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "system_prompt_summary = \"\"\"Hi! I am building an app for designed around the popular pokemon games. The app is meant for users to scroll through\n", 123 | "each available Pokemon, and look through their various statistics and abilities. Your job is take in an individual Pokemon's data in the form\n", 124 | "of a JSON format. From there, I'd like you to write a 3-5 sentence summary about the Pokemon's battle capabilities. Please address\n", 125 | "what might be their strengths and weaknesses? Thank you! The output should be a string that formatted as a paragraph that should not exceed\n", 126 | "five sentences. Reminder that these stats refer to the Pokemon video games, and not the cards or TV show.\n", 127 | "\n", 128 | "Keep in mind a few things regarding the structure of the input.\n", 129 | "\n", 130 | "+ The name and identifier of the pokemon is in the 'name' field.\n", 131 | "+ The main statistics for pokemon battling are in the 'Attack', 'Defense', 'Sp. Atk', 'Sp. Def', 'Speed' fields\n", 132 | "+ If a pokemon has a mega evolution denoted in the 'has_mega_evolution' column, then it is worth pointing out\n", 133 | "+ The 'evolution' field refers to Pokemon that it evolves into, and should not be confused with the Pokemon currently of interest.\n", 134 | "\"\"\"\n", 135 | "# Ask deepseek to summarize each pokemon's data, include the logic, and then place them both in a column\n", 136 | "pokemon_df[['LLM_SUMMARY', 'LLM_DEEP_THINK']] =pokemon_df['LLM_INPUT'].apply(lambda data : ask_deepseek(data, system_prompt_summary)).apply(pd.Series)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "pokemon_df\n", 146 | "print(pokemon_df['LLM_SUMMARY'][6])" 147 | ] 148 | } 149 | ], 150 | "metadata": { 151 | "kernelspec": { 152 | "display_name": "scraper_tutorial_env", 153 | "language": "python", 154 | "name": "python3" 155 | }, 156 | "language_info": { 157 | "codemirror_mode": { 158 | "name": "ipython", 159 | "version": 3 160 | }, 161 | "file_extension": ".py", 162 | "mimetype": "text/x-python", 163 | "name": "python", 164 | "nbconvert_exporter": "python", 165 | "pygments_lexer": "ipython3", 166 | "version": "3.11.3" 167 | } 168 | }, 169 | "nbformat": 4, 170 | "nbformat_minor": 2 171 | } 172 | -------------------------------------------------------------------------------- /DeepSeekPython/yt comments.csv: -------------------------------------------------------------------------------- 1 | VIDEO_ID,TEXT,PUBLISHED_AT 2 | b_DkQrJxpck,Terrific video on an important topic. Very helpful.,2024-06-18T18:11:26Z 3 | b_DkQrJxpck,"Thank you very much, bro! 4 | Excellent and clearly understandable example",2024-05-12T20:29:20Z 5 | b_DkQrJxpck,"man I have rewatched this video so many times this weekend, you made me change my whole code 6 | this video literally took me out of tutorial hell, 100% recommended 7 | I never understood inheritance and how to pass a whole game object into another engine object until now... you made me convert all of my noob code into very concise functions... 8 | this is one of the best tutorials and channels I've found so far about pygame, thanks! please keep the videos coming you're an amazing teacher!",2024-02-19T19:38:31Z 9 | b_DkQrJxpck,"Hey friend, how do I create usable items, like potions, in PyGame?",2024-02-01T13:38:41Z 10 | b_DkQrJxpck,Awesome info! Thank you so much :),2023-08-25T06:04:08Z 11 | b_DkQrJxpck,Great tutorial! Do you have any idea how to add a txt map to the game world?,2023-03-10T14:42:51Z 12 | b_DkQrJxpck,WOAH!,2023-02-05T17:01:58Z 13 | b_DkQrJxpck,"Thanks a lot for this! I 've been learning to program with pygame and its been hard. Your explanations are good and i just changed it a little to fit my first project. 14 | I also run self.state_stack[-1].events() in main loop events so i can control diferent set of keys :)",2023-02-01T15:42:36Z 15 | b_DkQrJxpck,Your tutorials are the best I’ve seen!!,2023-01-09T23:52:49Z 16 | b_DkQrJxpck,"Honestly one of the worst tutorials for someone who has already been building a game for a while, hard to implement and pair up variables from your specific use case, to mine.",2023-01-09T17:42:09Z 17 | b_DkQrJxpck,thank you so much for this video! you are the best!!,2022-11-26T15:51:06Z 18 | b_DkQrJxpck,"Excellent tutorial! I've been looking into game screens using state pattern and this is really helpful. 19 | 20 | The only thing I missed was a state diagram showing the state and their transitions, without it it's too magical (too much on-the-mind vs on-the-world knowledge) to know where to put the key handlers + enter_state calls.",2022-08-24T16:50:37Z 21 | b_DkQrJxpck,it doesn't work can u help me ? i want to change the player where can I found it ? and I want to add more menu,2022-08-01T07:47:49Z 22 | b_DkQrJxpck,"This really brings some structure into my game loop. But I still have 3 questions: 23 | 1. What do we use the game.state_stack variable for? I understand that it represents the state stack, but why do we need it when all the state instances are chained together? 24 | 2. Why do we check if the state stack's length is > 1 before we add the entering state on top? Wouldn't that mean that if its length is exactly 1, nothing would ever be added? 25 | 3. Game.update() only says ""pass"" - how is control transferred to the various states?",2022-07-05T19:27:17Z 26 | b_DkQrJxpck,"Great tutorial and explanation! I have a quick question: using this OOP template, how would one add text to menus?",2022-06-24T23:44:40Z 27 | b_DkQrJxpck,Thank you! What an amazing tutorial.,2022-03-31T13:53:29Z 28 | b_DkQrJxpck,"Very thx you for your tutorials. 29 | Very clean/clear. I appreciate to observe your organisation/structuration of your code too. 30 | Thx a lot !!",2022-03-14T09:44:41Z 31 | b_DkQrJxpck,I watched your video on how to create a tilemap but how would i implement it into something like this,2022-02-22T23:09:02Z 32 | b_DkQrJxpck,OMG I am currently participating in a game jam and your tutorials are SO USEFUL !!! thank you so much for doing them ! you deserve so much more views !,2022-02-08T20:06:44Z 33 | b_DkQrJxpck,"I did this and now idk how to continue, are any videos continuing parts of this one?",2022-02-03T19:07:17Z 34 | b_DkQrJxpck,"What is 'states.state'. It doesnt seem to be working for me. saying something like 35 | ' from states.Title import Title 36 | 37 | 38 | 39 | ModuleNotFoundError: No module named 'states.Title'; 'states' is not a package'",2021-12-07T20:09:27Z 40 | b_DkQrJxpck,underrated.,2021-11-28T11:24:59Z 41 | b_DkQrJxpck,What a great video!,2021-11-16T07:35:01Z 42 | b_DkQrJxpck,"Hey Christian, thank you for all that videos, they was very helpful for me. 43 | I'm creating the Plants vs Zombies in Pygame. It's almost done, but I need to implement a leaderboard function in this project, using SQLite (DataBase). 44 | Can you have any idea to how can I create this ? 45 | 46 | Thanks for the help!!",2021-11-02T19:02:37Z 47 | b_DkQrJxpck,"Thanks for this. Can I ask, how to apply collision for the player?",2021-10-22T08:35:35Z 48 | b_DkQrJxpck,"This is fantastic!!! I wish you would make a series around this simple concept. Like two small tile maps (or images) that you toggle between when you reach an edge (is that a new state? ). Or using text with a changing menu box size. Ex. If you expand magic maybe you’ve earned fire so one size box and later get healing. How do you automatically change the menu size instead of manually coding a different box. Or making a long string of text auto return inside a standard box for dialogue. 49 | 50 | I think your videos are best because it feels like you are setting us up to make large polished complicated games.",2021-09-04T18:28:06Z 51 | b_DkQrJxpck,"Awesome video! Question though, you wrote from states.state import State in the title.py module because it refers to the top level script?",2021-08-02T10:36:55Z 52 | b_DkQrJxpck,"I love your tutorials , they are very clear and helpful for newbies, could you make a inventory system tutorial in pygame ? Thanks",2021-07-23T15:24:53Z 53 | b_DkQrJxpck,Really helpful thank you. Hope to see more soon,2021-07-22T11:13:32Z 54 | b_DkQrJxpck,"please make a video on how to create mario game with pygame . Pleaseee, i need you help",2021-07-07T08:56:01Z 55 | b_DkQrJxpck,love your content! thanks a bunch.,2021-06-26T22:01:54Z 56 | b_DkQrJxpck,"I was pausing and typing in your code along with the video. I'm using Sublime, so I'm running the game from the terminal. I kept getting a black screen, so I started searching for errors. I gave up and just copy and pasted all your code. I'm still just getting a blank pygame window when I run it. Any suggestions?",2021-06-25T05:50:33Z 57 | b_DkQrJxpck,Why does it have so few views?? I think everyone should learn from this video,2021-06-24T14:33:33Z 58 | b_DkQrJxpck,"I love the words "" hi there "" so much 🤣🤣🤣 it's so cute",2021-06-24T14:31:46Z 59 | b_DkQrJxpck,I was finding this type of vidoe only,2021-06-23T06:37:47Z 60 | b_DkQrJxpck,"Epic 61 | 62 | 63 | 64 | 65 | I have no idea about anything you said in this video.",2021-06-23T00:57:06Z 66 | b_DkQrJxpck,Oh my god this video was amazing,2021-06-23T00:20:37Z 67 | Q6CCdCBVypg,"Hey everybody! In the spirit of learning, here are some ideas you can try and implement if you finished the video and are looking to add a bit more to this project. 68 | 69 | Exercise: Allow the players to put in their names so it doesn't just say ""Player 1"" and ""Player 2"" 70 | Exercise: Add a timer to the game so that each player only has a certain amount of time to make a decision (hint: checkout the built in ""time"" module) 71 | Exercise: Add an option to replay the game when it ends. Also add a game counter to keep track of how many times each player has won. 72 | 73 | Challenge Exercise: Add a single player mode with some AI functionality for the second player. It doesn't need to be a fancy algorithm, just think about how you can have the program make a valid move. Once you get it working, see if you can come up with something better! 74 | 75 | 76 | Happy coding!",2022-08-10T06:46:48Z 77 | Q6CCdCBVypg,i don't know if this is begginer tutorial. There are lots of lambda functions to save lines.,2024-12-06T20:06:28Z 78 | Q6CCdCBVypg,"It was a great tutorial, very easy to follow. But one thing i am not able to grasp completely. Why did we call the check_turn function to check who the winner is? I mean the check turn function only returns the number of turn. Or what i am able to understand is, that as soon as the player 1 (let's say) is done with their final winning turn, this function return that and the winner is declared! please help me understand this. thankyou! 79 | And once again, great tutorial!",2024-10-21T10:10:44Z 80 | Q6CCdCBVypg,Thank you,2024-07-23T15:06:16Z 81 | Q6CCdCBVypg,Thank you,2024-07-07T11:35:24Z 82 | Q6CCdCBVypg,yo is that visual code? or smth cuz i see it familiar,2024-05-12T16:52:47Z 83 | Q6CCdCBVypg,Amazing,2024-05-05T12:46:37Z 84 | Q6CCdCBVypg,such an awesome tutorial.,2024-04-27T06:42:24Z 85 | Q6CCdCBVypg,thank you for an amazing video. However im a little confused about line 19 in main file. How does that check if there is an X or O. The last part isnt even a dictionary. Id appreciate a response thanks :),2024-01-04T03:11:47Z 86 | Q6CCdCBVypg,Which application are you using??,2023-12-23T14:06:22Z 87 | Q6CCdCBVypg,"""churn"" lol",2023-12-03T10:02:25Z 88 | Q6CCdCBVypg,"its not working, at 2:03 draw_board has a red line underneath it so it isnt working im doing everything the video tells me to",2023-11-09T13:14:22Z 89 | Q6CCdCBVypg,This is a well put together tutorial video. Thanks for making it!,2023-10-21T17:55:44Z 90 | Q6CCdCBVypg,I don't understand how prev_turn == turn results in an invalid entry. Can someone help me understand?,2023-09-24T05:12:21Z 91 | Q6CCdCBVypg,Ok always x wins.. 🤣,2023-07-03T22:24:43Z 92 | Q6CCdCBVypg,amazing video!! 3:11 which keys did you use to convert them instantly? or was it cropped part of the video?,2023-06-14T12:42:43Z 93 | Q6CCdCBVypg,"This is amazing, thanks",2023-04-28T16:49:52Z 94 | Q6CCdCBVypg,i have just started python and it was very good experience thanks,2023-04-04T10:49:23Z 95 | Q6CCdCBVypg,Thank you! This was a great learning experience for me to follow along with and rebuild. Taught me some new tricks as a beginner to Python and was useful for fulfilling a project requirement for my course work.,2023-02-20T21:46:51Z 96 | Q6CCdCBVypg,Thank you for the video. I tweaked the code so that you could choose 1 or 2 players and play the computer.,2023-01-02T03:45:37Z 97 | Q6CCdCBVypg,can it be run on vs code?,2022-12-27T07:31:13Z 98 | Q6CCdCBVypg,"Hi! I think your logic is great here! I am very much a beginner in python and am curious if you have any *hints* to implemting another game play? does turn = 0 need to be reset somehow? 99 | I am trying to implent best 2/3 gameplay to your code. I feel very far off. I started as: 100 | 101 | if finished: 102 | if what_turn(turn) == 'X': 103 | print(""Player1 is the Winner!"") 104 | else: 105 | print(""Player2 is the Winner!"") 106 | else: 107 | print(""NO game!"") 108 | print('Do you want to play again?(Y/N)') 109 | play_again = input().lower() 110 | gameplay = True 111 | if play_again == 'n': 112 | gameplay= False 113 | print('\nThanks for playing!')",2022-12-09T13:40:35Z 114 | Q6CCdCBVypg,my code isn't that great (it's almost twice as long as yours) but the game works perfectly.,2022-12-03T20:10:00Z 115 | Q6CCdCBVypg,"Omg, i really need to learn a lot. I tried to do this exercise by my self and ended up contemplating bout suicide, I wish I was a pro like you at problem solving",2022-11-14T22:31:22Z 116 | Q6CCdCBVypg,bruh you literally destroy programmer who are making hour length video just for this simple code,2022-09-29T16:51:28Z 117 | Q6CCdCBVypg,there's a correction in the exit section if choice=='q' it should'nt be playing =false instead ......if choice =='q' exit(),2022-08-15T09:43:27Z 118 | Q6CCdCBVypg,Thank you!,2022-07-05T18:19:35Z 119 | Q6CCdCBVypg,Your turtorials are amazing! You deserve more support!,2022-04-30T12:22:59Z 120 | Q6CCdCBVypg,Underrated,2022-04-23T03:21:11Z 121 | Q6CCdCBVypg,Fantastic work!,2022-04-06T04:44:48Z 122 | Q6CCdCBVypg,Great to see you back! You are the best Pygame youtuber and your tutorials help me a TON.,2022-04-06T01:25:19Z 123 | Q6CCdCBVypg,"Good to see you back! You still have, by far, the best tutorial on the Game States I've ever seen.",2022-04-05T23:35:12Z 124 | Q6CCdCBVypg,Wow so cool,2022-04-05T22:53:18Z 125 | -------------------------------------------------------------------------------- /Framerate Independence/demo.py: -------------------------------------------------------------------------------- 1 | import pygame, time 2 | 3 | ################################# LOAD UP A BASIC WINDOW AND CLOCK ################################# 4 | pygame.init() 5 | pygame.font.init() 6 | font = pygame.font.SysFont('Calibri',40) 7 | DISPLAY_W, DISPLAY_H = 480, 270 8 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 9 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 10 | running = True 11 | clock = pygame.time.Clock() 12 | TARGET_FPS = 60 13 | WHITE = (255,255,255) 14 | ################################# LOAD VARIABLES AND OBJECTS################################### 15 | rect_pos = 0 16 | timer = 0 17 | velocity = 300 18 | prev_time = time.time() 19 | dt = 0 20 | record = 0 21 | passed, start = False, False 22 | FPS = 60 23 | #TARGET_FPS = 60 24 | ################################# GAME LOOP ########################## 25 | while running: 26 | # Limit framerate 27 | clock.tick(FPS) 28 | # Compute delta time 29 | now = time.time() 30 | dt = now - prev_time 31 | prev_time = now 32 | 33 | # Update the timer and move the rectangle 34 | if start: 35 | timer += dt 36 | rect_pos += velocity *dt 37 | ################################# CHECK PLAYER INPUT ################################# 38 | for event in pygame.event.get(): 39 | if event.type == pygame.QUIT: 40 | running = False 41 | if event.type == pygame.KEYDOWN: 42 | if event.key == pygame.K_SPACE: 43 | start = True 44 | 45 | if rect_pos > DISPLAY_W and not passed: 46 | record = timer 47 | passed = True 48 | ################################# UPDATE/ Animate SPRITE ################################# 49 | countdown = font.render("Time: " +str(round(timer,5)), False, (255,255,255)) 50 | 51 | fps_text = font.render("FPS: " +str(round(clock.get_fps(),2)), False, (255,255,255)) 52 | ################################# UPDATE WINDOW AND DISPLAY ################################# 53 | canvas.fill((0, 0, 0)) # Fills the entire screen with light blue 54 | canvas.blit(countdown, (0,0)) 55 | canvas.blit(fps_text, (0, 50)) 56 | pygame.draw.rect(canvas,WHITE,(rect_pos,DISPLAY_H/2 + 30,40,40)) 57 | if record: 58 | record_text = font.render("Time: " +str(round(record,5)), False, (255,255,255)) 59 | canvas.blit(record_text, (DISPLAY_W/4, DISPLAY_H/2)) 60 | window.blit(canvas, (0,0)) 61 | pygame.display.update() 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Game States/assets/font/PressStart2P-vaV7.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/font/PressStart2P-vaV7.ttf -------------------------------------------------------------------------------- /Game States/assets/map/cursor.aseprite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/map/cursor.aseprite -------------------------------------------------------------------------------- /Game States/assets/map/cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/map/cursor.png -------------------------------------------------------------------------------- /Game States/assets/map/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/map/grass.png -------------------------------------------------------------------------------- /Game States/assets/map/menu.aseprite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/map/menu.aseprite -------------------------------------------------------------------------------- /Game States/assets/map/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/map/menu.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_back1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_back1.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_back2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_back2.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_back3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_back3.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_back4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_back4.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_front1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_front1.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_front2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_front2.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_front3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_front3.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_front4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_front4.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_left1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_left1.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_left2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_left2.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_left3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_left3.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_left4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_left4.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_right1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_right1.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_right2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_right2.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_right3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_right3.png -------------------------------------------------------------------------------- /Game States/assets/sprites/player/player_right4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Game States/assets/sprites/player/player_right4.png -------------------------------------------------------------------------------- /Game States/game.py: -------------------------------------------------------------------------------- 1 | import os, time, pygame 2 | # Load our scenes 3 | from states.title import Title 4 | 5 | class Game(): 6 | def __init__(self): 7 | pygame.init() 8 | self.GAME_W,self.GAME_H = 480, 270 9 | self.SCREEN_WIDTH,self.SCREEN_HEIGHT = 960, 540 10 | self.game_canvas = pygame.Surface((self.GAME_W,self.GAME_H)) 11 | self.screen = pygame.display.set_mode((self.SCREEN_WIDTH,self.SCREEN_HEIGHT)) 12 | self.running, self.playing = True, True 13 | self.actions = {"left": False, "right": False, "up" : False, "down" : False, "action1" : False, "action2" : False, "start" : False} 14 | self.dt, self.prev_time = 0, 0 15 | self.state_stack = [] 16 | self.load_assets() 17 | self.load_states() 18 | 19 | def game_loop(self): 20 | while self.playing: 21 | self.get_dt() 22 | self.get_events() 23 | self.update() 24 | self.render() 25 | 26 | def get_events(self): 27 | for event in pygame.event.get(): 28 | if event.type == pygame.QUIT: 29 | self.playing = False 30 | self.running = False 31 | if event.type == pygame.KEYDOWN: 32 | if event.key == pygame.K_ESCAPE: 33 | self.playing = False 34 | self.running = False 35 | if event.key == pygame.K_a: 36 | self.actions['left'] = True 37 | if event.key == pygame.K_d: 38 | self.actions['right'] = True 39 | if event.key == pygame.K_w: 40 | self.actions['up'] = True 41 | if event.key == pygame.K_s: 42 | self.actions['down'] = True 43 | if event.key == pygame.K_p: 44 | self.actions['action1'] = True 45 | if event.key == pygame.K_o: 46 | self.actions['action2'] = True 47 | if event.key == pygame.K_RETURN: 48 | self.actions['start'] = True 49 | 50 | if event.type == pygame.KEYUP: 51 | if event.key == pygame.K_a: 52 | self.actions['left'] = False 53 | if event.key == pygame.K_d: 54 | self.actions['right'] = False 55 | if event.key == pygame.K_w: 56 | self.actions['up'] = False 57 | if event.key == pygame.K_s: 58 | self.actions['down'] = False 59 | if event.key == pygame.K_p: 60 | self.actions['action1'] = False 61 | if event.key == pygame.K_o: 62 | self.actions['action2'] = False 63 | if event.key == pygame.K_RETURN: 64 | self.actions['start'] = False 65 | 66 | def update(self): 67 | self.state_stack[-1].update(self.dt,self.actions) 68 | 69 | def render(self): 70 | self.state_stack[-1].render(self.game_canvas) 71 | # Render current state to the screen 72 | self.screen.blit(pygame.transform.scale(self.game_canvas,(self.SCREEN_WIDTH, self.SCREEN_HEIGHT)), (0,0)) 73 | pygame.display.flip() 74 | 75 | 76 | def get_dt(self): 77 | now = time.time() 78 | self.dt = now - self.prev_time 79 | self.prev_time = now 80 | 81 | def draw_text(self, surface, text, color, x, y): 82 | text_surface = self.font.render(text, True, color) 83 | #text_surface.set_colorkey((0,0,0)) 84 | text_rect = text_surface.get_rect() 85 | text_rect.center = (x, y) 86 | surface.blit(text_surface, text_rect) 87 | 88 | def load_assets(self): 89 | # Create pointers to directories 90 | self.assets_dir = os.path.join("assets") 91 | self.sprite_dir = os.path.join(self.assets_dir, "sprites") 92 | self.font_dir = os.path.join(self.assets_dir, "font") 93 | self.font= pygame.font.Font(os.path.join(self.font_dir, "PressStart2P-vaV7.ttf"), 20) 94 | 95 | def load_states(self): 96 | self.title_screen = Title(self) 97 | self.state_stack.append(self.title_screen) 98 | 99 | def reset_keys(self): 100 | for action in self.actions: 101 | self.actions[action] = False 102 | 103 | 104 | if __name__ == "__main__": 105 | g = Game() 106 | while g.running: 107 | g.game_loop() -------------------------------------------------------------------------------- /Game States/states/game_world.py: -------------------------------------------------------------------------------- 1 | import pygame, os 2 | from states.state import State 3 | from states.pause_menu import PauseMenu 4 | 5 | class Game_World(State): 6 | def __init__(self, game): 7 | State.__init__(self,game) 8 | self.player = Player(self.game) 9 | self.grass_img = pygame.image.load(os.path.join(self.game.assets_dir, "map", "grass.png")) 10 | 11 | def update(self,delta_time, actions): 12 | # Check if the game was paused 13 | if actions["start"]: 14 | new_state = PauseMenu(self.game) 15 | new_state.enter_state() 16 | self.player.update(delta_time, actions) 17 | def render(self, display): 18 | display.blit(self.grass_img, (0,0)) 19 | self.player.render(display) 20 | 21 | 22 | class Player(): 23 | def __init__(self,game): 24 | self.game = game 25 | self.load_sprites() 26 | self.position_x, self.position_y = 200,200 27 | self.current_frame, self.last_frame_update = 0,0 28 | 29 | 30 | def update(self,delta_time, actions): 31 | # Get the direction from inputs 32 | direction_x = actions["right"] - actions["left"] 33 | direction_y = actions["down"] - actions["up"] 34 | # Update the position 35 | self.position_x += 100 * delta_time * direction_x 36 | self.position_y += 100 * delta_time * direction_y 37 | # Animate the sprite 38 | self.animate(delta_time,direction_x,direction_y) 39 | 40 | 41 | def render(self, display): 42 | display.blit(self.curr_image, (self.position_x,self.position_y)) 43 | 44 | def animate(self, delta_time, direction_x, direction_y): 45 | # Compute how much time has passed since the frame last updated 46 | self.last_frame_update += delta_time 47 | # If no direction is pressed, set image to idle and return 48 | if not (direction_x or direction_y): 49 | self.curr_image = self.curr_anim_list[0] 50 | return 51 | # If an image was pressed, use the appropriate list of frames according to direction 52 | if direction_x: 53 | if direction_x > 0: self.curr_anim_list = self.right_sprites 54 | else: self.curr_anim_list = self.left_sprites 55 | if direction_y: 56 | if direction_y > 0: self.curr_anim_list = self.front_sprites 57 | else: self.curr_anim_list = self.back_sprites 58 | # Advance the animation if enough time has elapsed 59 | if self.last_frame_update > .15: 60 | self.last_frame_update = 0 61 | self.current_frame = (self.current_frame +1) % len(self.curr_anim_list) 62 | self.curr_image = self.curr_anim_list[self.current_frame] 63 | 64 | def load_sprites(self): 65 | # Get the diretory with the player sprites 66 | self.sprite_dir = os.path.join(self.game.sprite_dir, "player") 67 | self.front_sprites, self.back_sprites, self.right_sprites, self.left_sprites = [],[],[],[] 68 | # Load in the frames for each direction 69 | for i in range(1,5): 70 | self.front_sprites.append(pygame.image.load(os.path.join(self.sprite_dir, "player_front" + str(i) +".png"))) 71 | self.back_sprites.append(pygame.image.load(os.path.join(self.sprite_dir, "player_back" + str(i) +".png"))) 72 | self.right_sprites.append(pygame.image.load(os.path.join(self.sprite_dir, "player_right" + str(i) +".png"))) 73 | self.left_sprites.append(pygame.image.load(os.path.join(self.sprite_dir, "player_left" + str(i) +".png"))) 74 | # Set the default frames to facing front 75 | self.curr_image = self.front_sprites[0] 76 | self.curr_anim_list = self.front_sprites -------------------------------------------------------------------------------- /Game States/states/party.py: -------------------------------------------------------------------------------- 1 | import pygame, os 2 | from states.state import State 3 | 4 | class PartyMenu(State): 5 | def __init__(self, game): 6 | self.game = game 7 | State.__init__(self, game) 8 | 9 | def update(self, delta_time, actions): 10 | if actions["action2"]: 11 | self.exit_state() 12 | self.game.reset_keys() 13 | 14 | def render(self, display): 15 | display.fill((255,255,255)) 16 | self.game.draw_text(display, "PARTY MENU GOES HERE", (0,0,0), self.game.GAME_W/2, self.game.GAME_H/2 ) 17 | 18 | -------------------------------------------------------------------------------- /Game States/states/pause_menu.py: -------------------------------------------------------------------------------- 1 | import pygame, os 2 | from states.state import State 3 | from states.party import PartyMenu 4 | 5 | class PauseMenu(State): 6 | def __init__(self, game): 7 | self.game = game 8 | State.__init__(self, game) 9 | # Set the menu 10 | self.menu_img = pygame.image.load(os.path.join(self.game.assets_dir, "map", "menu.png")) 11 | self.menu_rect = self.menu_img.get_rect() 12 | self.menu_rect.center = (self.game.GAME_W*.85, self.game.GAME_H*.4) 13 | # Set the cursor and menu states 14 | self.menu_options = {0 :"Party", 1 : "Items", 2 :"Magic", 3 : "Exit"} 15 | self.index = 0 16 | self.cursor_img = pygame.image.load(os.path.join(self.game.assets_dir, "map", "cursor.png")) 17 | self.cursor_rect = self.cursor_img.get_rect() 18 | self.cursor_pos_y = self.menu_rect.y + 38 19 | self.cursor_rect.x, self.cursor_rect.y = self.menu_rect.x + 10, self.cursor_pos_y 20 | 21 | def update(self, delta_time, actions): 22 | self.update_cursor(actions) 23 | if actions["action1"]: 24 | self.transition_state() 25 | if actions["action2"]: 26 | self.exit_state() 27 | self.game.reset_keys() 28 | 29 | def render(self, display): 30 | # render the gameworld behind the menu, which is right before the pause menu on the stack 31 | #self.game.state_stack[-2].render(display) 32 | self.prev_state.render(display) 33 | display.blit(self.menu_img, self.menu_rect) 34 | display.blit(self.cursor_img, self.cursor_rect) 35 | 36 | def transition_state(self): 37 | if self.menu_options[self.index] == "Party": 38 | new_state = PartyMenu(self.game) 39 | new_state.enter_state() 40 | elif self.menu_options[self.index] == "Items": 41 | pass # TO-DO 42 | elif self.menu_options[self.index] == "Magic": 43 | pass # TO-DO 44 | elif self.menu_options[self.index] == "Exit": 45 | while len(self.game.state_stack) > 1: 46 | self.game.state_stack.pop() 47 | 48 | 49 | def update_cursor(self, actions): 50 | if actions['down']: 51 | self.index = (self.index + 1) % len(self.menu_options) 52 | elif actions['up']: 53 | self.index = (self.index - 1) % len(self.menu_options) 54 | self.cursor_rect.y = self.cursor_pos_y + (self.index * 32) 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Game States/states/state.py: -------------------------------------------------------------------------------- 1 | class State(): 2 | def __init__(self, game): 3 | self.game = game 4 | self.prev_state = None 5 | 6 | def update(self, delta_time, actions): 7 | pass 8 | def render(self, surface): 9 | pass 10 | 11 | def enter_state(self): 12 | if len(self.game.state_stack) > 1: 13 | self.prev_state = self.game.state_stack[-1] 14 | self.game.state_stack.append(self) 15 | 16 | def exit_state(self): 17 | self.game.state_stack.pop() -------------------------------------------------------------------------------- /Game States/states/title.py: -------------------------------------------------------------------------------- 1 | from states.state import State 2 | from states.game_world import Game_World 3 | 4 | class Title(State): 5 | def __init__(self, game): 6 | State.__init__(self, game) 7 | 8 | 9 | def update(self, delta_time, actions): 10 | if actions["start"]: 11 | new_state = Game_World(self.game) 12 | new_state.enter_state() 13 | self.game.reset_keys() 14 | 15 | def render(self, display): 16 | display.fill((255,255,255)) 17 | self.game.draw_text(display, "Game States Demo", (0,0,0), self.game.GAME_W/2, self.game.GAME_H/2 ) -------------------------------------------------------------------------------- /Menu System/game.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from menu import * 3 | 4 | 5 | class Game(): 6 | def __init__(self): 7 | pygame.init() 8 | self.running, self.playing = True, False 9 | self.UP_KEY, self.DOWN_KEY, self.START_KEY, self.BACK_KEY = False, False, False, False 10 | self.DISPLAY_W, self.DISPLAY_H = 480, 270 11 | self.display = pygame.Surface((self.DISPLAY_W,self.DISPLAY_H)) 12 | self.window = pygame.display.set_mode(((self.DISPLAY_W,self.DISPLAY_H))) 13 | self.font_name = '8-BIT WONDER.TTF' 14 | #self.font_name = pygame.font.get_default_font() 15 | self.BLACK, self.WHITE = (0, 0, 0), (255, 255, 255) 16 | self.main_menu = MainMenu(self) 17 | self.options = OptionsMenu(self) 18 | self.credits = CreditsMenu(self) 19 | self.curr_menu = self.main_menu 20 | 21 | def game_loop(self): 22 | while self.playing: 23 | self.check_events() 24 | if self.START_KEY: 25 | self.playing= False 26 | self.display.fill(self.BLACK) 27 | self.draw_text('Thanks for Playing', 20, self.DISPLAY_W/2, self.DISPLAY_H/2) 28 | self.window.blit(self.display, (0,0)) 29 | pygame.display.update() 30 | self.reset_keys() 31 | 32 | 33 | 34 | def check_events(self): 35 | for event in pygame.event.get(): 36 | if event.type == pygame.QUIT: 37 | self.running, self.playing = False, False 38 | self.curr_menu.run_display = False 39 | if event.type == pygame.KEYDOWN: 40 | if event.key == pygame.K_RETURN: 41 | self.START_KEY = True 42 | if event.key == pygame.K_BACKSPACE: 43 | self.BACK_KEY = True 44 | if event.key == pygame.K_DOWN: 45 | self.DOWN_KEY = True 46 | if event.key == pygame.K_UP: 47 | self.UP_KEY = True 48 | 49 | def reset_keys(self): 50 | self.UP_KEY, self.DOWN_KEY, self.START_KEY, self.BACK_KEY = False, False, False, False 51 | 52 | def draw_text(self, text, size, x, y ): 53 | font = pygame.font.Font(self.font_name,size) 54 | text_surface = font.render(text, True, self.WHITE) 55 | text_rect = text_surface.get_rect() 56 | text_rect.center = (x,y) 57 | self.display.blit(text_surface,text_rect) 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Menu System/main.py: -------------------------------------------------------------------------------- 1 | from game import Game 2 | 3 | g = Game() 4 | 5 | while g.running: 6 | g.curr_menu.display_menu() 7 | g.game_loop() 8 | -------------------------------------------------------------------------------- /Menu System/menu.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | class Menu(): 4 | def __init__(self, game): 5 | self.game = game 6 | self.mid_w, self.mid_h = self.game.DISPLAY_W / 2, self.game.DISPLAY_H / 2 7 | self.run_display = True 8 | self.cursor_rect = pygame.Rect(0, 0, 20, 20) 9 | self.offset = - 100 10 | 11 | def draw_cursor(self): 12 | self.game.draw_text('*', 15, self.cursor_rect.x, self.cursor_rect.y) 13 | 14 | def blit_screen(self): 15 | self.game.window.blit(self.game.display, (0, 0)) 16 | pygame.display.update() 17 | self.game.reset_keys() 18 | 19 | class MainMenu(Menu): 20 | def __init__(self, game): 21 | Menu.__init__(self, game) 22 | self.state = "Start" 23 | self.startx, self.starty = self.mid_w, self.mid_h + 30 24 | self.optionsx, self.optionsy = self.mid_w, self.mid_h + 50 25 | self.creditsx, self.creditsy = self.mid_w, self.mid_h + 70 26 | self.cursor_rect.midtop = (self.startx + self.offset, self.starty) 27 | 28 | def display_menu(self): 29 | self.run_display = True 30 | while self.run_display: 31 | self.game.check_events() 32 | self.check_input() 33 | self.game.display.fill(self.game.BLACK) 34 | self.game.draw_text('Main Menu', 20, self.game.DISPLAY_W / 2, self.game.DISPLAY_H / 2 - 20) 35 | self.game.draw_text("Start Game", 20, self.startx, self.starty) 36 | self.game.draw_text("Options", 20, self.optionsx, self.optionsy) 37 | self.game.draw_text("Credits", 20, self.creditsx, self.creditsy) 38 | self.draw_cursor() 39 | self.blit_screen() 40 | 41 | 42 | def move_cursor(self): 43 | if self.game.DOWN_KEY: 44 | if self.state == 'Start': 45 | self.cursor_rect.midtop = (self.optionsx + self.offset, self.optionsy) 46 | self.state = 'Options' 47 | elif self.state == 'Options': 48 | self.cursor_rect.midtop = (self.creditsx + self.offset, self.creditsy) 49 | self.state = 'Credits' 50 | elif self.state == 'Credits': 51 | self.cursor_rect.midtop = (self.startx + self.offset, self.starty) 52 | self.state = 'Start' 53 | elif self.game.UP_KEY: 54 | if self.state == 'Start': 55 | self.cursor_rect.midtop = (self.creditsx + self.offset, self.creditsy) 56 | self.state = 'Credits' 57 | elif self.state == 'Options': 58 | self.cursor_rect.midtop = (self.startx + self.offset, self.starty) 59 | self.state = 'Start' 60 | elif self.state == 'Credits': 61 | self.cursor_rect.midtop = (self.optionsx + self.offset, self.optionsy) 62 | self.state = 'Options' 63 | 64 | def check_input(self): 65 | self.move_cursor() 66 | if self.game.START_KEY: 67 | if self.state == 'Start': 68 | self.game.playing = True 69 | elif self.state == 'Options': 70 | self.game.curr_menu = self.game.options 71 | elif self.state == 'Credits': 72 | self.game.curr_menu = self.game.credits 73 | self.run_display = False 74 | 75 | class OptionsMenu(Menu): 76 | def __init__(self, game): 77 | Menu.__init__(self, game) 78 | self.state = 'Volume' 79 | self.volx, self.voly = self.mid_w, self.mid_h + 20 80 | self.controlsx, self.controlsy = self.mid_w, self.mid_h + 40 81 | self.cursor_rect.midtop = (self.volx + self.offset, self.voly) 82 | 83 | def display_menu(self): 84 | self.run_display = True 85 | while self.run_display: 86 | self.game.check_events() 87 | self.check_input() 88 | self.game.display.fill((0, 0, 0)) 89 | self.game.draw_text('Options', 20, self.game.DISPLAY_W / 2, self.game.DISPLAY_H / 2 - 30) 90 | self.game.draw_text("Volume", 15, self.volx, self.voly) 91 | self.game.draw_text("Controls", 15, self.controlsx, self.controlsy) 92 | self.draw_cursor() 93 | self.blit_screen() 94 | 95 | def check_input(self): 96 | if self.game.BACK_KEY: 97 | self.game.curr_menu = self.game.main_menu 98 | self.run_display = False 99 | elif self.game.UP_KEY or self.game.DOWN_KEY: 100 | if self.state == 'Volume': 101 | self.state = 'Controls' 102 | self.cursor_rect.midtop = (self.controlsx + self.offset, self.controlsy) 103 | elif self.state == 'Controls': 104 | self.state = 'Volume' 105 | self.cursor_rect.midtop = (self.volx + self.offset, self.voly) 106 | elif self.game.START_KEY: 107 | # TO-DO: Create a Volume Menu and a Controls Menu 108 | pass 109 | 110 | class CreditsMenu(Menu): 111 | def __init__(self, game): 112 | Menu.__init__(self, game) 113 | 114 | def display_menu(self): 115 | self.run_display = True 116 | while self.run_display: 117 | self.game.check_events() 118 | if self.game.START_KEY or self.game.BACK_KEY: 119 | self.game.curr_menu = self.game.main_menu 120 | self.run_display = False 121 | self.game.display.fill(self.game.BLACK) 122 | self.game.draw_text('Credits', 20, self.game.DISPLAY_W / 2, self.game.DISPLAY_H / 2 - 20) 123 | self.game.draw_text('Made by me', 15, self.game.DISPLAY_W / 2, self.game.DISPLAY_H / 2 + 10) 124 | self.blit_screen() 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /PS4 Controller/ps4_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "x": 0, 3 | "circle": 1, 4 | "square": 2, 5 | "triangle": 3, 6 | "share": 4, 7 | "PS": 5, 8 | "options": 6, 9 | "left_stick_click": 7, 10 | "right_stick_click": 8, 11 | "L1": 9, 12 | "R1": 10, 13 | "up_arrow": 11, 14 | "down_arrow": 12, 15 | "left_arrow": 13, 16 | "right_arrow": 14, 17 | "touchpad": 15 18 | 19 | } -------------------------------------------------------------------------------- /PS4 Controller/test.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import json, os 3 | 4 | ################################# LOAD UP A BASIC WINDOW ################################# 5 | pygame.init() 6 | DISPLAY_W, DISPLAY_H = 960, 570 7 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 8 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 9 | running = True 10 | player = pygame.Rect(DISPLAY_W/2, DISPLAY_H/2, 60,60) 11 | LEFT, RIGHT, UP, DOWN = False, False, False, False 12 | clock = pygame.time.Clock() 13 | color = 0 14 | ########################################################################################### 15 | 16 | #Initialize controller 17 | joysticks = [] 18 | for i in range(pygame.joystick.get_count()): 19 | joysticks.append(pygame.joystick.Joystick(i)) 20 | for joystick in joysticks: 21 | joystick.init() 22 | 23 | with open(os.path.join("ps4_keys.json"), 'r+') as file: 24 | button_keys = json.load(file) 25 | # 0: Left analog horizonal, 1: Left Analog Vertical, 2: Right Analog Horizontal 26 | # 3: Right Analog Vertical 4: Left Trigger, 5: Right Trigger 27 | analog_keys = {0:0, 1:0, 2:0, 3:0, 4:-1, 5: -1 } 28 | 29 | # START OF GAME LOOP 30 | while running: 31 | ################################# CHECK PLAYER INPUT ################################# 32 | for event in pygame.event.get(): 33 | if event.type == pygame.QUIT: 34 | running = False 35 | if event.type == pygame.KEYDOWN: 36 | ############### UPDATE SPRITE IF SPACE IS PRESSED ################################# 37 | pass 38 | 39 | # HANDLES BUTTON PRESSES 40 | if event.type == pygame.JOYBUTTONDOWN: 41 | if event.button == button_keys['left_arrow']: 42 | LEFT = True 43 | if event.button == button_keys['right_arrow']: 44 | RIGHT = True 45 | if event.button == button_keys['down_arrow']: 46 | DOWN = True 47 | if event.button == button_keys['up_arrow']: 48 | UP = True 49 | # HANDLES BUTTON RELEASES 50 | if event.type == pygame.JOYBUTTONUP: 51 | if event.button == button_keys['left_arrow']: 52 | LEFT = False 53 | if event.button == button_keys['right_arrow']: 54 | RIGHT = False 55 | if event.button == button_keys['down_arrow']: 56 | DOWN = False 57 | if event.button == button_keys['up_arrow']: 58 | UP = False 59 | 60 | #HANDLES ANALOG INPUTS 61 | if event.type == pygame.JOYAXISMOTION: 62 | analog_keys[event.axis] = event.value 63 | # print(analog_keys) 64 | # Horizontal Analog 65 | if abs(analog_keys[0]) > .4: 66 | if analog_keys[0] < -.7: 67 | LEFT = True 68 | else: 69 | LEFT = False 70 | if analog_keys[0] > .7: 71 | RIGHT = True 72 | else: 73 | RIGHT = False 74 | # Vertical Analog 75 | if abs(analog_keys[1]) > .4: 76 | if analog_keys[1] < -.7: 77 | UP = True 78 | else: 79 | UP = False 80 | if analog_keys[1] > .7: 81 | DOWN = True 82 | else: 83 | DOWN = False 84 | # Triggers 85 | if analog_keys[4] > 0: # Left trigger 86 | color += 2 87 | if analog_keys[5] > 0: # Right Trigger 88 | color -= 2 89 | 90 | 91 | 92 | 93 | 94 | 95 | # Handle Player movement 96 | if LEFT: 97 | player.x -=5 #*(-1 * analog_keys[0]) 98 | if RIGHT: 99 | player.x += 5 #* analog_keys[0] 100 | if UP: 101 | player.y -= 5 102 | if DOWN: 103 | player.y += 5 104 | 105 | if color < 0: 106 | color = 0 107 | elif color > 255: 108 | color = 255 109 | 110 | 111 | ################################# UPDATE WINDOW AND DISPLAY ################################# 112 | canvas.fill((255,255,255)) 113 | pygame.draw.rect(canvas, (0,0 + color,255), player) 114 | window.blit(canvas, (0,0)) 115 | clock.tick(60) 116 | pygame.display.update() 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Physics/main.py: -------------------------------------------------------------------------------- 1 | from tiles import * 2 | from spritesheet import Spritesheet 3 | from player import Player 4 | ################################# LOAD UP A BASIC WINDOW AND CLOCK ################################# 5 | pygame.init() 6 | DISPLAY_W, DISPLAY_H = 480, 270 7 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 8 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 9 | running = True 10 | clock = pygame.time.Clock() 11 | TARGET_FPS = 60 12 | ################################# LOAD PLAYER AND SPRITESHEET################################### 13 | spritesheet = Spritesheet('spritesheet.png') 14 | player = Player() 15 | #################################### LOAD THE LEVEL ####################################### 16 | map = TileMap('test_level.csv', spritesheet ) 17 | player.position.x, player.position.y = map.start_x, map.start_y 18 | 19 | ################################# GAME LOOP ########################## 20 | while running: 21 | dt = clock.tick(60) * .001 * TARGET_FPS 22 | ################################# CHECK PLAYER INPUT ################################# 23 | for event in pygame.event.get(): 24 | if event.type == pygame.QUIT: 25 | running = False 26 | if event.type == pygame.KEYDOWN: 27 | if event.key == pygame.K_LEFT: 28 | player.LEFT_KEY = True 29 | elif event.key == pygame.K_RIGHT: 30 | player.RIGHT_KEY = True 31 | elif event.key == pygame.K_SPACE: 32 | player.jump() 33 | 34 | 35 | if event.type == pygame.KEYUP: 36 | if event.key == pygame.K_LEFT: 37 | player.LEFT_KEY = False 38 | elif event.key == pygame.K_RIGHT: 39 | player.RIGHT_KEY = False 40 | elif event.key == pygame.K_SPACE: 41 | if player.is_jumping: 42 | player.velocity.y *= .25 43 | player.is_jumping = False 44 | 45 | 46 | ################################# UPDATE/ Animate SPRITE ################################# 47 | player.update(dt) 48 | ################################# UPDATE WINDOW AND DISPLAY ################################# 49 | canvas.fill((0, 180, 240)) # Fills the entire screen with light blue 50 | map.draw_map(canvas) 51 | player.draw(canvas) 52 | #canvas.blit(player_img, player_rect) 53 | window.blit(canvas, (0,0)) 54 | pygame.display.update() 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Physics/player.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from spritesheet import Spritesheet 3 | 4 | class Player(pygame.sprite.Sprite): 5 | def __init__(self): 6 | pygame.sprite.Sprite.__init__(self) 7 | self.image = Spritesheet('spritesheet.png').parse_sprite('chick.png') 8 | self.rect = self.image.get_rect() 9 | self.LEFT_KEY, self.RIGHT_KEY, self.FACING_LEFT = False, False, False 10 | self.is_jumping, self.on_ground = False, False 11 | self.gravity, self.friction = .35, -.12 12 | self.position, self.velocity = pygame.math.Vector2(0, 0), pygame.math.Vector2(0, 0) 13 | self.acceleration = pygame.math.Vector2(0, self.gravity) 14 | 15 | def draw(self, display): 16 | display.blit(self.image, (self.rect.x, self.rect.y)) 17 | 18 | def update(self, dt): 19 | self.horizontal_movement(dt) 20 | self.vertical_movement(dt) 21 | 22 | def horizontal_movement(self, dt): 23 | self.acceleration.x = 0 24 | if self.LEFT_KEY: 25 | self.acceleration.x -= .3 26 | elif self.RIGHT_KEY: 27 | self.acceleration.x += .3 28 | self.acceleration.x += self.velocity.x * self.friction 29 | self.velocity.x += self.acceleration.x * dt 30 | self.limit_velocity(4) 31 | self.position.x += self.velocity.x * dt + (self.acceleration.x * .5) * (dt * dt) 32 | self.rect.x = self.position.x 33 | 34 | def vertical_movement(self, dt): 35 | self.velocity.y += self.acceleration.y * dt 36 | if self.velocity.y > 7: self.velocity.y = 7 37 | self.position.y += self.velocity.y * dt + (self.acceleration.y * .5) * (dt * dt) 38 | if self.position.y > 128: 39 | self.on_ground = True 40 | self.velocity.y = 0 41 | self.position.y = 128 42 | self.rect.bottom = self.position.y 43 | 44 | 45 | def limit_velocity(self, max_vel): 46 | min(-max_vel, max(self.velocity.x, max_vel)) 47 | if abs(self.velocity.x) < .01: self.velocity.x = 0 48 | 49 | def jump(self): 50 | if self.on_ground: 51 | self.is_jumping = True 52 | self.velocity.y -= 8 53 | self.on_ground = False 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /Physics/spritesheet.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "chick.png": 4 | { 5 | "frame": {"x":0,"y":0,"w":16,"h":16}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 9 | "sourceSize": {"w":16,"h":16} 10 | }, 11 | "grass.png": 12 | { 13 | "frame": {"x":16,"y":0,"w":16,"h":16}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 17 | "sourceSize": {"w":16,"h":16} 18 | }, 19 | "grass2.png": 20 | { 21 | "frame": {"x":32,"y":0,"w":16,"h":16}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 25 | "sourceSize": {"w":16,"h":16} 26 | }}, 27 | "meta": { 28 | "app": "https://www.codeandweb.com/texturepacker", 29 | "version": "1.0", 30 | "image": "test.png", 31 | "format": "RGBA8888", 32 | "size": {"w":48,"h":16}, 33 | "scale": "1", 34 | "smartupdate": "$TexturePacker:SmartUpdate:a66d329312f98accd6de9452ce22c2dc:dc1a8a27030eb8549f6ad949e95565fe:02ab132358d6d8b512e80119463a8329$" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Physics/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Physics/spritesheet.png -------------------------------------------------------------------------------- /Physics/spritesheet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import json 3 | 4 | 5 | class Spritesheet: 6 | def __init__(self, filename): 7 | self.filename = filename 8 | self.sprite_sheet = pygame.image.load(filename).convert() 9 | self.meta_data = self.filename.replace('png', 'json') 10 | with open(self.meta_data) as f: 11 | self.data = json.load(f) 12 | f.close() 13 | 14 | 15 | 16 | def get_sprite(self, x, y, w, h): 17 | sprite = pygame.Surface((w, h)) 18 | sprite.set_colorkey((0,0,0)) 19 | sprite.blit(self.sprite_sheet,(0, 0),(x, y, w, h)) 20 | return sprite 21 | 22 | def parse_sprite(self, name): 23 | sprite = self.data['frames'][name]['frame'] 24 | x, y, w, h = sprite["x"], sprite["y"], sprite["w"], sprite["h"] 25 | image = self.get_sprite(x, y, w, h) 26 | return image 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Physics/test_level.csv: -------------------------------------------------------------------------------- 1 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 2 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 3 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 4 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 5 | -1,-1,-1,-1,-1,-1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 6 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 7 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 8 | -1,-1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 9 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,1 10 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 11 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 12 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 13 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 14 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 15 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 16 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 17 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 18 | -------------------------------------------------------------------------------- /Physics/tiles.py: -------------------------------------------------------------------------------- 1 | import pygame, csv, os 2 | 3 | class Tile(pygame.sprite.Sprite): 4 | def __init__(self, image, x, y, spritesheet): 5 | pygame.sprite.Sprite.__init__(self) 6 | self.image = spritesheet.parse_sprite(image) 7 | # Manual load in: self.image = pygame.image.load(image) 8 | self.rect = self.image.get_rect() 9 | self.rect.x, self.rect.y = x, y 10 | 11 | def draw(self, surface): 12 | surface.blit(self.image, (self.rect.x, self.rect.y)) 13 | 14 | class TileMap(): 15 | def __init__(self, filename, spritesheet): 16 | self.tile_size = 16 17 | self.start_x, self.start_y = 0, 0 18 | self.spritesheet = spritesheet 19 | self.tiles = self.load_tiles(filename) 20 | self.map_surface = pygame.Surface((self.map_w, self.map_h)) 21 | self.map_surface.set_colorkey((0, 0, 0)) 22 | self.load_map() 23 | 24 | def draw_map(self, surface): 25 | surface.blit(self.map_surface, (0,0)) 26 | 27 | def load_map(self): 28 | for tile in self.tiles: 29 | tile.draw(self.map_surface) 30 | 31 | def read_csv(self, filename): 32 | map = [] 33 | with open(os.path.join(filename)) as data: 34 | data = csv.reader(data, delimiter=',') 35 | for row in data: 36 | map.append(list(row)) 37 | return map 38 | 39 | def load_tiles(self, filename): 40 | tiles = [] 41 | map = self.read_csv(filename) 42 | x, y = 0, 0 43 | for row in map: 44 | x = 0 45 | for tile in row: 46 | if tile == '0': 47 | self.start_x, self.start_y = x * self.tile_size, y * self.tile_size 48 | elif tile == '1': 49 | tiles.append(Tile('grass.png', x * self.tile_size, y * self.tile_size, self.spritesheet)) 50 | elif tile == '2': 51 | tiles.append(Tile('grass2.png', x * self.tile_size, y * self.tile_size, self.spritesheet)) 52 | # Move to next tile in current row 53 | x += 1 54 | 55 | # Move to next row 56 | y += 1 57 | # Store the size of the tile map 58 | self.map_w, self.map_h = x * self.tile_size, y * self.tile_size 59 | return tiles 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /PyRacer/Assets/images/GO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/images/GO.png -------------------------------------------------------------------------------- /PyRacer/Assets/images/PressStart2P-vaV7.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/images/PressStart2P-vaV7.ttf -------------------------------------------------------------------------------- /PyRacer/Assets/images/Streetlight1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/images/Streetlight1.png -------------------------------------------------------------------------------- /PyRacer/Assets/images/Streetlight2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/images/Streetlight2.png -------------------------------------------------------------------------------- /PyRacer/Assets/images/Streetlight3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/images/Streetlight3.png -------------------------------------------------------------------------------- /PyRacer/Assets/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/images/background.png -------------------------------------------------------------------------------- /PyRacer/Assets/images/car.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/images/car.png -------------------------------------------------------------------------------- /PyRacer/Assets/sounds/go.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/sounds/go.wav -------------------------------------------------------------------------------- /PyRacer/Assets/sounds/light.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/sounds/light.wav -------------------------------------------------------------------------------- /PyRacer/Assets/sounds/racing_song.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/PyRacer/Assets/sounds/racing_song.ogg -------------------------------------------------------------------------------- /PyRacer/car.py: -------------------------------------------------------------------------------- 1 | import pygame,os 2 | 3 | class Car(): 4 | def __init__(self, game): 5 | self.game = game 6 | self.position, self.distance = 0, 0 7 | self.position_int = self.game.DISPLAY_W / 2 + int(self.game.SCREEN_WIDTH * self.position / 2) - 14 8 | self.speed = 0 9 | self.curvature = 0 10 | self.image = pygame.image.load(os.path.join(self.game.img_dir,"car.png")).convert() 11 | self.image.set_colorkey((0,0,0)) 12 | self.rect = self.image.get_rect() 13 | 14 | def clamp_speed(self): 15 | self.speed = max(0, self.speed) 16 | self.speed = min(self.speed,1) 17 | 18 | def update(self): 19 | # Update the Car's movement 20 | if self.game.actions['accel']: 21 | self.speed += .5 * self.game.dt 22 | else: 23 | self.speed -= .25 * self.game.dt 24 | 25 | if self.game.actions['brake']: 26 | self.speed -=.75 * self.game.dt 27 | 28 | if self.game.actions['left']: 29 | self.curvature -= .3 * self.game.dt 30 | if self.game.actions['right']: 31 | self.curvature += .3 * self.game.dt 32 | 33 | if abs(self.curvature - self.game.map.track_curvature) >= .55: 34 | self.speed -= 5 * self.game.dt 35 | 36 | self.clamp_speed() 37 | self.distance += 70 * self.speed * self.game.dt 38 | 39 | def draw(self): 40 | self.position = self.curvature - self.game.map.track_curvature 41 | self.position_int = self.game.DISPLAY_W / 2 + int(self.game.DISPLAY_W * self.position / 2) 42 | self.rect.center = (self.position_int, 220) 43 | self.game.display.blit(self.image, self.rect) 44 | pygame.draw.rect(self.game.display, (0,0,0), (self.position_int, 220, 4, 4) ) -------------------------------------------------------------------------------- /PyRacer/game.py: -------------------------------------------------------------------------------- 1 | import pygame, time, os 2 | from map import Map 3 | 4 | class Game(): 5 | def __init__(self): 6 | pygame.mixer.pre_init(44100, -16, 1, 512) # Prevents delay in jumping sound 7 | pygame.init() 8 | # Intitialize display surface and screen 9 | self.DISPLAY_W, self.DISPLAY_H = 480, 270 # Dimensions of the 'canvas'. Use this for game logic May be upscaled later 10 | self.SCREEN_WIDTH, self.SCREEN_HEIGHT = 480 * 2 , 270 * 2 # Dimensions of the screen that canvas will be drawn onto 11 | self.display = pygame.Surface((self.DISPLAY_W,self.DISPLAY_H)) 12 | self.screen = pygame.display.set_mode((self.SCREEN_WIDTH,self.SCREEN_HEIGHT)) 13 | self.screen.set_alpha(None) 14 | pygame.display.set_caption("PyRacer") 15 | # Initialize clock, controls, and other variables 16 | self.running, self.playing = True, False 17 | self.load_assets() 18 | self.prev_time = time.time() 19 | self.fps_list = [] 20 | self.actions = {'left' : False, 'right' : False, 'accel' : False, 'brake' : False, 'start' : False} 21 | self.best_time = 1000000000000 22 | 23 | # Resets any logic that needs to be reset after every game loop 24 | # May include player, enemies, level, objects, etc 25 | def reset(self): 26 | self.map = Map(self) 27 | self.go_text = 0 28 | self.lap_time = 0 29 | self.countdown = 3 30 | self.countdownUpdate = time.time() 31 | self.counting_down = True 32 | self.complete = False 33 | self.finished_countdown = 0 34 | self.light_sound.play() 35 | 36 | 37 | 38 | # Main Game Loop. Starts by resetting the game, and then loops until playing is set to false 39 | def game_loop(self): 40 | self.reset() 41 | pygame.mixer.music.play(-1) 42 | while self.playing: 43 | self.get_dt() 44 | self.get_events() 45 | if self.countdown > 0: self.count_down() 46 | else: self.update() 47 | self.render() 48 | pygame.mixer.music.stop() 49 | 50 | def get_events(self): 51 | # Gets all events from the user, stores them in the 'actions' dictionary 52 | for event in pygame.event.get(): 53 | if event.type == pygame.QUIT: 54 | self.playing = False 55 | self.running = False 56 | if event.type == pygame.KEYDOWN: 57 | if event.key == pygame.K_ESCAPE: 58 | self.playing = False 59 | self.running = False 60 | if event.key == pygame.K_a: 61 | self.actions['left'] = True 62 | if event.key == pygame.K_d: 63 | self.actions['right'] = True 64 | if event.key == pygame.K_w: 65 | self.actions['accel'] = True 66 | if event.key == pygame.K_s: 67 | self.actions['brake'] = True 68 | if event.key == pygame.K_RETURN: 69 | self.actions['start'] = True 70 | 71 | if event.type == pygame.KEYUP: 72 | if event.key == pygame.K_a: 73 | self.actions['left'] = False 74 | if event.key == pygame.K_d: 75 | self.actions['right'] = False 76 | if event.key == pygame.K_w: 77 | self.actions['accel'] = False 78 | if event.key == pygame.K_s: 79 | self.actions['brake'] = False 80 | if event.key == pygame.K_RETURN: 81 | self.actions['start'] = False 82 | 83 | # Update any of the game sprites 84 | def update(self): 85 | self.timer() 86 | self.map.update() 87 | if self.complete: self.complete_timer() 88 | 89 | # Draw all sprites and images onto the screen 90 | def render(self): 91 | self.map.render() 92 | self.draw_startup() 93 | self.draw_to_screen() 94 | # Helper function to draw our 'canvas' to the screen 95 | def draw_to_screen(self): 96 | self.screen.blit(pygame.transform.scale(self.display,(self.SCREEN_WIDTH, self.SCREEN_HEIGHT)), (0,0)) 97 | pygame.display.update() 98 | 99 | # Displays Main Menu 100 | def main_menu(self): 101 | display_menu = True 102 | while display_menu: 103 | self.get_events() 104 | # If start is pressed, play the game 105 | display_menu = self.running 106 | if self.actions['start']: 107 | display_menu = False 108 | self.playing = True 109 | self.display.fill((209,29,39)) 110 | self.draw_lg_text("PyRacer!", ((255, 250, 239)), self.DISPLAY_W *.5, self.DISPLAY_H * .4) 111 | self.draw_text("Press Start to Play", ((255, 250, 239)), self.DISPLAY_W *.22, self.DISPLAY_H * .5) 112 | self.draw_to_screen() 113 | 114 | def draw_startup(self): 115 | if self.countdown >0: 116 | self.display.blit(self.light_images[self.countdown-1],(self.DISPLAY_W * .5 - 32, 50)) 117 | elif self.go_text < .75: 118 | self.go_sound.play() 119 | self.go_text += self.dt 120 | self.display.blit(self.go_img,(self.DISPLAY_W * .5 - 32, 50)) 121 | 122 | # Computes FPS, delta time, and caps the frame rate if specified in the FPS() class 123 | def get_dt(self): 124 | now = time.time() 125 | self.dt = now - self.prev_time 126 | self.prev_time = now 127 | self.get_fps() 128 | 129 | def get_fps(self): 130 | fps = 0 131 | if self.dt: fps = 1/self.dt 132 | if len(self.fps_list) == 50: 133 | self.fps_list.pop(0) 134 | self.fps_list.append(fps) 135 | avg_fps = sum(self.fps_list) / len(self.fps_list) 136 | pygame.display.set_caption('PyRacer - FPS:' + str(round(avg_fps,2))) 137 | 138 | # Resets all the keys to False. Useful for Menus 139 | def reset_keys(self): 140 | for key in self.actions: 141 | self.actions[key] = False 142 | 143 | def draw_text(self, text, color, x, y): 144 | text_surface = self.font.render(text, True, color) 145 | text_surface.set_colorkey((0,0,0)) 146 | text_rect = text_surface.get_rect() 147 | text_rect.x, text_rect.y = x, y 148 | self.display.blit(text_surface, text_rect) 149 | 150 | def draw_lg_text(self, text, color, x, y): 151 | text_surface = self.lg_font.render(text, True, color) 152 | text_surface.set_colorkey((0,0,0)) 153 | text_rect = text_surface.get_rect() 154 | text_rect.center = (x,y) 155 | self.display.blit(text_surface, text_rect) 156 | 157 | 158 | def timer(self): 159 | self.lap_time += self.dt 160 | 161 | def complete_timer(self): 162 | self.finished_countdown += self.dt 163 | if self.finished_countdown > 3: 164 | self.playing = False 165 | 166 | def count_down(self): 167 | now = time.time() 168 | if now - self.countdownUpdate > 1: 169 | self.countdownUpdate = now 170 | self.countdown -=1 171 | self.light_sound.play() 172 | if self.countdown > 0: 173 | self.counting_down = False 174 | 175 | def load_assets(self): 176 | self.load_directories() 177 | self.load_images() 178 | self.load_sounds() 179 | 180 | def load_directories(self): 181 | self.dir = os.path.join(os.path.dirname(os.path.abspath("game.py"))) # Gets the directory name of the game.py file 182 | self.img_dir = os.path.join(self.dir,"Assets", "images") 183 | self.sound_dir = os.path.join(self.dir,"Assets","sounds") 184 | 185 | 186 | def load_images(self): 187 | # Load all the images 188 | self.light_images = [ 189 | pygame.image.load(os.path.join(self.img_dir, "streetlight3.png")).convert(), 190 | pygame.image.load(os.path.join(self.img_dir, "streetlight2.png")).convert(), 191 | pygame.image.load(os.path.join(self.img_dir, "streetlight1.png")).convert() 192 | ] 193 | self.go_img = pygame.image.load(os.path.join(self.img_dir, "GO.png")).convert() 194 | for image in self.light_images: 195 | image.set_colorkey((0,0,0)) 196 | self.go_img.set_colorkey((0,0,0)) 197 | # Load all the fonts 198 | self.font = pygame.font.Font(os.path.join(self.img_dir, "PressStart2P-vaV7.ttf"), 14) 199 | self.lg_font = pygame.font.Font(os.path.join(self.img_dir, "PressStart2P-vaV7.ttf"), 28) 200 | 201 | def load_sounds(self): 202 | self.theme = pygame.mixer.music.load( os.path.join(self.sound_dir,"racing_song.ogg") ) 203 | self.light_sound = pygame.mixer.Sound(os.path.join(self.sound_dir,"light.wav")) 204 | self.go_sound = pygame.mixer.Sound(os.path.join(self.sound_dir, "go.wav")) 205 | self.light_sound.set_volume(.3) 206 | self.go_sound.set_volume(.2) 207 | 208 | 209 | 210 | if __name__ == "__main__": 211 | g = Game() 212 | while g.running: 213 | g.main_menu() 214 | g.game_loop() -------------------------------------------------------------------------------- /PyRacer/map.py: -------------------------------------------------------------------------------- 1 | import pygame, math,os 2 | from car import Car 3 | 4 | class Map(): 5 | def __init__(self, game): 6 | self.game = game 7 | self.mid_w, self.mid_h = int(self.game.DISPLAY_W /2), int(self.game.DISPLAY_H /2) 8 | self.car = Car(self.game) 9 | self.curvature = 0 10 | self.track_curvature = 0 11 | self.track_length = 0 12 | self.load_track() 13 | self.lap, self.counted = 0,True 14 | self.lap_times = [] 15 | self.background_img = pygame.image.load(os.path.join(self.game.img_dir, "background.png")).convert() 16 | 17 | def render(self): 18 | #self.update() 19 | self.draw_map() 20 | 21 | def update(self): 22 | # Update the car 23 | self.car.update() 24 | self.update_track() 25 | if self.lap > 2: 26 | self.lap = 2 27 | self.game.complete = True 28 | self.game.best_time = min(self.game.best_time, sum(self.lap_times)) 29 | 30 | def update_track(self): 31 | track_section, offset = 0, 0 32 | 33 | if self.car.distance >= self.track_length: 34 | self.car.distance -= self.track_length 35 | 36 | # optimise Curvature Calculations 37 | while track_section < len(self.track) and offset <= self.car.distance: 38 | offset += self.track[track_section][1] 39 | track_section += 1 40 | target_curvature = self.track[track_section - 1][0] 41 | 42 | track_curve_diff = (target_curvature - self.curvature) * self.game.dt * self.car.speed 43 | self.curvature += track_curve_diff 44 | 45 | self.track_curvature += self.curvature * self.game.dt * self.car.speed 46 | 47 | if track_section == 1 and not self.counted: 48 | self.store_times() 49 | self.counted = True 50 | self.lap += 1 51 | 52 | if track_section > 1: 53 | self.counted = False 54 | 55 | def draw_map(self): 56 | 57 | self.game.display.blit(self.background_img, (0, 0)) 58 | 59 | x,y = 0,0 60 | # Draw the Entire map 61 | while y < self.mid_h: 62 | x = 0 63 | while x < self.game.DISPLAY_W: 64 | # Calculate perspective (farther tiles are closer to 1) 65 | perspective = float(y / self.mid_w) 66 | 67 | midpoint = .5 + self.curvature * math.pow(1 - perspective,3) 68 | road_w = .2 + perspective * .9 69 | clip_width = road_w * .16 70 | 71 | road_w *= .5 72 | 73 | LeftGrass = int( (midpoint - road_w - clip_width) * self.game.DISPLAY_W ) 74 | LeftClip = int((midpoint - road_w) * self.game.DISPLAY_W) 75 | RightClip = int((midpoint + road_w) * self.game.DISPLAY_W) 76 | RightGrass = int((midpoint + road_w + clip_width) * self.game.DISPLAY_W) 77 | 78 | nRow = self.mid_h + y 79 | 80 | #grass_color = (0,255,100) 81 | grass_color = ( 194, 178, 128) 82 | grass_val = math.sin(20 * math.pow(1 - perspective, 3) + self.car.distance * .1) 83 | if grass_val > 0: grass_color = ( 198, 163, 80) 84 | 85 | clip_color = (255,0,0) 86 | clip_val = math.sin(80 * math.pow(1 - perspective, 2) + self.car.distance * .5 ) 87 | if clip_val > 0: clip_color = (255,255,255) 88 | # Draw the appropriate tile 89 | if x >= 0 and x < LeftGrass: 90 | pygame.draw.rect(self.game.display, grass_color, (x,nRow,8,8)) 91 | if x >= LeftGrass and x < LeftClip: 92 | pygame.draw.rect(self.game.display, clip_color, (x, nRow, 8, 8)) 93 | if x >= LeftClip and x < RightClip: 94 | pygame.draw.rect(self.game.display, (89,89,89), (x, nRow, 8, 8)) 95 | if x >= RightClip and x < RightGrass: 96 | pygame.draw.rect(self.game.display, clip_color, (x, nRow, 8, 8)) 97 | if x >= RightGrass and x < self.game.DISPLAY_W: 98 | pygame.draw.rect(self.game.display, grass_color, (x,nRow,8,8)) 99 | x += 8 100 | y += 8 101 | 102 | # Draw the player's car 103 | self.car.draw() 104 | self.draw_stats() 105 | 106 | def draw_stats(self): 107 | speed_color = (255,255,255) 108 | if self.car.speed > .9: 109 | speed_color = (255,0,0) 110 | self.game.draw_text('Speed (Km): ' + str(round(self.car.speed * 100,2)) , speed_color, 10,10) 111 | self.game.draw_text('Time: ' + str(round(self.game.lap_time, 2)), (255, 255, 255), 10, 30) 112 | self.game.draw_text('Lap: ' + str(self.lap) + "/2", (255, 255, 255), 10, 50) 113 | # Draw lap times 114 | i = 0 115 | for lap in self.lap_times: 116 | self.game.draw_text('Lap ' + str(i+ 1) +': ' + str(round(lap,2)), (255, 255, 255) , 117 | self.game.DISPLAY_W - 200, 10 + (20 * i)) 118 | i += 1 119 | 120 | # If the track is complete, draw text that displays the players times 121 | if self.game.complete: 122 | self.game.draw_lg_text('FINISHED', (0, 0, 255), self.mid_w - 10,self.mid_h) 123 | self.game.draw_text('Best Time:' + str(round(self.game.best_time,2)), (0, 0, 255), self.mid_w*.5,self.mid_h + 40) 124 | 125 | def load_track(self): 126 | self.track = [ 127 | (0,10), # Start/ Finish Line 128 | (0,200), 129 | (.5,200), 130 | (0,200), 131 | (.6, 200), 132 | (-.5,200), 133 | (.4,200), 134 | (-.6,200), 135 | (0,100) 136 | ] 137 | # Comput the total track length 138 | for lane in self.track: 139 | self.track_length += lane[1] 140 | 141 | def store_times(self): 142 | self.lap_times.append(self.game.lap_time) 143 | self.game.lap_time = 0 144 | self.game.go_sound.play() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Hey there! 2 | 3 | I'm Christian, and I make games as a hobby. I also run a YouTube channel where I post tutorials every now and then. 4 | 5 | This is a repository for the code written in my YouTube tutorials. Feel free to use the code to your liking. If you happen to use any of my code in your projects and wanna give me a little nod of credit, it's always appreciated. 6 | 7 | Also consider checking out my youtube channel by clicking the duckling below (and maybe even subscribing): 8 | 9 | [![Youtube Channe](https://github.com/ChristianD37/YoutubeTutorials/blob/master/readme%20images/logo.gif)](https://www.youtube.com/channel/UCB2mKxxXPK3X8SJkAc-db3A) 10 | 11 | Hope you learn a lot : ) 12 | -------------------------------------------------------------------------------- /RockPaperScissors/art.py: -------------------------------------------------------------------------------- 1 | def draw(choice): 2 | if choice == "rock": 3 | # Player chose rock, print rock 4 | return(""" 5 | _______ 6 | ---' ____) 7 | (_____) 8 | (_____) 9 | (____) 10 | ---.__(___) 11 | """) 12 | elif choice == "paper": 13 | # Player chose rock, print rock 14 | return(""" 15 | _______ 16 | ---' ____)____ 17 | ______) 18 | _______) 19 | _______) 20 | ---.__________) 21 | """) 22 | elif choice == "scissors": 23 | return(""" 24 | _______ 25 | ---' ____)____ 26 | ______) 27 | __________) 28 | (____) 29 | ---.__(___) 30 | """) -------------------------------------------------------------------------------- /RockPaperScissors/main.py: -------------------------------------------------------------------------------- 1 | import os, random 2 | from art import draw 3 | choices = ["rock", "paper", "scissors"] 4 | 5 | def determine_winner(player, opp): 6 | # Check if its a tie 7 | if player == opp: 8 | return("It's a tie game!") 9 | # Check if the player won 10 | elif ((player == "rock" and opp == "scissors") or 11 | (player == "paper" and opp == "rock") or 12 | (player == "scissors" and opp == "paper") ): 13 | return ("You won, congratulations!") 14 | # If they didn't win, then we know they lost 15 | else: 16 | return("You lost, sorry!") 17 | 18 | playing, invalid = True, False 19 | while playing: 20 | if not invalid: 21 | print("Choose rock, paper or scissors") 22 | else: 23 | print("Invalid input, please type rock, paper or scissors") 24 | invalid = False 25 | print("Or enter q to quit") 26 | # Get the player input, make it lowercase 27 | player_choice = input().lower() 28 | # generate a random choice for the computer 29 | opp_choice = random.choice(choices) 30 | # Check and see if the player made a valid entry 31 | if player_choice in choices: 32 | # Draw the turn and check who won 33 | print("You chose: "+ player_choice + draw(player_choice)) 34 | print("The opponent chose: " + opp_choice + draw(opp_choice)) 35 | print(determine_winner(player_choice, opp_choice)) 36 | elif player_choice == "q": 37 | # The player wants to leave, so end the loop 38 | playing = False 39 | else: 40 | invalid = True 41 | 42 | # The iteration of the game is done, ask to play again 43 | if playing and not invalid: 44 | replay = input("Wanna play again? Type yes to replay\nor enter anything else to end the game\n").lower() 45 | print() 46 | playing = replay == "yes" 47 | 48 | # Clear the screen 49 | os.system('cls' if os.name == 'nt' else 'clear') 50 | 51 | 52 | 53 | #os.system('cls' if os.name == 'nt' else 'clear') 54 | print("Thanks for playing!") -------------------------------------------------------------------------------- /Switch Sales and Reviews Scrape/Switch Sales and Reviews.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Switch Sales and Reviews Scrape/Switch Sales and Reviews.xlsx -------------------------------------------------------------------------------- /Switch Sales and Reviews Scrape/switch_scrape.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# Install a pip package in the current Jupyter kernel to format data into dataframes\n", 10 | "import sys\n", 11 | "!{sys.executable} -m pip install pandas\n", 12 | "\n", 13 | "# Install a pip package to perform the html document parsing and extract information\n", 14 | "!{sys.executable} -m pip install bs4\n", 15 | "\n", 16 | "# Install a pip package to export the data in an excel format\n", 17 | "!{sys.executable} -m pip install openpyxl" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 6, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import pandas as pd\n", 27 | "from bs4 import BeautifulSoup\n", 28 | "import requests, time, datetime, re, random" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 7, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "data": { 38 | "text/plain": [ 39 | "200" 40 | ] 41 | }, 42 | "execution_count": 7, 43 | "metadata": {}, 44 | "output_type": "execute_result" 45 | } 46 | ], 47 | "source": [ 48 | "wiki_url = \"https://en.wikipedia.org/wiki/List_of_best-selling_Nintendo_Switch_video_games\"\n", 49 | "headers = {'User-Agent' : \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36\"}\n", 50 | "response = requests.get(wiki_url, headers=headers)\n", 51 | "response.status_code" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 12, 57 | "metadata": {}, 58 | "outputs": [ 59 | { 60 | "data": { 61 | "text/html": [ 62 | "
\n", 63 | "\n", 76 | "\n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | " \n", 169 | " \n", 170 | " \n", 171 | " \n", 172 | " \n", 173 | " \n", 174 | " \n", 175 | " \n", 176 | " \n", 177 | " \n", 178 | " \n", 179 | " \n", 180 | " \n", 181 | " \n", 182 | " \n", 183 | " \n", 184 | " \n", 185 | " \n", 186 | " \n", 187 | " \n", 188 | " \n", 189 | " \n", 190 | " \n", 191 | " \n", 192 | " \n", 193 | " \n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | "
RankTitleCopies soldAs ofRelease date[a]Genre(s)Developer(s)Publisher(s)
01Mario Kart 8 Deluxe53.79 million[4]March 31, 2023April 28, 2017Kart racingNintendo EPDNintendo
12Animal Crossing: New Horizons42.21 million[4]March 31, 2023March 20, 2020Social simulationNintendo EPDNintendo
23Super Smash Bros. Ultimate31.09 million[4]March 31, 2023December 7, 2018FightingBandai Namco StudiosSora Ltd.Nintendo
34The Legend of Zelda: Breath of the Wild29.81 million[4]March 31, 2023March 3, 2017Action-adventureNintendo EPDNintendo
45Pokémon Sword and Shield25.82 million[4]March 31, 2023November 15, 2019Role-playingGame FreakThe Pokémon CompanyNintendo
...........................
7372Fitness Boxing1 million[25]September 8, 2020December 20, 2018ExergamerhythmImagineerJP: ImagineerNA/PAL: Nintendo
7472Fitness Boxing 2: Rhythm and Exercise1 million[26]December 9, 2021December 4, 2020ExergamerhythmImagineerJP: ImagineerNA/PAL: Nintendo
7572Shin Megami Tensei V1 million[27]April 18, 2022November 11, 2021Role-playingAtlusJP: AtlusNA: SegaPAL: Nintendo
7672Story of Seasons: Pioneers of Olive Town1 million[28]November 18, 2021February 25, 2021Simulationrole-playingMarvelousXseed Games
7772Thief Simulator1 million[29]July 16, 2021May 19, 2019StealthNoble MuffinsForever Entertainment
\n", 214 | "

78 rows × 8 columns

\n", 215 | "
" 216 | ], 217 | "text/plain": [ 218 | " Rank Title Copies sold \n", 219 | "0 1 Mario Kart 8 Deluxe 53.79 million[4] \\\n", 220 | "1 2 Animal Crossing: New Horizons 42.21 million[4] \n", 221 | "2 3 Super Smash Bros. Ultimate 31.09 million[4] \n", 222 | "3 4 The Legend of Zelda: Breath of the Wild 29.81 million[4] \n", 223 | "4 5 Pokémon Sword and Shield 25.82 million[4] \n", 224 | ".. ... ... ... \n", 225 | "73 72 Fitness Boxing 1 million[25] \n", 226 | "74 72 Fitness Boxing 2: Rhythm and Exercise 1 million[26] \n", 227 | "75 72 Shin Megami Tensei V 1 million[27] \n", 228 | "76 72 Story of Seasons: Pioneers of Olive Town 1 million[28] \n", 229 | "77 72 Thief Simulator 1 million[29] \n", 230 | "\n", 231 | " As of Release date[a] Genre(s) \n", 232 | "0 March 31, 2023 April 28, 2017 Kart racing \\\n", 233 | "1 March 31, 2023 March 20, 2020 Social simulation \n", 234 | "2 March 31, 2023 December 7, 2018 Fighting \n", 235 | "3 March 31, 2023 March 3, 2017 Action-adventure \n", 236 | "4 March 31, 2023 November 15, 2019 Role-playing \n", 237 | ".. ... ... ... \n", 238 | "73 September 8, 2020 December 20, 2018 Exergamerhythm \n", 239 | "74 December 9, 2021 December 4, 2020 Exergamerhythm \n", 240 | "75 April 18, 2022 November 11, 2021 Role-playing \n", 241 | "76 November 18, 2021 February 25, 2021 Simulationrole-playing \n", 242 | "77 July 16, 2021 May 19, 2019 Stealth \n", 243 | "\n", 244 | " Developer(s) Publisher(s) \n", 245 | "0 Nintendo EPD Nintendo \n", 246 | "1 Nintendo EPD Nintendo \n", 247 | "2 Bandai Namco StudiosSora Ltd. Nintendo \n", 248 | "3 Nintendo EPD Nintendo \n", 249 | "4 Game Freak The Pokémon CompanyNintendo \n", 250 | ".. ... ... \n", 251 | "73 Imagineer JP: ImagineerNA/PAL: Nintendo \n", 252 | "74 Imagineer JP: ImagineerNA/PAL: Nintendo \n", 253 | "75 Atlus JP: AtlusNA: SegaPAL: Nintendo \n", 254 | "76 Marvelous Xseed Games \n", 255 | "77 Noble Muffins Forever Entertainment \n", 256 | "\n", 257 | "[78 rows x 8 columns]" 258 | ] 259 | }, 260 | "execution_count": 12, 261 | "metadata": {}, 262 | "output_type": "execute_result" 263 | } 264 | ], 265 | "source": [ 266 | "# parse data from the html into a beautifulsoup object\n", 267 | "soup = BeautifulSoup(response.text, 'html.parser')\n", 268 | "soup\n", 269 | "\n", 270 | "# Find the table of switch sales \n", 271 | "html_table =soup.find('table',{'class':\"wikitable\"})\n", 272 | "html_table\n", 273 | "\n", 274 | "# Convert the wikipedia table into a pandas dataframe\n", 275 | "switch_sales_df = pd.read_html(str(html_table))[0]\n", 276 | "switch_sales_df" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 23, 282 | "metadata": {}, 283 | "outputs": [], 284 | "source": [ 285 | "def copies_sold_to_numeric(value):\n", 286 | " parsed_string = re.sub(r'million(\\[[a-z]*[0-9]*\\])*', '', value)\n", 287 | " return float(parsed_string) * 1000000\n", 288 | "\n", 289 | "def convert_date(date):\n", 290 | " # Month day, Year\n", 291 | " format_type = '%B %d, %Y' # The format it currently is\n", 292 | " datetime_str = datetime.datetime.strptime(date, format_type)\n", 293 | " return datetime_str.strftime(\"%m/%d/%Y\") # mm/dd/yyyy\n", 294 | "\n", 295 | "def create_metacritic_url(title):\n", 296 | " mod_title = re.sub(r'[.:,\\']', \"\", title)\n", 297 | " mod_title = mod_title.replace(\" \", \"-\")\n", 298 | " mod_title = mod_title.replace(\"é\", \"e\")\n", 299 | " return 'https://www.metacritic.com/game/switch/' + mod_title.lower() + \"/details\"" 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": 26, 305 | "metadata": {}, 306 | "outputs": [ 307 | { 308 | "data": { 309 | "text/html": [ 310 | "
\n", 311 | "\n", 324 | "\n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 414 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | "
RankTitleGenre(s)Developer(s)Publisher(s)Release_dateAs_ofCopies_soldMetacritic_url
01Mario Kart 8 DeluxeKart racingNintendo EPDNintendo04/28/201703/31/202353790000.0https://www.metacritic.com/game/switch/mario-k...
12Animal Crossing: New HorizonsSocial simulationNintendo EPDNintendo03/20/202003/31/202342210000.0https://www.metacritic.com/game/switch/animal-...
23Super Smash Bros. UltimateFightingBandai Namco StudiosSora Ltd.Nintendo12/07/201803/31/202331090000.0https://www.metacritic.com/game/switch/super-s...
34The Legend of Zelda: Breath of the WildAction-adventureNintendo EPDNintendo03/03/201703/31/202329810000.0https://www.metacritic.com/game/switch/the-leg...
45Pokémon Sword and ShieldRole-playingGame FreakThe Pokémon CompanyNintendo11/15/201903/31/202325820000.0https://www.metacritic.com/game/switch/pokemon...
..............................
7372Fitness BoxingExergamerhythmImagineerJP: ImagineerNA/PAL: Nintendo12/20/201809/08/20201000000.0https://www.metacritic.com/game/switch/fitness...
7472Fitness Boxing 2: Rhythm and ExerciseExergamerhythmImagineerJP: ImagineerNA/PAL: Nintendo12/04/202012/09/20211000000.0https://www.metacritic.com/game/switch/fitness...
7572Shin Megami Tensei VRole-playingAtlusJP: AtlusNA: SegaPAL: Nintendo11/11/202104/18/20221000000.0https://www.metacritic.com/game/switch/shin-me...
7672Story of Seasons: Pioneers of Olive TownSimulationrole-playingMarvelousXseed Games02/25/202111/18/20211000000.0https://www.metacritic.com/game/switch/story-o...
7772Thief SimulatorStealthNoble MuffinsForever Entertainment05/19/201907/16/20211000000.0https://www.metacritic.com/game/switch/thief-s...
\n", 474 | "

78 rows × 9 columns

\n", 475 | "
" 476 | ], 477 | "text/plain": [ 478 | " Rank Title Genre(s) \n", 479 | "0 1 Mario Kart 8 Deluxe Kart racing \\\n", 480 | "1 2 Animal Crossing: New Horizons Social simulation \n", 481 | "2 3 Super Smash Bros. Ultimate Fighting \n", 482 | "3 4 The Legend of Zelda: Breath of the Wild Action-adventure \n", 483 | "4 5 Pokémon Sword and Shield Role-playing \n", 484 | ".. ... ... ... \n", 485 | "73 72 Fitness Boxing Exergamerhythm \n", 486 | "74 72 Fitness Boxing 2: Rhythm and Exercise Exergamerhythm \n", 487 | "75 72 Shin Megami Tensei V Role-playing \n", 488 | "76 72 Story of Seasons: Pioneers of Olive Town Simulationrole-playing \n", 489 | "77 72 Thief Simulator Stealth \n", 490 | "\n", 491 | " Developer(s) Publisher(s) \n", 492 | "0 Nintendo EPD Nintendo \\\n", 493 | "1 Nintendo EPD Nintendo \n", 494 | "2 Bandai Namco StudiosSora Ltd. Nintendo \n", 495 | "3 Nintendo EPD Nintendo \n", 496 | "4 Game Freak The Pokémon CompanyNintendo \n", 497 | ".. ... ... \n", 498 | "73 Imagineer JP: ImagineerNA/PAL: Nintendo \n", 499 | "74 Imagineer JP: ImagineerNA/PAL: Nintendo \n", 500 | "75 Atlus JP: AtlusNA: SegaPAL: Nintendo \n", 501 | "76 Marvelous Xseed Games \n", 502 | "77 Noble Muffins Forever Entertainment \n", 503 | "\n", 504 | " Release_date As_of Copies_sold \n", 505 | "0 04/28/2017 03/31/2023 53790000.0 \\\n", 506 | "1 03/20/2020 03/31/2023 42210000.0 \n", 507 | "2 12/07/2018 03/31/2023 31090000.0 \n", 508 | "3 03/03/2017 03/31/2023 29810000.0 \n", 509 | "4 11/15/2019 03/31/2023 25820000.0 \n", 510 | ".. ... ... ... \n", 511 | "73 12/20/2018 09/08/2020 1000000.0 \n", 512 | "74 12/04/2020 12/09/2021 1000000.0 \n", 513 | "75 11/11/2021 04/18/2022 1000000.0 \n", 514 | "76 02/25/2021 11/18/2021 1000000.0 \n", 515 | "77 05/19/2019 07/16/2021 1000000.0 \n", 516 | "\n", 517 | " Metacritic_url \n", 518 | "0 https://www.metacritic.com/game/switch/mario-k... \n", 519 | "1 https://www.metacritic.com/game/switch/animal-... \n", 520 | "2 https://www.metacritic.com/game/switch/super-s... \n", 521 | "3 https://www.metacritic.com/game/switch/the-leg... \n", 522 | "4 https://www.metacritic.com/game/switch/pokemon... \n", 523 | ".. ... \n", 524 | "73 https://www.metacritic.com/game/switch/fitness... \n", 525 | "74 https://www.metacritic.com/game/switch/fitness... \n", 526 | "75 https://www.metacritic.com/game/switch/shin-me... \n", 527 | "76 https://www.metacritic.com/game/switch/story-o... \n", 528 | "77 https://www.metacritic.com/game/switch/thief-s... \n", 529 | "\n", 530 | "[78 rows x 9 columns]" 531 | ] 532 | }, 533 | "execution_count": 26, 534 | "metadata": {}, 535 | "output_type": "execute_result" 536 | } 537 | ], 538 | "source": [ 539 | "# Mutate variables to better fit data standards\n", 540 | "switch_sales_df_formatted = switch_sales_df.assign(\n", 541 | " Release_date = lambda df: df['Release date[a]'].map(lambda var: convert_date(var) ),\n", 542 | " As_of = lambda df: df['As of'].map(lambda var: convert_date(var) ),\n", 543 | " Copies_sold = lambda df: df['Copies sold'].map(lambda var: copies_sold_to_numeric(var)),\n", 544 | " Metacritic_url = lambda df: df['Title'].map(lambda var: create_metacritic_url(var) )\n", 545 | ").drop(columns=['Release date[a]', 'As of', 'Copies sold'], axis= 1)\n", 546 | "switch_sales_df_formatted" 547 | ] 548 | }, 549 | { 550 | "cell_type": "code", 551 | "execution_count": 41, 552 | "metadata": {}, 553 | "outputs": [ 554 | { 555 | "data": { 556 | "text/plain": [ 557 | "{'description': \"Race and battle your friends in the definitive version of Mario Kart 8. Hit the road with the definitive version of Mario Kart 8 and play anytime, anywhere! Race your friends or battle them in a revised battle mode on new and returning battle courses. Play locally in up to 4-player multiplayer in 1080p while playing in TV Mode. Every track from the Wii U version, including DLC, makes a glorious return. Plus, the Inklings appear as all-new guest characters, along with returning favorites, such as King Boo, Dry Bones, and Bowser Jr. Race your friends in the definitive version of Mario Kart 8, only on Nintendo Switch. Race as every character on every track from the Wii U version, including DLC characters and tracks. Pop some balloons in the revamped Battle mode, complete with Balloon Battle and Bob-omb Blast. Battle on new courses, like Urchin Underpass and Battle Stadium, or returning ones, such as GCN Luigi's Mansion and SNES Battle Course 1. Inkling Girl & Inkling Boy from Splatoon, King Boo, Dry Bones, and Bowser Jr. join the roster. Players can choose a new Smart Steering feature which makes driving and staying on the track easy for novice players and kids even at 200cc. Three new vehicles have been added, two are even inspired by Splatoon. Carry two items at the same time. Returning items include Boo, the item stealing ghost, and the Feather, which gives you a high jump in battle mode. Play your friends in local wireless multiplayer with up to 8 players. Drive through in 1080p HD quality in TV mode. Play on the go with handheld mode and play anytime, anywhere.\",\n", 558 | " 'meta_score': 92.0,\n", 559 | " 'meta_ratings': 95,\n", 560 | " 'user_score': 8.6,\n", 561 | " 'user_ratings': 2698}" 562 | ] 563 | }, 564 | "execution_count": 41, 565 | "metadata": {}, 566 | "output_type": "execute_result" 567 | } 568 | ], 569 | "source": [ 570 | "req = requests.get(\"https://www.metacritic.com/game/switch/mario-kart-8-deluxe/details\", headers=headers)\n", 571 | "soup = BeautifulSoup(req.text, 'html.parser')\n", 572 | "\n", 573 | "description = soup.find(\"div\", class_ = \"summary_detail product_summary\").find(\"span\", class_='data').text\n", 574 | "meta_score = soup.find(\"div\", class_ = \"metascore_wrap feature_metascore\").find(\"span\").text\n", 575 | "meta_ratings = soup.find(\"div\", class_ = \"metascore_wrap feature_metascore\").find(\"div\",class_=\"summary\").find(\"p\").find(\"span\", class_=\"count\").find(\"a\").find(\"span\").text\n", 576 | "user_score = soup.find(\"div\", class_ = \"userscore_wrap feature_userscore\").find(\"a\", class_ = \"metascore_anchor\").find(\"div\").text\n", 577 | "user_ratings = soup.find(\"div\", class_ = \"userscore_wrap feature_userscore\").find(\"div\",class_=\"summary\").find(\"span\", class_=\"count\").text\n", 578 | "\n", 579 | "meta_ratings = re.findall('[0-9]+', meta_ratings)[0]\n", 580 | "user_ratings = re.findall('[0-9]+', user_ratings)[0]\n", 581 | "\n", 582 | "{'description' : description, \n", 583 | " 'meta_score' : float(meta_score) , \n", 584 | " 'meta_ratings' : int(meta_ratings), \n", 585 | " 'user_score' : float(user_score), \n", 586 | " 'user_ratings' : int(user_ratings) }\n" 587 | ] 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": 45, 592 | "metadata": {}, 593 | "outputs": [], 594 | "source": [ 595 | "def scrape_metacritic(html):\n", 596 | " soup = BeautifulSoup(html, 'html.parser')\n", 597 | "\n", 598 | " description = soup.find(\"div\", class_ = \"summary_detail product_summary\").find(\"span\", class_='data').text\n", 599 | " meta_score = soup.find(\"div\", class_ = \"metascore_wrap feature_metascore\").find(\"span\").text\n", 600 | " meta_ratings = soup.find(\"div\", class_ = \"metascore_wrap feature_metascore\").find(\"div\",class_=\"summary\").find(\"p\").find(\"span\", class_=\"count\").find(\"a\").find(\"span\").text\n", 601 | " user_score = soup.find(\"div\", class_ = \"userscore_wrap feature_userscore\").find(\"a\", class_ = \"metascore_anchor\").find(\"div\").text\n", 602 | " user_ratings = soup.find(\"div\", class_ = \"userscore_wrap feature_userscore\").find(\"div\",class_=\"summary\").find(\"span\", class_=\"count\").text\n", 603 | "\n", 604 | " meta_ratings = re.findall('[0-9]+', meta_ratings)[0]\n", 605 | " user_ratings = re.findall('[0-9]+', user_ratings)[0]\n", 606 | "\n", 607 | " return {'description' : description, \n", 608 | " 'meta_score' : float(meta_score) , \n", 609 | " 'meta_ratings' : int(meta_ratings), \n", 610 | " 'user_score' : float(user_score), \n", 611 | " 'user_ratings' : int(user_ratings) }\n", 612 | "\n", 613 | "def perform_random_delay(delay = 3, random_offset = .5):\n", 614 | " time.sleep(delay + random.uniform(0,random_offset))" 615 | ] 616 | }, 617 | { 618 | "cell_type": "code", 619 | "execution_count": 46, 620 | "metadata": {}, 621 | "outputs": [], 622 | "source": [ 623 | "urls = switch_sales_df_formatted[\"Metacritic_url\"].tolist()\n", 624 | "metacritic_results = []\n", 625 | "\n", 626 | "for url in urls:\n", 627 | " # Go to the URL\n", 628 | " req = requests.get(url, headers=headers)\n", 629 | " # If the url is successfully reached, then attempt to scrape the page\n", 630 | " if req.status_code == 200:\n", 631 | " # PERFORM SCRAPING LOGIC HERE\n", 632 | " try:\n", 633 | " curr_url_result = scrape_metacritic(req.text)\n", 634 | " curr_url_result['Metacritic_url'], curr_url_result['status_code'] = url, 200\n", 635 | " curr_url_result['parsed'] = True\n", 636 | " except: \n", 637 | " # If the url is not valid, then return an empty dicitonary and denote the status code\n", 638 | " curr_url_result = {'description' : '','meta_score' : '', 'meta_ratings' : '', \n", 639 | " 'user_score' : '', 'user_ratings' :'', 'status_code' : 200,\n", 640 | " 'Metacritic_url' : url, 'parsed' : False}\n", 641 | " else: \n", 642 | " # If the url is not valid, then return an empty dicitonary and denote the status code\n", 643 | " curr_url_result = {'description' : '','meta_score' : '', 'meta_ratings' : '', \n", 644 | " 'user_score' : '', 'user_ratings' :'', 'status_code' : req.status_code,\n", 645 | " 'Metacritic_url' : url, 'parsed' : False}\n", 646 | " # append the result\n", 647 | " metacritic_results.append(curr_url_result)\n", 648 | " # perform a random delay between scrapes\n", 649 | " perform_random_delay()" 650 | ] 651 | }, 652 | { 653 | "cell_type": "code", 654 | "execution_count": 48, 655 | "metadata": {}, 656 | "outputs": [ 657 | { 658 | "data": { 659 | "text/html": [ 660 | "
\n", 661 | "\n", 674 | "\n", 675 | " \n", 676 | " \n", 677 | " \n", 678 | " \n", 679 | " \n", 680 | " \n", 681 | " \n", 682 | " \n", 683 | " \n", 684 | " \n", 685 | " \n", 686 | " \n", 687 | " \n", 688 | " \n", 689 | " \n", 690 | " \n", 691 | " \n", 692 | " \n", 693 | " \n", 694 | " \n", 695 | " \n", 696 | " \n", 697 | " \n", 698 | " \n", 699 | " \n", 700 | " \n", 701 | " \n", 702 | " \n", 703 | " \n", 704 | " \n", 705 | " \n", 706 | " \n", 707 | " \n", 708 | " \n", 709 | " \n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | " \n", 716 | " \n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | " \n", 743 | " \n", 744 | " \n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | "
descriptionmeta_scoremeta_ratingsuser_scoreuser_ratingsMetacritic_urlstatus_codeparsed
0Race and battle your friends in the definitive...92.0958.62698https://www.metacritic.com/game/switch/mario-k...200True
1If the hustle and bustle of modern life’s got ...90.01115.66583https://www.metacritic.com/game/switch/animal-...200True
2Inklings from the Splatoon series, as well as ...93.0998.63780https://www.metacritic.com/game/switch/super-s...200True
3Forget everything you know about The Legend of...97.01098.720388https://www.metacritic.com/game/switch/the-leg...200True
4https://www.metacritic.com/game/switch/pokemon...404False
...........................
73Get off the couch and get moving with fun, box...66.0146.550https://www.metacritic.com/game/switch/fitness...200True
74https://www.metacritic.com/game/switch/fitness...404False
75The ambitions of god and human clash amidst th...84.0987.5749https://www.metacritic.com/game/switch/shin-me...200True
76Inspired by tales of their grandfather's pione...71.0607.188https://www.metacritic.com/game/switch/story-o...200True
77https://www.metacritic.com/game/switch/thief-s...200False
\n", 812 | "

78 rows × 8 columns

\n", 813 | "
" 814 | ], 815 | "text/plain": [ 816 | " description meta_score meta_ratings \n", 817 | "0 Race and battle your friends in the definitive... 92.0 95 \\\n", 818 | "1 If the hustle and bustle of modern life’s got ... 90.0 111 \n", 819 | "2 Inklings from the Splatoon series, as well as ... 93.0 99 \n", 820 | "3 Forget everything you know about The Legend of... 97.0 109 \n", 821 | "4 \n", 822 | ".. ... ... ... \n", 823 | "73 Get off the couch and get moving with fun, box... 66.0 14 \n", 824 | "74 \n", 825 | "75 The ambitions of god and human clash amidst th... 84.0 98 \n", 826 | "76 Inspired by tales of their grandfather's pione... 71.0 60 \n", 827 | "77 \n", 828 | "\n", 829 | " user_score user_ratings Metacritic_url \n", 830 | "0 8.6 2698 https://www.metacritic.com/game/switch/mario-k... \\\n", 831 | "1 5.6 6583 https://www.metacritic.com/game/switch/animal-... \n", 832 | "2 8.6 3780 https://www.metacritic.com/game/switch/super-s... \n", 833 | "3 8.7 20388 https://www.metacritic.com/game/switch/the-leg... \n", 834 | "4 https://www.metacritic.com/game/switch/pokemon... \n", 835 | ".. ... ... ... \n", 836 | "73 6.5 50 https://www.metacritic.com/game/switch/fitness... \n", 837 | "74 https://www.metacritic.com/game/switch/fitness... \n", 838 | "75 7.5 749 https://www.metacritic.com/game/switch/shin-me... \n", 839 | "76 7.1 88 https://www.metacritic.com/game/switch/story-o... \n", 840 | "77 https://www.metacritic.com/game/switch/thief-s... \n", 841 | "\n", 842 | " status_code parsed \n", 843 | "0 200 True \n", 844 | "1 200 True \n", 845 | "2 200 True \n", 846 | "3 200 True \n", 847 | "4 404 False \n", 848 | ".. ... ... \n", 849 | "73 200 True \n", 850 | "74 404 False \n", 851 | "75 200 True \n", 852 | "76 200 True \n", 853 | "77 200 False \n", 854 | "\n", 855 | "[78 rows x 8 columns]" 856 | ] 857 | }, 858 | "execution_count": 48, 859 | "metadata": {}, 860 | "output_type": "execute_result" 861 | } 862 | ], 863 | "source": [ 864 | "reviews_df = pd.DataFrame(metacritic_results)\n", 865 | "reviews_df" 866 | ] 867 | }, 868 | { 869 | "cell_type": "code", 870 | "execution_count": 49, 871 | "metadata": {}, 872 | "outputs": [ 873 | { 874 | "data": { 875 | "text/html": [ 876 | "
\n", 877 | "\n", 890 | "\n", 891 | " \n", 892 | " \n", 893 | " \n", 894 | " \n", 895 | " \n", 896 | " \n", 897 | " \n", 898 | " \n", 899 | " \n", 900 | " \n", 901 | " \n", 902 | " \n", 903 | " \n", 904 | " \n", 905 | " \n", 906 | " \n", 907 | " \n", 908 | " \n", 909 | " \n", 910 | " \n", 911 | " \n", 912 | " \n", 913 | " \n", 914 | " \n", 915 | " \n", 916 | " \n", 917 | " \n", 918 | " \n", 919 | " \n", 920 | " \n", 921 | " \n", 922 | " \n", 923 | " \n", 924 | " \n", 925 | " \n", 926 | " \n", 927 | " \n", 928 | " \n", 929 | " \n", 930 | " \n", 931 | " \n", 932 | " \n", 933 | " \n", 934 | " \n", 935 | " \n", 936 | " \n", 937 | " \n", 938 | " \n", 939 | " \n", 940 | " \n", 941 | " \n", 942 | " \n", 943 | " \n", 944 | " \n", 945 | " \n", 946 | " \n", 947 | " \n", 948 | " \n", 949 | " \n", 950 | " \n", 951 | " \n", 952 | " \n", 953 | " \n", 954 | " \n", 955 | " \n", 956 | " \n", 957 | " \n", 958 | " \n", 959 | " \n", 960 | " \n", 961 | " \n", 962 | " \n", 963 | " \n", 964 | " \n", 965 | " \n", 966 | " \n", 967 | " \n", 968 | " \n", 969 | " \n", 970 | " \n", 971 | " \n", 972 | " \n", 973 | " \n", 974 | " \n", 975 | " \n", 976 | " \n", 977 | " \n", 978 | " \n", 979 | " \n", 980 | " \n", 981 | " \n", 982 | " \n", 983 | " \n", 984 | " \n", 985 | " \n", 986 | " \n", 987 | " \n", 988 | " \n", 989 | " \n", 990 | " \n", 991 | " \n", 992 | " \n", 993 | " \n", 994 | " \n", 995 | " \n", 996 | " \n", 997 | " \n", 998 | " \n", 999 | " \n", 1000 | " \n", 1001 | " \n", 1002 | " \n", 1003 | " \n", 1004 | " \n", 1005 | " \n", 1006 | " \n", 1007 | " \n", 1008 | " \n", 1009 | " \n", 1010 | " \n", 1011 | " \n", 1012 | " \n", 1013 | " \n", 1014 | " \n", 1015 | " \n", 1016 | " \n", 1017 | " \n", 1018 | " \n", 1019 | " \n", 1020 | " \n", 1021 | " \n", 1022 | " \n", 1023 | " \n", 1024 | " \n", 1025 | " \n", 1026 | " \n", 1027 | " \n", 1028 | " \n", 1029 | " \n", 1030 | " \n", 1031 | " \n", 1032 | " \n", 1033 | " \n", 1034 | " \n", 1035 | " \n", 1036 | " \n", 1037 | " \n", 1038 | " \n", 1039 | " \n", 1040 | " \n", 1041 | " \n", 1042 | " \n", 1043 | " \n", 1044 | " \n", 1045 | " \n", 1046 | " \n", 1047 | " \n", 1048 | " \n", 1049 | " \n", 1050 | " \n", 1051 | " \n", 1052 | " \n", 1053 | " \n", 1054 | " \n", 1055 | " \n", 1056 | " \n", 1057 | " \n", 1058 | " \n", 1059 | " \n", 1060 | " \n", 1061 | " \n", 1062 | " \n", 1063 | " \n", 1064 | " \n", 1065 | " \n", 1066 | " \n", 1067 | " \n", 1068 | " \n", 1069 | " \n", 1070 | " \n", 1071 | " \n", 1072 | " \n", 1073 | " \n", 1074 | " \n", 1075 | " \n", 1076 | " \n", 1077 | " \n", 1078 | " \n", 1079 | " \n", 1080 | " \n", 1081 | " \n", 1082 | " \n", 1083 | " \n", 1084 | " \n", 1085 | " \n", 1086 | " \n", 1087 | " \n", 1088 | " \n", 1089 | " \n", 1090 | " \n", 1091 | " \n", 1092 | " \n", 1093 | " \n", 1094 | " \n", 1095 | " \n", 1096 | " \n", 1097 | " \n", 1098 | " \n", 1099 | " \n", 1100 | " \n", 1101 | " \n", 1102 | " \n", 1103 | " \n", 1104 | " \n", 1105 | " \n", 1106 | " \n", 1107 | " \n", 1108 | " \n", 1109 | " \n", 1110 | " \n", 1111 | " \n", 1112 | " \n", 1113 | " \n", 1114 | " \n", 1115 | " \n", 1116 | " \n", 1117 | " \n", 1118 | " \n", 1119 | " \n", 1120 | " \n", 1121 | " \n", 1122 | " \n", 1123 | "
RankTitleGenre(s)Developer(s)Publisher(s)Release_dateAs_ofCopies_soldMetacritic_urldescriptionmeta_scoremeta_ratingsuser_scoreuser_ratingsstatus_codeparsed
01Mario Kart 8 DeluxeKart racingNintendo EPDNintendo04/28/201703/31/202353790000.0https://www.metacritic.com/game/switch/mario-k...Race and battle your friends in the definitive...92.0958.62698200True
12Animal Crossing: New HorizonsSocial simulationNintendo EPDNintendo03/20/202003/31/202342210000.0https://www.metacritic.com/game/switch/animal-...If the hustle and bustle of modern life’s got ...90.01115.66583200True
23Super Smash Bros. UltimateFightingBandai Namco StudiosSora Ltd.Nintendo12/07/201803/31/202331090000.0https://www.metacritic.com/game/switch/super-s...Inklings from the Splatoon series, as well as ...93.0998.63780200True
34The Legend of Zelda: Breath of the WildAction-adventureNintendo EPDNintendo03/03/201703/31/202329810000.0https://www.metacritic.com/game/switch/the-leg...Forget everything you know about The Legend of...97.01098.720388200True
45Pokémon Sword and ShieldRole-playingGame FreakThe Pokémon CompanyNintendo11/15/201903/31/202325820000.0https://www.metacritic.com/game/switch/pokemon...404False
...................................................
7372Fitness BoxingExergamerhythmImagineerJP: ImagineerNA/PAL: Nintendo12/20/201809/08/20201000000.0https://www.metacritic.com/game/switch/fitness...Get off the couch and get moving with fun, box...66.0146.550200True
7472Fitness Boxing 2: Rhythm and ExerciseExergamerhythmImagineerJP: ImagineerNA/PAL: Nintendo12/04/202012/09/20211000000.0https://www.metacritic.com/game/switch/fitness...404False
7572Shin Megami Tensei VRole-playingAtlusJP: AtlusNA: SegaPAL: Nintendo11/11/202104/18/20221000000.0https://www.metacritic.com/game/switch/shin-me...The ambitions of god and human clash amidst th...84.0987.5749200True
7672Story of Seasons: Pioneers of Olive TownSimulationrole-playingMarvelousXseed Games02/25/202111/18/20211000000.0https://www.metacritic.com/game/switch/story-o...Inspired by tales of their grandfather's pione...71.0607.188200True
7772Thief SimulatorStealthNoble MuffinsForever Entertainment05/19/201907/16/20211000000.0https://www.metacritic.com/game/switch/thief-s...200False
\n", 1124 | "

78 rows × 16 columns

\n", 1125 | "
" 1126 | ], 1127 | "text/plain": [ 1128 | " Rank Title Genre(s) \n", 1129 | "0 1 Mario Kart 8 Deluxe Kart racing \\\n", 1130 | "1 2 Animal Crossing: New Horizons Social simulation \n", 1131 | "2 3 Super Smash Bros. Ultimate Fighting \n", 1132 | "3 4 The Legend of Zelda: Breath of the Wild Action-adventure \n", 1133 | "4 5 Pokémon Sword and Shield Role-playing \n", 1134 | ".. ... ... ... \n", 1135 | "73 72 Fitness Boxing Exergamerhythm \n", 1136 | "74 72 Fitness Boxing 2: Rhythm and Exercise Exergamerhythm \n", 1137 | "75 72 Shin Megami Tensei V Role-playing \n", 1138 | "76 72 Story of Seasons: Pioneers of Olive Town Simulationrole-playing \n", 1139 | "77 72 Thief Simulator Stealth \n", 1140 | "\n", 1141 | " Developer(s) Publisher(s) \n", 1142 | "0 Nintendo EPD Nintendo \\\n", 1143 | "1 Nintendo EPD Nintendo \n", 1144 | "2 Bandai Namco StudiosSora Ltd. Nintendo \n", 1145 | "3 Nintendo EPD Nintendo \n", 1146 | "4 Game Freak The Pokémon CompanyNintendo \n", 1147 | ".. ... ... \n", 1148 | "73 Imagineer JP: ImagineerNA/PAL: Nintendo \n", 1149 | "74 Imagineer JP: ImagineerNA/PAL: Nintendo \n", 1150 | "75 Atlus JP: AtlusNA: SegaPAL: Nintendo \n", 1151 | "76 Marvelous Xseed Games \n", 1152 | "77 Noble Muffins Forever Entertainment \n", 1153 | "\n", 1154 | " Release_date As_of Copies_sold \n", 1155 | "0 04/28/2017 03/31/2023 53790000.0 \\\n", 1156 | "1 03/20/2020 03/31/2023 42210000.0 \n", 1157 | "2 12/07/2018 03/31/2023 31090000.0 \n", 1158 | "3 03/03/2017 03/31/2023 29810000.0 \n", 1159 | "4 11/15/2019 03/31/2023 25820000.0 \n", 1160 | ".. ... ... ... \n", 1161 | "73 12/20/2018 09/08/2020 1000000.0 \n", 1162 | "74 12/04/2020 12/09/2021 1000000.0 \n", 1163 | "75 11/11/2021 04/18/2022 1000000.0 \n", 1164 | "76 02/25/2021 11/18/2021 1000000.0 \n", 1165 | "77 05/19/2019 07/16/2021 1000000.0 \n", 1166 | "\n", 1167 | " Metacritic_url \n", 1168 | "0 https://www.metacritic.com/game/switch/mario-k... \\\n", 1169 | "1 https://www.metacritic.com/game/switch/animal-... \n", 1170 | "2 https://www.metacritic.com/game/switch/super-s... \n", 1171 | "3 https://www.metacritic.com/game/switch/the-leg... \n", 1172 | "4 https://www.metacritic.com/game/switch/pokemon... \n", 1173 | ".. ... \n", 1174 | "73 https://www.metacritic.com/game/switch/fitness... \n", 1175 | "74 https://www.metacritic.com/game/switch/fitness... \n", 1176 | "75 https://www.metacritic.com/game/switch/shin-me... \n", 1177 | "76 https://www.metacritic.com/game/switch/story-o... \n", 1178 | "77 https://www.metacritic.com/game/switch/thief-s... \n", 1179 | "\n", 1180 | " description meta_score meta_ratings \n", 1181 | "0 Race and battle your friends in the definitive... 92.0 95 \\\n", 1182 | "1 If the hustle and bustle of modern life’s got ... 90.0 111 \n", 1183 | "2 Inklings from the Splatoon series, as well as ... 93.0 99 \n", 1184 | "3 Forget everything you know about The Legend of... 97.0 109 \n", 1185 | "4 \n", 1186 | ".. ... ... ... \n", 1187 | "73 Get off the couch and get moving with fun, box... 66.0 14 \n", 1188 | "74 \n", 1189 | "75 The ambitions of god and human clash amidst th... 84.0 98 \n", 1190 | "76 Inspired by tales of their grandfather's pione... 71.0 60 \n", 1191 | "77 \n", 1192 | "\n", 1193 | " user_score user_ratings status_code parsed \n", 1194 | "0 8.6 2698 200 True \n", 1195 | "1 5.6 6583 200 True \n", 1196 | "2 8.6 3780 200 True \n", 1197 | "3 8.7 20388 200 True \n", 1198 | "4 404 False \n", 1199 | ".. ... ... ... ... \n", 1200 | "73 6.5 50 200 True \n", 1201 | "74 404 False \n", 1202 | "75 7.5 749 200 True \n", 1203 | "76 7.1 88 200 True \n", 1204 | "77 200 False \n", 1205 | "\n", 1206 | "[78 rows x 16 columns]" 1207 | ] 1208 | }, 1209 | "execution_count": 49, 1210 | "metadata": {}, 1211 | "output_type": "execute_result" 1212 | } 1213 | ], 1214 | "source": [ 1215 | "# Merge the two dataframes together\n", 1216 | "switch_sales_reviews_df = switch_sales_df_formatted.merge(reviews_df)\n", 1217 | "switch_sales_reviews_df" 1218 | ] 1219 | }, 1220 | { 1221 | "cell_type": "code", 1222 | "execution_count": 51, 1223 | "metadata": {}, 1224 | "outputs": [ 1225 | { 1226 | "data": { 1227 | "text/plain": [ 1228 | "0.8846153846153846" 1229 | ] 1230 | }, 1231 | "execution_count": 51, 1232 | "metadata": {}, 1233 | "output_type": "execute_result" 1234 | } 1235 | ], 1236 | "source": [ 1237 | "sum(switch_sales_reviews_df['parsed']) / switch_sales_reviews_df.shape[0]" 1238 | ] 1239 | }, 1240 | { 1241 | "cell_type": "code", 1242 | "execution_count": null, 1243 | "metadata": {}, 1244 | "outputs": [], 1245 | "source": [ 1246 | "switch_sales_reviews_df.to_excel(\"Switch Sales and Reviews.xlsx\", index = False)\n", 1247 | "switch_sales_reviews_df.to_csv(\"Switch Sales and Reviews.csv\", index = False)" 1248 | ] 1249 | } 1250 | ], 1251 | "metadata": { 1252 | "kernelspec": { 1253 | "display_name": "scraper_tutorial_env", 1254 | "language": "python", 1255 | "name": "python3" 1256 | }, 1257 | "language_info": { 1258 | "codemirror_mode": { 1259 | "name": "ipython", 1260 | "version": 3 1261 | }, 1262 | "file_extension": ".py", 1263 | "mimetype": "text/x-python", 1264 | "name": "python", 1265 | "nbconvert_exporter": "python", 1266 | "pygments_lexer": "ipython3", 1267 | "version": "3.11.3" 1268 | }, 1269 | "orig_nbformat": 4 1270 | }, 1271 | "nbformat": 4, 1272 | "nbformat_minor": 2 1273 | } 1274 | -------------------------------------------------------------------------------- /TicTacToe/helpers.py: -------------------------------------------------------------------------------- 1 | def draw_board(spots): 2 | board = (f"|{spots[1]}|{spots[2]}|{spots[3]}|\n" 3 | f"|{spots[4]}|{spots[5]}|{spots[6]}|\n" 4 | f"|{spots[7]}|{spots[8]}|{spots[9]}|") 5 | print(board) 6 | 7 | 8 | def check_turn(turn): 9 | if turn % 2 == 0: return 'O' 10 | else: return 'X' 11 | 12 | def check_for_win(dict): 13 | # Handle Horizontal Cases 14 | if (spots[1] == spots[2] == spots[3]) \ 15 | or (spots[4] == spots[5] == spots[6]) \ 16 | or (spots[7] == spots[8] == spots[9]): 17 | return True 18 | # Handle Vertical Cases 19 | elif (spots[1] == spots[4] == spots[7]) \ 20 | or (spots[2] == spots[5] == spots[8]) \ 21 | or (spots[3] == spots[6] == spots[9]): 22 | return True 23 | # Diagonal Cases 24 | elif (spots[1] == spots[5] == spots[9]) \ 25 | or (spots[3] == spots[5] == spots[7]): 26 | return True 27 | 28 | else: return False -------------------------------------------------------------------------------- /TicTacToe/tictactoe.py: -------------------------------------------------------------------------------- 1 | import os 2 | from helpers import check_turn, check_for_win, draw_board 3 | 4 | # Declare all the variables we're going to need 5 | spots = {1 : '1', 2 : '2', 3: '3', 4 : '4', 5 : '5', 6 | 6 : '6', 7 : '7', 8 : '8', 9 : '9'} 7 | playing, complete = True, False 8 | turn = 0 9 | prev_turn = -1 10 | # Game Loop 11 | while playing: 12 | # Reset the screen 13 | os.system('cls' if os.name == 'nt' else 'clear') 14 | # Draw the current Game Board 15 | draw_board(spots) 16 | # If an invalid turn occurred, let the player know 17 | if prev_turn == turn: 18 | print("Invalid spot selected, please pick another.") 19 | prev_turn = turn 20 | print("Player " + str((turn % 2) +1 ) + "'s turn: Pick your spot or press q to quit") 21 | 22 | # Get input and make sure it's valid 23 | choice = input() 24 | # The game has ended, 25 | if choice == 'q': 26 | playing = False 27 | elif str.isdigit(choice) and int(choice) in spots: 28 | # Check if the spot is already taken. 29 | if not spots[int(choice)] in {"X", "O"}: 30 | # If not, update board and increment the turn 31 | turn += 1 32 | spots[int(choice)] = check_turn(turn) 33 | 34 | # Check if the game has ended (and if someone won) 35 | if check_for_win(spots): playing, complete = False, True 36 | if turn > 8: playing = False 37 | 38 | # Update the board one last time. 39 | os.system('cls' if os.name == 'nt' else 'clear') 40 | draw_board(spots) 41 | # If there was a winner, say who won 42 | if complete: 43 | if check_turn(turn) == 'X': print("Player 1 Wins!") 44 | else: print("Player 2 Wins!") 45 | else: 46 | # Tie Game 47 | print("No Winner") 48 | 49 | print("Thanks for playing!") -------------------------------------------------------------------------------- /Tile Collisions/main.py: -------------------------------------------------------------------------------- 1 | from tiles import * 2 | from spritesheet import Spritesheet 3 | from player import Player 4 | ################################# LOAD UP A BASIC WINDOW AND CLOCK ################################# 5 | pygame.init() 6 | DISPLAY_W, DISPLAY_H = 480, 270 7 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 8 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 9 | running = True 10 | clock = pygame.time.Clock() 11 | TARGET_FPS = 60 12 | ################################# LOAD PLAYER AND SPRITESHEET################################### 13 | spritesheet = Spritesheet('spritesheet.png') 14 | player = Player() 15 | #################################### LOAD THE LEVEL ####################################### 16 | map = TileMap('test_level.csv', spritesheet ) 17 | player.position.x, player.position.y = map.start_x, map.start_y 18 | 19 | ################################# GAME LOOP ########################## 20 | while running: 21 | dt = clock.tick(60) * .001 * TARGET_FPS 22 | ################################# CHECK PLAYER INPUT ################################# 23 | for event in pygame.event.get(): 24 | if event.type == pygame.QUIT: 25 | running = False 26 | if event.type == pygame.KEYDOWN: 27 | if event.key == pygame.K_LEFT: 28 | player.LEFT_KEY = True 29 | elif event.key == pygame.K_RIGHT: 30 | player.RIGHT_KEY = True 31 | elif event.key == pygame.K_SPACE: 32 | player.jump() 33 | 34 | 35 | if event.type == pygame.KEYUP: 36 | if event.key == pygame.K_LEFT: 37 | player.LEFT_KEY = False 38 | elif event.key == pygame.K_RIGHT: 39 | player.RIGHT_KEY = False 40 | elif event.key == pygame.K_SPACE: 41 | if player.is_jumping: 42 | player.velocity.y *= .25 43 | player.is_jumping = False 44 | 45 | 46 | ################################# UPDATE/ Animate SPRITE ################################# 47 | player.update(dt, map.tiles) 48 | ################################# UPDATE WINDOW AND DISPLAY ################################# 49 | canvas.fill((0, 180, 240)) # Fills the entire screen with light blue 50 | map.draw_map(canvas) 51 | player.draw(canvas) 52 | window.blit(canvas, (0,0)) 53 | pygame.display.update() 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Tile Collisions/player.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from spritesheet import Spritesheet 3 | 4 | class Player(pygame.sprite.Sprite): 5 | def __init__(self): 6 | pygame.sprite.Sprite.__init__(self) 7 | self.LEFT_KEY, self.RIGHT_KEY, self.FACING_LEFT = False, False, False 8 | self.is_jumping, self.on_ground = False, False 9 | self.gravity, self.friction = .35, -.12 10 | self.image = Spritesheet('spritesheet.png').parse_sprite('chick.png') 11 | self.rect = self.image.get_rect() 12 | self.position, self.velocity = pygame.math.Vector2(0,0), pygame.math.Vector2(0,0) 13 | self.acceleration = pygame.math.Vector2(0,self.gravity) 14 | 15 | def draw(self, display): 16 | display.blit(self.image, (self.rect.x, self.rect.y)) 17 | 18 | def update(self, dt, tiles): 19 | self.horizontal_movement(dt) 20 | self.checkCollisionsx(tiles) 21 | self.vertical_movement(dt) 22 | self.checkCollisionsy(tiles) 23 | 24 | def horizontal_movement(self,dt): 25 | self.acceleration.x = 0 26 | if self.LEFT_KEY: 27 | self.acceleration.x -= .3 28 | elif self.RIGHT_KEY: 29 | self.acceleration.x += .3 30 | self.acceleration.x += self.velocity.x * self.friction 31 | self.velocity.x += self.acceleration.x * dt 32 | self.limit_velocity(4) 33 | self.position.x += self.velocity.x * dt + (self.acceleration.x * .5) * (dt * dt) 34 | self.rect.x = self.position.x 35 | 36 | def vertical_movement(self,dt): 37 | self.velocity.y += self.acceleration.y * dt 38 | if self.velocity.y > 7: self.velocity.y = 7 39 | self.position.y += self.velocity.y * dt + (self.acceleration.y * .5) * (dt * dt) 40 | self.rect.bottom = self.position.y 41 | 42 | def limit_velocity(self, max_vel): 43 | self.velocity.x = max(-max_vel, min(self.velocity.x, max_vel)) 44 | if abs(self.velocity.x) < .01: self.velocity.x = 0 45 | 46 | def jump(self): 47 | if self.on_ground: 48 | self.is_jumping = True 49 | self.velocity.y -= 8 50 | self.on_ground = False 51 | 52 | def get_hits(self, tiles): 53 | hits = [] 54 | for tile in tiles: 55 | if self.rect.colliderect(tile): 56 | hits.append(tile) 57 | return hits 58 | 59 | def checkCollisionsx(self, tiles): 60 | collisions = self.get_hits(tiles) 61 | for tile in collisions: 62 | if self.velocity.x > 0: # Hit tile moving right 63 | self.position.x = tile.rect.left - self.rect.w 64 | self.rect.x = self.position.x 65 | elif self.velocity.x < 0: # Hit tile moving left 66 | self.position.x = tile.rect.right 67 | self.rect.x = self.position.x 68 | 69 | def checkCollisionsy(self, tiles): 70 | self.on_ground = False 71 | self.rect.bottom += 1 72 | collisions = self.get_hits(tiles) 73 | for tile in collisions: 74 | if self.velocity.y > 0: # Hit tile from the top 75 | self.on_ground = True 76 | self.is_jumping = False 77 | self.velocity.y = 0 78 | self.position.y = tile.rect.top 79 | self.rect.bottom = self.position.y 80 | elif self.velocity.y < 0: # Hit tile from the bottom 81 | self.velocity.y = 0 82 | self.position.y = tile.rect.bottom + self.rect.h 83 | self.rect.bottom = self.position.y 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /Tile Collisions/spritesheet.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "chick.png": 4 | { 5 | "frame": {"x":0,"y":0,"w":16,"h":16}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 9 | "sourceSize": {"w":16,"h":16} 10 | }, 11 | "grass.png": 12 | { 13 | "frame": {"x":16,"y":0,"w":16,"h":16}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 17 | "sourceSize": {"w":16,"h":16} 18 | }, 19 | "grass2.png": 20 | { 21 | "frame": {"x":32,"y":0,"w":16,"h":16}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 25 | "sourceSize": {"w":16,"h":16} 26 | }}, 27 | "meta": { 28 | "app": "https://www.codeandweb.com/texturepacker", 29 | "version": "1.0", 30 | "image": "test.png", 31 | "format": "RGBA8888", 32 | "size": {"w":48,"h":16}, 33 | "scale": "1", 34 | "smartupdate": "$TexturePacker:SmartUpdate:a66d329312f98accd6de9452ce22c2dc:dc1a8a27030eb8549f6ad949e95565fe:02ab132358d6d8b512e80119463a8329$" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tile Collisions/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Tile Collisions/spritesheet.png -------------------------------------------------------------------------------- /Tile Collisions/spritesheet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import json 3 | 4 | 5 | class Spritesheet: 6 | def __init__(self, filename): 7 | self.filename = filename 8 | self.sprite_sheet = pygame.image.load(filename).convert() 9 | self.meta_data = self.filename.replace('png', 'json') 10 | with open(self.meta_data) as f: 11 | self.data = json.load(f) 12 | f.close() 13 | 14 | 15 | 16 | def get_sprite(self, x, y, w, h): 17 | sprite = pygame.Surface((w, h)) 18 | sprite.set_colorkey((0,0,0)) 19 | sprite.blit(self.sprite_sheet,(0, 0),(x, y, w, h)) 20 | return sprite 21 | 22 | def parse_sprite(self, name): 23 | sprite = self.data['frames'][name]['frame'] 24 | x, y, w, h = sprite["x"], sprite["y"], sprite["w"], sprite["h"] 25 | image = self.get_sprite(x, y, w, h) 26 | return image 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Tile Collisions/test_level.csv: -------------------------------------------------------------------------------- 1 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 2 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 3 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 4 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 5 | -1,-1,-1,-1,-1,-1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 6 | -1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 7 | -1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 8 | -1,-1,0,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 9 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,1 10 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 11 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 12 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 13 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 14 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 15 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 16 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 17 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 18 | -------------------------------------------------------------------------------- /Tile Collisions/tiles.py: -------------------------------------------------------------------------------- 1 | import pygame, csv, os 2 | 3 | class Tile(pygame.sprite.Sprite): 4 | def __init__(self, image, x, y, spritesheet): 5 | pygame.sprite.Sprite.__init__(self) 6 | self.image = spritesheet.parse_sprite(image) 7 | # Manual load in: self.image = pygame.image.load(image) 8 | self.rect = self.image.get_rect() 9 | self.rect.x, self.rect.y = x, y 10 | 11 | def draw(self, surface): 12 | surface.blit(self.image, (self.rect.x, self.rect.y)) 13 | 14 | class TileMap(): 15 | def __init__(self, filename, spritesheet): 16 | self.tile_size = 16 17 | self.start_x, self.start_y = 0, 0 18 | self.spritesheet = spritesheet 19 | self.tiles = self.load_tiles(filename) 20 | self.map_surface = pygame.Surface((self.map_w, self.map_h)) 21 | self.map_surface.set_colorkey((0, 0, 0)) 22 | self.load_map() 23 | 24 | def draw_map(self, surface): 25 | surface.blit(self.map_surface, (0,0)) 26 | 27 | def load_map(self): 28 | for tile in self.tiles: 29 | tile.draw(self.map_surface) 30 | 31 | def read_csv(self, filename): 32 | map = [] 33 | with open(os.path.join(filename)) as data: 34 | data = csv.reader(data, delimiter=',') 35 | for row in data: 36 | map.append(list(row)) 37 | return map 38 | 39 | def load_tiles(self, filename): 40 | tiles = [] 41 | map = self.read_csv(filename) 42 | x, y = 0, 0 43 | for row in map: 44 | x = 0 45 | for tile in row: 46 | if tile == '0': 47 | self.start_x, self.start_y = x * self.tile_size, y * self.tile_size 48 | elif tile == '1': 49 | tiles.append(Tile('grass.png', x * self.tile_size, y * self.tile_size, self.spritesheet)) 50 | elif tile == '2': 51 | tiles.append(Tile('grass2.png', x * self.tile_size, y * self.tile_size, self.spritesheet)) 52 | # Move to next tile in current row 53 | x += 1 54 | 55 | # Move to next row 56 | y += 1 57 | # Store the size of the tile map 58 | self.map_w, self.map_h = x * self.tile_size, y * self.tile_size 59 | return tiles 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Tilemap/main.py: -------------------------------------------------------------------------------- 1 | from tiles import * 2 | from spritesheet import Spritesheet 3 | 4 | 5 | ################################# LOAD UP A BASIC WINDOW AND CLOCK ################################# 6 | pygame.init() 7 | DISPLAY_W, DISPLAY_H = 480, 270 8 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 9 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 10 | running = True 11 | clock = pygame.time.Clock() 12 | 13 | ################################# LOAD PLAYER AND SPRITESHEET################################### 14 | spritesheet = Spritesheet('spritesheet.png') 15 | player_img = spritesheet.parse_sprite('chick.png') 16 | player_rect = player_img.get_rect() 17 | 18 | #################################### LOAD THE LEVEL ####################################### 19 | map = TileMap('test_level.csv', spritesheet ) 20 | player_rect.x, player_rect.y = map.start_x, map.start_y 21 | 22 | ################################# GAME LOOP ########################## 23 | while running: 24 | clock.tick(60) 25 | ################################# CHECK PLAYER INPUT ################################# 26 | for event in pygame.event.get(): 27 | if event.type == pygame.QUIT: 28 | running = False 29 | if event.type == pygame.KEYDOWN: 30 | pass 31 | # if event.key == pygame.K_LEFT: 32 | # player.LEFT_KEY, player.FACING_LEFT = True, True 33 | # elif event.key == pygame.K_RIGHT: 34 | # player.RIGHT_KEY, player.FACING_LEFT = True, False 35 | # 36 | # if event.type == pygame.KEYUP: 37 | # if event.key == pygame.K_LEFT: 38 | # player.LEFT_KEY = False 39 | # elif event.key == pygame.K_RIGHT: 40 | # player.RIGHT_KEY = False 41 | 42 | ################################# UPDATE/ Animate SPRITE ################################# 43 | 44 | ################################# UPDATE WINDOW AND DISPLAY ################################# 45 | canvas.fill((0, 180, 240)) # Fills the entire screen with light blue 46 | map.draw_map(canvas) 47 | canvas.blit(player_img, player_rect) 48 | window.blit(canvas, (0,0)) 49 | pygame.display.update() 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Tilemap/spritesheet.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "chick.png": 4 | { 5 | "frame": {"x":0,"y":0,"w":16,"h":16}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 9 | "sourceSize": {"w":16,"h":16} 10 | }, 11 | "grass.png": 12 | { 13 | "frame": {"x":16,"y":0,"w":16,"h":16}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 17 | "sourceSize": {"w":16,"h":16} 18 | }, 19 | "grass2.png": 20 | { 21 | "frame": {"x":32,"y":0,"w":16,"h":16}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":16,"h":16}, 25 | "sourceSize": {"w":16,"h":16} 26 | }}, 27 | "meta": { 28 | "app": "https://www.codeandweb.com/texturepacker", 29 | "version": "1.0", 30 | "image": "test.png", 31 | "format": "RGBA8888", 32 | "size": {"w":48,"h":16}, 33 | "scale": "1", 34 | "smartupdate": "$TexturePacker:SmartUpdate:a66d329312f98accd6de9452ce22c2dc:dc1a8a27030eb8549f6ad949e95565fe:02ab132358d6d8b512e80119463a8329$" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tilemap/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/Tilemap/spritesheet.png -------------------------------------------------------------------------------- /Tilemap/spritesheet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import json 3 | 4 | 5 | class Spritesheet: 6 | def __init__(self, filename): 7 | self.filename = filename 8 | self.sprite_sheet = pygame.image.load(filename).convert() 9 | self.meta_data = self.filename.replace('png', 'json') 10 | with open(self.meta_data) as f: 11 | self.data = json.load(f) 12 | f.close() 13 | 14 | 15 | 16 | def get_sprite(self, x, y, w, h): 17 | sprite = pygame.Surface((w, h)) 18 | sprite.set_colorkey((0,0,0)) 19 | sprite.blit(self.sprite_sheet,(0, 0),(x, y, w, h)) 20 | return sprite 21 | 22 | def parse_sprite(self, name): 23 | sprite = self.data['frames'][name]['frame'] 24 | x, y, w, h = sprite["x"], sprite["y"], sprite["w"], sprite["h"] 25 | image = self.get_sprite(x, y, w, h) 26 | return image 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Tilemap/test_level.csv: -------------------------------------------------------------------------------- 1 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 2 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 3 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 4 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 5 | -1,-1,-1,-1,-1,-1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 6 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 7 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 8 | -1,-1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 9 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,1 10 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 11 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 12 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 13 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 14 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 15 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 16 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 17 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2,2,2,-1,-1,-1,-1,2,2,2,2 18 | -------------------------------------------------------------------------------- /Tilemap/tiles.py: -------------------------------------------------------------------------------- 1 | import pygame, csv, os 2 | 3 | class Tile(pygame.sprite.Sprite): 4 | def __init__(self, image, x, y, spritesheet): 5 | pygame.sprite.Sprite.__init__(self) 6 | self.image = spritesheet.parse_sprite(image) 7 | # Manual load in: self.image = pygame.image.load(image) 8 | self.rect = self.image.get_rect() 9 | self.rect.x, self.rect.y = x, y 10 | 11 | def draw(self, surface): 12 | surface.blit(self.image, (self.rect.x, self.rect.y)) 13 | 14 | class TileMap(): 15 | def __init__(self, filename, spritesheet): 16 | self.tile_size = 16 17 | self.start_x, self.start_y = 0, 0 18 | self.spritesheet = spritesheet 19 | self.tiles = self.load_tiles(filename) 20 | self.map_surface = pygame.Surface((self.map_w, self.map_h)) 21 | self.map_surface.set_colorkey((0, 0, 0)) 22 | self.load_map() 23 | 24 | def draw_map(self, surface): 25 | surface.blit(self.map_surface, (0, 0)) 26 | 27 | def load_map(self): 28 | for tile in self.tiles: 29 | tile.draw(self.map_surface) 30 | 31 | def read_csv(self, filename): 32 | map = [] 33 | with open(os.path.join(filename)) as data: 34 | data = csv.reader(data, delimiter=',') 35 | for row in data: 36 | map.append(list(row)) 37 | return map 38 | 39 | def load_tiles(self, filename): 40 | tiles = [] 41 | map = self.read_csv(filename) 42 | x, y = 0, 0 43 | for row in map: 44 | x = 0 45 | for tile in row: 46 | if tile == '0': 47 | self.start_x, self.start_y = x * self.tile_size, y * self.tile_size 48 | elif tile == '1': 49 | tiles.append(Tile('grass.png', x * self.tile_size, y * self.tile_size, self.spritesheet)) 50 | elif tile == '2': 51 | tiles.append(Tile('grass2.png', x * self.tile_size, y * self.tile_size, self.spritesheet)) 52 | # Move to next tile in current row 53 | x += 1 54 | 55 | # Move to next row 56 | y += 1 57 | # Store the size of the tile map 58 | self.map_w, self.map_h = x * self.tile_size, y * self.tile_size 59 | return tiles 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /readme images/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/readme images/logo.gif -------------------------------------------------------------------------------- /spritesheet/spritesheet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import json 3 | 4 | class Spritesheet: 5 | def __init__(self, filename): 6 | self.filename = filename 7 | self.sprite_sheet = pygame.image.load(filename).convert() 8 | self.meta_data = self.filename.replace('png', 'json') 9 | with open(self.meta_data) as f: 10 | self.data = json.load(f) 11 | f.close() 12 | 13 | 14 | 15 | def get_sprite(self, x, y, w, h): 16 | sprite = pygame.Surface((w, h)) 17 | sprite.set_colorkey((0,0,0)) 18 | sprite.blit(self.sprite_sheet,(0, 0),(x, y, w, h)) 19 | return sprite 20 | 21 | def parse_sprite(self, name): 22 | sprite = self.data['frames'][name]['frame'] 23 | x, y, w, h = sprite["x"], sprite["y"], sprite["w"], sprite["h"] 24 | image = self.get_sprite(x, y, w, h) 25 | return image 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /spritesheet/test_game.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from spritesheet import Spritesheet 3 | 4 | ################################# LOAD UP A BASIC WINDOW ################################# 5 | pygame.init() 6 | DISPLAY_W, DISPLAY_H = 480, 270 7 | canvas = pygame.Surface((DISPLAY_W,DISPLAY_H)) 8 | window = pygame.display.set_mode(((DISPLAY_W,DISPLAY_H))) 9 | running = True 10 | ########################################################################################### 11 | 12 | my_spritesheet = Spritesheet('trainer_sheet.png') 13 | trainer = [my_spritesheet.parse_sprite('trainer1.png'), my_spritesheet.parse_sprite('trainer2.png'),my_spritesheet.parse_sprite('trainer3.png'), 14 | my_spritesheet.parse_sprite('trainer4.png'),my_spritesheet.parse_sprite('trainer5.png')] 15 | f_trainer = [my_spritesheet.parse_sprite('f_trainer1.png'), my_spritesheet.parse_sprite('f_trainer2.png'),my_spritesheet.parse_sprite('f_trainer3.png'), 16 | my_spritesheet.parse_sprite('f_trainer4.png'),my_spritesheet.parse_sprite('f_trainer5.png')] 17 | index = 0 18 | 19 | while running: 20 | ################################# CHECK PLAYER INPUT ################################# 21 | for event in pygame.event.get(): 22 | if event.type == pygame.QUIT: 23 | running = False 24 | if event.type == pygame.KEYDOWN: 25 | ############### UPDATE SPRITE IF SPACE IS PRESSED ################################# 26 | if event.key == pygame.K_SPACE: 27 | index = (index + 1) % len(trainer) 28 | 29 | 30 | ################################# UPDATE WINDOW AND DISPLAY ################################# 31 | canvas.fill((255,255,255)) 32 | canvas.blit(trainer[index], (0, DISPLAY_H - 128)) 33 | canvas.blit(f_trainer[index], (128, DISPLAY_H - 128)) 34 | window.blit(canvas, (0,0)) 35 | pygame.display.update() 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /spritesheet/trainer_sheet.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "trainer1.png": 4 | { 5 | "frame": {"x":0,"y":0,"w":128,"h":128}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 9 | "sourceSize": {"w":128,"h":128} 10 | }, 11 | "trainer2.png": 12 | { 13 | "frame": {"x":128,"y":0,"w":128,"h":128}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 17 | "sourceSize": {"w":128,"h":128} 18 | }, 19 | "trainer3.png": 20 | { 21 | "frame": {"x":256,"y":0,"w":128,"h":128}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 25 | "sourceSize": {"w":128,"h":128} 26 | }, 27 | "trainer4.png": 28 | { 29 | "frame": {"x":384,"y":0,"w":128,"h":128}, 30 | "rotated": false, 31 | "trimmed": false, 32 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 33 | "sourceSize": {"w":128,"h":128} 34 | }, 35 | "trainer5.png": 36 | { 37 | "frame": {"x":512,"y":0,"w":128,"h":128}, 38 | "rotated": false, 39 | "trimmed": false, 40 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 41 | "sourceSize": {"w":128,"h":128} 42 | }}, 43 | "meta": { 44 | "app": "https://www.codeandweb.com/texturepacker", 45 | "version": "1.0", 46 | "image": "trainer_sheet.png", 47 | "format": "RGBA8888", 48 | "size": {"w":640,"h":128}, 49 | "scale": "1", 50 | "smartupdate": "$TexturePacker:SmartUpdate:e024fc465eb10905077cc6c0fb2a59cf:1f7959a1eb4c24cc1e49ddb6702f6c99:9e52acbb65607b5336e40f8226573675$" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spritesheet/trainer_sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/spritesheet/trainer_sheet.png -------------------------------------------------------------------------------- /spritesheet/trainer_sheet_two.json: -------------------------------------------------------------------------------- 1 | {"frames": { 2 | 3 | "f_trainer1.png": 4 | { 5 | "frame": {"x":1,"y":1,"w":128,"h":128}, 6 | "rotated": false, 7 | "trimmed": false, 8 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 9 | "sourceSize": {"w":128,"h":128} 10 | }, 11 | "f_trainer2.png": 12 | { 13 | "frame": {"x":131,"y":1,"w":128,"h":128}, 14 | "rotated": false, 15 | "trimmed": false, 16 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 17 | "sourceSize": {"w":128,"h":128} 18 | }, 19 | "f_trainer3.png": 20 | { 21 | "frame": {"x":261,"y":1,"w":128,"h":128}, 22 | "rotated": false, 23 | "trimmed": false, 24 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 25 | "sourceSize": {"w":128,"h":128} 26 | }, 27 | "f_trainer4.png": 28 | { 29 | "frame": {"x":391,"y":1,"w":128,"h":128}, 30 | "rotated": false, 31 | "trimmed": false, 32 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 33 | "sourceSize": {"w":128,"h":128} 34 | }, 35 | "f_trainer5.png": 36 | { 37 | "frame": {"x":521,"y":1,"w":128,"h":128}, 38 | "rotated": false, 39 | "trimmed": false, 40 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 41 | "sourceSize": {"w":128,"h":128} 42 | }, 43 | "trainer1.png": 44 | { 45 | "frame": {"x":651,"y":1,"w":128,"h":128}, 46 | "rotated": false, 47 | "trimmed": false, 48 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 49 | "sourceSize": {"w":128,"h":128} 50 | }, 51 | "trainer2.png": 52 | { 53 | "frame": {"x":781,"y":1,"w":128,"h":128}, 54 | "rotated": false, 55 | "trimmed": false, 56 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 57 | "sourceSize": {"w":128,"h":128} 58 | }, 59 | "trainer3.png": 60 | { 61 | "frame": {"x":911,"y":1,"w":128,"h":128}, 62 | "rotated": false, 63 | "trimmed": false, 64 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 65 | "sourceSize": {"w":128,"h":128} 66 | }, 67 | "trainer4.png": 68 | { 69 | "frame": {"x":1041,"y":1,"w":128,"h":128}, 70 | "rotated": false, 71 | "trimmed": false, 72 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 73 | "sourceSize": {"w":128,"h":128} 74 | }, 75 | "trainer5.png": 76 | { 77 | "frame": {"x":1171,"y":1,"w":128,"h":128}, 78 | "rotated": false, 79 | "trimmed": false, 80 | "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, 81 | "sourceSize": {"w":128,"h":128} 82 | }}, 83 | "meta": { 84 | "app": "https://www.codeandweb.com/texturepacker", 85 | "version": "1.0", 86 | "image": "two_trainer_sheet.png", 87 | "format": "RGBA8888", 88 | "size": {"w":1300,"h":130}, 89 | "scale": "1", 90 | "smartupdate": "$TexturePacker:SmartUpdate:d28ac0b7501a5529d4fb7e6446af8ab1:9baa5314d68a3adb51408e50596929c6:b0ad5120fa5d71ceb5167809dad98e22$" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /spritesheet/trainer_sheet_two.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChristianD37/YoutubeTutorials/e02db910063737ec1e47b1f358e13ad5b5c90993/spritesheet/trainer_sheet_two.png --------------------------------------------------------------------------------