import pygame
import sys
import math
# 초기화 및 화면 설정
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("모바일 RPG 칼 싸움 게임")
clock = pygame.time.Clock()
# 색상 정의
WHITE = (255, 255, 255)
BLUE = (0, 102, 204) # 플레이어
RED = (204, 0, 0) # 적
YELLOW = (255, 204, 0) # 칼 효과 및 버튼
GRAY = (50, 50, 50)
UI_ALPHA = (255, 255, 255, 100)
# 가상 조이스틱 클래스
class Joystick:
def __init__(self, x, y, radius):
self.base_x = x
self.base_y = y
self.radius = radius
self.handle_x = x
self.handle_y = y
self.handle_radius = radius // 2
self.is_dragging = False
self.vector_x = 0.0
self.vector_y = 0.0
def handle_event(self, event):
if event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.FINGERDOWN:
pos = event.pos if hasattr(event, 'pos') else (event.x * WIDTH, event.y * HEIGHT)
dist = math.hypot(pos[0] - self.base_x, pos[1] - self.base_y)
if dist <= self.radius:
self.is_dragging = True
elif event.type == pygame.MOUSEBUTTONUP or event.type == pygame.FINGERUP:
self.is_dragging = False
self.handle_x = self.base_x
self.handle_y = self.base_y
self.vector_x = 0.0
self.vector_y = 0.0
elif (event.type == pygame.MOUSEMOTION or event.type == pygame.FINGERMOTION) and self.is_dragging:
pos = event.pos if hasattr(event, 'pos') else (event.x * WIDTH, event.y * HEIGHT)
dx = pos[0] - self.base_x
dy = pos[1] - self.base_y
dist = math.hypot(dx, dy)
if dist > self.radius:
dx = (dx / dist) * self.radius
dy = (dy / dist) * self.radius
self.handle_x = self.base_x + dx
self.handle_y = self.base_y + dy
self.vector_x = dx / self.radius
self.vector_y = dy / self.radius
def draw(self, surface):
# 조이스틱 배경 베이스
pygame.draw.circle(surface, (200, 200, 200), (self.base_x, self.base_y), self.radius, 3)
# 조이스틱 손잡이
pygame.draw.circle(surface, (255, 255, 255), (int(self.handle_x), int(self.handle_y)), self.handle_radius)
# 공격 버튼 클래스
class AttackButton:
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
def is_touched(self, pos):
dist = math.hypot(pos[0] - self.x, pos[1] - self.y)
return dist <= self.radius
def draw(self, surface):
pygame.draw.circle(surface, YELLOW, (self.x, self.y), self.radius)
# 버튼 텍스트
font = pygame.font.SysFont(None, 24)
text = font.render("ATTACK", True, (0, 0, 0))
surface.blit(text, (self.x - 28, self.y - 8))
# 캐릭터 클래스
class Character:
def __init__(self, x, y, color):
self.x = x
self.y = y
self.radius = 20
self.color = color
self.hp = 100
self.speed = 4
self.is_attacking = False
self.attack_timer = 0
self.angle = 0 # 라디안 단위 방향각
self.attack_rect = pygame.Rect(0, 0, 0, 0)
def move(self, vx, vy):
if vx != 0 or vy != 0:
self.x += vx * self.speed
self.y += vy * self.speed
self.angle = math.atan2(vy, vx)
def attack(self):
if not self.is_attacking:
self.is_attacking = True
self.attack_timer = 12
def update(self):
if self.is_attacking:
self.attack_timer -= 1
if self.attack_timer <= 0:
self.is_attacking = False
def draw(self, surface):
# 캐릭터 본체 (원형)
pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), self.radius)
# 칼 공격 이펙트 (부채꼴/원호 형태)
if self.is_attacking:
sword_length = 50
end_x = self.x + math.cos(self.angle) * sword_length
end_y = self.y + math.sin(self.angle) * sword_length
pygame.draw.line(surface, YELLOW, (self.x, self.y), (end_x, end_y), 10)
# 체력바
pygame.draw.rect(surface, RED, (self.x - 20, self.y - 30, 40, 5))
pygame.draw.rect(surface, (0, 255, 0), (self.x - 20, self.y - 30, 40 * (max(0, self.hp) / 100), 5))
# UI 객체 생성
joystick = Joystick(120, HEIGHT - 120, 60)
attack_btn = AttackButton(WIDTH - 100, HEIGHT - 100, 40)
# 캐릭터 생성
player = Character(200, 300, BLUE)
enemy = Character(600, 300, RED)
hit_cooldown = 0
running = True
while running:
clock.tick(60)
screen.fill(GRAY)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 조이스틱 입력 전달
joystick.handle_event(event)
# 공격 버튼 클릭 판정
if event.type == pygame.MOUSEBUTTONDOWN:
if attack_btn.is_touched(event.pos):
player.attack()
# 플레이어 이동 (조이스틱 벡터 사용)
player.move(joystick.vector_x, joystick.vector_y)
# 단순 적 AI (플레이어를 향해 이동)
if enemy.hp > 0:
edx = player.x - enemy.x
edy = player.y - enemy.y
dist = math.hypot(edx, edy)
if dist > 30:
enemy.move(edx / dist, edy / dist)
# 상태 업데이트
player.update()
enemy.update()
# 공격 피격 판정 (거리 및 각도 기준)
if player.is_attacking and enemy.hp > 0 and hit_cooldown == 0:
dist_to_enemy = math.hypot(enemy.x - player.x, enemy.y - player.y)
if dist_to_enemy <= 60: # 사정거리 내
angle_to_enemy = math.atan2(enemy.y - player.y, enemy.x - player.x)
# 공격 방향 각도 차이 확인
angle_diff = abs(player.angle - angle_to_enemy)
if angle_diff < 1.0 or angle_diff > (2 * math.pi - 1.0):
enemy.hp -= 25
hit_cooldown = 15
if hit_cooldown > 0:
hit_cooldown -= 1
# 그리기
player.draw(screen)
if enemy.hp > 0:
enemy.draw(screen)
# 모바일 UI 그리기 (화면 제일 위)
joystick.draw(screen)
attack_btn.draw(screen)
pygame.display.flip()
pygame.quit()
sys.exit()