반응형

런타임(디버그)에 collision shape이 표시되도록 해 보자.

 

CollisionShape2D를 추가하고 크기를 조절한다.

 

Debug - Visible Collision Shapes를 체크한다.

 

현재 씬을 실행(디버그)하면 collision shape이 표시된다.

 

반응형
Posted by J-sean
:
반응형

오브젝트가 충돌한 포인트를 확인해 보자.

 

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
class DebugDrawer : public b2Draw
{
public:
    DebugDrawer(SDL_Renderer* renderer) {
        this->renderer = renderer;
    }
private:
    SDL_Renderer* renderer;
    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {}
    void DrawPoint(const b2Vec2& p, float sizeconst b2Color& color) {}
    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
        SDL_SetRenderDrawColor(renderer, 25500, SDL_ALPHA_OPAQUE);
        SDL_FRect* rect = new SDL_FRect{ vertices[0].x, vertices[0].y, vertices[1].x
            - vertices[0].x, vertices[3].y - vertices[0].y };
        SDL_RenderDrawRectF(renderer, rect);
    }
    void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) {}
    void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) {}
    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {}
    void DrawTransform(const b2Transform& xf) {}
};
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // DebugDraw 정의
    DebugDrawer* debugDrawer = new DebugDrawer(renderer);
    debugDrawer->SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit |
        b2Draw::e_centerOfMassBit | b2Draw::e_aabbBit | b2Draw::e_pairBit);
    world.SetDebugDraw(debugDrawer);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
    playerBody->CreateFixture(&fixtureDef);
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
    SDL_Rect groundRect = { 040050010 };
    groundBody->CreateFixture(&groundBox, 0.0f);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
    SDL_Rect wallRect = { 300010480 };
    wallBody->CreateFixture(&wallBox, 0.0f);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
 
        // DebugDraw 그리기
        world.DebugDraw();
 
        // 가장 마지막에 contact point 그리기
        SDL_SetRenderDrawColor(renderer, 02550, SDL_ALPHA_OPAQUE);
        for (b2Contact* contact = world.GetContactList(); contact; contact = contact->GetNext()) {
            for (int i = 0; i < contact->GetManifold()->pointCount; i++)
            {
                b2Vec2 point = contact->GetFixtureA()->GetBody()->GetWorldPoint(
                    contact->GetManifold()->points[i].localPoint);
                SDL_FRect rect = { point.x - 5, point.y - 51010 };
                SDL_RenderFillRectF(renderer, &rect);
            }
        }
        // A very important point to note if you do this, is that the existence of a contact in these
        // lists does not mean that the two fixtures of the contact are actually touching - it only means
        // their AABBs are touching. If you want to know if the fixtures themselves are really touching
        // you can use IsTouching() to check.
 
        SDL_RenderPresent(renderer);
    }
 
    delete debugDrawer;
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

충돌 포인트가 녹색 사각형으로 표시된다.

 

완전히 멈추면 두 개의 포인트가 표시된다.

 

 

