diff --git a/tutorials/flappy-bird/step_3/CMakeLists.txt b/tutorials/flappy-bird/step_3/CMakeLists.txt new file mode 100644 index 00000000..3336581e --- /dev/null +++ b/tutorials/flappy-bird/step_3/CMakeLists.txt @@ -0,0 +1,78 @@ +if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR "Prevented in-tree build. Please create a build directory outside of the source code and call cmake from there") +endif () + +##! Minimum version of the CMake. +cmake_minimum_required(VERSION 3.14) + +##! C++ Standard needed by the SDK is 17 +set(CMAKE_CXX_STANDARD 17) + +##! Our Project title, here flappy-bird. +project(flappy-bird DESCRIPTION "An awesome flappy-bird" LANGUAGES CXX) + +##! The SDK need's clang as main compiler. +if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + message(FATAL_ERROR "Only Clang is supported (minimum LLVM 8.0)") + endif() +endif () + +##! We will let know the SDK if our on Linux +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set(LINUX TRUE) +endif () + +##! We include the module from CMake for fetching dependencies +include(FetchContent) + +##! We declare information about the dependance that we want to fetch. +FetchContent_Declare( + antara-gaming-sdk + URL https://github.com/KomodoPlatform/antara-gaming-sdk/archive/master.zip +) + +##! We set extras modules from the SDK that we want to use, here we will use the SFML module. +set(USE_SFML_ANTARA_WRAPPER ON) + +##! We fetch our dependence +FetchContent_MakeAvailable(antara-gaming-sdk) + +##! Calling this macros provided by the sdk will if you are on Apple init the environment for this OS (std::filesystem). +init_apple_env() + +##! Osx bundle icon +set(ICON) +configure_icon_osx(data/osx/kmd_logo.icns ICON kmd_logo.icns) + +##! We create the executable with the project name +add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${ICON} flappy-bird.cpp) + +##! Setting output directory +set_target_properties(${PROJECT_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/" + ) + +##! We link the SDK modules that we want to use to our executable +target_link_libraries(${PROJECT_NAME} PUBLIC antara::world antara::sfml antara::collisions) + +##! Move assets +if (WIN32) + file(COPY assets DESTINATION ${CMAKE_BINARY_DIR}/bin/) + ADD_CUSTOM_COMMAND(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory "${SFML_BINARY_DIR}/lib" "${CMAKE_BINARY_DIR}/bin/" + COMMENT "copying dlls …" + $ + ) + + ADD_CUSTOM_COMMAND(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${SFML_SOURCE_DIR}/extlibs/bin/x64/openal32.dll" "${CMAKE_BINARY_DIR}/bin/openal32.dll" + COMMENT "copying dlls …" + $ + ) +endif () + +if (APPLE) + file(COPY assets DESTINATION ${CMAKE_BINARY_DIR}/bin/${PROJECT_NAME}.app/Contents/Resources) +endif() \ No newline at end of file diff --git a/tutorials/flappy-bird/step_3/assets/config/game.config.maker.json b/tutorials/flappy-bird/step_3/assets/config/game.config.maker.json new file mode 100644 index 00000000..6690ed44 --- /dev/null +++ b/tutorials/flappy-bird/step_3/assets/config/game.config.maker.json @@ -0,0 +1 @@ +{"background_color":[0,0,0,255],"canvas_height":1080.0,"canvas_width":1920.0,"custom_canvas_height":true,"custom_canvas_width":true,"native_desktop_mode":false,"scale_mode":"fit","window_height":1080.0,"window_title":"game title","window_width":1920.0} \ No newline at end of file diff --git a/tutorials/flappy-bird/step_3/assets/fonts/sansation.ttf b/tutorials/flappy-bird/step_3/assets/fonts/sansation.ttf new file mode 100644 index 00000000..d85fbc81 Binary files /dev/null and b/tutorials/flappy-bird/step_3/assets/fonts/sansation.ttf differ diff --git a/tutorials/flappy-bird/step_3/data/linux/komodo_icon.png b/tutorials/flappy-bird/step_3/data/linux/komodo_icon.png new file mode 100644 index 00000000..89e5dd28 Binary files /dev/null and b/tutorials/flappy-bird/step_3/data/linux/komodo_icon.png differ diff --git a/tutorials/flappy-bird/step_3/data/linux/org.antara.gaming.sfml.flappybird.appdata.xml b/tutorials/flappy-bird/step_3/data/linux/org.antara.gaming.sfml.flappybird.appdata.xml new file mode 100644 index 00000000..7e6a1046 --- /dev/null +++ b/tutorials/flappy-bird/step_3/data/linux/org.antara.gaming.sfml.flappybird.appdata.xml @@ -0,0 +1,20 @@ + + org.antara.gaming.sfml.flappybird.desktop + MIT + MIT + flappy-bird + flappy-bird tutorial antara gaming sdk + +

