Merge pull request #6 from BreizhHardware/dev_felix

Dev felix
This commit is contained in:
Félix MARQUET
2025-03-27 14:42:44 +01:00
committed by GitHub
11 changed files with 430 additions and 42 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

74
main.py
View File

@@ -1,20 +1,30 @@
import pygame
import sys
from pygame.locals import *
# Import from pygame_basics
from src.game import (
initialize_game,
from src.game import initialize_game
from src.constant import (
displaysurface,
FramePerSec,
font,
FPS,
WIDTH,
HEIGHT,
ORIGINAL_WIDTH,
ORIGINAL_HEIGHT,
fullscreen,
)
from src.constant import displaysurface, FramePerSec, font, FPS, WIDTH, HEIGHT
# Import from menu
from src.Menu.Menu import Menu
from src.Menu.Leaderboard import Leaderboard
from src.Camera import Camera
def main():
# Declare globals that we'll modify
global displaysurface, fullscreen, ORIGINAL_WIDTH, ORIGINAL_HEIGHT
# Add camera initialization
camera = Camera(WIDTH, HEIGHT)
# Game states
MENU = 0
PLAYING = 1
@@ -27,7 +37,7 @@ def main():
leaderboard = Leaderboard()
# Initialize game components
P1, PT1, platforms, all_sprites = initialize_game()
P1, PT1, platforms, all_sprites, background = initialize_game("map_test.json")
# Main game loop
while True:
@@ -42,6 +52,28 @@ def main():
else:
pygame.quit()
sys.exit()
elif event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
# Store current window size before going fullscreen
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = displaysurface.get_size()
displaysurface = pygame.display.set_mode(
(0, 0), pygame.FULLSCREEN
)
else:
# Return to windowed mode with previous size
displaysurface = pygame.display.set_mode(
(ORIGINAL_WIDTH, ORIGINAL_HEIGHT), pygame.RESIZABLE
)
elif (
event.type == VIDEORESIZE
): # Fixed indentation - moved out of K_F11 condition
if not fullscreen:
displaysurface = pygame.display.set_mode(
(event.w, event.h), pygame.RESIZABLE
)
# Update window dimensions
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = event.w, event.h
# Handle menu events
if current_state == MENU:
@@ -73,10 +105,28 @@ def main():
# Regular game code
P1.move()
P1.update()
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
# Display FPS and coordinates
# Update camera to follow player
camera.update(P1)
# Clear screen
displaysurface.fill((0, 0, 0))
if background:
parallax_factor = 0.3
bg_x = camera.camera.x * parallax_factor
bg_y = camera.camera.y * parallax_factor
displaysurface.blit(background, (bg_x, bg_y))
# Draw all sprites with camera offset applied
for entity in all_sprites:
# Calculate position adjusted for camera
camera_adjusted_rect = entity.rect.copy()
camera_adjusted_rect.x += camera.camera.x
camera_adjusted_rect.y += camera.camera.y
displaysurface.blit(entity.surf, camera_adjusted_rect)
# Display FPS and coordinates (fixed position UI elements)
fps = int(FramePerSec.get_fps())
fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255))
displaysurface.blit(fps_text, (10, 10))

166
map_test.json Normal file
View File

@@ -0,0 +1,166 @@
{
"name": "Level 1",
"width": 2400,
"height": 800,
"background": "assets/map/background/forest_bg.jpg",
"gravity": 1.0,
"ground": [
{
"id": "main_ground",
"x": -1000,
"y": 780,
"width": 1800,
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg"
},
{
"id": "pit",
"x": 800,
"y": 780,
"width": 200,
"height": 20,
"is_hole": true
},
{
"id": "main_ground_2",
"x": 1000,
"y": 900,
"width": 1800,
"height": 200,
"texture": "assets/map/platform/grass_texture.jpg"
}
],
"platforms": [
{
"id": "platform1",
"x": 300,
"y": 600,
"width": 200,
"height": 20,
"texture": "assets/map/platform/grass_texture.jpg",
"is_moving": false
},
{
"id": "platform2",
"x": 700,
"y": 500,
"width": 150,
"height": 20,
"texture": "assets/map/platform/grass_texture.jpg",
"is_moving": true,
"movement": {
"type": "linear",
"points": [
{"x": 700, "y": 500},
{"x": 700, "y": 300}
],
"speed": 2.0,
"wait_time": 1.0
}
},
{
"id": "platform3",
"x": 1200,
"y": 400,
"width": 100,
"height": 20,
"texture": "assets/map/platform/grass_texture.jpg",
"is_moving": true,
"movement": {
"type": "circular",
"center": {"x": 1200, "y": 400},
"radius": 100,
"speed": 0.02,
"clockwise": true
}
}
],
"enemies": [
{
"id": "enemy1",
"type": "walker",
"x": 500,
"y": 760,
"health": 100,
"damage": 20,
"behavior": "patrol",
"patrol_points": [
{"x": 400, "y": 760},
{"x": 600, "y": 760}
],
"speed": 1.5,
"sprite_sheet": "assets/map/enemy/walker_enemy.png"
},
{
"id": "enemy2",
"type": "flyer",
"x": 1000,
"y": 400,
"health": 50,
"damage": 10,
"behavior": "chase",
"detection_radius": 200,
"speed": 2.0,
"sprite_sheet": "assets/map/enemy/flying_enemy.png"
},
{
"id": "enemy3",
"type": "turret",
"x": 1500,
"y": 700,
"health": 200,
"damage": 30,
"behavior": "stationary",
"attack_interval": 2.0,
"attack_range": 300,
"sprite_sheet": "assets/map/enemy/turret_enemy.png"
}
],
"collectibles": [
{
"id": "coin1",
"type": "coin",
"x": 350,
"y": 550,
"value": 10,
"sprite": "assets/map/collectibles/coin.png"
},
{
"id": "power_up1",
"type": "speed_boost",
"x": 900,
"y": 450,
"duration": 5.0,
"sprite": "assets/map/collectibles/speed_boost.png"
}
],
"checkpoints": [
{
"id": "checkpoint1",
"x": 1200,
"y": 760,
"width": 50,
"height": 50,
"sprite": "assets/map/checkpoints/checkpoint.png"
}
],
"spawn_point": {
"x": 50,
"y": 700
},
"exit": {
"x": 2300,
"y": 700,
"width": 50,
"height": 80,
"next_level": "Level 2",
"sprite": "assets/map/exit/door.png"
}
}

