commit
278edfcb2b
@ -0,0 +1,33 @@ |
||||
--- |
||||
name: Bug report |
||||
about: Create a report to help us improve |
||||
title: '' |
||||
labels: bug |
||||
assignees: Moros1138 |
||||
|
||||
--- |
||||
|
||||
**Describe the bug** |
||||
A clear and concise description of what the bug is. |
||||
|
||||
**To Reproduce** |
||||
Steps to reproduce the behavior: |
||||
1. Go to '...' |
||||
2. Click on '....' |
||||
3. Scroll down to '....' |
||||
4. See error |
||||
|
||||
**Expected behavior** |
||||
A clear and concise description of what you expected to happen. |
||||
|
||||
**Screenshots** |
||||
If applicable, add screenshots to help explain your problem. |
||||
|
||||
**Desktop (please complete the following information):** |
||||
- OS: [e.g. Arch, Manjaro, Ubuntu, Windows] |
||||
- Toolchain: [e.g. Clang, GCC, Visual Studio] |
||||
- Toolchain Version: [e.g. gcc 11.2.0] |
||||
- CMake Version: [e.g. 3.10] |
||||
|
||||
**Additional context** |
||||
Add any other context about the problem here. |
@ -0,0 +1,20 @@ |
||||
--- |
||||
name: Feature request |
||||
about: Suggest an idea for this project |
||||
title: "[Feature Request]" |
||||
labels: enhancement |
||||
assignees: Moros1138 |
||||
|
||||
--- |
||||
|
||||
**Is your feature request related to a problem? Please describe.** |
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] |
||||
|
||||
**Describe the solution you'd like** |
||||
A clear and concise description of what you want to happen. |
||||
|
||||
**Describe alternatives you've considered** |
||||
A clear and concise description of any alternative solutions or features you've considered. |
||||
|
||||
**Additional context** |
||||
Add any other context or screenshots about the feature request here. |
@ -0,0 +1,8 @@ |
||||
build |
||||
linux-build |
||||
emscripten-build |
||||
nmake-build |
||||
mingw-build |
||||
vs-build |
||||
.vscode |
||||
.cache |
@ -0,0 +1,292 @@ |
||||
# require version 3.10 or higher |
||||
cmake_minimum_required(VERSION 3.10) |
||||
|
||||
# |
||||
# Project |
||||
# |
||||
# - dictates the output executable filename |
||||
# |
||||
project(TestApp) |
||||
|
||||
# Options you can set via command-line |
||||
option(HAS_TERMINAL "Show a terminal window for STDOUT/STDERR" ON) |
||||
|
||||
# |
||||
# C_CXX_SOURCES_DIR |
||||
# |
||||
# - the place where your C/C++ source files are located |
||||
# |
||||
set(C_CXX_SOURCES_DIR "src") |
||||
|
||||
# |
||||
# C_CXX_HEADERS_DIR |
||||
# |
||||
# - the place where your C/C++ header files are located |
||||
# |
||||
set(C_CXX_HEADERS_DIR "include") |
||||
|
||||
# |
||||
# ASSETS_DIR |
||||
# |
||||
# - the place where your pictures, sound files, etc.. live |
||||
# |
||||
set(ASSETS_DIR "assets") |
||||
|
||||
########################################################################## |
||||
# DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING!! # |
||||
########################################################################## |
||||
|
||||
# Set C++ Standards |
||||
set(CMAKE_CXX_STANDARD 20) |
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON) |
||||
set(CMAKE_CXX_EXTENSIONS OFF) |
||||
|
||||
# output executable basename |
||||
set(OutputExecutable "${CMAKE_PROJECT_NAME}") |
||||
|
||||
###################################################################### |
||||
# Directories |
||||
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") |
||||
|
||||
# We need to specify the output for each configuration to make it work |
||||
# on Visual Studio solutions. |
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin") |
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin") |
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin") |
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_PROFILE "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_PROFILE "${CMAKE_BINARY_DIR}/lib") |
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_PROFILE "${CMAKE_BINARY_DIR}/bin") |
||||
|
||||
set(SOURCE_DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${ASSETS_DIR}) |
||||
set(SOURCE_CXX_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${C_CXX_HEADERS_DIR}) |
||||
set(SOURCE_CXX_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${C_CXX_SOURCES_DIR}) |
||||
|
||||
# Source Files are Curated Here |
||||
file( |
||||
GLOB_RECURSE SOURCE_CXX_FILES |
||||
"${SOURCE_CXX_SRC_DIR}/*.cpp" |
||||
) |
||||
|
||||
# Search in the "cmake" directory for additional CMake modules. |
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) |
||||
|
||||
# Executable aka binary output |
||||
add_executable(${OutputExecutable} ${SOURCE_CXX_FILES}) |
||||
|
||||
###################################################################### |
||||
# MacOS |
||||
###################################################################### |
||||
|
||||
|
||||
if(APPLE) |
||||
|
||||
# OpenGL |
||||
set(OpenGL_GL_PREFERENCE LEGACY) |
||||
find_package(OpenGL REQUIRED) |
||||
include_directories(${OpenGL_INCLUDE_DIRS}) |
||||
target_link_libraries(${OutputExecutable} ${OpenGL_LIBRARIES} OpenGL::GL) |
||||
|
||||
# Carbon |
||||
FIND_LIBRARY(CARBON_LIBRARY Carbon) |
||||
target_link_libraries(${OutputExecutable} ${CARBON_LIBRARY}) |
||||
|
||||
# GLUT |
||||
find_package(GLUT REQUIRED) |
||||
target_link_libraries(${OutputExecutable} ${GLUT_LIBRARIES}) |
||||
|
||||
# Threads |
||||
find_package(Threads REQUIRED) |
||||
target_link_libraries(${OutputExecutable} Threads::Threads) |
||||
include_directories(${Threads_INCLUDE_DIRS}) |
||||
|
||||
find_package(PNG REQUIRED) |
||||
target_link_libraries(${OutputExecutable} PNG::PNG) |
||||
include_directories(${PNG_INCLUDE_DIRS}) |
||||
|
||||
endif() # APPLE |
||||
|
||||
###################################################################### |
||||
# Windows: MinGW |
||||
###################################################################### |
||||
if(WIN32 AND MINGW) |
||||
|
||||
# OpenGL |
||||
set(OpenGL_GL_PREFERENCE LEGACY) |
||||
find_package(OpenGL REQUIRED) |
||||
include_directories(${OpenGL_INCLUDE_DIRS}) |
||||
target_link_libraries(${OutputExecutable} ${OpenGL_LIBRARIES} OpenGL::GL) |
||||
|
||||
if (NOT HAS_TERMINAL) |
||||
target_link_libraries(${OutputExecutable} -mwindows -municode) |
||||
endif (NOT HAS_TERMINAL) |
||||
|
||||
# GDI+ |
||||
set(GDIPLUS_LIBRARY gdiplus) |
||||
target_link_libraries(${OutputExecutable} ${GDIPLUS_LIBRARY}) |
||||
|
||||
# Shlwapi |
||||
set(SHLWAPI_LIBRARY shlwapi) |
||||
target_link_libraries(${OutputExecutable} ${SHLWAPI_LIBRARY}) |
||||
|
||||
# Dwmapi |
||||
set(DWMAPI_LIBRARY dwmapi) |
||||
target_link_libraries(${OutputExecutable} ${DWMAPI_LIBRARY}) |
||||
|
||||
# stdc++fs |
||||
target_link_libraries(${OutputExecutable} stdc++fs) |
||||
|
||||
endif() |
||||
|
||||
###################################################################### |
||||
# Windows: Visual Studio / MSVC |
||||
###################################################################### |
||||
if(WIN32 AND MSVC) |
||||
|
||||
# OpenGL |
||||
set(OpenGL_GL_PREFERENCE LEGACY) |
||||
find_package(OpenGL REQUIRED) |
||||
include_directories(${OpenGL_INCLUDE_DIRS}) |
||||
target_link_libraries(${OutputExecutable} ${OpenGL_LIBRARIES} OpenGL::GL) |
||||
|
||||
# set the startup project to the target executable instead of ALL_BUILD |
||||
set_property( |
||||
DIRECTORY |
||||
${CMAKE_CURRENT_SOURCE_DIR} |
||||
PROPERTY |
||||
VS_STARTUP_PROJECT |
||||
${OutputExecutable} |
||||
) |
||||
|
||||
# set working directory for Visual Studio Debugger |
||||
set_target_properties( |
||||
${OutputExecutable} PROPERTIES |
||||
VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/bin" |
||||
) |
||||
|
||||
# set subsytem, console if HAS_TERMINAL is true. windows if not |
||||
if (HAS_TERMINAL) |
||||
target_link_options(${OutputExecutable} PRIVATE "/SUBSYSTEM:CONSOLE") |
||||
else () |
||||
target_link_options(${OutputExecutable} PRIVATE "/SUBSYSTEM:WINDOWS") |
||||
endif () |
||||
|
||||
# GDI+ |
||||
set(GDIPLUS_LIBRARY gdiplus) |
||||
target_link_libraries(${OutputExecutable} ${GDIPLUS_LIBRARY}) |
||||
|
||||
# Shlwapi |
||||
set(SHLWAPI_LIBRARY shlwapi) |
||||
target_link_libraries(${OutputExecutable} ${SHLWAPI_LIBRARY}) |
||||
|
||||
# Dwmapi |
||||
set(DWMAPI_LIBRARY dwmapi) |
||||
target_link_libraries(${OutputExecutable} ${DWMAPI_LIBRARY}) |
||||
|
||||
endif() # Visual Studio / MSVC |
||||
|
||||
###################################################################### |
||||
# Linux: using anything? |
||||
###################################################################### |
||||
if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN) |
||||
|
||||
# OpenGL |
||||
set(OpenGL_GL_PREFERENCE LEGACY) |
||||
find_package(OpenGL REQUIRED) |
||||
include_directories(${OpenGL_INCLUDE_DIRS}) |
||||
target_link_libraries(${OutputExecutable} ${OpenGL_LIBRARIES} OpenGL::GL) |
||||
|
||||
# X11 |
||||
find_package(X11 REQUIRED) |
||||
target_link_libraries(${OutputExecutable} X11::X11) |
||||
|
||||
include_directories(${X11_INCLUDE_DIRS}) |
||||
|
||||
# Threads |
||||
find_package(Threads REQUIRED) |
||||
target_link_libraries(${OutputExecutable} Threads::Threads) |
||||
include_directories(${Threads_INCLUDE_DIRS}) |
||||
|
||||
find_package(PNG REQUIRED) |
||||
target_link_libraries(${OutputExecutable} PNG::PNG) |
||||
include_directories(${PNG_INCLUDE_DIRS}) |
||||
|
||||
# stdc++fs |
||||
target_link_libraries(${OutputExecutable} stdc++fs) |
||||
|
||||
# dl, for miniaudio |
||||
target_link_libraries(${OutputExecutable} dl) |
||||
|
||||
endif() # Linux |
||||
|
||||
###################################################################### |
||||
# Emscripten |
||||
###################################################################### |
||||
if (EMSCRIPTEN) |
||||
|
||||
# Generate an HTML file |
||||
set(CMAKE_EXECUTABLE_SUFFIX .html) |
||||
|
||||
# Build Cache: SDL2_mixer, libpng, zlib |
||||
execute_process(COMMAND "${EMSCRIPTEN_ROOT_PATH}/embuilder${EMCC_SUFFIX}" build sdl2_mixer libpng zlib) |
||||
|
||||
if(EXISTS "${SOURCE_DATA_DIR}" AND IS_DIRECTORY "${SOURCE_DATA_DIR}") |
||||
target_link_options( |
||||
${OutputExecutable} |
||||
PRIVATE |
||||
-sALLOW_MEMORY_GROWTH=1 |
||||
-sMAX_WEBGL_VERSION=2 |
||||
-sMIN_WEBGL_VERSION=2 |
||||
-sUSE_LIBPNG=1 |
||||
-sLLD_REPORT_UNDEFINED |
||||
--preload-file ${SOURCE_DATA_DIR}@assets) |
||||
else() |
||||
target_link_options( |
||||
${OutputExecutable} |
||||
PRIVATE |
||||
-sALLOW_MEMORY_GROWTH=1 |
||||
-sMAX_WEBGL_VERSION=2 |
||||
-sMIN_WEBGL_VERSION=2 |
||||
-sUSE_LIBPNG=1 |
||||
-sLLD_REPORT_UNDEFINED) |
||||
endif() |
||||
|
||||
endif() # Emscripten |
||||
|
||||
###################################################################### |
||||
# Set include directory |
||||
###################################################################### |
||||
if(IS_DIRECTORY ${SOURCE_CXX_INCLUDE_DIR}) |
||||
include_directories(${SOURCE_CXX_INCLUDE_DIR}) |
||||
endif() |
||||
|
||||
###################################################################### |
||||
# Copy assets/ directory target |
||||
###################################################################### |
||||
|
||||
set(DATA_OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin/${ASSETS_DIR}) |
||||
|
||||
file(GLOB_RECURSE src_data_files |
||||
RELATIVE ${SOURCE_DATA_DIR}/ "${SOURCE_DATA_DIR}/*.*" "${SOURCE_DATA_DIR}/*") |
||||
foreach(fn ${src_data_files}) |
||||
add_custom_command( |
||||
OUTPUT ${DATA_OUTPUT_DIR}/${fn} |
||||
COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_DATA_DIR}/${fn} ${DATA_OUTPUT_DIR}/${fn} |
||||
MAIN_DEPENDENCY ${SOURCE_DATA_DIR}/${fn}) |
||||
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/${fn}) |
||||
endforeach() |
||||
|
||||
add_custom_target(copy_data DEPENDS ${out_data_files}) |
||||
|
||||
# Copy Asset Files, if not Emscripten |
||||
if (NOT EMSCRIPTEN) |
||||
add_dependencies(${OutputExecutable} copy_data) |
||||
endif() |
@ -0,0 +1,69 @@ |
||||
The following license applies to the repository, unless otherwised credit. |
||||
|
||||
+---------------------------------------------------------------------------- |
||||
| The Unlicense |
||||
+---------------------------------------------------------------------------- |
||||
|
||||
This is free and unencumbered software released into the public domain. |
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute |
||||
this software, either in source code form or as a compiled binary, for any |
||||
purpose, commercial or non-commercial, and by any means. |
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors of |
||||
this software dedicate any and all copyright interest in the software to |
||||
the public domain. We make this dedication for the benefit of the public |
||||
at large and to the detriment of our heirs and successors. We intend this |
||||
dedication to be an overt act of relinquishment in perpetuity of all present |
||||
and future rights to this software under copyright law. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
||||
For more information, please refer to the project's repository: |
||||
|
||||
https://github.com/Moros1138/pge-template-project or moros1138@gmail.com |
||||
|
||||
|
||||
+---------------------------------------------------------------------------- |
||||
| External Licenses |
||||
+---------------------------------------------------------------------------- |
||||
The following license applies to usage of the olcPixelGameEngine and olcSoundWaveEngine: |
||||
|
||||
https://github.com/OneLoneCoder/olcPixelGameEngine |
||||
https://github.com/OneLoneCoder/olcSoundWaveEngine |
||||
|
||||
|
||||
License (OLC-3) |
||||
~~~~~~~~~~~~~~~ |
||||
|
||||
Copyright 2018 - 2022 OneLoneCoder.com |
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, |
||||
are permitted provided that the following conditions are met: |
||||
|
||||
1. Redistributions or derivations of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce the above |
||||
copyright notice. This list of conditions and the following disclaimer must be |
||||
reproduced in the documentation and/or other materials provided with the distribution. |
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may |
||||
be used to endorse or promote products derived from this software without specific |
||||
prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||
SUCH DAMAGE. |
@ -0,0 +1,217 @@ |
||||
# PGE Template Project v2.25 |
||||
|
||||
This is a template project for use with the [olcPixelGameEngine](https://github.com/OneLoneCoder/olcPixelGameEngine). It serves as a jumping off point for you to build your masterpiece application. |
||||
|
||||
## Features |
||||
|
||||
CMake script for cross-platform building. Tested environments include: |
||||
|
||||
* Linux - with UNIX Makefiles, GNU GCC and LLVM Clang |
||||
* MacOS - with UNIX Makefiles, XCode and LLVM Clang |
||||
* Windows - with Visual Studio, NMake Makefiles, and MinGW Makefiles |
||||
* Emscripten - with UNIX Makefiles, NMake Makefiles, and MinGW Makefiles |
||||
|
||||
# Preparing your Environment |
||||
|
||||
The instructions to prepare your environment have been broken up for convenience. Simply follow the instructions that are pertinent to your situation. |
||||
|
||||
## Linux |
||||
|
||||
### **Requirements** |
||||
|
||||
* C/C++ Toolchain for your Linux distro |
||||
* CMake |
||||
* Git |
||||
* libpng |
||||
* Mesa OpenGL Development Libraries |
||||
|
||||
### **Ubuntu and Ubuntu based distros** |
||||
|
||||
Update your package manager by issuing the following command: |
||||
|
||||
``` |
||||
sudo apt update |
||||
``` |
||||
|
||||
Install toolchain and required software by issuing the following command: |
||||
|
||||
``` |
||||
sudo apt install build-essential cmake git libpng-dev libglu1-mesa-dev |
||||
``` |
||||
|
||||
### **Arch, Manjaro, and Arch based distros** |
||||
|
||||
``` |
||||
sudo pacman -Sy base-devel cmake git libpng mesa |
||||
``` |
||||
|
||||
## MacOS |
||||
|
||||
### **Requirements** |
||||
|
||||
* XCode |
||||
* [Homebrew Package Manager](https://brew.sh/) |
||||
* libpng |
||||
* CMake |
||||
* git |
||||
|
||||
Install XCode from the App Store. |
||||
|
||||
Open the ``Terminal`` App from Finder. go to Applications -> Utilities |
||||
|
||||
Follow the instructions at the [Homebrew Website](https://brew.sh/) to install the Homebrew package manager. |
||||
|
||||
Once Homebrew is installed, issue the following command to install ``cmake``,``libpng``, and ``git``: |
||||
|
||||
|
||||
``` |
||||
brew install libpng |
||||
brew install cmake |
||||
brew install git |
||||
``` |
||||
|
||||
## Windows |
||||
|
||||
### Requirements |
||||
|
||||
* Chocolatey |
||||
* CMake |
||||
* Toolchain (MinGW or Visual Studio / NMake) |
||||
|
||||
The following will be required whether you use MinGW or Visual Studio.You will need to open Powershell, as Administrator. |
||||
|
||||
Visit the [Chocolatey website](https://chocolatey.org/) for instructions on how to install Chocolatey. |
||||
|
||||
Once you've got Chocolatey installed, we can install CMake: |
||||
|
||||
``` |
||||
choco install cmake |
||||
``` |
||||
|
||||
Say ``yes`` to all of the scripts Chocolatey wants you to run! |
||||
|
||||
After the installation has completed, find the Cmake ``bin`` directory, it is typically ``C:\Program Files\CMake\bin`` and add it to your path! |
||||
|
||||
Confirm CMake is installed and in your path by issuing the following command in a Command Prompt: |
||||
|
||||
``` |
||||
cmake --version |
||||
``` |
||||
|
||||
If you recieve an ``command not found`` error double check that you have actually added CMake to your path. |
||||
|
||||
## MinGW |
||||
|
||||
Install MinGW via ``choco install mingw`` from Powershell as Administrator |
||||
|
||||
|
||||
## Visual Studio / NMake |
||||
|
||||
Download and install [Visual Studio: Community Edition](https://visualstudio.microsoft.com/downloads/). |
||||
|
||||
Ensure that you have installed the Desktop C++ option! |
||||
|
||||
# **Usage** |
||||
|
||||
IF YOU HAVE MADE IT HERE AND YOU HAVE NOT SET UP YOUR DEVELOPMENT ENVIRONMENT, GO BACK UP AND READ THE INSTRUCTIONS AGAIN! |
||||
|
||||
## Linux / MacOS (with default toolchains) |
||||
|
||||
Open a Terminal and navigate to the directory which you downloaded the project. Issue the following command: |
||||
|
||||
``` |
||||
cmake . -B linux-build -G "Unix Makefiles" |
||||
``` |
||||
|
||||
CMake will generate UNIX Makefiles you can use to build the project, like so: |
||||
|
||||
``` |
||||
cmake --build linux-build |
||||
``` |
||||
|
||||
The compiled binary will be located in ``linux-build/bin`` directory. |
||||
|
||||
**NOTE: if you're executing the program, ensure you have the correct working directory, which contains the executable!** |
||||
|
||||
## MacOS (with XCode) |
||||
|
||||
Open a Terminal and navigate to the directory which you downloaded the project. Issue the following command: |
||||
|
||||
``` |
||||
cmake . -B xcode-build -G "xcode" |
||||
``` |
||||
|
||||
CMake will generate an XCode project in ``xcode-build``. You can use it like any other XCode project. |
||||
|
||||
## Linux / MacOS (Emscripten) |
||||
|
||||
**These instructions assume you have Emscripten installed, activated, and have the environment set up for an active Terminal.** |
||||
|
||||
Open a Terminal and navigate to the directory which you downloaded the project. Issue the following command: |
||||
|
||||
``` |
||||
emcmake cmake . -B emscripten-build |
||||
``` |
||||
|
||||
Emscripten's ``emcmake`` utility will invoke CMake with all the magic required to make it work with Emscripten. Generating UNIX Makefiles you can use to build the project, like so: |
||||
|
||||
``` |
||||
cmake --build emscripten-build |
||||
``` |
||||
|
||||
The compiled HTML, Javascript, WebAssembly, and Data will be in the ``emscripten-build/bin`` directory. |
||||
|
||||
If you lack some sort of live server extension to your IDE, you can view it using the ``emrun`` utility, like so: |
||||
|
||||
``` |
||||
emrun path/to/build/bin/PROJECTNAME.html |
||||
``` |
||||
|
||||
This command should launch the project in your default web browser. |
||||
|
||||
## Windows (MinGW) |
||||
|
||||
Open the ``Command Prompt`` prompt and navigate to the directory which you downloaded the project. Issue the following command: |
||||
|
||||
``` |
||||
cmake . -B mingw-build -G "MinGW Makefiles" |
||||
``` |
||||
|
||||
CMake will generate MinGW Makefiles you can use to build the project, like so: |
||||
|
||||
``` |
||||
cmake --build mingw-build |
||||
``` |
||||
|
||||
The compiled binary will be located in the ``mingw-build/bin`` directory. |
||||
|
||||
**NOTE: if you're executing the program, ensure you have the correct working directory, which contains the executable!** |
||||
|
||||
## Windows (NMake) |
||||
|
||||
Open the ``x64 Native Tools Command Prompt for VS 2022`` prompt and navigate to the directory which you downloaded the project. Issue the following command: |
||||
|
||||
``` |
||||
cmake . -B nmake-build -G "NMake Makefiles" |
||||
``` |
||||
|
||||
CMake will generate NMake Makefiles you can use to build the project, like so: |
||||
|
||||
``` |
||||
cmake --build nmake-build |
||||
``` |
||||
|
||||
The compiled binary will be located in ``nmake-build/bin`` directory. |
||||
|
||||
**NOTE: if you're executing the program, ensure you have the correct working directory, which contains the executable!** |
||||
|
||||
## Windows (Visual Studio) |
||||
|
||||
Open the ``Command Prompt`` prompt and navigate to the directory which you downloaded the project. Issue the following command: |
||||
|
||||
``` |
||||
cmake . -B vs-build |
||||
``` |
||||
|
||||
CMake will generate a Visual Studio solution and project in ``vs-build``. You can use it like any other Visual Studio Project. |
||||
|
After Width: | Height: | Size: 45 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,477 @@ |
||||
#pragma once |
||||
/*
|
||||
olcPGEX_MiniAudio.h |
||||
|
||||
+-------------------------------------------------------------+ |
||||
| OneLoneCoder Pixel Game Engine Extension | |
||||
| MiniAudio v1.5 | |
||||
+-------------------------------------------------------------+ |
||||
|
||||
NOTE: UNDER ACTIVE DEVELOPMENT - THERE MAY BE BUGS/GLITCHES |
||||
|
||||
What is this? |
||||
~~~~~~~~~~~~~ |
||||
This extension abstracts the very robust and powerful miniaudio |
||||
library. It provides simple loading and playback of WAV and MP3 |
||||
files. Because it's built on top of miniaudio, it requires next |
||||
to no addictional build configurations in order to be built |
||||
for cross-platform. |
||||
|
||||
License (OLC-3) |
||||
~~~~~~~~~~~~~~~ |
||||
|
||||
Copyright 2023 Moros Smith <moros1138@gmail.com> |
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, |
||||
are permitted provided that the following conditions are met: |
||||
|
||||
1. Redistributions or derivations of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce the above |
||||
copyright notice. This list of conditions and the following disclaimer must be |
||||
reproduced in the documentation and/or other materials provided with the distribution. |
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may |
||||
be used to endorse or promote products derived from this software without specific |
||||
prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY |
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||
SUCH DAMAGE. |
||||
|
||||
Links |
||||
~~~~~ |
||||
YouTube: https://www.youtube.com/@Moros1138
|
||||
GitHub: https://www.github.com/Moros1138
|
||||
Homepage: https://www.moros1138.com
|
||||
*/ |
||||
|
||||
#include "olcPixelGameEngine.h" |
||||
#include <exception> |
||||
|
||||
#ifdef OLC_PGEX_MINIAUDIO |
||||
#define MINIAUDIO_IMPLEMENTATION |
||||
#endif |
||||
|
||||
#include "miniaudio.h" |
||||
|
||||
namespace olc |
||||
{ |
||||
class MiniAudio : public olc::PGEX |
||||
{ |
||||
public: |
||||
std::string name = "olcPGEX_MiniAudio v1.5"; |
||||
|
||||
public: |
||||
MiniAudio(); |
||||
~MiniAudio(); |
||||
virtual bool OnBeforeUserUpdate(float& fElapsedTime) override; |
||||
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); |
||||
static bool backgroundPlay; |
||||
|
||||
public: // CONFIGURATION
|
||||
// set whether audio will continue playing when the app has lost focus
|
||||
void SetBackgroundPlay(bool state); |
||||
|
||||
public: // LOADING ROUTINES
|
||||
const int LoadSound(const std::string& path); |
||||
void UnloadSound(const int id); |
||||
|
||||
public: // PLAYBACK CONTROLS
|
||||
// plays a sample, can be set to loop
|
||||
void Play(const int id, const bool loop = false); |
||||
// plays a sound file, as a one off, and automatically unloads it
|
||||
void Play(const std::string& path); |
||||
// stops a sample, rewinds to beginning
|
||||
void Stop(const int id); |
||||
// pauses a sample, does not change position
|
||||
void Pause(const int id); |
||||
// toggle between play and pause
|
||||
void Toggle(const int id, bool rewind = false); |
||||
|
||||
public: // SEEKING CONTROLS
|
||||
// seek to the provided position in the sound, by milliseconds
|
||||
void Seek(const int id, const unsigned long long milliseconds); |
||||
// seek to the provided position in the sound, by float 0.f is beginning, 1.0f is end
|
||||
void Seek(const int id, const float& location); |
||||
// seek forward from current position by the provided time
|
||||
void Forward(const int id, const unsigned long long milliseconds); |
||||
// seek forward from current position by the provided time
|
||||
void Rewind(const int id, const unsigned long long milliseconds); |
||||
|
||||
public: // MISC CONTROLS
|
||||
// set volume of a sound, 0.0f is mute, 1.0f is full
|
||||
void SetVolume(const int id, const float& volume); |
||||
// set pan of a sound, -1.0f is left, 1.0f is right, 0.0f is center
|
||||
void SetPan(const int id, const float& pan); |
||||
// set pitch of a sound, 1.0f is normal
|
||||
void SetPitch(const int id, const float& pitch); |
||||
|
||||
public: // MISC INFORMATION
|
||||
// determine if a sound is playing
|
||||
bool IsPlaying(const int id); |
||||
// gets the current position in the sound, in milliseconds
|
||||
unsigned long long GetCursorMilliseconds(const int id); |
||||
// gets the current position in the sound, as a float between 0.0f and 1.0f
|
||||
float GetCursorFloat(const int id); |
||||
|
||||
public: // ADVANCED FEATURES for those who want to use more of miniaudio
|
||||
// gets the currently loaded persistent sounds
|
||||
const std::vector<ma_sound*>& GetSounds() const; |
||||
// gets the currently loaded one-off sounds
|
||||
const std::vector<ma_sound*>& GetOneOffSounds() const; |
||||
// gets a pointer to the ma_device
|
||||
ma_device* GetDevice(); |
||||
// gets a pointer to the ma_engine
|
||||
ma_engine* GetEngine(); |
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
Soooo, i'm not going to spend a whole lot of time |
||||
documenting miniaudio features, if you want to |
||||
know more I invite you to visit their very very |
||||
nicely documented webiste at: |
||||
|
||||
https://miniaud.io/docs/manual/index.html
|
||||
*/ |
||||
|
||||
ma_device device; |
||||
ma_engine engine; |
||||
ma_resource_manager resourceManager; |
||||
|
||||
// sample rate for the device and engine
|
||||
int sampleRate; |
||||
// this is where the sounds are kept
|
||||
std::vector<ma_sound*> vecSounds; |
||||
std::vector<ma_sound*> vecOneOffSounds; |
||||
}; |
||||
|
||||
/**
|
||||
* EXCEPTIONS, long story short. I needed to be able |
||||
* to construct the PGEX in a state where it could |
||||
* fail at runtime. If you have a better way of |
||||
* accomplishing the PGEX pattern without using |
||||
* exceptions, I'm open to suggestions! |
||||
*/ |
||||
struct MiniAudioDeviceException : public std::exception |
||||
{ |
||||
const char* what() const throw() |
||||
{ |
||||
return "Failed to initialize a device."; |
||||
} |
||||
}; |
||||
|
||||
struct MiniAudioResourceManagerException : public std::exception |
||||
{ |
||||
const char* what() const throw() |
||||
{ |
||||
return "Failed to initialize the resource manager."; |
||||
} |
||||
}; |
||||
|
||||
struct MiniAudioEngineException : public std::exception |
||||
{ |
||||
const char* what() const throw() |
||||
{ |
||||
return "Failed to initialize the audio engine."; |
||||
} |
||||
}; |
||||
|
||||
struct MiniAudioSoundException : public std::exception |
||||
{ |
||||
const char* what() const throw() |
||||
{ |
||||
return "Failed to initialize a sound."; |
||||
} |
||||
}; |
||||
|
||||
} |
||||
|
||||
|
||||
#ifdef OLC_PGEX_MINIAUDIO |
||||
#undef OLC_PGEX_MINIAUDIO |
||||
|
||||
namespace olc |
||||
{ |
||||
bool MiniAudio::backgroundPlay = false; |
||||
|
||||
MiniAudio::MiniAudio() : olc::PGEX(true) |
||||
{ |
||||
sampleRate = 48000; |
||||
|
||||
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_playback); |
||||
deviceConfig.playback.format = ma_format_f32; |
||||
deviceConfig.playback.channels = 2; |
||||
deviceConfig.sampleRate = sampleRate; |
||||
deviceConfig.dataCallback = MiniAudio::data_callback; |
||||
deviceConfig.pUserData = &engine; |
||||
|
||||
if(ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) |
||||
throw MiniAudioDeviceException(); |
||||
|
||||
ma_resource_manager_config resourceManagerConfig = ma_resource_manager_config_init(); |
||||
resourceManagerConfig.decodedFormat = ma_format_f32; |
||||
resourceManagerConfig.decodedChannels = 0; |
||||
resourceManagerConfig.decodedSampleRate = sampleRate; |
||||
|
||||
#ifdef __EMSCRIPTEN__ |
||||
resourceManagerConfig.jobThreadCount = 0;
|
||||
resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; |
||||
resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; |
||||
#endif |
||||
|
||||
if(ma_resource_manager_init(&resourceManagerConfig, &resourceManager) != MA_SUCCESS) |
||||
throw MiniAudioResourceManagerException(); |
||||
|
||||
ma_engine_config engineConfig = ma_engine_config_init(); |
||||
engineConfig.pDevice = &device; |
||||
engineConfig.pResourceManager = &resourceManager;
|
||||
|
||||
if(ma_engine_init(&engineConfig, &engine) != MA_SUCCESS) |
||||
throw MiniAudioEngineException(); |
||||
|
||||
MiniAudio::backgroundPlay = false;
|
||||
} |
||||
|
||||
MiniAudio::~MiniAudio() |
||||
{ |
||||
for(auto sound : vecSounds) |
||||
{ |
||||
if(sound != nullptr) |
||||
{ |
||||
ma_sound_uninit(sound); |
||||
delete sound; |
||||
} |
||||
} |
||||
|
||||
ma_resource_manager_uninit(&resourceManager); |
||||
|
||||
ma_engine_uninit(&engine);
|
||||
} |
||||
|
||||
bool MiniAudio::OnBeforeUserUpdate(float& fElapsedTime) |
||||
{ |
||||
#ifdef __EMSCRIPTEN__ |
||||
ma_resource_manager_process_next_job(&resourceManager); |
||||
#endif |
||||
|
||||
for(int i = 0; i < vecOneOffSounds.size(); i++) |
||||
{ |
||||
if(!ma_sound_is_playing(vecOneOffSounds.at(i))) |
||||
{ |
||||
ma_sound_uninit(vecOneOffSounds.at(i)); |
||||
vecOneOffSounds.erase(vecOneOffSounds.begin() + i); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
void MiniAudio::data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) |
||||
{ |
||||
if(!MiniAudio::backgroundPlay && !pge->IsFocused()) |
||||
return; |
||||
|
||||
ma_engine_read_pcm_frames((ma_engine*)(pDevice->pUserData), pOutput, frameCount, NULL); |
||||
} |
||||
|
||||
void MiniAudio::SetBackgroundPlay(bool state) |
||||
{ |
||||
MiniAudio::backgroundPlay = state; |
||||
} |
||||
|
||||
const int MiniAudio::LoadSound(const std::string& path) |
||||
{ |
||||
// create the sound
|
||||
ma_sound* sound = new ma_sound(); |
||||
|
||||
// load it from the file and decode it
|
||||
if(ma_sound_init_from_file(&engine, path.c_str(), MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, NULL, NULL, sound) != MA_SUCCESS) |
||||
throw MiniAudioSoundException(); |
||||
|
||||
// attempt to re-use an empty slot
|
||||
for(int i = 0; i < vecSounds.size(); i++) |
||||
{ |
||||
if(vecSounds.at(i) == nullptr) |
||||
{ |
||||
vecSounds.at(i) = sound; |
||||
return i; |
||||
} |
||||
} |
||||
|
||||
// no empty slots, make more room!
|
||||
const int id = vecSounds.size(); |
||||
vecSounds.push_back(sound); |
||||
|
||||
return id; |
||||
} |
||||
|
||||
void MiniAudio::UnloadSound(const int id) |
||||
{ |
||||
ma_sound_uninit(vecSounds.at(id)); |
||||
delete vecSounds.at(id); |
||||
vecSounds.at(id) = nullptr; |
||||
} |
||||
|
||||
void MiniAudio::Play(const int id, const bool loop) |
||||
{ |
||||
if(ma_sound_is_playing(vecSounds.at(id))) |
||||
{ |
||||
ma_sound_seek_to_pcm_frame(vecSounds.at(id), 0); |
||||
return; |
||||
} |
||||
|
||||
ma_sound_set_looping(vecSounds.at(id), loop); |
||||
ma_sound_start(vecSounds.at(id)); |
||||
} |
||||
|
||||
void MiniAudio::Play(const std::string& path) |
||||
{ |
||||
// create the sound
|
||||
ma_sound* sound = new ma_sound(); |
||||
|
||||
// load it from the file and decode it
|
||||
if(ma_sound_init_from_file(&engine, path.c_str(), MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, NULL, NULL, sound) != MA_SUCCESS) |
||||
throw MiniAudioSoundException(); |
||||
|
||||
ma_sound_start(sound); |
||||
vecOneOffSounds.push_back(sound); |
||||
} |
||||
|
||||
void MiniAudio::Stop(const int id) |
||||
{ |
||||
ma_sound_seek_to_pcm_frame(vecSounds.at(id), 0); |
||||
ma_sound_stop(vecSounds.at(id)); |
||||
} |
||||
|
||||
void MiniAudio::Pause(const int id) |
||||
{ |
||||
auto it = vecSounds.begin() + id; |
||||
ma_sound_stop(vecSounds.at(id)); |
||||
} |
||||
|
||||
void MiniAudio::Toggle(const int id, bool rewind) |
||||
{ |
||||
if(ma_sound_is_playing(vecSounds.at(id))) |
||||
{ |
||||
ma_sound_stop(vecSounds.at(id)); |
||||
|
||||
if(rewind) |
||||
ma_sound_seek_to_pcm_frame(vecSounds.at(id), 0); |
||||
|
||||
return; |
||||
} |
||||
|
||||
ma_sound_start(vecSounds.at(id)); |
||||
} |
||||
|
||||
void MiniAudio::Seek(const int id, const unsigned long long milliseconds) |
||||
{ |
||||
unsigned long long frame = (milliseconds * engine.sampleRate) / 1000; |
||||
|
||||
ma_sound_seek_to_pcm_frame(vecSounds.at(id), frame); |
||||
} |
||||
|
||||
void MiniAudio::Seek(const int id, const float& location) |
||||
{ |
||||
unsigned long long length; |
||||
ma_sound_get_length_in_pcm_frames(vecSounds.at(id), &length); |
||||
|
||||
unsigned long long frame = length * location; |
||||
|
||||
ma_sound_seek_to_pcm_frame(vecSounds.at(id), frame); |
||||
} |
||||
|
||||
void MiniAudio::Forward(const int id, const unsigned long long milliseconds) |
||||
{ |
||||
unsigned long long cursor; |
||||
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||
|
||||
unsigned long long frame = (milliseconds * engine.sampleRate) / 1000; |
||||
ma_sound_seek_to_pcm_frame(vecSounds.at(id), cursor + frame); |
||||
} |
||||
|
||||
void MiniAudio::Rewind(const int id, const unsigned long long milliseconds) |
||||
{ |
||||
unsigned long long cursor; |
||||
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||
|
||||
unsigned long long frame = (milliseconds * engine.sampleRate) / 1000; |
||||
ma_sound_seek_to_pcm_frame(vecSounds.at(id), cursor - frame); |
||||
} |
||||
|
||||
void MiniAudio::SetVolume(const int id, const float& volume) |
||||
{ |
||||
ma_sound_set_volume(vecSounds.at(id), volume); |
||||
} |
||||
|
||||
void MiniAudio::SetPan(const int id, const float& pan) |
||||
{ |
||||
ma_sound_set_pan(vecSounds.at(id), pan); |
||||
} |
||||
|
||||
void MiniAudio::SetPitch(const int id, const float& pitch) |
||||
{ |
||||
ma_sound_set_pitch(vecSounds.at(id), pitch); |
||||
} |
||||
|
||||
unsigned long long MiniAudio::GetCursorMilliseconds(const int id) |
||||
{ |
||||
unsigned long long cursor; |
||||
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||
|
||||
cursor /= sampleRate; |
||||
cursor /= 1000; |
||||
|
||||
return cursor; |
||||
} |
||||
|
||||
bool MiniAudio::IsPlaying(const int id) |
||||
{ |
||||
return ma_sound_is_playing(vecSounds.at(id)); |
||||
} |
||||
|
||||
float MiniAudio::GetCursorFloat(const int id) |
||||
{ |
||||
unsigned long long cursor; |
||||
ma_sound_get_cursor_in_pcm_frames(vecSounds.at(id), &cursor); |
||||
|
||||
unsigned long long length; |
||||
ma_sound_get_length_in_pcm_frames(vecSounds.at(id), &length); |
||||
|
||||
return (float)cursor / length; |
||||
} |
||||
|
||||
const std::vector<ma_sound*>& MiniAudio::GetSounds() const |
||||
{ |
||||
return vecSounds; |
||||
} |
||||
|
||||
const std::vector<ma_sound*>& MiniAudio::GetOneOffSounds() const |
||||
{ |
||||
return vecOneOffSounds; |
||||
} |
||||
|
||||
ma_device* MiniAudio::GetDevice() |
||||
{ |
||||
return &device; |
||||
} |
||||
|
||||
ma_engine* MiniAudio::GetEngine() |
||||
{ |
||||
return &engine; |
||||
} |
||||
|
||||
} // olc
|
||||
|
||||
#endif |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,364 @@ |
||||
#include "olcPixelGameEngine.h" |
||||
#include "olcPGEX_MiniAudio.h" |
||||
|
||||
|
||||
class OneLoneCoder_Asteroids : public olc::PixelGameEngine |
||||
{ |
||||
public: |
||||
OneLoneCoder_Asteroids() |
||||
{ |
||||
sAppName = "Asteroids"; |
||||
} |
||||
|
||||
private: |
||||
struct sSpaceObject |
||||
{ |
||||
int nSize; |
||||
float x; |
||||
float y; |
||||
float dx; |
||||
float dy; |
||||
float angle; |
||||
}; |
||||
|
||||
std::vector<sSpaceObject> vecAsteroids; |
||||
std::vector<sSpaceObject> vecBullets; |
||||
sSpaceObject player; |
||||
bool bDead = false; |
||||
int nScore = 0; |
||||
|
||||
std::vector<std::pair<float, float>> vecModelShip; |
||||
std::vector<std::pair<float, float>> vecModelAsteroid; |
||||
|
||||
std::map<std::string, olc::Renderable*> gfx; |
||||
std::map<std::string, int> sfx; |
||||
|
||||
olc::MiniAudio audio; |
||||
|
||||
public: |
||||
|
||||
bool OnUserCreate() override |
||||
{ |
||||
auto loadGraphic = [&](const std::string& key, const std::string& filepath) |
||||
{ |
||||
olc::Renderable* renderable = new olc::Renderable(); |
||||
renderable->Load(filepath); |
||||
gfx[key] = renderable; |
||||
}; |
||||
|
||||
auto loadSound = [&](const std::string& key, const std::string& filepath) |
||||
{ |
||||
sfx[key] = audio.LoadSound(filepath); |
||||
}; |
||||
|
||||
loadGraphic("background", "assets/gfx/space.png"); |
||||
|
||||
loadSound("bg-music", "assets/sounds/bg-music.wav"); |
||||
loadSound("laser", "assets/sounds/Laser_Shoot11.wav"); |
||||
loadSound("explosion", "assets/sounds/Explosions1.wav"); |
||||
loadSound("lose", "assets/sounds/lose9.wav"); |
||||
loadSound("thruster", "assets/sounds/thruster.wav"); |
||||
|
||||
vecModelShip =
|
||||
{ |
||||
{ 0.0f, -5.0f}, |
||||
{-2.5f, +2.5f}, |
||||
{+2.5f, +2.5f} |
||||
}; // A simple Isoceles Triangle
|
||||
|
||||
// Create a "jagged" circle for the asteroid. It's important it remains
|
||||
// mostly circular, as we do a simple collision check against a perfect
|
||||
// circle.
|
||||
int verts = 20; |
||||
for (int i = 0; i < verts; i++) |
||||
{ |
||||
float noise = (float)rand() / (float)RAND_MAX * 0.4f + 0.8f; |
||||
vecModelAsteroid.push_back(std::make_pair(noise * sinf(((float)i / (float)verts) * 6.28318f),
|
||||
noise * cosf(((float)i / (float)verts) * 6.28318f))); |
||||
} |
||||
|
||||
backgroundLayer = CreateLayer(); |
||||
EnableLayer(backgroundLayer, true); |
||||
|
||||
SetDrawTarget(backgroundLayer); |
||||
DrawSprite(0,0, gfx.at("background")->Sprite()); |
||||
SetDrawTarget(nullptr); |
||||
|
||||
ResetGame(); |
||||
audio.Play(sfx.at("bg-music"), true); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
bool OnUserDestroy() override |
||||
{ |
||||
for(auto it = gfx.begin(); it != gfx.end(); ++it) |
||||
{ |
||||
delete it->second; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
int backgroundLayer = -1; |
||||
|
||||
void ResetGame() |
||||
{ |
||||
// Initialise Player Position
|
||||
player.x = ScreenWidth() / 2.0f; |
||||
player.y = ScreenHeight() / 2.0f; |
||||
player.dx = 0.0f; |
||||
player.dy = 0.0f; |
||||
player.angle = 0.0f; |
||||
|
||||
vecBullets.clear(); |
||||
vecAsteroids.clear(); |
||||
|
||||
// Put in two asteroids
|
||||
vecAsteroids.push_back({ (int)16, player.x - 80.0f, player.y, 10.0f, 40.0f, 0.0f }); |
||||
vecAsteroids.push_back({ (int)16, player.x + 80.0f, player.y, -10.0f, -40.0f, 0.0f }); |
||||
|
||||
// Reset game
|
||||
bDead = false; |
||||
nScore = false; |
||||
} |
||||
|
||||
// Implements "wrap around" for various in-game sytems
|
||||
void WrapCoordinates(float ix, float iy, float &ox, float &oy) |
||||
{ |
||||
ox = ix; |
||||
oy = iy; |
||||
if (ix < 0.0f) ox = ix + (float)ScreenWidth(); |
||||
if (ix >= (float)ScreenWidth()) ox = ix - (float)ScreenWidth(); |
||||
if (iy < 0.0f) oy = iy + (float)ScreenHeight(); |
||||
if (iy >= (float)ScreenHeight()) oy = iy - (float)ScreenHeight(); |
||||
} |
||||
|
||||
// Overriden to handle toroidal drawing routines
|
||||
bool Draw(int x, int y, olc::Pixel col = olc::WHITE) override |
||||
{ |
||||
float fx, fy; |
||||
WrapCoordinates(x, y, fx, fy);
|
||||
return olc::PixelGameEngine::Draw(fx, fy, col); |
||||
} |
||||
|
||||
bool IsPointInsideCircle(float cx, float cy, float radius, float x, float y) |
||||
{ |
||||
return sqrt((x-cx)*(x-cx) + (y-cy)*(y-cy)) < radius; |
||||
} |
||||
|
||||
// Called by olcConsoleGameEngine
|
||||
bool OnUserUpdate(float fElapsedTime) override |
||||
{ |
||||
if (bDead) |
||||
ResetGame(); |
||||
|
||||
// Clear Screen
|
||||
Clear(olc::BLANK); |
||||
|
||||
// Steer Ship
|
||||
if (GetKey(olc::LEFT).bHeld) |
||||
player.angle -= 5.0f * fElapsedTime; |
||||
if (GetKey(olc::RIGHT).bHeld) |
||||
player.angle += 5.0f * fElapsedTime; |
||||
|
||||
// Thrust? Apply ACCELERATION
|
||||
if (GetKey(olc::UP).bHeld) |
||||
{ |
||||
// ACCELERATION changes VELOCITY (with respect to time)
|
||||
player.dx += sin(player.angle) * 20.0f * fElapsedTime; |
||||
player.dy += -cos(player.angle) * 20.0f * fElapsedTime; |
||||
} |
||||
|
||||
if(GetKey(olc::UP).bPressed) |
||||
audio.Play(sfx.at("thruster"), true); |
||||
|
||||
if(GetKey(olc::UP).bReleased) |
||||
audio.Stop(sfx.at("thruster")); |
||||
|
||||
// VELOCITY changes POSITION (with respect to time)
|
||||
player.x += player.dx * fElapsedTime; |
||||
player.y += player.dy * fElapsedTime; |
||||
|
||||
// Keep ship in gamespace
|
||||
WrapCoordinates(player.x, player.y, player.x, player.y); |
||||
|
||||
// Check ship collision with asteroids
|
||||
for (auto &a : vecAsteroids) |
||||
if (IsPointInsideCircle(a.x, a.y, a.nSize, player.x, player.y)) |
||||
{ |
||||
bDead = true; // Uh oh...
|
||||
audio.Play(sfx.at("lose")); |
||||
} |
||||
|
||||
|
||||
// Fire Bullet in direction of player
|
||||
if (GetKey(olc::SPACE).bReleased) |
||||
{ |
||||
vecBullets.push_back({ 0, player.x, player.y, 150.0f * sinf(player.angle), -150.0f * cosf(player.angle), 100.0f }); |
||||
audio.Play(sfx.at("laser")); |
||||
} |
||||
|
||||
|
||||
// Update and draw asteroids
|
||||
for (auto &a : vecAsteroids) |
||||
{ |
||||
// VELOCITY changes POSITION (with respect to time)
|
||||
a.x += a.dx * fElapsedTime; |
||||
a.y += a.dy * fElapsedTime; |
||||
a.angle += 0.5f * fElapsedTime; // Add swanky rotation :)
|
||||
|
||||
// Asteroid coordinates are kept in game space (toroidal mapping)
|
||||
WrapCoordinates(a.x, a.y, a.x, a.y); |
||||
|
||||
// Draw Asteroids
|
||||
DrawWireFrameModel(vecModelAsteroid, a.x, a.y, a.angle, (float)a.nSize, olc::YELLOW);
|
||||
} |
||||
|
||||
// Any new asteroids created after collision detection are stored
|
||||
// in a temporary vector, so we don't interfere with the asteroids
|
||||
// vector iterator in the for(auto)
|
||||
std::vector<sSpaceObject> newAsteroids; |
||||
|
||||
// Update Bullets
|
||||
for (auto &b : vecBullets) |
||||
{ |
||||
b.x += b.dx * fElapsedTime; |
||||
b.y += b.dy * fElapsedTime; |
||||
WrapCoordinates(b.x, b.y, b.x, b.y); |
||||
b.angle -= 1.0f * fElapsedTime; |
||||
|
||||
// Check collision with asteroids
|
||||
for (auto &a : vecAsteroids) |
||||
{ |
||||
//if (IsPointInsideRectangle(a.x, a.y, a.x + a.nSize, a.y + a.nSize, b.x, b.y))
|
||||
if(IsPointInsideCircle(a.x, a.y, a.nSize, b.x, b.y)) |
||||
{ |
||||
// Asteroid Hit - Remove bullet
|
||||
// We've already updated the bullets, so force bullet to be offscreen
|
||||
// so it is cleaned up by the removal algorithm.
|
||||
b.x = -100; |
||||
|
||||
// Create child asteroids
|
||||
if (a.nSize > 4) |
||||
{ |
||||
float angle1 = ((float)rand() / (float)RAND_MAX) * 6.283185f; |
||||
float angle2 = ((float)rand() / (float)RAND_MAX) * 6.283185f; |
||||
newAsteroids.push_back({ (int)a.nSize >> 1 ,a.x, a.y, 30.0f * sinf(angle1), 30.0f * cosf(angle1), 0.0f }); |
||||
newAsteroids.push_back({ (int)a.nSize >> 1 ,a.x, a.y, 30.0f * sinf(angle2), 30.0f * cosf(angle2), 0.0f }); |
||||
} |
||||
|
||||
// Remove asteroid - Same approach as bullets
|
||||
a.x = -100; |
||||
nScore += 100; // Small score increase for hitting asteroid
|
||||
audio.Play(sfx.at("explosion")); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Append new asteroids to existing vector
|
||||
for(auto a:newAsteroids) |
||||
vecAsteroids.push_back(a); |
||||
|
||||
// Clear up dead objects - They are out of game space
|
||||
|
||||
// Remove asteroids that have been blown up
|
||||
if (vecAsteroids.size() > 0) |
||||
{ |
||||
auto i = remove_if(vecAsteroids.begin(), vecAsteroids.end(), [&](sSpaceObject o) { return (o.x < 0); }); |
||||
if (i != vecAsteroids.end()) |
||||
vecAsteroids.erase(i); |
||||
} |
||||
|
||||
if (vecAsteroids.empty()) // If no asteroids, level complete! :) - you win MORE asteroids!
|
||||
{ |
||||
// Level Clear
|
||||
nScore += 1000; // Large score for level progression
|
||||
vecAsteroids.clear(); |
||||
vecBullets.clear(); |
||||
|
||||
// Add two new asteroids, but in a place where the player is not, we'll simply
|
||||
// add them 90 degrees left and right to the player, their coordinates will
|
||||
// be wrapped by th enext asteroid update
|
||||
vecAsteroids.push_back({ (int)16, 80.0f * sinf(player.angle - 3.14159f/2.0f) + player.x, |
||||
80.0f * cosf(player.angle - 3.14159f/2.0f) + player.y, |
||||
60.0f * sinf(player.angle), 60.0f*cosf(player.angle), 0.0f }); |
||||
|
||||
vecAsteroids.push_back({ (int)16, 80.0f * sinf(player.angle + 3.14159f/2.0f) + player.x, |
||||
80.0f * cosf(player.angle + 3.14159f/2.0f) + player.y, |
||||
60.0f * sinf(-player.angle), 60.0f*cosf(-player.angle), 0.0f }); |
||||
} |
||||
|
||||
// Remove bullets that have gone off screen
|
||||
if (vecBullets.size() > 0) |
||||
{ |
||||
auto i = remove_if(vecBullets.begin(), vecBullets.end(), [&](sSpaceObject o) { return (o.x < 1 || o.y < 1 || o.x >= ScreenWidth() - 1 || o.y >= ScreenHeight() - 1); }); |
||||
if (i != vecBullets.end()) |
||||
vecBullets.erase(i); |
||||
} |
||||
|
||||
// Draw Bullets
|
||||
for (auto b : vecBullets) |
||||
Draw(b.x, b.y); |
||||
|
||||
// Draw Ship
|
||||
DrawWireFrameModel(vecModelShip, player.x, player.y, player.angle); |
||||
|
||||
// Draw Score
|
||||
DrawString(2, 2, "SCORE: " + std::to_string(nScore)); |
||||
|
||||
return !GetKey(olc::ESCAPE).bPressed; |
||||
} |
||||
|
||||
void DrawWireFrameModel(const std::vector<std::pair<float, float>> &vecModelCoordinates, float x, float y, float r = 0.0f, float s = 1.0f, olc::Pixel col = olc::WHITE) |
||||
{ |
||||
// pair.first = x coordinate
|
||||
// pair.second = y coordinate
|
||||
|
||||
// Create translated model vector of coordinate pairs
|
||||
std::vector<std::pair<float, float>> vecTransformedCoordinates; |
||||
int verts = vecModelCoordinates.size(); |
||||
vecTransformedCoordinates.resize(verts); |
||||
|
||||
// Rotate
|
||||
for (int i = 0; i < verts; i++) |
||||
{ |
||||
vecTransformedCoordinates[i].first = vecModelCoordinates[i].first * cosf(r) - vecModelCoordinates[i].second * sinf(r); |
||||
vecTransformedCoordinates[i].second = vecModelCoordinates[i].first * sinf(r) + vecModelCoordinates[i].second * cosf(r); |
||||
} |
||||
|
||||
// Scale
|
||||
for (int i = 0; i < verts; i++) |
||||
{ |
||||
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first * s; |
||||
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second * s; |
||||
} |
||||
|
||||
// Translate
|
||||
for (int i = 0; i < verts; i++) |
||||
{ |
||||
vecTransformedCoordinates[i].first = vecTransformedCoordinates[i].first + x; |
||||
vecTransformedCoordinates[i].second = vecTransformedCoordinates[i].second + y; |
||||
} |
||||
|
||||
// Draw Closed Polygon
|
||||
for (int i = 0; i < verts + 1; i++) |
||||
{ |
||||
int j = (i + 1); |
||||
DrawLine(vecTransformedCoordinates[i % verts].first, vecTransformedCoordinates[i % verts].second,
|
||||
vecTransformedCoordinates[j % verts].first, vecTransformedCoordinates[j % verts].second, col); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
|
||||
int main() |
||||
{ |
||||
OneLoneCoder_Asteroids game; |
||||
if(game.Construct(320, 180, 4, 4)) |
||||
game.Start(); |
||||
|
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,4 @@ |
||||
#define OLC_PGEX_MINIAUDIO |
||||
#include "olcPGEX_MiniAudio.h" |
||||
|
||||
|
@ -0,0 +1,3 @@ |
||||
#define OLC_PGE_APPLICATION |
||||
#include "olcPixelGameEngine.h" |
||||
|
Loading…
Reference in new issue