Extra2D/docs/API_Tutorial/07_UI_System.md

338 lines
8.5 KiB
Markdown
Raw Normal View History

# Extra2D API 教程 - 07. UI 系统
## 按钮 (Button)
Extra2D 提供 Button 组件用于创建交互式按钮。
### 创建按钮
```cpp
// 创建按钮
auto button = Button::create();
// 设置位置
button->setPosition(Vec2(640.0f, 360.0f));
// 设置锚点(中心)
button->setAnchor(0.5f, 0.5f);
// 添加到场景
addChild(button);
```
### 设置按钮文本
```cpp
// 加载字体
auto font = resources.loadFont("assets/font.ttf", 28, true);
// 设置按钮字体和文本
button->setFont(font);
button->setText("点击我");
button->setTextColor(Colors::Black);
```
### 设置按钮样式
```cpp
// 设置背景颜色(普通、悬停、按下)
button->setBackgroundColor(
Colors::White, // 普通状态
Colors::LightGray, // 悬停状态
Colors::Gray // 按下状态
);
// 设置边框
button->setBorder(Colors::Black, 2.0f);
// 设置内边距
button->setPadding(Vec2(20.0f, 10.0f));
// 设置固定大小
button->setCustomSize(200.0f, 50.0f);
```
### 透明按钮
```cpp
// 创建透明按钮(仅文本可点击)
auto button = Button::create();
button->setFont(font);
button->setText("菜单项");
button->setTextColor(Colors::Black);
// 透明背景
button->setBackgroundColor(
Colors::Transparent,
Colors::Transparent,
Colors::Transparent
);
// 无边框
button->setBorder(Colors::Transparent, 0.0f);
// 无内边距
button->setPadding(Vec2(0.0f, 0.0f));
```
## 菜单系统
### 创建菜单
```cpp
class MenuScene : public Scene {
public:
void onEnter() override {
Scene::onEnter();
auto &resources = Application::instance().resources();
font_ = resources.loadFont("assets/font.ttf", 28, true);
float centerX = 640.0f;
float startY = 300.0f;
// 创建菜单按钮
createMenuButton("开始游戏", centerX, startY, 0);
createMenuButton("继续游戏", centerX, startY + 50.0f, 1);
createMenuButton("退出", centerX, startY + 100.0f, 2);
menuCount_ = 3;
selectedIndex_ = 0;
updateMenuColors();
}
void onUpdate(float dt) override {
Scene::onUpdate(dt);
auto &input = Application::instance().input();
// 方向键切换选择
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_UP)) {
selectedIndex_ = (selectedIndex_ - 1 + menuCount_) % menuCount_;
updateMenuColors();
} else if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
selectedIndex_ = (selectedIndex_ + 1) % menuCount_;
updateMenuColors();
}
// A键确认
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_A)) {
executeMenuItem();
}
}
private:
Ptr<FontAtlas> font_;
std::vector<Ptr<Button>> buttons_;
int selectedIndex_ = 0;
int menuCount_ = 0;
void createMenuButton(const std::string &text, float x, float y, int index) {
auto button = Button::create();
button->setFont(font_);
button->setText(text);
button->setTextColor(Colors::Black);
button->setBackgroundColor(
Colors::Transparent,
Colors::Transparent,
Colors::Transparent
);
button->setBorder(Colors::Transparent, 0.0f);
button->setPadding(Vec2(0.0f, 0.0f));
button->setCustomSize(200.0f, 40.0f);
button->setAnchor(0.5f, 0.5f);
button->setPosition(x, y);
addChild(button);
buttons_.push_back(button);
}
void updateMenuColors() {
for (int i = 0; i < buttons_.size(); ++i) {
if (buttons_[i]) {
buttons_[i]->setTextColor(
i == selectedIndex_ ? Colors::Red : Colors::Black
);
}
}
}
void executeMenuItem() {
switch (selectedIndex_) {
case 0: startGame(); break;
case 1: continueGame(); break;
case 2: exitGame(); break;
}
}
void startGame() {
// 切换到游戏场景
}
void continueGame() {
// 继续游戏
}
void exitGame() {
Application::instance().quit();
}
};
```
## 绘制文字
### 基本文字绘制
```cpp
void onRender(RenderBackend &renderer) override {
Scene::onRender(renderer);
if (font_) {
// 绘制文字
renderer.drawText(*font_, "Hello World",
Vec2(100.0f, 100.0f), Colors::White);
// 绘制带颜色的文字
renderer.drawText(*font_, "红色文字",
Vec2(100.0f, 150.0f), Colors::Red);
}
}
```
### 格式化文字
```cpp
void onRender(RenderBackend &renderer) override {
Scene::onRender(renderer);
if (infoFont_) {
auto &app = Application::instance();
// 绘制 FPS
std::stringstream ss;
ss << "FPS: " << app.fps();
renderer.drawText(*infoFont_, ss.str(),
Vec2(50.0f, 50.0f), Colors::Yellow);
// 绘制节点数量
ss.str("");
ss << "Nodes: " << nodes_.size();
renderer.drawText(*infoFont_, ss.str(),
Vec2(50.0f, 80.0f), Colors::White);
}
}
```
## 完整示例
```cpp
class StartScene : public Scene {
public:
void onEnter() override {
Scene::onEnter();
auto &app = Application::instance();
auto &resources = app.resources();
// 加载背景
auto bgTex = resources.loadTexture("assets/background.jpg");
if (bgTex) {
auto bg = Sprite::create(bgTex);
bg->setAnchor(0.0f, 0.0f);
addChild(bg);
}
// 加载字体
font_ = resources.loadFont("assets/font.ttf", 32, true);
float centerX = app.getConfig().width / 2.0f;
// 创建标题
titleBtn_ = Button::create();
titleBtn_->setFont(font_);
titleBtn_->setText("游戏标题");
titleBtn_->setTextColor(Colors::Gold);
titleBtn_->setBackgroundColor(
Colors::Transparent,
Colors::Transparent,
Colors::Transparent
);
titleBtn_->setBorder(Colors::Transparent, 0.0f);
titleBtn_->setAnchor(0.5f, 0.5f);
titleBtn_->setPosition(centerX, 200.0f);
addChild(titleBtn_);
// 创建菜单按钮
createMenuButton("新游戏", centerX, 350.0f, 0);
createMenuButton("继续", centerX, 400.0f, 1);
createMenuButton("退出", centerX, 450.0f, 2);
menuCount_ = 3;
updateMenuColors();
}
void onUpdate(float dt) override {
Scene::onUpdate(dt);
auto &input = Application::instance().input();
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_UP)) {
selectedIndex_ = (selectedIndex_ - 1 + menuCount_) % menuCount_;
updateMenuColors();
} else if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_DPAD_DOWN)) {
selectedIndex_ = (selectedIndex_ + 1) % menuCount_;
updateMenuColors();
}
if (input.isButtonPressed(SDL_CONTROLLER_BUTTON_A)) {
executeMenuItem();
}
}
private:
Ptr<FontAtlas> font_;
Ptr<Button> titleBtn_;
std::vector<Ptr<Button>> menuButtons_;
int selectedIndex_ = 0;
int menuCount_ = 0;
void createMenuButton(const std::string &text, float x, float y, int index) {
auto btn = Button::create();
btn->setFont(font_);
btn->setText(text);
btn->setTextColor(Colors::White);
btn->setBackgroundColor(
Colors::Transparent,
Colors::Transparent,
Colors::Transparent
);
btn->setBorder(Colors::Transparent, 0.0f);
btn->setAnchor(0.5f, 0.5f);
btn->setPosition(x, y);
addChild(btn);
menuButtons_.push_back(btn);
}
void updateMenuColors() {
for (int i = 0; i < menuButtons_.size(); ++i) {
if (menuButtons_[i]) {
menuButtons_[i]->setTextColor(
i == selectedIndex_ ? Colors::Red : Colors::White
);
}
}
}
void executeMenuItem() {
switch (selectedIndex_) {
case 0: /* 新游戏 */ break;
case 1: /* 继续 */ break;
case 2: Application::instance().quit(); break;
}
}
};
```
## 下一步
- [08. 音频系统](08_Audio_System.md)