自己设计一个游戏通用的2D摄像机累。Camera2D

在2D游戏开发中,摄像机是一个至关重要的组件,允许开发者控制玩家的视角,决定他们能看到游戏世界的哪些部分。这对于引导玩家的注意力和提供沉浸式体验非常重要,在战斗场景中聚焦于战斗区域;而在探索场景中扩大视野以展示更多的环境细节,跟随主要角色移动,确保玩家始终能够看到控制中的角色和周围的环境,将摄像机焦点对准可交互对象,用来提示玩家这些对象的存在和重要性,还可以用来实现特殊效果,如震动或动态缩放等,在下文中我们将会实现这样一个摄像机类用于自己的游戏。
ICON

一个合格的摄像机应该具有以下功能。

本人习惯已屏幕中心为基准处理数据,并且正向使用坐标,所有以下实现已本人使用舒适维主进行实现,我们来弄一些碎片吧。
  • 视口控制:设置视口大小适应不同的分辨率和屏幕尺寸。
    1
    2
    3
    4
    5
    6
    void SetCameraCenter(short int Viewport_Width, short int Viewport_Height) {
    ViewportWidth = Viewport_Width;
    ViewportHeight = Viewport_Height;
    ViewportCenterX = ViewportWidth * 0.5f;
    ViewportCenterY = ViewportHeight * 0.5f;
    }
  • 焦点跟随:视点跟随焦点移动。
    1
    2
    3
    4
    5
    6
    7
    8
    void SetTarget(float targetX, float targetY) {
    TargetX = targetX;
    TargetY = targetY;
    }
    void SmoothMoveToPosition(float smoothing = 0.5f) {
    CameraFocusX += (targetX - CameraFocusX) * smoothing;
    CameraFocusY += (targetY - CameraFocusY) * smoothing;
    }
  • 坐标转换:屏幕坐标与世界坐标之间转换,便于处理用户输入和游戏对象的位置。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void ScreenToWorld(float screenX, float screenY, float& worldX, float& worldY) const {
    worldX = CameraFocusX - (screenX - ViewportCenterX) / Zoom;
    worldY = CameraFocusY - (screenY - ViewportCenterY) / Zoom;
    }

    void WorldToScreen(float worldX, float worldY, float& screenX, float& screenY) const {
    screenX = ViewportCenterX + (CameraFocusX - worldX) * Zoom;
    screenY = ViewportCenterY + (CameraFocusY - worldY) * Zoom;
    }
  • 缩放功能:适应不同的游戏场景和提供不同的视觉体验。
    1
    2
    3
    4
    5
    6
    float GetScale() const { return Zoom; }

    void SetScale(float zoom = 1)
    {
    Zoom = zoom;
    }
  • 边界限制:设置移动边界,防止摄像机移动到游戏世界之外。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    void SetWorldSize(float Width, float Height) {
    WorldBoundaryLeft = -Width * 0.5f;
    WorldBoundaryTop = -Height * 0.5f;
    WorldBoundaryRight = Width * 0.5f;
    WorldBoundaryBottom = Height * 0.5f;
    }

    bool SetWorldBoundaries(float left, float top, float right, float bottom) {
    if (left < right && top < bottom) {
    WorldBoundaryLeft = left;
    WorldBoundaryTop = top;
    WorldBoundaryRight = right;
    WorldBoundaryBottom = bottom;
    return true;
    }
    return false;
    }

    void ViewportCheckBoundaries() {
    float scaledOffsetX = ViewportCenterX / Zoom;
    float scaledOffsetY = ViewportCenterY / Zoom;
    CameraFocusX = std::max(WorldBoundaryLeft + scaledOffsetX, std::min(CameraFocusX, WorldBoundaryRight - scaledOffsetX));
    CameraFocusY = std::max(WorldBoundaryTop + scaledOffsetY, std::min(CameraFocusY, WorldBoundaryBottom - scaledOffsetY));

    if (ViewportWidth / Zoom > WorldBoundaryRight - WorldBoundaryLeft) {
    CameraFocusX = (WorldBoundaryLeft + WorldBoundaryRight) * 0.5f;
    }
    if (ViewportHeight / Zoom > WorldBoundaryBottom - WorldBoundaryTop) {
    CameraFocusY = (WorldBoundaryTop + WorldBoundaryBottom) * 0.5f;
    }
    }
  • 平滑过渡:避免视角突变给玩家带来不适。
    1
    2
    3
    4
    5
    6
    7
    8
    void SmoothMoveToPosition(float targetX, float targetY, float smoothing = 0.5f) {
    CameraFocusX += (targetX - CameraFocusX) * smoothing;
    CameraFocusY += (targetY - CameraFocusY) * smoothing;
    }

    void Scale(float zoom = 1){
    Zoom += zoom;
    }
  • 抖动效果:模拟冲击爆炸等效果来增强游戏的氛围和反馈。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void Shake(float intensityX = 5.5f, float intensityY = 5.5f) {
    std::uniform_real_distribution<float> disX(-intensityX, intensityX);
    std::uniform_real_distribution<float> disY(-intensityY, intensityY);
    CameraFocusX += disX(gen);
    CameraFocusY += disY(gen);
    }

    void ShakeCircle(float intensityX = 5.5f, float intensityY = 5.5f) {
    std::uniform_real_distribution<float> disX(-intensityX, intensityX);
    std::uniform_real_distribution<float> disY(-intensityY, intensityY);
    std::uniform_real_distribution<float> angle(-360.0f, 360.0f);
    float radian = angle(gen) / 360 * m_PI * 2;
    CameraFocusX += disX(gen) * std::cos(radian);
    CameraFocusY += disY(gen) * std::sin(radian);
    }

