首页 > 代码库 > 碰撞的简单例子

碰撞的简单例子

1. 注意m_offset的值大小,这个值会导致当Player卡在两个Object时跳跃失效的程度

2. 注意Player类中调用handleKeyPressed函数的位置,主要是为了取到Player移动的方向

3. Object类中lastTop等函数,主要是为了获取前一次的位置

4. 注意PlayState类中handleCollision函数,优先检测左右碰撞

技术分享
  1 #ifndef OBJECT_HPP
  2 #define OBJECT_HPP
  3 
  4 #include <SFML/Graphics/RectangleShape.hpp>
  5 #include <SFML/Graphics/RenderTarget.hpp>
  6 
  7 class Object : public sf::Drawable {
  8 public:
  9     Object()
 10     : m_velocityX(0.f)
 11     , m_velocityY(0.f)
 12     , m_gravity(0.8f)
 13     , m_lastTop(top())
 14     , m_lastBottom(bottom())
 15     , m_lastLeft(left())
 16     , m_lastRight(right())
 17     , m_offset(1.f)
 18     {
 19         m_object.setFillColor(sf::Color::Transparent);
 20         m_object.setOutlineColor(sf::Color::Red);
 21         m_object.setOutlineThickness(-2.f);
 22     }
 23 
 24     virtual ~Object() = default;
 25 
 26     float top() { return m_object.getPosition().y; }
 27     float bottom() { return m_object.getPosition().y + m_object.getSize().y; }
 28     float left() { return m_object.getPosition().x; }
 29     float right() { return m_object.getPosition().x + m_object.getSize().x; }
 30 
 31     float lastTop() { return m_lastTop; }
 32     float lastBottom() { return m_lastBottom; }
 33     float lastLeft() { return m_lastLeft; }
 34     float lastRight() { return m_lastRight; }
 35 
 36     sf::Vector2f getPosition() { return m_object.getPosition(); }
 37 
 38     void setPosition(float x, float y) { setPositionX(x); setPositionY(y); }
 39     void setPositionX(float x) { m_object.setPosition(x, m_object.getPosition().y); }
 40     void setPositionY(float y) { m_object.setPosition(m_object.getPosition().x, y); }
 41 
 42     sf::Vector2f getVelocity() { return{ getVelocityX(), getVelocityY() }; }
 43     float getVelocityX() { return m_velocityX; }
 44     float getVelocityY() { return m_velocityY; }
 45 
 46     void setVelocity(float velocityX, float velocityY) { setVelocityX(velocityX); setVelocityY(velocityY); }
 47     void setVelocityX(float velocityX) { m_velocityX = velocityX; }
 48     void setVelocityY(float velocityY) { m_velocityY = velocityY; }
 49 
 50     sf::Vector2f getSize() { return{ getWidth(), getHeight() }; }
 51     float getWidth() { return m_object.getSize().x; }
 52     float getHeight() { return m_object.getSize().y; }
 53 
 54     void setSize(float width, float height) { setWidth(width); setHeight(height); }
 55     void setWidth(float width) { m_object.setSize(sf::Vector2f(width, m_object.getSize().y)); }
 56     void setHeight(float height) { m_object.setSize(sf::Vector2f(m_object.getSize().x, height)); }
 57 
 58     sf::FloatRect getGlobalBounds() { return m_object.getGlobalBounds(); }
 59 
 60     virtual void Object::update() {
 61         if (m_velocityY > 0.f) {
 62             m_lastTop = top() - m_velocityY - m_offset;
 63             m_lastBottom = bottom() - m_velocityY - m_offset;
 64         }
 65         else {
 66             m_lastTop = top() - m_velocityY + m_offset;
 67             m_lastBottom = bottom() - m_velocityY + m_offset;
 68         }
 69 
 70         if (m_velocityX > 0.f) {
 71             m_lastLeft = left() - m_velocityX - m_offset;
 72             m_lastRight = right() - m_velocityX - m_offset;
 73         }
 74         else {
 75             m_lastLeft = left() - m_velocityX + m_offset;
 76             m_lastRight = right() - m_velocityX + m_offset;
 77         }
 78     }
 79 
 80 protected:
 81     sf::RectangleShape m_object;
 82 
 83     float m_velocityX;
 84     float m_velocityY;
 85     float m_gravity;
 86 
 87     float m_lastTop;
 88     float m_lastBottom;
 89     float m_lastLeft;
 90     float m_lastRight;
 91 
 92     float m_offset;
 93 
 94 private:
 95     void draw(sf::RenderTarget& rt, sf::RenderStates states) const override {
 96         rt.draw(m_object, states);
 97     }
 98 };
 99 