18
src/Camera.py Normal file
View File

@@ -0,0 +1,18 @@
# src/Camera.py
import pygame
from src.constant import WIDTH, HEIGHT
class Camera:
def __init__(self, width, height):
self.camera = pygame.Rect(0, 0, width, height)
self.width = width
self.height = height
def update(self, target):
# Center the target in the camera view
x = -target.rect.centerx + WIDTH // 2
y = -target.rect.centery + HEIGHT // 2
# Update camera position
self.camera = pygame.Rect(x, y, self.width, self.height)

View File

@@ -1,9 +1,12 @@
import pygame
import os
from pygame.math import Vector2 as vec
class Entity(pygame.sprite.Sprite):
def __init__(self, pos=(0, 0), size=(30, 30), color=(255, 255, 255)):
def __init__(
self, pos=(0, 0), size=(30, 30), color=(255, 255, 255), texturePath=""
):
super().__init__()
self.pos = vec(pos)
self.vel = vec(0, 0)
@@ -14,6 +17,13 @@ class Entity(pygame.sprite.Sprite):
self.surf.fill(color)
self.rect = self.surf.get_rect()
self.update_rect()
if os.path.isfile(texturePath):
self.surf = pygame.image.load(texturePath).convert_alpha()
self.surf = pygame.transform.scale(self.surf, size)
self.rect = self.surf.get_rect()
else:
print("Texture path not found")
print(texturePath)
def update_rect(self):
"""Update rect position based on entity position"""

View File

@@ -1,10 +1,11 @@
import pygame
from src.Entity.Entity import Entity
from src.constant import WIDTH, HEIGHT
class Platform(Entity):
def __init__(self, width, height, x, y, color=(255, 0, 0)):
super().__init__(pos=(x, y), size=(width, height), color=color)
def __init__(self, width, height, x, y, color=(255, 0, 0), texturePath=""):
super().__init__(
pos=(x, y), size=(width, height), color=color, texturePath=texturePath
)
# Override rect setting for platforms if needed
self.rect = self.surf.get_rect(center=(x, y))

View File

@@ -124,10 +124,12 @@ class Player(Entity):
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_q]:
self.acc.x = -ACC
self.moving = True
if pressed_keys[K_a]:
self.dash(-ACC)
# Check if X is > 0 to prevent player from going off screen
if self.pos.x > 0:
self.acc.x = -ACC
self.moving = True
if pressed_keys[K_a]:
self.dash(-ACC)
if pressed_keys[K_d]:
self.acc.x = ACC
self.moving = True
@@ -149,13 +151,13 @@ class Player(Entity):
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
# Prevent the player from moving off-screen horizontally
if self.pos.x > WIDTH - self.rect.width / 2:
self.pos.x = WIDTH - self.rect.width / 2
self.vel.x = 0
if self.pos.x < self.rect.width / 2:
self.pos.x = self.rect.width / 2
self.vel.x = 0
# Remove screen boundary restrictions
# if self.pos.x > WIDTH - self.rect.width / 2:
# self.pos.x = WIDTH - self.rect.width / 2
# self.vel.x = 0
# if self.pos.x < self.rect.width / 2:
# self.pos.x = self.rect.width / 2
# self.vel.x = 0
self.rect.midbottom = self.pos

126
src/Map/parser.py Normal file
View File

@@ -0,0 +1,126 @@
import json
import pygame
import os
from src.Entity.Platform import Platform
from src.Entity.Player import Player
from src.constant import WIDTH, HEIGHT, all_sprites, platforms
class MapParser:
def __init__(self):
self.all_sprites = all_sprites
self.platforms = platforms
self.enemies = pygame.sprite.Group()
self.collectibles = pygame.sprite.Group()
self.player = None
def load_map(self, map_file):
"""Load and parse a map from JSON file"""
try:
with open(map_file, "r") as file:
map_data = json.load(file)
# Create all game objects from map data
self.create_map_objects(map_data)
return {
"player": self.player,
"all_sprites": self.all_sprites,
"platforms": self.platforms,
"enemies": self.enemies,
"collectibles": self.collectibles,
"map_properties": {
"name": map_data.get("name", "Unnamed Level"),
"width": map_data.get("width", WIDTH),
"height": map_data.get("height", HEIGHT),
},
}
except Exception as e:
print(f"Error loading map: {e}")
return None
def create_map_objects(self, map_data):
"""Create all game objects from map data"""
# Clear existing sprites
self.all_sprites.empty()
self.platforms.empty()
self.enemies.empty()
self.collectibles.empty()
# Create ground elements
if "ground" in map_data:
for ground in map_data["ground"]:
if not ground.get("is_hole", False):
platform = Platform(
ground["width"],
ground["height"],
ground["x"] + ground["width"] / 2,
ground["y"],
(255, 0, 0),
ground["texture"],
)
self.platforms.add(platform)
self.all_sprites.add(platform)
# Create platforms
if "platforms" in map_data:
for platform_data in map_data["platforms"]:
platform = Platform(
platform_data["width"],
platform_data["height"],
platform_data["x"] + platform_data["width"] / 2,
platform_data["y"],
(255, 0, 0),
platform_data["texture"],
)
# Add moving platform properties if needed
if platform_data.get("is_moving", False):
platform.is_moving = True
movement = platform_data["movement"]
platform.movement_type = movement["type"]
if movement["type"] == "linear":
platform.movement_points = movement["points"]
platform.movement_speed = movement["speed"]
platform.wait_time = movement.get("wait_time", 0)
platform.current_point = 0
platform.current_direction = 1
platform.wait_counter = 0
elif movement["type"] == "circular":
platform.center = movement["center"]
platform.radius = movement["radius"]
platform.angle = 0
platform.angular_speed = movement["speed"]
platform.clockwise = movement.get("clockwise", True)
self.platforms.add(platform)
self.all_sprites.add(platform)
# Create player at spawn point
spawn = map_data.get("spawn_point", {"x": 50, "y": 700})
self.player = Player()
self.player.pos.x = spawn["x"]
self.player.pos.y = spawn["y"]
self.all_sprites.add(self.player)
# Create enemies (requires Enemy class implementation)
if "enemies" in map_data:
pass # You'll need to implement enemy creation
# Create collectibles (requires Collectible class implementation)
if "collectibles" in map_data:
pass # You'll need to implement collectible creation
# Create background image
if "background" in map_data:
print(f"Loading background image: {map_data['background']}")
if os.path.isfile(map_data["background"]):
background = pygame.image.load(map_data["background"]).convert_alpha()
background = pygame.transform.scale(background, (WIDTH, HEIGHT))
self.background = background
else:
print(f"Background image not found: {map_data['background']}")
else:
self.background = None

View File

@@ -5,14 +5,17 @@ pygame.init()
FPS = 60
ACC = 0.5
FRIC = -0.12
WIDTH = 800
HEIGHT = 600
WIDTH = 1200
HEIGHT = 800
platforms = pygame.sprite.Group()
vec = pygame.math.Vector2
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)
pygame.display.set_caption("Game")
FramePerSec = pygame.time.Clock()
all_sprites = pygame.sprite.Group()
fullscreen = False
ORIGINAL_WIDTH = WIDTH
ORIGINAL_HEIGHT = HEIGHT
try:
font = pygame.font.SysFont("Arial", 20)

View File

@@ -4,23 +4,35 @@ from pygame.locals import *
from src.Entity.Platform import Platform
from src.Entity.Player import Player
from src.constant import displaysurface, FramePerSec, font, FPS, platforms, all_sprites
from src.Map.parser import MapParser
def initialize_game():
# Clear previous sprites if any
platforms.empty()
all_sprites.empty()
def initialize_game(map_file="map_test.json"):
"""Initialize game with map from JSON file"""
parser = MapParser()
map_objects = parser.load_map(map_file)
# Create new game objects
PT1 = Platform(1200, 20, 200, 400)
P1 = Player()
if not map_objects:
# Fallback to default setup if map loading fails
platforms.empty()
all_sprites.empty()
# Add them to the groups
platforms.add(PT1)
all_sprites.add(PT1)
all_sprites.add(P1)
PT1 = Platform(1200, 20, 600, 400)
P1 = Player()
return P1, PT1, platforms, all_sprites
platforms.add(PT1)
all_sprites.add(PT1)
all_sprites.add(P1)
return P1, PT1, platforms, all_sprites, None # Return None for background
return (
map_objects["player"],
None, # No specific platform reference needed
map_objects["platforms"],
map_objects["all_sprites"],
parser.background, # Return the loaded background
)
def run_game(P1, all_sprites):