1

私は現在、C++ の基礎を学んだ後、SFML を使い始めました。配列、参照、およびそれ以前のすべてについて学びましたが、クラスを使用するという概念を理解するのに苦労しました。

SFML で単純なスプライト移動プログラムを作成しましたが、この情報をクラスに移動したいと思います (「プレーヤー」と呼ぶことにしましょう)。私はたくさんいじりましたが、それを機能させることができません。

プレーヤーの入力をチェックするクラスで関数を作成しようとしましたが、メインで作成したスプライトにアクセスできません。プレーヤーに関連するすべてのものを Player クラスに移動したいのですが、アドバイスが必要です。

これを行う正しい方法は何ですか?(戻ってクラスについて学ぶとは言わないでください。ここでクラスについて学びたいのです!)

main.cpp

#include <SFML/Graphics.hpp>
#include <string>
#include <iostream>

int main()
{
    //character position
    enum Direction{ Down, Left, Right, Up };
    sf::Vector2i source(1, Down);

    //window
    sf::RenderWindow window(sf::VideoMode(1200, 700), "Testing");
    window.setKeyRepeatEnabled(false);

    //player character
    sf::Texture pTexture;
    sf::Sprite pSprite;
    if(!pTexture.loadFromFile("image/playerSprite.png"))
        std::cout << "Texture Error" << std::endl;
    pSprite.setTexture(pTexture);
    pSprite.setScale(1.5f, 1.5f);

    //game loop
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear();

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) //move up
        {

            source.y = Up;
            pSprite.move(0, -0.2);

            //animation
            source.x++;
            if(source.x * 32 >= pTexture.getSize().x)
            {
                source.x = 0;
            }
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) //move down
        {
            source.y = Down;
            pSprite.move(0, 0.2);

            //animation
            source.x++;
            if(source.x * 32 >= pTexture.getSize().x)
            {
                source.x = 0;
            }
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) //move right
        {
            source.y = Right;
            pSprite.move(0.2, 0);

            //animation
            source.x++;
            if(source.x * 32 >= pTexture.getSize().x)
            {
                source.x = 0;
            }
        }
        else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) //move left
        {
            source.y = Left;
            pSprite.move(-0.2, 0);

            //animation
            source.x++;
            if(source.x * 32 >= pTexture.getSize().x)
            {
                source.x = 0;
            }
        }

        pSprite.setTextureRect(sf::IntRect(source.x * 32, source.y * 32, 32, 32));
        window.draw(pSprite);
        window.display();

    }

    return 0;
}
4

1 に答える 1

5

免責事項: そのような答えを期待するべきではありません。要点を理解するには、OOP についてもっと読む必要があります。これは SFML とは関係ありません。これは単なる基本的なリファクタリングです。

OOPで考える方法

まず最初に、機能をコーディングする前に、状況に本当に適した OOP 構造を設計する必要があります。各クラスを全体の一部として見てください。それがあなたのプログラムです。実際、クラスは、意味のある方法でクラス内のデータ (またはメソッド パラメーターを介して提供されるデータ) にのみ影響を与える便利なメソッドを備えた単なるデータの集合体です。

C++で動作させる方法を理解するには、C++ の基本(OOP の部分) を参照してください。概念は、他のプログラミング言語でも同様です。

提供されたコードの操作

あなたが要求したのは Player クラスでした。プログラムのメイン ロジックからプレーヤー コードを取り出すのは素晴らしい考えです。「自分のプレーヤー コードが機能するために必要なものは何か?」と自問する必要があります。

プレイヤークラス

基本的に、プレーヤーはスプライトと位置のみです。そのため、これらのデータをプライベート メンバーとして Player クラスにカプセル化します。これにより、他のコードがプレーヤーのデータをいじるのを防ぎます。プレーヤー データを使用するには、それぞれがプレーヤーのみに影響するクラスのメソッドを提供する必要があります。

テクスチャとスプライト

意図的にテクスチャをプレイヤーの外に置いています。テクスチャは重いオブジェクトです。そのため、Sprite オブジェクトはテクスチャへのポインタのみを保持します。スプライトは軽量で、簡単に変更およびコピーできます。テクスチャ オブジェクトやその他のアセットの管理は別のテーマですが、ここに私自身のリソース マネージャー コードを示します

オプション

私はあなたのコードをあまり変更しませんでしたが、移動を処理する方法を変更しPlayer::Directionて、パラメーターを持つ「移動」メソッドを 1 つだけにすることができます。

