-4

SFML を使用して C++ でスネーク ゲームを作成しようとしています。以下は私のコードです。問題は、どうすればヘビに自分自身を噛ませることができるのかわからないことです。各スネーク ブロックは最後のブロックの位置を取り、完全にまっすぐなスネークになります。したがって、それはアルゴリズムの問​​題です。アドバイスをありがとう:)ところで、編集者がそれらを取り除くため、インクルードにハッシュタグと<を入れませんでした:/ BTW2:同様の応答を探しましたが、それらのどれも私のアプローチではなく、別の言語でもありませんでした. どうぞ:

#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Audio.hpp>
#include <deque>

void selfIncrement();

sf::Event event;
sf::Clock clockSnake;
sf::Time elapse;

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

int dir = Up;

int n = 1;

class SnakeBlock
{
public:

SnakeBlock * next;
sf::Texture texture;
sf::Sprite snakeblock;
int lastX, lastY;
};

std::deque<SnakeBlock> Snake;

int main()
{
elapse = clockSnake.getElapsedTime();

sf::Music epicMusic;
epicMusic.openFromFile("epicmusic.wav");
epicMusic.play();

SnakeBlock snakeHead;
snakeHead.texture.loadFromFile("spritesheetsnake.png", sf::IntRect(0,0,20,22));
snakeHead.snakeblock.setTexture(snakeHead.texture);
SnakeBlock snakeBody1;
snakeBody1.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));
SnakeBlock snakeBody2;
snakeBody2.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));

Snake.push_front(snakeHead);
Snake.push_front(snakeBody1);
Snake.push_front(snakeBody2);
Snake[2].snakeblock.setPosition(500,350);
Snake[1].snakeblock.setPosition(475, 338);
Snake[0].snakeblock.setPosition(450, 316);

sf::RenderWindow window(sf::VideoMode(1028,768), "SFML Snake");
window.setFramerateLimit(60);
while(window.isOpen())
{
    while(window.pollEvent(event))
    {
        switch(event.type)
        {
            case sf::Event::Closed:
            epicMusic.stop();
            window.close();
            break;

            default:
            break;
        }

    }

    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
            dir = Left;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
            dir = Right;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
            dir = Down;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
            dir = Up;
    }
    if(dir == Up)
    {

        Snake[0].snakeblock.move(0,-2);
        Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x, Snake[n-1].snakeblock.getPosition().y+20);
    }
    if(dir == Down)
    {
        Snake[0].snakeblock.move(0,2);
        Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x, Snake[n-1].snakeblock.getPosition().y-20);
    }
    if(dir == Left)
    {
        Snake[0].snakeblock.move(-2,0);
        Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x+20, Snake[n-1].snakeblock.getPosition().y);
    }
    if(dir == Right)
    {
        Snake[0].snakeblock.move(2,0);
        Snake[n].snakeblock.setPosition(Snake[n-1].snakeblock.getPosition().x-20, Snake[n-1].snakeblock.getPosition().y);
    }
    window.clear(sf::Color::Red);
    n++;
    if( n > 2)
    {
        n = 1;
    }
    //selfIncrement();
    for(unsigned int m = 0; m < Snake.size(); m++)
    {
        window.draw(Snake[m].snakeblock);
    }

    window.display();
}
return 0;

}

/*void selfIncrement()
{
elapse = clockSnake.getElapsedTime();
if(elapse.asSeconds() > 0.10)
{
    n++;
    clockSnake.restart();
}
if(n > Snake.size())
{
    n = 1;
}
}
*/

新しい原則を使用した新しいコード::

#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Audio.hpp>
#include <deque>

void advanceStep();

sf::Event event;
sf::Clock clockSnake;
sf::Time elapse;

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

sf::Vector2i direction;
int dir = Up;

int n = 1;

class SnakeBlock
{
public:

sf::Texture texture;
sf::Sprite snakeblock;
int dir = 0;
int lastX, lastY;
};

SnakeBlock element;
std::deque<SnakeBlock> Snake;