100 #endif
Object.hpp
技术分享
 1 #ifndef PLAYER_HPP
 2 #define PLAYER_HPP
 3 
 4 #include "Object.hpp"
 5 
 6 #include <SFML/Window/Keyboard.hpp>
 7 
 8 class Player : public Object {
 9 public:
10     Player::Player()
11     : m_leftForce(-4.f)
12     , m_rightForce(4.f)
13     , m_jumpForce(-12.f)
14     , m_groundLeft(0.f)
15     , m_groundRight(0.f)
16     , m_ground(false)
17     {
18 
19     }
20 
21     void Player::update() {
22         Object::update();
23 
24         m_object.move(m_velocityX, 0.f);
25         m_velocityX = 0.f;
26 
27         if (!m_ground) {
28             m_object.move(0.f, m_velocityY);
29             m_velocityY += m_gravity;
30         }
31 
32         handleKeyPressed();
33 
34         if (right() < m_groundLeft || left() > m_groundRight) {
35             m_ground = false;
36         }
37     }
38 
39     void Player::setGround(float left, float right) {
40         m_groundLeft = left;
41         m_groundRight = right;
42         m_ground = true;
43     }
44 
45     bool isGround() { return m_ground; }
46 
47 private:
48     float m_leftForce;
49     float m_rightForce;
50 
51     float m_jumpForce;
52 
53     float m_groundLeft;
54     float m_groundRight;
55 
56     bool m_ground;
57 
58     void Player::handleKeyPressed() {
59         if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) {
60             m_velocityX = m_leftForce;
61         }
62 
63         if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) {
64             m_velocityX = m_rightForce;
65         }
66 
67         if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space) && m_ground) {
68             m_velocityY = m_jumpForce;
69             m_ground = false;
70         }
71 
72         if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) {
73             //m_ground = false;
74             //m_velocityY = m_jumpForce;
75         }
76     }
77 };
78 
79 #endif
Player.hpp
技术分享
 1 #ifndef PLAY_STATE_HPP
 2 #define PLAY_STATE_HPP
 3 
 4 #include "State.hpp"
 5 #include "Player.hpp"
 6 
 7 #include <tmxlite/Map.hpp>
 8 
 9 #include <memory>