Written in c++17

+
+ org.antara.gaming.sfml.flappybird.desktop + https://github.com/KomodoPlatform/antara-gaming-sdk + + + https://www.freedesktop.org/software/appstream/docs/images/scr-examples/geany-good.png + + + + org.antara.gaming.sfml.flappybird.desktop + +
\ No newline at end of file diff --git a/tutorials/flappy-bird/step_3/data/linux/org.antara.gaming.sfml.flappybird.desktop b/tutorials/flappy-bird/step_3/data/linux/org.antara.gaming.sfml.flappybird.desktop new file mode 100644 index 00000000..d525c088 --- /dev/null +++ b/tutorials/flappy-bird/step_3/data/linux/org.antara.gaming.sfml.flappybird.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=flappy-bird +Exec=flappy-bird +Icon=komodo_icon +Categories=Game; \ No newline at end of file diff --git a/tutorials/flappy-bird/step_3/data/osx/Packaging_CMakeDMGBackground.tif b/tutorials/flappy-bird/step_3/data/osx/Packaging_CMakeDMGBackground.tif new file mode 100644 index 00000000..91c4b130 Binary files /dev/null and b/tutorials/flappy-bird/step_3/data/osx/Packaging_CMakeDMGBackground.tif differ diff --git a/tutorials/flappy-bird/step_3/data/osx/Packaging_CMakeDMGSetup.scpt b/tutorials/flappy-bird/step_3/data/osx/Packaging_CMakeDMGSetup.scpt new file mode 100644 index 00000000..2bea22d8 --- /dev/null +++ b/tutorials/flappy-bird/step_3/data/osx/Packaging_CMakeDMGSetup.scpt @@ -0,0 +1,57 @@ +on run argv + set image_name to item 1 of argv + + tell application "Finder" + tell disk image_name + + -- wait for the image to finish mounting + set open_attempts to 0 + repeat while open_attempts < 4 + try + open + delay 1 + set open_attempts to 5 + close + on error errStr number errorNumber + set open_attempts to open_attempts + 1 + delay 10 + end try + end repeat + delay 5 + + -- open the image the first time and save a DS_Store with just + -- background and icon setup + open + set current view of container window to icon view + set theViewOptions to the icon view options of container window + set background picture of theViewOptions to file ".background:background.tif" + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to 128 + delay 5 + close + + -- next setup the position of the app and Applications symlink + -- plus hide all the window decorationPackaging_CMakeDMGBackground.tif + open + update without registering applications + tell container window + set sidebar width to 0 + set statusbar visible to false + set toolbar visible to false + set the bounds to { 400, 100, 900, 465 } + set position of item "flappy-bird.app" to { 133, 200 } + set position of item "Applications" to { 378, 200 } + end tell + update without registering applications + delay 5 + close + + -- one last open and close so you can see everything looks correct + open + delay 5 + close + + end tell + delay 1 +end tell +end run \ No newline at end of file diff --git a/tutorials/flappy-bird/step_3/data/osx/kmd_logo.icns b/tutorials/flappy-bird/step_3/data/osx/kmd_logo.icns new file mode 100644 index 00000000..5977224d Binary files /dev/null and b/tutorials/flappy-bird/step_3/data/osx/kmd_logo.icns differ diff --git a/tutorials/flappy-bird/step_3/data/osx/sfml_flappybird_install.cmake b/tutorials/flappy-bird/step_3/data/osx/sfml_flappybird_install.cmake new file mode 100644 index 00000000..2bda51ae --- /dev/null +++ b/tutorials/flappy-bird/step_3/data/osx/sfml_flappybird_install.cmake @@ -0,0 +1,35 @@ +if (APPLE) + set_target_properties(${PROJECT_NAME} PROPERTIES + MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}" + RESOURCE data/osx/${PROJECT_NAME}.icns + MACOSX_BUNDLE_ICON_FILE ${PROJECT_NAME} + MACOSX_BUNDLE_SHORT_VERSION_STRING 0.0.1 + MACOSX_BUNDLE_LONG_VERSION_STRING 0.0.1 + MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in") + add_custom_command(TARGET ${PROJECT_NAME} + POST_BUILD COMMAND + ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" + $) +endif () + +if (APPLE) + install(TARGETS ${PROJECT_NAME} + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime + ) + + # Note Mac specific extension .app + set(APPS "\${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}.app") + + # Directories to look for dependencies + set(DIRS ${CMAKE_BINARY_DIR}) + + install(CODE "include(BundleUtilities) + fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")") + + set(CPACK_GENERATOR "DRAGNDROP") + set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/data/osx/Packaging_CMakeDMGSetup.scpt") + set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_CURRENT_SOURCE_DIR}/data/osx/Packaging_CMakeDMGBackground.tif") + set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") + include(CPack) +endif () \ No newline at end of file diff --git a/tutorials/flappy-bird/step_3/flappy-bird.cpp b/tutorials/flappy-bird/step_3/flappy-bird.cpp new file mode 100644 index 00000000..a9b7ac95 --- /dev/null +++ b/tutorials/flappy-bird/step_3/flappy-bird.cpp @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// For convenience +using namespace antara::gaming; +using namespace std::string_literals; + +// Constants +struct flappy_bird_constants { + // Pipes + const float gap_height{265.f}; + const float column_start_distance{700.f}; + const float column_min{0.2f}; + const float column_max{0.8f}; + const float column_thickness{100.f}; + const float column_distance{400.f}; + const std::size_t column_count{6}; + const float pipe_cap_extra_width{10.f}; + const float pipe_cap_height{50.f}; + const graphics::color pipe_color{92, 181, 61}; + const graphics::outline_color pipe_outline_color{2.0f, graphics::color{76, 47, 61}}; + + // Background + const float ground_thickness{100.0f}; + const float grass_thickness{20.0f}; + const graphics::color background_color{82, 189, 199}; + const graphics::color ground_color{220, 209, 143}; + const graphics::color grass_color{132, 227, 90}; + const graphics::outline_color grass_outline_color{2.0f, graphics::color{76, 47, 61}}; +}; + +// Random number generator +namespace { + std::random_device rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + float random_float(float lower, float higher) { + std::uniform_real_distribution dist(lower, higher); + return dist(gen); + } +} + +// A Flappy Bird column which has two pipes +struct pipe { + entt::entity body{entt::null}; + entt::entity cap{entt::null}; + + // Destroy pipe + void destroy(entt::registry ®istry) { + registry.destroy(body); + registry.destroy(cap); + } +}; + +// Column is made of two pipes +struct column { + // Entities representing the Flappy Bird pipes + pipe top_pipe{entt::null}; + pipe bottom_pipe{entt::null}; + + // Destroy pipes and this column + void destroy(entt::registry ®istry, entt::entity entity) { + top_pipe.destroy(registry); + bottom_pipe.destroy(registry); + registry.destroy(entity); + } +}; + +// Logic functions +namespace { + void tag_game_scene(entt::registry ®istry, entt::entity entity, bool dynamic = false) { + // Tag game scene + registry.assign>(entity); + + // Tag dynamic + if(dynamic) registry.assign>(entity); + } + + // Returns a random gap start position Y + float get_random_gap_start_pos(const entt::registry ®istry) { + // Retrieve constants + const auto canvas_height = registry.ctx().canvas.size.y(); + const auto constants = registry.ctx(); + + float top_limit = canvas_height * constants.column_min; + float bottom_limit = canvas_height * constants.column_max - constants.gap_height; + + return random_float(top_limit, bottom_limit); + } +} + +// Factory functions +namespace { + // Factory for pipes, requires to know if it's a top one, position x of the column, and the gap starting position Y + pipe create_pipe(entt::registry ®istry, bool is_top, float pos_x, float gap_start_pos_y) { + // Retrieve constants + const auto canvas_height = registry.ctx().canvas.size.y(); + const auto constants = registry.ctx(); + + // PIPE BODY + // Top pipe is at Y: 0 and bottom pipe is at canvas_height, bottom of the canvas + transform::position_2d body_pos{pos_x, is_top ? 0.f : canvas_height}; + + // Size X is the column thickness, + // Size Y is the important part. + // If it's a top pipe, gap_start_pos_y should be bottom of the rectangle + // So half size should be gap_start_pos_y since center of the rectangle is at 0. + // If it's the bottom pipe, top of the rectangle will be at gap_start_pos_y + gap_height + // So half size should be canvas_height - (gap_start_pos_y + gap_height) + // Since these are half-sizes, and the position is at the screen border, we multiply these sizes by two + math::vec2f body_size{constants.column_thickness, + is_top ? + gap_start_pos_y * 2.0f : + (canvas_height - (gap_start_pos_y + constants.gap_height)) * 2.0f}; + + auto body = geometry::blueprint_rectangle(registry, body_size, constants.pipe_color, body_pos, + constants.pipe_outline_color); + + // PIPE CAP + // Let's prepare the pipe cap + // Size of the cap is defined in constants + math::vec2f cap_size{constants.column_thickness + constants.pipe_cap_extra_width, constants.pipe_cap_height}; + + // Position, X is same as the body. Bottom of the cap is aligned with bottom of the body, + // or start of the gap, we will use start of the gap here, minus half of the cap height + transform::position_2d cap_pos{body_pos.x(), + is_top ? + gap_start_pos_y - constants.pipe_cap_height * 0.5f : + gap_start_pos_y + constants.gap_height + constants.pipe_cap_height * 0.5f + }; + + // Construct the cap + auto cap = geometry::blueprint_rectangle(registry, cap_size, constants.pipe_color, cap_pos, + constants.pipe_outline_color); + + // Set layers, cap should be in front of body + registry.assign>(cap); + registry.assign>(body); + tag_game_scene(registry, cap, true); + tag_game_scene(registry, body, true); + + // Construct a pipe with body and cap and return it + return {body, cap}; + } + + // Factory to create single column + void create_column(entt::registry ®istry, float pos_x) noexcept { + // Create a fresh entity for a new column + auto entity_column = registry.create(); + + // Get a random gap start position Y, between pipes + float gap_start_pos_y = get_random_gap_start_pos(registry); + + // Create pipes, is_top variable is false for bottom one + auto top_pipe = create_pipe(registry, true, pos_x, gap_start_pos_y); + auto bottom_pipe = create_pipe(registry, false, pos_x, gap_start_pos_y); + + // Make a column from these two pipes and mark it as "column" + registry.assign(entity_column, top_pipe, bottom_pipe); + registry.assign>(entity_column); + tag_game_scene(registry, entity_column, true); + } + + // Factory for creating a Flappy Bird columns + void create_columns(entt::registry ®istry) noexcept { + // Retrieve constants + const auto constants = registry.ctx(); + + // Spawn columns out of the screen, out of the canvas + const float column_pos_offset = constants.column_start_distance + constants.column_thickness * 2.0f; + + // Create the columns + for (std::size_t i = 0; i < constants.column_count; ++i) { + // Horizontal position (X) increases for every column, keeping the distance + float pos_x = column_pos_offset + i * constants.column_distance; + + create_column(registry, pos_x); + } + } + + // Factory for creating a Flappy Bird background + void create_background(entt::registry ®istry) noexcept { + // Retrieve constants + const auto[canvas_width, canvas_height] = registry.ctx().canvas.size; + const auto constants = registry.ctx(); + + // Create Sky + { + // Sky is whole canvas so position is middle of it + transform::position_2d pos{canvas_width * 0.5f, canvas_height * 0.5f}; + + // And the size is full canvas + math::vec2f size{canvas_width, canvas_height}; + + auto sky = geometry::blueprint_rectangle(registry, size, constants.background_color, pos); + registry.assign>(sky); + tag_game_scene(registry, sky); + } + + // Create Grass + { + // Ground expands to whole canvas width so position is middle of it, + // But position Y is at top of the ground, so it's canvas height minus ground thickness + transform::position_2d pos{canvas_width * 0.5f, canvas_height - constants.ground_thickness}; + + // Size X is full canvas but the height is defined in constants + // We also make it a bit longer by adding the thickness of the outline to hide the outline at sides + math::vec2f size{canvas_width + constants.grass_outline_color.thickness * 2.0f, constants.grass_thickness}; + + auto grass = geometry::blueprint_rectangle(registry, size, constants.grass_color, pos, + constants.grass_outline_color); + registry.assign>(grass); + tag_game_scene(registry, grass); + } + + // Create Ground + { + // Ground expands to whole canvas width so position is middle of it, + // But position Y is at bottom of the screen so it's full canvas_height minus half of the ground thickness + transform::position_2d pos{canvas_width * 0.5f, canvas_height - constants.ground_thickness * 0.5f}; + + // Size X is full canvas but the height is defined in constants + math::vec2f size{canvas_width, constants.ground_thickness}; + + auto ground = geometry::blueprint_rectangle(registry, size, constants.ground_color, pos); + registry.assign>(ground); + tag_game_scene(registry, ground); + } + } +} + +// Game Scene +class game_scene final : public scenes::base_scene { +public: + game_scene(entt::registry ®istry) noexcept : base_scene(registry) { + // Set the constants that will be used in the program + registry.set(); + + // Create everything + create_background(registry); + init_dynamic_objects(registry); + } + + // Scene name + std::string scene_name() noexcept final { + return "game_scene"; + } + +private: + // Update the game every tick + void update() noexcept final { + } + + // Initialize dynamic objects, this function is called at start and resets + void init_dynamic_objects(entt::registry ®istry) { + create_columns(registry); + } +}; + +// Game world +struct flappy_bird_world : world::app { + // Game entry point + flappy_bird_world() noexcept { + // Load the graphical system + auto &graphic_system = system_manager_.create_system(); + + // Load the resources system + entity_registry_.set(entity_registry_); + + // Load the input system with the window from the graphical system + system_manager_.create_system(graphic_system.get_window()); + + // Load the scenes manager + auto &scene_manager = system_manager_.create_system(); + + // Change the current_scene to "game_scene" by pushing it. + scene_manager.change_scene(std::make_unique(entity_registry_), true); + } +}; + +int main() { + // Declare the world + flappy_bird_world game; + + // Run the game + return game.run(); +} \ No newline at end of file