int main()
{
elapse = clockSnake.getElapsedTime();

sf::Music epicMusic;
epicMusic.openFromFile("epicmusic.wav");
epicMusic.play();

SnakeBlock snakeHead;
snakeHead.texture.loadFromFile("spritesheetsnake.png", sf::IntRect(0,0,20,22));
snakeHead.snakeblock.setTexture(snakeHead.texture);
SnakeBlock snakeBody1;
snakeBody1.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));
SnakeBlock snakeBody2;
snakeBody2.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));

Snake.push_front(snakeHead);
Snake.push_front(snakeBody1);
Snake.push_front(snakeBody2);
Snake[2].snakeblock.setPosition(500,350);
Snake[1].snakeblock.setPosition(475, 338);
Snake[0].snakeblock.setPosition(450, 316);

sf::RenderWindow window(sf::VideoMode(1028,768), "SFML Snake");
window.setFramerateLimit(10);
while(window.isOpen())
{
    while(window.pollEvent(event))
    {
        switch(event.type)
        {
            case sf::Event::Closed:
            epicMusic.stop();
            window.close();
            break;

            default:
            break;
        }

    }

    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
            dir = Left;
            direction.x = -1;
            direction.y = 0;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
            dir = Right;
            direction.x = 1;
            direction.y = 0;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
            dir = Down;
            direction.x = 0;
            direction.y = -1;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
            dir = Up;
            direction.x = 0;
            direction.y = 1;
    }
    if(dir == Up)
    {

        Snake[0].snakeblock.move(0,-2);
        Snake[1].snakeblock.setPosition(Snake[0].snakeblock.getPosition().x, Snake[0].snakeblock.getPosition().y+20);
        Snake[2].snakeblock.setPosition(Snake[1].snakeblock.getPosition().x, Snake[1].snakeblock.getPosition().y+20);
    }
    if(dir == Down)
    {
        Snake[0].snakeblock.move(0,2);
        Snake[1].snakeblock.setPosition(Snake[0].snakeblock.getPosition().x, Snake[0].snakeblock.getPosition().y-20);
        Snake[2].snakeblock.setPosition(Snake[1].snakeblock.getPosition().x, Snake[1].snakeblock.getPosition().y-20);
    }
    if(dir == Left)
    {
        Snake[0].snakeblock.move(-2,0);
        Snake[1].snakeblock.setPosition(Snake[0].snakeblock.getPosition().x+20, Snake[0].snakeblock.getPosition().y);
        Snake[2].snakeblock.setPosition(Snake[1].snakeblock.getPosition().x+20, Snake[1].snakeblock.getPosition().y);
    }
    if(dir == Right)
    {
        Snake[0].snakeblock.move(2,0);
        Snake[1].snakeblock.setPosition(Snake[0].snakeblock.getPosition().x-20, Snake[0].snakeblock.getPosition().y);
        Snake[2].snakeblock.setPosition(Snake[1].snakeblock.getPosition().x-20, Snake[1].snakeblock.getPosition().y);
    }
    window.clear(sf::Color::Red);
    advanceStep();
    for(unsigned int m = 0; m < Snake.size(); m++)
    {
        window.draw(Snake[m].snakeblock);
    }
    window.display();
}
return 0;

}

void advanceStep()
{
sf::Vector2f headpos(Snake[0].snakeblock.getPosition());

headpos.x += 22 * direction.x;
headpos.y += 22 * direction.y;

Snake[0].snakeblock.setPosition(headpos);

Snake.pop_back();
Snake.push_front(Snake[0]);
}

あなたが言ったことすべてを考慮に入れた新しいコードですが、おそらく pop_back..

#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Audio.hpp>
#include <deque>

void advanceStep();

sf::Event event;
sf::Clock clockSnake;
sf::Time elapse;

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

sf::Vector2i direction(0, 0);
int dir = Up;


class SnakeBlock
{
public:

sf::Texture texture;
sf::Sprite snakeblock;
int dir;
};

std::deque<SnakeBlock> Snake;

int main()
{
elapse = clockSnake.getElapsedTime();

sf::Music epicMusic;
epicMusic.openFromFile("epicmusic.wav");
epicMusic.play();

SnakeBlock snakeHead;
snakeHead.texture.loadFromFile("spritesheetsnake.png", sf::IntRect(0,0,20,22));
snakeHead.snakeblock.setTexture(snakeHead.texture);
SnakeBlock snakeBody1;
snakeBody1.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));
SnakeBlock snakeBody2;
snakeBody2.snakeblock.setTexture(*(snakeHead.snakeblock.getTexture()));

