Vector正在破坏我的程序

Vector is crashing my program

本文关键字:我的 程序 Vector      更新时间:2023-10-16

我的SFML游戏出现了严重问题。我一整天都在努力寻找解决方案,尝试了不同的方法,但到目前为止都没有成功。这些是我的.h文件:Bullet.h

#pragma once
#include <SFMLGraphics.hpp>
#include <iostream>
#include <vector>
class Bullet
{
friend class Player;
friend class Game;
float width;
float height;
float x;
float y;
std::vector<Bullet*> projectiles;
sf::RectangleShape bullet;
void draw_projectiles(sf::RenderWindow &window);
void make_projectiles();
public:
void check();
Bullet();
~Bullet();
};   

Game.h

#pragma once
#include <SFMLGraphics.hpp>
#include "Player.h"
#include "Bullet.h"
#include <vector>
//#include "Enemy.h"
class Game
{
friend class Player;
sf::RenderWindow* window;
sf::Event* evnt;
Player* player;
Bullet* bullet;
public:
void Loop();
void game_func();
Game();
~Game();
};

Player.h

#pragma once
#include <SFMLGraphics.hpp>
#include <iostream>
#include "Game.h"
#include "Bullet.h"
class Player
{
sf::RectangleShape player;
Bullet* bullet;
int ammo;
float width;
float height;
int x;
int y;
float vel;
public:
void draw(sf::RenderWindow &window);
void move(sf::Event &evnt, sf::RenderWindow &window);
Player();
~Player();
};   

这里是cpp文件Bullet.cpp

#include "Bullet.h"
void Bullet::check()
{
x = bullet.getPosition().x;
y = bullet.getPosition().y;
}
void Bullet::draw_projectiles(sf::RenderWindow &window)
{
for (int i = 0; i < 10; i++)
{
window.draw(projectiles[i]->bullet);
}
}
void Bullet::make_projectiles()
{
projectiles.push_back(new Bullet());
}
Bullet::Bullet()
{
std::cout << "zostal utworzony nowy obiekt" << std::endl;
width = 50;
height = 50;
bullet = sf::RectangleShape(sf::Vector2f(width, height));
bullet.setFillColor(sf::Color::Yellow);
bullet.setPosition(0, 0);
x = bullet.getPosition().x;
y = bullet.getPosition().y;
}
Bullet::~Bullet(){}

Game.cpp

#include "Game.h"
Game::Game()
{
window= new sf::RenderWindow(sf::VideoMode(1280, 720), "SFML Game",             
sf::Style::Close);
player = new Player();
}
Game::~Game(){}
void Game::Loop()
{
while (window->isOpen())
{
sf::Event evnt;
while (window->pollEvent(evnt))
{   
//events
if (evnt.type==sf::Event::Closed)
window->close();
player->move(evnt, *window);
window->clear();
player->draw(*window);
window->display();
bullet->draw_projectiles(*window);
}
}
}
void Game::game_func()
{
Game::Loop();
}

Player.cpp

#include "Player.h"
void Player::draw(sf::RenderWindow &window)
{
window.draw(player);
}
void Player::move(sf::Event &evnt, sf::RenderWindow &window)
{    
x = player.getPosition().x;
y = player.getPosition().y;
float width = window.getSize().x;
float height = window.getSize().y;
Bullet obj;
if (evnt.type == sf::Event::KeyPressed)
{
//movement
if (evnt.key.code == sf::Keyboard::Key::W)
{
if (y <= 0)
{
return;
}
player.move(0, -1 * vel);
}
if (evnt.key.code == sf::Keyboard::Key::S)
{
if (y >= height - Player::height)
{
return;
}
player.move(0, 1 * vel);
}
if (evnt.key.code == sf::Keyboard::Key::A)
{
if (x <= 0)
{
return;
}
player.move(-1 * vel, 0);
}
if (evnt.key.code == sf::Keyboard::D)
{
if(x>width-Player::width)
{
return;
}
player.move(1 * vel, 0);
}
if (evnt.key.code == sf::Keyboard::Space)
{
obj.make_projectiles();
}
}
}
Player::Player()
{
width = 100;
height = 100;
vel = 10;
player =  sf::RectangleShape(sf::Vector2f(width, height));
player.setFillColor(sf::Color::Red);
player.setPosition(sf::Vector2f(15, 20));
}
Player::~Player(){}

和main.cpp

#include <SFML/Graphics.hpp>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
#include "Game.h"
int main()
{   
Game gme;
gme.game_func();
return 0;
}

