안녕하세요 JollyTree입니다 (•̀ᴗ•́)و
SFML의 sf:Rect<T> 클래스 내 intersects() 메소드는 사각형 영역에 대한 충돌(Collision) 감지를 지원합니다. 2개의 Sprite나 Shape가 존재하면서 서로 영역이 겹친다면 이를 충돌로 인식할 수 있습니다.이번 포스팅은 1:n의 RectangleShape 개체를 화면에 출력하고 1을 플레이어로 가정하여 n개의 개체들과 충돌하는 것을 공유하려고 합니다.
기본 컨셉은 먼저 MAX_ENEMY_COUNT 20개 만큼 enemies 에 저장합니다. 20개의 적들은 1~25 x 1~25의 랜덤한 크기로 1~500의 랜덤한 위치에서 등장합니다. 사용자는 상/하/좌/우 방향키로 플레이어(rect_player)를 조정하여 위에서 아래로 박스들과의 충돌을 시도합니다.
만약 20개의 적들 중 플레이어(rect_player)와 충돌이 발생하면 해당 적은 개체 목록에서 사라질뿐만 아니라 화면에서도 사라지게됩니다. 모든 개체들과 충돌을 유발하여 남은 개체 수가 0 이되면 프로그램은 종료됩니다.
아래 RectangleShape, CircleShape 개체 대신 Sprite 개체로 바꿔도 충돌 실험은 가능할 것 같은데 스프라이트의 투명색을 인식해서 정확히 경계선 체크가 될지는 확인을 해보아야 할것 같습니다. 만약 SFML이 지원하지 않는다면 직접 개발을 해야 하지 않을까 합니다. 이 부분은 나중에 확인되면 공유할게요.
//RectangleShape
//CircleShape vector<CircleShape> enemies; |
예제에서는 충돌이 발생하면 rect_player는 그대로 있고 충돌한 적이 사라지게 되어 있습니다. 만약, 플레이어(rect_player)를 사라지게 하고 싶으면 n개의 플레이어(rect_player)의 총 개수를 카운드 해야 할 것 같네요. 목숨이 하나라면 그냥 화면상에서 사라지고 게임을 끝내면 되구요.
SFML sf:Rect<T> 클래스 intersects()를 이용 개체 충돌 예제(Example):
#include <SFML/Graphics.hpp>
#include <iostream>
#define MOVE_PIXEL 5.f
#define MAX_ENEMY_COUNT 20
using namespace std;
using namespace sf;
float rand_number(int max)
{
float num = (float)(rand() % max + 1);
return num;
}
int main()
{
Clock clock;
float interval = 0;
float rectx_p = 0, recty_p = 0;
float rectx_n = 50, recty_n = 50;
srand((unsigned int)time(NULL));
RectangleShape rect_player;
vector<RectangleShape> enemies;
RectangleShape enemy_rect;
rect_player.setSize(sf::Vector2f(15, 15));
rect_player.setOutlineColor(sf::Color::Green);
rect_player.setOutlineThickness(2);
rect_player.setPosition(150, 400);
for (int i = 0; i < MAX_ENEMY_COUNT; i++)
{
enemy_rect.setSize(Vector2f(rand_number(25), rand_number(25)));
enemy_rect.setOutlineColor(sf::Color::Red);
enemy_rect.setOutlineThickness(5);
enemy_rect.setPosition(rand_number(500), recty_n);
Vector2f rpos = enemy_rect.getPosition();
cout << "rpos.x = " << rpos.x << "rpos.y = " << rpos.y << endl;
enemies.push_back(enemy_rect);
}
cout << "프로그램이 시작되었습니다." << endl;
RenderWindow app(VideoMode(504, 504), "https://maincodes.tistory.com/");
app.setFramerateLimit(60);
while (app.isOpen())
{
float time = clock.getElapsedTime().asSeconds();
clock.restart();
interval += time;
Event event;
while (app.pollEvent(event))
{
if (event.type == Event::EventType::Closed)
{
app.close();
cout << "프로그램이 종료되었습니다." << endl;
}
if (Keyboard::isKeyPressed(Keyboard::Left))
rect_player.move(-MOVE_PIXEL, 0);
if (Keyboard::isKeyPressed(Keyboard::Right))
rect_player.move(MOVE_PIXEL, 0);
if (Keyboard::isKeyPressed(Keyboard::Up))
rect_player.move(0, -MOVE_PIXEL);
if (Keyboard::isKeyPressed(Keyboard::Down))
rect_player.move(0, MOVE_PIXEL);
}
app.clear(Color::Black);
if ((int)interval % 1 == 0)
{
vector< RectangleShape>::iterator iter;
for (iter = enemies.begin(); iter != enemies.end(); iter++)
{
//내려오는 속도를 랜덤하게 설정
(*iter).move(0, rand_number(3));
Vector2f pos = (*iter).getPosition();
if (pos.y > 500)
(*iter).setPosition(rand_number(400), recty_n);
}
}
//상자들에 대한 글로벌 바운드 구하고, 플레이어 상자와 충돌감지
vector< RectangleShape>::iterator iter;
for (int i = 0; i < enemies.size(); i++)
{
if ((enemies[i].getGlobalBounds()).intersects(rect_player.getGlobalBounds()))
{
enemies.erase(enemies.begin() + i);
cout << i << "번째 상자와 충돌 발생!" << endl;
}
}
//적 상자 그리기
for (iter = enemies.begin(); iter != enemies.end(); iter++)
app.draw(*iter);
//플레이어 상자 그리기
app.draw(rect_player);
app.display();
if (enemies.size() == 0)
{
cout << "충돌 시험이 끝났습니다. 프로그램을 종료합니다." << endl;
app.close();
break;
}
}
return 0;
}
|
cs |
실행결과(Output):
예제는 플레이어(rect_player)가 적 상자들을 따라가서 없애야 하지만 위에서 잠시 설명했듯이 조금 응용하면 적 상자들과의 충돌을 피해는 유명한 닷지 게임(총알 피하기) 형태로도 변경이 가능합니다. 특별히 계획을 가지고 있진 않지만 우주를 배경으로 한 닷지 게임을 스터디 목적으로 직접 제작해 보고 싶은 마음이 듭니다. :)
아래 예제는 RectangleShape 대신 CircleShape로 변경한 예제입니다. 내용은 거의 동일하고요. 사각형이 아닌 원이기 때문에 중간에 setSize() 대신 setRadious() 메소드를 사용한 정도만 차이가 있어요.
CircleShape를 이용한 충돌 시험 예제(Example):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
#include <SFML/Graphics.hpp>
#include <iostream>
#define MOVE_PIXEL 5.f
#define MAX_ENEMY_COUNT 20
using namespace std;
using namespace sf;
float rand_number(int max)
{
float num = (float)(rand() % max + 1);
return num;
}
int main()
{
Clock clock;
float interval = 0;
float rectx_p = 0, recty_p = 0;
float rectx_n = 50, recty_n = 50;
srand((unsigned int)time(NULL));
RectangleShape rect_player;
vector<CircleShape> enemies;
CircleShape enemy_circle;
rect_player.setSize(sf::Vector2f(15, 15));
rect_player.setOutlineColor(sf::Color::Green);
rect_player.setOutlineThickness(2);
rect_player.setPosition(150, 400);
for (int i = 0; i < MAX_ENEMY_COUNT; i++)
{
enemy_circle.setRadius(rand_number(25));
enemy_circle.setOutlineColor(sf::Color::Red);
enemy_circle.setOutlineThickness(5);
enemy_circle.setPosition(rand_number(500), recty_n);
Vector2f rpos = enemy_circle.getPosition();
cout << "rpos.x = " << rpos.x << "rpos.y = " << rpos.y << endl;
enemies.push_back(enemy_circle);
}
cout << "프로그램이 시작되었습니다." << endl;
RenderWindow app(VideoMode(504, 504), "https://maincodes.tistory.com/");
app.setFramerateLimit(60);
while (app.isOpen())
{
float time = clock.getElapsedTime().asSeconds();
clock.restart();
interval += time;
Event event;
while (app.pollEvent(event))
{
if (event.type == Event::EventType::Closed)
{
app.close();
cout << "프로그램이 종료되었습니다." << endl;
}
if (Keyboard::isKeyPressed(Keyboard::Left))
rect_player.move(-MOVE_PIXEL, 0);
if (Keyboard::isKeyPressed(Keyboard::Right))
rect_player.move(MOVE_PIXEL, 0);
if (Keyboard::isKeyPressed(Keyboard::Up))
rect_player.move(0, -MOVE_PIXEL);
if (Keyboard::isKeyPressed(Keyboard::Down))
rect_player.move(0, MOVE_PIXEL);
}
app.clear(Color::Black);
if ((int)interval % 1 == 0)
{
vector< CircleShape>::iterator iter;
for (iter = enemies.begin(); iter != enemies.end(); iter++)
{
//내려오는 속도를 랜덤하게 설정
(*iter).move(0, rand_number(3));
Vector2f pos = (*iter).getPosition();
if (pos.y > 500)
(*iter).setPosition(rand_number(400), recty_n);
}
}
//상자들에 대한 글로벌 바운드 구하고, 플레이어 상자와 충돌감지
vector<CircleShape>::iterator iter;
for (int i = 0; i < enemies.size(); i++)
{
if ((enemies[i].getGlobalBounds()).intersects(rect_player.getGlobalBounds()))
{
enemies.erase(enemies.begin() + i);
cout << i << "번째 원과 충돌 발생!" << endl;
}
}
//적 상자 그리기
for (iter = enemies.begin(); iter != enemies.end(); iter++)
app.draw(*iter);
//플레이어 상자 그리기
app.draw(rect_player);
app.display();
if (enemies.size() == 0)
{
cout << "충돌 시험이 끝났습니다. 프로그램을 종료합니다." << endl;
app.close();
break;
}
}
return 0;
}
|
cs |
실행결과(Output):
관심있는 분들께 조금이라도 도움이 되었으면 좋겠습니다. 이상 JollyTree였습니다 (•̀ᴗ•́)و
'Coding & Programming > C, C++, SFML' 카테고리의 다른 글
[C/C++, SFML] 사이먼 게임(Simon Game) 만들기(Build a Simon Game) (2) | 2021.04.06 |
---|---|
[C/C++, SFML] 10. 키보드 이벤트(Event) 및 상태(State) 처리하기 (2) | 2021.04.04 |
[C/C++, SFML] 9. 엔티티(Sprite, Text, Share 등) 위치, 로테이션, 스케일 등 모양 바꾸기 (0) | 2021.04.02 |
[C/C++, SFML] 8. 마우스 이벤트(Event) 및 상태(State) 처리하기 (4) | 2021.04.01 |
[C/C++, SFML] 7. 오디오(음악, 효과음) 파일 읽고 재생하기(SoundBuffer, Sound) (2) | 2021.03.30 |