Snake.push_back(snakeHead);
Snake.push_back(snakeBody1);
Snake.push_back(snakeBody2);

Snake[2].snakeblock.setPosition(500,350);
Snake[1].snakeblock.setPosition(475, 338);
Snake[0].snakeblock.setPosition(450, 316);

sf::RenderWindow window(sf::VideoMode(1028,768), "SFML Snake");
window.setFramerateLimit(1);
while(window.isOpen())
{
    while(window.pollEvent(event))
    {
        switch(event.type)
        {
            case sf::Event::Closed:
            epicMusic.stop();
            window.close();
            break;

            default:
            break;
        }

    }

    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
            direction.x = -1;
            for(int i = 1; i < Snake.size(); i++)
            {
                Snake[i].snakeblock.setPosition(Snake[i+1].snakeblock.getPosition().x-20, Snake[i+1].snakeblock.getPosition().y);
            }
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
            direction.x = 1;
            for(int j = 1; j < Snake.size(); j++)
            {
                Snake[j].snakeblock.setPosition(Snake[j+1].snakeblock.getPosition().x+20, Snake[j+1].snakeblock.getPosition().y);
            }
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
            direction.y = -1;
            for(int l = 1; l < Snake.size(); l++)
            {
                Snake[l].snakeblock.setPosition(Snake[l+1].snakeblock.getPosition().x, Snake[l+1].snakeblock.getPosition().y-22);
            }
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
            direction.y = 1;
            for(int o = 1; o < Snake.size(); o++)
            {
                Snake[o].snakeblock.setPosition(Snake[o+1].snakeblock.getPosition().x, Snake[o+1].snakeblock.getPosition().y+22);
            }
    }
    window.clear(sf::Color::Red);
    advanceStep();
    for(unsigned int m = 1; m < Snake.size(); m++)
    {
        window.draw(Snake[m].snakeblock);
    }
    window.display();
}
return 0;

}

void advanceStep()
{
sf::Vector2f headpos;
headpos.x = Snake[0].snakeblock.getPosition().x;
headpos.y = Snake[0].snakeblock.getPosition().y;

headpos.x += 22 * direction.x;
headpos.y += 22 * direction.y;

SnakeBlock element;
element.snakeblock.setPosition(headpos);

Snake.pop_back();
Snake.push_front(element);
}
4

4 に答える 4

3

あなたの実装には多くの問題があります。すべての問題を指摘するのではなく、大まかな概要を示します。あなたのヘビが最大長のセグメントのリストであるとしましょう:

  • 各更新ステップで:
    1. 次の位置 = 頭の位置 + 現在の方向。
    2. 次の位置で、キューの先頭にセグメントを追加します。
    3. キューの長さが最大長を超えている間、キューの末尾のセグメントを削除します。
  • キーボード入力では、現在の方向を変更するだけです。
  • スネークの長さを変更する必要がある場合は、最大長を変更するだけです。更新ステップでスネークの長さが調整されます。

衝突とは、意味的には、ヘビの頭が体の他の部分に衝突することです。

衝突を検出するには、各更新ステップでこれを実装します。体の残りの部分に対して頭を確認します。交差点があれば、衝突が発生しました。

ところで、スネークの最大長が減少したときに、よりスムーズな「縮小」効果が必要な場合は、たとえば、更新ごとに最大 2 つのセグメントを削除することができます (キューの長さ > 最大長で、セグメントが削除されている間 < 縮小率)。

編集:整数(または列挙型、現在使用しているのと同じ概念)に基づいて方向を変更する方法を疑問に思っている以下のコメントに対処します。ここにいくつかの擬似コードがあります:

Vec2 direction(0, 0);

if (currentDirection == Up)
    direction.y = -1;
else if (currentDirection == Down)
    direction.y = 1;
else if (currentDirection == Left)
    direction.x = -1;
else if (currentDirection == Right)
    direction.x = 1;

// direction is now a vector containing an x and y offset, which you can
// apply to the current position of the snake head to find the next
// position.
于 2013-08-15T23:59:01.320 に答える
2

それは、物事を過度に複雑にすることで得られるものです。