我尝试了很多不同的东西,但不明白为什么它不起作用。我正在试用Visual Studio 15。所以这里是我得到的错误:

Exception thrown: read access violation.
std::_Vector_alloc<std::_Vec_base_types<Bullet *,std::allocator<Bullet *> > 
>::_Mylast(...) returned 0x18.

我意识到代码并不完美,也有点混乱,但我只是一个开始努力学习新东西的工程师。我将感谢任何帮助!

我在最后几段中回答了你的问题,你可以跳到那一段,但我建议你看看所有这些。首先,你应该了解一个基本游戏在代码中应该是什么样子。

游戏逻辑

您可以在两个主要功能中分离游戏逻辑。初始化和循环。

初始化

在初始化功能中,你基本上加载了游戏运行所需的一切(这只适用于小型游戏,因为在内存中加载几十个精灵可能不是大型游戏的最佳解决方案。随着时间的推移,你会找到加载和释放资源的正确时间)。

循环

这被称为主循环游戏循环。这个循环应该执行3个主要函数处理用户输入、更新世界和渲染世界。这个循环应该在游戏运行时(即窗口打开时)执行

所以你在pseudo-c++中的main应该是这样的:

Init();
while (window.isOpen())
{
HandleEvents(window);  //user input
Update(elapsedTime);
Render(window);
}

我将解释这些函数的作用、参数的含义以及这些函数如何映射到代码中。请记住,每个功能都有一个特定的任务,而且只有这个任务。当我在屏幕上绘制精灵时,我不会检查用户是否按下了按钮。

用户输入

从按下按钮和鼠标点击到按下退出按钮和调整窗口大小的一切都被称为用户输入。用户的操作生成所谓的事件,我们在每个循环开始时处理这些事件。现在,这些事件是特定于窗口的(如果窗口最小化或未聚焦,则无法控制玩家)。这意味着窗口会生成事件(如果我在技术上对这个错误,请纠正我)。这就是在处理事件时需要通过窗口的原因。

事件

在处理事件之前,您需要了解sf::Event是如何生成的(请参阅sfml页面上的更多信息)。长话短说sf::Event是一个并集(一次只有一个字段有效)。也就是说,如果你试图在window.pollEvent()返回sf::Event::JoystickEvent时访问event.key,你会得到一个未定义的行为(我快乐地生活了很长一段时间,不知道工会是什么,从未使用过工会,也可能永远不会,但它们是一个非常有趣的概念,至少值得一读)。好的,所以通过调用window.pollEvent()并向其传递一个sf::event实例来创建一个事件对象。此函数将为您提供队列中的事件,直到不再提供事件为止,也就是它返回false的时候。考虑到这一点,您的事件处理代码将类似于:

sf::Event ev;
while (window.pollEvent(ev))
{
switch (ev.type)
{
//code for each type needed by your application
}
}

请记住,关键事件不处理实时输入(sf::Keyboard::isKeyPressed处理实时输入)。这意味着,如果你想让你的角色在按下按钮时移动,通过事件处理会导致延迟,这可以用打字的工作方式来最好地解释(例如,当你按下"a"时,第一个字符会立即写入,其余的输入会在注册前延迟一秒钟)。这是一种解释方式,但可能不是最技术性的(我在这里寻求一点帮助:))。无论如何,这个问题可以通过使用sf::Keyboard的静态方法来解决,也可以通过在Player类中保留一个bool来回答事件KeyPressedKeyReleased(更新将基于该布尔来处理)。

世界更新

这是您的逻辑代码(尽管玩家的移动也可以在事件部分处理,因为它是基于它们的)。在这里,你可以更新你的实体(根据敌人的AI再移动一个区块),在地图上移动太阳等。请记住,这与绘图部分无关,在本节中,你只更改对象的状态。在你的游戏中,这意味着,在你通过用户触发的事件发射投射物后,每一帧你都会移动投射物。此代码通常需要某种帧计数方法。

一帧是循环的迭代,你可以说游戏在每一帧都会更新和绘制自己。框架是一个非常重要的概念,因为它们会产生一些问题。如果游戏在每一帧都会更新自己,这意味着抛射物在移动的每一帧,所以这意味着他的移动取决于你的电脑可以运行的FPS。这是一个问题,因为虽然你的游戏可能在你的电脑上以稳定的60 FPS的速度运行,但在我的电脑上,它可能以53或其他随机值运行。这意味着我电脑上的投射物会移动得更慢,我们不希望这样。

帧独立移动

