예제 소스
인베이더 게임으로 배우는 휴리스틱과 AI 브리지 · 파일 17개
gamecanvas.cpp
#include "gamecanvas.h"
#include <QFont>
#include <QKeyEvent>
#include <QPainter>
#include <QPaintEvent>
using namespace Invader;
namespace {
void drawAlienSprite(QPainter &p, const QRect &a, int phase)
{
const int ox = a.x();
const int oy = a.y();
const QColor body(78, 220, 125);
const QColor dark(38, 130, 72);
const QColor eye(20, 60, 35);
p.setPen(Qt::NoPen);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.fillRect(a, body);
// 상단 안테나
p.fillRect(a.center().x() - 1, oy - 4, 2, 5, QColor(200, 240, 210));
p.fillRect(a.center().x() - 2, oy - 5, 4, 2, dark);
// 눈
p.fillRect(ox + 4, oy + 3, 5, 5, eye);
p.fillRect(a.right() - 8, oy + 3, 5, 5, eye);
// 입/하관 홈
p.setCompositionMode(QPainter::CompositionMode_Clear);
p.fillRect(QRect(ox + 7, oy + 9, a.width() - 14, 3), QColor(0, 0, 0, 0));
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
// 다리(2프레임: 좌우 교대)
const int skew = (phase == 0) ? 0 : 3;
p.fillRect(ox + 2 + skew, a.bottom() - 4, 6, 4, dark);
p.fillRect(a.right() - 7 - skew, a.bottom() - 4, 6, 4, dark);
}
void drawUfoSprite(QPainter &p, const QRect &u)
{
p.setPen(Qt::NoPen);
p.setBrush(QColor(210, 70, 120));
p.drawEllipse(u.adjusted(4, 10, -4, -2));
p.setBrush(QColor(170, 55, 100));
p.drawEllipse(u.adjusted(1, 4, -1, -8));
p.setBrush(QColor(255, 255, 210));
p.drawEllipse(u.adjusted(10, 5, -10, -11));
// 창 불빛
p.setBrush(QColor(255, 250, 180));
for (int i = 0; i < 3; ++i) {
const int x = u.left() + 9 + i * 11;
p.drawEllipse(QRect(x, u.top() + 6, 6, 6));
}
}
} // namespace
GameCanvas::GameCanvas(QWidget *parent)
: QWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
const int w = kWorldW * m_scale;
const int h = kWorldH * m_scale;
setMinimumSize(w, h);
setMaximumSize(w, h);
}
void GameCanvas::refreshView()
{
update();
}
void GameCanvas::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing, false);
p.fillRect(rect(), QColor(10, 12, 28));
if (!m_engine) {
p.setPen(Qt::white);
p.drawText(rect(), Qt::AlignCenter, QStringLiteral("no engine"));
return;
}
// 별(장식)
p.setPen(QColor(80, 90, 130, 90));
for (int i = 0; i < 40; ++i) {
const int x = (i * 97 + m_engine->tickCount()) % kWorldW;
const int y = kHudH + 8 + (i * 53) % (kPlayBottom - kHudH - 40);
p.drawPoint(x, y);
}
p.setPen(QPen(QColor(40, 50, 90), 1));
p.drawLine(0, kHudH, kWorldW, kHudH);
if (m_engine->ufoActive())
drawUfoSprite(p, m_engine->ufoRect());
for (const Bunker &b : m_engine->bunkers())
p.drawImage(b.pos, b.mask);
const int aphase = m_engine->alienAnimPhase();
for (const QRect &a : m_engine->alienRects())
drawAlienSprite(p, a, aphase);
for (const Bullet &b : m_engine->bullets()) {
p.setPen(Qt::NoPen);
p.setBrush(b.fromPlayer ? QColor(255, 230, 80) : QColor(120, 200, 255));
p.drawRect(b.rect);
}
for (const ExplosionParticle &ex : m_engine->particles()) {
QColor c = ex.color;
c.setAlphaF(qBound(0.15, ex.life / 14.0, 1.0));
p.fillRect(QRectF(ex.pos.x() - 1, ex.pos.y() - 1, 4, 4), c);
}
const QRect pr = m_engine->playerRect();
p.setBrush(QColor(220, 220, 240));
p.setPen(Qt::NoPen);
p.drawRect(pr);
p.setBrush(QColor(180, 180, 220));
p.drawRect(QRect(pr.center().x() - 4, pr.y() - 6, 8, 6));
if (m_engine->status() == GameStatus::Intermission) {
const int sec = m_engine->intermissionSecondsDisplay();
if (sec > 0) {
const int phase = (m_engine->tickCount() / 6) % 2; // 2컷 애니메이션
QFont f;
f.setPixelSize(phase == 0 ? 120 : 136);
f.setBold(true);
f.setStyleHint(QFont::SansSerif);
p.setFont(f);
const QRect area(0, kHudH, kWorldW, kPlayBottom - kHudH);
const QString text = QString::number(sec);
// 2컷: "팝" 느낌 (외곽/빛/오프셋이 번갈아 변함)
p.setPen(QPen(QColor(0, 0, 0), phase == 0 ? 6 : 8));
p.drawText(area.translated(phase == 0 ? 3 : 2, phase == 0 ? 3 : 4), Qt::AlignCenter, text);
p.setPen(phase == 0 ? QColor(255, 235, 60) : QColor(255, 252, 210));
p.drawText(area, Qt::AlignCenter, text);
}
}
if (m_paused) {
// 게임 진행은 멈추고, 중앙에 "PAUSE"만 표시.
const QRect area(0, kHudH, kWorldW, kPlayBottom - kHudH);
const int phase = (m_engine ? (m_engine->tickCount() / 10) % 2 : 0);
QFont f;
f.setPixelSize(phase == 0 ? 86 : 92);
f.setBold(true);
f.setStyleHint(QFont::SansSerif);
p.setFont(f);
p.setPen(QPen(QColor(0, 0, 0), 8));
p.drawText(area.translated(3, 3), Qt::AlignCenter, QStringLiteral("PAUSE"));
p.setPen(QColor(230, 240, 255));
p.drawText(area, Qt::AlignCenter, QStringLiteral("PAUSE"));
}
}
void GameCanvas::keyPressEvent(QKeyEvent *event)
{
QWidget::keyPressEvent(event);
}