もう少し助けて、主題に関するガイドラインをさらに提供するために、「前方宣言」を使用し、方向列挙をクラス内に移動しました。あなたが望むものを達成するための最良の方法ではないかもしれませんが、私はあなたが迷子にならないようにあなた自身のコードを変更しただけです.

コード

とにかく、これが私の行き先です。

Player.h

#ifndef PLAYER_H_
#define PLAYER_H_

#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Sprite.hpp>

// Forward Declaration
namespace sf {
class Texture;
}

// provide your namespace to avoid collision/ambiguities
namespace test {
/*
 *
 */
class Player: public sf::Drawable {
public:

    enum Direction {
        Down, Left, Right, Up
    };

    Player(const sf::Texture& playerTexture);
    virtual ~Player();

    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;

    void moveUp();
    void moveDown();
    void moveLeft();
    void moveRight();

private:

    sf::Sprite mSprite;
    sf::Vector2i mSource;

};

} /* end namespace test */
#endif /* PLAYER_H_ */

Player.cpp

#include "Player.h"

// you need this because of forward declaration
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

namespace test {
Player::Player(const sf::Texture& imagePath) :
                mSprite(imagePath),
                mSource(1, Player::Down) {

    // do not need that line anymore, thanks to initialiser list
    //pSprite.setTexture(pTexture);

    mSprite.setScale(1.5f, 1.5f);

}

Player::~Player() {
    // TODO Auto-generated destructor stub
}

void Player::draw(sf::RenderTarget& target, sf::RenderStates states) const {
    target.draw(mSprite, states);
}

void Player::moveUp() {
    mSource.y = Up;
    mSprite.move(0, -0.2);

    //animation
    mSource.x++;
    if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
        mSource.x = 0;
    }

    mSprite.setTextureRect(sf::IntRect(mSource.x * 32, mSource.y * 32, 32, 32));
}

void Player::moveDown() {
    mSource.y = Down;
    mSprite.move(0, 0.2);

    //animation
    mSource.x++;
    if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
        mSource.x = 0;
    }
}

void Player::moveLeft() {
    mSource.y = Left;
    mSprite.move(-0.2, 0);

    //animation
    mSource.x++;
    if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
        mSource.x = 0;
    }
}

void Player::moveRight() {
    mSource.y = Right;
    mSprite.move(0.2, 0);

    //animation
    mSource.x++;
    if (mSource.x * 32 >= (int) mSprite.getTexture()->getSize().x) {
        mSource.x = 0;
    }
}

} /* end namespace test */

main.cpp

#include <SFML/Graphics.hpp>
//#include <string> // not used for now
#include <iostream>

// don't forget to include your own header
#include "Player.h"

int main() {

    // just to save typing the "std::"
    using std::cout;
    using std::endl;
    using std::cerr;

    //window
    sf::RenderWindow window(sf::VideoMode(1200, 700), "Testing");
    window.setKeyRepeatEnabled(false);

    //player texture
    sf::Texture pTexture;
    if (!pTexture.loadFromFile("image/playerSprite.png")) {
        cerr << "Texture Error" << endl;
    }

    test::Player thePlayer(pTexture);

    //game loop
    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }

        window.clear();

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) //move up
                {
            thePlayer.moveUp();
        } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) //move down
                {
            thePlayer.moveDown();
        } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) //move right
                {
            thePlayer.moveRight();
        } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) //move left
                {
            thePlayer.moveLeft();
        }

        window.draw(thePlayer);
        window.display();

    }

    return 0;
}

その他のグッドプラクティス

アクセサー、または Getters/Setterは、クラスのプライベート メンバーへのアクセスを提供するメンバー関数です。

コードでは、次のようなことができます。

 class Player {
    public: 
        Player(const sf::Texture& playerTexture);
        virtual ~Player();

       // to give access to a const reference of the sprite
       // One could call it like: sf::Sprite mySprite = myPlayerObject.getSprite();
       // notice also that the method itself is const, which assure you that
       // myPlayerObject won't change by calling getSprite()
       const sf::Sprite& getSprite() const{
           return mSprite;
       }

       // setSprite is not a const method, so it will change the data
       // inside myPlayerObject
       void setSprite(const sf::Sprite& newSprite){
           mSprite = newSprite;
       }

    private:
        sf::Sprite mSprite;
        sf::Vector2i mSource;
    };
于 2013-10-02T18:18:46.680 に答える