발전하는 춘배
[원고, SDL3] 5. 윈도우 오목 게임 만들기 - 버그해결 본문
문제 3가지가 있었다. 해결해보자.
1. 바둑알 놓아지는 위치
void Game::HandleEvent(const SDL_Event& e) {
if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
int x = e.button.x;
int y = e.button.y;
int cellSize = 1200 / (BOARDSIZE + 1);
int col = x / cellSize;
int row = y / cellSize;
if (row >= 0 && row < BOARDSIZE && col >= 0 && col < BOARDSIZE) {
if (board[row][col] == 0) {
board[row][col] = isUser1Turn ? 1 : 2;
isUser1Turn = !isUser1Turn;
}
}
}
}

지금코드다. 계산해보면 cellSizesms 75 나온다. 격자상에서 board[1][1]에 해당하는 위치는 (150,150)일 터이다. 그니깐 다시 말하면 대충 저기 이모티콘 있는 정도 위치를 클릭했을 때 board[1][1]의 값이 변화해야 된다.
그럼 합리적으로 생각했을때 (150-(75/2),150-(75/2)) ~ (150+(75/2),(150+(75/2))의 사각형 영역을 클릭했을 때 board[1][1]이 바뀌면 될거다. 그러면 col = x / cellSize로 구할 게 아니라 x-(75/2) / cellSize로 구해야 한다. 평행이동 했다고 생각하면 됨. row도 마찬가지.
int col = (x - (cellSize/2)) / cellSize;
int row = (y - (cellSize/2)) / cellSize;
해결완료.
3. 게임 승리 판정 타이밍
지금:
일단 위에 HandleEvent 안에서 바둑알 놓으면(클릭하면) 내부적으로 턴이 넘어간다. 근데 메인함수 보면
while (!quit) {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) {
quit = true;
}
game.HandleEvent(e);
}
// 바둑판 렌더링
DrawGrid(renderer);
// 바둑알 렌더링
game.Update();
game.Render(renderer);
SDL_RenderPresent(renderer);
}
이렇게 되어 있는데, 승리 조건 판정은 한참 밑에 Update()에다가 짜 놓았다. 그리고 승리 조건도 아무나 승리했는가? 를 보는 게 아니라 '지금 턴인 유저가 승리했는가?' 만 판정하기 때문에 실제론 이미 a가 승리했지만 b까지 돌을 놓고, 내부적으로 a의 차례가 돌았을 때 Update()에서 승리조건을 감지하므로 승리 즉시 승리하지 못하는 상황인 것이다. 이건 애초에 SDL 로직을 적용하면서 게임 로직이 바뀌어야 했던 거라서 바꿔주면 된다. 서순의 문제일 뿐.
어떻게 바꿀까 잠깐 생각하다가, game.Update()에서 턴을 넘기는 게 맞겠다는 생각을 했다. 직관적이다. 게임.업데이트() 함수에서 턴을 업데이트한다. 핸들이벤트()에서는 딱 클릭 들어오면 board[][]에 돌을 등록한다.
라고 생각했지만 문제가 생긴다. 내가 약간 오해하고 있었던 건, SDL_PollEvent() while문이 다음 이벤트까지 break를 걸어주는 건 줄 알았는데 아니었고, 따라서 매 프레임마다 game.Update()가 실행되며 매 프레임마다 턴이 바뀐다. 그러면 안 되는 거니깐 과감하게 Update()를 포기한다. 핸들이벤트()에서 제대로 착수가 됐을 때 승리 여부도 확인하고, 턴도 바꾼다.
3번문제도 해결.
2. 바둑알 깜빡거림
바둑알이 무지하게 깜빡거린다. 이유가 뭘까?
1) 관찰을 해봤더니 마우스를 열심히 움직일 수록 더 깜빡거린다.
그렇다면 while(SDL_PollEvent())는 여태 있었던 이벤트들을 하나씩 스택에서 꺼내가며 처리하는 거니깐,,,
이벤트가 너무 많이 쌓이면 그동안엔 바둑알 렌더링이 안 되는 거니깐 이렇게 깜빡거리는 건가? 근데 그렇다기엔 바둑판은 안깜빡거린다. 그래도 밑져야 본전이니깐 저 while문 안에서도 바둑알 렌더링 함수를 불러봤지만 역시 변하는 건 없었다.
2) 일단 매 프레임 일어나는 건 바둑판 렌더링, 바둑알 렌더링말곤 없다. 바둑알 렌더링이 쉽지 않은 건가? 한번 보자.
void Game::Render(SDL_Renderer* renderer) {
// 돌 그리기
int cellSize = 1200 / (BOARDSIZE + 1);
for (int i = 0; i < BOARDSIZE; i++) {
for (int j = 0; j < BOARDSIZE; j++) {
if (board[i][j] != 0) {
int cx = (j + 1) * cellSize;
int cy = (i + 1) * cellSize;
DrawFilledCircle(renderer, cx, cy, cellSize / 2 - 2, board[i][j] == 1 ? 0 : 255); // 검정/흰 돌
}
}
}
}
15*15 board에서 돌이 놓인 위치를 찾아 DrawFilledCircle 함수를 실행한다.
DrawFilledCircle은 모든 좌표에 점을 찍는, 오버헤드가 좀 있는 작업이다. 그래서 그런 건 아닐까?
테스트를 위해 DrawFilledCircle 대신 원의 테두리만 그리는 함수로 갈아끼워본다.
똑같다. 결국 함수 자체의 오버헤드가 문제는 아니다. 그럼 뭐가 문제일까?
3) 해결
gpt의 도움을 받아보았다. 해결되었다.
원인은 다음과 같았다
원래 코드에서 매 프레임 다음과 같은 일들을 한다.
바둑판 렌더링 -> SDL_RenderPresent(renderer) -> 바둑알 렌더링 -> SDL_RenderPresent(renderer)
이렇게 된 이유는 바둑판 렌더링 코드들을 함수로 뽑아내면서 SDL_RenderPresent(renderer)까지 합쳐서 뽑아버린 까닭임.
이럼 무슨 문제가 생기냐면 바둑알 없이 화면이 그려짐 -> 바둑알 포함된 화면이 그려짐 -> (다음프레임) -> 바둑알 없이 화면이 그려짐 -> 바둑알 포함된 화면이 그려짐. 이래서 바둑알이 생겼다없어졌다 한거다.
결론!
SDL_RenderPresent(renderer);는 프레임당 한번 호출하자.
근데 그건 그렇고 여기서 의문이 생겼다.
그럼 바둑판은 왜 안 깜빡거린 것일까?
gpt형님한테 물어봤다.
이해는 했어. 근데 나 그럼 궁금한 거. 바둑판은 왜 안 깜빡거려?
보드 그리기 -> SDL_RenderPresent(renderer); -> 바둑알 그리기 -> SDL_RenderPresent(renderer);
가 한 프레임에 모두 있다면, 첫 SDL_RenderPresent(renderer);때 렌더러 버퍼는 비워지지 않아?
그럼 바둑알 그린 이후 SDL_RenderPresent(renderer);때는 바둑판이 없어지고 바둑알만 그려져야 하는 게 맞는 거 아니야?
답변 요약:
SDL_RenderPresent()는 버퍼를 “비우는” 게 아니라 “스왑(swap)”하는 거다.
SDL은 내부적으로 더블 버퍼링(double buffering) 을 쓴다.
즉, 두 개의 버퍼(front, back)를 번갈아 가면서 사용한다.
- front buffer → 지금 모니터에 보이는 화면
- back buffer → SDL_RenderCopy, SDL_RenderDraw 같은 함수로 그리고 있는 임시 공간
SDL_RenderPresent(renderer)를 호출하면 back buffer의 내용을 front buffer와 교체 (→ 화면에 표시)
버퍼 초기화는 SDL_RenderClear();를 호출해야함.
아하 이해가 됐다.
결과적으로 오목 게임의 핵심은 완성이 됐다.
이제 게임답게 시작화면이랑 게임승리문구정도 띄우고 다시하기 기능까지만 추가하고 끝내려 한다.
'.원고' 카테고리의 다른 글
| [원고, C++/OOP] 로그라이크 게임 만들어보기 1 - 클래스 헤더 파일 분리, enum class, 공격과 피해 책임 분리 (0) | 2026.02.07 |
|---|---|
| [원고, SDL3](完) 6. 윈도우 오목 게임 만들기 - 메뉴화면, 종료화면, 게임 상태의 표현, cmake 써먹어보기, 컴파일 옵션들 이해해보기 (3) | 2025.08.25 |
| [원고, SDL3] 4. 윈도우 오목 게임 만들기 - SDL써서 그래픽 띄우기, 클릭이벤트 써먹기 (1) | 2025.08.24 |
| [원고, SDL3] 3. 윈도우 오목 게임 만들기 - 클래스 파일 분할, 분할컴파일 (3) | 2025.08.24 |
| [원고, SDL3] 2. 윈도우 오목게임 만들기 기록 1 (0) | 2025.08.24 |
