Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

495 lignes
19 KiB

  1. /**********************************************************************************************
  2. *
  3. * raylib - Advance Game template
  4. *
  5. * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload)
  6. *
  7. * Copyright (c) 2014 Ramon Santamaria (@raysan5)
  8. *
  9. * This software is provided "as-is", without any express or implied warranty. In no event
  10. * will the authors be held liable for any damages arising from the use of this software.
  11. *
  12. * Permission is granted to anyone to use this software for any purpose, including commercial
  13. * applications, and to alter it and redistribute it freely, subject to the following restrictions:
  14. *
  15. * 1. The origin of this software must not be misrepresented; you must not claim that you
  16. * wrote the original software. If you use this software in a product, an acknowledgment
  17. * in the product documentation would be appreciated but is not required.
  18. *
  19. * 2. Altered source versions must be plainly marked as such, and must not be misrepresented
  20. * as being the original software.
  21. *
  22. * 3. This notice may not be removed or altered from any source distribution.
  23. *
  24. **********************************************************************************************/
  25. #include "raylib.h"
  26. #include "screens.h"
  27. #include <stdio.h>
  28. #include <stdlib.h> // Required for: malloc(), free()
  29. #include <math.h> // Required for: sqrtf(), asinf()
  30. #define MAX_SAMPLES_SPEED 7 // Max speed for samples movement
  31. #define MIN_SAMPLES_SPEED 3 // Min speed for samples movement
  32. #define SAMPLES_SPACING 100 // Separation between samples in pixels
  33. #define SAMPLES_MULTIPLIER 700 // Defines sample data scaling value (it would be adjusted to MAX_GAME_HEIGHT)
  34. #define MAX_GAME_HEIGHT 400 // Defines max possible amplitude between samples (game area)
  35. //----------------------------------------------------------------------------------
  36. // Types and Structures Definition
  37. //----------------------------------------------------------------------------------
  38. typedef struct Player {
  39. Vector2 position;
  40. Vector2 speed;
  41. int width;
  42. int height;
  43. Color color;
  44. } Player;
  45. typedef struct Sample {
  46. Vector2 position;
  47. float value; // Raw audio sample value (normalized)
  48. int radius;
  49. bool active; // Define if sample is active (can be collected)
  50. bool collected; // Define if sample has been collected
  51. bool renderable; // Define if sample should be rendered
  52. Color color;
  53. } Sample;
  54. //----------------------------------------------------------------------------------
  55. // Global Variables Definition (local to this module)
  56. //----------------------------------------------------------------------------------
  57. // Gameplay screen global variables
  58. static int framesCounter;
  59. static int finishScreen;
  60. static bool pause;
  61. // Player variables
  62. static Player player;
  63. static Rectangle playerArea; // Define player movement area (and sample collection limits)
  64. static float warpCounter; // Time warp counter
  65. static float synchro; // Calculates collected samples relation [0..1]
  66. static int combo;
  67. static int maxCombo;
  68. static Rectangle waveRec;
  69. // Samples variables
  70. static Sample *samples; // Game samples
  71. static int totalSamples; // Total game samples (proportional to waveData num samples)
  72. static int collectedSamples; // Samples collected by player
  73. static int currentSample; // Last sample to go through player collect area
  74. static float samplesSpeed; // All samples move at the same speed
  75. static float waveTime; // Total sample time in ms
  76. // Resources variables
  77. static Texture2D texBackground;
  78. static Texture2D texPlayer;
  79. static Texture2D texSampleSmall;
  80. static Texture2D texSampleMid;
  81. static Texture2D texSampleBig;
  82. static RenderTexture2D waveTarget;
  83. static Sound fxSampleOn; // Collected sample sound
  84. static Sound fxSampleOff; // Miss sample sound
  85. static Sound fxPause; // Pause sound
  86. // Debug variables
  87. //------------------------------------------------------------------------------------
  88. // Module Functions Declaration (local)
  89. //------------------------------------------------------------------------------------
  90. static void DrawSamplesMap(Sample *samples, int sampleCount, int playedSamples, Rectangle bounds, Color color);
  91. //----------------------------------------------------------------------------------
  92. // Gameplay Screen Functions Definition
  93. //----------------------------------------------------------------------------------
  94. // Gameplay Screen Initialization logic
  95. void InitGameplayScreen(void)
  96. {
  97. framesCounter = 0;
  98. finishScreen = 0;
  99. pause = false;
  100. endingStatus = 0;
  101. // Textures loading
  102. texBackground = LoadTexture("resources/textures/background_gameplay.png");
  103. texPlayer = LoadTexture("resources/textures/player.png");
  104. texSampleSmall = LoadTexture("resources/textures/sample_small.png");
  105. texSampleMid = LoadTexture("resources/textures/sample_mid.png");
  106. texSampleBig = LoadTexture("resources/textures/sample_big.png");
  107. waveRec = (Rectangle){ 32, 32, 1280 - 64, 105 };
  108. waveTarget = LoadRenderTexture(waveRec.width, waveRec.height);
  109. // Sound loading
  110. fxSampleOn = LoadSound("resources/audio/sample_on.wav");
  111. fxSampleOff = LoadSound("resources/audio/sample_off.wav");
  112. fxPause = LoadSound("resources/audio/pause.wav");
  113. SetSoundVolume(fxSampleOn, 0.6f);
  114. SetSoundVolume(fxPause, 0.5f);
  115. // Initialize player data
  116. playerArea = (Rectangle){ 200, 160, 80, 400 };
  117. player.width = 20;
  118. player.height = 60;
  119. player.speed = (Vector2){ 15, 15 };
  120. player.color = GOLD;
  121. player.position = (Vector2){ playerArea.x + playerArea.width/2 - texPlayer.width/2,
  122. playerArea.y + playerArea.height/2 - texPlayer.height/2 };
  123. warpCounter = 395;
  124. synchro = 0.2f;
  125. combo = 0;
  126. maxCombo = 0;
  127. // Initialize wave and samples data
  128. Wave wave = LoadWave("resources/audio/wave.ogg");
  129. float *waveData = GetWaveData(wave); // TODO: Be careful with channels!
  130. // We calculate the required parameters to adjust audio time to gameplay time
  131. // that way samples collected correspond to audio playing
  132. // Synchonization is not perfect due to possible rounding issues (float to int)
  133. waveTime = wave.sampleCount/wave.sampleRate; // Total sample time in seconds
  134. float requiredSamples = (MAX_SAMPLES_SPEED*waveTime*60 - 1000)/SAMPLES_SPACING;
  135. int samplesDivision = (int)(wave.sampleCount/requiredSamples);
  136. totalSamples = wave.sampleCount/samplesDivision;
  137. // We don't need wave any more (already got waveData)
  138. UnloadWave(wave);
  139. collectedSamples = 0;
  140. // Init samples
  141. samples = (Sample *)malloc(totalSamples*sizeof(Sample));
  142. // Normalize wave data (min vs max values) to scale properly
  143. float minSampleValue = 0.0f;
  144. float maxSampleValue = 0.0f;
  145. for (int i = 0; i < totalSamples; i++)
  146. {
  147. if (waveData[i*samplesDivision] < minSampleValue) minSampleValue = waveData[i*samplesDivision];
  148. if (waveData[i*samplesDivision] > maxSampleValue) maxSampleValue = waveData[i*samplesDivision];
  149. }
  150. float sampleScaleFactor = 1.0f/(maxSampleValue - minSampleValue); // 400 pixels maximum size
  151. // Initialize samples
  152. for (int i = 0; i < totalSamples; i++)
  153. {
  154. samples[i].value = waveData[i*samplesDivision]*sampleScaleFactor; // Normalized value [-1.0..1.0]
  155. samples[i].position.x = player.position.x + 1000 + i*SAMPLES_SPACING;
  156. samples[i].position.y = GetScreenHeight()/2 + samples[i].value*SAMPLES_MULTIPLIER;
  157. if (samples[i].position.y > GetScreenHeight()/2 + MAX_GAME_HEIGHT/2) samples[i].position.y = GetScreenHeight()/2 - MAX_GAME_HEIGHT/2;
  158. else if (samples[i].position.y < GetScreenHeight()/2 - MAX_GAME_HEIGHT/2) samples[i].position.y = GetScreenHeight()/2 + MAX_GAME_HEIGHT/2;
  159. samples[i].radius = 6;
  160. samples[i].active = true;
  161. samples[i].collected = false;
  162. samples[i].color = RED;
  163. samples[i].renderable = false;
  164. }
  165. samplesSpeed = MAX_SAMPLES_SPEED;
  166. currentSample = 0;
  167. //FILE *samplesFile = fopen("resources/samples.data", "wb");
  168. //fwrite(samples, totalSamples*sizeof(Sample), 1, samplesFile);
  169. //fclose(samplesFile);
  170. // We already saved the samples we needed for the game, we can free waveData
  171. free(waveData);
  172. // Load and start playing music
  173. // NOTE: Music is loaded in main code base
  174. StopMusicStream(music);
  175. PlayMusicStream(music);
  176. }
  177. // Gameplay Screen Update logic
  178. void UpdateGameplayScreen(void)
  179. {
  180. if (IsKeyPressed('P'))
  181. {
  182. PlaySound(fxPause);
  183. pause = !pause;
  184. if (pause) PauseMusicStream(music);
  185. else ResumeMusicStream(music);
  186. }
  187. if (!pause)
  188. {
  189. framesCounter++; // Time starts counting to awake enemies
  190. // Player movement logic (mouse)
  191. player.position.y = GetMousePosition().y;
  192. // Player movement logic (keyboard)
  193. if (IsKeyDown(KEY_W)) player.position.y -= player.speed.y;
  194. else if (IsKeyDown(KEY_S)) player.position.y += player.speed.y;
  195. // Player movement logic (gamepad)
  196. /*
  197. if (IsGamepadAvailable(GAMEPAD_PLAYER1))
  198. {
  199. Vector2 movement = { 0.0f };
  200. movement.x = GetGamepadAxisMovement(GAMEPAD_PLAYER1, GAMEPAD_PS3_AXIS_LEFT_X);
  201. movement.y = GetGamepadAxisMovement(GAMEPAD_PLAYER1, GAMEPAD_PS3_AXIS_LEFT_Y);
  202. player.position.x += movement.x*0.1f; // Scale gamepad movement value
  203. player.position.y += movement.y*0.1f; // Scale gamepad movement value
  204. }
  205. */
  206. // Player logic: check player area limits
  207. if (player.position.x < playerArea.x) player.position.x = playerArea.x;
  208. else if ((player.position.x + player.width) > (playerArea.x + playerArea.width)) player.position.x = playerArea.x + playerArea.width - player.width;
  209. if (player.position.y < playerArea.y) player.position.y = playerArea.y;
  210. else if ((player.position.y + player.height) > (playerArea.y + playerArea.height)) player.position.y = playerArea.y + playerArea.height - player.height;
  211. // Samples logic
  212. for (int i = 0; i < totalSamples; i++)
  213. {
  214. // Samples movement logic
  215. samples[i].position.x -= samplesSpeed;
  216. if (((samples[i].position.x + samples[i].radius) > -SAMPLES_SPACING) &&
  217. ((samples[i].position.x - samples[i].radius) < GetScreenWidth())) samples[i].renderable = true;
  218. else samples[i].renderable = false;
  219. // Samples catch logic
  220. if (!samples[i].collected && CheckCollisionCircleRec(samples[i].position, samples[i].radius, (Rectangle){ (int)player.position.x, (int)player.position.y, player.width, player.height }))
  221. {
  222. samples[i].collected = true;
  223. collectedSamples++;
  224. synchro += 0.02;
  225. combo++;
  226. if (combo > maxCombo) maxCombo = combo;
  227. if (synchro >= 1.0f) synchro = 1.0f;
  228. // Set sound pitch depending on sample position (base pitch: 1.0f)
  229. // NOTE: waveData[i*WAVE_SAMPLES_DIV] is scaled to [0.3..1.7]
  230. SetSoundPitch(fxSampleOn, samples[i].value*1.4f + 0.7f);
  231. PlaySound(fxSampleOn);
  232. }
  233. if ((samples[i].position.x - samples[i].radius) < player.position.x)
  234. {
  235. currentSample = i; // Register last sample going out range
  236. if (samples[i].active)
  237. {
  238. samples[i].active = false;
  239. if (!samples[i].collected)
  240. {
  241. synchro -= 0.05f;
  242. PlaySound(fxSampleOff);
  243. combo = 0;
  244. }
  245. }
  246. }
  247. }
  248. if (IsKeyDown(KEY_SPACE) && (warpCounter > 0))
  249. {
  250. warpCounter--;
  251. if (warpCounter < 0) warpCounter = 0;
  252. samplesSpeed -= 0.1f;
  253. if (samplesSpeed <= MIN_SAMPLES_SPEED) samplesSpeed = MIN_SAMPLES_SPEED;
  254. SetMusicPitch(music, samplesSpeed/MAX_SAMPLES_SPEED);
  255. }
  256. else
  257. {
  258. warpCounter++;
  259. if (warpCounter > 395) warpCounter = 395;
  260. samplesSpeed += 0.1f;
  261. if (samplesSpeed >= MAX_SAMPLES_SPEED) samplesSpeed = MAX_SAMPLES_SPEED;
  262. SetMusicPitch(music, samplesSpeed/MAX_SAMPLES_SPEED);
  263. }
  264. // Check ending conditions
  265. if (currentSample >= totalSamples - 1)
  266. {
  267. endingStatus = 1; // Win
  268. finishScreen = 1;
  269. }
  270. if (synchro <= 0.0f)
  271. {
  272. synchro = 0.0f;
  273. endingStatus = 2; // Loose
  274. finishScreen = 1;
  275. }
  276. }
  277. }
  278. // Gameplay Screen Draw logic
  279. void DrawGameplayScreen(void)
  280. {
  281. // Draw background
  282. DrawTexture(texBackground, 0, 0, WHITE);
  283. // Screen elements drawing
  284. //DrawRectangleLines(playerArea.x, playerArea.y, playerArea.width, playerArea.height, BLUE);
  285. DrawRectangle(0, GetScreenHeight()/2 - 1, GetScreenWidth(), 2, Fade(BLUE, 0.3f));
  286. //DrawRectangleLines(0, GetScreenHeight()/2 - MAX_GAME_HEIGHT/2, GetScreenWidth(), MAX_GAME_HEIGHT, GRAY);
  287. // Draw samples
  288. for (int i = 0; i < totalSamples - 1; i++)
  289. {
  290. if (samples[i].renderable)
  291. {
  292. Color col = samples[i].color;
  293. if (i < (currentSample + 1)) col = Fade(DARKGRAY, 0.5f);
  294. else col = WHITE;
  295. if (!samples[i].collected)
  296. {
  297. //DrawCircleV(samples[i].position, samples[i].radius, col);
  298. if (combo > 30) DrawTexture(texSampleSmall, samples[i].position.x - texSampleSmall.width/2, samples[i].position.y - texSampleSmall.height/2, col);
  299. else if (combo > 15) DrawTexture(texSampleMid, samples[i].position.x - texSampleMid.width/2, samples[i].position.y - texSampleMid.height/2, col);
  300. else DrawTexture(texSampleBig, samples[i].position.x - texSampleBig.width/2, samples[i].position.y - texSampleBig.height/2, col);
  301. }
  302. if (i < (currentSample + 1)) col = Fade(GRAY, 0.3f);
  303. else col = Fade(RED, 0.5f);
  304. // Draw line between samples
  305. DrawLineEx(samples[i].position, samples[i + 1].position, 3.0f, col);
  306. }
  307. }
  308. // Draw player
  309. //DrawRectangle((int)player.position.x, (int)player.position.y, player.width, player.height, player.color);
  310. DrawTexture(texPlayer, player.position.x - 32, player.position.y - 24, WHITE);
  311. // Draw pause message
  312. if (pause) DrawTextEx(font, "WAVE PAUSED", (Vector2){ 235, 400 }, font.baseSize*2, 0, WHITE);
  313. // Draw number of samples
  314. //DrawText(FormatText("%05i", collectedSamples), 900, 200, 40, GRAY);
  315. //DrawText(FormatText("%05i", totalSamples), 900, 250, 40, GRAY);
  316. DrawTextEx(font, FormatText("%05i / %05i", collectedSamples, totalSamples), (Vector2){810, 170}, font.baseSize, -2, SKYBLUE);
  317. // Draw combo
  318. DrawTextEx(font, FormatText("Combo: %02i [max: %02i]", combo, maxCombo), (Vector2){200, 170}, font.baseSize/2, -2, SKYBLUE);
  319. // Draw synchonicity level
  320. DrawRectangle(99, 622, 395, 32, Fade(RAYWHITE, 0.8f));
  321. if (synchro <= 0.3f) DrawRectangle(99, 622, synchro*395, 32, Fade(RED, 0.8f));
  322. else if (synchro <= 0.8f) DrawRectangle(99, 622, synchro*395, 32, Fade(ORANGE,0.8f));
  323. else if (synchro < 1.0f) DrawRectangle(99, 622, synchro*395, 32, Fade(LIME,0.8f));
  324. else DrawRectangle(99, 622, synchro*395, 32, Fade(GREEN, 0.9f));
  325. DrawRectangleLines(99, 622, 395, 32, MAROON);
  326. if (synchro == 1.0f) DrawTextEx(font, FormatText("%02i%%", (int)(synchro*100)), (Vector2){99 + 390, 600}, font.baseSize, -2, GREEN);
  327. else DrawTextEx(font, FormatText("%02i%%", (int)(synchro*100)), (Vector2){99 + 390, 600}, font.baseSize, -2, SKYBLUE);
  328. // Draw time warp coool-down bar
  329. DrawRectangle(754, 622, 395, 32, Fade(RAYWHITE, 0.8f));
  330. DrawRectangle(754, 622, warpCounter, 32, Fade(SKYBLUE, 0.8f));
  331. DrawRectangleLines(754, 622, 395, 32, DARKGRAY);
  332. //DrawText(FormatText("%02i%%", (int)(synchro*100)), 754 + 410, 628, 20, DARKGRAY);
  333. DrawTextEx(font, FormatText("%02i%%", (int)((float)warpCounter/395.0f*100.0f)), (Vector2){754 + 390, 600}, font.baseSize, -2, SKYBLUE);
  334. // Draw wave
  335. if (waveTarget.texture.id <= 0) // Render target could not be loaded (OpenGL 1.1)
  336. {
  337. // Draw wave directly on screen
  338. DrawSamplesMap(samples, totalSamples, currentSample, waveRec, MAROON);
  339. DrawRectangle(waveRec.x + (int)currentSample*1215/totalSamples, waveRec.y, 2, 99, DARKGRAY);
  340. }
  341. else
  342. {
  343. // Draw wave using render target
  344. BeginTextureMode(waveTarget);
  345. ClearBackground(BLANK);
  346. DrawSamplesMap(samples, totalSamples, currentSample, (Rectangle){ 0, 0, waveTarget.texture.width, waveTarget.texture.height }, MAROON);
  347. EndTextureMode();
  348. // TODO: Apply antialiasing shader
  349. DrawTextureEx(waveTarget.texture, (Vector2){ waveRec.x, waveRec.y }, 0.0f, 1.0f, WHITE);
  350. DrawRectangle(waveRec.x + (int)currentSample*1215/totalSamples, waveRec.y, 2, 99, DARKGRAY);
  351. }
  352. }
  353. // Gameplay Screen Unload logic
  354. void UnloadGameplayScreen(void)
  355. {
  356. StopMusicStream(music);
  357. // Unload textures
  358. UnloadTexture(texBackground);
  359. UnloadTexture(texPlayer);
  360. UnloadTexture(texSampleSmall);
  361. UnloadTexture(texSampleMid);
  362. UnloadTexture(texSampleBig);
  363. UnloadRenderTexture(waveTarget);
  364. // Unload sounds
  365. UnloadSound(fxSampleOn);
  366. UnloadSound(fxSampleOff);
  367. UnloadSound(fxPause);
  368. free(samples); // Unload game samples
  369. }
  370. // Gameplay Screen should finish?
  371. int FinishGameplayScreen(void)
  372. {
  373. return finishScreen;
  374. }
  375. //------------------------------------------------------------------------------------
  376. // Module Functions Definitions (local)
  377. //------------------------------------------------------------------------------------
  378. // Draw samples in wave form (including already played samples in a different color!)
  379. // NOTE: For proper visualization, MSAA x4 is recommended, alternatively
  380. // it should be rendered to a bigger texture and then scaled down with
  381. // bilinear/trilinear texture filtering
  382. static void DrawSamplesMap(Sample *samples, int sampleCount, int playedSamples, Rectangle bounds, Color color)
  383. {
  384. // NOTE: We just pick a sample to draw every increment
  385. float sampleIncrementX = (float)bounds.width/sampleCount;
  386. Color col = color;
  387. for (int i = 0; i < sampleCount - 1; i++)
  388. {
  389. if (i < playedSamples) col = GRAY;
  390. else col = color;
  391. DrawLineV((Vector2){ (float)bounds.x + (float)i*sampleIncrementX, (float)(bounds.y + bounds.height/2) + samples[i].value*bounds.height },
  392. (Vector2){ (float)bounds.x + (float)(i + 1)*sampleIncrementX, (float)(bounds.y + bounds.height/2) + + samples[i + 1].value*bounds.height }, col);
  393. }
  394. }