我的SFML游戏存在严重问题。 我一直在努力找到一个解决方案,尝试了不同的东西,但到目前为止我没有任何作用。 这些是我的.h文件: 的 Bullet.h
#pragma once
#include <SFML\Graphics.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 <SFML\Graphics.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 <SFML\Graphics.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.
我知道代码并不完美而且有点乱,但我只是一个尝试学习新东西的人。 我将不胜感激任何帮助!
答案 0 :(得分:2)
我在最后几段回答了你的问题,你可以跳到那一段,但是我很高兴你看看这一切。首先,您应该了解基本游戏在代码中应该是什么样子。
游戏逻辑
您可以将游戏逻辑分为两个主要功能。初始化和循环。
<强>初始化强>
在初始化功能中,你基本上可以加载游戏运行所需的一切(仅适用于小型游戏,因为在内存中加载数十个精灵可能不是大型精灵的最佳解决方案。随着时间的推移你# 39; ll找出加载和释放资源的正确时间)。
循环
这称为主循环或游戏循环。该循环应该执行3个主要功能。 处理用户输入,更新世界和渲染世界。该循环应该在游戏运行时执行(即在窗口打开时)
所以你的伪c ++主要应该是这样的:
Init();
while (window.isOpen())
{
HandleEvents(window); //user input
Update(elapsedTime);
Render(window);
}
我将解释这些函数的作用,参数的含义以及这些函数如何映射到您的代码。请记住,每个功能都有一个特定的任务,只有那个。当我在屏幕上绘制精灵时,我不会检查用户是否按了按钮。
用户输入
从按下按钮和鼠标单击到按退出按钮并调整窗口大小的所有内容都称为用户输入。用户的操作会生成所谓的事件,我们会在每个循环开始时处理它们。现在这些事件是特定于窗口的(如果窗口最小化或未聚焦,则无法控制播放器)。这意味着窗口会生成事件(如果我在技术上遇到错误,请纠正我)。这就是当您处理事件时需要通过窗口的原因。
<强>事件强>
在处理事件之前,您需要了解如何制作sf :: Event(请参阅sfml
页面上的更多信息)。长话短说sf :: Event是一个联合(一次只有一个字段有效)。也就是说,如果您在event.key
返回window.pollEvent()
时尝试访问sf::Event::JoystickEvent
,您将获得未定义的行为(我过着幸福的生活而不知道哪些联盟是什么,从未使用过它们可能永远不会,但它们是一个非常有趣的概念,值得至少阅读)。好的,所以通过调用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
会这样做)。这意味着如果您希望角色在按住按钮时移动,则按事件处理它将导致延迟,这可以通过打字工作的方式得到最好的解释(当您按住&#39; a&#39; for例如,第一个字符立即写入,其余输入在注册前延迟一秒钟)。这是一种解释它的方式,但也许不是最技术性的(我在这里要求一些帮助:))。无论如何,这个问题可以通过使用sf :: Keyboard的静态方法或者在Player类中保留bool
来解决事件KeyPressed
和KeyReleased
(更新)来解决将根据那个布尔来处理。
世界更新
这是您的逻辑代码(虽然玩家移动也可以在事件部分处理,因为它基于它们)。在这里你更新你的实体(根据他的AI移动敌人一个块),在地图上移动太阳等等。请记住,这与绘图部分无关,在本节中你只改变你的状态。对象。在你的游戏中,这意味着,在你通过用户触发的事件发射投射之后,你移动投射物的每一帧。这段代码通常需要某种帧计数方法。
<强>帧强>
一个帧是循环的迭代,你可以说游戏更新并在每个帧中绘制自己。框架是一个非常重要的概念,因为它们会产生一些问题。如果游戏每帧更新一次,这意味着射弹正在移动的每一帧,这意味着他的移动依赖于你的电脑可以运行的FPS。这是一个问题,因为虽然你的游戏可以在你的电脑上以你想要的速度运行,但是稳定的60 FPS速率,在我的游戏中,它可能会以53或其他随机值运行。这意味着我电脑上的弹丸移动速度会变慢,我们也不想这样。
独立于帧的移动
这可以通过计算帧来实现。您可以这样做的一种方法是计算自上一帧以来经过的秒数,记住您可以获得实体在该特定帧中移动所需的空间量。例如,你想以100px / sec的速度移动你的射弹。如果你有2FPS意味着在2帧中它需要移动100px,所以每帧移动100 / 2
px。公式为finalDistance / framerate
。有更多的方法可以做到这一点,但在我看来,这是最简单的理解。那么如何在SFML中实现呢?您基本上保留了在每次更新结束时重新启动的时钟。 getElapsedTime
并重启会执行此操作,但重新启动会返回elapsedTime
,因此最好一次调用它,因为逐个调用它们可能会导致不同的时间和desyncs。
sf::Clock clock;
while (window.isOpen())
{
HandleEvents(window);
Update(clock.restart());
Render(window);
}
您只需使用move(vector * clock.getElapsedTime().asSeconds())
移动您的实体,因为sf::Vector
已为operator*
的{{1}}重载了asSeconds()
。
<强>渲染强>
渲染部分可能非常复杂,但是sfml
使它变得简单快速#34;基本上它的工作原理如下:清除屏幕,绘制实体,显示屏幕。更具技术性的答案如下:窗口由2个缓冲区组成,一个是可见的,一个是隐藏的。可见的是您在屏幕上看到的那个。当您致电clear()
时,您基本上清除了隐藏的那个,draw()
也会在隐藏的窗口上绘制,最后display()
交换缓冲区。
这意味着除非您致电window.display()
,否则您将无法看到任何结果,并且如果您在绘图前没有致电clear()
,那么您将获得窗口xp体验。因此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的类时,预计这个子弹将代表你游戏中的一个射弹,但当你的子弹制造&#34;射弹&#34;并存储弹丸,它成为一个超自然的实体。你的子弹atm持有指向其他子弹实例的指针,这些子弹可以指向其他子弹的实例。这是一团糟。除非您想要创建某种图形或某种树,否则您没有任何理由存储同一类实例的指针。
朋友太多
如果每个班级都是每个班级的朋友,那么你创建私人领域的原因是什么?朋友是一个非常强大的概念,只有在你不具备其他选择的情况下才应谨慎使用。我避免使用此关键字的唯一原因是它创建的混乱。它创建与公共属性相同的效果。当一切都可以从任何地方访问时,一切都可以从各处被破坏。当您创建一组操作属性的方法时,就会知道问题所在。
<强>结论强>
我可能会建议再多看一下c ++,然后调试你的游戏,或者从头开始重新创建它。虽然我知道尝试新事物的感觉如何,但你应该小心谨慎,不要把自己射到腿上,当你遇到这样的错误时,不要害怕回到基础。管理内存有问题吗?阅读有关动态内存分配的更多信息,使用它做一些示例应用。除此之外,我注意到你还在使用课程。我说练习是完美的。看看其他人的代码,即使是像sfml这样的第三方库也可能会给你一些关于良好课堂实践的提示。好处是不需要查看这些库的源代码,只需使用它们的接口即可。如果你喜欢它,这意味着它写得很好,你可以借用那种风格的一部分并在你的课堂上实现。如果您对任何其他问题有任何疑问,我会非常高兴并希望通过电子邮件帮助您。
我将对此作出结论。答案 1 :(得分:1)
我相信你正试图获得十个射弹:
for (int i = 0; i < 10; i++)
{
window.draw(projectiles[i]->bullet);
}
但你一次只能添加一个:
projectiles.push_back(new Bullet());