이제 검은색으로 법선 벡터 그리는 코드를 추가하자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 가장 마지막에 contact point & 법선 벡터 그리기
for (b2Contact* contact = world.GetContactList(); contact; contact = contact->GetNext()) {
    for (int i = 0; i < contact->GetManifold()->pointCount; i++)
    {
        b2Vec2 point = contact->GetFixtureA()->GetBody()->GetWorldPoint(
            contact->GetManifold()->points[i].localPoint);
        SDL_FRect rect = { point.x - 5, point.y - 51010 };
        SDL_SetRenderDrawColor(renderer, 02550, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRectF(renderer, &rect);
 
        SDL_SetRenderDrawColor(renderer, 000, SDL_ALPHA_OPAQUE);
        SDL_RenderDrawLineF(renderer, point.x, point.y,
            point.x + contact->GetManifold()->localNormal.x * 20, point.y + contact->GetManifold()->localNormal.y * 20);
    }
}
// A very important point to note if you do this, is that the existence of a contact in these
// lists does not mean that the two fixtures of the contact are actually touching - it only means
// their AABBs are touching. If you want to know if the fixtures themselves are really touching
// you can use IsTouching() to check.
 

 

코드를 수정하고 빌드한다.

 

녹색 충돌 포인트 위에 검은색 법선 벡터가 그려진다.

 

 

 

속도(velocity) 벡터는 아래와 같은 코드로 표시할 수 있다.

 

1
2
3
4
SDL_SetRenderDrawColor(renderer, 25500, SDL_ALPHA_OPAQUE);
b2Vec2 position = playerBody->GetPosition();
SDL_RenderDrawLineF(renderer, position.x, position.y,
    position.x + playerBody->GetLinearVelocity().x * 10, position.y + playerBody->GetLinearVelocity().y * 10);
 

 

 

 

 

※ 참고

Box2D b2ContactListener

 

반응형
Posted by J-sean
:
반응형

한 오브젝트에 여러개의 Shape을 추가해 충돌을 감지해 보자.

XXXShape이나 FixtureDef를 CreateFixture()에 전달하면 충돌 감지기가 추가된다.

 

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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
class DebugDrawer : public b2Draw
{
public:
    DebugDrawer(SDL_Renderer* renderer) {
        this->renderer = renderer;
    }
private:
    SDL_Renderer* renderer;
    void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {}
    void DrawPoint(const b2Vec2& p, float sizeconst b2Color& color) {}
    void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) {
        SDL_SetRenderDrawColor(renderer, 25500, SDL_ALPHA_OPAQUE);
        SDL_FRect* rect = new SDL_FRect{ vertices[0].x, vertices[0].y, vertices[1].x
            - vertices[0].x, vertices[3].y - vertices[0].y };
        SDL_RenderDrawRectF(renderer, rect);
    }
    void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) {}
    void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) {}
    void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) {}
    void DrawTransform(const b2Transform& xf) {}
};
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // DebugDraw 정의
    DebugDrawer* debugDrawer = new DebugDrawer(renderer);
    debugDrawer->SetFlags(b2Draw::e_shapeBit | b2Draw::e_jointBit |
        b2Draw::e_centerOfMassBit | b2Draw::e_aabbBit | b2Draw::e_pairBit);
    world.SetDebugDraw(debugDrawer);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
    playerBody->CreateFixture(&fixtureDef);
    // Player에 shape 추가
    b2PolygonShape another;
    another.SetAsBox(1040, b2Vec2(0-60), 0.0f);
    playerBody->CreateFixture(&another, 1.0f);
    // Creates a fixture from a shape and attach it to this body. This is a convenience function.
    // Use b2FixtureDef if you need to set parameters like friction, restitution, user data, or
    // filtering. If the density is non-zero, this function automatically updates the mass of the body.
    /*
    b2Fixture* temp = NULL;
    for (b2Fixture* fix = playerBody->GetFixtureList(); fix; fix=fix->GetNext()) {
        std::cout << fix->GetFriction() << std::endl;
        if (fix->GetFriction() == 0.2f)
            temp = fix;
        // 여기서 바로 playerBody->DestroyFixture()를 호출하면 fix가 파괴되어 GetNext()를 호출 할 수
        // 없게 된다. 임시 변수에 저장하고 for문을 벗어나면 파괴하자.
    }
    if (temp != NULL)
        playerBody->DestroyFixture(temp);
    // 이 예제에서는 Friction값을 비교해서 삭제할 Fixture를 선택했지만 필요하다면
    // UserData를 설정해서 비교하자.
    */
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
    SDL_Rect groundRect = { 040050010 };
    groundBody->CreateFixture(&groundBox, 0.0f);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
    SDL_Rect wallRect = { 300010480 };
    wallBody->CreateFixture(&wallBox, 0.0f);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
 
        // 가장 마지막에 DebugDraw 그리기
        world.DebugDraw();
 
        SDL_RenderPresent(renderer);
    }
 
    delete debugDrawer;
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

실행하면 공룡의 뿔같은 충돌 감지기가 추가된 모습이 보인다.

 

뿔 처럼 생긴 충돌 감지기는 크기, 위치, Density만 설정되어있다.

 

반응형
Posted by J-sean
:
반응형

