가능해요! PyQt6만으로도 “움직이고, 서로 부딪히면(충돌) 반응하는” 2D 오브젝트를 만들 수 있습니다. 보통은 QGraphicsView / QGraphicsScene / QGraphicsItem 3종 세트를 쓰고, QTimer로 매 프레임 갱신(게임 루프)합니다. 충돌 판정은
- QGraphicsScene.collidingItems(item) (추천, 간단)
- 또는 QGraphicsItem.collidingItems() / collidesWithItem()
로 할 수 있어요.
개념 매핑 (PyGame ↔ PyQt6)
- 화면(Surface) → QGraphicsView
- 월드/레이어 → QGraphicsScene
- 스프라이트/Actor → QGraphicsItem 하위 클래스 (QGraphicsRectItem, QGraphicsPixmapItem 등)
- 게임 루프/업데이트 → QTimer로 16ms(≈60FPS)마다 위치 갱신 + 충돌 체크
- 충돌 판정 → scene.collidingItems(item) 또는 아이템 메서드
최소 예제: 키보드로 플레이어 이동 + 적과 충돌 감지
아래 코드는
- 방향키로 사각형(Player)을 움직이고,
- 원(Enemy)이 자동으로 좌우로 이동/반사,
- 두 오브젝트가 부딪히면 플레이어 색을 바꾸는
간단한 데모입니다.
# pip install PyQt6
import sys
from PyQt6.QtCore import Qt, QTimer, QPointF
from PyQt6.QtGui import QBrush, QColor
from PyQt6.QtWidgets import (
QApplication, QGraphicsView, QGraphicsScene,
QGraphicsRectItem, QGraphicsEllipseItem
)
SPEED = 5 # 플레이어 이동 속도 (px/frame)
ENEMY_SPEED = 3 # 적 이동 속도
class Player(QGraphicsRectItem):
def __init__(self, x, y, w=40, h=40):
super().__init__(0, 0, w, h)
self.setPos(x, y)
self.setBrush(QBrush(QColor("#4caf50")))
# 키 입력 받으려면 포커스 가능 + 포커스 요청
self.setFlag(self.GraphicsItemFlag.ItemIsFocusable, True)
self.setFocus()
def keyPressEvent(self, event):
dx = dy = 0
if event.key() == Qt.Key.Key_Left:
dx = -SPEED
elif event.key() == Qt.Key.Key_Right:
dx = SPEED
elif event.key() == Qt.Key.Key_Up:
dy = -SPEED
elif event.key() == Qt.Key.Key_Down:
dy = SPEED
if dx or dy:
self.moveBy(dx, dy)
super().keyPressEvent(event)
class Enemy(QGraphicsEllipseItem):
def __init__(self, x, y, w=40, h=40):
super().__init__(0, 0, w, h)
self.setPos(x, y)
self.setBrush(QBrush(QColor("#f44336")))
self.vx = ENEMY_SPEED
def update_motion(self, scene_rect):
# 좌우로 이동, 씬 경계에서 반사
self.moveBy(self.vx, 0)
item_rect = self.mapToScene(self.rect()).boundingRect()
if item_rect.right() >= scene_rect.right() or item_rect.left() <= scene_rect.left():
self.vx *= -1
class Game(QGraphicsView):
def __init__(self):
super().__init__()
self.setWindowTitle("PyQt6 Game-like Demo")
self.scene = QGraphicsScene(-300, -200, 600, 400) # (x, y, w, h)
self.setScene(self.scene)
self.setRenderHint(self.RenderHint.Antialiasing)
# 배경
self.setBackgroundBrush(QBrush(QColor("#20232a")))
# 오브젝트 생성
self.player = Player(-20, 0)
self.enemy = Enemy(-150, -20, 40, 40)
self.scene.addItem(self.player)
self.scene.addItem(self.enemy)
# 타이머로 게임 루프 (≈60 FPS)
self.timer = QTimer(self)
self.timer.timeout.connect(self.game_loop)
self.timer.start(16)
def clamp_player_in_scene(self):
# 플레이어가 씬 바깥으로 못 나가게 경계 처리
scene_rect = self.scene.sceneRect()
br = self.player.mapToScene(self.player.rect()).boundingRect()
dx = dy = 0
if br.left() < scene_rect.left(): dx = scene_rect.left() - br.left()
if br.right() > scene_rect.right(): dx = scene_rect.right() - br.right()
if br.top() < scene_rect.top(): dy = scene_rect.top() - br.top()
if br.bottom() > scene_rect.bottom(): dy = scene_rect.bottom() - br.bottom()
if dx or dy:
self.player.moveBy(dx, dy)
def game_loop(self):
# 1) 적 이동/반사
self.enemy.update_motion(self.scene.sceneRect())
# 2) 경계 처리
self.clamp_player_in_scene()
# 3) 충돌 체크: scene.collidingItems(player)
collisions = self.scene.collidingItems(self.player)
if self.enemy in collisions:
# 충돌 시 색상 변경
self.player.setBrush(QBrush(QColor("#ffd54f")))
else:
self.player.setBrush(QBrush(QColor("#4caf50")))
def main():
app = QApplication(sys.argv)
view = Game()
view.resize(900, 600)
view.show()
sys.exit(app.exec())
if __name__ == "__main__":
main()
팁 & 확장 포인트
- 스프라이트/이미지: QGraphicsPixmapItem에 QPixmap을 넣으면 이미지 스프라이트처럼 사용 가능합니다.
- 애니메이션: 단순 이동은 QTimer가 직관적이지만, 위치/회전 같은 속성은 QPropertyAnimation도 활용 가능.
- 정교한 충돌: 기본 충돌은 바운딩/모양 기반. 복잡한 경우 커스텀 shape()(QPainterPath 반환) 재정의로 히트박스를 세밀화할 수 있어요.
- 씬 분할/최적화: Qt의 그래픽스 씬은 내부적으로 공간 분할(쿼드트리 유사)을 사용해 충돌/화면 갱신이 꽤 효율적입니다. 수백~수천 개도 단순 2D에선 가능.
- 대안: 더 게임스러운 스택을 원하면 Qt Quick(QML) + Sprite, Particles, Animation 조합도 선택지입니다. 하지만 PyQt6 위 QGraphics만으로도 간단한 아케이드/퍼즐류는 충분히 구현됩니다.
원하시면 이 예제를 바탕으로 다중 적/총알, 타일맵, FPS 표시, 사운드, 씬 전환까지 확장한 템플릿도 바로 만들어 드릴게요.
'Codyssey > PyQT6' 카테고리의 다른 글
PyQT 키입력 받기 (0) | 2025.09.09 |
---|