Adds application files
37
.clang-format
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
# This configuration requires clang-format 3.8 or higher.
|
||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: Mozilla
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
AccessModifierOffset: '0'
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: 'true'
|
||||||
|
AlignConsecutiveDeclarations: 'true'
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: 'true'
|
||||||
|
AlignTrailingComments: 'true'
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: 'false'
|
||||||
|
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortLoopsOnASingleLine: 'false'
|
||||||
|
AlwaysBreakBeforeMultilineStrings: 'false'
|
||||||
|
BreakBeforeBraces: Allman
|
||||||
|
BreakBeforeTernaryOperators: 'true'
|
||||||
|
ColumnLimit: '0'
|
||||||
|
FixNamespaceComments: 'true'
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IndentCaseLabels: 'true'
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
IndentWidth: '2'
|
||||||
|
NamespaceIndentation: None
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: 'true'
|
||||||
|
SortIncludes: 'true'
|
||||||
|
SortUsingDeclarations: 'true'
|
||||||
|
SpacesBeforeTrailingComments: 5
|
||||||
|
TabWidth: '2'
|
||||||
|
UseTab: Never
|
||||||
|
|
||||||
|
...
|
||||||
59
.gitignore
vendored
@@ -1,3 +1,57 @@
|
|||||||
|
# ---> C
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Linker output
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
|
||||||
# ---> C++
|
# ---> C++
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
*.d
|
*.d
|
||||||
@@ -31,4 +85,9 @@
|
|||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Editor's mess
|
||||||
|
.vscode/
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
|||||||
288
CMakeLists.txt
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
cmake_minimum_required (VERSION 3.23)
|
||||||
|
|
||||||
|
#--- v1.0.0 ---
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Setup compiler settings ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Set C language standard
|
||||||
|
set(CMAKE_C_STANDARD 17)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_C_EXTENSIONS OFF) # Only standard features, no compiler-specific extensions
|
||||||
|
|
||||||
|
# Set C++ language standard
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF) # Only standard features, no compiler-specific extensions
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Project configuration ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Define the project settings
|
||||||
|
project("quokkaNoid")
|
||||||
|
|
||||||
|
# Source directory
|
||||||
|
set(SRC_DIR "src")
|
||||||
|
|
||||||
|
# Resource files (add resource files here if needed)
|
||||||
|
set(RESOURCE_FILES "")
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Sanity checks ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Ensure build type is set (Debug, Release, etc.)
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
message(FATAL_ERROR "CMAKE_BUILD_TYPE must be set")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Ensure DEV_LIB environment variable is defined (used for external libraries)
|
||||||
|
if(NOT DEFINED ENV{DEV_LIB})
|
||||||
|
message(FATAL_ERROR "DEV_LIB environment variable must be defined")
|
||||||
|
endif()
|
||||||
|
set(DEV_LIB $ENV{DEV_LIB})
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Include directories ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# General include directories (add your common include paths here)
|
||||||
|
set(GENERAL_INCLUDE_DIRS
|
||||||
|
"src"
|
||||||
|
"sdi_toolBox_1.0.x"
|
||||||
|
"${DEV_LIB}/boost_1_87_0"
|
||||||
|
"${DEV_LIB}/SDL2/SDL2-2.30.4/include"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional include directories for Debug configuration
|
||||||
|
set(DEBUG_INCLUDE_DIRS
|
||||||
|
# "path/to/debug/include"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional include directories for Release configuration
|
||||||
|
set(RELEASE_INCLUDE_DIRS
|
||||||
|
# "path/to/debug/include"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Library directories ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# General library directories (add your common library paths here)
|
||||||
|
set(GENERAL_LIBRARY_DIRS
|
||||||
|
${CMAKE_BINARY_DIR}
|
||||||
|
"${DEV_LIB}/boost_1_87_0/stage/lib"
|
||||||
|
"${DEV_LIB}/SDL2/SDL2-2.30.4/lib/x64"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional library directories for Debug configuration
|
||||||
|
set(DEBUG_LIBRARY_DIRS
|
||||||
|
# "path/to/debug/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional library directories for Release configuration
|
||||||
|
set(RELEASE_LIBRARY_DIRS
|
||||||
|
# "path/to/debug/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Preprocessor definitions ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# General preprocessor definitions (add your common defines here)
|
||||||
|
set(GENERAL_PREPROCESSOR_DEFINITIONS
|
||||||
|
"_CRT_SECURE_NO_DEPRECATE"
|
||||||
|
"_CRT_NONSTDC_NO_DEPRECATE"
|
||||||
|
"_UNICODE"
|
||||||
|
"_WINDOWS"
|
||||||
|
"NOMINMAX"
|
||||||
|
"UNICODE"
|
||||||
|
"WIN32"
|
||||||
|
"WIN32_LEAN_AND_MEAN"
|
||||||
|
|
||||||
|
#"SDL_STATIC"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional preprocessor definitions for Debug configuration
|
||||||
|
set(DEBUG_PREPROCESSOR_DEFINITIONS
|
||||||
|
"_DEBUG"
|
||||||
|
"DEBUG"
|
||||||
|
"WXDEBUG"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional preprocessor definitions for Release configuration
|
||||||
|
set(RELEASE_PREPROCESSOR_DEFINITIONS
|
||||||
|
"NDEBUG"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Libraries and DLLs ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# General libraries to link (add your common libraries here)
|
||||||
|
set(GENERAL_LIB
|
||||||
|
"SDL2main.lib"
|
||||||
|
"SDL2.lib"
|
||||||
|
"SDL2_image.lib"
|
||||||
|
"SDL2_ttf.lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional libraries for Debug configuration
|
||||||
|
set(DEBUG_LIB
|
||||||
|
# "libDebug.lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional libraries for Release configuration
|
||||||
|
set(RELEASE_LIB
|
||||||
|
# "libRelease.lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
# General DLLs to copy after build (add your common DLLs here)
|
||||||
|
set(GENERAL_BIN
|
||||||
|
"${DEV_LIB}/SDL2/SDL2-2.30.4/lib/x64/SDL2.dll"
|
||||||
|
"${DEV_LIB}/SDL2/SDL2-2.30.4/lib/x64/SDL2_image.dll"
|
||||||
|
"${DEV_LIB}/SDL2/SDL2-2.30.4/lib/x64/SDL2_ttf.dll"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional DLLs for Debug configuration
|
||||||
|
set(DEBUG_BIN
|
||||||
|
# "path/to/debug/dll"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Additional DLLs for Release configuration
|
||||||
|
set(RELEASE_BIN
|
||||||
|
# "path/to/release/dll"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- MSVC Debug Information Format Policy ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Ensure CMake policy CMP0141 is set to NEW to control the MSVC debug information format.
|
||||||
|
# This sets CMAKE_MSVC_DEBUG_INFORMATION_FORMAT to "EditAndContinue" for Debug and RelWithDebInfo configurations,
|
||||||
|
# and to "ProgramDatabase" for other configurations, but only when using the MSVC compiler.
|
||||||
|
if (POLICY CMP0141)
|
||||||
|
cmake_policy(SET CMP0141 NEW)
|
||||||
|
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Source files gathering ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Collect all C and C++ source files recursively
|
||||||
|
file(GLOB_RECURSE SOURCES_C "${SRC_DIR}/*.c")
|
||||||
|
file(GLOB_RECURSE SOURCES_CPP "${SRC_DIR}/*.cpp")
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Target definition ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Define the main executable target
|
||||||
|
add_executable(${PROJECT_NAME}
|
||||||
|
${SOURCES_C}
|
||||||
|
${SOURCES_CPP}
|
||||||
|
${RESOURCE_FILES}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Target properties setup ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Setup include directories
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||||
|
${GENERAL_INCLUDE_DIRS}
|
||||||
|
$<$<CONFIG:Debug>:${DEBUG_INCLUDE_DIRS}>
|
||||||
|
$<$<CONFIG:Release>:${RELEASE_INCLUDE_DIRS}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup library directories
|
||||||
|
target_link_directories(${PROJECT_NAME} PRIVATE
|
||||||
|
${GENERAL_LIBRARY_DIRS}
|
||||||
|
$<$<CONFIG:Debug>:${DEBUG_LIBRARY_DIRS}>
|
||||||
|
$<$<CONFIG:Release>:${RELEASE_LIBRARY_DIRS}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup preprocessor definitions
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||||
|
${GENERAL_PREPROCESSOR_DEFINITIONS}
|
||||||
|
$<$<CONFIG:Debug>:${DEBUG_PREPROCESSOR_DEFINITIONS}>
|
||||||
|
$<$<CONFIG:Release>:${RELEASE_PREPROCESSOR_DEFINITIONS}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup linked libraries
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
|
${GENERAL_LIB}
|
||||||
|
$<$<CONFIG:Debug>:${DEBUG_LIB}>
|
||||||
|
$<$<CONFIG:Release>:${RELEASE_LIB}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setup compiler and linker options (MSVC specific)
|
||||||
|
if(MSVC)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /MP)
|
||||||
|
target_link_options(${PROJECT_NAME} PRIVATE "/ignore:4099" /PROFILE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Post-build: Copy DLLs ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Copy DLLs to the output directory after build
|
||||||
|
if(GENERAL_BIN OR DEBUG_BIN OR RELEASE_BIN)
|
||||||
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
${GENERAL_BIN}
|
||||||
|
$<$<CONFIG:Debug>:${DEBUG_BIN}>
|
||||||
|
$<$<CONFIG:Release>:${RELEASE_BIN}>
|
||||||
|
${CMAKE_BINARY_DIR}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------
|
||||||
|
#--- Project compilation log ---
|
||||||
|
#------------------------------------------------
|
||||||
|
# Print project and environment information for diagnostics
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "--- Platform Information --------------------------")
|
||||||
|
message(STATUS "System Name: ${CMAKE_SYSTEM_NAME}")
|
||||||
|
message(STATUS "Processor: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
message(STATUS "CMake Generator: ${CMAKE_GENERATOR}")
|
||||||
|
message(STATUS "Install Prefix: ${CMAKE_INSTALL_PREFIX}")
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "--- Compiler/Language Settings --------------------")
|
||||||
|
message(STATUS "C++ Standard: ${CMAKE_CXX_STANDARD}")
|
||||||
|
message(STATUS "Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
|
||||||
|
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
message(STATUS "Common Compiler Flags: ${CMAKE_CXX_FLAGS}")
|
||||||
|
message(STATUS "Debug Compiler Flags: ${CMAKE_CXX_FLAGS_DEBUG}")
|
||||||
|
message(STATUS "Release Compiler Flags: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
message(STATUS "Common Linker Flags: ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
message(STATUS "Debug Linker Flags: ${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
|
||||||
|
message(STATUS "Release Linker Flags: ${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "--- Path Information ------------------------------")
|
||||||
|
message(STATUS "Source Dir: ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
message(STATUS "Binary Dir: ${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
if(MSVC)
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "--- Microsoft Visual C++ (MSVC) Information -------")
|
||||||
|
# Check the major compiler version
|
||||||
|
if(MSVC_VERSION GREATER_EQUAL 1950)
|
||||||
|
message(STATUS "MSVC Compiler Version: ${MSVC_VERSION} (Visual Studio 2026 or newer)")
|
||||||
|
elseif(MSVC_VERSION GREATER_EQUAL 1930)
|
||||||
|
message(STATUS "MSVC Compiler Version: ${MSVC_VERSION} (Visual Studio 2022)")
|
||||||
|
endif()
|
||||||
|
message(STATUS "MSVC Toolset Version: ${MSVC_TOOLSET_VERSION}")
|
||||||
|
message(STATUS "CPP Compiler Version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||||
|
message(STATUS "C Compiler Version: ${CMAKE_C_COMPILER_VERSION}")
|
||||||
|
endif()
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "--- Project Information ---------------------------")
|
||||||
|
message(STATUS "Project Name: ${PROJECT_NAME}")
|
||||||
|
message(STATUS "Preset Name: ${PRESET_NAME}")
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
message(STATUS "---------------------------------------------------")
|
||||||
|
|
||||||
43
CMakePresets.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "windows-base",
|
||||||
|
"hidden": true,
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/out/build/${presetName}",
|
||||||
|
"installDir": "${sourceDir}/out/install/${presetName}",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_C_COMPILER": "cl.exe",
|
||||||
|
"CMAKE_CXX_COMPILER": "cl.exe"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Windows"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x64-debug",
|
||||||
|
"displayName": "x64 Debug",
|
||||||
|
"inherits": "windows-base",
|
||||||
|
"architecture": {
|
||||||
|
"value": "x64",
|
||||||
|
"strategy": "external"
|
||||||
|
},
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Debug",
|
||||||
|
"PRESET_NAME": "x64 Debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "x64-release",
|
||||||
|
"displayName": "x64 Release",
|
||||||
|
"inherits": "x64-debug",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release",
|
||||||
|
"PRESET_NAME": "x64 Release"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
assets/Breakout Tile Set Free/.DS_Store
vendored
Normal file
15
assets/Breakout Tile Set Free/License.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
Breakout Game Tile Set Free
|
||||||
|
|
||||||
|
From ImagineLabs.Rocks
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
These game assets are being released under the license (Creative Commons Zero, CC0) http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
You may use these assets in personal and commercial projects.
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Visit our site http://www.imaginelabs.rocks for more great game assets.
|
||||||
BIN
assets/Breakout Tile Set Free/PNG/01-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/02-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/Breakout Tile Set Free/PNG/03-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/04-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/Breakout Tile Set Free/PNG/05-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/06-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/07-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/08-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/09-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/10-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/11-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/12-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/13-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/14-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/15-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/16-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/Breakout Tile Set Free/PNG/17-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/18-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/19-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/Breakout Tile Set Free/PNG/20-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/21-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/Breakout Tile Set Free/PNG/22-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/Breakout Tile Set Free/PNG/23-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/Breakout Tile Set Free/PNG/24-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/Breakout Tile Set Free/PNG/25-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/Breakout Tile Set Free/PNG/26-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/Breakout Tile Set Free/PNG/27-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/Breakout Tile Set Free/PNG/28-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
assets/Breakout Tile Set Free/PNG/29-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
assets/Breakout Tile Set Free/PNG/30-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
assets/Breakout Tile Set Free/PNG/31-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/Breakout Tile Set Free/PNG/32-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/Breakout Tile Set Free/PNG/33-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/34-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/35-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/36-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/Breakout Tile Set Free/PNG/37-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
assets/Breakout Tile Set Free/PNG/38-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/39-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/40-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/41-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/42-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/43-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/44-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/Breakout Tile Set Free/PNG/45-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/Breakout Tile Set Free/PNG/46-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/Breakout Tile Set Free/PNG/47-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/Breakout Tile Set Free/PNG/48-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/Breakout Tile Set Free/PNG/49-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/Breakout Tile Set Free/PNG/50-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/51-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/52-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/53-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/54-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/55-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/Breakout Tile Set Free/PNG/56-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
assets/Breakout Tile Set Free/PNG/57-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/Breakout Tile Set Free/PNG/58-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/Breakout Tile Set Free/PNG/59-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
assets/Breakout Tile Set Free/PNG/60-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
assets/Breakout Tile Set Free/PNG/61-Breakout-Tiles.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 346 KiB |
@@ -0,0 +1,63 @@
|
|||||||
|
<TextureAtlas imagePath="Breakout_Tile_Free.png">
|
||||||
|
<SubTexture name="01-Breakout-Tiles.png" x="772" y="390" width="384" height="128"/>
|
||||||
|
<SubTexture name="02-Breakout-Tiles.png" x="0" y="0" width="384" height="128"/>
|
||||||
|
<SubTexture name="03-Breakout-Tiles.png" x="0" y="130" width="384" height="128"/>
|
||||||
|
<SubTexture name="04-Breakout-Tiles.png" x="0" y="260" width="384" height="128"/>
|
||||||
|
<SubTexture name="05-Breakout-Tiles.png" x="0" y="390" width="384" height="128"/>
|
||||||
|
<SubTexture name="06-Breakout-Tiles.png" x="0" y="520" width="384" height="128"/>
|
||||||
|
<SubTexture name="07-Breakout-Tiles.png" x="772" y="260" width="384" height="128"/>
|
||||||
|
<SubTexture name="08-Breakout-Tiles.png" x="772" y="130" width="384" height="128"/>
|
||||||
|
<SubTexture name="09-Breakout-Tiles.png" x="772" y="0" width="384" height="128"/>
|
||||||
|
<SubTexture name="10-Breakout-Tiles.png" x="772" y="650" width="384" height="128"/>
|
||||||
|
<SubTexture name="11-Breakout-Tiles.png" x="386" y="650" width="384" height="128"/>
|
||||||
|
<SubTexture name="12-Breakout-Tiles.png" x="386" y="520" width="384" height="128"/>
|
||||||
|
<SubTexture name="13-Breakout-Tiles.png" x="386" y="390" width="384" height="128"/>
|
||||||
|
<SubTexture name="14-Breakout-Tiles.png" x="386" y="260" width="384" height="128"/>
|
||||||
|
<SubTexture name="15-Breakout-Tiles.png" x="386" y="130" width="384" height="128"/>
|
||||||
|
<SubTexture name="16-Breakout-Tiles.png" x="386" y="0" width="384" height="128"/>
|
||||||
|
<SubTexture name="17-Breakout-Tiles.png" x="772" y="520" width="384" height="128"/>
|
||||||
|
<SubTexture name="18-Breakout-Tiles.png" x="0" y="650" width="384" height="128"/>
|
||||||
|
<SubTexture name="19-Breakout-Tiles.png" x="386" y="780" width="384" height="128"/>
|
||||||
|
<SubTexture name="20-Breakout-Tiles.png" x="0" y="780" width="384" height="128"/>
|
||||||
|
<SubTexture name="21-Breakout-Tiles.png" x="1533" y="392" width="128" height="128"/>
|
||||||
|
<SubTexture name="22-Breakout-Tiles.png" x="1403" y="132" width="128" height="128"/>
|
||||||
|
<SubTexture name="23-Breakout-Tiles.png" x="1403" y="262" width="128" height="128"/>
|
||||||
|
<SubTexture name="24-Breakout-Tiles.png" x="1403" y="392" width="128" height="128"/>
|
||||||
|
<SubTexture name="25-Breakout-Tiles.png" x="1403" y="522" width="128" height="128"/>
|
||||||
|
<SubTexture name="26-Breakout-Tiles.png" x="1507" y="652" width="128" height="128"/>
|
||||||
|
<SubTexture name="27-Breakout-Tiles.png" x="1533" y="132" width="128" height="128"/>
|
||||||
|
<SubTexture name="28-Breakout-Tiles.png" x="1533" y="262" width="128" height="128"/>
|
||||||
|
<SubTexture name="29-Breakout-Tiles.png" x="1574" y="782" width="128" height="128"/>
|
||||||
|
<SubTexture name="30-Breakout-Tiles.png" x="1533" y="522" width="128" height="128"/>
|
||||||
|
<SubTexture name="31-Breakout-Tiles.png" x="1403" y="66" width="243" height="64"/>
|
||||||
|
<SubTexture name="32-Breakout-Tiles.png" x="1158" y="0" width="243" height="64"/>
|
||||||
|
<SubTexture name="33-Breakout-Tiles.png" x="1084" y="912" width="243" height="64"/>
|
||||||
|
<SubTexture name="34-Breakout-Tiles.png" x="1084" y="846" width="243" height="64"/>
|
||||||
|
<SubTexture name="35-Breakout-Tiles.png" x="1017" y="780" width="243" height="64"/>
|
||||||
|
<SubTexture name="36-Breakout-Tiles.png" x="839" y="912" width="243" height="64"/>
|
||||||
|
<SubTexture name="37-Breakout-Tiles.png" x="1329" y="858" width="243" height="64"/>
|
||||||
|
<SubTexture name="38-Breakout-Tiles.png" x="1329" y="792" width="243" height="64"/>
|
||||||
|
<SubTexture name="39-Breakout-Tiles.png" x="1403" y="0" width="243" height="64"/>
|
||||||
|
<SubTexture name="40-Breakout-Tiles.png" x="1329" y="924" width="243" height="64"/>
|
||||||
|
<SubTexture name="41-Breakout-Tiles.png" x="1158" y="66" width="243" height="64"/>
|
||||||
|
<SubTexture name="42-Breakout-Tiles.png" x="349" y="910" width="243" height="64"/>
|
||||||
|
<SubTexture name="43-Breakout-Tiles.png" x="594" y="910" width="243" height="64"/>
|
||||||
|
<SubTexture name="44-Breakout-Tiles.png" x="1262" y="726" width="243" height="64"/>
|
||||||
|
<SubTexture name="45-Breakout-Tiles.png" x="1158" y="132" width="243" height="64"/>
|
||||||
|
<SubTexture name="46-Breakout-Tiles.png" x="1158" y="198" width="243" height="64"/>
|
||||||
|
<SubTexture name="47-Breakout-Tiles.png" x="1158" y="264" width="243" height="64"/>
|
||||||
|
<SubTexture name="48-Breakout-Tiles.png" x="1158" y="330" width="243" height="64"/>
|
||||||
|
<SubTexture name="49-Breakout-Tiles.png" x="1158" y="396" width="243" height="64"/>
|
||||||
|
<SubTexture name="50-Breakout-Tiles.png" x="1158" y="462" width="243" height="64"/>
|
||||||
|
<SubTexture name="51-Breakout-Tiles.png" x="1158" y="528" width="243" height="64"/>
|
||||||
|
<SubTexture name="52-Breakout-Tiles.png" x="1158" y="594" width="243" height="64"/>
|
||||||
|
<SubTexture name="53-Breakout-Tiles.png" x="1158" y="660" width="243" height="64"/>
|
||||||
|
<SubTexture name="54-Breakout-Tiles.png" x="839" y="846" width="243" height="64"/>
|
||||||
|
<SubTexture name="55-Breakout-Tiles.png" x="772" y="780" width="243" height="64"/>
|
||||||
|
<SubTexture name="56-Breakout-Tiles.png" x="0" y="910" width="347" height="64"/>
|
||||||
|
<SubTexture name="57-Breakout-Tiles.png" x="1574" y="912" width="115" height="64"/>
|
||||||
|
<SubTexture name="58-Breakout-Tiles.png" x="1403" y="652" width="64" height="64"/>
|
||||||
|
<SubTexture name="59-Breakout-Tiles.png" x="772" y="846" width="64" height="61"/>
|
||||||
|
<SubTexture name="60-Breakout-Tiles.png" x="1637" y="652" width="64" height="58"/>
|
||||||
|
<SubTexture name="61-Breakout-Tiles.png" x="0" y="990" width="10" height="21"/>
|
||||||
|
</TextureAtlas>
|
||||||
BIN
assets/Breakout Tile Set Free/Vector/.DS_Store
vendored
Normal file
BIN
assets/Breakout Tile Set Free/Vector/Breakout Tile Set Free.eps
Normal file
2535
assets/Breakout Tile Set Free/Vector/Breakout Tile Set Free.svg
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
assets/Breakout Tile Set Free/preview.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
assets/Breakout Tile Set Free/sample.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/Breakout_Tile_Set_Free.zip
Normal file
BIN
assets/home.jpg
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
src/assets/home.jpg
Normal file
|
After Width: | Height: | Size: 201 KiB |
12870
src/assets/home.jpg.h
Normal file
67
src/logger/logger.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "quokka_gfx/core/events/eventBus.h"
|
||||||
|
|
||||||
|
#include <iostream> // For std::cout
|
||||||
|
#include <syncstream> // For std::osyncstream (C++20) to ensure thread-safe output
|
||||||
|
#include <thread> // For std::thread and std::mutex
|
||||||
|
#include <utility> // For std::forward
|
||||||
|
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
class Logger : public quokka_gfx::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Logger(); // Default constructor
|
||||||
|
virtual ~Logger() = default; // Default destructor
|
||||||
|
Logger(const Logger &obj) = delete; // Copy constructor
|
||||||
|
Logger(Logger &&obj) = delete; // Move constructor
|
||||||
|
Logger &operator=(const Logger &obj) = delete; // Copy assignment operator
|
||||||
|
Logger &operator=(Logger &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void log(Args &&...args); // Variadic template function for logging
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::jthread m_loggerThread; // Waits for events thread
|
||||||
|
|
||||||
|
private:
|
||||||
|
void startThread(); // Start the logger thread
|
||||||
|
};
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Default constructor */
|
||||||
|
inline Logger::Logger()
|
||||||
|
{
|
||||||
|
subscribe(quokka_gfx::toID("log"));
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Variadic template function for logging */
|
||||||
|
template<typename... Args>
|
||||||
|
void Logger::log(Args &&...args)
|
||||||
|
{
|
||||||
|
std::osyncstream out(std::cout);
|
||||||
|
(out << ... << args) << std::endl;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
/* Start the logger thread */
|
||||||
|
inline void Logger::startThread()
|
||||||
|
{
|
||||||
|
m_loggerThread = std::jthread([&](const std::stop_token &st)
|
||||||
|
{
|
||||||
|
while (!st.stop_requested())
|
||||||
|
{
|
||||||
|
while (getEventCount() > 0)
|
||||||
|
{
|
||||||
|
const auto event = popEvent();
|
||||||
|
if (event)
|
||||||
|
{
|
||||||
|
if (event->getType() == quokka_gfx::toID("log"))
|
||||||
|
log(event->getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep for a short duration to prevent busy waiting
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
} });
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
31
src/main.cpp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "logger/logger.h"
|
||||||
|
#include "quokka_gfx/quokka_gfx.h"
|
||||||
|
#include "screens/home/homeScreen.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
//--------------------------------------------------------------
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// App initialization
|
||||||
|
Logger logger; // Initialize the logger
|
||||||
|
|
||||||
|
logger.log("Quokkanoid Game Started!");
|
||||||
|
|
||||||
|
// Start the quokka GFX application instance
|
||||||
|
quokka_gfx::Application app("Quokkanoid");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.initScreen(make_unique<HomeScreen>(&app));
|
||||||
|
app.run();
|
||||||
|
}
|
||||||
|
catch (const exception &e)
|
||||||
|
{
|
||||||
|
cerr << "Error: " << e.what() << endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
//--------------------------------------------------------------
|
||||||
158
src/quokka_gfx/core/application.cpp
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#include "application.h"
|
||||||
|
|
||||||
|
#include "../utils/color.h"
|
||||||
|
#include "assets/assetManager.h"
|
||||||
|
#include "events/inputManager.h"
|
||||||
|
#include "renderer/rendererManager.h"
|
||||||
|
#include "screens/screenManager.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace quokka_gfx;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
Application::Application(const std::string &title)
|
||||||
|
{
|
||||||
|
init(title);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Default destructor */
|
||||||
|
Application::~Application()
|
||||||
|
{
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Initialize the first screen */
|
||||||
|
void Application::initScreen(std::unique_ptr<Screen> screen) const
|
||||||
|
{
|
||||||
|
m_screenManager->setScreen(std::move(screen));
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Main loop */
|
||||||
|
void Application::run()
|
||||||
|
{
|
||||||
|
auto lastTime = SDL_GetPerformanceCounter();
|
||||||
|
const auto frequency = static_cast<double>(SDL_GetPerformanceFrequency());
|
||||||
|
|
||||||
|
while (m_running)
|
||||||
|
{
|
||||||
|
// Calculate delta time in seconds for smooth updates regardless of frame rate
|
||||||
|
const auto currentTime = SDL_GetPerformanceCounter();
|
||||||
|
const auto deltaTime = static_cast<double>((currentTime - lastTime)) / frequency;
|
||||||
|
lastTime = currentTime;
|
||||||
|
|
||||||
|
handleEvents();
|
||||||
|
update(deltaTime);
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Quit the application */
|
||||||
|
void Application::quit()
|
||||||
|
{
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
AssetManager *Application::assetManager() const
|
||||||
|
{
|
||||||
|
return m_assetManager.get();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
InputManager *Application::inputManager() const
|
||||||
|
{
|
||||||
|
return m_inputManager.get();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
RendererManager *Application::rendererManager() const
|
||||||
|
{
|
||||||
|
return m_rendererManager.get();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
ScreenManager *Application::screenManager() const
|
||||||
|
{
|
||||||
|
return m_screenManager.get();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Initialisation */
|
||||||
|
void Application::init(const std::string &title)
|
||||||
|
{
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0)
|
||||||
|
throw std::runtime_error("Unable to initialize SDL: " + std::string(SDL_GetError()));
|
||||||
|
|
||||||
|
m_window = SDL_CreateWindow(
|
||||||
|
title.c_str(),
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
1200,
|
||||||
|
896,
|
||||||
|
SDL_WINDOW_SHOWN);
|
||||||
|
|
||||||
|
if (!m_window)
|
||||||
|
throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError()));
|
||||||
|
|
||||||
|
m_sdlRenderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
if (!m_sdlRenderer)
|
||||||
|
throw std::runtime_error("Unable to create renderer: " + std::string(SDL_GetError()));
|
||||||
|
|
||||||
|
// Enable alpha blending for transparency support
|
||||||
|
SDL_SetRenderDrawBlendMode(m_sdlRenderer, SDL_BLENDMODE_BLEND);
|
||||||
|
|
||||||
|
// Initialize subsystems
|
||||||
|
m_assetManager = std::make_unique<AssetManager>(m_sdlRenderer);
|
||||||
|
m_inputManager = std::make_unique<InputManager>();
|
||||||
|
m_rendererManager = std::make_unique<RendererManager>(this);
|
||||||
|
m_screenManager = std::make_unique<ScreenManager>();
|
||||||
|
|
||||||
|
m_running = true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Handle SDL events */
|
||||||
|
void Application::handleEvents()
|
||||||
|
{
|
||||||
|
auto eventProcessing = m_inputManager->getEventProcessing();
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event))
|
||||||
|
{
|
||||||
|
if (event.type == SDL_QUIT)
|
||||||
|
{
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
eventProcessing.process(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_screenManager->handleEvents();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Update game state */
|
||||||
|
void Application::update(const double deltaTime) const
|
||||||
|
{
|
||||||
|
m_screenManager->update(deltaTime);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Render the game */
|
||||||
|
void Application::render() const
|
||||||
|
{
|
||||||
|
m_rendererManager->clear(Color(30, 30, 30));
|
||||||
|
|
||||||
|
m_screenManager->render(m_rendererManager.get()); // Render the current screen
|
||||||
|
|
||||||
|
m_rendererManager->present(); // Present the rendered content to the screen
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Cleanup resources */
|
||||||
|
void Application::cleanup() const
|
||||||
|
{
|
||||||
|
if (m_sdlRenderer)
|
||||||
|
SDL_DestroyRenderer(m_sdlRenderer);
|
||||||
|
|
||||||
|
if (m_window)
|
||||||
|
SDL_DestroyWindow(m_window);
|
||||||
|
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
60
src/quokka_gfx/core/application.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class Screen;
|
||||||
|
class AssetManager;
|
||||||
|
class InputManager;
|
||||||
|
class RendererManager;
|
||||||
|
class ScreenManager;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class Application
|
||||||
|
{
|
||||||
|
friend class RendererManager;
|
||||||
|
friend class Screen;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Application() = delete; // Default constructor
|
||||||
|
virtual ~Application(); // Default destructor
|
||||||
|
Application(const Application &obj) = delete; // Copy constructor
|
||||||
|
Application(Application &&obj) = delete; // Move constructor
|
||||||
|
Application &operator=(const Application &obj) = delete; // Copy assignment operator
|
||||||
|
Application &operator=(Application &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit Application(const std::string &title); // Constructor
|
||||||
|
|
||||||
|
void initScreen(std::unique_ptr<Screen> screen) const; // Initialize the first screen
|
||||||
|
|
||||||
|
void run(); // Main loop
|
||||||
|
void quit(); // Quit the application
|
||||||
|
|
||||||
|
// Accessors for subsystems
|
||||||
|
[[nodiscard]] AssetManager *assetManager() const;
|
||||||
|
[[nodiscard]] InputManager *inputManager() const;
|
||||||
|
[[nodiscard]] RendererManager *rendererManager() const;
|
||||||
|
[[nodiscard]] ScreenManager *screenManager() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SDL_Window *m_window = nullptr;
|
||||||
|
SDL_Renderer *m_sdlRenderer = nullptr;
|
||||||
|
|
||||||
|
bool m_running = false; // Main loop flag
|
||||||
|
|
||||||
|
// Subsystems
|
||||||
|
std::unique_ptr<AssetManager> m_assetManager; // Asset manager
|
||||||
|
std::unique_ptr<InputManager> m_inputManager; // Input manager
|
||||||
|
std::unique_ptr<RendererManager> m_rendererManager; // Renderer manager
|
||||||
|
std::unique_ptr<ScreenManager> m_screenManager; // Screen manager
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init(const std::string &title); // Initialisation
|
||||||
|
void handleEvents(); // Handle SDL events
|
||||||
|
void update(double deltaTime) const; // Update game state
|
||||||
|
void render() const; // Render the game
|
||||||
|
void cleanup() const; // Cleanup resources
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
121
src/quokka_gfx/core/assets/assetManager.cpp
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
#include "assetManager.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
#include <SDL2/SDL_rwops.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace quokka_gfx;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
AssetManager::AssetManager(SDL_Renderer *renderer)
|
||||||
|
{
|
||||||
|
// Initialization
|
||||||
|
m_renderer = renderer;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
void AssetManager::clear()
|
||||||
|
{
|
||||||
|
m_textures.clear();
|
||||||
|
m_fonts.clear();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
bool AssetManager::loadTexture(const HashID &id, const std::filesystem::path &filePath)
|
||||||
|
{
|
||||||
|
// Check if the texture is already loaded
|
||||||
|
if (m_textures.contains(id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load the texture from the file path
|
||||||
|
SDL_Texture *texture = IMG_LoadTexture(m_renderer, filePath.string().c_str());
|
||||||
|
if (!texture)
|
||||||
|
{
|
||||||
|
cerr << format("Failed to load texture: {}, Error: {}",
|
||||||
|
filePath.string(),
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the texture in the map and return it
|
||||||
|
m_textures.emplace(id, Texture(texture, SDL_DestroyTexture));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Load a texture from a memory buffer */
|
||||||
|
bool AssetManager::loadTexture(const HashID &id, const std::span<const uint8_t> &buffer)
|
||||||
|
{
|
||||||
|
// Check if the texture is already loaded
|
||||||
|
if (m_textures.contains(id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load the texture from the memory buffer
|
||||||
|
const auto rw = SDL_RWFromConstMem(buffer.data(), static_cast<int>(buffer.size()));
|
||||||
|
if (!rw)
|
||||||
|
{
|
||||||
|
cerr << format("Failed to create RWops from memory buffer, Error: {}",
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture *texture = IMG_LoadTexture_RW(m_renderer, rw, 1);
|
||||||
|
if (!texture)
|
||||||
|
{
|
||||||
|
cerr << format("Failed to load texture from memory buffer, Error: {}",
|
||||||
|
SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the texture in the map and return it
|
||||||
|
m_textures.emplace(id, Texture(texture, SDL_DestroyTexture));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
SDL_Texture *AssetManager::getTexture(const HashID &id) const
|
||||||
|
{
|
||||||
|
if (m_textures.contains(id))
|
||||||
|
return m_textures.at(id).get();
|
||||||
|
|
||||||
|
cerr << format("Texture with ID '{}' not found.", id);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
void AssetManager::unloadTexture(const HashID &id)
|
||||||
|
{
|
||||||
|
m_textures.erase(id);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Load a font from a file path with a specific size */
|
||||||
|
TTF_Font *AssetManager::loadFont(const HashID &id, const std::filesystem::path &filePath, int fontSize)
|
||||||
|
{
|
||||||
|
// Check if the font is already loaded
|
||||||
|
if (m_fonts.contains(id))
|
||||||
|
return m_fonts.at(id).get();
|
||||||
|
|
||||||
|
// Load the font from the file path
|
||||||
|
TTF_Font *font = TTF_OpenFont(filePath.string().c_str(), fontSize);
|
||||||
|
if (!font)
|
||||||
|
{
|
||||||
|
cerr << format("Failed to load font: {}, Error: {}", filePath.string(), TTF_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the font in the map and return it
|
||||||
|
m_fonts.emplace(id, Font(font, TTF_CloseFont));
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get a font by its ID */
|
||||||
|
TTF_Font *AssetManager::getFont(const HashID &id) const
|
||||||
|
{
|
||||||
|
if (m_fonts.contains(id))
|
||||||
|
return m_fonts.at(id).get();
|
||||||
|
|
||||||
|
cerr << format("Font with ID '{}' not found.", id);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Unload a font by its ID */
|
||||||
|
void AssetManager::unloadFont(const HashID &id)
|
||||||
|
{
|
||||||
|
m_fonts.erase(id);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
46
src/quokka_gfx/core/assets/assetManager.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../defs.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class Application;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class AssetManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AssetManager() = delete; // Default constructor
|
||||||
|
virtual ~AssetManager() = default; // Default destructor
|
||||||
|
AssetManager(const AssetManager &obj) = delete; // Copy constructor
|
||||||
|
AssetManager(AssetManager &&obj) = delete; // Move constructor
|
||||||
|
AssetManager &operator=(const AssetManager &obj) = delete; // Copy assignment operator
|
||||||
|
AssetManager &operator=(AssetManager &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit AssetManager(SDL_Renderer *renderer); // Constructor
|
||||||
|
void clear(); // Clear all loaded assets
|
||||||
|
|
||||||
|
// Texture management
|
||||||
|
bool loadTexture(const HashID &id, const std::filesystem::path &filePath); // Load a texture from a file path
|
||||||
|
bool loadTexture(const HashID &id, const std::span<const uint8_t> &buffer); // Load a texture from a memory buffer
|
||||||
|
[[nodiscard]] SDL_Texture *getTexture(const HashID &id) const; // Get a texture by its ID
|
||||||
|
void unloadTexture(const HashID &id); // Unload a texture by its ID
|
||||||
|
|
||||||
|
// Font management
|
||||||
|
TTF_Font *loadFont(const HashID &id, const std::filesystem::path &filePath, int fontSize);
|
||||||
|
[[nodiscard]] TTF_Font *getFont(const HashID &id) const;
|
||||||
|
void unloadFont(const HashID &id);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SDL_Renderer *m_renderer = nullptr;
|
||||||
|
|
||||||
|
std::unordered_map<HashID, Texture> m_textures;
|
||||||
|
std::unordered_map<HashID, Font> m_fonts;
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
30
src/quokka_gfx/core/defs.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
//------------------------------------------------------------
|
||||||
|
using HashID = uint32_t;
|
||||||
|
using Texture = std::unique_ptr<SDL_Texture, void (*)(SDL_Texture *)>;
|
||||||
|
using Font = std::unique_ptr<TTF_Font, void (*)(TTF_Font *)>;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
// Hash function for IDs using FNV-1a algorithm
|
||||||
|
inline constexpr HashID toID(const std::string &id)
|
||||||
|
{
|
||||||
|
constexpr uint32_t FNV_offset_basis = 2166136261u;
|
||||||
|
constexpr uint32_t FNV_prime = 16777619u;
|
||||||
|
|
||||||
|
uint32_t hash_value = FNV_offset_basis;
|
||||||
|
for (const char c : id)
|
||||||
|
{
|
||||||
|
hash_value ^= static_cast<uint32_t>(static_cast<uint8_t>(c));
|
||||||
|
hash_value *= FNV_prime;
|
||||||
|
}
|
||||||
|
return hash_value;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
168
src/quokka_gfx/core/events/eventBus.cpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#include "eventBus.h"
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace quokka_gfx;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
Event::Event(const HashID &type) noexcept
|
||||||
|
{
|
||||||
|
m_type = type;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the type ID of the event */
|
||||||
|
HashID Event::getType() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Default destructor */
|
||||||
|
Listener::~Listener()
|
||||||
|
{
|
||||||
|
unsubscribeAll();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Post an event to the listener */
|
||||||
|
void Listener::pushEvent(std::shared_ptr<Event> event)
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_receivedEventsMutex);
|
||||||
|
m_receivedEvents.push(std::move(event));
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the number of received events */
|
||||||
|
size_t Listener::getEventCount() const
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_receivedEventsMutex);
|
||||||
|
return m_receivedEvents.size();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the next event from the queue */
|
||||||
|
std::shared_ptr<Event> Listener::popEvent()
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_receivedEventsMutex);
|
||||||
|
if (m_receivedEvents.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto event = m_receivedEvents.front();
|
||||||
|
m_receivedEvents.pop();
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Post an event to the event bus */
|
||||||
|
void Listener::postEvent(const std::shared_ptr<Event> &event) const
|
||||||
|
{
|
||||||
|
EventBus::getInstance().notify(event);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if subscribed to a specific event type */
|
||||||
|
bool Listener::isSubscribed(const HashID &eventType) const
|
||||||
|
{
|
||||||
|
return EventBus::getInstance().isSubscribed(eventType, this);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Subscribe to a specific event type*/
|
||||||
|
void Listener::subscribe(const HashID &eventType)
|
||||||
|
{
|
||||||
|
EventBus::getInstance().subscribe(eventType, this);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Unsubscribe from a specific event type */
|
||||||
|
void Listener::unsubscribe(const HashID &eventType)
|
||||||
|
{
|
||||||
|
EventBus::getInstance().unsubscribe(eventType, this);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Unsubscribe from all events */
|
||||||
|
void Listener::unsubscribeAll()
|
||||||
|
{
|
||||||
|
EventBus::getInstance().unsubscribeAll(this);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the singleton instance */
|
||||||
|
EventBus &EventBus::getInstance()
|
||||||
|
{
|
||||||
|
static auto instance = std::unique_ptr<EventBus>(new EventBus());
|
||||||
|
return *instance;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Notify all listeners subscribed to the event type */
|
||||||
|
void EventBus::notify(const std::shared_ptr<Event> &event) const
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_listenersMutex);
|
||||||
|
|
||||||
|
// No listeners subscribed to this event type, nothing to do
|
||||||
|
if (!m_listeners.contains(event->getType()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Notify all listeners subscribed to this event type
|
||||||
|
for (const auto listener : m_listeners.at(event->getType()))
|
||||||
|
listener->pushEvent(event);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if a listener is subscribed to a specific event type */
|
||||||
|
bool EventBus::isSubscribed(const HashID &eventType, const Listener *listener) const
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_listenersMutex);
|
||||||
|
|
||||||
|
// Event type not registered, listener cannot be subscribed
|
||||||
|
if (!m_listeners.contains(eventType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Event type already registered, check if the listener is in the list
|
||||||
|
const auto &listeners = m_listeners.at(eventType);
|
||||||
|
return ranges::find(listeners, listener) != listeners.end();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Subscribe a listener to a specific event type */
|
||||||
|
void EventBus::subscribe(const HashID &eventType, Listener *listener)
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_listenersMutex);
|
||||||
|
|
||||||
|
// Event type not registered yet, create a new entry with the listener
|
||||||
|
if (!m_listeners.contains(eventType))
|
||||||
|
{
|
||||||
|
m_listeners[eventType] = { listener };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event type already registered, add the listener if not already subscribed
|
||||||
|
auto &listeners = m_listeners[eventType];
|
||||||
|
if (ranges::find(listeners, listener) == listeners.end())
|
||||||
|
listeners.push_back(listener);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Unsubscribe a listener from a specific event type */
|
||||||
|
void EventBus::unsubscribe(const HashID &eventType, Listener *listener)
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_listenersMutex);
|
||||||
|
|
||||||
|
// Event type not registered, nothing to do
|
||||||
|
if (!m_listeners.contains(eventType))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Event type registered, remove the listener if subscribed
|
||||||
|
auto &listeners = m_listeners[eventType];
|
||||||
|
const auto it = ranges::find(listeners, listener);
|
||||||
|
if (it != listeners.end())
|
||||||
|
listeners.erase(it);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Unsubscribe a listener from all event types */
|
||||||
|
void EventBus::unsubscribeAll(Listener *listener)
|
||||||
|
{
|
||||||
|
scoped_lock lock(m_listenersMutex);
|
||||||
|
|
||||||
|
// Iterate through all event types and remove the listener from each list
|
||||||
|
for (const auto &eventType : m_listeners | views::keys)
|
||||||
|
unsubscribe(eventType, listener);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
104
src/quokka_gfx/core/events/eventBus.h
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../defs.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <typeindex>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class EventBus;
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
// Base class for all events
|
||||||
|
class Event
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Event() = delete; // Default constructor
|
||||||
|
virtual ~Event() = default; // Default destructor
|
||||||
|
Event(const Event &obj) = delete; // Copy constructor
|
||||||
|
Event(Event &&obj) = delete; // Move constructor
|
||||||
|
Event &operator=(const Event &obj) = delete; // Copy assignment operator
|
||||||
|
Event &operator=(Event &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit Event(const HashID &type) noexcept; // Constructor
|
||||||
|
|
||||||
|
[[nodiscard]] HashID getType() const; // Get the type ID of the event
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HashID m_type; // Type ID of the event
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
// Base class for all event listeners
|
||||||
|
class Listener
|
||||||
|
{
|
||||||
|
friend class EventBus;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Listener() = default; // Default constructor
|
||||||
|
virtual ~Listener(); // Default destructor
|
||||||
|
Listener(const Listener &obj) = delete; // Copy constructor
|
||||||
|
Listener(Listener &&obj) = delete; // Move constructor
|
||||||
|
Listener &operator=(const Listener &obj) = delete; // Copy assignment operator
|
||||||
|
Listener &operator=(Listener &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
void pushEvent(std::shared_ptr<Event> event); // Post an event to the listener
|
||||||
|
|
||||||
|
[[nodiscard]] size_t getEventCount() const; // Get the number of received events
|
||||||
|
[[nodiscard]] std::shared_ptr<Event> popEvent(); // Get the next event from the queue
|
||||||
|
|
||||||
|
void postEvent(const std::shared_ptr<Event> &event) const; // Post an event to the event bus
|
||||||
|
|
||||||
|
[[nodiscard]] bool isSubscribed(const HashID &eventType) const; // Check if subscribed to a specific event type
|
||||||
|
void subscribe(const HashID &eventType); // Subscribe to a specific event type
|
||||||
|
void unsubscribe(const HashID &eventType); // Unsubscribe from a specific event type
|
||||||
|
void unsubscribeAll(); // Unsubscribe from all events
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable std::mutex m_receivedEventsMutex; // Mutex for thread-safe access to the event queue
|
||||||
|
std::queue<std::shared_ptr<Event>> m_receivedEvents; // Queue of received events
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
// Singleton EventBus for managing event distribution
|
||||||
|
class EventBus
|
||||||
|
{
|
||||||
|
friend class Listener;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static EventBus &getInstance(); // Get the singleton instance
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~EventBus() = default; // Default destructor
|
||||||
|
EventBus(const EventBus &obj) = delete; // Copy constructor
|
||||||
|
EventBus(EventBus &&obj) = delete; // Move constructor
|
||||||
|
EventBus &operator=(const EventBus &obj) = delete; // Copy assignment operator
|
||||||
|
EventBus &operator=(EventBus &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
void notify(const std::shared_ptr<Event> &event) const; // Notify all listeners subscribed to the event type
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mutable std::recursive_mutex m_listenersMutex; // Mutex for thread-safe access to the listeners map
|
||||||
|
std::unordered_map<HashID, std::vector<Listener *>> m_listeners; // Map of event type IDs to lists of subscribed listeners
|
||||||
|
|
||||||
|
private:
|
||||||
|
EventBus() = default; // Default constructor
|
||||||
|
|
||||||
|
[[nodiscard]] bool isSubscribed(const HashID &eventType, const Listener *listener) const; // Check if a listener is subscribed to a specific event type
|
||||||
|
void subscribe(const HashID &eventType, Listener *listener); // Subscribe a listener to a specific event type
|
||||||
|
void unsubscribe(const HashID &eventType, Listener *listener); // Unsubscribe a listener from a specific event type
|
||||||
|
void unsubscribeAll(Listener *listener); // Unsubscribe a listener from all event types
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
318
src/quokka_gfx/core/events/inputManager.cpp
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
#include "inputManager.h"
|
||||||
|
|
||||||
|
#include <ranges>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace quokka_gfx;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get event processing session */
|
||||||
|
InputProcessing InputManager::getEventProcessing()
|
||||||
|
{
|
||||||
|
return InputProcessing(this);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the current state of a key */
|
||||||
|
InputManager::KeyState InputManager::getKeyState(const SDL_Scancode key) const
|
||||||
|
{
|
||||||
|
const auto it = m_keys.keyStates.find(key);
|
||||||
|
return it != m_keys.keyStates.end() ? it->second : KeyState::Up;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if a key was pressed this frame */
|
||||||
|
bool InputManager::isKeyPressed(const SDL_Scancode key) const
|
||||||
|
{
|
||||||
|
if (!m_keys.keyStates.contains(key))
|
||||||
|
return false;
|
||||||
|
return m_keys.keyStates.at(key) == KeyState::Pressed;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if a key is currently held down */
|
||||||
|
bool InputManager::isKeyHeld(const SDL_Scancode key) const
|
||||||
|
{
|
||||||
|
if (!m_keys.keyStates.contains(key))
|
||||||
|
return false;
|
||||||
|
return m_keys.keyStates.at(key) == KeyState::Held;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if a key was released this frame */
|
||||||
|
bool InputManager::isKeyReleased(const SDL_Scancode key) const
|
||||||
|
{
|
||||||
|
if (!m_keys.keyStates.contains(key))
|
||||||
|
return false;
|
||||||
|
return m_keys.keyStates.at(key) == KeyState::Released;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if a key is currently down */
|
||||||
|
bool InputManager::isKeyDown(const SDL_Scancode key) const
|
||||||
|
{
|
||||||
|
if (!m_keys.keyStates.contains(key))
|
||||||
|
return false;
|
||||||
|
const auto state = m_keys.keyStates.at(key);
|
||||||
|
return state == KeyState::Pressed || state == KeyState::Held;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if a key is currently up */
|
||||||
|
bool InputManager::isKeyUp(const SDL_Scancode key) const
|
||||||
|
{
|
||||||
|
if (!m_keys.keyStates.contains(key))
|
||||||
|
return false;
|
||||||
|
const auto state = m_keys.keyStates.at(key);
|
||||||
|
return state == KeyState::Up || state == KeyState::Released;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Shift modifier is currently down */
|
||||||
|
bool InputManager::isShiftDown() const
|
||||||
|
{
|
||||||
|
return isLShiftDown() || isRShiftDown();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Left Shift modifier is currently down */
|
||||||
|
bool InputManager::isLShiftDown() const
|
||||||
|
{
|
||||||
|
return isKeyDown(SDL_SCANCODE_LSHIFT);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Right Shift modifier is currently down */
|
||||||
|
bool InputManager::isRShiftDown() const
|
||||||
|
{
|
||||||
|
return isKeyDown(SDL_SCANCODE_RSHIFT);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Ctrl modifier is currently down */
|
||||||
|
bool InputManager::isCtrlDown() const
|
||||||
|
{
|
||||||
|
return isLCtrlDown() || isRCtrlDown();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Left Ctrl modifier is currently down */
|
||||||
|
bool InputManager::isLCtrlDown() const
|
||||||
|
{
|
||||||
|
return isKeyDown(SDL_SCANCODE_LCTRL);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Right Ctrl modifier is currently down */
|
||||||
|
bool InputManager::isRCtrlDown() const
|
||||||
|
{
|
||||||
|
return isKeyDown(SDL_SCANCODE_RCTRL);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Alt modifier is currently down */
|
||||||
|
bool InputManager::isAltDown() const
|
||||||
|
{
|
||||||
|
return isLAltDown() || isRAltDown();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Left Alt modifier is currently down */
|
||||||
|
bool InputManager::isLAltDown() const
|
||||||
|
{
|
||||||
|
return isKeyDown(SDL_SCANCODE_LALT);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if Right Alt modifier is currently down */
|
||||||
|
bool InputManager::isRAltDown() const
|
||||||
|
{
|
||||||
|
return isKeyDown(SDL_SCANCODE_RALT);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get current mouse X position */
|
||||||
|
int InputManager::getMouseX() const
|
||||||
|
{
|
||||||
|
return m_mouse.posX;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get current mouse Y position */
|
||||||
|
int InputManager::getMouseY() const
|
||||||
|
{
|
||||||
|
return m_mouse.posY;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get current mouse position */
|
||||||
|
Point InputManager::getMousePosition() const
|
||||||
|
{
|
||||||
|
return Point(m_mouse.posX, m_mouse.posY);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get mouse movement delta X since last frame */
|
||||||
|
int InputManager::getMouseDeltaX() const
|
||||||
|
{
|
||||||
|
return m_mouse.deltaX;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get mouse movement delta Y since last frame */
|
||||||
|
int InputManager::getMouseDeltaY() const
|
||||||
|
{
|
||||||
|
return m_mouse.deltaY;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the current state of a mouse button */
|
||||||
|
InputManager::KeyState InputManager::getMouseButtonState(const MouseButton button) const
|
||||||
|
{
|
||||||
|
auto it = m_mouse.buttonStates.find(button);
|
||||||
|
return it != m_mouse.buttonStates.end() ? it->second : KeyState::Up;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get current mouse X position */
|
||||||
|
bool InputManager::isMouseButtonPressed(const MouseButton button) const
|
||||||
|
{
|
||||||
|
if (!m_mouse.buttonStates.contains(button))
|
||||||
|
return false;
|
||||||
|
return m_mouse.buttonStates.at(button) == KeyState::Pressed;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get current mouse Y position */
|
||||||
|
bool InputManager::isMouseButtonHeld(const MouseButton button) const
|
||||||
|
{
|
||||||
|
if (!m_mouse.buttonStates.contains(button))
|
||||||
|
return false;
|
||||||
|
return m_mouse.buttonStates.at(button) == KeyState::Held;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get mouse movement delta X since last frame */
|
||||||
|
bool InputManager::isMouseButtonReleased(const MouseButton button) const
|
||||||
|
{
|
||||||
|
if (!m_mouse.buttonStates.contains(button))
|
||||||
|
return false;
|
||||||
|
return m_mouse.buttonStates.at(button) == KeyState::Released;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get mouse movement delta Y since last frame */
|
||||||
|
bool InputManager::isMouseButtonDown(const MouseButton button) const
|
||||||
|
{
|
||||||
|
if (!m_mouse.buttonStates.contains(button))
|
||||||
|
return false;
|
||||||
|
const auto state = m_mouse.buttonStates.at(button);
|
||||||
|
return state == KeyState::Pressed || state == KeyState::Held;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Check if a mouse button is currently up */
|
||||||
|
bool InputManager::isMouseButtonUp(const MouseButton button) const
|
||||||
|
{
|
||||||
|
if (!m_mouse.buttonStates.contains(button))
|
||||||
|
return false;
|
||||||
|
const auto state = m_mouse.buttonStates.at(button);
|
||||||
|
return state == KeyState::Up || state == KeyState::Released;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get scroll wheel delta since last frame */
|
||||||
|
int InputManager::getMouseWheelDelta() const
|
||||||
|
{
|
||||||
|
return m_mouse.wheelDelta;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
InputProcessing::InputProcessing(InputManager *inputEventHandle)
|
||||||
|
{
|
||||||
|
if (!inputEventHandle)
|
||||||
|
throw std::invalid_argument("InputManager pointer cannot be null");
|
||||||
|
m_inputEventHandle = inputEventHandle;
|
||||||
|
|
||||||
|
begin();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Default destructor */
|
||||||
|
InputProcessing::~InputProcessing()
|
||||||
|
{
|
||||||
|
end();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Process an SDL event */
|
||||||
|
void InputProcessing::process(const SDL_Event &event) const
|
||||||
|
{
|
||||||
|
switch (event.type)
|
||||||
|
{
|
||||||
|
// ==== KEYBOARD ====
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
if (!event.key.repeat) // Skip auto-repeated keydown events
|
||||||
|
{
|
||||||
|
m_inputEventHandle->m_keys.keyStates[event.key.keysym.scancode] = InputManager::KeyState::Pressed;
|
||||||
|
m_inputEventHandle->m_keys.keysPressed.insert(event.key.keysym.scancode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYUP:
|
||||||
|
m_inputEventHandle->m_keys.keyStates[event.key.keysym.scancode] = InputManager::KeyState::Released;
|
||||||
|
m_inputEventHandle->m_keys.keysReleased.insert(event.key.keysym.scancode);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ==== MOUSE - MOTION ====
|
||||||
|
case SDL_MOUSEMOTION:
|
||||||
|
m_inputEventHandle->m_mouse.posX = event.motion.x;
|
||||||
|
m_inputEventHandle->m_mouse.posY = event.motion.y;
|
||||||
|
m_inputEventHandle->m_mouse.deltaX = event.motion.xrel;
|
||||||
|
m_inputEventHandle->m_mouse.deltaY = event.motion.yrel;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ==== MOUSE - BOUTONS ====
|
||||||
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
|
m_inputEventHandle->m_mouse.buttonStates[static_cast<InputManager::MouseButton>(event.button.button)] = InputManager::KeyState::Pressed;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_MOUSEBUTTONUP:
|
||||||
|
m_inputEventHandle->m_mouse.buttonStates[static_cast<InputManager::MouseButton>(event.button.button)] = InputManager::KeyState::Released;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// ==== MOUSE - WHEEL ====
|
||||||
|
case SDL_MOUSEWHEEL:
|
||||||
|
m_inputEventHandle->m_mouse.wheelDelta = event.wheel.y; // positive = towards up, negative = towards down
|
||||||
|
break;
|
||||||
|
|
||||||
|
// // ===== SAISIE DE TEXTE =====
|
||||||
|
// case SDL_TEXTINPUT:
|
||||||
|
// if (m_textInputCallback)
|
||||||
|
// {
|
||||||
|
// m_textInputCallback(event.text.text);
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Called at the beginning of event processing */
|
||||||
|
void InputProcessing::begin() const
|
||||||
|
{
|
||||||
|
// This function is called at the beginning of event processing to
|
||||||
|
// reset states and prepare for new events
|
||||||
|
|
||||||
|
// Initialize key states for this frame
|
||||||
|
m_inputEventHandle->m_keys.keysPressed.clear();
|
||||||
|
m_inputEventHandle->m_keys.keysReleased.clear();
|
||||||
|
|
||||||
|
// Initialize mouse parameters
|
||||||
|
m_inputEventHandle->m_mouse.lastPosX = m_inputEventHandle->m_mouse.posX;
|
||||||
|
m_inputEventHandle->m_mouse.lastPosY = m_inputEventHandle->m_mouse.posY;
|
||||||
|
m_inputEventHandle->m_mouse.deltaX = 0;
|
||||||
|
m_inputEventHandle->m_mouse.deltaY = 0;
|
||||||
|
m_inputEventHandle->m_mouse.wheelDelta = 0;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Called at the end of event processing */
|
||||||
|
void InputProcessing::end() const
|
||||||
|
{
|
||||||
|
// This function is called at the end of event processing
|
||||||
|
|
||||||
|
// Update key states
|
||||||
|
for (auto &state : m_inputEventHandle->m_keys.keyStates | views::values)
|
||||||
|
{
|
||||||
|
if (state == InputManager::KeyState::Pressed)
|
||||||
|
state = InputManager::KeyState::Held;
|
||||||
|
else if (state == InputManager::KeyState::Released)
|
||||||
|
state = InputManager::KeyState::Up;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mouse button states
|
||||||
|
for (auto &state : m_inputEventHandle->m_mouse.buttonStates | views::values)
|
||||||
|
{
|
||||||
|
if (state == InputManager::KeyState::Pressed)
|
||||||
|
state = InputManager::KeyState::Held;
|
||||||
|
else if (state == InputManager::KeyState::Released)
|
||||||
|
state = InputManager::KeyState::Up;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
126
src/quokka_gfx/core/events/inputManager.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../utils/point.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class InputProcessing;
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class InputManager
|
||||||
|
{
|
||||||
|
friend class InputProcessing;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class KeyState : uint8_t
|
||||||
|
{
|
||||||
|
Up = 0, // Released
|
||||||
|
Pressed, // Pressed (this frame only)
|
||||||
|
Held, // Held
|
||||||
|
Released // Released (this frame only)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MouseButton : uint8_t
|
||||||
|
{
|
||||||
|
Left = SDL_BUTTON_LEFT,
|
||||||
|
Middle = SDL_BUTTON_MIDDLE,
|
||||||
|
Right = SDL_BUTTON_RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
InputManager() = default; // Default constructor
|
||||||
|
virtual ~InputManager() = default; // Default destructor
|
||||||
|
InputManager(const InputManager &obj) = delete; // Copy constructor
|
||||||
|
InputManager(InputManager &&obj) = delete; // Move constructor
|
||||||
|
InputManager &operator=(const InputManager &obj) = delete; // Copy assignment operator
|
||||||
|
InputManager &operator=(InputManager &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
InputProcessing getEventProcessing(); // Get event processing session
|
||||||
|
|
||||||
|
// Keyboard accessors
|
||||||
|
[[nodiscard]] KeyState getKeyState(SDL_Scancode key) const; // Get the current state of a key
|
||||||
|
[[nodiscard]] bool isKeyPressed(SDL_Scancode key) const; // Check if a key was pressed this frame
|
||||||
|
[[nodiscard]] bool isKeyHeld(SDL_Scancode key) const; // Check if a key is currently held down
|
||||||
|
[[nodiscard]] bool isKeyReleased(SDL_Scancode key) const; // Check if a key was released this frame
|
||||||
|
[[nodiscard]] bool isKeyDown(SDL_Scancode key) const; // Check if a key is currently down
|
||||||
|
[[nodiscard]] bool isKeyUp(SDL_Scancode key) const; // Check if a key is currently up
|
||||||
|
|
||||||
|
[[nodiscard]] bool isShiftDown() const; // Check if Shift modifier is currently down
|
||||||
|
[[nodiscard]] bool isLShiftDown() const; // Check if Left Shift modifier is currently down
|
||||||
|
[[nodiscard]] bool isRShiftDown() const; // Check if Right Shift modifier is currently down
|
||||||
|
[[nodiscard]] bool isCtrlDown() const; // Check if Ctrl modifier is currently down
|
||||||
|
[[nodiscard]] bool isLCtrlDown() const; // Check if Left Ctrl modifier is currently down
|
||||||
|
[[nodiscard]] bool isRCtrlDown() const; // Check if Right Ctrl modifier is currently down
|
||||||
|
[[nodiscard]] bool isAltDown() const; // Check if Alt modifier is currently down
|
||||||
|
[[nodiscard]] bool isLAltDown() const; // Check if Left Alt modifier is currently down
|
||||||
|
[[nodiscard]] bool isRAltDown() const; // Check if Right Alt modifier is currently down
|
||||||
|
|
||||||
|
// Mouse accessors
|
||||||
|
[[nodiscard]] int getMouseX() const; // Get current mouse X position
|
||||||
|
[[nodiscard]] int getMouseY() const; // Get current mouse Y position
|
||||||
|
[[nodiscard]] Point getMousePosition() const; // Get current mouse position as a rectangle (1x1)
|
||||||
|
[[nodiscard]] int getMouseDeltaX() const; // Get mouse movement delta X since last frame
|
||||||
|
[[nodiscard]] int getMouseDeltaY() const; // Get mouse movement delta Y since last frame
|
||||||
|
|
||||||
|
[[nodiscard]] KeyState getMouseButtonState(MouseButton button) const; // Get the current state of a mouse button
|
||||||
|
[[nodiscard]] bool isMouseButtonPressed(MouseButton button) const; // Check if a mouse button was pressed this frame
|
||||||
|
[[nodiscard]] bool isMouseButtonHeld(MouseButton button) const; // Check if a mouse button is currently held down
|
||||||
|
[[nodiscard]] bool isMouseButtonReleased(MouseButton button) const; // Check if a mouse button was released this frame
|
||||||
|
[[nodiscard]] bool isMouseButtonDown(MouseButton button) const; // Check if a mouse button is currently down
|
||||||
|
[[nodiscard]] bool isMouseButtonUp(MouseButton button) const; // Check if a mouse button is currently up
|
||||||
|
|
||||||
|
[[nodiscard]] int getMouseWheelDelta() const; // Get scroll wheel delta since last frame
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Key states
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
std::unordered_map<SDL_Scancode, KeyState> keyStates; // Key states (Pressed, Held, Released)
|
||||||
|
std::unordered_set<SDL_Scancode> keysPressed; // Keys pressed this frame
|
||||||
|
std::unordered_set<SDL_Scancode> keysReleased; // Keys released this frame
|
||||||
|
} m_keys;
|
||||||
|
|
||||||
|
// Mouse states, position and movement
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
std::unordered_map<MouseButton, KeyState> buttonStates; // Button states (Pressed, Held, Released)
|
||||||
|
int posX = 0; // Current X position of the mouse
|
||||||
|
int posY = 0; // Current Y position of the mouse
|
||||||
|
int lastPosX = 0; // Previous X position of the mouse (for delta calculation)
|
||||||
|
int lastPosY = 0; // Previous Y position of the mouse (for delta calculation)
|
||||||
|
int deltaX = 0; // Movement delta X since last frame
|
||||||
|
int deltaY = 0; // Movement delta Y since last frame
|
||||||
|
int wheelDelta = 0; // Scroll wheel delta since last frame
|
||||||
|
} m_mouse;
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class InputProcessing
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InputProcessing() = delete; // Default constructor
|
||||||
|
virtual ~InputProcessing(); // Default destructor
|
||||||
|
InputProcessing(const InputProcessing &obj) = delete; // Copy constructor
|
||||||
|
InputProcessing(InputProcessing &&obj) = delete; // Move constructor
|
||||||
|
InputProcessing &operator=(const InputProcessing &obj) = delete; // Copy assignment operator
|
||||||
|
InputProcessing &operator=(InputProcessing &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit InputProcessing(InputManager *inputEventHandle); // Constructor
|
||||||
|
|
||||||
|
void process(const SDL_Event &event) const; // Process an SDL event
|
||||||
|
|
||||||
|
protected:
|
||||||
|
InputManager *m_inputEventHandle = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void begin() const; // Called at the beginning of event processing
|
||||||
|
void end() const; // Called at the end of event processing
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
173
src/quokka_gfx/core/renderer/rendererManager.cpp
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
#include "rendererManager.h"
|
||||||
|
|
||||||
|
#include "../application.h"
|
||||||
|
#include "../assets/assetManager.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace quokka_gfx;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
RendererManager::RendererManager(Application *app)
|
||||||
|
{
|
||||||
|
m_app = app;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the underlying SDL_Renderer */
|
||||||
|
SDL_Renderer *RendererManager::getSDLRenderer() const
|
||||||
|
{
|
||||||
|
if (!m_app || !m_app->m_sdlRenderer)
|
||||||
|
throw std::runtime_error("SDL Renderer not initialized");
|
||||||
|
|
||||||
|
return m_app->m_sdlRenderer;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Present the rendered content to the screen */
|
||||||
|
void RendererManager::present() const
|
||||||
|
{
|
||||||
|
SDL_RenderPresent(getSDLRenderer());
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Clear the renderer with a specific color */
|
||||||
|
void RendererManager::clear(const Color &color) const
|
||||||
|
{
|
||||||
|
setDrawColor(color);
|
||||||
|
SDL_RenderClear(getSDLRenderer());
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Draw a rectangle */
|
||||||
|
void RendererManager::drawRect(const Rect &rect, const Color &color, const bool filled) const
|
||||||
|
{
|
||||||
|
setDrawColor(color);
|
||||||
|
const auto sdlRect = rect.toSDL();
|
||||||
|
if (filled)
|
||||||
|
SDL_RenderFillRect(getSDLRenderer(), &sdlRect);
|
||||||
|
else
|
||||||
|
SDL_RenderDrawRect(getSDLRenderer(), &sdlRect);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Draw a line between two points */
|
||||||
|
void RendererManager::drawLine(const Point &start, const Point &end, const Color &color) const
|
||||||
|
{
|
||||||
|
setDrawColor(color);
|
||||||
|
SDL_RenderDrawLine(getSDLRenderer(), start.x, start.y, end.x, end.y);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Draw a single point */
|
||||||
|
void RendererManager::drawPoint(const Point &point, const Color &color) const
|
||||||
|
{
|
||||||
|
setDrawColor(color);
|
||||||
|
SDL_RenderDrawPoint(getSDLRenderer(), point.x, point.y);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Draw text at a specific position */
|
||||||
|
void RendererManager::drawText(const std::string &text, const HashID &fontID, const Point &point, const Color &color, int fontSize) const
|
||||||
|
{
|
||||||
|
const auto font = m_app->m_assetManager->getFont(fontID);
|
||||||
|
|
||||||
|
const auto textTexture = renderTextTexture(text, font, color);
|
||||||
|
if (!textTexture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int texW, texH;
|
||||||
|
SDL_QueryTexture(textTexture.get(), nullptr, nullptr, &texW, &texH);
|
||||||
|
|
||||||
|
const Rect destRect(point.x, point.y, texW, texH);
|
||||||
|
drawTexture(textTexture.get(), destRect);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Draw text centered within a rectangle */
|
||||||
|
void RendererManager::drawTextCentered(const std::string &text, const HashID &fontID, const Rect &rect, const Color &color, int fontSize) const
|
||||||
|
{
|
||||||
|
const auto font = m_app->m_assetManager->getFont(fontID);
|
||||||
|
|
||||||
|
const auto textTexture = renderTextTexture(text, font, color);
|
||||||
|
if (!textTexture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int texW, texH;
|
||||||
|
SDL_QueryTexture(textTexture.get(), nullptr, nullptr, &texW, &texH);
|
||||||
|
|
||||||
|
// Center the text within the given rectangle
|
||||||
|
const int x = rect.x + (rect.w - texW) / 2;
|
||||||
|
const int y = rect.y + (rect.h - texH) / 2;
|
||||||
|
|
||||||
|
const Rect destRect(x, y, texW, texH);
|
||||||
|
drawTexture(textTexture.get(), destRect);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Draw a texture at a specific position and size */
|
||||||
|
void RendererManager::drawTexture(SDL_Texture *texture, const Rect &dest, const Rect &source) const
|
||||||
|
{
|
||||||
|
if (!texture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const SDL_Rect sdlDest = dest.toSDL();
|
||||||
|
const SDL_Rect *sdlSrc = nullptr;
|
||||||
|
SDL_Rect srcRect;
|
||||||
|
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
srcRect = source.toSDL();
|
||||||
|
sdlSrc = &srcRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderCopy(getSDLRenderer(), texture, sdlSrc, &sdlDest);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Set the current drawing color */
|
||||||
|
void RendererManager::setDrawColor(const Color &color) const
|
||||||
|
{
|
||||||
|
SDL_SetRenderDrawColor(getSDLRenderer(),
|
||||||
|
color.r(),
|
||||||
|
color.g(),
|
||||||
|
color.b(),
|
||||||
|
color.a());
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Set the clipping rectangle (nullptr to disable) */
|
||||||
|
void RendererManager::setClipRect(const Rect *rect) const
|
||||||
|
{
|
||||||
|
if (rect)
|
||||||
|
{
|
||||||
|
const SDL_Rect sdlRect = rect->toSDL();
|
||||||
|
SDL_RenderSetClipRect(getSDLRenderer(), &sdlRect);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SDL_RenderSetClipRect(getSDLRenderer(), nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the current window size in pixels */
|
||||||
|
SDL_Point RendererManager::getWindowSize() const
|
||||||
|
{
|
||||||
|
int w, h;
|
||||||
|
SDL_GetRendererOutputSize(getSDLRenderer(), &w, &h);
|
||||||
|
return { w, h };
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Render text to a texture for drawing */
|
||||||
|
Texture RendererManager::renderTextTexture(const std::string &text, TTF_Font *font, const Color &color) const
|
||||||
|
{
|
||||||
|
if (text.empty())
|
||||||
|
return { nullptr, SDL_DestroyTexture };
|
||||||
|
|
||||||
|
const SDL_Color sdlColor = { color.r(), color.g(), color.b(), color.a() };
|
||||||
|
SDL_Surface *surface = TTF_RenderText_Blended(font, text.c_str(), sdlColor);
|
||||||
|
if (!surface)
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to render text surface: " << TTF_GetError() << std::endl;
|
||||||
|
return { nullptr, SDL_DestroyTexture };
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture *texture = SDL_CreateTextureFromSurface(getSDLRenderer(), surface);
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
|
||||||
|
if (!texture)
|
||||||
|
std::cerr << "Failed to create texture from text: " << SDL_GetError() << std::endl;
|
||||||
|
|
||||||
|
return { texture, SDL_DestroyTexture };
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
64
src/quokka_gfx/core/renderer/rendererManager.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../defs.h"
|
||||||
|
#include "../../utils/color.h"
|
||||||
|
#include "../../utils/point.h"
|
||||||
|
#include "../../utils/rect.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class Application;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class RendererManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RendererManager() = delete; // Default constructor
|
||||||
|
virtual ~RendererManager() = default; // Default destructor
|
||||||
|
RendererManager(const RendererManager &obj) = delete; // Copy constructor
|
||||||
|
RendererManager(RendererManager &&obj) = delete; // Move constructor
|
||||||
|
RendererManager &operator=(const RendererManager &obj) = delete; // Copy assignment operator
|
||||||
|
RendererManager &operator=(RendererManager &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit RendererManager(Application *app); // Constructor
|
||||||
|
|
||||||
|
[[nodiscard]] SDL_Renderer *getSDLRenderer() const; // Get the underlying SDL_Renderer
|
||||||
|
|
||||||
|
void present() const; // Present the rendered content to the screen
|
||||||
|
|
||||||
|
void clear(const Color &color = Black) const; // Clear the renderer with a specific color
|
||||||
|
|
||||||
|
// Drawing primitives
|
||||||
|
void drawRect(const Rect &rect, const Color &color, bool filled = false) const; // Draw a rectangle
|
||||||
|
void drawLine(const Point &start, const Point &end, const Color &color) const; // Draw a line between two points
|
||||||
|
void drawPoint(const Point &point, const Color &color) const; // Draw a single point
|
||||||
|
|
||||||
|
// Text rendering
|
||||||
|
void drawText(const std::string &text, // Draw text at a specific position
|
||||||
|
const HashID &fontID,
|
||||||
|
const Point &point,
|
||||||
|
const Color &color = White,
|
||||||
|
int fontSize = 24) const;
|
||||||
|
void drawTextCentered(const std::string &text, // Draw text centered within a rectangle
|
||||||
|
const HashID &fontID,
|
||||||
|
const Rect &rect,
|
||||||
|
const Color &color = White,
|
||||||
|
int fontSize = 24) const;
|
||||||
|
|
||||||
|
// Texture management
|
||||||
|
void drawTexture(SDL_Texture *texture, const Rect &dest, const Rect &source = {}) const; // Draw a texture at a specific position and size
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
void setDrawColor(const Color &color) const; // Set the current drawing color
|
||||||
|
void setClipRect(const Rect *rect) const; // Set the clipping rectangle (nullptr to disable)
|
||||||
|
[[nodiscard]] SDL_Point getWindowSize() const; // Get the current window size in pixels
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Application *m_app = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Texture renderTextTexture(const std::string &text, TTF_Font *font, const Color &color) const; // Render text to a texture for drawing
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
61
src/quokka_gfx/core/screens/screen.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../application.h"
|
||||||
|
#include "../events/eventBus.h"
|
||||||
|
#include "../events/inputManager.h"
|
||||||
|
#include "quokka_gfx/utils/rect.h"
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class RendererManager;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class Screen : public Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Screen() = delete; // Default constructor
|
||||||
|
virtual ~Screen() = default; // Default destructor
|
||||||
|
Screen(const Screen &obj) = delete; // Copy constructor
|
||||||
|
Screen(Screen &&obj) = delete; // Move constructor
|
||||||
|
Screen &operator=(const Screen &obj) = delete; // Copy assignment operator
|
||||||
|
Screen &operator=(Screen &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit Screen(Application *app); // Constructor
|
||||||
|
|
||||||
|
virtual void on_enter() = 0; // Called when the screen is entered
|
||||||
|
virtual void on_exit() = 0; // Called when the screen is exited
|
||||||
|
virtual void handleEvents() = 0; // Manage events
|
||||||
|
virtual void update(const double deltaTime) = 0; // Update the logic of the screen
|
||||||
|
virtual void render(RendererManager *renderer) = 0; // Render the screen
|
||||||
|
|
||||||
|
[[nodiscard]] InputManager *getInputManager() const; // Get the input manager for handling events
|
||||||
|
|
||||||
|
Rect getScreenRect() const; // Return the window size as a rectangle for easy rendering and event handling
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Application *m_app = nullptr; // Pointer to the application
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
inline Screen::Screen(Application *app)
|
||||||
|
{
|
||||||
|
m_app = app;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the input manager for handling events */
|
||||||
|
inline InputManager *Screen::getInputManager() const
|
||||||
|
{
|
||||||
|
return m_app->m_inputManager.get();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Return the window size as a rectangle for easy rendering and event handling */
|
||||||
|
inline Rect Screen::getScreenRect() const
|
||||||
|
{
|
||||||
|
if (!m_app || !m_app->m_window)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
SDL_GetWindowSize(m_app->m_window, &width, &height);
|
||||||
|
return Rect(0, 0, width, height);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
96
src/quokka_gfx/core/screens/screenManager.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "screenManager.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace quokka_gfx;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Default constructor */
|
||||||
|
ScreenManager::ScreenManager()
|
||||||
|
: m_screenTransition(this)
|
||||||
|
{
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Set the current screen, replacing any existing screens on the stack */
|
||||||
|
void ScreenManager::setScreen(std::unique_ptr<Screen> screen)
|
||||||
|
{
|
||||||
|
// Clear the stack of screens
|
||||||
|
while (!m_screenStack.empty())
|
||||||
|
{
|
||||||
|
// Call on_exit for the current screen
|
||||||
|
m_screenStack.top()->on_exit();
|
||||||
|
|
||||||
|
// Remove the current screen
|
||||||
|
m_screenStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the new screen on the stack
|
||||||
|
m_screenStack.push(std::move(screen));
|
||||||
|
|
||||||
|
// Call on_enter for the new screen
|
||||||
|
if (!m_screenStack.empty())
|
||||||
|
m_screenStack.top()->on_enter();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Push a new screen onto the stack */
|
||||||
|
void ScreenManager::pushScreen(std::unique_ptr<Screen> screen)
|
||||||
|
{
|
||||||
|
// Insert the new screen on top of the stack
|
||||||
|
m_screenStack.push(std::move(screen));
|
||||||
|
|
||||||
|
// Call on_enter for the new screen
|
||||||
|
if (!m_screenStack.empty())
|
||||||
|
m_screenStack.top()->on_enter();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Pop the current screen from the stack */
|
||||||
|
void ScreenManager::popScreen()
|
||||||
|
{
|
||||||
|
if (!m_screenStack.empty())
|
||||||
|
{
|
||||||
|
// Call on_exit for the current screen
|
||||||
|
m_screenStack.top()->on_exit();
|
||||||
|
|
||||||
|
// Remove the current screen
|
||||||
|
m_screenStack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Handle events for the current screen */
|
||||||
|
void ScreenManager::handleEvents()
|
||||||
|
{
|
||||||
|
// If a screen transition is in progress, block event handling for the current screen
|
||||||
|
if (m_screenTransition.getState() != ScreenTransition::State::None)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_screenStack.empty())
|
||||||
|
m_screenStack.top()->handleEvents();
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Update the current screen */
|
||||||
|
void ScreenManager::update(const double deltaTime)
|
||||||
|
{
|
||||||
|
if (!m_screenStack.empty())
|
||||||
|
m_screenStack.top()->update(deltaTime);
|
||||||
|
|
||||||
|
// Update the screen transition
|
||||||
|
m_screenTransition.update(deltaTime);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Render the current screen */
|
||||||
|
void ScreenManager::render(RendererManager *renderer)
|
||||||
|
{
|
||||||
|
if (!m_screenStack.empty())
|
||||||
|
m_screenStack.top()->render(renderer);
|
||||||
|
|
||||||
|
// Render the screen transition
|
||||||
|
m_screenTransition.render(renderer);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the current screen */
|
||||||
|
Screen *ScreenManager::getCurrentScreen() const
|
||||||
|
{
|
||||||
|
if (!m_screenStack.empty())
|
||||||
|
return m_screenStack.top().get();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
39
src/quokka_gfx/core/screens/screenManager.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "screen.h"
|
||||||
|
#include "screenTransition.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class RendererManager;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class ScreenManager
|
||||||
|
{
|
||||||
|
friend class ScreenTransition;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScreenManager(); // Default constructor
|
||||||
|
virtual ~ScreenManager() = default; // Default destructor
|
||||||
|
ScreenManager(const ScreenManager &obj) = delete; // Copy constructor
|
||||||
|
ScreenManager(ScreenManager &&obj) = delete; // Move constructor
|
||||||
|
ScreenManager &operator=(const ScreenManager &obj) = delete; // Copy assignment operator
|
||||||
|
ScreenManager &operator=(ScreenManager &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
void setScreen(std::unique_ptr<Screen> screen); // Set the current screen, replacing any existing screens on the stack
|
||||||
|
void pushScreen(std::unique_ptr<Screen> screen); // Push a new screen onto the stack
|
||||||
|
void popScreen(); // Pop the current screen from the stack
|
||||||
|
|
||||||
|
void handleEvents(); // Handle events for the current screen
|
||||||
|
void update(double deltaTime); // Update the current screen
|
||||||
|
void render(RendererManager *renderer); // Render the current screen
|
||||||
|
|
||||||
|
[[nodiscard]] Screen *getCurrentScreen() const; // Get the current screen
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ScreenTransition m_screenTransition; // Screen transition manager
|
||||||
|
std::stack<std::unique_ptr<Screen>> m_screenStack; // Stack of screens
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
33
src/quokka_gfx/core/screens/screenTransition.cpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include "screenManager.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace quokka_gfx;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
ScreenTransition::ScreenTransition(ScreenManager *screenManager)
|
||||||
|
{
|
||||||
|
m_screenManager = screenManager;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the current state of the screen transition */
|
||||||
|
ScreenTransition::State ScreenTransition::getState() const
|
||||||
|
{
|
||||||
|
return m_state;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Get the progress of the screen transition (0.0 to 1.0) */
|
||||||
|
float ScreenTransition::getProgress() const
|
||||||
|
{
|
||||||
|
return m_progress;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Manage the transition between screens, updating the current screen */
|
||||||
|
void ScreenTransition::update(double deltaTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Render the transition between screens, rendering the current screen */
|
||||||
|
void ScreenTransition::render(RendererManager *renderer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
52
src/quokka_gfx/core/screens/screenTransition.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../utils/color.h"
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
class RendererManager;
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class ScreenTransition
|
||||||
|
{
|
||||||
|
static constexpr Color BackgroundColor{ 30, 30, 30 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class State : uint8_t
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
TransitioningIn,
|
||||||
|
TransitioningOut
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScreenTransition() = delete; // Default constructor
|
||||||
|
virtual ~ScreenTransition() = default; // Default destructor
|
||||||
|
ScreenTransition(const ScreenTransition &obj) = delete; // Copy constructor
|
||||||
|
ScreenTransition(ScreenTransition &&obj) = delete; // Move constructor
|
||||||
|
ScreenTransition &operator=(const ScreenTransition &obj) = delete; // Copy assignment operator
|
||||||
|
ScreenTransition &operator=(ScreenTransition &&obj) = delete; // Move assignment operator
|
||||||
|
|
||||||
|
explicit ScreenTransition(ScreenManager *screenManager); // Constructor
|
||||||
|
|
||||||
|
// void setScreen(std::unique_ptr<Screen> screen); // Set the current screen, replacing any existing screens on the stack
|
||||||
|
// void pushScreen(std::unique_ptr<Screen> screen); // Push a new screen onto the stack
|
||||||
|
// void popScreen(); // Pop the current screen from the stack
|
||||||
|
|
||||||
|
[[nodiscard]] State getState() const; // Get the current state of the screen transition
|
||||||
|
[[nodiscard]] float getProgress() const; // Get the progress of the screen transition (0.0 to 1.0)
|
||||||
|
|
||||||
|
void update(double deltaTime); // Manage the transition between screens, updating the current screen
|
||||||
|
void render(RendererManager *renderer); // Render the transition between screens, rendering the current screen
|
||||||
|
|
||||||
|
//[[nodiscard]] Screen *getCurrentScreen() const; // Get the current screen
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ScreenManager *m_screenManager = nullptr; // Pointer to the screen manager
|
||||||
|
|
||||||
|
State m_state = State::None; // Current state of the screen transition
|
||||||
|
float m_progress = 0.0f; // Progress of the screen transition (0.0 to 1.0)
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
26
src/quokka_gfx/quokka_gfx.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* QuokkaGFX - Lightweight SDL2 wrapper - umbrella header
|
||||||
|
*
|
||||||
|
* Single header include for easy integration.
|
||||||
|
* Usage: #include "quokka_gfx/quokka_gfx.h"
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Core classes
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/assets/assetManager.h"
|
||||||
|
#include "core/defs.h"
|
||||||
|
#include "core/events/inputManager.h"
|
||||||
|
#include "core/renderer/rendererManager.h"
|
||||||
|
#include "core/screens/screen.h"
|
||||||
|
#include "core/screens/screenManager.h"
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
#include "utils/color.h"
|
||||||
|
#include "utils/point.h"
|
||||||
|
#include "utils/rect.h"
|
||||||
|
|
||||||
|
// SDL2 main headers
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <SDL2/SDL_ttf.h>
|
||||||
92
src/quokka_gfx/utils/color.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class Color
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr Color() = default; // Default constructor
|
||||||
|
virtual ~Color() = default; // Default destructor
|
||||||
|
Color(const Color &obj) = default; // Copy constructor
|
||||||
|
Color(Color &&obj) = default; // Move constructor
|
||||||
|
Color &operator=(const Color &obj) = default; // Copy assignment operator
|
||||||
|
Color &operator=(Color &&obj) = default; // Move assignment operator
|
||||||
|
|
||||||
|
constexpr explicit Color(uint32_t color = 0xFFFFFFFF); // Constructor
|
||||||
|
constexpr explicit Color(uint8_t red = 255, // Constructor
|
||||||
|
uint8_t green = 255,
|
||||||
|
uint8_t blue = 255,
|
||||||
|
uint8_t alpha = 255);
|
||||||
|
|
||||||
|
[[nodiscard]] uint32_t operator()() const; // Returns the color as a single uint32_t in ARGB format
|
||||||
|
[[nodiscard]] uint8_t r() const; // Returns the red component
|
||||||
|
[[nodiscard]] uint8_t g() const; // Returns the green component
|
||||||
|
[[nodiscard]] uint8_t b() const; // Returns the blue component
|
||||||
|
[[nodiscard]] uint8_t a() const; // Returns the alpha component
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t m_color = 0xFFFFFFFF; // Color value in ARGB format for fast comparisons and hashing
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
inline constexpr Color::Color(const uint32_t color)
|
||||||
|
{
|
||||||
|
m_color = color;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
inline constexpr Color::Color(const uint8_t red, const uint8_t green, const uint8_t blue, const uint8_t alpha)
|
||||||
|
{
|
||||||
|
m_color = (static_cast<uint32_t>(alpha) << 24) |
|
||||||
|
(static_cast<uint32_t>(red) << 16) |
|
||||||
|
(static_cast<uint32_t>(green) << 8) |
|
||||||
|
static_cast<uint32_t>(blue);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Returns the color as a single uint32_t in ARGB format */
|
||||||
|
inline uint32_t Color::operator()() const
|
||||||
|
{
|
||||||
|
return m_color;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Returns the red component */
|
||||||
|
inline uint8_t Color::r() const
|
||||||
|
{
|
||||||
|
return (m_color >> 16) & 0xFF;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Returns the green component */
|
||||||
|
inline uint8_t Color::g() const
|
||||||
|
{
|
||||||
|
return (m_color >> 8) & 0xFF;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Returns the blue component */
|
||||||
|
inline uint8_t Color::b() const
|
||||||
|
{
|
||||||
|
return m_color & 0xFF;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Returns the alpha component */
|
||||||
|
inline uint8_t Color::a() const
|
||||||
|
{
|
||||||
|
return (m_color >> 24) & 0xFF;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
|
||||||
|
/* --- */
|
||||||
|
|
||||||
|
//------------------------------------------------------------
|
||||||
|
inline constexpr Color White{ 255, 255, 255, 255 };
|
||||||
|
inline constexpr Color Black{ 0, 0, 0, 255 };
|
||||||
|
inline constexpr Color Red{ 255, 0, 0, 255 };
|
||||||
|
inline constexpr Color Green{ 0, 255, 0, 255 };
|
||||||
|
inline constexpr Color Blue{ 0, 0, 255, 255 };
|
||||||
|
inline constexpr Color Yellow{ 255, 255, 0, 255 };
|
||||||
|
inline constexpr Color Gray{ 128, 128, 128, 255 };
|
||||||
|
inline constexpr Color DarkGray{ 64, 64, 64, 255 };
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||
46
src/quokka_gfx/utils/point.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
namespace quokka_gfx
|
||||||
|
{
|
||||||
|
//------------------------------------------------------------
|
||||||
|
class Point
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Point() = default; // Default constructor
|
||||||
|
virtual ~Point() = default; // Default destructor
|
||||||
|
Point(const Point &obj) = default; // Copy constructor
|
||||||
|
Point(Point &&obj) = default; // Move constructor
|
||||||
|
Point &operator=(const Point &obj) = default; // Copy assignment operator
|
||||||
|
Point &operator=(Point &&obj) = default; // Move assignment operator
|
||||||
|
|
||||||
|
explicit Point(int xPos = 0, // Constructor
|
||||||
|
int yPos = 0);
|
||||||
|
|
||||||
|
bool operator==(const Point &point) const; // Equality operator
|
||||||
|
|
||||||
|
[[nodiscard]] SDL_Point toSDL() const; // Convert to SDL_Point for rendering
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
};
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Constructor */
|
||||||
|
inline Point::Point(const int xPos, const int yPos)
|
||||||
|
{
|
||||||
|
x = xPos;
|
||||||
|
y = yPos;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
inline bool Point::operator==(const Point &point) const
|
||||||
|
{
|
||||||
|
return x == point.x && y == point.y;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
/* Convert to SDL_Point for rendering */
|
||||||
|
inline SDL_Point Point::toSDL() const
|
||||||
|
{
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------
|
||||||
|
} // namespace quokka_gfx
|
||||||