这可以通过对帧进行计数来实现。一种方法是计算自上一帧以来经过的秒数,考虑到这一点,您可以获得实体在该特定帧中移动所需的空间量。例如,你想以100px/秒的速度移动你的投射物。如果你有2FPS,这意味着在2帧中它需要移动100px,所以每帧移动100 / 2px。因此公式为finalDistance / framerate。有更多的方法可以做到这一点,但在我看来,这是一开始最简单的理解。那么这是如何在SFML中实现的呢?您基本上保留一个时钟,在每次更新结束时重新启动。getElapsedTime和restart会这样做,但restart会返回elapsedTime,所以最好调用一次,因为逐个调用它们可能会导致不同的时间和取消同步。

sf::Clock clock;
while (window.isOpen())
{
HandleEvents(window);
Update(clock.restart());
Render(window);
}

您只需使用move(vector * clock.getElapsedTime().asSeconds())移动您的实体,因为sf::Vector已经为浮点重载了operator*(返回类型为asSeconds())。

渲染

渲染部分可能非常复杂,但sfml使其"简单而快速"。基本上它是这样工作的:你清除屏幕,绘制实体,显示屏幕。更技术性的答案是:窗口由2个缓冲区组成,一个可见,一个隐藏。可见的就是你在屏幕上看到的那个。当调用clear()时,基本上清除了隐藏的窗口,draw()也在隐藏窗口上绘制,最后display()交换缓冲区。这意味着,除非调用window.display(),否则不会看到任何结果;如果在绘图前不调用clear(),则会获得windowxp体验。因此,Render函数可能如下所示:

window.clear();
window.draw(player); //or player.draw(window) based on your implementation
//other draws
window.display();

您的问题

代码中发生的情况是,您试图访问不存在的东西。你一次添加一个投射物,但每帧你画10个。

解决方案

记下你的物品。由于您使用的是已经提供的向量,因此std::vector::size会准确地返回您所期望的内容,因此您的代码将变成以下内容:

for (int i = 0; i < yourProjectiles.size(); i++)
{
window.draw(yourProjectiles[i]->bullet);
}

或者,您可以使用迭代器(查找它们):

for (auto it = yourProjectiles.begin(); it != yourProjectiles.end(); ++it)
{
window.draw(it->bullet);
}

内存管理

您不会释放内存。你必须研究动态内存分配。基本原则是,对于每一个新的,都应该有一个删除。大部分时间应该在类的析构函数中处理释放部分。我想可能有人建议使用智能指针(std::shared_ptr)来管理你的内存,但我不建议你这样做,因为你还处于起步阶段。智能指针是一个你应该记住的概念,但当你刚开始的时候,最好面对手动内存管理的困难(直到你习惯为止)。

代码组织

一个类只能用于一个目的。当你创建一个名为Bullet的类时,预计这个Bullet将代表你游戏中的一个投射物,但当你的Bullet制造"投射物"并存储投射物时,它就会变成一个超自然实体。您的子弹头atm保存指向其他子弹头实例的指针,这些指针保存指向其他弹头实例的指针。这真是一团糟。除非你想创建某种图形或树,否则你没有任何理由存储同一类实例的指针。

朋友太多

如果每个类都是每个类的朋友,那么你创建私有字段的原因是什么?朋友是一个非常强大的概念,应该小心使用,只有在你没有其他选择的情况下。我避免使用这个关键词的唯一原因是它造成的混乱。它创建了与公共属性相同的效果。当所有东西都可以从任何地方进入时,所有东西都可能从任何地方被摧毁。当你创建了一小组处理属性的方法时,你就知道问题出在哪里了

结论

我可能建议多研究一下c++,然后调试你的游戏,或者从头开始重新创建它。虽然我知道尝试新事物的感觉,但你应该始终小心,不要射中自己的腿,当你遇到这样的错误时,不要害怕回到最基本的东西。你在管理内存方面有问题吗?阅读更多关于动态内存分配的信息,做一些使用它的应用程序示例。此外,我注意到你还处于使用类的初级阶段。我认为熟能生巧。看看其他人的代码,即使是像sfml这样的第三方库也可能会给你一些关于良好课堂实践的提示。好处是不需要查看这些库的源代码,只需使用它们的接口即可。如果你喜欢它,这意味着它写得很好,你可以借用这种风格的一部分并在你的类中实现。最后,我要说,如果你对任何事情有任何其他问题,我很高兴并渴望通过电子邮件帮助你。

我相信你正试图访问十个投射物:

for (int i = 0; i < 10; i++)
{
window.draw(projectiles[i]->bullet);
}

但一次只能添加一个:

projectiles.push_back(new Bullet());
相关文章: