如何让蛇咬人

How to make the snake bite?

本文关键字:      更新时间:2023-10-16

我正试图用SFML在C++中制作一个蛇游戏。下面是我的代码。问题是,我不知道我怎么可能让蛇咬自己:每个蛇块都占据最后一个的位置,从而形成一条完全笔直的蛇。所以这更像是一个算法问题。感谢您的建议:)BTW没有放入hashtages和<在include中,因为编辑器将其删除:/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);
}

您的实现有很多问题。与其指出每一个问题,不如简单介绍一下。假设你的蛇是一个分段列表,具有最大长度:

  • 在每个更新步骤中:
    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.

基本原理是,你需要检测头部是否到达了自己的任何一块——如果是,它就"自己被咬了"。

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

编辑:要让蛇"摆动",你需要使用一个"记住"头部移动的方向,每次移动都应该将该块的方向"洗牌"到它后面的块。

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

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

因为您使用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",并且不需要每次都选择不同的变量名,对吧?

如果你仍然对蛇有问题,试着写一个卷轴射击游戏。例如:

  1. 偏执狂
  2. 滚动射击

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

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

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

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

当你把它应用到蛇的尾巴部分时,转弯就被去除了。并添加了当你检测到按键按下时,捕捉到它的方向和位置就是蛇头的位置。

我的实现就在这里,你可以看看我在说什么。

相关文章:
  • 没有找到相关文章