mirror of
https://github.com/zhenyan121/SporeBG-Conid.git
synced 2026-04-10 06:14:08 +08:00
Added a lots of the file to write the pixel game but due to some problem ,i dicide to change sdl3 to sfml,a more modern lib
This commit is contained in:
@@ -41,6 +41,7 @@ set(SOURCE_FILES
|
|||||||
src/ui/managers/GameUIManager.cpp
|
src/ui/managers/GameUIManager.cpp
|
||||||
src/ui/managers/MainMenuUIManager.cpp
|
src/ui/managers/MainMenuUIManager.cpp
|
||||||
src/graphics/ui/UIRenderer.cpp
|
src/graphics/ui/UIRenderer.cpp
|
||||||
|
src/graphics/font/BitmapFont.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
PRIVATE
|
PRIVATE
|
||||||
SDL3::SDL3
|
SDL3::SDL3
|
||||||
SDL3_ttf::SDL3_ttf
|
SDL3_ttf::SDL3_ttf
|
||||||
|
SDL3_image::SDL3_image
|
||||||
)
|
)
|
||||||
|
|
||||||
# ========== Windows: 复制 DLL ==========
|
# ========== Windows: 复制 DLL ==========
|
||||||
@@ -82,6 +84,9 @@ if (WIN32)
|
|||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
${STDCPP_DLL_PATH}
|
${STDCPP_DLL_PATH}
|
||||||
$<TARGET_FILE_DIR:${PROJECT_NAME}>
|
$<TARGET_FILE_DIR:${PROJECT_NAME}>
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
$<TARGET_FILE:SDL3_image::SDL3_image>
|
||||||
|
$<TARGET_FILE_DIR:${PROJECT_NAME}>
|
||||||
COMMENT "Copying MinGW runtime DLLs"
|
COMMENT "Copying MinGW runtime DLLs"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -25,3 +25,14 @@ FetchContent_Declare(
|
|||||||
)
|
)
|
||||||
|
|
||||||
FetchContent_MakeAvailable(SDL3_ttf)
|
FetchContent_MakeAvailable(SDL3_ttf)
|
||||||
|
|
||||||
|
# ===========================
|
||||||
|
# SDL3_image
|
||||||
|
# ===========================
|
||||||
|
FetchContent_Declare(
|
||||||
|
SDL3_image
|
||||||
|
GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git
|
||||||
|
GIT_TAG release-3.2.0
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(SDL3_image)
|
||||||
@@ -11,14 +11,14 @@ WindowManager::~WindowManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WindowManager::Initialize(GameConfig& config) {
|
bool WindowManager::Initialize(GameConfig& config) {
|
||||||
m_width = config.windowWidth;
|
m_logicalWidth = config.logicalWidth;
|
||||||
m_height = config.windowHeight;
|
m_logicalHeight = config.logicalHeight;
|
||||||
// 创建窗口(支持高DPI和横屏)[3,4](@ref)
|
// 创建窗口(支持高DPI和横屏)[3,4](@ref)
|
||||||
m_window = SDL_CreateWindow(
|
m_window = SDL_CreateWindow(
|
||||||
"孢子棋", // 窗口标题,显示在标题栏上
|
"孢子棋", // 窗口标题,显示在标题栏上
|
||||||
m_width, // 窗口的逻辑宽度(例如 800),用于统一布局,不受屏幕 DPI 影响
|
m_logicalWidth, // 窗口的逻辑宽度(例如 800),用于统一布局,不受屏幕 DPI 影响
|
||||||
m_height, // 窗口的逻辑高度(例如 600)
|
m_logicalHeight, // 窗口的逻辑高度(例如 600)
|
||||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | // 启用高像素密度支持(HiDPI/Retina),确保在高分屏上画面清晰
|
//SDL_WINDOW_HIGH_PIXEL_DENSITY | // 启用高像素密度支持(HiDPI/Retina),确保在高分屏上画面清晰
|
||||||
SDL_WINDOW_RESIZABLE // 允许用户调整窗口大小(可拉伸)
|
SDL_WINDOW_RESIZABLE // 允许用户调整窗口大小(可拉伸)
|
||||||
);
|
);
|
||||||
if (!m_window) {
|
if (!m_window) {
|
||||||
@@ -34,14 +34,28 @@ bool WindowManager::Initialize(GameConfig& config) {
|
|||||||
"创建渲染器失败: %s", SDL_GetError());
|
"创建渲染器失败: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关键设置:启用像素模式
|
// 关键设置:启用像素模式
|
||||||
|
|
||||||
// 设置逻辑呈现模式,实现分辨率自适应[3](@ref)
|
/* // 设置逻辑呈现模式,实现分辨率自适应[3](@ref)
|
||||||
SDL_SetRenderLogicalPresentation(m_renderer,
|
SDL_SetRenderLogicalPresentation(m_renderer,
|
||||||
m_width,
|
m_width,
|
||||||
m_height,
|
m_height,
|
||||||
SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
||||||
SDL_SetWindowSize(m_window, m_width, m_height);
|
*/
|
||||||
|
SDL_SetWindowSize(m_window, UI::StartWindowWidth, UI::StartWindowHeight);
|
||||||
|
// 创建逻辑画布
|
||||||
|
// RGBA8888: 32位色,8位红绿蓝和透明度
|
||||||
|
// TARGET: 纹理可作为渲染目标
|
||||||
|
m_logicalTexture = SDL_CreateTexture(
|
||||||
|
m_renderer,
|
||||||
|
SDL_PIXELFORMAT_RGBA8888,
|
||||||
|
SDL_TEXTUREACCESS_TARGET,
|
||||||
|
m_logicalWidth,
|
||||||
|
m_logicalHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,13 +65,28 @@ void WindowManager::Shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::Clear() {
|
void WindowManager::Clear() {
|
||||||
|
// 1. 设置渲染目标为逻辑纹理
|
||||||
|
SDL_SetRenderTarget(m_renderer, m_logicalTexture);
|
||||||
// 设置画笔颜色
|
// 设置画笔颜色
|
||||||
SDL_SetRenderDrawColor(m_renderer, 255, 255, 255, 255);
|
SDL_SetRenderDrawColor(m_renderer, 255, 255, 255, 255);
|
||||||
// 使用画笔颜色填充整个屏幕
|
// 使用画笔颜色填充整个逻辑画布
|
||||||
SDL_RenderClear(m_renderer);
|
SDL_RenderClear(m_renderer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::Present() {
|
void WindowManager::Present() {
|
||||||
|
|
||||||
|
// 4. 切回默认渲染目标(窗口)
|
||||||
|
SDL_SetRenderTarget(m_renderer, nullptr);
|
||||||
|
SDL_RenderClear(m_renderer); // 清的是窗口(黑边
|
||||||
|
SDL_FRect dstRect;
|
||||||
|
calculateDstRect(dstRect);
|
||||||
|
SDL_RenderTexture (
|
||||||
|
m_renderer,
|
||||||
|
m_logicalTexture, // 源:你已经画好的逻辑画布
|
||||||
|
nullptr, // srcRect:源区域(nullptr = 整张)
|
||||||
|
&dstRect // dstRect:贴到哪里 & 贴多大
|
||||||
|
);
|
||||||
SDL_RenderPresent(m_renderer);
|
SDL_RenderPresent(m_renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,3 +107,19 @@ bool WindowManager::setFullscreen(bool isFullscreen) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::calculateDstRect(SDL_FRect& dstRect) {
|
||||||
|
// 获取窗口的实际尺寸(像素)
|
||||||
|
SDL_GetWindowSize(m_window, &m_windowWidth, &m_windowHeight);
|
||||||
|
// 计算缩放比例,整数缩放
|
||||||
|
int scaleX = m_windowWidth / m_logicalWidth;
|
||||||
|
int scaleY = m_windowHeight / m_logicalHeight;
|
||||||
|
int scale = (scaleX < scaleY) ? scaleX : scaleY;
|
||||||
|
|
||||||
|
// 计算目标矩形的尺寸
|
||||||
|
dstRect.w = static_cast<float>(m_logicalWidth * scale);
|
||||||
|
dstRect.h = static_cast<float>(m_logicalHeight * scale);
|
||||||
|
// 居中显示
|
||||||
|
dstRect.x = static_cast<float>((m_windowWidth - dstRect.w) / 2);
|
||||||
|
dstRect.y = static_cast<float>((m_windowHeight - dstRect.h) / 2);
|
||||||
|
}
|
||||||
@@ -23,7 +23,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
SDL_Window* m_window;
|
SDL_Window* m_window;
|
||||||
SDL_Renderer* m_renderer;
|
SDL_Renderer* m_renderer;
|
||||||
int m_width;
|
int m_logicalWidth;
|
||||||
int m_height;
|
int m_logicalHeight;
|
||||||
|
int m_windowWidth;
|
||||||
|
int m_windowHeight;
|
||||||
bool m_isFullscreen = false;
|
bool m_isFullscreen = false;
|
||||||
|
SDL_Texture* m_logicalTexture = nullptr;
|
||||||
|
// 计算缩放后的目标矩形
|
||||||
|
void calculateDstRect(SDL_FRect& dstRect);
|
||||||
|
|
||||||
};
|
};
|
||||||
208
src/graphics/font/BitmapFont.cpp
Normal file
208
src/graphics/font/BitmapFont.cpp
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#include "BitmapFont.h"
|
||||||
|
|
||||||
|
#include <SDL3_image/SDL_image.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
static bool startsWith(const std::string& s, const char* prefix) {
|
||||||
|
return s.size() >= std::strlen(prefix) &&
|
||||||
|
s.compare(0, std::strlen(prefix), prefix) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// 构造 / 析构
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
BitmapFont::BitmapFont() = default;
|
||||||
|
|
||||||
|
BitmapFont::~BitmapFont() {
|
||||||
|
for (SDL_Texture* tex : m_pages) {
|
||||||
|
SDL_DestroyTexture(tex);
|
||||||
|
}
|
||||||
|
m_pages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// 加载 .fnt
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
bool BitmapFont::load(const std::string& fntPath, SDL_Renderer* renderer) {
|
||||||
|
m_renderer = renderer;
|
||||||
|
|
||||||
|
std::ifstream file(fntPath);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
SDL_Log("BitmapFont: 无法打开 %s", fntPath.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .fnt 所在目录(用于加载 .tga)
|
||||||
|
std::string baseDir =
|
||||||
|
std::filesystem::path(fntPath).parent_path().string();
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(file, line)) {
|
||||||
|
|
||||||
|
// common 行:行高
|
||||||
|
if (startsWith(line, "common ")) {
|
||||||
|
sscanf(line.c_str(),
|
||||||
|
"common lineHeight=%d",
|
||||||
|
&m_lineHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// page 行:加载贴图
|
||||||
|
else if (startsWith(line, "page ")) {
|
||||||
|
int id = 0;
|
||||||
|
char filename[256]{};
|
||||||
|
|
||||||
|
sscanf(line.c_str(),
|
||||||
|
"page id=%d file=\"%255[^\"]\"",
|
||||||
|
&id, filename
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((int)m_pages.size() <= id) {
|
||||||
|
m_pages.resize(id + 1, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string texPath = baseDir + "/" + filename;
|
||||||
|
|
||||||
|
SDL_Surface* surf = IMG_Load(texPath.c_str());
|
||||||
|
if (!surf) {
|
||||||
|
SDL_Log("BitmapFont: 无法加载贴图 %s", texPath.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture* tex =
|
||||||
|
SDL_CreateTextureFromSurface(renderer, surf);
|
||||||
|
SDL_DestroySurface(surf);
|
||||||
|
|
||||||
|
// 像素风关键:最近邻
|
||||||
|
SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
|
||||||
|
|
||||||
|
m_pages[id] = tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// char 行:字形数据
|
||||||
|
else if (startsWith(line, "char ")) {
|
||||||
|
uint32_t id = 0;
|
||||||
|
BitmapGlyph g{};
|
||||||
|
|
||||||
|
sscanf(line.c_str(),
|
||||||
|
"char id=%u x=%d y=%d width=%d height=%d "
|
||||||
|
"xoffset=%d yoffset=%d xadvance=%d page=%d",
|
||||||
|
&id,
|
||||||
|
&g.x, &g.y,
|
||||||
|
&g.w, &g.h,
|
||||||
|
&g.xOffset, &g.yOffset,
|
||||||
|
&g.xAdvance,
|
||||||
|
&g.page
|
||||||
|
);
|
||||||
|
|
||||||
|
m_glyphs[id] = g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// 绘制文本
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
void BitmapFont::drawText(const std::string& text, int x, int y) const {
|
||||||
|
int cursorX = x;
|
||||||
|
int cursorY = y;
|
||||||
|
|
||||||
|
for (uint32_t cp : utf8ToCodepoints(text)) {
|
||||||
|
|
||||||
|
// 换行
|
||||||
|
if (cp == '\n') {
|
||||||
|
cursorX = x;
|
||||||
|
cursorY += m_lineHeight;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_glyphs.find(cp);
|
||||||
|
if (it == m_glyphs.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BitmapGlyph& g = it->second;
|
||||||
|
|
||||||
|
SDL_FRect src{
|
||||||
|
(float)g.x,
|
||||||
|
(float)g.y,
|
||||||
|
(float)g.w,
|
||||||
|
(float)g.h
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_FRect dst{
|
||||||
|
(float)(cursorX + g.xOffset),
|
||||||
|
(float)(cursorY + g.yOffset),
|
||||||
|
(float)g.w,
|
||||||
|
(float)g.h
|
||||||
|
};
|
||||||
|
|
||||||
|
SDL_RenderTexture(
|
||||||
|
m_renderer,
|
||||||
|
m_pages[g.page],
|
||||||
|
&src,
|
||||||
|
&dst
|
||||||
|
);
|
||||||
|
|
||||||
|
cursorX += g.xAdvance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// 行高
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
int BitmapFont::getLineHeight() const {
|
||||||
|
return m_lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// UTF-8 → Unicode codepoint(最小但够用)
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
std::vector<uint32_t>
|
||||||
|
BitmapFont::utf8ToCodepoints(const std::string& text) {
|
||||||
|
std::vector<uint32_t> result;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < text.size();) {
|
||||||
|
uint8_t c = (uint8_t)text[i];
|
||||||
|
|
||||||
|
if (c < 0x80) {
|
||||||
|
result.push_back(c);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
else if ((c >> 5) == 0x6) {
|
||||||
|
uint32_t cp =
|
||||||
|
((text[i] & 0x1F) << 6) |
|
||||||
|
(text[i + 1] & 0x3F);
|
||||||
|
result.push_back(cp);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if ((c >> 4) == 0xE) {
|
||||||
|
uint32_t cp =
|
||||||
|
((text[i] & 0x0F) << 12) |
|
||||||
|
((text[i + 1] & 0x3F) << 6) |
|
||||||
|
(text[i + 2] & 0x3F);
|
||||||
|
result.push_back(cp);
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
else if ((c >> 3) == 0x1E) {
|
||||||
|
uint32_t cp =
|
||||||
|
((text[i] & 0x07) << 18) |
|
||||||
|
((text[i + 1] & 0x3F) << 12) |
|
||||||
|
((text[i + 2] & 0x3F) << 6) |
|
||||||
|
(text[i + 3] & 0x3F);
|
||||||
|
result.push_back(cp);
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 非法 UTF-8,跳过
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
70
src/graphics/font/BitmapFont.h
Normal file
70
src/graphics/font/BitmapFont.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
BitmapFont.h
|
||||||
|
--------------------------------
|
||||||
|
Bitmap Font(BMFont / AngelCode)渲染器
|
||||||
|
- 支持 .fnt(文本格式)
|
||||||
|
- 支持多 page(多张 .tga)
|
||||||
|
- 支持 UTF-8 / 中文
|
||||||
|
- SDL3 / 像素风(NEAREST)
|
||||||
|
|
||||||
|
用法示例:
|
||||||
|
BitmapFont font;
|
||||||
|
font.load("assets/fonts/sanhan.fnt", renderer);
|
||||||
|
font.drawText("你好,像素世界", 10, 10);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// 单个字形(对应 .fnt 中的 char 行)
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
struct BitmapGlyph {
|
||||||
|
int x = 0; // 在贴图中的 x
|
||||||
|
int y = 0; // 在贴图中的 y
|
||||||
|
int w = 0; // 字形宽度
|
||||||
|
int h = 0; // 字形高度
|
||||||
|
|
||||||
|
int xOffset = 0; // 绘制时 x 偏移
|
||||||
|
int yOffset = 0; // 绘制时 y 偏移
|
||||||
|
int xAdvance = 0;// 光标前进量
|
||||||
|
|
||||||
|
int page = 0; // 所属贴图页
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// BitmapFont 类
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
class BitmapFont {
|
||||||
|
public:
|
||||||
|
BitmapFont();
|
||||||
|
~BitmapFont();
|
||||||
|
|
||||||
|
// 加载 .fnt 文件
|
||||||
|
bool load(const std::string& fntPath, SDL_Renderer* renderer);
|
||||||
|
|
||||||
|
// 绘制文本(UTF-8)
|
||||||
|
void drawText(const std::string& text, int x, int y) const;
|
||||||
|
|
||||||
|
// 获取行高(用于换行 / UI 布局)
|
||||||
|
int getLineHeight() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// UTF-8 → Unicode codepoint
|
||||||
|
static std::vector<uint32_t> utf8ToCodepoints(const std::string& text);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_Renderer* m_renderer = nullptr;
|
||||||
|
|
||||||
|
int m_lineHeight = 0;
|
||||||
|
|
||||||
|
// Unicode → Glyph
|
||||||
|
std::unordered_map<uint32_t, BitmapGlyph> m_glyphs;
|
||||||
|
|
||||||
|
// 所有 page 对应的纹理
|
||||||
|
std::vector<SDL_Texture*> m_pages;
|
||||||
|
};
|
||||||
@@ -5,7 +5,8 @@ TextRenderer::TextRenderer(SDL_Renderer* renderer, FontManager* fontManager) :
|
|||||||
m_fontManager(fontManager),
|
m_fontManager(fontManager),
|
||||||
m_renderer(renderer)
|
m_renderer(renderer)
|
||||||
{
|
{
|
||||||
|
//m_bitmapFont = std::make_unique<BitmapFont>();
|
||||||
|
//m_bitmapFont->load("assets/fonts/sanhan.fnt", renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextRenderer::~TextRenderer() {
|
TextRenderer::~TextRenderer() {
|
||||||
@@ -62,6 +63,8 @@ void TextRenderer::renderText(const std::string& text, TextStyle style, int x, i
|
|||||||
static_cast<float>(cached.width),
|
static_cast<float>(cached.width),
|
||||||
static_cast<float>(cached.height) };
|
static_cast<float>(cached.height) };
|
||||||
SDL_RenderTexture(m_renderer, cached.texture, NULL, &dest);
|
SDL_RenderTexture(m_renderer, cached.texture, NULL, &dest);
|
||||||
|
|
||||||
|
//m_bitmapFont->drawText(text, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string& text, TextStyle style) {
|
TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string& text, TextStyle style) {
|
||||||
@@ -91,6 +94,7 @@ TextRenderer::CachedText TextRenderer::createAndCacheTexture(const std::string&
|
|||||||
SDL_Log("错误:无法创建纹理\n");
|
SDL_Log("错误:无法创建纹理\n");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
// 设置纹理缩放模式为最近邻
|
||||||
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST);
|
||||||
// 保存结果
|
// 保存结果
|
||||||
result.texture = texture;
|
result.texture = texture;
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
#include <SDL3_ttf/SDL_ttf.h>
|
#include <SDL3_ttf/SDL_ttf.h>
|
||||||
#include "Textstyle.h"
|
#include "Textstyle.h"
|
||||||
#include "FontManager.h"
|
#include "FontManager.h"
|
||||||
|
#include "BitmapFont.h"
|
||||||
|
#include <memory>
|
||||||
/**
|
/**
|
||||||
* @class TextRenderer
|
* @class TextRenderer
|
||||||
* @brief 文本渲染器
|
* @brief 文本渲染器
|
||||||
@@ -163,5 +164,5 @@ private:
|
|||||||
* @see CachedText, m_cache
|
* @see CachedText, m_cache
|
||||||
*/
|
*/
|
||||||
CachedText createAndCacheTexture(const std::string& text, TextStyle style);
|
CachedText createAndCacheTexture(const std::string& text, TextStyle style);
|
||||||
|
//std::unique_ptr<BitmapFont> m_bitmapFont;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -99,14 +99,14 @@ void UIRenderer::renderText(const Type& data) {
|
|||||||
auto [textW, textH] = m_textRenderer->getTextSize(m_text, m_textStyle);
|
auto [textW, textH] = m_textRenderer->getTextSize(m_text, m_textStyle);
|
||||||
|
|
||||||
// 如果组件的宽高为 0,则使用文本尺寸填充(相当于自动调整控件大小)
|
// 如果组件的宽高为 0,则使用文本尺寸填充(相当于自动调整控件大小)
|
||||||
float boxW = m_rect.w;
|
int boxW = m_rect.w;
|
||||||
float boxH = m_rect.h;
|
int boxH = m_rect.h;
|
||||||
if (boxW <= 0.0f) boxW = static_cast<float>(textW);
|
if (boxW <= 0) boxW = textW;
|
||||||
if (boxH <= 0.0f) boxH = static_cast<float>(textH);
|
if (boxH <= 0) boxH = textH;
|
||||||
|
|
||||||
// 计算文本左上角坐标以实现居中
|
// 计算文本左上角坐标以实现居中
|
||||||
int textX = static_cast<int>(m_rect.x + (boxW - textW) / 2.0f);
|
int textX = m_rect.x + (boxW - textW) / 2;
|
||||||
int textY = static_cast<int>(m_rect.y + (boxH - textH) / 2.0f);
|
int textY = m_rect.y + (boxH - textH) / 2;
|
||||||
|
|
||||||
// 渲染文本(TextRenderer 的 x,y 为左上角)
|
// 渲染文本(TextRenderer 的 x,y 为左上角)
|
||||||
m_textRenderer->renderText(m_text, m_textStyle, textX, textY);
|
m_textRenderer->renderText(m_text, m_textStyle, textX, textY);
|
||||||
|
|||||||
@@ -34,9 +34,13 @@ public:
|
|||||||
* @brief 设置组件位置
|
* @brief 设置组件位置
|
||||||
* @param x X坐标(屏幕像素)
|
* @param x X坐标(屏幕像素)
|
||||||
* @param y Y坐标(屏幕像素)
|
* @param y Y坐标(屏幕像素)
|
||||||
|
* @param w 宽度(屏幕像素)
|
||||||
|
* @param h 高度(屏幕像素)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
virtual void setPosition(int x, int y) {
|
virtual void setRect(int x, int y, int w, int h) {
|
||||||
|
m_rect.w = static_cast<float>(w);
|
||||||
|
m_rect.h = static_cast<float>(h);
|
||||||
m_rect.x = static_cast<float>(x);
|
m_rect.x = static_cast<float>(x);
|
||||||
m_rect.y = static_cast<float>(y);
|
m_rect.y = static_cast<float>(y);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
#include "Button.h"
|
#include "Button.h"
|
||||||
#include "graphics/font/TextRenderer.h"
|
//#include "graphics/font/TextRenderer.h"
|
||||||
|
|
||||||
Button::Button()
|
Button::Button()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Button::Button(TextRenderer* textRenderer) : m_textRenderer(textRenderer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Button::Button(
|
Button::Button(
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
TextStyle style,
|
TextStyle style,
|
||||||
int x,
|
int x,
|
||||||
int y,
|
int y,
|
||||||
TextRenderer* textRenderer,
|
int w,
|
||||||
|
int h,
|
||||||
|
//TextRenderer* textRenderer,
|
||||||
SDL_Color backgroundColor,
|
SDL_Color backgroundColor,
|
||||||
int borderThickness,
|
int borderThickness,
|
||||||
SDL_Color borderColor
|
SDL_Color borderColor
|
||||||
) : m_textRenderer(textRenderer)
|
) //: m_textRenderer(textRenderer)
|
||||||
{
|
{
|
||||||
m_rect.x = static_cast<float>(x);
|
m_rect.x = static_cast<float>(x);
|
||||||
m_rect.y = static_cast<float>(y);
|
m_rect.y = static_cast<float>(y);
|
||||||
@@ -29,12 +30,14 @@ Button::Button(
|
|||||||
m_buttonData.borderColor = borderColor;
|
m_buttonData.borderColor = borderColor;
|
||||||
|
|
||||||
// 如果提供了 TextRenderer,则立即测量文本并更新控件尺寸
|
// 如果提供了 TextRenderer,则立即测量文本并更新控件尺寸
|
||||||
if (m_textRenderer) {
|
/*if (m_textRenderer) {
|
||||||
auto [w, h] = m_textRenderer->getTextSize(text, style);
|
auto [w, h] = m_textRenderer->getTextSize(text, style);
|
||||||
m_rect.w = static_cast<float>(w);
|
m_rect.w = static_cast<float>(w);
|
||||||
m_rect.h = static_cast<float>(h);
|
m_rect.h = static_cast<float>(h);
|
||||||
m_buttonData.rect = m_rect;
|
m_buttonData.rect = m_rect;
|
||||||
}
|
}*/
|
||||||
|
m_rect.w = static_cast<float>(w);
|
||||||
|
m_rect.h = static_cast<float>(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -43,24 +46,24 @@ void Button::setText(const std::string& text, TextStyle style) {
|
|||||||
m_buttonData.textstytle = style;
|
m_buttonData.textstytle = style;
|
||||||
|
|
||||||
// 如果提供了 TextRenderer,则立即测量文本并更新控件尺寸
|
// 如果提供了 TextRenderer,则立即测量文本并更新控件尺寸
|
||||||
if (m_textRenderer) {
|
/*if (m_textRenderer) {
|
||||||
auto [w, h] = m_textRenderer->getTextSize(text, style);
|
auto [w, h] = m_textRenderer->getTextSize(text, style);
|
||||||
m_rect.w = static_cast<float>(w);
|
m_rect.w = static_cast<float>(w);
|
||||||
m_rect.h = static_cast<float>(h);
|
m_rect.h = static_cast<float>(h);
|
||||||
m_buttonData.rect = m_rect;
|
m_buttonData.rect = m_rect;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setText(const std::string& text) {
|
void Button::setText(const std::string& text) {
|
||||||
m_buttonData.text = text;
|
m_buttonData.text = text;
|
||||||
|
|
||||||
// 如果提供了 TextRenderer,则立即测量文本并更新控件尺寸
|
// 如果提供了 TextRenderer,则立即测量文本并更新控件尺寸
|
||||||
if (m_textRenderer) {
|
/*if (m_textRenderer) {
|
||||||
auto [w, h] = m_textRenderer->getTextSize(text, m_buttonData.textstytle);
|
auto [w, h] = m_textRenderer->getTextSize(text, m_buttonData.textstytle);
|
||||||
m_rect.w = static_cast<float>(w);
|
m_rect.w = static_cast<float>(w);
|
||||||
m_rect.h = static_cast<float>(h);
|
m_rect.h = static_cast<float>(h);
|
||||||
m_buttonData.rect = m_rect;
|
m_buttonData.rect = m_rect;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void Button::setBackgroundColor(SDL_Color normal) {
|
void Button::setBackgroundColor(SDL_Color normal) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "ui/base/UIRenderData.h"
|
#include "ui/base/UIRenderData.h"
|
||||||
#include "ui/base/UIComponent.h"
|
#include "ui/base/UIComponent.h"
|
||||||
|
#include "utils/Config.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
// 前向声明,避免在头文件包含过多实现细节
|
// 前向声明,避免在头文件包含过多实现细节
|
||||||
@@ -10,15 +11,15 @@ class Button : public UIComponent{
|
|||||||
public:
|
public:
|
||||||
// 默认构造(不进行自动测量)
|
// 默认构造(不进行自动测量)
|
||||||
Button();
|
Button();
|
||||||
// 可以传入 TextRenderer 指针以便在 setText 时立即计算文字尺寸并更新 rect
|
|
||||||
explicit Button(TextRenderer* textRenderer);
|
|
||||||
|
|
||||||
explicit Button(
|
explicit Button(
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
TextStyle style = {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}},
|
TextStyle style = {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}},
|
||||||
int x = 0,
|
int x = 0,
|
||||||
int y = 0,
|
int y = 0,
|
||||||
TextRenderer* textRenderer = nullptr,
|
int w = UI::ButtonSize,
|
||||||
|
int h = UI::ButtonSize,
|
||||||
|
//TextRenderer* textRenderer = nullptr,
|
||||||
SDL_Color backgroundColor = {200, 200, 200, 255},
|
SDL_Color backgroundColor = {200, 200, 200, 255},
|
||||||
int borderThickness = 0,
|
int borderThickness = 0,
|
||||||
SDL_Color borderColor = {0, 0, 0, 255}
|
SDL_Color borderColor = {0, 0, 0, 255}
|
||||||
@@ -78,7 +79,7 @@ private:
|
|||||||
std::function<void()> m_callback;
|
std::function<void()> m_callback;
|
||||||
ButtonData m_buttonData;
|
ButtonData m_buttonData;
|
||||||
// 用于在 setText 时测量文本尺寸(非拥有)
|
// 用于在 setText 时测量文本尺寸(非拥有)
|
||||||
TextRenderer* m_textRenderer = nullptr;
|
//TextRenderer* m_textRenderer = nullptr;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -126,10 +126,10 @@ void GameUIManager::updateGameState(GameState state) {
|
|||||||
void GameUIManager::setupUIComponents() {
|
void GameUIManager::setupUIComponents() {
|
||||||
// 这里可以添加更多的UI组件初始化逻辑
|
// 这里可以添加更多的UI组件初始化逻辑
|
||||||
|
|
||||||
auto button = std::make_unique<Button>(m_textRenderer);
|
auto button = std::make_unique<Button>();
|
||||||
button->setBackgroundColor({255, 100, 0, 255});
|
button->setBackgroundColor({255, 100, 0, 255});
|
||||||
button->setBorder(2, {0, 0, 0, 255});
|
button->setBorder(2, {0, 0, 0, 255});
|
||||||
button->setPosition(20, 20);
|
button->setRect(20, 20, 200, 100);
|
||||||
button->setEnabled(true);
|
button->setEnabled(true);
|
||||||
button->setVisible(true);
|
button->setVisible(true);
|
||||||
button->setText("Please Choose", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
button->setText("Please Choose", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
||||||
@@ -137,18 +137,16 @@ void GameUIManager::setupUIComponents() {
|
|||||||
m_buttons.emplace(button->getNameHash(), std::move(button));
|
m_buttons.emplace(button->getNameHash(), std::move(button));
|
||||||
|
|
||||||
auto label = std::make_unique<Label>();
|
auto label = std::make_unique<Label>();
|
||||||
label->setPosition(1200, 20);
|
label->setRect(1200, 20, 200, 50);
|
||||||
label->setText("0 0", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
label->setText("0 0", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
||||||
label->setName("MousePositionLabel");
|
label->setName("MousePositionLabel");
|
||||||
m_labels.emplace(label->getNameHash(), std::move(label));
|
m_labels.emplace(label->getNameHash(), std::move(label));
|
||||||
|
|
||||||
auto restartButton = std::make_unique<Button>(
|
auto restartButton = std::make_unique<Button>(
|
||||||
"Restart",
|
"Restart",
|
||||||
(TextStyle){"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}},
|
(TextStyle){"unifont.otf", 48, {0, 0, 0, 255}},
|
||||||
700,
|
700,
|
||||||
500,
|
500
|
||||||
m_textRenderer,
|
|
||||||
(SDL_Color){100, 255, 100, 255}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
restartButton->setCallback([this](){
|
restartButton->setCallback([this](){
|
||||||
|
|||||||
@@ -13,19 +13,26 @@ MainMenuUIManager::~MainMenuUIManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainMenuUIManager::init() {
|
void MainMenuUIManager::init() {
|
||||||
auto button = std::make_unique<Button>(m_textRenderer);
|
auto button = std::make_unique<Button>();
|
||||||
|
|
||||||
button->setBackgroundColor({0, 150, 255, 255});
|
button->setBackgroundColor({0, 150, 255, 255});
|
||||||
|
|
||||||
button->setBorder(2, {0, 0, 0, 255});
|
button->setBorder(2, {0, 0, 0, 255});
|
||||||
button->setPosition(650, 430);
|
|
||||||
|
button->setRect(0, 0, UI::ButtonSize * 4, UI::ButtonSize * 2);
|
||||||
|
|
||||||
button->setName("StartButton");
|
button->setName("StartButton");
|
||||||
button->setText("Start Game", {"SourceHanSansSC-Regular.otf", 64, {255, 255, 255, 255}});
|
|
||||||
|
button->setText("Start Game", {"SourceHanSansSC-Regular.otf", UI::UI_NORMAL_FONT_SIZE, {255, 255, 255, 255}});
|
||||||
|
|
||||||
button->setCallback([this](){
|
button->setCallback([this](){
|
||||||
SDL_Log("Start Game button clicked!");
|
SDL_Log("Start Game button clicked!");
|
||||||
m_eventCallback("GameScene");
|
m_eventCallback("GameScene");
|
||||||
});
|
});
|
||||||
|
|
||||||
m_buttons.emplace(button->getNameHash(), std::move(button));
|
m_buttons.emplace(button->getNameHash(), std::move(button));
|
||||||
auto label = std::make_unique<Label>();
|
auto label = std::make_unique<Label>();
|
||||||
label->setPosition(1200, 20);
|
label->setRect(1200, 20, 200, 50);
|
||||||
label->setText("0 0", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
label->setText("0 0", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
||||||
label->setName("MousePositionLabel");
|
label->setName("MousePositionLabel");
|
||||||
m_labels.emplace(label->getNameHash(), std::move(label));
|
m_labels.emplace(label->getNameHash(), std::move(label));
|
||||||
|
|||||||
@@ -3,11 +3,22 @@
|
|||||||
|
|
||||||
// 统一管理配置
|
// 统一管理配置
|
||||||
struct GameConfig {
|
struct GameConfig {
|
||||||
int windowWidth = 1600;
|
/*
|
||||||
int windowHeight = 900;
|
tileSize: 16x16 每个格子的逻辑大小(像素)
|
||||||
|
屏幕 = 20 × 11 tiles = 320x176 逻辑分辨率
|
||||||
|
*/
|
||||||
|
int logicalWidth = 320;
|
||||||
|
int logicalHeight = 180;
|
||||||
std::string windowTitle = "孢子棋";
|
std::string windowTitle = "孢子棋";
|
||||||
bool vsync = true;
|
bool vsync = true;
|
||||||
int uiScale = 2;
|
int scale = 2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
1280x720 scale 4
|
||||||
|
1920x1080 scale 6
|
||||||
|
2560x1440 scale 8
|
||||||
|
3840x2160 scale 12
|
||||||
|
*/
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
// 获取棋盘渲染区域信息(用于坐标转换)
|
// 获取棋盘渲染区域信息(用于坐标转换)
|
||||||
@@ -16,3 +27,21 @@ struct BoardArea {
|
|||||||
int cellSize; // 每格像素大小
|
int cellSize; // 每格像素大小
|
||||||
int rows, cols; // 行列数
|
int rows, cols; // 行列数
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ui 相关常量
|
||||||
|
namespace UI
|
||||||
|
{
|
||||||
|
constexpr int TopBarHeight = 16;
|
||||||
|
constexpr int HotbarHeight = 32;
|
||||||
|
constexpr int ButtonSize = 16;
|
||||||
|
constexpr int SlotSize = 16;
|
||||||
|
constexpr int PanelPadding = 4;
|
||||||
|
constexpr int FontHeight = 8;
|
||||||
|
constexpr int StartWindowWidth = 320 * 4; // 初始窗口宽度(像素)
|
||||||
|
constexpr int StartWindowHeight = 180 * 4; // 初始窗口高度(像素)
|
||||||
|
// 字体大小(逻辑像素)
|
||||||
|
constexpr int DIALOG_FONT_SIZE = 14;
|
||||||
|
constexpr int UI_SMALL_FONT_SIZE = 8;
|
||||||
|
constexpr int UI_NORMAL_FONT_SIZE = 12;
|
||||||
|
constexpr int UI_LARGE_FONT_SIZE = 16;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user