【问题标题】:How to make the snake bite?如何让蛇咬人?
【发布时间】:2013-08-15 23:43:02
【问题描述】:

我正在尝试使用 SFML 用 C++ 制作蛇游戏。 下面是我的代码。问题是我不知道我怎么可能让蛇咬自己:每个蛇块占据最后一个的位置,从而形成一条完全笔直的蛇。所以这更像是一个算法问题。 谢谢你的建议:) 顺便说一句,没有把标签和

#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);
}

【问题讨论】:

  • 每个蛇块都知道自己的位置,对吧?一个简单的解决方案是,每次更新,检查每个非头块是否与头块重叠。
  • 请完整阅读我的帖子:“这条蛇完全是直的,因此它不能咬自己!”。
  • +1 获得最棒的问题标题
  • @pm100 我对此的投票实际上是“接口允许单位在 Java 中使用设计模式攻击地面、空中或两者兼而有之的能力”(stackoverflow.com/questions/18200459/…)
  • @user2687718 你拥有解决这个问题所需的一切,人们利用他们的空闲时间来帮助你完成你应该能够自己完成的家庭作业。不需要态度(请参阅下面我的回答中的 cmets)。

标签: c++


【解决方案1】:

你的实现有很多问题。这里没有指出每一个问题,而是一个粗略的大纲。假设您的蛇是一个分段列表,具有最大长度:

  • 在每个更新步骤中:
    1. 下一个位置 = 头部位置 + 当前方向。
    2. 在队列头部添加段,在下一个位置。
    3. 当队列长度大于最大长度时,删除队列尾部的一段。
  • 在键盘输入时,只需更改当前方向。
  • 如果您需要更改蛇的长度,只需更改最大长度即可 - 然后更新步骤将调整蛇的长度。

从语义上讲,碰撞是指蛇头撞到身体的任何其他部位。

要检测冲突,请在每个更新步骤中执行此操作。检查头部与身体的其余部分。如果有交叉路口,就会发生碰撞。

顺便说一句,如果您想要在最大蛇长减少时获得更平滑的“收缩”效果,您可以使用例如每次更新最多删除两个段(队列长度>最大长度,删除段

编辑:在下面解决您的评论,想知道如何根据整数(或枚举,我看到您现在使用的 - 相同的概念)改变方向;这里还有一些伪代码:

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.

【讨论】:

  • 首先,你没有看过去的cmets,也没有完整阅读我的帖子,否则你会明白这不是碰撞问题。其次,当前方向是一个整数,可以等于 0、1、2 和 3,我真的不明白如何使用它来使运动看起来可能会咬到自己,或者你需要开发更多。
  • 我上面概述的算法确切地向您展示了如何使蛇具有改变方向的能力。要根据整数值在给定方向上移动蛇,您可以使用 switchif,或者选择不同的方向表示。由你决定。您可能希望查看有关控制流语句的教程 - 虽然您的代码表明您了解如何使用 if,但您的评论表明您无法理解如何根据整数常量修改方向,所以我很困惑.您在这里拥有解决此问题所需的一切。
  • 是因为你没有明确我必须用方向整数值的条件来改变方向。希望你以后对别人说的更准确一些,不要怪他们不理解。但我仍然感谢您的帮助。
  • 你的帖子里的代码真的写了吗?该代码大量使用了switchif 块。我相信我假设您知道如何使用它们是合理的。如果你从其他地方复制了这段代码,你会在这个项目中遇到很多麻烦。
【解决方案2】:

基本原理是你需要检测头部是否到达自身的任何块 - 如果是,它就是“咬自己”。

因此,当您刚刚移动蛇(包括“生长”它,如果适用)时,请检查头部是否与构成蛇的任何其他“块”的位置相匹配。

编辑:要使蛇“摆动”,您需要使用“记住”头部移动的方向,并且每次移动都应该将该块的方向“洗牌”到它后面的那个方向。

换句话说,将dir 条目添加到SnakeBlock,并使用它来移动每个块。

【讨论】:

  • 不,你不明白...我希望蛇可以有一个运动,这样它就可以咬自己。我这里不谈碰撞!就目前而言,它只是一条完全笔直的蛇,不会咬自己。你明白我吗?
  • 感谢您现在相关的回答,但我不明白 shuffle 这个词,您能用其他词解释那部分吗?
【解决方案3】:

这就是过度复杂化的结果。

因为您使用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 之外的任何地方更新snake。 您应该使用 push/pop 或移动元素的循环,而不是两者。 你不知道,你可以在所有循环中使用“i”,并且不需要每次都选择不同的变量名,对吧?

如果您对蛇仍有疑问,请尝试写一个scroll shooter。例如:

  1. Arcanoid。
  2. 卷轴射击游戏。

除非您想在这些游戏中添加粒子系统,否则它们应该比蛇更容易,因为在这些游戏中,一个对象只有一个精灵。

【讨论】:

  • 谢谢,现在我明白你的意思了,所以我应该制作 4 个称为 Up/Down/Left/right 的向量并在我的 setPos 中使用它们!
  • @user2687718:不,我想你只是忘了移动蛇块。在每一步中删除最后一个,并添加新块作为头部。如果你一直试图只移动第一个和最后一个块(这就是你所做的),什么都不会发生。你的代码中没有一个“pop”(pop_front/pop_back),你知道......
  • 它是因为我正在尝试测试它是否是一条由 2/3 段组成的蛇,稍后我会做 pop/more push_front
  • 另外,我真的不能做你刚刚写的,因为在 SFML 中,一切都在一个大循环 window.isOpen() 中,所以我真的不知道如何使用你的想法..
  • @user2687718:我没有看到问题,也没有任何区别。也许你应该先尝试一些更简单的东西?或切换到游戏制作者?或阅读更多教程?首先学习如何处理具有非小数段的传统蛇,然后你就可以开始复杂化了。
【解决方案4】:

据我了解,目前你的蛇仍然是一条直线,你希望它能够像其他游戏一样转动。

我在游戏中做了什么,我有两种数据结构,一种用于蛇部分(位置和方向),另一种用于转弯(方向和位置)。

在游戏循环中,Snake 部分沿其方向移动,但如果其位置与转弯方向匹配,则蛇部分将其方向更改为转弯方向并相应地更新其位置。

当你将它应用到蛇的尾部时会移除转弯。并在检测到按键时添加,捕捉它的方向和位置是蛇的头部位置。

我的实现是here,你可以看看我在说什么。

【讨论】:

  • 感谢您尝试使用您的实现检查此 pastebin pastebin.com/SDJKE4FD 我没有看到问题
  • 您需要两个队列,一个用于蛇,另一个用于所有转弯。在您的代码中,我只看到一个蛇队列和一个全局方向(这将导致您的蛇沿直线移动)。当您按下键时,您需要将方向和位置存储在另一个队列中,并且在移动蛇之前将每个蛇部分的位置与该转弯的位置进行比较,并相应地更改蛇部分的方向。
猜你喜欢
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 1970-01-01
  • 2011-07-25
  • 2022-11-19
  • 2015-06-30
  • 2019-05-17
相关资源
最近更新 更多