Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

155 rader
7.5 KiB

11 månader sedan
  1. /*******************************************************************************************
  2. *
  3. * raylib [textures] example - Fog of war
  4. *
  5. * Example originally created with raylib 4.2, last time updated with raylib 4.2
  6. *
  7. * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
  8. * BSD-like license that allows static linking with closed source software
  9. *
  10. * Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)
  11. *
  12. ********************************************************************************************/
  13. #include "raylib.h"
  14. #include <stdlib.h> // Required for: calloc(), free()
  15. #define MAP_TILE_SIZE 32 // Tiles size 32x32 pixels
  16. #define PLAYER_SIZE 16 // Player size
  17. #define PLAYER_TILE_VISIBILITY 2 // Player can see 2 tiles around its position
  18. // Map data type
  19. typedef struct Map {
  20. unsigned int tilesX; // Number of tiles in X axis
  21. unsigned int tilesY; // Number of tiles in Y axis
  22. unsigned char *tileIds; // Tile ids (tilesX*tilesY), defines type of tile to draw
  23. unsigned char *tileFog; // Tile fog state (tilesX*tilesY), defines if a tile has fog or half-fog
  24. } Map;
  25. //------------------------------------------------------------------------------------
  26. // Program main entry point
  27. //------------------------------------------------------------------------------------
  28. int main(void)
  29. {
  30. // Initialization
  31. //--------------------------------------------------------------------------------------
  32. int screenWidth = 800;
  33. int screenHeight = 450;
  34. InitWindow(screenWidth, screenHeight, "raylib [textures] example - fog of war");
  35. Map map = { 0 };
  36. map.tilesX = 25;
  37. map.tilesY = 15;
  38. // NOTE: We can have up to 256 values for tile ids and for tile fog state,
  39. // probably we don't need that many values for fog state, it can be optimized
  40. // to use only 2 bits per fog state (reducing size by 4) but logic will be a bit more complex
  41. map.tileIds = (unsigned char *)calloc(map.tilesX*map.tilesY, sizeof(unsigned char));
  42. map.tileFog = (unsigned char *)calloc(map.tilesX*map.tilesY, sizeof(unsigned char));
  43. // Load map tiles (generating 2 random tile ids for testing)
  44. // NOTE: Map tile ids should be probably loaded from an external map file
  45. for (unsigned int i = 0; i < map.tilesY*map.tilesX; i++) map.tileIds[i] = GetRandomValue(0, 1);
  46. // Player position on the screen (pixel coordinates, not tile coordinates)
  47. Vector2 playerPosition = { 180, 130 };
  48. int playerTileX = 0;
  49. int playerTileY = 0;
  50. // Render texture to render fog of war
  51. // NOTE: To get an automatic smooth-fog effect we use a render texture to render fog
  52. // at a smaller size (one pixel per tile) and scale it on drawing with bilinear filtering
  53. RenderTexture2D fogOfWar = LoadRenderTexture(map.tilesX, map.tilesY);
  54. SetTextureFilter(fogOfWar.texture, TEXTURE_FILTER_BILINEAR);
  55. SetTargetFPS(60); // Set our game to run at 60 frames-per-second
  56. //--------------------------------------------------------------------------------------
  57. // Main game loop
  58. while (!WindowShouldClose()) // Detect window close button or ESC key
  59. {
  60. // Update
  61. //----------------------------------------------------------------------------------
  62. // Move player around
  63. if (IsKeyDown(KEY_RIGHT)) playerPosition.x += 5;
  64. if (IsKeyDown(KEY_LEFT)) playerPosition.x -= 5;
  65. if (IsKeyDown(KEY_DOWN)) playerPosition.y += 5;
  66. if (IsKeyDown(KEY_UP)) playerPosition.y -= 5;
  67. // Check player position to avoid moving outside tilemap limits
  68. if (playerPosition.x < 0) playerPosition.x = 0;
  69. else if ((playerPosition.x + PLAYER_SIZE) > (map.tilesX*MAP_TILE_SIZE)) playerPosition.x = (float)map.tilesX*MAP_TILE_SIZE - PLAYER_SIZE;
  70. if (playerPosition.y < 0) playerPosition.y = 0;
  71. else if ((playerPosition.y + PLAYER_SIZE) > (map.tilesY*MAP_TILE_SIZE)) playerPosition.y = (float)map.tilesY*MAP_TILE_SIZE - PLAYER_SIZE;
  72. // Previous visited tiles are set to partial fog
  73. for (unsigned int i = 0; i < map.tilesX*map.tilesY; i++) if (map.tileFog[i] == 1) map.tileFog[i] = 2;
  74. // Get current tile position from player pixel position
  75. playerTileX = (int)((playerPosition.x + MAP_TILE_SIZE/2)/MAP_TILE_SIZE);
  76. playerTileY = (int)((playerPosition.y + MAP_TILE_SIZE/2)/MAP_TILE_SIZE);
  77. // Check visibility and update fog
  78. // NOTE: We check tilemap limits to avoid processing tiles out-of-array-bounds (it could crash program)
  79. for (int y = (playerTileY - PLAYER_TILE_VISIBILITY); y < (playerTileY + PLAYER_TILE_VISIBILITY); y++)
  80. for (int x = (playerTileX - PLAYER_TILE_VISIBILITY); x < (playerTileX + PLAYER_TILE_VISIBILITY); x++)
  81. if ((x >= 0) && (x < (int)map.tilesX) && (y >= 0) && (y < (int)map.tilesY)) map.tileFog[y*map.tilesX + x] = 1;
  82. //----------------------------------------------------------------------------------
  83. // Draw
  84. //----------------------------------------------------------------------------------
  85. // Draw fog of war to a small render texture for automatic smoothing on scaling
  86. BeginTextureMode(fogOfWar);
  87. ClearBackground(BLANK);
  88. for (unsigned int y = 0; y < map.tilesY; y++)
  89. for (unsigned int x = 0; x < map.tilesX; x++)
  90. if (map.tileFog[y*map.tilesX + x] == 0) DrawRectangle(x, y, 1, 1, BLACK);
  91. else if (map.tileFog[y*map.tilesX + x] == 2) DrawRectangle(x, y, 1, 1, Fade(BLACK, 0.8f));
  92. EndTextureMode();
  93. BeginDrawing();
  94. ClearBackground(RAYWHITE);
  95. for (unsigned int y = 0; y < map.tilesY; y++)
  96. {
  97. for (unsigned int x = 0; x < map.tilesX; x++)
  98. {
  99. // Draw tiles from id (and tile borders)
  100. DrawRectangle(x*MAP_TILE_SIZE, y*MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE,
  101. (map.tileIds[y*map.tilesX + x] == 0)? BLUE : Fade(BLUE, 0.9f));
  102. DrawRectangleLines(x*MAP_TILE_SIZE, y*MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE, Fade(DARKBLUE, 0.5f));
  103. }
  104. }
  105. // Draw player
  106. DrawRectangleV(playerPosition, (Vector2){ PLAYER_SIZE, PLAYER_SIZE }, RED);
  107. // Draw fog of war (scaled to full map, bilinear filtering)
  108. DrawTexturePro(fogOfWar.texture, (Rectangle){ 0, 0, (float)fogOfWar.texture.width, (float)-fogOfWar.texture.height },
  109. (Rectangle){ 0, 0, (float)map.tilesX*MAP_TILE_SIZE, (float)map.tilesY*MAP_TILE_SIZE },
  110. (Vector2){ 0, 0 }, 0.0f, WHITE);
  111. // Draw player current tile
  112. DrawText(TextFormat("Current tile: [%i,%i]", playerTileX, playerTileY), 10, 10, 20, RAYWHITE);
  113. DrawText("ARROW KEYS to move", 10, screenHeight-25, 20, RAYWHITE);
  114. EndDrawing();
  115. //----------------------------------------------------------------------------------
  116. }
  117. // De-Initialization
  118. //--------------------------------------------------------------------------------------
  119. free(map.tileIds); // Free allocated map tile ids
  120. free(map.tileFog); // Free allocated map tile fog state
  121. UnloadRenderTexture(fogOfWar); // Unload render texture
  122. CloseWindow(); // Close window and OpenGL context
  123. //--------------------------------------------------------------------------------------
  124. return 0;
  125. }