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