1 | /******************************************************************************************* |
2 | * |
3 | * raylib [core] example - 2d camera platformer |
4 | * |
5 | * This example has been created using raylib 2.5 (www.raylib.com) |
6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) |
7 | * |
8 | * Example contributed by arvyy (@arvyy) and reviewed by Ramon Santamaria (@raysan5) |
9 | * |
10 | * Copyright (c) 2019 arvyy (@arvyy) |
11 | * |
12 | ********************************************************************************************/ |
13 | |
14 | #include "raylib.h" |
15 | #include "raymath.h" |
16 | |
17 | #define G 400 |
18 | #define PLAYER_JUMP_SPD 350.f |
19 | #define PLAYER_HOR_SPD 200.f |
20 | |
21 | typedef struct Player { |
22 | Vector2 position; |
23 | float speed; |
24 | bool canJump; |
25 | } Player; |
26 | |
27 | typedef struct EnvItem { |
28 | Rectangle rect; |
29 | int blocking; |
30 | Color color; |
31 | } EnvItem; |
32 | |
33 | |
34 | void UpdatePlayer(Player *player, EnvItem *envItems, int envItemsLength, float delta); |
35 | |
36 | void UpdateCameraCenter(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height); |
37 | void UpdateCameraCenterInsideMap(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height); |
38 | void UpdateCameraCenterSmoothFollow(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height); |
39 | void UpdateCameraEvenOutOnLanding(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height); |
40 | void UpdateCameraPlayerBoundsPush(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height); |
41 | |
42 | |
43 | int main(void) |
44 | { |
45 | // Initialization |
46 | //-------------------------------------------------------------------------------------- |
47 | const int screenWidth = 800; |
48 | const int screenHeight = 450; |
49 | |
50 | InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera" ); |
51 | |
52 | Player player = { 0 }; |
53 | player.position = (Vector2){ 400, 280 }; |
54 | player.speed = 0; |
55 | player.canJump = false; |
56 | EnvItem envItems[] = { |
57 | {{ 0, 0, 1000, 400 }, 0, LIGHTGRAY }, |
58 | {{ 0, 400, 1000, 200 }, 1, GRAY }, |
59 | {{ 300, 200, 400, 10 }, 1, GRAY }, |
60 | {{ 250, 300, 100, 10 }, 1, GRAY }, |
61 | {{ 650, 300, 100, 10 }, 1, GRAY } |
62 | }; |
63 | |
64 | int envItemsLength = sizeof(envItems)/sizeof(envItems[0]); |
65 | |
66 | Camera2D camera = { 0 }; |
67 | camera.target = player.position; |
68 | camera.offset = (Vector2){ screenWidth/2, screenHeight/2 }; |
69 | camera.rotation = 0.0f; |
70 | camera.zoom = 1.0f; |
71 | |
72 | // Store pointers to the multiple update camera functions |
73 | void (*cameraUpdaters[])(Camera2D*, Player*, EnvItem*, int, float, int, int) = { |
74 | UpdateCameraCenter, |
75 | UpdateCameraCenterInsideMap, |
76 | UpdateCameraCenterSmoothFollow, |
77 | UpdateCameraEvenOutOnLanding, |
78 | UpdateCameraPlayerBoundsPush |
79 | }; |
80 | |
81 | int cameraOption = 0; |
82 | int cameraUpdatersLength = sizeof(cameraUpdaters)/sizeof(cameraUpdaters[0]); |
83 | |
84 | char *cameraDescriptions[] = { |
85 | "Follow player center" , |
86 | "Follow player center, but clamp to map edges" , |
87 | "Follow player center; smoothed" , |
88 | "Follow player center horizontally; updateplayer center vertically after landing" , |
89 | "Player push camera on getting too close to screen edge" |
90 | }; |
91 | |
92 | SetTargetFPS(60); |
93 | //-------------------------------------------------------------------------------------- |
94 | |
95 | // Main game loop |
96 | while (!WindowShouldClose()) |
97 | { |
98 | // Update |
99 | //---------------------------------------------------------------------------------- |
100 | float deltaTime = GetFrameTime(); |
101 | |
102 | UpdatePlayer(&player, envItems, envItemsLength, deltaTime); |
103 | |
104 | camera.zoom += ((float)GetMouseWheelMove()*0.05f); |
105 | |
106 | if (camera.zoom > 3.0f) camera.zoom = 3.0f; |
107 | else if (camera.zoom < 0.25f) camera.zoom = 0.25f; |
108 | |
109 | if (IsKeyPressed(KEY_R)) |
110 | { |
111 | camera.zoom = 1.0f; |
112 | player.position = (Vector2){ 400, 280 }; |
113 | } |
114 | |
115 | if (IsKeyPressed(KEY_C)) cameraOption = (cameraOption + 1)%cameraUpdatersLength; |
116 | |
117 | // Call update camera function by its pointer |
118 | cameraUpdaters[cameraOption](&camera, &player, envItems, envItemsLength, deltaTime, screenWidth, screenHeight); |
119 | //---------------------------------------------------------------------------------- |
120 | |
121 | // Draw |
122 | //---------------------------------------------------------------------------------- |
123 | BeginDrawing(); |
124 | |
125 | ClearBackground(LIGHTGRAY); |
126 | |
127 | BeginMode2D(camera); |
128 | |
129 | for (int i = 0; i < envItemsLength; i++) DrawRectangleRec(envItems[i].rect, envItems[i].color); |
130 | |
131 | Rectangle playerRect = { player.position.x - 20, player.position.y - 40, 40, 40 }; |
132 | DrawRectangleRec(playerRect, RED); |
133 | |
134 | EndMode2D(); |
135 | |
136 | DrawText("Controls:" , 20, 20, 10, BLACK); |
137 | DrawText("- Right/Left to move" , 40, 40, 10, DARKGRAY); |
138 | DrawText("- Space to jump" , 40, 60, 10, DARKGRAY); |
139 | DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom" , 40, 80, 10, DARKGRAY); |
140 | DrawText("- C to change camera mode" , 40, 100, 10, DARKGRAY); |
141 | DrawText("Current camera mode:" , 20, 120, 10, BLACK); |
142 | DrawText(cameraDescriptions[cameraOption], 40, 140, 10, DARKGRAY); |
143 | |
144 | EndDrawing(); |
145 | //---------------------------------------------------------------------------------- |
146 | } |
147 | |
148 | // De-Initialization |
149 | //-------------------------------------------------------------------------------------- |
150 | CloseWindow(); // Close window and OpenGL context |
151 | //-------------------------------------------------------------------------------------- |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | void UpdatePlayer(Player *player, EnvItem *envItems, int envItemsLength, float delta) |
157 | { |
158 | if (IsKeyDown(KEY_LEFT)) player->position.x -= PLAYER_HOR_SPD*delta; |
159 | if (IsKeyDown(KEY_RIGHT)) player->position.x += PLAYER_HOR_SPD*delta; |
160 | if (IsKeyDown(KEY_SPACE) && player->canJump) |
161 | { |
162 | player->speed = -PLAYER_JUMP_SPD; |
163 | player->canJump = false; |
164 | } |
165 | |
166 | int hitObstacle = 0; |
167 | for (int i = 0; i < envItemsLength; i++) |
168 | { |
169 | EnvItem *ei = envItems + i; |
170 | Vector2 *p = &(player->position); |
171 | if (ei->blocking && |
172 | ei->rect.x <= p->x && |
173 | ei->rect.x + ei->rect.width >= p->x && |
174 | ei->rect.y >= p->y && |
175 | ei->rect.y < p->y + player->speed*delta) |
176 | { |
177 | hitObstacle = 1; |
178 | player->speed = 0.0f; |
179 | p->y = ei->rect.y; |
180 | } |
181 | } |
182 | |
183 | if (!hitObstacle) |
184 | { |
185 | player->position.y += player->speed*delta; |
186 | player->speed += G*delta; |
187 | player->canJump = false; |
188 | } |
189 | else player->canJump = true; |
190 | } |
191 | |
192 | void UpdateCameraCenter(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height) |
193 | { |
194 | camera->offset = (Vector2){ width/2, height/2 }; |
195 | camera->target = player->position; |
196 | } |
197 | |
198 | void UpdateCameraCenterInsideMap(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height) |
199 | { |
200 | camera->target = player->position; |
201 | camera->offset = (Vector2){ width/2, height/2 }; |
202 | float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000; |
203 | |
204 | for (int i = 0; i < envItemsLength; i++) |
205 | { |
206 | EnvItem *ei = envItems + i; |
207 | minX = fminf(ei->rect.x, minX); |
208 | maxX = fmaxf(ei->rect.x + ei->rect.width, maxX); |
209 | minY = fminf(ei->rect.y, minY); |
210 | maxY = fmaxf(ei->rect.y + ei->rect.height, maxY); |
211 | } |
212 | |
213 | Vector2 max = GetWorldToScreen2D((Vector2){ maxX, maxY }, *camera); |
214 | Vector2 min = GetWorldToScreen2D((Vector2){ minX, minY }, *camera); |
215 | |
216 | if (max.x < width) camera->offset.x = width - (max.x - width/2); |
217 | if (max.y < height) camera->offset.y = height - (max.y - height/2); |
218 | if (min.x > 0) camera->offset.x = width/2 - min.x; |
219 | if (min.y > 0) camera->offset.y = height/2 - min.y; |
220 | } |
221 | |
222 | void UpdateCameraCenterSmoothFollow(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height) |
223 | { |
224 | static float minSpeed = 30; |
225 | static float minEffectLength = 10; |
226 | static float fractionSpeed = 0.8f; |
227 | |
228 | camera->offset = (Vector2){ width/2, height/2 }; |
229 | Vector2 diff = Vector2Subtract(player->position, camera->target); |
230 | float length = Vector2Length(diff); |
231 | |
232 | if (length > minEffectLength) |
233 | { |
234 | float speed = fmaxf(fractionSpeed*length, minSpeed); |
235 | camera->target = Vector2Add(camera->target, Vector2Scale(diff, speed*delta/length)); |
236 | } |
237 | } |
238 | |
239 | void UpdateCameraEvenOutOnLanding(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height) |
240 | { |
241 | static float evenOutSpeed = 700; |
242 | static int eveningOut = false; |
243 | static float evenOutTarget; |
244 | |
245 | camera->offset = (Vector2){ width/2, height/2 }; |
246 | camera->target.x = player->position.x; |
247 | |
248 | if (eveningOut) |
249 | { |
250 | if (evenOutTarget > camera->target.y) |
251 | { |
252 | camera->target.y += evenOutSpeed*delta; |
253 | |
254 | if (camera->target.y > evenOutTarget) |
255 | { |
256 | camera->target.y = evenOutTarget; |
257 | eveningOut = 0; |
258 | } |
259 | } |
260 | else |
261 | { |
262 | camera->target.y -= evenOutSpeed*delta; |
263 | |
264 | if (camera->target.y < evenOutTarget) |
265 | { |
266 | camera->target.y = evenOutTarget; |
267 | eveningOut = 0; |
268 | } |
269 | } |
270 | } |
271 | else |
272 | { |
273 | if (player->canJump && (player->speed == 0) && (player->position.y != camera->target.y)) |
274 | { |
275 | eveningOut = 1; |
276 | evenOutTarget = player->position.y; |
277 | } |
278 | } |
279 | } |
280 | |
281 | void UpdateCameraPlayerBoundsPush(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height) |
282 | { |
283 | static Vector2 bbox = { 0.2f, 0.2f }; |
284 | |
285 | Vector2 bboxWorldMin = GetScreenToWorld2D((Vector2){ (1 - bbox.x)*0.5f*width, (1 - bbox.y)*0.5f*height }, *camera); |
286 | Vector2 bboxWorldMax = GetScreenToWorld2D((Vector2){ (1 + bbox.x)*0.5f*width, (1 + bbox.y)*0.5f*height }, *camera); |
287 | camera->offset = (Vector2){ (1 - bbox.x)*0.5f * width, (1 - bbox.y)*0.5f*height }; |
288 | |
289 | if (player->position.x < bboxWorldMin.x) camera->target.x = player->position.x; |
290 | if (player->position.y < bboxWorldMin.y) camera->target.y = player->position.y; |
291 | if (player->position.x > bboxWorldMax.x) camera->target.x = bboxWorldMin.x + (player->position.x - bboxWorldMax.x); |
292 | if (player->position.y > bboxWorldMax.y) camera->target.y = bboxWorldMin.y + (player->position.y - bboxWorldMax.y); |
293 | } |
294 | |