You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SFML多Enemy精灵渲染异常问题求助

SFML多Enemy渲染问题解决

问题现象

  • 引入Enemies.h渲染多个Enemy时,Enemy::draw中window.draw(healthText)语句报错;
  • 注释文本绘制代码后,Enemy精灵仅显示红色占位框,移除sprite.setColor(sf::Color::Red)则完全不显示(纹理加载失效);
  • 仅渲染单个Enemy时,纹理、文本均正常显示,程序运行流畅。

核心原因

SFML的sf::Texture和sf::Font属于GPU资源管理类,它们的默认拷贝构造是浅拷贝:拷贝后的对象会共享底层GPU资源,但原对象销毁时会释放该资源,导致拷贝对象的资源直接失效。

你的代码中,每次调用enemies.push_back(enemy)时,会触发Enemy的拷贝构造,将临时创建的Enemy对象拷贝到vector中。临时对象销毁后,其内部的texture和font资源被释放,vector中的Enemy对象就会使用失效的资源,从而出现纹理红框、文本绘制报错的问题。单个Enemy时没有拷贝操作,因此资源正常。

修复方案

将sf::Texture和sf::Font改为全局共享资源,让所有Enemy对象复用同一资源,避免拷贝导致的资源失效。具体步骤:

  1. 在Enemies类中预加载全局的纹理和字体,仅加载一次;
  2. 修改Enemy类的构造函数,接收纹理和字体的引用,不再自行加载;
  3. 移除Enemy类中texture和font的成员变量,改用外部传入的共享资源。

修改后的代码示例

Enemy.h

#pragma once
#include<SFML/Graphics.hpp>

class Enemy
{
    sf::Vector2f position;
    sf::Sprite sprite;
    sf::Vector2f velocity;
    int health;
    sf::Text healthText;
public:
    // 修改构造函数,接收共享纹理和字体的引用
    Enemy(sf::Texture& texture, sf::Font& font, sf::Vector2f position=sf::Vector2f(0.f,0.f), sf::Vector2f velocity = sf::Vector2f(1.f,1.f), int health = 100);
    void draw(sf::RenderWindow& window);
    void update(sf::Time& clockTime);
};

Enemy.cpp

#include "Enemy.h"

Enemy::Enemy(sf::Texture& texture, sf::Font& font, sf::Vector2f position, sf::Vector2f velocity, int health)
{
    this->position = position;
    this->velocity = velocity;
    this->health = health;
    
    // 使用外部传入的共享纹理
    sprite.setColor(sf::Color::Red);
    sprite.setTexture(texture);
    sprite.setPosition(position);
    
    // 使用外部传入的共享字体
    healthText.setFont(font);
    healthText.setString(std::to_string(health));
    healthText.setPosition(position.x, position.y-10);
}

void Enemy::draw(sf::RenderWindow& window)
{
    window.draw(sprite);
    window.draw(healthText);
}

void Enemy::update(sf::Time& clockTime)
{
    sprite.move(velocity * float(clockTime.asMicroseconds()/10000));
    healthText.setPosition(sprite.getPosition().x, sprite.getPosition().y - 10);
}

Enemies.h

#pragma once
#include "Enemy.h"
#include<SFML/Graphics.hpp>

class Enemies
{
    std::vector<Enemy> enemies;
    sf::Texture enemyTexture; // 全局共享纹理
    sf::Font healthFont;      // 全局共享字体
public:
    Enemies(); // 添加构造函数用于预加载资源
    void draw(sf::RenderWindow& window);
    void update(sf::Time& clockTime,sf::Int64& spawnMeasure);
};

Enemies.cpp

#include "Enemies.h"
#include<stdexcept>

Enemies::Enemies()
{
    // 预加载纹理和字体,仅执行一次
    if (!enemyTexture.loadFromFile("./Assets/Images/enemy.png")) 
        throw std::runtime_error("Enemy image not found.");
    if (!healthFont.loadFromFile("./Assets/Fonts/RobotoMono-VariableFont_wght.ttf")) 
        throw std::runtime_error("Font not found.");
}

void Enemies::draw(sf::RenderWindow& window)
{
    for (Enemy& enemy : enemies)
    {
        enemy.draw(window);
    }
}

void Enemies::update(sf::Time& clockTime,sf::Int64& measure)
{
    if(measure <= 1000000)
        measure += clockTime.asMicroseconds();
    else {
        // 创建Enemy时传入共享的纹理和字体
        Enemy enemy(enemyTexture, healthFont, sf::Vector2f(rand() % 1920, rand() % 1080));
        enemies.push_back(enemy);
        measure = 0;
    }
    for (Enemy& enemy : enemies)
    {
        enemy.update(clockTime);
    }
}

main.cpp

无需修改,保持原代码即可。

额外优化建议

  • 可将std::vector<Enemy>改为std::vector<std::unique_ptr<Enemy>>,彻底避免Enemy对象拷贝,进一步提升性能;
  • 用C++11的<random>库替代rand(),获得更均匀的随机分布;
  • 给healthText设置合适的字符大小(如healthText.setCharacterSize(12)),避免文本显示异常。

内容的提问来源于stack exchange,提问作者Gaurav Jangir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 06:34:57