渲染与相机
你能看到游戏世界,全靠这哥俩。
逻辑分辨率
ShitEngine 默认用 1280x720 作为逻辑坐标空间。你写代码时永远在这个"理想屏幕"上思考,渲染器会自动适配到真实窗口,多出的部分用黑边填满(Letterbox)。
逻辑分辨率可以通过 settings.json 修改:
{
"window": {
"title": "My Game",
"width": 1920,
"height": 1080,
"targetFPS": 60
}
}// 也可以在代码中查询当前逻辑分辨率
int w = Renderer::GetLogicalWidth(); // 返回 int
int h = Renderer::GetLogicalHeight(); // 返回 int精灵渲染
SpriteRenderer — 最简单的画图方式
把 SpriteRenderer 挂到 GameObject 上,告诉它纹理路径,它帮你画出来:
auto* sprite = go->addComponent<Shit::SpriteRenderer>();
sprite->setTexturePath("textures/player.png");纹理由 ResourceManager 自动缓存。同一个 png 加载一百次也只占一份内存。
源矩形裁剪
精灵图集(sprite-sheet)的用法——不画整张图,只画其中一小块:
// 只裁剪纹理中 (0, 0, 32, 32) 的区域
sprite->setSourceRect({0.0f, 0.0f, 32.0f, 32.0f});
// 清空恢复整图
sprite->setSourceRect(std::nullopt);这个接口是 AnimationComponent 在背后调用的——你不需要手动管理它。
Sprite 数据类
Sprite 是一个纯数据容器,描述"画什么":
Shit::Sprite sprite("player.png", SDL_FRect{0, 0, 32, 32});
sprite.setFlipped(true);
// 从 SpriteSheet 快速取帧
Shit::SpriteSheet sheet(4, 8, 32, 32);
sprite.setFrame(sheet, 5); // 等价于 setSourceRect(sheet.getFrameRect(5))显示与隐藏
RendererComponent 支持运行时隐藏,不渲染但保留组件状态:
sprite->setVisible(false);
sprite->isVisible(); // 返回 bool相机
相机决定你能看到什么。没有相机等于闭着眼睛玩游戏。
基本用法
auto* camera = scene->createGameObject("camera");
camera->addComponent<Shit::TransformComponent>();
camera->addComponent<Shit::CameraComponent>();相机的位置由 TransformComponent 决定,缩放由 CameraComponent 控制:
auto* cam = camera->getComponent<Shit::CameraComponent>();
cam->setZoom(5.0f); // 放大,像素风游戏必备
cam->setSize({ 320, 180 }); // 世界大小(逻辑分辨率单位)坐标转换
// 世界坐标 → 屏幕像素
Vector2 screenPos = camera->worldToScreen(worldPos);
// 屏幕像素 → 世界坐标
Vector2 worldPos = camera->screenToWorld(screenPos);做点击检测、拖拽交互的时候特别好用。
像素单位比(PixelPerUnit)
CameraComponent 暴露了 getPixelPerUnit(),表示一个逻辑单位对应多少像素。setZoom() 改变的是视口大小,getPixelPerUnit() 会随之变化,从而自动缩放所有精灵。
多相机分屏
多个相机可以共享同一个场景,各画各的:
// 玩家 1 视口:左半边
auto* p1cam = p1->getComponent<Shit::CameraComponent>();
p1cam->setViewportRatio({0.0f, 0.0f, 0.5f, 1.0f});
// 玩家 2 视口:右半边
auto* p2cam = p2->getComponent<Shit::CameraComponent>();
p2cam->setViewportRatio({0.5f, 0.0f, 0.5f, 1.0f});真实用途:
| 要干嘛 | 怎么搞 |
|---|---|
| 分屏合作 | 两台相机各占一半 |
| 小地图 | 右下角一个小视口 |
| 画中画 | 两相机嵌套 |
| 子弹镜 | 相机跟子弹走 |
优先级
多个相机渲染时,优先级低的先画(在底层)。
cam->setPriority(0); // 背景层
cam->setPriority(10); // UI 层,盖在上面裁剪
超出相机视野范围的精灵不会被渲染。
渲染流程
每帧的铁打流程:
1. ClearScreen() — 清屏
2. BehaviorSystem::update() — 游戏逻辑(优先级 0)
3. PhysicsSystem2D::update() — 物理步进(优先级 50,可选)
4. RenderSystem::update() — 游戏世界渲染(优先级 100)
a. 排序相机(按优先级)
b. 排序精灵(按 Z-Index)
c. 对每个相机:设置视口 → 裁剪 → 渲染可见精灵
5. UIRenderSystem::update() — UI 系统叠在最上层(优先级 200)
a. 输入 Raycasting
b. 按钮状态更新
c. 按 zIndex 从下到上绘制所有 UI 控件
6. Renderer::Present() — 统一显示结果(在 Game::run() 末尾)引擎的 Present 统一在 Game::run() 的末尾执行——游戏世界和 UI 同框只交换一次缓冲区。
Z-Index 控制谁盖在谁上面:
sprite->setZIndex(10); // 值越大越靠上,游戏世界内有效UI 系统
引擎内置了一套完整的 Retained-Mode UI 系统(Canvas / UIImage / UIText / UIButton / UITextInput),走独立的 UIRenderSystem,不受相机/视口影响,自动在游戏世界之上叠加绘制。
详见 UI 系统。
直接绘制(DrawSprite)
除了 UI 系统和挂载 SpriteRenderer,也可以用静态方法直接在屏幕上画精灵:
// 在屏幕坐标 (100, 50) 画一个精灵,自动使用纹理原始尺寸
Shit::Sprite uiSprite("ui/healthbar.png");
Renderer::DrawSprite(uiSprite, {100.0f, 50.0f});
// 指定目标尺寸
Renderer::DrawSprite(uiSprite, {100.0f, 50.0f}, Vector2{200.0f, 40.0f});DrawSprite 不依赖 GameObject/Camera 体系,直接在屏幕像素坐标上绘制。但需要你自己管理每帧的绘制调用,推荐优先使用 UI 系统。