此刻Camera2D类的所有碎片都已经获得,我们把他拼起来吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
class Camera2D {
public:
Camera2D(float Viewport_Width, float Viewport_Height,
float World_Width, float World_Height,
float FocusX = 0, float FocusY = 0) :
ViewportCenterX(Viewport_Width * 0.5f),
ViewportCenterY(Viewport_Height * 0.5f),
ViewportWidth(Viewport_Width),
ViewportHeight(Viewport_Height),
WorldBoundaryLeft(-World_Width * 0.5),
WorldBoundaryTop(-World_Height * 0.5),
WorldBoundaryRight(World_Width * 0.5),
WorldBoundaryBottom(World_Height * 0.5),
TargetX(0),
TargetY(0),
CameraFocusX(FocusX),
CameraFocusY(FocusY),
Zoom(1){}

~Camera2D() = default;

void ScreenToWorld(float screenX, float screenY, float& worldX, float& worldY) const {
worldX = CameraFocusX - (screenX - ViewportCenterX) / Zoom;
worldY = CameraFocusY - (screenY - ViewportCenterY) / Zoom;
}

void WorldToScreen(float worldX, float worldY, float& screenX, float& screenY) const {
screenX = ViewportCenterX + (CameraFocusX - worldX) * Zoom;
screenY = ViewportCenterY + (CameraFocusY - worldY) * Zoom;
}

float GetScale() const { return Zoom; }

void SetScale(float zoom = 1)
{
Zoom = zoom;
}

void Scale(float zoom = 1)
{
Zoom += zoom;
}

void SmoothMoveToPosition(float targetX, float targetY, float smoothing = 0.5f) {
CameraFocusX += (targetX - CameraFocusX) * smoothing;
CameraFocusY += (targetY - CameraFocusY) * smoothing;
}

void SetTarget(float targetX, float targetY) {
TargetX = targetX;
TargetY = targetY;
}

void SmoothMoveToTarget(float smoothing = 0.5f) {
CameraFocusX += (TargetX - CameraFocusX) * smoothing;
CameraFocusY += (TargetY - CameraFocusY) * smoothing;
}

void Shake(float intensityX = 5.5f, float intensityY = 5.5f) {
std::uniform_real_distribution<float> disX(-intensityX, intensityX);
std::uniform_real_distribution<float> disY(-intensityY, intensityY);
CameraFocusX += disX(gen);
CameraFocusY += disY(gen);
}

void ShakeCircle(float intensityX = 5.5f, float intensityY = 5.5f) {
std::uniform_real_distribution<float> disX(-intensityX, intensityX);
std::uniform_real_distribution<float> disY(-intensityY, intensityY);
std::uniform_real_distribution<float> angle(-360.0f, 360.0f);
float radian = angle(gen) / 360 * 3.1415926535 * 2;
CameraFocusX += disX(gen) * std::cos(radian);
CameraFocusY += disY(gen) * std::sin(radian);
}

void SetCameraCenter(short int Viewport_Width, short int Viewport_Height) {
ViewportWidth = Viewport_Width;
ViewportHeight = Viewport_Height;
ViewportCenterX = ViewportWidth * 0.5f;
ViewportCenterY = ViewportHeight * 0.5f;
}

void SetFocus(float FocusX, float FocusY) {
CameraFocusX = FocusX;
CameraFocusY = FocusY;
}

void Move(float deltaX, float deltaY) {
CameraFocusX += deltaX;
CameraFocusY += deltaY;
}

float GetFocusX() const { return CameraFocusX; }

float GetFocusY() const { return CameraFocusY; }

void SetWorldSize(float Width, float Height) {
WorldBoundaryLeft = -Width * 0.5f;
WorldBoundaryTop = -Height * 0.5f;
WorldBoundaryRight = Width * 0.5f;
WorldBoundaryBottom = Height * 0.5f;
}

bool SetWorldBoundaries(float left, float top, float right, float bottom) {
if (left < right && top < bottom) {
WorldBoundaryLeft = left;
WorldBoundaryTop = top;
WorldBoundaryRight = right;
WorldBoundaryBottom = bottom;
return true;
}
return false;
}

void ViewportCheckBoundaries() {
float scaledOffsetX = ViewportCenterX / Zoom;
float scaledOffsetY = ViewportCenterY / Zoom;
CameraFocusX = std::max(WorldBoundaryLeft + scaledOffsetX, std::min(CameraFocusX, WorldBoundaryRight - scaledOffsetX));
CameraFocusY = std::max(WorldBoundaryTop + scaledOffsetY, std::min(CameraFocusY, WorldBoundaryBottom - scaledOffsetY));

if (ViewportWidth / Zoom > WorldBoundaryRight - WorldBoundaryLeft) {
CameraFocusX = (WorldBoundaryLeft + WorldBoundaryRight) * 0.5f;
}
if (ViewportHeight / Zoom > WorldBoundaryBottom - WorldBoundaryTop) {
CameraFocusY = (WorldBoundaryTop + WorldBoundaryBottom) * 0.5f;
}
}

void GetFocusRect(float& left, float& top, float& right, float& bottom) {
left = CameraFocusX - ViewportCenterX;
top = CameraFocusY - ViewportCenterY;
right = CameraFocusX + ViewportCenterX;
bottom = CameraFocusY + ViewportCenterY;
}

private:
std::random_device rd;
std::mt19937 gen(rd());
float TargetX, TargetY;
float CameraFocusX, CameraFocusY;
float ViewportCenterX, ViewportCenterY, ViewportWidth, ViewportHeight;
float Zoom, WorldBoundaryLeft, WorldBoundaryTop, WorldBoundaryRight, WorldBoundaryBottom;
};

这是什么?

ICON

自己设计一个游戏通用的2D摄像机累。Camera2D

http://voidgame.space/articles/QiNuoTu/Camera2D/

作者

VoidGameSpace

发布于

2024-06-04

更新于

2024-06-04

许可协议

评论