Adds application files

This commit is contained in:
Sylvain Schneider
2026-02-16 15:41:41 +01:00
parent 647a5e2119
commit 08796c2f66
105 changed files with 18099 additions and 1 deletions

37
.clang-format Normal file
View 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
View File

@@ -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++
# Prerequisites
*.d
@@ -31,4 +85,9 @@
*.exe
*.out
*.app
build/
# Editor's mess
.vscode/
.vs/

288
CMakeLists.txt Normal file
View 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
View 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"
}
}
]
}

View File

@@ -1,2 +1,2 @@
# quokkanoid
# Quokkanoid.

BIN
assets/Breakout Tile Set Free/.DS_Store vendored Normal file

Binary file not shown.

View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

View File

@@ -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>

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

BIN
assets/home.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

BIN
src/assets/home.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

12870
src/assets/home.jpg.h Normal file

File diff suppressed because it is too large Load Diff

67
src/logger/logger.h Normal file
View 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
View 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;
}
//--------------------------------------------------------------

View 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();
}
//------------------------------------------------------------

View 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

View 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);
}
//------------------------------------------------------------

View 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

View 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

View 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);
}
//------------------------------------------------------------

View 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

View 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;
}
}
//------------------------------------------------------------

View 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

View 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 };
}
//------------------------------------------------------------

View 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

View 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

View 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;
}
//------------------------------------------------------------

View 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

View 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)
{
}
//------------------------------------------------------------

View 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

View 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>

View 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

View 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

Some files were not shown because too many files have changed in this diff Show More