增加缩放功能和运行截图
This commit is contained in:
@@ -39,3 +39,4 @@ Thumbs.db
|
|||||||
*.uid
|
*.uid
|
||||||
.godot/
|
.godot/
|
||||||
.import/
|
.import/
|
||||||
|
.codegraph/
|
||||||
|
|||||||
@@ -60,8 +60,19 @@ target_link_libraries(mana_core PUBLIC PkgConfig::PUGIXML)
|
|||||||
target_compile_options(mana_core PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(mana_core PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
|
|
||||||
add_executable(mana_pet_world
|
add_executable(mana_pet_world
|
||||||
|
src/app/LogicalViewport.cpp
|
||||||
src/app/main.cpp
|
src/app/main.cpp
|
||||||
src/battle/BattleScene.cpp
|
src/battle/BattleScene.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(mana_pet_world PRIVATE mana_core PkgConfig::RAYLIB)
|
target_link_libraries(mana_pet_world PRIVATE mana_core PkgConfig::RAYLIB)
|
||||||
target_compile_options(mana_pet_world PRIVATE -Wall -Wextra -Wpedantic)
|
target_compile_options(mana_pet_world PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
add_executable(logical_viewport_test
|
||||||
|
tests/app/LogicalViewportTest.cpp
|
||||||
|
src/app/LogicalViewport.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(logical_viewport_test PRIVATE src/app)
|
||||||
|
target_compile_options(logical_viewport_test PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
|
add_test(NAME logical_viewport_test COMMAND logical_viewport_test)
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#include "LogicalViewport.h"
|
||||||
|
|
||||||
|
namespace mana::app {
|
||||||
|
|
||||||
|
LogicalViewport ComputeLogicalViewport(int windowWidth, int windowHeight)
|
||||||
|
{
|
||||||
|
const float safeWindowWidth = static_cast<float>(std::max(1, windowWidth));
|
||||||
|
const float safeWindowHeight = static_cast<float>(std::max(1, windowHeight));
|
||||||
|
const float scale = std::min(
|
||||||
|
safeWindowWidth / static_cast<float>(kLogicalScreenWidth),
|
||||||
|
safeWindowHeight / static_cast<float>(kLogicalScreenHeight));
|
||||||
|
const float width = static_cast<float>(kLogicalScreenWidth) * scale;
|
||||||
|
const float height = static_cast<float>(kLogicalScreenHeight) * scale;
|
||||||
|
return {
|
||||||
|
(safeWindowWidth - width) * 0.5f,
|
||||||
|
(safeWindowHeight - height) * 0.5f,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
scale,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
LogicalPoint WindowToLogicalPoint(float windowX, float windowY, const LogicalViewport& viewport)
|
||||||
|
{
|
||||||
|
const float safeScale = std::max(0.0001f, viewport.scale);
|
||||||
|
return {
|
||||||
|
(windowX - viewport.x) / safeScale,
|
||||||
|
(windowY - viewport.y) / safeScale,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowRect LogicalRectToWindowRect(LogicalRect rect, const LogicalViewport& viewport)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
viewport.x + rect.x * viewport.scale,
|
||||||
|
viewport.y + rect.y * viewport.scale,
|
||||||
|
rect.width * viewport.scale,
|
||||||
|
rect.height * viewport.scale,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mana::app
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace mana::app {
|
||||||
|
|
||||||
|
constexpr int kLogicalScreenWidth = 1280;
|
||||||
|
constexpr int kLogicalScreenHeight = 720;
|
||||||
|
constexpr int kMinimumWindowWidth = 640;
|
||||||
|
constexpr int kMinimumWindowHeight = 360;
|
||||||
|
|
||||||
|
struct LogicalViewport {
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float width = static_cast<float>(kLogicalScreenWidth);
|
||||||
|
float height = static_cast<float>(kLogicalScreenHeight);
|
||||||
|
float scale = 1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LogicalPoint {
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LogicalRect {
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float width = 0.0f;
|
||||||
|
float height = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WindowRect {
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float width = 0.0f;
|
||||||
|
float height = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
LogicalViewport ComputeLogicalViewport(int windowWidth, int windowHeight);
|
||||||
|
LogicalPoint WindowToLogicalPoint(float windowX, float windowY, const LogicalViewport& viewport);
|
||||||
|
WindowRect LogicalRectToWindowRect(LogicalRect rect, const LogicalViewport& viewport);
|
||||||
|
|
||||||
|
} // namespace mana::app
|
||||||
+59
-22
@@ -7,6 +7,7 @@
|
|||||||
#include "InteractableVisual.h"
|
#include "InteractableVisual.h"
|
||||||
#include "InventoryUiModel.h"
|
#include "InventoryUiModel.h"
|
||||||
#include "ItemIconCatalog.h"
|
#include "ItemIconCatalog.h"
|
||||||
|
#include "LogicalViewport.h"
|
||||||
#include "MapAtmosphere.h"
|
#include "MapAtmosphere.h"
|
||||||
#include "MinimapAsset.h"
|
#include "MinimapAsset.h"
|
||||||
#include "MusicAssets.h"
|
#include "MusicAssets.h"
|
||||||
@@ -50,6 +51,20 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using mana::app::kLogicalScreenHeight;
|
||||||
|
using mana::app::kLogicalScreenWidth;
|
||||||
|
using mana::app::kMinimumWindowHeight;
|
||||||
|
using mana::app::kMinimumWindowWidth;
|
||||||
|
|
||||||
|
Camera2D LogicalUiCamera(const mana::app::LogicalViewport& viewport)
|
||||||
|
{
|
||||||
|
Camera2D camera{};
|
||||||
|
camera.offset = {viewport.x, viewport.y};
|
||||||
|
camera.target = {0.0f, 0.0f};
|
||||||
|
camera.zoom = viewport.scale;
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
struct TextureEntry {
|
struct TextureEntry {
|
||||||
Texture2D texture{};
|
Texture2D texture{};
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
@@ -2960,22 +2975,25 @@ void DrawTexturedProgressBar(Runtime& rt, Rectangle bounds, float ratio, Color t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawMiniMap(Runtime& rt, Font font)
|
void DrawMiniMap(Runtime& rt, Font font, const mana::app::LogicalViewport& logicalViewport)
|
||||||
{
|
{
|
||||||
(void)font;
|
(void)font;
|
||||||
|
|
||||||
const Rectangle panel{8.0f, 8.0f, 160.0f, 96.0f};
|
const Rectangle panel{8.0f, 8.0f, 160.0f, 96.0f};
|
||||||
const Rectangle viewport{panel.x + 5.0f, panel.y + 5.0f, panel.width - 10.0f, panel.height - 10.0f};
|
const Rectangle mapViewport{panel.x + 5.0f, panel.y + 5.0f, panel.width - 10.0f, panel.height - 10.0f};
|
||||||
DrawMinimapPanel(rt, panel);
|
DrawMinimapPanel(rt, panel);
|
||||||
|
|
||||||
const float mapWidth = static_cast<float>(std::max(1, rt.map.width * rt.map.tileWidth));
|
const float mapWidth = static_cast<float>(std::max(1, rt.map.width * rt.map.tileWidth));
|
||||||
const float mapHeight = static_cast<float>(std::max(1, rt.map.height * rt.map.tileHeight));
|
const float mapHeight = static_cast<float>(std::max(1, rt.map.height * rt.map.tileHeight));
|
||||||
|
const mana::app::WindowRect scissor = mana::app::LogicalRectToWindowRect(
|
||||||
|
{mapViewport.x, mapViewport.y, mapViewport.width, mapViewport.height},
|
||||||
|
logicalViewport);
|
||||||
|
|
||||||
BeginScissorMode(
|
BeginScissorMode(
|
||||||
static_cast<int>(viewport.x),
|
static_cast<int>(std::floor(scissor.x)),
|
||||||
static_cast<int>(viewport.y),
|
static_cast<int>(std::floor(scissor.y)),
|
||||||
static_cast<int>(viewport.width),
|
static_cast<int>(std::ceil(scissor.width)),
|
||||||
static_cast<int>(viewport.height));
|
static_cast<int>(std::ceil(scissor.height)));
|
||||||
|
|
||||||
if (!rt.minimapPath.empty() && std::filesystem::exists(rt.minimapPath)) {
|
if (!rt.minimapPath.empty() && std::filesystem::exists(rt.minimapPath)) {
|
||||||
Texture2D& minimap = LoadTextureCached(rt, rt.minimapPath);
|
Texture2D& minimap = LoadTextureCached(rt, rt.minimapPath);
|
||||||
@@ -2989,14 +3007,14 @@ void DrawMiniMap(Runtime& rt, Font font)
|
|||||||
playerRatio.x * textureW - 5.0f,
|
playerRatio.x * textureW - 5.0f,
|
||||||
playerRatio.y * textureH - 5.0f,
|
playerRatio.y * textureH - 5.0f,
|
||||||
};
|
};
|
||||||
const float sourceW = std::min(viewport.width, textureW);
|
const float sourceW = std::min(mapViewport.width, textureW);
|
||||||
const float sourceH = std::min(viewport.height, textureH);
|
const float sourceH = std::min(mapViewport.height, textureH);
|
||||||
const float scrollX = std::clamp(minimapPlayerPos.x - viewport.width * 0.5f, 0.0f, std::max(0.0f, textureW - sourceW));
|
const float scrollX = std::clamp(minimapPlayerPos.x - mapViewport.width * 0.5f, 0.0f, std::max(0.0f, textureW - sourceW));
|
||||||
const float scrollY = std::clamp(minimapPlayerPos.y - viewport.height * 0.5f, 0.0f, std::max(0.0f, textureH - sourceH));
|
const float scrollY = std::clamp(minimapPlayerPos.y - mapViewport.height * 0.5f, 0.0f, std::max(0.0f, textureH - sourceH));
|
||||||
const Rectangle sourceRect{scrollX, scrollY, sourceW, sourceH};
|
const Rectangle sourceRect{scrollX, scrollY, sourceW, sourceH};
|
||||||
const Rectangle mapRect{
|
const Rectangle mapRect{
|
||||||
viewport.x,
|
mapViewport.x,
|
||||||
viewport.y,
|
mapViewport.y,
|
||||||
sourceW,
|
sourceW,
|
||||||
sourceH,
|
sourceH,
|
||||||
};
|
};
|
||||||
@@ -3008,8 +3026,8 @@ void DrawMiniMap(Runtime& rt, Font font)
|
|||||||
0.0f,
|
0.0f,
|
||||||
WHITE);
|
WHITE);
|
||||||
const Vector2 playerPoint{
|
const Vector2 playerPoint{
|
||||||
viewport.x + minimapPlayerPos.x - scrollX + 5.0f,
|
mapViewport.x + minimapPlayerPos.x - scrollX + 5.0f,
|
||||||
viewport.y + minimapPlayerPos.y - scrollY + 5.0f,
|
mapViewport.y + minimapPlayerPos.y - scrollY + 5.0f,
|
||||||
};
|
};
|
||||||
if (const std::optional<Vector2> target = CurrentQuestTargetPosition(rt)) {
|
if (const std::optional<Vector2> target = CurrentQuestTargetPosition(rt)) {
|
||||||
const Vector2 targetRatio{
|
const Vector2 targetRatio{
|
||||||
@@ -3017,10 +3035,10 @@ void DrawMiniMap(Runtime& rt, Font font)
|
|||||||
std::clamp(target->y / mapHeight, 0.0f, 1.0f),
|
std::clamp(target->y / mapHeight, 0.0f, 1.0f),
|
||||||
};
|
};
|
||||||
const Vector2 targetPoint{
|
const Vector2 targetPoint{
|
||||||
viewport.x + targetRatio.x * textureW - scrollX,
|
mapViewport.x + targetRatio.x * textureW - scrollX,
|
||||||
viewport.y + targetRatio.y * textureH - scrollY,
|
mapViewport.y + targetRatio.y * textureH - scrollY,
|
||||||
};
|
};
|
||||||
if (CheckCollisionPointRec(targetPoint, viewport)) {
|
if (CheckCollisionPointRec(targetPoint, mapViewport)) {
|
||||||
DrawCircleV(targetPoint, 6.0f, Color{139, 214, 146, 235});
|
DrawCircleV(targetPoint, 6.0f, Color{139, 214, 146, 235});
|
||||||
DrawCircleLines(static_cast<int>(targetPoint.x), static_cast<int>(targetPoint.y), 10.0f, Color{255, 255, 221, 255});
|
DrawCircleLines(static_cast<int>(targetPoint.x), static_cast<int>(targetPoint.y), 10.0f, Color{255, 255, 221, 255});
|
||||||
DrawCircleLines(static_cast<int>(targetPoint.x), static_cast<int>(targetPoint.y), 15.0f, Color{139, 214, 146, 170});
|
DrawCircleLines(static_cast<int>(targetPoint.x), static_cast<int>(targetPoint.y), 15.0f, Color{139, 214, 146, 170});
|
||||||
@@ -3295,10 +3313,10 @@ void DrawDebugCoordinates(Runtime& rt, Font font)
|
|||||||
DrawTextCn(font, CompactSlotLabel(warpLine, 58).c_str(), {panel.x + 14.0f, panel.y + 102.0f}, 14.0f, Color{205, 208, 190, 245});
|
DrawTextCn(font, CompactSlotLabel(warpLine, 58).c_str(), {panel.x + 14.0f, panel.y + 102.0f}, 14.0f, Color{205, 208, 190, 245});
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawUi(Runtime& rt, Font font)
|
void DrawUi(Runtime& rt, Font font, const mana::app::LogicalViewport& logicalViewport)
|
||||||
{
|
{
|
||||||
if (rt.minimapVisible) {
|
if (rt.minimapVisible) {
|
||||||
DrawMiniMap(rt, font);
|
DrawMiniMap(rt, font, logicalViewport);
|
||||||
}
|
}
|
||||||
const Rectangle tracker{846.0f, 18.0f, 416.0f, 86.0f};
|
const Rectangle tracker{846.0f, 18.0f, 416.0f, 86.0f};
|
||||||
DrawRectangleRounded(tracker, 0.05f, 6, Color{244, 231, 186, 222});
|
DrawRectangleRounded(tracker, 0.05f, 6, Color{244, 231, 186, 222});
|
||||||
@@ -7024,7 +7042,9 @@ int main(int argc, char** argv)
|
|||||||
PrewarmMapEntityState(rt, IndoorAliasMapName());
|
PrewarmMapEntityState(rt, IndoorAliasMapName());
|
||||||
RefreshProgress(rt);
|
RefreshProgress(rt);
|
||||||
|
|
||||||
InitWindow(1280, 720, "玛纳宠物世界");
|
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
||||||
|
InitWindow(kLogicalScreenWidth, kLogicalScreenHeight, "玛纳宠物世界");
|
||||||
|
SetWindowMinSize(kMinimumWindowWidth, kMinimumWindowHeight);
|
||||||
SetExitKey(0);
|
SetExitKey(0);
|
||||||
rt.graphicsReady = true;
|
rt.graphicsReady = true;
|
||||||
InitAudioDevice();
|
InitAudioDevice();
|
||||||
@@ -7035,12 +7055,20 @@ int main(int argc, char** argv)
|
|||||||
PrewarmRuntimeGraphics(rt);
|
PrewarmRuntimeGraphics(rt);
|
||||||
|
|
||||||
Camera2D camera{};
|
Camera2D camera{};
|
||||||
camera.offset = {640.0f, 360.0f};
|
camera.offset = {static_cast<float>(kLogicalScreenWidth) * 0.5f, static_cast<float>(kLogicalScreenHeight) * 0.5f};
|
||||||
camera.zoom = 1.55f;
|
camera.zoom = 1.55f;
|
||||||
rt.observerCamera = rt.player;
|
rt.observerCamera = rt.player;
|
||||||
rt.observerZoom = camera.zoom;
|
rt.observerZoom = camera.zoom;
|
||||||
|
|
||||||
while (!WindowShouldClose()) {
|
while (!WindowShouldClose()) {
|
||||||
|
const mana::app::LogicalViewport viewport = mana::app::ComputeLogicalViewport(GetScreenWidth(), GetScreenHeight());
|
||||||
|
SetMouseOffset(
|
||||||
|
static_cast<int>(std::round(-viewport.x)),
|
||||||
|
static_cast<int>(std::round(-viewport.y)));
|
||||||
|
SetMouseScale(
|
||||||
|
static_cast<float>(kLogicalScreenWidth) / std::max(1.0f, viewport.width),
|
||||||
|
static_cast<float>(kLogicalScreenHeight) / std::max(1.0f, viewport.height));
|
||||||
|
const Camera2D uiCamera = LogicalUiCamera(viewport);
|
||||||
const float dt = GetFrameTime();
|
const float dt = GetFrameTime();
|
||||||
if (rt.currentMusicLoaded) {
|
if (rt.currentMusicLoaded) {
|
||||||
UpdateMusicStream(rt.currentMusic);
|
UpdateMusicStream(rt.currentMusic);
|
||||||
@@ -7088,17 +7116,24 @@ int main(int argc, char** argv)
|
|||||||
ProcessTexturePrewarmQueue(rt, 2, 0.0015);
|
ProcessTexturePrewarmQueue(rt, 2, 0.0015);
|
||||||
|
|
||||||
camera.target = rt.mode == Mode::Observer ? rt.observerCamera : rt.player;
|
camera.target = rt.mode == Mode::Observer ? rt.observerCamera : rt.player;
|
||||||
|
camera.offset = {static_cast<float>(GetScreenWidth()) * 0.5f, static_cast<float>(GetScreenHeight()) * 0.5f};
|
||||||
camera.zoom = rt.mode == Mode::Observer ? rt.observerZoom : 1.55f;
|
camera.zoom = rt.mode == Mode::Observer ? rt.observerZoom : 1.55f;
|
||||||
|
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
ClearBackground(BLACK);
|
ClearBackground(BLACK);
|
||||||
|
|
||||||
if (rt.mode == Mode::TitleMenu) {
|
if (rt.mode == Mode::TitleMenu) {
|
||||||
|
BeginMode2D(uiCamera);
|
||||||
DrawTitleMenu(rt, font);
|
DrawTitleMenu(rt, font);
|
||||||
|
EndMode2D();
|
||||||
} else if (rt.mode == Mode::TitleHelp) {
|
} else if (rt.mode == Mode::TitleHelp) {
|
||||||
|
BeginMode2D(uiCamera);
|
||||||
DrawTitleHelp(rt, font);
|
DrawTitleHelp(rt, font);
|
||||||
|
EndMode2D();
|
||||||
} else if (rt.mode == Mode::Battle) {
|
} else if (rt.mode == Mode::Battle) {
|
||||||
|
BeginMode2D(uiCamera);
|
||||||
DrawBattle(rt, font);
|
DrawBattle(rt, font);
|
||||||
|
EndMode2D();
|
||||||
} else {
|
} else {
|
||||||
BeginMode2D(camera);
|
BeginMode2D(camera);
|
||||||
struct VisibleMapDraw {
|
struct VisibleMapDraw {
|
||||||
@@ -7269,7 +7304,8 @@ int main(int argc, char** argv)
|
|||||||
EndMode2D();
|
EndMode2D();
|
||||||
|
|
||||||
DrawMapAtmosphere(rt);
|
DrawMapAtmosphere(rt);
|
||||||
DrawUi(rt, font);
|
BeginMode2D(uiCamera);
|
||||||
|
DrawUi(rt, font, viewport);
|
||||||
if (rt.nearbyWarp && rt.mode == Mode::Explore) {
|
if (rt.nearbyWarp && rt.mode == Mode::Explore) {
|
||||||
DrawInteractionPrompt(rt, font, "进入", rt.warpPrompt);
|
DrawInteractionPrompt(rt, font, "进入", rt.warpPrompt);
|
||||||
} else if (rt.nearbyNpc && rt.mode == Mode::Explore) {
|
} else if (rt.nearbyNpc && rt.mode == Mode::Explore) {
|
||||||
@@ -7301,6 +7337,7 @@ int main(int argc, char** argv)
|
|||||||
DrawPanel(rt, 470, 610, 340, 54);
|
DrawPanel(rt, 470, 610, 340, 54);
|
||||||
DrawTextCn(font, "观察模式", {590, 626}, 22.0f, RAYWHITE);
|
DrawTextCn(font, "观察模式", {590, 626}, 22.0f, RAYWHITE);
|
||||||
}
|
}
|
||||||
|
EndMode2D();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawWakeBlinkOverlay(rt);
|
DrawWakeBlinkOverlay(rt);
|
||||||
|
|||||||
@@ -648,10 +648,7 @@ BattleUiCommand ReadBattleUiCommand(BattleMenuMode& mode, int& selectedIndex, in
|
|||||||
};
|
};
|
||||||
|
|
||||||
const auto logicalMousePosition = []() {
|
const auto logicalMousePosition = []() {
|
||||||
const Vector2 mouse = GetMousePosition();
|
return GetMousePosition();
|
||||||
const float screenW = static_cast<float>(std::max(1, GetScreenWidth()));
|
|
||||||
const float screenH = static_cast<float>(std::max(1, GetScreenHeight()));
|
|
||||||
return Vector2{mouse.x * 1280.0f / screenW, mouse.y * 720.0f / screenH};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto panelCellAt = [](Rectangle panel, Vector2 point) {
|
const auto panelCellAt = [](Rectangle panel, Vector2 point) {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 648 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 774 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 386 KiB |
Reference in New Issue
Block a user