From 3ff3a2defcad7fa9aab8b97dce64b951b1628277 Mon Sep 17 00:00:00 2001 From: Rohan Liston Date: Sat, 2 Dec 2017 17:02:26 +1100 Subject: [PATCH 1/5] Add linux port --- README.md | 3 +- linux/CMakeLists.txt | 19 ++ linux/CommandLineFPS.cpp | 306 ++++++++++++++++++ linux/FindNcursesw.cmake | 204 ++++++++++++ linux/README.md | 26 ++ .../CommandLineFPS.cpp | 0 6 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 linux/CMakeLists.txt create mode 100644 linux/CommandLineFPS.cpp create mode 100644 linux/FindNcursesw.cmake create mode 100644 linux/README.md rename CommandLineFPS.cpp => windows/CommandLineFPS.cpp (100%) diff --git a/README.md b/README.md index 14f3e0f..8c50465 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # CommandLineFPS + A First Person Shooter at the command line? Yup... Please see the source file on how to configure your command line before running. -This is designed for MS Windows +Windows and Linux versions are available. See the relevant source directory for details. diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..ca15818 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.5) # CMake version check +project(CommandLineFPS) # Create project "CommandLineFPS" +set(CMAKE_CXX_STANDARD 11) # Enable c++11 standard + +# Include our FindNcursesw.cmake file +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_MODULE_PATH}) + +# Find Ncursesw +find_package( Ncursesw REQUIRED ) +include_directories( ${CURSES_INCLUDE_DIRS} ) + +# Add CommandLineFPS.cpp file of project root directory as source file +set(SOURCE_FILES CommandLineFPS.cpp) + +# Add executable target with source files listed in SOURCE_FILES variable +add_executable(CommandLineFPS ${SOURCE_FILES}) + +# Link ncursesw library +target_link_libraries( CommandLineFPS ${CURSES_LIBRARIES} ) diff --git a/linux/CommandLineFPS.cpp b/linux/CommandLineFPS.cpp new file mode 100644 index 0000000..5ac8daa --- /dev/null +++ b/linux/CommandLineFPS.cpp @@ -0,0 +1,306 @@ +/* + OneLoneCoder.com - Command Line First Person Shooter (FPS) Engine + "Why were games not done like this is 1990?" - @Javidx9 + Disclaimer + ~~~~~~~~~~ + I don't care what you use this for. It's intended to be educational, and perhaps + to the oddly minded - a little bit of fun. Please hack this, change it and use it + in any way you see fit. BUT, you acknowledge that I am not responsible for anything + bad that happens as a result of your actions. However, if good stuff happens, I + would appreciate a shout out, or at least give the blog some publicity for me. + Cheers! + Background + ~~~~~~~~~~ + Whilst waiting for TheMexicanRunner to start the finale of his NesMania project, + his Twitch stream had a counter counting down for a couple of hours until it started. + With some time on my hands, I thought it might be fun to see what the graphical + capabilities of the console are. Turns out, not very much, but hey, it's nice to think + Wolfenstein could have existed a few years earlier, and in just ~200 lines of code. + IMPORTANT!!!! + ~~~~~~~~~~~~~ + READ ME BEFORE RUNNING!!! This program expects the console dimensions to be set to + 120 Columns by 40 Rows. I recommend a small font "Consolas" at size 16. You can do this + by running the program, and right clicking on the console title bar, and specifying + the properties. You can also choose to default to them in the future. + + Controls: A = Turn Left, D = Turn Right, W = Walk Forwards, S = Walk Backwards + Future Modifications + ~~~~~~~~~~~~~~~~~~~~ + 1) Shade block segments based on angle from player, i.e. less light reflected off + walls at side of player. Walls straight on are brightest. + 2) Find an interesting and optimised ray-tracing method. I'm sure one must exist + to more optimally search the map space + 3) Add bullets! + 4) Add bad guys! + Author + ~~~~~~ + Twitter: @javidx9 + Blog: www.onelonecoder.com + Linux Port + ~~~~~~~~~~ + Author: Rohan Liston + GitHub: github.com/rohanliston + Video: + ~~~~~~ + https://youtu.be/xW8skO7MFYw + Last Updated: 27/02/2017 +*/ + +#include +#include +#include +#include +#include +#include + +using namespace std; + +int nScreenWidth = 120; // Console Screen Size X (columns) +int nScreenHeight = 40; // Console Screen Size Y (rows) +int nMapWidth = 16; // World Dimensions +int nMapHeight = 16; + +float fPlayerX = 14.7f; // Player Start Position +float fPlayerY = 5.09f; +float fPlayerA = 0.0f; // Player Start Rotation +float fFOV = 3.14159f / 4.0f; // Field of View +float fDepth = 16.0f; // Maximum rendering distance +float fSpeed = 150.0f; // Walking Speed + +int main() +{ + // NCurses setup + setlocale(LC_ALL, ""); // Set locale for UTF-8 support + initscr(); // Initialise NCurses screen + noecho(); // Don't echo input to screen + curs_set(0); // Don't show terminal cursor + nodelay(stdscr, true); // Don't halt program while waiting for input + cbreak(); // Make input characters immediately available to the program + + // Create Screen Buffer + auto *screen = new wchar_t[nScreenWidth * nScreenHeight]; + + // Create Map of world space # = wall block, . = space + wstring map; + map += L"#########......."; + map += L"#..............."; + map += L"#.......########"; + map += L"#..............#"; + map += L"#......##......#"; + map += L"#......##......#"; + map += L"#..............#"; + map += L"###............#"; + map += L"##.............#"; + map += L"#......####..###"; + map += L"#......#.......#"; + map += L"#......#.......#"; + map += L"#..............#"; + map += L"#......#########"; + map += L"#..............#"; + map += L"################"; + + auto tp1 = chrono::system_clock::now(); + auto tp2 = chrono::system_clock::now(); + + bool finished = false; + while (!finished) + { + // We'll need time differential per frame to calculate modification + // to movement speeds, to ensure consistant movement, as ray-tracing + // is non-deterministic + tp2 = chrono::system_clock::now(); + chrono::duration elapsedTime = tp2 - tp1; + tp1 = tp2; + float fElapsedTime = elapsedTime.count(); + + // Measure terminal size + int terminalWidth; + int terminalHeight; + bool cleared = false; + getmaxyx(stdscr, terminalHeight, terminalWidth); + + // Ensure terminal size is OK + while (terminalHeight != nScreenHeight || terminalWidth != nScreenWidth) + { + if (!cleared) + { + clear(); + cleared = true; + } + + ostringstream out; + out << "Resize your terminal to (" << nScreenWidth << ", " << nScreenHeight << ") - current size is (" << terminalWidth << ", " << terminalHeight << ")"; + mvaddstr(0, 0, out.str().c_str()); + refresh(); + getmaxyx(stdscr, terminalHeight, terminalWidth); + } + + int key = getch(); + switch (key) { + case 'a': + // CCW Rotation + fPlayerA -= (fSpeed * 0.75f) * fElapsedTime; + break; + case 'd': + // CW Rotation + fPlayerA += (fSpeed * 0.75f) * fElapsedTime; + break; + case 'w': + // Forward movement + fPlayerX += sinf(fPlayerA) * fSpeed * fElapsedTime;; + fPlayerY += cosf(fPlayerA) * fSpeed * fElapsedTime;; + if (map.c_str()[(int) fPlayerX * nMapWidth + (int) fPlayerY] == '#') { + fPlayerX -= sinf(fPlayerA) * fSpeed * fElapsedTime;; + fPlayerY -= cosf(fPlayerA) * fSpeed * fElapsedTime;; + } + break; + case 's': + // Backward movement + fPlayerX -= sinf(fPlayerA) * fSpeed * fElapsedTime;; + fPlayerY -= cosf(fPlayerA) * fSpeed * fElapsedTime;; + if (map.c_str()[(int) fPlayerX * nMapWidth + (int) fPlayerY] == '#') { + fPlayerX += sinf(fPlayerA) * fSpeed * fElapsedTime;; + fPlayerY += cosf(fPlayerA) * fSpeed * fElapsedTime;; + } + break; + case 'q': + // Quit + finished = true; + break; + default: + break; + } + + for (int x = 0; x < nScreenWidth; x++) + { + // For each column, calculate the projected ray angle into world space + float fRayAngle = (fPlayerA - fFOV/2.0f) + ((float)x / (float)nScreenWidth) * fFOV; + + // Find distance to wall + float fStepSize = 0.1f; // Increment size for ray casting, decrease to increase resolution + float fDistanceToWall = 0.0f; + + bool bHitWall = false; // Set when ray hits wall block + bool bBoundary = false; // Set when ray hits boundary between two wall blocks + + float fEyeX = sinf(fRayAngle); // Unit vector for ray in player space + float fEyeY = cosf(fRayAngle); + + // Incrementally cast ray from player, along ray angle, testing for + // intersection with a block + while (!bHitWall && fDistanceToWall < fDepth) + { + fDistanceToWall += fStepSize; + int nTestX = (int)(fPlayerX + fEyeX * fDistanceToWall); + int nTestY = (int)(fPlayerY + fEyeY * fDistanceToWall); + + // Test if ray is out of bounds + if (nTestX < 0 || nTestX >= nMapWidth || nTestY < 0 || nTestY >= nMapHeight) + { + bHitWall = true; // Just set distance to maximum depth + fDistanceToWall = fDepth; + } + else + { + // Ray is inbounds so test to see if the ray cell is a wall block + if (map.c_str()[nTestX * nMapWidth + nTestY] == '#') + { + // Ray has hit wall + bHitWall = true; + + // To highlight tile boundaries, cast a ray from each corner + // of the tile, to the player. The more coincident this ray + // is to the rendering ray, the closer we are to a tile + // boundary, which we'll shade to add detail to the walls + vector> p; + + // Test each corner of hit tile, storing the distance from + // the player, and the calculated dot product of the two rays + for (int tx = 0; tx < 2; tx++) + for (int ty = 0; ty < 2; ty++) + { + // Angle of corner to eye + float vy = (float)nTestY + ty - fPlayerY; + float vx = (float)nTestX + tx - fPlayerX; + float d = sqrt(vx*vx + vy*vy); + float dot = (fEyeX * vx / d) + (fEyeY * vy / d); + p.push_back(make_pair(d, dot)); + } + + // Sort Pairs from closest to farthest + sort(p.begin(), p.end(), [](const pair &left, const pair &right) {return left.first < right.first; }); + + // First two/three are closest (we will never see all four) + float fBound = 0.005; + if (acos(p.at(0).second) < fBound) bBoundary = true; + if (acos(p.at(1).second) < fBound) bBoundary = true; + if (acos(p.at(2).second) < fBound) bBoundary = true; + } + } + } + + // Calculate distance to ceiling and floor + int nCeiling = (int)((float)(nScreenHeight/2.0) - nScreenHeight / ((float)fDistanceToWall)); + int nFloor = nScreenHeight - nCeiling; + + // Shader walls based on distance + short nShade = ' '; + if (fDistanceToWall <= fDepth / 4.0f) nShade = 0x2588; // Very close + else if (fDistanceToWall < fDepth / 3.0f) nShade = 0x2593; + else if (fDistanceToWall < fDepth / 2.0f) nShade = 0x2592; + else if (fDistanceToWall < fDepth) nShade = 0x2591; + else nShade = ' '; // Too far away + + if (bBoundary) + nShade = ' '; // Black it out + + for (int y = 0; y < nScreenHeight; y++) + { + // Each Row + if(y <= nCeiling) + screen[y*nScreenWidth + x] = ' '; + else if(y > nCeiling && y <= nFloor) + screen[y*nScreenWidth + x] = nShade; + else // Floor + { + // Shade floor based on distance + float b = 1.0f - (((float)y -nScreenHeight/2.0f) / ((float)nScreenHeight / 2.0f)); + if (b < 0.25) nShade = '#'; + else if (b < 0.5) nShade = 'x'; + else if (b < 0.75) nShade = '.'; + else if (b < 0.9) nShade = '-'; + else nShade = ' '; + screen[y*nScreenWidth + x] = nShade; + } + } + } + + // Display Frame + mvaddwstr(0, 0, screen); + + // Display Stats + wchar_t stats[40]; + swprintf(stats, 40, L"X=%3.2f, Y=%3.2f, A=%3.2f FPS=%3.2f ", fPlayerX, fPlayerY, fPlayerA, 1.0f/fElapsedTime); + mvaddwstr(0, 0, stats); + + // Display Map + for (int nx = 0; nx < nMapWidth; nx++) + { + for (int ny = 0; ny < nMapWidth; ny++) + { + mvaddch(ny+1, nx, (chtype)map[ny * nMapWidth + nx]); + } + } + + // Display Player + mvaddch((int)fPlayerX+1, ((int)fPlayerY), 'P'); + + // Draw screen + refresh(); + } + + endwin(); + return 0; +} + +// That's It!! - Jx9 diff --git a/linux/FindNcursesw.cmake b/linux/FindNcursesw.cmake new file mode 100644 index 0000000..e572c70 --- /dev/null +++ b/linux/FindNcursesw.cmake @@ -0,0 +1,204 @@ +#.rst: +# FindNcursesw +# ------------ +# +# Find the ncursesw (wide ncurses) include file and library. +# +# Based on FindCurses.cmake which comes with CMake. +# +# Checks for ncursesw first. If not found, it then executes the +# regular old FindCurses.cmake to look for for ncurses (or curses). +# +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# ``CURSES_FOUND`` +# True if curses is found. +# ``NCURSESW_FOUND`` +# True if ncursesw is found. +# ``CURSES_INCLUDE_DIRS`` +# The include directories needed to use Curses. +# ``CURSES_LIBRARIES`` +# The libraries needed to use Curses. +# ``CURSES_HAVE_CURSES_H`` +# True if curses.h is available. +# ``CURSES_HAVE_NCURSES_H`` +# True if ncurses.h is available. +# ``CURSES_HAVE_NCURSES_NCURSES_H`` +# True if ``ncurses/ncurses.h`` is available. +# ``CURSES_HAVE_NCURSES_CURSES_H`` +# True if ``ncurses/curses.h`` is available. +# ``CURSES_HAVE_NCURSESW_NCURSES_H`` +# True if ``ncursesw/ncurses.h`` is available. +# ``CURSES_HAVE_NCURSESW_CURSES_H`` +# True if ``ncursesw/curses.h`` is available. +# +# Set ``CURSES_NEED_NCURSES`` to ``TRUE`` before the +# ``find_package(Ncursesw)`` call if NCurses functionality is required. +# +#============================================================================= +# Copyright 2001-2014 Kitware, Inc. +# modifications: Copyright 2015 kahrl +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their 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. +# +# ------------------------------------------------------------------------------ +# +# The above copyright and license notice applies to distributions of +# CMake in source and binary form. Some source files contain additional +# notices of original copyright by their contributors; see each source +# for details. Third-party software packages supplied with CMake under +# compatible licenses provide their own copyright notices documented in +# corresponding subdirectories. +# +# ------------------------------------------------------------------------------ +# +# CMake was initially developed by Kitware with the following sponsorship: +# +# * National Library of Medicine at the National Institutes of Health +# as part of the Insight Segmentation and Registration Toolkit (ITK). +# +# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel +# Visualization Initiative. +# +# * National Alliance for Medical Image Computing (NAMIC) is funded by the +# National Institutes of Health through the NIH Roadmap for Medical Research, +# Grant U54 EB005149. +# +# * Kitware, Inc. +#============================================================================= + +include(CheckLibraryExists) + +find_library(CURSES_NCURSESW_LIBRARY NAMES ncursesw + DOC "Path to libncursesw.so or .lib or .a") + +set(CURSES_USE_NCURSES FALSE) +set(CURSES_USE_NCURSESW FALSE) + +if(CURSES_NCURSESW_LIBRARY) + set(CURSES_USE_NCURSES TRUE) + set(CURSES_USE_NCURSESW TRUE) +endif() + +if(CURSES_USE_NCURSESW) + get_filename_component(_cursesLibDir "${CURSES_NCURSESW_LIBRARY}" PATH) + get_filename_component(_cursesParentDir "${_cursesLibDir}" PATH) + + find_path(CURSES_INCLUDE_PATH + NAMES ncursesw/ncurses.h ncursesw/curses.h ncurses.h curses.h + HINTS "${_cursesParentDir}/include" + ) + + # Previous versions of FindCurses provided these values. + if(NOT DEFINED CURSES_LIBRARY) + set(CURSES_LIBRARY "${CURSES_NCURSESW_LIBRARY}") + endif() + + CHECK_LIBRARY_EXISTS("${CURSES_NCURSESW_LIBRARY}" + cbreak "" CURSES_NCURSESW_HAS_CBREAK) + if(NOT CURSES_NCURSESW_HAS_CBREAK) + find_library(CURSES_EXTRA_LIBRARY tinfo HINTS "${_cursesLibDir}" + DOC "Path to libtinfo.so or .lib or .a") + find_library(CURSES_EXTRA_LIBRARY tinfo ) + endif() + + # Report whether each possible header name exists in the include directory. + if(NOT DEFINED CURSES_HAVE_NCURSESW_NCURSES_H) + if(EXISTS "${CURSES_INCLUDE_PATH}/ncursesw/ncurses.h") + set(CURSES_HAVE_NCURSESW_NCURSES_H "${CURSES_INCLUDE_PATH}/ncursesw/ncurses.h") + else() + set(CURSES_HAVE_NCURSESW_NCURSES_H "CURSES_HAVE_NCURSESW_NCURSES_H-NOTFOUND") + endif() + endif() + if(NOT DEFINED CURSES_HAVE_NCURSESW_CURSES_H) + if(EXISTS "${CURSES_INCLUDE_PATH}/ncursesw/curses.h") + set(CURSES_HAVE_NCURSESW_CURSES_H "${CURSES_INCLUDE_PATH}/ncursesw/curses.h") + else() + set(CURSES_HAVE_NCURSESW_CURSES_H "CURSES_HAVE_NCURSESW_CURSES_H-NOTFOUND") + endif() + endif() + if(NOT DEFINED CURSES_HAVE_NCURSES_H) + if(EXISTS "${CURSES_INCLUDE_PATH}/ncurses.h") + set(CURSES_HAVE_NCURSES_H "${CURSES_INCLUDE_PATH}/ncurses.h") + else() + set(CURSES_HAVE_NCURSES_H "CURSES_HAVE_NCURSES_H-NOTFOUND") + endif() + endif() + if(NOT DEFINED CURSES_HAVE_CURSES_H) + if(EXISTS "${CURSES_INCLUDE_PATH}/curses.h") + set(CURSES_HAVE_CURSES_H "${CURSES_INCLUDE_PATH}/curses.h") + else() + set(CURSES_HAVE_CURSES_H "CURSES_HAVE_CURSES_H-NOTFOUND") + endif() + endif() + + + find_library(CURSES_FORM_LIBRARY form HINTS "${_cursesLibDir}" + DOC "Path to libform.so or .lib or .a") + find_library(CURSES_FORM_LIBRARY form ) + + # Need to provide the *_LIBRARIES + set(CURSES_LIBRARIES ${CURSES_LIBRARY}) + + if(CURSES_EXTRA_LIBRARY) + set(CURSES_LIBRARIES ${CURSES_LIBRARIES} ${CURSES_EXTRA_LIBRARY}) + endif() + + if(CURSES_FORM_LIBRARY) + set(CURSES_LIBRARIES ${CURSES_LIBRARIES} ${CURSES_FORM_LIBRARY}) + endif() + + # Provide the *_INCLUDE_DIRS result. + set(CURSES_INCLUDE_DIRS ${CURSES_INCLUDE_PATH}) + set(CURSES_INCLUDE_DIR ${CURSES_INCLUDE_PATH}) # compatibility + + # handle the QUIETLY and REQUIRED arguments and set CURSES_FOUND to TRUE if + # all listed variables are TRUE + include(FindPackageHandleStandardArgs) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Ncursesw DEFAULT_MSG + CURSES_LIBRARY CURSES_INCLUDE_PATH) + set(CURSES_FOUND ${NCURSESW_FOUND}) + +else() + find_package(Curses) + set(NCURSESW_FOUND FALSE) +endif() + +mark_as_advanced( + CURSES_INCLUDE_PATH + CURSES_CURSES_LIBRARY + CURSES_NCURSES_LIBRARY + CURSES_NCURSESW_LIBRARY + CURSES_EXTRA_LIBRARY + CURSES_FORM_LIBRARY + ) diff --git a/linux/README.md b/linux/README.md new file mode 100644 index 0000000..ff84f84 --- /dev/null +++ b/linux/README.md @@ -0,0 +1,26 @@ +# CommandLineFPS - Linux Version + +## Prerequisites + +* CMake - C/C++ build system +* NCursesw - Library for interacting with the terminal + +### Installation + +* Ubuntu: `sudo apt-get install cmake libncursesw5-dev` +* Other distros: `¯\_(ツ)_/¯` + +## Build + +```sh +mkdir build +cd build +cmake .. +make +``` + +## Run + +`./CommandLineFPS` + + diff --git a/CommandLineFPS.cpp b/windows/CommandLineFPS.cpp similarity index 100% rename from CommandLineFPS.cpp rename to windows/CommandLineFPS.cpp From 1b04a83f9dc314d0b442bfaf81575ee8e021671f Mon Sep 17 00:00:00 2001 From: Rohan Liston Date: Sat, 2 Dec 2017 17:04:19 +1100 Subject: [PATCH 2/5] Use consistent spacing --- linux/CommandLineFPS.cpp | 358 +++++++++++++++++++-------------------- 1 file changed, 179 insertions(+), 179 deletions(-) diff --git a/linux/CommandLineFPS.cpp b/linux/CommandLineFPS.cpp index 5ac8daa..ca02ab2 100644 --- a/linux/CommandLineFPS.cpp +++ b/linux/CommandLineFPS.cpp @@ -1,49 +1,49 @@ /* - OneLoneCoder.com - Command Line First Person Shooter (FPS) Engine - "Why were games not done like this is 1990?" - @Javidx9 - Disclaimer - ~~~~~~~~~~ - I don't care what you use this for. It's intended to be educational, and perhaps - to the oddly minded - a little bit of fun. Please hack this, change it and use it - in any way you see fit. BUT, you acknowledge that I am not responsible for anything - bad that happens as a result of your actions. However, if good stuff happens, I - would appreciate a shout out, or at least give the blog some publicity for me. - Cheers! - Background - ~~~~~~~~~~ - Whilst waiting for TheMexicanRunner to start the finale of his NesMania project, - his Twitch stream had a counter counting down for a couple of hours until it started. - With some time on my hands, I thought it might be fun to see what the graphical - capabilities of the console are. Turns out, not very much, but hey, it's nice to think - Wolfenstein could have existed a few years earlier, and in just ~200 lines of code. - IMPORTANT!!!! - ~~~~~~~~~~~~~ - READ ME BEFORE RUNNING!!! This program expects the console dimensions to be set to - 120 Columns by 40 Rows. I recommend a small font "Consolas" at size 16. You can do this - by running the program, and right clicking on the console title bar, and specifying - the properties. You can also choose to default to them in the future. - - Controls: A = Turn Left, D = Turn Right, W = Walk Forwards, S = Walk Backwards - Future Modifications - ~~~~~~~~~~~~~~~~~~~~ - 1) Shade block segments based on angle from player, i.e. less light reflected off - walls at side of player. Walls straight on are brightest. - 2) Find an interesting and optimised ray-tracing method. I'm sure one must exist - to more optimally search the map space - 3) Add bullets! - 4) Add bad guys! - Author - ~~~~~~ - Twitter: @javidx9 - Blog: www.onelonecoder.com + OneLoneCoder.com - Command Line First Person Shooter (FPS) Engine + "Why were games not done like this is 1990?" - @Javidx9 + Disclaimer + ~~~~~~~~~~ + I don't care what you use this for. It's intended to be educational, and perhaps + to the oddly minded - a little bit of fun. Please hack this, change it and use it + in any way you see fit. BUT, you acknowledge that I am not responsible for anything + bad that happens as a result of your actions. However, if good stuff happens, I + would appreciate a shout out, or at least give the blog some publicity for me. + Cheers! + Background + ~~~~~~~~~~ + Whilst waiting for TheMexicanRunner to start the finale of his NesMania project, + his Twitch stream had a counter counting down for a couple of hours until it started. + With some time on my hands, I thought it might be fun to see what the graphical + capabilities of the console are. Turns out, not very much, but hey, it's nice to think + Wolfenstein could have existed a few years earlier, and in just ~200 lines of code. + IMPORTANT!!!! + ~~~~~~~~~~~~~ + READ ME BEFORE RUNNING!!! This program expects the console dimensions to be set to + 120 Columns by 40 Rows. I recommend a small font "Consolas" at size 16. You can do this + by running the program, and right clicking on the console title bar, and specifying + the properties. You can also choose to default to them in the future. + + Controls: A = Turn Left, D = Turn Right, W = Walk Forwards, S = Walk Backwards + Future Modifications + ~~~~~~~~~~~~~~~~~~~~ + 1) Shade block segments based on angle from player, i.e. less light reflected off + walls at side of player. Walls straight on are brightest. + 2) Find an interesting and optimised ray-tracing method. I'm sure one must exist + to more optimally search the map space + 3) Add bullets! + 4) Add bad guys! + Author + ~~~~~~ + Twitter: @javidx9 + Blog: www.onelonecoder.com Linux Port ~~~~~~~~~~ Author: Rohan Liston GitHub: github.com/rohanliston - Video: - ~~~~~~ - https://youtu.be/xW8skO7MFYw - Last Updated: 27/02/2017 + Video: + ~~~~~~ + https://youtu.be/xW8skO7MFYw + Last Updated: 27/02/2017 */ #include @@ -77,41 +77,41 @@ int main() nodelay(stdscr, true); // Don't halt program while waiting for input cbreak(); // Make input characters immediately available to the program - // Create Screen Buffer - auto *screen = new wchar_t[nScreenWidth * nScreenHeight]; - - // Create Map of world space # = wall block, . = space - wstring map; - map += L"#########......."; - map += L"#..............."; - map += L"#.......########"; - map += L"#..............#"; - map += L"#......##......#"; - map += L"#......##......#"; - map += L"#..............#"; - map += L"###............#"; - map += L"##.............#"; - map += L"#......####..###"; - map += L"#......#.......#"; - map += L"#......#.......#"; - map += L"#..............#"; - map += L"#......#########"; - map += L"#..............#"; - map += L"################"; - - auto tp1 = chrono::system_clock::now(); - auto tp2 = chrono::system_clock::now(); + // Create Screen Buffer + auto *screen = new wchar_t[nScreenWidth * nScreenHeight]; + + // Create Map of world space # = wall block, . = space + wstring map; + map += L"#########......."; + map += L"#..............."; + map += L"#.......########"; + map += L"#..............#"; + map += L"#......##......#"; + map += L"#......##......#"; + map += L"#..............#"; + map += L"###............#"; + map += L"##.............#"; + map += L"#......####..###"; + map += L"#......#.......#"; + map += L"#......#.......#"; + map += L"#..............#"; + map += L"#......#########"; + map += L"#..............#"; + map += L"################"; + + auto tp1 = chrono::system_clock::now(); + auto tp2 = chrono::system_clock::now(); bool finished = false; - while (!finished) - { - // We'll need time differential per frame to calculate modification - // to movement speeds, to ensure consistant movement, as ray-tracing - // is non-deterministic - tp2 = chrono::system_clock::now(); - chrono::duration elapsedTime = tp2 - tp1; - tp1 = tp2; - float fElapsedTime = elapsedTime.count(); + while (!finished) + { + // We'll need time differential per frame to calculate modification + // to movement speeds, to ensure consistant movement, as ray-tracing + // is non-deterministic + tp2 = chrono::system_clock::now(); + chrono::duration elapsedTime = tp2 - tp1; + tp1 = tp2; + float fElapsedTime = elapsedTime.count(); // Measure terminal size int terminalWidth; @@ -171,120 +171,120 @@ int main() break; } - for (int x = 0; x < nScreenWidth; x++) - { - // For each column, calculate the projected ray angle into world space - float fRayAngle = (fPlayerA - fFOV/2.0f) + ((float)x / (float)nScreenWidth) * fFOV; - - // Find distance to wall - float fStepSize = 0.1f; // Increment size for ray casting, decrease to increase resolution - float fDistanceToWall = 0.0f; - - bool bHitWall = false; // Set when ray hits wall block - bool bBoundary = false; // Set when ray hits boundary between two wall blocks - - float fEyeX = sinf(fRayAngle); // Unit vector for ray in player space - float fEyeY = cosf(fRayAngle); - - // Incrementally cast ray from player, along ray angle, testing for - // intersection with a block - while (!bHitWall && fDistanceToWall < fDepth) - { - fDistanceToWall += fStepSize; - int nTestX = (int)(fPlayerX + fEyeX * fDistanceToWall); - int nTestY = (int)(fPlayerY + fEyeY * fDistanceToWall); - - // Test if ray is out of bounds - if (nTestX < 0 || nTestX >= nMapWidth || nTestY < 0 || nTestY >= nMapHeight) - { - bHitWall = true; // Just set distance to maximum depth - fDistanceToWall = fDepth; - } - else - { - // Ray is inbounds so test to see if the ray cell is a wall block - if (map.c_str()[nTestX * nMapWidth + nTestY] == '#') - { - // Ray has hit wall - bHitWall = true; - - // To highlight tile boundaries, cast a ray from each corner - // of the tile, to the player. The more coincident this ray - // is to the rendering ray, the closer we are to a tile - // boundary, which we'll shade to add detail to the walls - vector> p; - - // Test each corner of hit tile, storing the distance from - // the player, and the calculated dot product of the two rays - for (int tx = 0; tx < 2; tx++) - for (int ty = 0; ty < 2; ty++) - { - // Angle of corner to eye - float vy = (float)nTestY + ty - fPlayerY; - float vx = (float)nTestX + tx - fPlayerX; - float d = sqrt(vx*vx + vy*vy); - float dot = (fEyeX * vx / d) + (fEyeY * vy / d); - p.push_back(make_pair(d, dot)); - } - - // Sort Pairs from closest to farthest - sort(p.begin(), p.end(), [](const pair &left, const pair &right) {return left.first < right.first; }); - - // First two/three are closest (we will never see all four) - float fBound = 0.005; - if (acos(p.at(0).second) < fBound) bBoundary = true; - if (acos(p.at(1).second) < fBound) bBoundary = true; - if (acos(p.at(2).second) < fBound) bBoundary = true; - } - } - } - - // Calculate distance to ceiling and floor - int nCeiling = (int)((float)(nScreenHeight/2.0) - nScreenHeight / ((float)fDistanceToWall)); - int nFloor = nScreenHeight - nCeiling; - - // Shader walls based on distance - short nShade = ' '; - if (fDistanceToWall <= fDepth / 4.0f) nShade = 0x2588; // Very close - else if (fDistanceToWall < fDepth / 3.0f) nShade = 0x2593; - else if (fDistanceToWall < fDepth / 2.0f) nShade = 0x2592; - else if (fDistanceToWall < fDepth) nShade = 0x2591; - else nShade = ' '; // Too far away - - if (bBoundary) + for (int x = 0; x < nScreenWidth; x++) + { + // For each column, calculate the projected ray angle into world space + float fRayAngle = (fPlayerA - fFOV/2.0f) + ((float)x / (float)nScreenWidth) * fFOV; + + // Find distance to wall + float fStepSize = 0.1f; // Increment size for ray casting, decrease to increase resolution + float fDistanceToWall = 0.0f; + + bool bHitWall = false; // Set when ray hits wall block + bool bBoundary = false; // Set when ray hits boundary between two wall blocks + + float fEyeX = sinf(fRayAngle); // Unit vector for ray in player space + float fEyeY = cosf(fRayAngle); + + // Incrementally cast ray from player, along ray angle, testing for + // intersection with a block + while (!bHitWall && fDistanceToWall < fDepth) + { + fDistanceToWall += fStepSize; + int nTestX = (int)(fPlayerX + fEyeX * fDistanceToWall); + int nTestY = (int)(fPlayerY + fEyeY * fDistanceToWall); + + // Test if ray is out of bounds + if (nTestX < 0 || nTestX >= nMapWidth || nTestY < 0 || nTestY >= nMapHeight) + { + bHitWall = true; // Just set distance to maximum depth + fDistanceToWall = fDepth; + } + else + { + // Ray is inbounds so test to see if the ray cell is a wall block + if (map.c_str()[nTestX * nMapWidth + nTestY] == '#') + { + // Ray has hit wall + bHitWall = true; + + // To highlight tile boundaries, cast a ray from each corner + // of the tile, to the player. The more coincident this ray + // is to the rendering ray, the closer we are to a tile + // boundary, which we'll shade to add detail to the walls + vector> p; + + // Test each corner of hit tile, storing the distance from + // the player, and the calculated dot product of the two rays + for (int tx = 0; tx < 2; tx++) + for (int ty = 0; ty < 2; ty++) + { + // Angle of corner to eye + float vy = (float)nTestY + ty - fPlayerY; + float vx = (float)nTestX + tx - fPlayerX; + float d = sqrt(vx*vx + vy*vy); + float dot = (fEyeX * vx / d) + (fEyeY * vy / d); + p.push_back(make_pair(d, dot)); + } + + // Sort Pairs from closest to farthest + sort(p.begin(), p.end(), [](const pair &left, const pair &right) {return left.first < right.first; }); + + // First two/three are closest (we will never see all four) + float fBound = 0.005; + if (acos(p.at(0).second) < fBound) bBoundary = true; + if (acos(p.at(1).second) < fBound) bBoundary = true; + if (acos(p.at(2).second) < fBound) bBoundary = true; + } + } + } + + // Calculate distance to ceiling and floor + int nCeiling = (int)((float)(nScreenHeight/2.0) - nScreenHeight / ((float)fDistanceToWall)); + int nFloor = nScreenHeight - nCeiling; + + // Shader walls based on distance + short nShade = ' '; + if (fDistanceToWall <= fDepth / 4.0f) nShade = 0x2588; // Very close + else if (fDistanceToWall < fDepth / 3.0f) nShade = 0x2593; + else if (fDistanceToWall < fDepth / 2.0f) nShade = 0x2592; + else if (fDistanceToWall < fDepth) nShade = 0x2591; + else nShade = ' '; // Too far away + + if (bBoundary) nShade = ' '; // Black it out - for (int y = 0; y < nScreenHeight; y++) - { - // Each Row - if(y <= nCeiling) - screen[y*nScreenWidth + x] = ' '; - else if(y > nCeiling && y <= nFloor) - screen[y*nScreenWidth + x] = nShade; - else // Floor - { - // Shade floor based on distance - float b = 1.0f - (((float)y -nScreenHeight/2.0f) / ((float)nScreenHeight / 2.0f)); - if (b < 0.25) nShade = '#'; - else if (b < 0.5) nShade = 'x'; - else if (b < 0.75) nShade = '.'; - else if (b < 0.9) nShade = '-'; - else nShade = ' '; - screen[y*nScreenWidth + x] = nShade; - } - } - } + for (int y = 0; y < nScreenHeight; y++) + { + // Each Row + if(y <= nCeiling) + screen[y*nScreenWidth + x] = ' '; + else if(y > nCeiling && y <= nFloor) + screen[y*nScreenWidth + x] = nShade; + else // Floor + { + // Shade floor based on distance + float b = 1.0f - (((float)y -nScreenHeight/2.0f) / ((float)nScreenHeight / 2.0f)); + if (b < 0.25) nShade = '#'; + else if (b < 0.5) nShade = 'x'; + else if (b < 0.75) nShade = '.'; + else if (b < 0.9) nShade = '-'; + else nShade = ' '; + screen[y*nScreenWidth + x] = nShade; + } + } + } // Display Frame mvaddwstr(0, 0, screen); - // Display Stats + // Display Stats wchar_t stats[40]; swprintf(stats, 40, L"X=%3.2f, Y=%3.2f, A=%3.2f FPS=%3.2f ", fPlayerX, fPlayerY, fPlayerA, 1.0f/fElapsedTime); mvaddwstr(0, 0, stats); - // Display Map - for (int nx = 0; nx < nMapWidth; nx++) + // Display Map + for (int nx = 0; nx < nMapWidth; nx++) { for (int ny = 0; ny < nMapWidth; ny++) { @@ -300,7 +300,7 @@ int main() } endwin(); - return 0; + return 0; } // That's It!! - Jx9 From 827ee2175e46ac5ed5288ce3f74cb89373d254a7 Mon Sep 17 00:00:00 2001 From: Rohan Liston Date: Sat, 2 Dec 2017 17:05:08 +1100 Subject: [PATCH 3/5] Align comments --- linux/CommandLineFPS.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/CommandLineFPS.cpp b/linux/CommandLineFPS.cpp index ca02ab2..177cb1c 100644 --- a/linux/CommandLineFPS.cpp +++ b/linux/CommandLineFPS.cpp @@ -177,10 +177,10 @@ int main() float fRayAngle = (fPlayerA - fFOV/2.0f) + ((float)x / (float)nScreenWidth) * fFOV; // Find distance to wall - float fStepSize = 0.1f; // Increment size for ray casting, decrease to increase resolution + float fStepSize = 0.1f; // Increment size for ray casting, decrease to increase resolution float fDistanceToWall = 0.0f; - bool bHitWall = false; // Set when ray hits wall block + bool bHitWall = false; // Set when ray hits wall block bool bBoundary = false; // Set when ray hits boundary between two wall blocks float fEyeX = sinf(fRayAngle); // Unit vector for ray in player space From e56a8c1025a68913f7d7a47066455dfa88ba7c43 Mon Sep 17 00:00:00 2001 From: Rohan Liston Date: Sat, 2 Dec 2017 17:06:08 +1100 Subject: [PATCH 4/5] Align code/comments --- linux/CommandLineFPS.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/linux/CommandLineFPS.cpp b/linux/CommandLineFPS.cpp index 177cb1c..b3c298f 100644 --- a/linux/CommandLineFPS.cpp +++ b/linux/CommandLineFPS.cpp @@ -245,11 +245,11 @@ int main() // Shader walls based on distance short nShade = ' '; - if (fDistanceToWall <= fDepth / 4.0f) nShade = 0x2588; // Very close - else if (fDistanceToWall < fDepth / 3.0f) nShade = 0x2593; - else if (fDistanceToWall < fDepth / 2.0f) nShade = 0x2592; - else if (fDistanceToWall < fDepth) nShade = 0x2591; - else nShade = ' '; // Too far away + if (fDistanceToWall <= fDepth / 4.0f) nShade = 0x2588; // Very close + else if (fDistanceToWall < fDepth / 3.0f) nShade = 0x2593; + else if (fDistanceToWall < fDepth / 2.0f) nShade = 0x2592; + else if (fDistanceToWall < fDepth) nShade = 0x2591; + else nShade = ' '; // Too far away if (bBoundary) nShade = ' '; // Black it out @@ -267,9 +267,9 @@ int main() float b = 1.0f - (((float)y -nScreenHeight/2.0f) / ((float)nScreenHeight / 2.0f)); if (b < 0.25) nShade = '#'; else if (b < 0.5) nShade = 'x'; - else if (b < 0.75) nShade = '.'; + else if (b < 0.75) nShade = '.'; else if (b < 0.9) nShade = '-'; - else nShade = ' '; + else nShade = ' '; screen[y*nScreenWidth + x] = nShade; } } From 1ee47bfca7345ec08e7d1b048f5d50178416c258 Mon Sep 17 00:00:00 2001 From: Rohan Liston Date: Sat, 2 Dec 2017 17:12:55 +1100 Subject: [PATCH 5/5] Add screenshot --- linux/README.md | 1 + linux/screenshot.png | Bin 0 -> 67403 bytes 2 files changed, 1 insertion(+) create mode 100644 linux/screenshot.png diff --git a/linux/README.md b/linux/README.md index ff84f84..c90cd74 100644 --- a/linux/README.md +++ b/linux/README.md @@ -1,5 +1,6 @@ # CommandLineFPS - Linux Version +![Screenshot](screenshot.png) ## Prerequisites * CMake - C/C++ build system diff --git a/linux/screenshot.png b/linux/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..0bc1dff03dee9b1cef60a62fa1be827e69fa7252 GIT binary patch literal 67403 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 z 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 literal 0 HcmV?d00001