충돌을 감지하고 어떤 오브젝트가 충돌했는지 확인해 보자.

 

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
129
130
131
132
133
134
135
#include <iostream>
#include "SDL.h"
#include "box2d/box2d.h"
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
    SDL_Surface* playerSurface = SDL_LoadBMP("player.bmp");
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, playerSurface);
 
    // World 정의
    b2Vec2 gravity(0.0f, 9.8f);
    b2World world(gravity);
 
    // Player 정의
    b2BodyDef playerBodyDef;
    playerBodyDef.type = b2_dynamicBody;
    playerBodyDef.position.Set(0.0f, 0.0f);
    playerBodyDef.linearVelocity = b2Vec2(50.0f, 0.0f);
    playerBodyDef.angularVelocity = 0.2f;
    b2Body* playerBody = world.CreateBody(&playerBodyDef);
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox((float)playerSurface->/ 2, (float)playerSurface->/ 2);
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    fixtureDef.friction = 0.3f;
    fixtureDef.restitution = 0.5f;
    playerBody->CreateFixture(&fixtureDef);
    // Player 유저 정보
    const char* playerName = "Player";
    playerBody->GetUserData().pointer = reinterpret_cast<uintptr_t>(playerName);
 
    // Ground 정의
    b2BodyDef groundBodyDef;
    groundBodyDef.position.Set(0.0f, 400.0f);
    b2Body* groundBody = world.CreateBody(&groundBodyDef);
    b2PolygonShape groundBox;
    groundBox.SetAsBox(500.0f, 0.0f);
    SDL_Rect groundRect = { 040050010 };
    groundBody->CreateFixture(&groundBox, 0.0f);
    // Ground 유저 정보
    const char* groundName = "Ground";
    groundBody->GetUserData().pointer = reinterpret_cast<uintptr_t>(groundName);
 
    // Wall 정의
    b2BodyDef wallBodyDef;
    wallBodyDef.position.Set(300.0f, 0.0f);
    b2Body* wallBody = world.CreateBody(&wallBodyDef);
    b2PolygonShape wallBox;
    wallBox.SetAsBox(0.0f, 480.0f);
    SDL_Rect wallRect = { 300010480 };
    wallBody->CreateFixture(&wallBox, 0.0f);
    // Wall 유저 정보
    const char* wallName = "Wall";
    wallBody->GetUserData().pointer = reinterpret_cast<uintptr_t>(wallName);
 
    float timeStep = 1.0f / 500.0f;
    int velocityIterations = 6;
    int positionIterations = 2;
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_SPACE) {
                    playerBody->SetTransform(b2Vec2(0.0f, 0.0f), 0.0f);
                    playerBody->SetLinearVelocity(b2Vec2(50.0f, 0.0f));
                    playerBody->SetAngularVelocity(0.2f);
                }
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
 
            default:
                break;
            }
        }
 
        world.Step(timeStep, velocityIterations, positionIterations);
 
        for (b2Contact* c = world.GetContactList(); c; c = c->GetNext())
        {
            if (c->IsTouching()) {
                printf("FixtureA: %s\n", (char*)(c->GetFixtureA()->GetBody()->GetUserData().pointer));
                printf("FixtureB: %s\n", (char*)(c->GetFixtureB()->GetBody()->GetUserData().pointer));
                printf("Count: %d\n", c->GetManifold()->pointCount);
            }
            //if (c->GetFixtureA()->IsSensor() == false)
                //c->GetFixtureA()->SetSensor(true);
        }
        // A very important point to note if you do this, is that the existence of a contact in these
        // lists does not mean that the two fixtures of the contact are actually touching - it only
        // means their AABBs are touching. If you want to know if the fixtures themselves are really
        // touching you can use c->IsTouching() to check.
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
 
        // Ground 그리기
        SDL_SetRenderDrawColor(renderer, 22014020, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &groundRect);
 
        // Wall 그리기
        SDL_SetRenderDrawColor(renderer, 646464, SDL_ALPHA_OPAQUE);
        SDL_RenderFillRect(renderer, &wallRect);
 
        // Player 그리기        
        b2Vec2 playerPosition = playerBody->GetPosition();
        SDL_Rect destRect = { (int)playerPosition.x - playerSurface->/ 2,
            (int)playerPosition.y - playerSurface->/ 2, playerSurface->w, playerSurface->h };
        SDL_RendererFlip flip = SDL_FLIP_NONE;
        float angle = playerBody->GetAngle() * (180 / (float)M_PI);
 
        SDL_RenderCopyEx(renderer, texture, NULL&destRect, angle, NULL, flip);
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(playerSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 작성하고 빌드한다.

오브젝트 UserData 설정시 SetUserData()를 사용하면 안된다는 것에 주의한다. (아래 참고 참조)

 

실행하면 Player 오브젝트가 던져지고 벽과 바닥에 부딪힌다.

 

부딪힌 오브젝트와 몇 개의 포인트에서 충돌이 발생한지 확인 할 수 있다.

 

바닥에 완전히 정지하면 2개 포인트에서 충돌이 계속 발생한다.

 

※ 참고

Box2D Collision Module

Box2D Dynamic Module - Contacts

Box2D UserData

Box2D SetUserData()

 

반응형
Posted by J-sean
:
반응형

타일맵에 Collision Layer, Collision Mask를 설정하고 게임 실행 중 코드에서 Custom Data에 따라 적절히 바꿀 수 있다.

 

타일맵을 준비한다.

 

TileMap - Tile Set- Physics Layers - Collision Layer/Mask를 설정한다.

 

TileMap - Tile Set - Custom Data Layers에 원하는 데이터 이름과 타입을 설정한다.

 

Custom Data를 지정할 타일을 선택하고 Custom Data에 원하는 데이터 값을 설정한다. 위에서 bool 타입을 설정했기 때문에 On(True)이 표시된다. (0은 첫 번째 타일맵 레이어를 뜻한다)

 

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
using Godot;
 
public partial class player : Node2D
{
    [Export]
    CharacterBody2D character;
 
    public override void _Ready()
    {
        //character = GetNode<CharacterBody2D>("CharacterBody2D");
    }
 
    public override void _Process(double delta)
    {
        Vector2I mousePosition = GetNode<TileMap>("TileMap").LocalToMap(GetViewport().GetMousePosition());
        // 마우스 커서 위치를 타일맵 좌표로 변환한다.
        int layers = GetNode<TileMap>("TileMap").GetLayersCount();
        // 타일맵 레이어 갯수를 가져온다.
 
        for (int layer = 0; layer < layers; layer++)
        {
            TileData td = GetNode<TileMap>("TileMap").GetCellTileData(layer, mousePosition);
            // 마우스가 위치한 타일의 타일 데이터를 가져온다.
            if (td != null)
            {
                GD.Print($"Layer: {layer}");
                GD.Print($"CustomData: {td.GetCustomData("extraData")}");
                // TileMap - Inspector - Custom Data Layers 에서 추가한 변수를
                // TileSet탭 - Tiles탭 - Select - Custom Data 에서 세팅한 값.
 
                //GetNode<TileMap>("TileMap").TileSet.SetPhysicsLayerCollisionLayer(layer, 8);
                //GetNode<TileMap>("TileMap").TileSet.SetPhysicsLayerCollisionMask(layer, 16);
                // extraData 값을 확인하고 그에 맞게 레이어 값을 바꿀 수 있다.
 
                //int groundLayer = 1;
                //int buildingLayer = 2;
                //character.SetCollisionLayerValue(groundLayer, false);
                //character.SetCollisionMaskValue(buildingLayer, true);
                // 아니면 상화에 따라 캐릭터의 collision layer/mask 값을 바꿀 수 있다.
                // 주의할 점은 SetCollisionXXXValue()의 레이어 값은 1~32이므로 미리 변경하고 싶은
                // 레이어 번호를 변수에 저장하고 사용하자. 0 이 들어가면 안된다.
            }
            else
            {
                GD.Print("NULL");
            }
 
            GD.Print($"Collision Layer:" +
                $"{GetNode<TileMap>("TileMap").TileSet.GetPhysicsLayerCollisionLayer(layer)}");
            GD.Print($"Collision Mask:" +
                $"{GetNode<TileMap>("TileMap").TileSet.GetPhysicsLayerCollisionMask(layer)}");
            // 1번 레이어(마스크) value: 1
            // 2번 레이어(마스크) value: 2
            // 3번 레이어(마스크) value: 4
            // 4번 레이어(마스크) value: 8
            // ... 9번 레이어(마스크) value: 256 ...
        }
    }
}
 

 

 

스크립트를 작성한다.

 

게임을 실행하면 마우스 위치에 따라 정보가 표시된다.

 

반응형

'Godot' 카테고리의 다른 글

[Godot] 2D Navigation Basic Setup  (0) 2023.10.06
[Godot] Wall Jump 벽 점프  (0) 2023.10.02
[Godot] RayCast2D C# Example  (0) 2023.09.27
[Godot] Area2D Gravity 중력  (0) 2023.09.27
[Godot] AddChild(), RemoveChild()  (0) 2023.09.26
Posted by J-sean
:

[Godot] RayCast2D C# Example

Godot 2023. 9. 27. 17:45 |
반응형

RayCast2D C# 예제.

 

씬을 준비하고 RayCast2D와 스크립트를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    public override void _Draw()
    {
        DrawLine(GetNode<RayCast2D>("RayCast2D").Position,
            GetNode<RayCast2D>("RayCast2D").Position +
            GetNode<RayCast2D>("RayCast2D").TargetPosition,
            new Color(1000.5f), 5);
        // Ray를 확인할 수 있도록 선으로 표시한다.
    }
 
    public override void _PhysicsProcess(double delta)
    {    
        if (GetNode<RayCast2D>("RayCast2D").IsColliding())
        {
            Node obj = GetNode<RayCast2D>("RayCast2D").GetCollider() as Node;            
            GD.Print(obj.Name);
            // Ray와 충돌한 오브젝트의 이름을 출력한다.
        }
   ...
 

 

 

스크립트를 작성하고 실행하면 Ray와 충돌하는 오브젝트의 이름이 출력된다.

 

※ 참고

RayCast2D

 

반응형
Posted by J-sean
:
반응형

보글보글 같은 플랫폼 게임에서 볼 수 있는 캐릭터와 플랫폼의 한 쪽 방향 충돌 체크를 구현해 보자.

 

화면과 같이 Sprite2D, StaticBody2D, CollisionShape2D를 이용해 배경과 바닥을 구성한다.

 

gound.jpg
0.20MB

 

화면과 같이 Sprite2D, StaticBody2D, CollisionShape2D를 이용해 플랫폼을 구성한다.

CollisionShape2D - One Way Collision 속성을 체크한다.

필요하다면 Sprite2D - Transform - Scale을 조절한다.

 

Platform.png
0.01MB

 

화면과 같이 CharacterBody2D, Sprite2D, CollisionShape2D를 이용해 플레이어를 구성한다.

플레이어는 스크립트를 추가한다.

 

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
using Godot;
 
public partial class Player : CharacterBody2D
{
    public const float Speed = 300.0f;
    public const float JumpVelocity = -600.0f;
    // 플랫폼에 올라갈 수 있을 정도의 점프
 
    // Get the gravity from the project settings to be synced with RigidBody nodes.
    public float gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
 
    public override void _PhysicsProcess(double delta)
    {
        Vector2 velocity = Velocity;
 
        // Add the gravity.
        if (!IsOnFloor())
            velocity.Y += gravity * (float)delta;
 
        // Handle Jump.
        if (Input.IsActionJustPressed("ui_accept"&& IsOnFloor())
            velocity.Y = JumpVelocity;
 
        // 플랫폼에서 내려오기
        if (Input.IsActionJustPressed("ui_down"&& IsOnFloor())
            Position += new Vector2(01);
 
        // Get the input direction and handle the movement/deceleration.
        // As good practice, you should replace UI actions with custom gameplay actions.
        Vector2 direction = Input.GetVector("ui_left""ui_right""ui_up""ui_down");
        // direction 벡터는 키 입력이 반영되어 길이가 1인 normalized vector 값이 설정된다.
        // 예를들어 오른쪽 방향키가 눌렸다면 (1, 0), 아래 방향키가 눌렸다면 (0, 1),
        // 왼쪽+위 방향키가 눌렸다면 (-0.707, -0.707)이 설정된다.
        if (direction != Vector2.Zero)
        {
            velocity.X = direction.X * Speed;
        }
        else
        {
            velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed);
        }
 
        Velocity = velocity;
        MoveAndSlide();
    }
}
 

 

 

플레이어 스크립트를 위와 같이 작성한다.

 

그라운드, 플랫폼, 플레이어로 메인 씬을 구성한다.

 

게임을 실행하고 플랫폼에서 동작을 테스트 한다.

 

※ 참고

Using CharacterBody2d/3D

CharacterBody2D

 

반응형
Posted by J-sean
:
반응형

스프라이트 그룹의 충돌을 감지해 보자.

 

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
import pygame
 
pygame.init()
pygame.display.set_caption("Super fun game development")
screen = pygame.display.set_mode((640480))
clock = pygame.time.Clock()
 
class Player(pygame.sprite.Sprite):
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
 
        self.direction = -1
        self.speed = 4
        self.image = pygame.image.load("player.png").convert()
        self.image.set_colorkey(self.image.get_at((00)))
        self.size = (self.image.get_width()*1.5self.image.get_height()*1.5)
        self.image = pygame.transform.scale(self.image, self.size)
        self.rect = self.image.get_rect(center=position)
 
    def flip_image(self):
        self.image = pygame.transform.flip(self.image, TrueFalse)
        
    def update(self):
        pass
# 플레이어 클래스
 
class Bubble(pygame.sprite.Sprite):
    def __init__(self, position):
        pygame.sprite.Sprite.__init__(self)
 
        self.image = pygame.image.load("bubble.png").convert()
        self.image.set_colorkey(self.image.get_at((00)))
        self.size = (self.image.get_width()*6self.image.get_height()*6)
        self.image = pygame.transform.scale(self.image, self.size)
        self.rect = self.image.get_rect(center=position)
        self.collided = False
 
    def update(self):
        if self.collided == True:
            self.rect.top -= 1
# 버블 클래스
# 충돌(self.collided)을 감지하면 위치(self.rect.top)가 변한다.
 
def main():
    player = Player((screen.get_width()/2, screen.get_height()/2))
    player_sprite = pygame.sprite.Group(player)
    # 플레이어 스프라이트 그룹
 
    bubbles = [
        Bubble((40, screen.get_height()/2)),
        Bubble((160, screen.get_height()/2)),
        Bubble((480, screen.get_height()/2)),
        Bubble((600, screen.get_height()/2))]
    bubble_sprites = pygame.sprite.Group(bubbles)
    # 버블 스프라이트 그룹
 
    running = True
 
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False
 
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            if player.direction > 0:
                player.flip_image()
                player.direction = -1
            player.rect.move_ip(-player.speed, 0)
        if keys[pygame.K_RIGHT]:
            if player.direction < 0:
                player.flip_image()
                player.direction = 1
            player.rect.move_ip(player.speed, 0)
 
        collision = pygame.sprite.spritecollide(player, bubble_sprites, False)
        for bubble in collision:
            bubble.collided = True
        # 플레이어와 버블의 충돌을 감지하고 충돌한 버블의 collided 값을 True로 바꾼다.
 
        player_sprite.update()
        bubble_sprites.update()
 
        screen.fill("black")
        player_sprite.draw(screen)
        bubble_sprites.draw(screen)
    
        pygame.display.flip()
        clock.tick(60)
 
    pygame.quit()
 
if __name__ == '__main__':
   main()
 

 

 

공룡과 충돌한 버블은 하늘로 올라간다.

※ 참고

pygame.sprite.spritecollideany()
Simple test if a sprite intersects anything in a group.
● spritecollideany(sprite, group, collided = None) -> Sprite Collision with the returned sprite.
● spritecollideany(sprite, group, collided = None) -> None No collision
If the sprite collides with any single sprite in the group, a single sprite from the group is returned. On no collision None is returned.

If you don't need all the features of the pygame.sprite.spritecollide() function, this function will be a bit quicker.

The collided argument is a callback function used to calculate if two sprites are colliding. It should take two sprites as values and return a bool value indicating if they are colliding. If collided is not passed, then all sprites must have a "rect" value, which is a rectangle of the sprite area, which will be used to calculate the collision.

 

반응형
Posted by J-sean
: