Agar.io by ChatGPT
unknown
python
10 months ago
13 kB
5
Indexable
import pygame
import random
import math
import sys
import array
pygame.init()
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=512)
# --------------------- USTAWIENIA GRY ---------------------
# Rozmiar okna
WIN_WIDTH, WIN_HEIGHT = 800, 600
# Rozmiar „świata”
WORLD_WIDTH, WORLD_HEIGHT = 1600, 1200
FPS = 60
# Kolory
WHITE = (255, 255, 255)
BLACK = ( 0, 0, 0)
BLUE = ( 0, 0, 255)
# Przygotowanie okna
screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT))
pygame.display.set_caption("Agar.io z mądrzejszymi NPC i rankingiem")
clock = pygame.time.Clock()
# --------------------- GENEROWANIE DŹWIĘKÓW ---------------------
def generate_sine_wave(frequency=440, duration_ms=200, volume=0.5, sample_rate=44100):
"""
Generuje tablicę bajtów zawierającą próbki sinusoidy (16 bit, stereo)
o zadanej częstotliwości, czasie trwania i głośności.
"""
num_samples = int(sample_rate * duration_ms / 1000.0)
amplitude = int(32767 * volume) # dla 16-bit max = 32767
samples = array.array('h') # 'h' = short (16-bit) - kanał lewy
for i in range(num_samples):
sample_value = int(amplitude * math.sin(2.0 * math.pi * frequency * i / sample_rate))
samples.append(sample_value)
# Tworzymy stereo: kopiujemy próbkę L jako R
stereo_samples = array.array('h')
for s in samples:
stereo_samples.append(s) # left
stereo_samples.append(s) # right
sound = pygame.mixer.Sound(buffer=stereo_samples.tobytes())
return sound
sound_eat_food = generate_sine_wave(frequency=600, duration_ms=100, volume=0.5)
sound_eat_npc = generate_sine_wave(frequency=300, duration_ms=200, volume=0.6)
# --------------------- KLASY OBIEKTÓW ---------------------
class Blob:
"""Podstawowa klasa dla wszelkich 'kulek' (gracz, NPC, jedzenie)."""
def __init__(self, x, y, radius, color=BLACK, speed=0):
self.x = x
self.y = y
self.radius = radius
self.color = color
self.speed = speed
def distance_to(self, other) -> float:
return math.hypot(self.x - other.x, self.y - other.y)
def check_collision(self, other) -> bool:
return self.distance_to(other) <= self.radius + other.radius
def draw(self, surface, camera_offset_x, camera_offset_y):
screen_x = int(self.x - camera_offset_x)
screen_y = int(self.y - camera_offset_y)
pygame.draw.circle(surface, self.color, (screen_x, screen_y), self.radius)
class Player(Blob):
"""Klasa dla gracza (sterowanego przez człowieka)."""
def __init__(self, x, y, radius, color, speed, name="TY"):
super().__init__(x, y, radius, color, speed)
self.name = name
def move(self):
keys = pygame.key.get_pressed()
dx, dy = 0, 0
if keys[pygame.K_w] or keys[pygame.K_UP]:
dy = -1
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
dy = 1
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
dx = -1
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
dx = 1
# Normalizacja wektora ruchu
if dx != 0 or dy != 0:
length = math.hypot(dx, dy)
dx /= length
dy /= length
# Ruch
self.x += dx * self.speed
self.y += dy * self.speed
# Ograniczenie do obszaru świata
if self.x - self.radius < 0:
self.x = self.radius
if self.x + self.radius > WORLD_WIDTH:
self.x = WORLD_WIDTH - self.radius
if self.y - self.radius < 0:
self.y = self.radius
if self.y + self.radius > WORLD_HEIGHT:
self.y = WORLD_HEIGHT - self.radius
class NPC(Blob):
"""Klasa dla 'mądrzejszych' NPC."""
def __init__(self, x, y, radius, color, speed, name):
super().__init__(x, y, radius, color, speed)
self.name = name
# Kierunek startowy - losowy
self.dir_x = random.choice([-1, 1])
self.dir_y = random.choice([-1, 1])
def move(self, player, npcs):
"""
Prostsza 'inteligencja':
- Szuka najbliższego obiektu (player lub inny NPC).
- Jeśli tamten jest większy -> uciekamy.
- Jeśli tamten jest mniejszy -> gonimy.
- Jeśli w pobliżu nie ma nikogo -> losowy ruch (z niewielką szansą na zmianę kierunku).
"""
# Najpierw szukamy najbliższego 'gracza' lub NPC (z pominięciem samego siebie).
target = self.find_closest(player, npcs)
if target is not None:
# Obliczamy odległość i wektor do celu
dist = self.distance_to(target)
dx = (target.x - self.x)
dy = (target.y - self.y)
if dist != 0:
dx /= dist
dy /= dist
# Decyzja w zależności od wielkości
if target.radius > self.radius:
# Uciekamy -> ruch w przeciwną stronę
dx = -dx
dy = -dy
else:
# Gonimy -> dx, dy jak powyżej (czyli w stronę celu)
pass
# Poruszamy się z własną prędkością w wyznaczonym kierunku
self.x += dx * self.speed
self.y += dy * self.speed
else:
# Brak celu w zasięgu -> losowy ruch, ze zmianą kierunku 1%
if random.random() < 0.01:
self.dir_x = random.choice([-1, 1])
self.dir_y = random.choice([-1, 1])
self.x += self.dir_x * self.speed
self.y += self.dir_y * self.speed
# Odbijanie od krawędzi świata
if self.x - self.radius < 0:
self.x = self.radius
self.dir_x *= -1
if self.x + self.radius > WORLD_WIDTH:
self.x = WORLD_WIDTH - self.radius
self.dir_x *= -1
if self.y - self.radius < 0:
self.y = self.radius
self.dir_y *= -1
if self.y + self.radius > WORLD_HEIGHT:
self.y = WORLD_HEIGHT - self.radius
self.dir_y *= -1
def find_closest(self, player, npcs, vision_radius=300):
"""
Znajdź najbliższy obiekt (NPC lub gracza) w promieniu 'vision_radius'.
Zwraca obiekt (Player/NPC) lub None, jeśli nic nie znaleziono.
"""
closest_obj = None
min_dist = float('inf')
# Najpierw sprawdzamy gracza
if player != self:
dist = self.distance_to(player)
if dist < min_dist and dist <= vision_radius:
min_dist = dist
closest_obj = player
# Następnie sprawdzamy innych NPC
for npc in npcs:
if npc is self:
continue # omijamy samego siebie
dist = self.distance_to(npc)
if dist < min_dist and dist <= vision_radius:
min_dist = dist
closest_obj = npc
return closest_obj
# --------------------- FUNKCJE POMOCNICZE ---------------------
def create_npcs(n):
"""
Tworzy n NPC-ów w losowych miejscach na planszy.
Używamy podanej listy nazw i wybieramy je losowo.
"""
possible_names = [
"Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
"Gamma", "Hotel", "India", "Juliet", "Kombo",
"Mike", "November", "Oscar", "Papa", "Quebec", "Romeo",
"Tango", "Uniform", "Victor",
"Xray", "Zero"
]
npcs = []
for _ in range(n):
radius = random.randint(10, 20)
x = random.randint(radius, WORLD_WIDTH - radius)
y = random.randint(radius, WORLD_HEIGHT - radius)
color = (
random.randint(50, 255),
random.randint(50, 255),
random.randint(50, 255)
)
speed = random.randint(1, 3)
name = random.choice(possible_names)
npcs.append(NPC(x, y, radius, color, speed, name))
return npcs
def create_food(n):
"""Tworzy n elementów 'jedzenia' (małe kulki)."""
food_list = []
for _ in range(n):
radius = random.randint(3, 5)
x = random.randint(radius, WORLD_WIDTH - radius)
y = random.randint(radius, WORLD_HEIGHT - radius)
color = (
random.randint(100, 255),
random.randint(100, 255),
random.randint(100, 255)
)
food_list.append(Blob(x, y, radius, color, 0))
return food_list
def show_end_screen(message):
"""Wyświetla ekran końcowy z komunikatem."""
font = pygame.font.SysFont(None, 80)
text_surface = font.render(message, True, (255, 0, 0))
rect = text_surface.get_rect(center=(WIN_WIDTH // 2, WIN_HEIGHT // 2))
screen.fill(WHITE)
screen.blit(text_surface, rect)
pygame.display.flip()
pygame.time.wait(3000)
def draw_scoreboard(surface, player, npcs):
"""
Rysuje w lewym górnym rogu listę aktywnych „graczy” (gracz + NPC)
posortowaną malejąco po promieniu (radius).
Format: NICK (radius)
"""
font = pygame.font.SysFont(None, 26)
all_players = [player] + npcs
all_players.sort(key=lambda p: p.radius, reverse=True)
x = 10
y = 10
for blob in all_players:
text_surface = font.render(f"{blob.name} ({blob.radius})", True, BLACK)
surface.blit(text_surface, (x, y))
y += 25 # odstęp między kolejnymi liniami
# --------------------- LOGIKA GŁÓWNA ---------------------
def main():
# Tworzymy obiekty
player = Player(WORLD_WIDTH//2, WORLD_HEIGHT//2, 15, BLUE, speed=3, name="TY")
npcs = create_npcs(10)
food = create_food(50) # Można zwiększyć liczbę jedzenia
running = True
while running:
clock.tick(FPS)
# Obsługa zdarzeń
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Ruch gracza i NPC
player.move()
for npc in npcs:
npc.move(player, npcs) # mądrzejszy ruch
# Kolizje z jedzeniem – dźwięk TYLKO gdy gracz zjada
for f in food[:]:
if player.check_collision(f):
sound_eat_food.play() # dźwięk tylko dla gracza
player.radius += f.radius // 2
food.remove(f)
else:
# Sprawdzamy, czy któryś z NPC zjadł jedzenie (bez dźwięku)
for npc in npcs:
if npc.check_collision(f):
npc.radius += f.radius // 2
food.remove(f)
break
# Kolizje między graczem a NPC
for npc in npcs[:]:
if player.check_collision(npc):
if player.radius > npc.radius:
# Gracz zjada NPC -> dźwięk
sound_eat_npc.play()
player.radius += npc.radius // 2
npcs.remove(npc)
else:
# Gracz zostaje zjedzony
show_end_screen("Przegrana!")
running = False
break
# Kolizje między NPC-ami (bez dźwięku)
for i in range(len(npcs)):
if npcs[i] is None:
continue
for j in range(i+1, len(npcs)):
if npcs[j] is None:
continue
if npcs[i].check_collision(npcs[j]):
if npcs[i].radius > npcs[j].radius:
npcs[i].radius += npcs[j].radius // 2
npcs[j] = None
else:
npcs[j].radius += npcs[i].radius // 2
npcs[i] = None
break
npcs = [npc for npc in npcs if npc is not None]
# Sprawdzenie zwycięstwa: WYGRANA, jeśli nie ma żadnych NPC
if len(npcs) == 0:
show_end_screen("Wygrana!")
running = False
# Rysowanie tła
screen.fill(WHITE)
# Kamera (offset)
camera_offset_x = player.x - WIN_WIDTH // 2
camera_offset_y = player.y - WIN_HEIGHT // 2
if camera_offset_x < 0:
camera_offset_x = 0
if camera_offset_x > WORLD_WIDTH - WIN_WIDTH:
camera_offset_x = WORLD_WIDTH - WIN_WIDTH
if camera_offset_y < 0:
camera_offset_y = 0
if camera_offset_y > WORLD_HEIGHT - WIN_HEIGHT:
camera_offset_y = WORLD_HEIGHT - WIN_HEIGHT
# Rysowanie jedzenia, NPC i gracza
for f in food:
f.draw(screen, camera_offset_x, camera_offset_y)
for npc in npcs:
npc.draw(screen, camera_offset_x, camera_offset_y)
player.draw(screen, camera_offset_x, camera_offset_y)
# Rysowanie rankingu
draw_scoreboard(screen, player, npcs)
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
Editor is loading...
Leave a Comment