を使用std::dequeするため、「次の」フィールドは必要ありません。または「移動」メソッド。また、ブロックは個別のテクスチャを必要としません (つまり、すべてのテクスチャが一意であることを意味しますが、これは正しくありません)。

ゲーム全体の大まかな概要 (コンストラクターは省略されています):

struct Vector2{
   int x, y;
};

typedef std::deque<Vector2> Vec2Deque;

struct Snake{
    Vec2 direction; //(+-1, +-1)
    int blockSize;
    Vec2Deque blocks;//for blocks you only need their coordinates.

    void render(){
        for(size_T i = 0; i < blocks.size(); i++)
             renderSpriteAt(blocks[i]);
    }

    void advanceStep(){
         Vector2 headPos = blocks[0];//take current head pos             
         headPos.x += blockSize * direction.x; //calculate next position based on direction
         headPos.y += blockSize * direction.y; //and block size. Direction is 2component vector.
         blocks.pop_back();//remove last element
         blocks.push_front(headPos);//insert new element with new position as a head. 
              //That'll automatically create illusion of movement.
    }
};

これにより、従来のグリッド ベースのスネークがシミュレートされます。

より優れたスネーク (スムーズに動き、小さな角度 (左に 1 度など) で曲がることができる) が必要な場合、事態はさらに複雑になります。

シンプルに考えてください。KISS の原理も参照してください。


あなたが言ったことすべてを考慮に入れた新しいコードですが、おそらくpop_backが原因で、ヘビは消えます

これを置き換えます:

if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
            direction.x = -1;
            for(int i = 1; i < Snake.size(); i++)
            {
                Snake[i].snakeblock.setPosition(Snake[i+1].snakeblock.getPosition().x-20, Snake[i+1].snakeblock.getPosition().y);
            }
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
            direction.x = 1;
            for(int j = 1; j < Snake.size(); j++)
            {
                Snake[j].snakeblock.setPosition(Snake[j+1].snakeblock.getPosition().x+20, Snake[j+1].snakeblock.getPosition().y);
            }
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
            direction.y = -1;
            for(int l = 1; l < Snake.size(); l++)
            {
                Snake[l].snakeblock.setPosition(Snake[l+1].snakeblock.getPosition().x, Snake[l+1].snakeblock.getPosition().y-22);
            }
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
            direction.y = 1;
            for(int o = 1; o < Snake.size(); o++)
            {
                Snake[o].snakeblock.setPosition(Snake[o+1].snakeblock.getPosition().x, Snake[o+1].snakeblock.getPosition().y+22);
            }
    }

これとともに:

if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
    {
            direction.x = -1;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
    {
            direction.x = 1;
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
    {
            direction.y = -1;
            for(int l = 1; l < Snake.size(); l++)
    }
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
    {
            direction.y = 1;
    }

AdvanceStep 以外の場所でスネークを更新しないでください。両方ではなく、プッシュ/ポップまたは要素をシフトするループのいずれかを使用する必要があります。すべてのループで「i」を使用でき、毎回異なる変数名を選択する必要がないことをご存知ですか?

それでもスネークに問題がある場合は、代わりにスクロール シューターを作成してみてください。例えば:

  1. アルカノイド。
  2. スクロールシューター。

これらのゲームにパーティクル システムを追加したい場合を除き、これらのゲームでは 1 つのオブジェクトにスプライトが 1 つしかないため、ヘビよりも簡単なはずです。

于 2013-08-16T00:08:44.583 に答える
1

私が理解しているように、現在、あなたのヘビは直線のままであり、他のゲームのように回転できるようにしたいと考えています。

私がゲームで行ったことは、ヘビのパーツ (位置と方向) とターン (方向と位置) の 2 つのデータ構造を持っていることです。

ゲーム ループでは、スネーク パーツはその方向に移動しますが、その位置がターンの方向と一致する場合、スネーク パーツはその方向をターンの方向に変更し、それに応じてその位置を更新します。

スネークの尻尾部分にかけるとターンが取れます。キーの押下を検出すると、その方向をキャプチャし、位置が蛇の頭の位置になるように追加します。

私の実装はこちらです。私が言っていることを確認できます。

于 2013-08-16T05:18:18.250 に答える