10 
11 class StateMachine;
12 
13 namespace sf {
14     class RenderWindow;
15 }
16 
17 class PlayState : public State {
18 public:
19     PlayState(StateMachine &machine, sf::RenderWindow &window, bool replace = true);
20 
21     void pause();
22     void resume();
23     void update();
24     void draw();
25 
26 private:
27     tmx::Map m_map;
28 
29     Player m_player;
30 
31     std::vector<Object> m_objects;
32 
33     void handleClosed();
34     void handleKeyPressed();
35 
36     void handleCollision();
37 };
38 
39 #endif
PlayState.hpp
技术分享
  1 #include "PlayState.hpp"
  2 #include "StateMachine.hpp"
  3 //#include "PauseState.hpp"
  4 
  5 #include <iostream>
  6 
  7 PlayState::PlayState(StateMachine &machine, sf::RenderWindow &window, bool replace)
  8 : State{ machine, window, replace }
  9 {
 10     m_map.load("assets/test.tmx");
 11 
 12     if (m_map.getOrientation() == tmx::Orientation::Orthogonal) {
 13         const auto& layers = m_map.getLayers();
 14         //sf::Vector2f tileCount(m_chunkSize.x / map.getTileSize().x, m_chunkSize.y / map.getTileSize().y);
 15 
 16         for (size_t i = 0; i < layers.size(); i++) {
 17             if (layers[i]->getVisible()) {
 18                 switch (layers[i]->getType()) {
 19                 case tmx::Layer::Type::Tile:
 20                     //m_tileLayers.push_back(std::make_shared<TileLayer>(m_map, m_objects, layers[i]));
 21                     break;
 22                 case tmx::Layer::Type::Object:
 23                 {
 24                     const auto& objects = dynamic_cast<tmx::ObjectGroup*>(layers[i].get())->getObjects();
 25 
 26                     for (const auto& object : objects) {
 27                         if (strcmp("Player", object.getType().c_str()) == 0) {
 28                             m_player.setPosition(object.getAABB().left, object.getAABB().top);
 29                             m_player.setSize(object.getAABB().width, object.getAABB().height);
 30                         }
 31                         else if (strcmp("Object", object.getType().c_str()) == 0) {
 32                         m_objects.emplace_back();
 33                         m_objects.back().setPosition(object.getAABB().left, object.getAABB().top);
 34                         m_objects.back().setSize(object.getAABB().width, object.getAABB().height);
 35                         }
 36                     }
 37                 }
 38                     break;
 39                 default:
 40                     break;
 41                 }
 42             }
 43         }
 44     }
 45 }
 46 
 47 void PlayState::pause() {
 48 
 49 }
 50 
 51 void PlayState::resume() {
 52 
 53 }
 54 
 55 void PlayState::update() {
 56     while (m_window.pollEvent(m_event)) {
 57         switch (m_event.type) {
 58         case sf::Event::Closed:
 59             handleClosed();
 60             break;
 61         case sf::Event::KeyPressed:
 62             handleKeyPressed();
 63             break;
 64         default:
 65             break;
 66         }
 67     }
 68 
 69     m_player.update();
 70 
 71     handleCollision();
 72 }
 73 
 74 void PlayState::draw() {
 75     m_window.clear();
 76 
 77     for (auto & object : m_objects) {
 78         m_window.draw(object);
 79     }
 80 
 81     m_window.draw(m_player);
 82 
 83     m_window.display();
 84 }
 85 
 86 void PlayState::handleClosed() {
 87     m_machine.quit();
 88 }
 89 
 90 void PlayState::handleKeyPressed() {
 91     switch (m_event.key.code) {
 92     case sf::Keyboard::Escape:
 93     {
 94         //m_next = StateMachine::build<MenuState>(m_machine, m_window, false);
 95         //m_machine.lastState();
 96     }
 97         break;
 98     default:
 99         break;
100     }
101 }
102 
103 void PlayState::handleCollision() {
104     for (auto &object : m_objects) {
105         if (m_player.getGlobalBounds().intersects(object.getGlobalBounds())) {
106             if (m_player.getVelocityX() > 0.f && m_player.lastRight() < object.left()) {
107                 // 从左往右碰撞
108                 m_player.setPositionX(object.left() - m_player.getWidth());
109                 m_player.setVelocityX(0.f);
110             }
111             else if (m_player.getVelocityX() < 0.f && m_player.lastLeft() > object.right()) {
112                 // 从右往左碰撞
113                 m_player.setPositionX(object.right());
114                 m_player.setVelocityX(0.f);
115             }
116 
117             if (!(m_player.isGround())) {
118                 if (m_player.getVelocityY() > 0.f && m_player.lastBottom() < object.top()) {
119                     // 上方下落
120                     m_player.setPositionY(object.top() - m_player.getHeight());
121                     m_player.setVelocityY(0.f);
122                     m_player.setGround(object.left(), object.right());
123                 }
124                 else if (m_player.getVelocityY() < 0.f && m_player.lastTop() > object.bottom()) {
125                     // 下方跳跃
126                     m_player.setPositionY(object.bottom());
127                     m_player.setVelocityY(0.f);
128                 }
129             }
130         }
131     }
132 }
PlayState.cpp

 

碰撞的简单例子