diff --git a/README.md b/README.md index 1308140..b421867 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ # SER-225 Fall 2021 Team A2 + #### [Original GitHub Link](https://github.com/LittleTealeaf/SER-225-Team-A2) [//]: # (Welcome to the FAll 2021 Team A2's SER-225's repository! If you are reading this from the text file, it is advised to either enable markdown rendering, view on your own repository or fork, or click the link above to view the published repository.) ## What is this project? + *Taken from the original archive provided* ->This is a game created for Quinnipiac's SER225 Agile Development class. Students will be paired up in teams for an entire semester, and will use agile development sprints to work further on this game. This usually involves adding features and fixing bugs (I'm sure there are bugs...) +> This is a game created for Quinnipiac's SER225 Agile Development class. Students will be paired up in teams for an entire semester, and will use agile development sprints to work further on this game. This usually involves adding features and fixing bugs (I'm sure there are bugs...) ## How do I use this project? + This is a standalone project, so running this program in either Eclipse or Intellij will work. However, much of this project has been created in IntelliJ, and setting up the project's java will be much easier in IntelliJ. You can download the Community Version of IntelliJ [here](https://www.jetbrains.com/idea/). ### Java 17 @@ -18,14 +21,17 @@ If you haven't gotten java recently, you probably are running an out-dated java The image above is showing the java version selector under the "project settings" (Look under `File`). Additionally, make sure that `Language Level` is also set to the most recent language level. ### Running the Project + There are two main classes used to run the project. First, and the most important, is the `Game.java` class. This can be found at [`src/Game/Game.java`](src/Game/Game.java). Running the main class will launch the full game. Additionally, there is a level editor that allows you to easily edit each level individually. To run this, you can run the main method in [`src/MapEditor/MapEditor.java`](src/MapEditor/MapEditor.java) ## Documentation -Ok, so you've probably been shown the original documentation link. Additionally, we have the original documentation located at [/docs](docs). Be warned, be very warned. We changed A LOT during our work here, so the documentation can and will be wrong. Specifically, when it comes to the following aspects of the game, assume that the documentation is of very little specific use. However, feel free to read it to understand the general basis of the project. - - Menu Screen, Level Loading, Screen Manager - - Player class - - Game Loop + +Ok, so you've probably been shown the original documentation link. Additionally, we have the original documentation located at [/docs_archive](docs_archive). Be warned, be very warned. We changed A LOT during our work here, so the documentation can and will be wrong. Specifically, when it comes to the following aspects of the game, assume that the documentation is of very little specific use. However, feel free to read it to understand the general basis of the project. + +- Menu Screen, Level Loading, Screen Manager +- Player class +- Game Loop That being said, many of the changes done have been documented in hopes to explain the madness. These comments are done in a way where in IntelliJ, hovering over any call should display a tooltip that describes that method, or at least if that method was documented. \ No newline at end of file diff --git a/Team A2/README.md b/Team A2/README.md index 8c55439..2e21fb2 100644 --- a/Team A2/README.md +++ b/Team A2/README.md @@ -1,4 +1,5 @@ # Team A2 + For our project, one of the tasks that we were required to do was to create and run test cases for each of our bug fixes or enhancements. We first used a `.docx` format, as seen in the [User Stories](User%20Stories) folder. However, in order to allow both the ease of viewing on any device, and a nice display on our github repository, we later switched to a `.md` format. We converted all previous test cases to the new format, and placed everything in [Test Cases](Test%20Cases) \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-01.md b/Team A2/Test Cases/Test Case SCP-01.md index 1f2166c..19ecd54 100644 --- a/Team A2/Test Cases/Test Case SCP-01.md +++ b/Team A2/Test Cases/Test Case SCP-01.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-1 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -21,6 +22,7 @@ |9|Hold shift while holding the right arrow key|The character moves right at a fast speed|P| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/7/2021 (Late, marked passed as task was submitted) - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-03.md b/Team A2/Test Cases/Test Case SCP-03.md index bed9581..d329124 100644 --- a/Team A2/Test Cases/Test Case SCP-03.md +++ b/Team A2/Test Cases/Test Case SCP-03.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-3 | | :--- | :--- | | Owner of Test | Jacob Conrad | @@ -18,6 +19,7 @@ |3c|Receive Enemy Attack (if it has one)|The player should die|Pass| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 10/12/2021 (Scrum Sprint 1 enemy) - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-04.md b/Team A2/Test Cases/Test Case SCP-04.md index a8d26d9..419c38d 100644 --- a/Team A2/Test Cases/Test Case SCP-04.md +++ b/Team A2/Test Cases/Test Case SCP-04.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-4 | | :--- | :--- | | Owner of Test | Ty Hutchison | @@ -15,8 +16,8 @@ |3|Go through the tutorial and learn how to play the game|User completes tutorial and properly knows how to play the game|| |4|Return ot the main menu after the level is complete|After the gold block is hit return to the main menu|F (Functionality Changed)| - ### Test Completion + - **Tester**: Thomas Kwashnak; - **Date of Test**: (Late Copy/Test 11/7/2021) - **Test Result**: F (Specific test failed because implementation and design has changed since creation of test case) \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-05.md b/Team A2/Test Cases/Test Case SCP-05.md index ba03ca2..756cb70 100644 --- a/Team A2/Test Cases/Test Case SCP-05.md +++ b/Team A2/Test Cases/Test Case SCP-05.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-5 | | :--- | :--- | | Owner of Test | Ty Hutchison | @@ -16,6 +17,7 @@ |4|Return to the main menu after level is completed|After the gold block is hit, returns to the main menu|F (Change in Design)| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/7/2021 (Late, done during copy) - **Test Result**: P (Only fails were not game-breaking) \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-06.md b/Team A2/Test Cases/Test Case SCP-06.md index ecf839b..30435ae 100644 --- a/Team A2/Test Cases/Test Case SCP-06.md +++ b/Team A2/Test Cases/Test Case SCP-06.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-6 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -15,8 +16,8 @@ |3|Restart the game and use the level selection in the main menu to select level 5|The player is loaded into the fifth level|P| |4|Play and beat the 5th level|The player can beat the fifth level, and it has a noticeable difference from the first level|P| - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 10/27/2021 - **Test Result**: Pass \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-07.md b/Team A2/Test Cases/Test Case SCP-07.md index b2acf81..48f0807 100644 --- a/Team A2/Test Cases/Test Case SCP-07.md +++ b/Team A2/Test Cases/Test Case SCP-07.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-7 | | :--- | :--- | | Owner of Test | Jack Handy | @@ -17,6 +18,7 @@ |5|Repeat actions 1 through 4 for each additional level|Tasks 1-4 have the correct expected result for each level|Pass for levels 6 and 7| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 10/27/2021 - **Test Result**: Pass (*Levels 6-7*) \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-08.md b/Team A2/Test Cases/Test Case SCP-08.md index edffd4e..a3fd6e8 100644 --- a/Team A2/Test Cases/Test Case SCP-08.md +++ b/Team A2/Test Cases/Test Case SCP-08.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-8 | | :--- | :--- | | Owner of Test | Jacob Conrad | @@ -12,8 +13,8 @@ |:---:| :--- | :---- | :---: | |1|Navigate to the Resources folder and open Dog.png | The Dog.png file is inside the resources folder and has the boss design sprite sheet inside it|Pass| - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 10/27/2021 - **Test Result**: Pass \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-09.md b/Team A2/Test Cases/Test Case SCP-09.md index 247ae4c..957bd13 100644 --- a/Team A2/Test Cases/Test Case SCP-09.md +++ b/Team A2/Test Cases/Test Case SCP-09.md @@ -19,6 +19,7 @@ |6| The player touches the boss | The player dies |Pass| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/10/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-10.md b/Team A2/Test Cases/Test Case SCP-10.md index c729780..f662425 100644 --- a/Team A2/Test Cases/Test Case SCP-10.md +++ b/Team A2/Test Cases/Test Case SCP-10.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-10| | :--- | :--- | | Owner of Test | Ty Hutchison| @@ -15,8 +16,8 @@ |3| User will read the Narrative Screen | User will read the narrative backstory and understand the games background | Pass | |4| User will complete the rest of the game | The User will be able to complete the rest of the game and complete the backstory | Pass (inferred) | - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/9/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-11.md b/Team A2/Test Cases/Test Case SCP-11.md index 49ffb35..233068d 100644 --- a/Team A2/Test Cases/Test Case SCP-11.md +++ b/Team A2/Test Cases/Test Case SCP-11.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-11 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -18,6 +19,7 @@ |6|Click on the "Start Game"|Clicking on "start game" begins the game|P| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/7/2021 (Completed during copying) - **Test Result**: Fail (Clicking back arrow does not navigate user back to the main menu) \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-15.md b/Team A2/Test Cases/Test Case SCP-15.md index a345e64..ca64297 100644 --- a/Team A2/Test Cases/Test Case SCP-15.md +++ b/Team A2/Test Cases/Test Case SCP-15.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-15| | :--- | :--- | | Owner of Test | Ty Hutchison| @@ -17,6 +18,7 @@ |5| Check base volume | The User will be able to comfortable play the game without having the music too loud | Pass | ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/9/2021 - **Test Result**: Passed diff --git a/Team A2/Test Cases/Test Case SCP-16.md b/Team A2/Test Cases/Test Case SCP-16.md index c9d9bf6..5c41b4c 100644 --- a/Team A2/Test Cases/Test Case SCP-16.md +++ b/Team A2/Test Cases/Test Case SCP-16.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-16 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -16,6 +17,7 @@ |4|Play the game|The user is able to play the game successfully without any sound|Pass| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 10/27/2021 - **Test Result**: Pass \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-20.md b/Team A2/Test Cases/Test Case SCP-20.md index 3e347d1..878c10a 100644 --- a/Team A2/Test Cases/Test Case SCP-20.md +++ b/Team A2/Test Cases/Test Case SCP-20.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-20 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -20,6 +21,7 @@ |4|Press escape|The options menu is closed and the main menu is showing again|P| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/7/2021 (Late, filled during copy of new format) - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-30.md b/Team A2/Test Cases/Test Case SCP-30.md index 1b22499..5aeeb77 100644 --- a/Team A2/Test Cases/Test Case SCP-30.md +++ b/Team A2/Test Cases/Test Case SCP-30.md @@ -1,12 +1,7 @@ ### Test Case Information \ -| TEST CASE ID | SCP-30 | -| :--- | :--- | -| Owner of Test | JACOB CONRAD | -| Test Name | ADD CHARACTER PREVIEW | -| Date of Last Revision | 11/9/2021 | -| Test Objective | Verify that a preview for the user to see when changing their character is in the options screen | +| TEST CASE ID | SCP-30 | | :--- | :--- | | Owner of Test | JACOB CONRAD | | Test Name | ADD CHARACTER PREVIEW | | Date of Last Revision | 11/9/2021 | | Test Objective | Verify that a preview for the user to see when changing their character is in the options screen | ### Procedure @@ -18,8 +13,8 @@ |4| Press "Green" | The green cat is displayed |Pass| |5| Press "Orange" | The orange cat is displayed |Pass| - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/10/2021 - **Test Result**: Passed (Adding backlog item for finalizing) \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-31.md b/Team A2/Test Cases/Test Case SCP-31.md index 01f04c9..0447bb0 100644 --- a/Team A2/Test Cases/Test Case SCP-31.md +++ b/Team A2/Test Cases/Test Case SCP-31.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-31 | | :--- | :--- | | Owner of Test | Jacob Conrad | @@ -20,6 +21,7 @@ |8|Restart the tutorial level|Health should be visible again and be at three hearts (not the previous zero hearts from losing and going to the menu)|Pass ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 10/27/2021 - **Test Result**: Pass \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-32.md b/Team A2/Test Cases/Test Case SCP-32.md index e565e94..b9bec51 100644 --- a/Team A2/Test Cases/Test Case SCP-32.md +++ b/Team A2/Test Cases/Test Case SCP-32.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-32| | :--- | :--- | | Owner of Test | Ty Hutchison| @@ -17,6 +18,7 @@ |5| Check in game instructions | The User will be able to press 'x' to see in game instructions | Pass | ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/9/2021 - **Test Result**: Passed diff --git a/Team A2/Test Cases/Test Case SCP-33.md b/Team A2/Test Cases/Test Case SCP-33.md index 50934f8..52333d8 100644 --- a/Team A2/Test Cases/Test Case SCP-33.md +++ b/Team A2/Test Cases/Test Case SCP-33.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-33 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -17,8 +18,8 @@ |5|Tap the E key really fast while facing left|The fireball attack starts from the character.|P |6|Tap the E key really fast while facing right|The fireball attack starts from the character|P - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 10/27/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-34.md b/Team A2/Test Cases/Test Case SCP-34.md index b9b69bb..23c9448 100644 --- a/Team A2/Test Cases/Test Case SCP-34.md +++ b/Team A2/Test Cases/Test Case SCP-34.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-34| | :--- | :--- | | Owner of Test | Ty Hutchison | @@ -15,8 +16,8 @@ |3| Find new map tile next to water tile | User will find the new map tile, sand, next to water tiles in game |Passed| |4| User will exit the game | The User will properly exit the game |Passed| - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/29/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-35.md b/Team A2/Test Cases/Test Case SCP-35.md index bf7742b..757f90c 100644 --- a/Team A2/Test Cases/Test Case SCP-35.md +++ b/Team A2/Test Cases/Test Case SCP-35.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-35 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -44,6 +45,7 @@ |34|Navigate and run into the bug to die, and hit escape to go back to the main menu|The player dies, and then the main menu is displayed after the player hits escape|P| ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/7/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-41.md b/Team A2/Test Cases/Test Case SCP-41.md index 9e0f169..b96bf62 100644 --- a/Team A2/Test Cases/Test Case SCP-41.md +++ b/Team A2/Test Cases/Test Case SCP-41.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-41 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -18,6 +19,7 @@ |6|Beat the first level|The first level is beatable, the "level complete" dialogue displays, and after a few seconds the user is taken to the next level|P| ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/7/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-44.md b/Team A2/Test Cases/Test Case SCP-44.md index fab3526..1b438a2 100644 --- a/Team A2/Test Cases/Test Case SCP-44.md +++ b/Team A2/Test Cases/Test Case SCP-44.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-44 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -23,6 +24,7 @@ |11|Attempt to walk off the edge on the right of the map|The player hits an invisible barrier preventing them from leaving the screen|Pass| ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/8/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-45.md b/Team A2/Test Cases/Test Case SCP-45.md index f795c6b..e45d775 100644 --- a/Team A2/Test Cases/Test Case SCP-45.md +++ b/Team A2/Test Cases/Test Case SCP-45.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | Test Case SCP-45 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -20,6 +21,7 @@ |8|Navigate to the platform and aim to land on the left edge of the moving platform|Player does not die when hitting the corner/size of the moving platform|P| ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/7/2021 - **Test Result**: Passed diff --git a/Team A2/Test Cases/Test Case SCP-46.md b/Team A2/Test Cases/Test Case SCP-46.md index 17b13d1..56d134f 100644 --- a/Team A2/Test Cases/Test Case SCP-46.md +++ b/Team A2/Test Cases/Test Case SCP-46.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-46 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -18,6 +19,7 @@ |6| Select the spike block tile and click on the map to place it. | The spike block is placed on the map. | Pass | ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/7/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-47.md b/Team A2/Test Cases/Test Case SCP-47.md index 18c7f0d..dbbedb3 100644 --- a/Team A2/Test Cases/Test Case SCP-47.md +++ b/Team A2/Test Cases/Test Case SCP-47.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-47| | :--- | :--- | | Owner of Test | Ty Hutchison| @@ -17,6 +18,7 @@ |5| Exit the game | The User will exit the game |Passed| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/29/2021 4:21 PM - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-48.md b/Team A2/Test Cases/Test Case SCP-48.md index 8f92c1a..711e0ba 100644 --- a/Team A2/Test Cases/Test Case SCP-48.md +++ b/Team A2/Test Cases/Test Case SCP-48.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-48 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -16,6 +17,7 @@ |5| Save the change compare the old txt file with the new one | The txt file has been changed |Pass| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/7/2021 - **Test Result**: Passed diff --git a/Team A2/Test Cases/Test Case SCP-49.md b/Team A2/Test Cases/Test Case SCP-49.md index c11eb6d..e8bce09 100644 --- a/Team A2/Test Cases/Test Case SCP-49.md +++ b/Team A2/Test Cases/Test Case SCP-49.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-49 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -26,6 +27,7 @@ |14| Restart the game and beat each level at every difficulty to ensure the levels are still possible. (Use the level select to make hardcore mode easier) | Each level is beat on every difficulty | Passed | ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/30/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-51.md b/Team A2/Test Cases/Test Case SCP-51.md index 95d43ec..01d517a 100644 --- a/Team A2/Test Cases/Test Case SCP-51.md +++ b/Team A2/Test Cases/Test Case SCP-51.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-51| | :--- | :--- | | Owner of Test | Ty Hutchison| @@ -14,8 +15,8 @@ |2| Hit Quit Option | The User will select the quit option in the main menu |Pass| |3| The game will close | The game will close |Pass| - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/29/2021 3:59 PM - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-52.md b/Team A2/Test Cases/Test Case SCP-52.md index b2c28d8..bf6b080 100644 --- a/Team A2/Test Cases/Test Case SCP-52.md +++ b/Team A2/Test Cases/Test Case SCP-52.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-52 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -7,14 +8,15 @@ | Test Objective | Testing Additions and Functionality Changes of Speed-Running Enhancements | #### Tested Enhancements - - Stopwatch - - Top-Right of screen when playing - - Entering from main menu resets timer - - Stores time took to complete each level, displays current level's time, as well as total time for all previous times - - Some way to display the user's final result (breakdown of levels) after beating final boss - - Deaths are in some way detrimental to a user's level time (either make death animation slower, or make death screen longer) - - Pausing the game pauses the time - - Level Completed Screens display quicker (shorter time) + +- Stopwatch + - Top-Right of screen when playing + - Entering from main menu resets timer + - Stores time took to complete each level, displays current level's time, as well as total time for all previous times + - Some way to display the user's final result (breakdown of levels) after beating final boss + - Deaths are in some way detrimental to a user's level time (either make death animation slower, or make death screen longer) + - Pausing the game pauses the time +- Level Completed Screens display quicker (shorter time) ### Procedure @@ -33,6 +35,7 @@ |11|Die, and then respawn|The timer is reset back to 0|Pass| ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/30/2021 - **Test Result**: Pass \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-55.md b/Team A2/Test Cases/Test Case SCP-55.md index 983eca9..08b462e 100644 --- a/Team A2/Test Cases/Test Case SCP-55.md +++ b/Team A2/Test Cases/Test Case SCP-55.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-55 | | :--- | :--- | | Owner of Test | Jacob Conrad | @@ -16,8 +17,8 @@ |4|Click on blue and then hover over orange or green|The blue cat should be displayed|Pass| |5|Click on green and then hover over blue or orange|The green cat should be displayed|Pass| - ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/29/2021 10:17 AM - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-56.md b/Team A2/Test Cases/Test Case SCP-56.md index ad163af..9a6caae 100644 --- a/Team A2/Test Cases/Test Case SCP-56.md +++ b/Team A2/Test Cases/Test Case SCP-56.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-56 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -9,7 +10,6 @@ |Software Requirement|**Screen recording software:** Needs to be able to record the screen such that the tester can go back and accurately measure the time between events. *Suggested:* OBS| |Prerequisites|Test Case SCP-57 must be completed prior to this merge| - ### Procedure |Step | Action | Expected Result | Measurement Field | @@ -25,11 +25,11 @@ |9|Complete the tutorial level|When the tutorial level is completed, the `level complete` is shown, and after a few seconds the player is loaded into the next level|**Measurement 5:** The time that the `level complete` screen is shown ### Test Execution + Instances of tests are listed in table below (template provided). Measurements listed in instructions above are listed below for test completer to evaluate. **Overall Test Completion** indicates whether all steps were completed and no errors / inability to follow instructions occurred. - |Date|Name|Device Information | Measure 1|Measure 2|Measure 3|Measure 5|Measure 4|Pass/Fail| |:---:|:---|:---|:---:|:---:|:---:|:---:|:---:|:---:| |[DATE]|[TESTER_NAME]|[CONFIG]|[MEASURE 1]|[MEASURE 2]|[MEASURE 3]|[MEASURE 4]|[MEASURE 5]|[PASS/FAIL]| @@ -40,9 +40,11 @@ Instances of tests are listed in table below (template provided). Measurements l [comment]: <> (Add test rows to end here ^^) ### Completion Criteria - - There are no outliers on all measurements + +- There are no outliers on all measurements ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/30/2021 - **Test Result**: Pass \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-57.md b/Team A2/Test Cases/Test Case SCP-57.md index ca6eca9..219f5ca 100644 --- a/Team A2/Test Cases/Test Case SCP-57.md +++ b/Team A2/Test Cases/Test Case SCP-57.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-57 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -7,13 +8,14 @@ | Test Objective | Ensure that all current functionalities of the player is implemented in new Player class and still functional | Functionality of `Player.java` tested: - - Movement, and movement animations - - Jumping, including gravity and hitting floor - - Animation while jumping - - Collision with enemies - - Shooting, including animations while standing still and moving - - Collision with map boundaries and map walls - - Ability to complete all levels with physics + +- Movement, and movement animations +- Jumping, including gravity and hitting floor +- Animation while jumping +- Collision with enemies +- Shooting, including animations while standing still and moving +- Collision with map boundaries and map walls +- Ability to complete all levels with physics ### Procedure @@ -48,6 +50,7 @@ Functionality of `Player.java` tested: |26|Play all levels of the game|Each level is completable|Pass| ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/30/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-58.md b/Team A2/Test Cases/Test Case SCP-58.md index a55f4c8..e15ef85 100644 --- a/Team A2/Test Cases/Test Case SCP-58.md +++ b/Team A2/Test Cases/Test Case SCP-58.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-58 | | :--- | :--- | | Owner of Test | Jacob Conrad | @@ -18,6 +19,7 @@ |4|Touch the boss |The cat should die|Pass| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/29/2021 10:20 AM - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-59.md b/Team A2/Test Cases/Test Case SCP-59.md index b284fc0..f2607b5 100644 --- a/Team A2/Test Cases/Test Case SCP-59.md +++ b/Team A2/Test Cases/Test Case SCP-59.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-59 | | :--- | :--- | | Owner of Test | Thomas Kwashnak | @@ -15,6 +16,7 @@ |3|Use `w/up arrow`,`a/left arrow`,`s/down arrow`,`d/right arrow` to navigate from and to each level listed|Navigating by a direction selects the first option visually in that direction. If the option is at the edge, the selection does not change.|Pass| ### Test Completion + - **Tester**: Nicholas Tourony - **Date of Test**: 11/18/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-60.md b/Team A2/Test Cases/Test Case SCP-60.md index a7a9aa6..6aabd35 100644 --- a/Team A2/Test Cases/Test Case SCP-60.md +++ b/Team A2/Test Cases/Test Case SCP-60.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-60| | :--- | :--- | | Owner of Test | Ty Hutchison| @@ -16,6 +17,7 @@ |4| Enter Main Menu Screen | The User will enter the Main Menu screen |Pass| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/29/2021 3:57 PM - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-61.md b/Team A2/Test Cases/Test Case SCP-61.md index 2c9c6c5..0b19df5 100644 --- a/Team A2/Test Cases/Test Case SCP-61.md +++ b/Team A2/Test Cases/Test Case SCP-61.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-61 | | :--- | :--- | | Owner of Test | Jacob Conrad | @@ -24,6 +25,7 @@ |12|Play through the level until you find the checkpoint flag. Touch the flag and then die|The user should respawn at the checkpoint flag|Pass| ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/30/2021 - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/Test Case SCP-62.md b/Team A2/Test Cases/Test Case SCP-62.md index c906095..0d43bb4 100644 --- a/Team A2/Test Cases/Test Case SCP-62.md +++ b/Team A2/Test Cases/Test Case SCP-62.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | SCP-46 | | :--- | :--- | | Owner of Test | Nicholas Tourony | @@ -18,6 +19,7 @@ |6| Select the passable floating platform tile and click on the map to place it. | The passable floating platform is placed on the map. | Pass | ### Test Completion + - **Tester**: Thomas Kwashnak - **Date of Test**: 11/29/2021 10:14 AM - **Test Result**: Passed \ No newline at end of file diff --git a/Team A2/Test Cases/template.md b/Team A2/Test Cases/template.md index 0c023d9..26f6c7c 100644 --- a/Team A2/Test Cases/template.md +++ b/Team A2/Test Cases/template.md @@ -1,4 +1,5 @@ ### Test Case Information + | TEST CASE ID | [TEST ID] | | :--- | :--- | | Owner of Test | [OWNER NAME] | @@ -15,6 +16,7 @@ |n| [STEP TITLE] | [EXPECTED RESULT] | [RESULT] | ### Test Completion + - **Tester**: [TESTER NAME] - **Date of Test**: DATE OF TEST - **Test Result**: TEST RESULT \ No newline at end of file diff --git a/docs/Advice/advice.md b/docs/Advice/advice.md deleted file mode 100644 index 7f26878..0000000 --- a/docs/Advice/advice.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -layout: default -title: Advice -nav_order: 9 -permalink: /Advice -search_exclude: true ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Advice - -This page contains some advice and tips from myself for developers looking to expand upon this game in a creative direction -of their choice. - -## How to start working on this game - -In the workplace, a majority of the time you will find yourself being put to work on an existing project where you don't -understand what it is doing, how the code works, or sometimes what the goal of the project even is. It is normal to feel -overwhelmed when this happens. I used to get really anxious during times like these (and sometimes still do), but truthfully -there is nothing to be anxious about. You can only do as much as you can, and coding is really hard, so my advice for you to -start working on this game is to just take things slow. - -Start by playing around with the code and changing minor things. Running the game and seeing your changes in action -will help you identify and confirm where different pieces of code logic is located. Maybe head over to the `Cat` class and -change its `jumpHeight` instance variable, or play around in the map editor. Just get a feel for the project and what you're -going to be working with. Also, don't be afraid to consult your peers, google, or this site for answers to your questions. - -For your first couple tasks you should pick up something on the small side. No reason to dive head first into things before -you understand how anything works. Whenever I am placed on a new project at work, I always look at the list of upcoming tasks -and take one or two easier ones (usually bug fixes) that will give me a chance to get a feel for how the project is laid out. Once -the initial "training" tasks are completed, take on a bigger one if you feel comfortable. Sometimes tasks that you think will take -an hour will take multiple days; that's just the way programming is. :man_shrugging: :woman_shrugging: - -Understanding how the game loop's `update` and `draw` cycles work is the key to success! - -## Copy and paste and the art of a template - -If you are creating something, such as a new enemy, you (almost) always have a starting point: existing code! -Using something else as a template is the key to working within an already defined logic flow. Most of the code written for this game -is "setting up" resources, and there's no need to reinvent the wheel if you don't have to. Go ahead and find an existing class that -has similar functionality to the thing you want to create, copy and paste the entire thing, and then start modifying values/adding code -as you see fit. You also have a perfectly working "clone" of the old class at first, so it will be set up and able to be tested in game -right away. - -While developing this program, I continually copy and pasted my own code to get things setup correctly. There's no need to memorize -things like that when the answers are all available to you -- use that saved brain space for memorizing more song lyrics! - -## Identify classes you will need to work with for a specific task - -There are tons of classes in this game (most game codebases are pretty large and complex), but you will find that -for each task you will rarely come into contact with more than two or three of them at a time. Identifying which classes you don't -have to touch for a specific task lets you really narrow down your focus to a much smaller unit of code. - -Once you have decided on a task to work on (whether it be adding a new feature, updating an existing feature, or fixing a bug), -take a few minutes to identify where in the project (which files/methods) will need to be modified/worked with in order to successfully -complete the task. While this may take a longer time at first, having a plan will make the task feel less daunting, give you -a place to at least start from, and also makes it easier for others to help you if you have some sort of "logical reasoning" that you -can explain to them for how you got to the point where you ended up stuck. Once you get more comfortable with the codebase, -this pre-planning step will take far less time to do, to the point where you'll probably just dive right into the code and ask questions -later. - -## Creating Game Art - -As a developer that has never attempted to make my own game art before creating this game, I have to say it was a lot of fun (even -though it's not very good). I recommend that everyone give a shot at creating some game art, even if it's just editing the colors -on existing sprites. While the logic that goes into running the game is extremely important for obvious reasons, the visuals of a game are also very important -as they are the "output" to the user of the program, and it never hurts to get involved a bit with the other side of things. - -With that said, the [spriters resource](https://www.spriters-resource.com/) is an amazing website full of graphics from -any game on any console that you can think of. It also has a companion site [sounds resource](https://www.sounds-resource.com/) for looking up music/sound files -(something that this game desperately needs). - -## Have fun - -Making a game is fun, so have fun! Be creative. There's no "right" or "wrong" way for you to expand upon this game, just choose a direction -that you will enjoy working on. I remember when I took this class back when I was a student at Quinnipiac, my team added Professors Hoffman, -Blake, and Duncan as enemies into the game because it was hilarious, and we ended up having a lot of fun just making each other laugh -while improving our overall coding skills. - -These were the professor graphics in case you were wondering (I miraculously found them on my old google drive account). - -![judgement-professor-graphics.png](../assets/images/judgement-professor-graphics.png) \ No newline at end of file diff --git a/docs/BugReport/bug-report.md b/docs/BugReport/bug-report.md deleted file mode 100644 index 28b53b1..0000000 --- a/docs/BugReport/bug-report.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -layout: default -title: Bug Report -nav_order: 7 -permalink: /BugReport -search_exclude: true ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Bug Report - -![bug-enemy.gif](../assets/images/bug-enemy.gif) - -Below is a list of known bugs and oddities in the game code. -There are definitely more bugs than what is listed here...they just haven't been discovered yet. - -## Options Menu cannot change aspect ratio - -During this Project Our team created an options menu with the hopes to adjust the volume and the Aspect Ratio or the size of the game. -After attempting to implement the aspect ratio adjuster into the game we soon realized that to change the size of the game window we would also -have to change the size of every object in the game. Because of this the aspect ration option was left unfinished. -Our team sees this as a bug or an unfinished improvment and would love to see it finished by another team. - -## Narrative Screen only lasts 15 seconds - -On the main menu there is a option for narrative this will take you to a narrative screen but only for 15 seconds onece the timer -runs out it will return you to the main menu. -To fix this bug we recommend making the narrative screen and actual screen object and placing a back button on it -so the player can say on this screen as long as they want. - -## White color rgb(255,255,255) is transparent - -I am not quite sure why this is, but if an image uses the color white with the rgb (255,255,255), the color will be -transparent when loaded in. I think it has to do with the way the `transformColorToTransparency` method works (which I lifted -from a StackOverflow answer) that I use in order to set a transparent color in an image when loading it in (the default transparent color -is magenta rgb (255,0,255)). Maybe there's a better transparency method out there that doesn't have this adverse effect on the color white? - -The good news is this is super easy to work around -- just use a white color with a different rgb in your images, such as (255,255,254) (which will look -no different in game than (255,255,255) I promise). Off the top of my head, the `Walrus.png` image uses this technique for its tusks because -pure white wouldn't show, which is how I found out about this bug. diff --git a/docs/EnhancementIdeas/enhancement-ideas.md b/docs/EnhancementIdeas/enhancement-ideas.md deleted file mode 100644 index ab2f5c5..0000000 --- a/docs/EnhancementIdeas/enhancement-ideas.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -layout: default -title: Enhancement Ideas -nav_order: 8 -permalink: /EnhancementIdeas -search_exclude: true ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Enhancement Ideas - -Below is a list of potential enhancement ideas that could be made to this game. - -## More levels - -Right now this game only contains five levels. We think that having a game have up to 20+ levels would -add lots of dimension to the game. Users enjoy working through challenging levels. - -## Increased ways to kill enemies - -We implemented a way to kill the enemies but many games feature the ability to kill AI by jumping on their head. -We think this would be a great addition to our game. - -## Lives/Health - -Name a single platformer game where you get a game over after getting touched by an enemy one time...yeah none of them do this -because it's not very fun. Most games have a concept of "lives" where you can try a level again before a game over occurs, and they -are booted back to the main menu. Some games implore the idea of health, where the player can be hurt by enemies multiple times -in a level before dying. - -## More ways to die - -Enemies work great to hinder the player's progression through a level, but most games have other means of doing so like falling -in a bottomless pit, landing on spikes, etc. Would definitely spice things up a bit. We included dying by jumping into the water -but using lava or other barriers would be interesting to see. - -## Game music/sounds - -We added sound to the game but having sound effects for jumping, walking, swimming, or dying would add a lot to this -simple game. - -## Slopes - -Just putting this here to warn anyone that is interested in implementing slope map tile types into the game -that it is HARD and I don't recommend doing it without a solid understanding of how the game's map tile system works. -There are so many ways to implement them with their own different pros and cons, and even games known for their -slopes like 2D Sonic the Hedgehog games have some bugs (you just don't notice them because in those games you move very fast). - -## Adjusting aspect ratio - -During Scrum Sprint 4, we struggled to complete the task of changing aspect ratio in the options screen. Many expert gamers -love this ability and adding this to the game would be very time consuming. We thought this would be a great additon as well. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/creating-a-simple-game-object.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/creating-a-simple-game-object.md deleted file mode 100644 index f068d6d..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/creating-a-simple-game-object.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -layout: default -title: Creating a Simple Game Object -nav_order: 1 -parent: Game Object -grand_parent: Game Code Details -permalink: /GameCodeDetails/CreatingSimpleGameObject ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Creating a Simple Game Object - -A `GameObject` class can be used for just a single image (one graphic). Even though `GameObject` extends from -`AnimatedSprite`, if no animation is desired for a specific game object, it provides certain constructors to essentially -treat itself as a one frame animation that never changes (which results in one image being used). - -For example, the following `GameObject` constructor can be used to define just a simple one image sprite: - -```java -public GameObject(BufferedImage image, float x, float y) { - // logic -} -``` - -And can be instantiated like this: - -```java -GameObject(ImageLoader.load("my_image.png"), 30, 100); -``` - -More details on using the `ImageLoader` class to read in images [here](../../../GameEngine/GameEngineSubSections/loading-images.md). An image file can contain any graphic, -and it will be loaded into the game and utilized by the `GameObject` in its `draw` cycle to "display" itself on screen. - -From there, calling its `draw` method will display it to the screen at its x and y location. - -## Setting the Map instance in a Game Object - -In the above example, the `GameObject` will always be drawn at its x and y position relative to the screen's coordinates. -Many classes however, such as the `Player` and `Enemy` classes, need to have their drawing logic changed based on where the map's -camera has moved (this creates that "scrolling" level effect). To add the `Map` instance to the `GameObject` for it to automatically -apply that draw logic, the `setMap` method can be used. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/game-object-with-animations.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/game-object-with-animations.md deleted file mode 100644 index 950a503..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/game-object-with-animations.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -layout: default -title: Game Object With Animations -nav_order: 2 -parent: Game Object -grand_parent: Game Code Details -permalink: /GameCodeDetails/GameObjectWithAnimations ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Game Object With Animations - -Every `GameObject` subclass automatically has support for animations. An animation is an array of `Frame` class data. -Different graphics for each frame should come from a sprite sheet image file. Generally, if you are creating a `GameObject` -with animations, a new class that extends of `GameObject` should be created to separate out the animation data. - -## Sprite Sheet - -The `SpriteSheet` class is used to define a sprite sheet that the `AnimatedSprite` class uses for loading animation graphics. -A sprite sheet is one image file that contains multiple graphics (frames images) for a sprite. For example, below -is the sprite sheet image file for the cat player character (the image file is `Cat.png`: - -![cat-spritesheet.png](../../../assets/images/cat-spritesheet.png) -![blueCat.png](../../../assets/images/blueCat.png) -![greenCat.png](../../../assets/images/greenCat.png) - -Each sprite image on the sprite sheet must be the same size, and each image must have one pixel in between it and -another image that would come next to or below it (to keep track of separate images, I drew black squares around each image, which also -adds the one pixel buffer between each image). -For the cat sprite sheet above, each sprite is 24x24 pixels. Animations for each sprite are shown in a horizontal line, -so the cat walking animation for example is four different frames. This is not a requirement, images on a sprite sheet can be placed -anywhere, but I find it easier to manage animations by organizing them like this. - -From here, a `SpriteSheet` class instance can be created, which will be passed into the `GameObject` class to be used to setup animations. -Here is an example of creating a `SpriteSheet` instance that the `Cat` class uses with the above cat sprite sheet image: - -```java -new SpriteSheet(ImageLoader.load("Cat.png"), 24, 24) -``` - -The 24x24 is the size of each sprite in the sheet (height and width). - -## Defining animations in a `GameObject` subclass - -Any class that extends `GameObject` can override `GameObject's` `getAnimations` method, which will load defined animations into the class -upon initialization. The setup to override this method looks like this: - -{% raw %} -```java -@Override -public HashMap getAnimations(SpriteSheet spriteSheet) { - return new HashMap() {{ - - }}; -} -``` -{% endraw %} - -The `AnimatedSprite` class keeps track of animations with a `HashMap` that maps the name of an animation (String) to an array of `Frame` data (which -can be thought of as an array of sprites with their own information like graphics, bounding collision rectangle, etc.). In this method, -animations can easily be added directly to this `HashMap` through the use of the earlier defined `SpriteSheet`. - -Since every image in a sprite sheet must be the same size, the `SpriteSheet` class is able to provide a `getSprite` method that will -grab a particular graphic from the sheet based on row and column index. -For example, in the above cat sprite sheet image, sprite index (0, 0) would be the standing cat in the top left corner. -Sprite indexes (1, 0), (1, 1), (1, 2), and (1, 3) would equate to each of the cat walking images (the cat walking sprites -are in row 1 of the sprite sheet, and there are then four columns with one image in each. - -![cat-spritesheet-with-indexes.png](../../../assets/images/cat-spritesheet-with-indexes.png) - -### One frame animation - -Using this `getSprite` method from the `SpriteSheet` class, it makes defining each animation easy. Below is an example from the `Cat` class -which defines a one frame animation of the cat standing still. There are two separate versions of this animation defined: a stand right animation, -and a stand left animation (which will flip the image horizontally -- no need to do it manually when it can be done through code): - -{% raw %} -```java -@Override -public HashMap getAnimations(SpriteSheet spriteSheet) { - return new HashMap() {{ - - // adds STAND_RIGHT animation - put("STAND_RIGHT", new Frame[] { - new FrameBuilder(spriteSheet.getSprite(0, 0), 0) - .withScale(3) - .withBounds(8, 9, 8, 9) - .build() - }); - - // adds STAND_LEFT animation - put("STAND_LEFT", new Frame[] { - new FrameBuilder(spriteSheet.getSprite(0, 0), 0) - .withScale(3) - .withImageEffect(ImageEffect.FLIP_HORIZONTAL) - .withBounds(8, 9, 8, 9) - .build() - }); - }}; -} -``` -{% endraw %} - -This uses the [builder pattern](../game-patterns.md#builder-pattern) with the `FrameBuilder` class to build a `Frame` object instance. -The animations are put into the `HashMap` by first specifying a String for the animation's name (e.g. "STAND_RIGHT" and "STAND_LEFT"). - -Notice that both standing animations specify the sprite sheet image index of (0, 0) (cat at top left corner standing still), -a delay value of 0 (since this animation is one frame there is no need to specify a delay value), -and then several attributes ar eset such as `scale`, `imageEffect`, and `bounds`. These are all optional parameters and have default values -if not specifically set (for example, if no `withImageEffect` is specified, the image by default will not have any image effects like flipping horizontally -applied to it). - -### Animations with multiple frames - -While one frame animations are fine at times, most animations have multiple frames, such as in the above cat sprite sheet -where there are four frames for the cat walking. The same method for adding one frame animations also works for multiple frame animations, -just instead of just one `Frame` in the array, multiple `Frames` will be included. - -Below is an example from the `Cat` class which defines the "WALK_RIGHT" animation, which -has four frames image indexes (1,0), (1,1), (1,2), and (1,3): - -{% raw %} -```java -@Override -public HashMap getAnimations(SpriteSheet spriteSheet) { - return new HashMap() {{ - - // add WALK_RIGHT animation - put("WALK_RIGHT", new Frame[] { - new FrameBuilder(spriteSheet.getSprite(1, 0), 200) - .withScale(3) - .withBounds(8, 9, 8, 9) - .build(), - new FrameBuilder(spriteSheet.getSprite(1, 1), 200) - .withScale(3) - .withBounds(8, 9, 8, 9) - .build(), - new FrameBuilder(spriteSheet.getSprite(1, 2), 200) - .withScale(3) - .withBounds(8, 9, 8, 9) - .build(), - new FrameBuilder(spriteSheet.getSprite(1, 3), 200) - .withScale(3) - .withBounds(8, 9, 8, 9) - .build() - }); - - }}; -} -``` -{% endraw %} - -That's it! A `Frame` array is used, and multiple `FrameBuilders` are then used to build each `Frame`. Notice the `delay` -for each `Frame` is set to 200 -- this means that if "WALK_RIGHT" is the current animation, every 200 milliseconds the game will change the current frame -to the next frame in the array. Once the last frame is reached and completed, it will wrap back around to the first. The result in game -would look like this: - -![cat-walking.gif](../../../assets/images/cat-walking-right.gif) - -Any number of `Frames` can be added to an animation, and any number of animations can be added to a `GameObject`. - -Also, putting a delay value of `-1` will prevent a frame from transitioning to the next frame, so this can be useful to place in the -last frame of an animation to prevent the animation from wrapping around. - -## Changing a Game Object's current animation while the game is running - -It is common to want to change a `GameObject's` current animation (and as a result current frame) that is shown while the game is running. -For example, with the player character `Cat`, the animation should change from "STAND_RIGHT" to "WALK_RIGHT" when the right key is pressed. -The `AnimatedSprite` class provides some instance variables that can be used to manipulate animation/frame data: - -- **currentAnimationName** -- the current animation that is being used, changing this to a new animation name will switch to that new animation -- **currentFrameIndex** -- the current frame index of an animation -- **hasAnimationLooped** -- will be true if an animation has looped at least one time (gone from the last frame index back to the first) - -For example, in the `Player` class's `update` logic for when the player is stnading and the right key is pressed, it will change -the `currentAnimationName` to "WALK_RIGHT". - -```java -public void update() { - - // if right key is pressed, set animation name to WALK_RIGHT and move game object to the right - if (Keyboard.isKeyDown(Key.RIGHT)) { - currentAnimationName = "WALK_RIGHT"; - moveRight(1); - - // if right key is not pressed, set animation name to STAND_RIGHT - } else { - currentAnimationName = "STAND_RIGHT"; - } - - // this super call is important as otherwise the AnimatedSprite class can't run its logic to - // change animations/update frames - super.update(); -} -``` diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enemies.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enemies.md deleted file mode 100644 index 43f77b1..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enemies.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -layout: default -title: Enemies -nav_order: 6 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/Enemies ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Enemies - -## What is an enemy? - -An enemy (represented by the `Enemy` class in the `Level` package) is a `MapEntity` subclass. Enemies in a platformer game -serve to hinder the player from completing a level. For example, in Mario games, enemies like goombas and koopas can kill Mario -if Mario intersects with them in a certain way. - -The `Enemy` class adds a `touchedPlayer` method that any `Enemy` subclass can override. -An enemy can be given its own animation and graphics information, -as well as its own `update` cycle which defines its behavior. There really isn't any limit to what an enemy can be made to do, -it's just up to the implementer's coding skills! - -As of right now, the `Enemy` classes base `touchedPlayer` method will hurt the player (which in turn kills the player) if the player -touches the enemy. If this behavior is not desired, you can remove that line of code, or override the `touchedPlayer` method -in an enemy class and include logic to determine if the enemy should be hurt or not. - -Most enemies adhere to similar collision detection to the player, and can follow those collision rules using the `GameObject` class's -collision methods just like the player does. More details on collision detection and handling can be found [here](../PlayerSubSections/collision-detection.md). - -## Enemy Subclass - -In the `Enemies` package, there are currently three subclasses of the `Enemy` class -- `BugEnemy`, `DinosaurEnemy`, and `Fireball`. -Each one of these classes defines an enemy in the game, which can be seen in the game's one level. - -Enemies can also set a few attributes such as: -- **isRespawnable** -- if the enemy respawns when it becomes inactive and then active again or not; if set to false, the enemy will be "left in place" next time it becomes active -- **isUpdateOffScreen** -- if the enemy should be updated even when it would not be technically considered "active" by the camera - -## Adding a new enemy to the game - -This is simple -- create a new class in the `Enemies` package, subclass the `Enemy` class, and then just implement -desired logic from there. I recommend copying an existing enemy class as a "template" of sorts to help set up and design the enemy. - -## Adding an enemy to a map - -In a map subclass's `loadEnemies` method, enemies can be defined and added to the map's enemy list. For example, in `TestMap`, -a `BugEnemy` and `DinosaurEnemy` are created and added to the enemy list: - -```java -@Override -public ArrayList loadEnemies() { - ArrayList enemies = new ArrayList<>(); - enemies.add(new BugEnemy(getPositionByTileIndex(15, 9), Direction.LEFT)); - enemies.add(new DinosaurEnemy(getPositionByTileIndex(19, 1).addY(2), getPositionByTileIndex(22, 1).addY(2), Direction.RIGHT)); - return enemies; -} -``` - -## Enemies currently in game - -### Bug Enemy - -![bug-enemy.gif](../../../assets/images/bug-enemy.gif) - -This enemy is defined by the `BugEnemy` class. I tried to replicate a typical goomba's movement patterns from Mario. Essentially, -the bug enemy will continually walk forward. If it hits a wall, it will turn around. If it walks off the edge of a cliff, it will -fall down until it touches the ground again before it starts walking forward again. - -The image file for the bug enemy is `BugEnemy.png`. - -### Dinosaur Enemy - -![dinosaur-enemy-walk.gif](../../../assets/images/dinosaur-enemy-walk.gif) - -This enemy is defnied by the `DinosaurEnemy` class. It will walk back and forth between two specific points. Every few seconds, -it will stop, turn red, and shoot out a fireball. - -![dino-enemy-shoot.png](../../../assets/images/dino-enemy-shoot.png) - -![fireball.png](../../../assets/images/fireball.png) - -This enemy is good to reference to see how to have an enemy create another enemy (the dinosaur enemy in this case -creates a fireball enemy and adds it to the map): - -```java -// determine fireball starting x location (relative to dinosaur enemy's current location), speed and direction -// based on the direction the dinosaur is facing, the fireball's direction is chosen (either right or left) -int fireballX; -float movementSpeed; -if (facingDirection == Direction.RIGHT) { - fireballX = Math.round(getX()) + getScaledWidth(); - movementSpeed = 1.5f; -} else { - fireballX = Math.round(getX()); - movementSpeed = -1.5f; -} - -// determine fireball starting y location (relative to dinosaur enemy's current location)) -int fireballY = Math.round(getY()) + 4; - -// create fireball enemy -Fireball fireball = new Fireball(new Point(fireballX, fireballY), movementSpeed, 1000, map); - -// add the fireball enemy to the map's enemy list -map.addEnemy(fireball); -``` - -The image file for the dinosaur enemy is `DinosaurEnemy.png`. - -### Fireball - -![fireball.png](../../../assets/images/fireball.png) - -As mentioned in the previous [dinosaur enemy](#dinosaur-enemy) section, the dinosaur enemy will shoot out a fireball every so often. -The fireball will travel straight for a few seconds before disappearing (it will also disappear if it hits a solid tile). -Notice that this enemy sets itself to `REMOVED` when it should disappear -- a map entity with a `REMOVED` status will be permanently -removed from the map enemies list and will not ever respawn. - -```java -if (existenceTimer.isTimeUp()) { - this.mapEntityStatus = MapEntityStatus.REMOVED; -} -``` - -The image file for the fireball is `Fireball.png`. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enhanced-map-tiles.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enhanced-map-tiles.md deleted file mode 100644 index 2946957..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enhanced-map-tiles.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -layout: default -title: Enhanced Map Tiles -nav_order: 7 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/EnhancedMapTiles ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Enhanced Map Tiles - -## What is an enhanced map tile? - -An enhanced map tile (represented by the `EnhancedMapTile` class in the `Level` package) is a `MapEntity` subclass. The idea behind -this class is that it acts just like a `MapTile` does in every way, but with the ability to define its own `update` and `draw` logic -instead of just going with the `MapTile's` default logic. This allows a map tile to essentially do whatever it wants while still -being counted as a map tile, meaning the `Player` will still consider it during its collision checks based on the `EnhancedMapTile's` tile type. Like -every `MapEntity` subclass, an `EnhancedMapTile` during its `update` cycle will be given a reference to the `Player` instance, -so it is able to interact with the player directly. - -And yes, I know the name "enhanced map tile" is dumb, I couldn't think of a better name to describe these and now I'm over it. - -## Enhanced Map Tile Subclass - -In the `EnhancedMapTiles` package, there are currently two subclasses of the `EnhancedMapTile` class -- `HorizontalMovingPlatform` and `EndLevelBox`. -Each one of these classes defines an enhanced map tile in the game, which can be seen in the game's one level. - -## Adding a new enhanced map tile to the game - -This is simple -- create a new class in the `EnhancedMapTiles` package, subclass the `EnhancedMapTile` class, and then just implement -desired logic from there. I recommend copying an existing enhanced map tile class as a "template" of sorts to help set up and design the enhanced map tile. - -## Adding an enhanced map tile to a map - -In a map subclass's `loadEnhancedMapTiles` method, enhanced map tiles can be defined and added to the map's enhanced map tile list. For example, in `TestMap`, -a `HorizontalMovingPlatform` and `EndLevelBox` are created and added to the enhanced map tile list: - -```java -@Override -public ArrayList loadEnhancedMapTiles() { - ArrayList enhancedMapTiles = new ArrayList<>(); - - enhancedMapTiles.add(new HorizontalMovingPlatform( - ImageLoader.load("GreenPlatform.png"), - getPositionByTileIndex(24, 6), - getPositionByTileIndex(27, 6), - TileType.JUMP_THROUGH_PLATFORM, - 3, - new Rectangle(0, 6,16,4), - Direction.RIGHT - )); - - enhancedMapTiles.add(new EndLevelBox( - getPositionByTileIndex(32, 7), - )); - - return enhancedMapTiles; -} -``` - -The horizontal moving platform has a pretty beefy constructor, but don't let that overwhelm you, it's all just information needed to set it up correctly. - -## Enhanced map tiles currently in game - -### Horizontal Moving Platform - -![horizontal-moving-platform.png](../../../assets/images/horizontal-moving-platform.png) - -This enhanced map tile is defined by the `HorizontalMovingPlatform` class. It is what it sounds like -- a platform that moves -horizontally back and forth. The constructor for this class definitely has too much going on, but the goal was to create a platform -a more generic class where any image could be used as the platform. The level currently uses the above simple green platform image. - -For this class, a start and stop position are passed in, and the platform will just continually go back and forth. The platform in game -is set to a tile type of `JUMP_THROUGH_PLATFORM` which allows the player to stand on it but also allows the player to jump through it from below. - -While the player is standing on the platform, the `HorizontalMovingPlatform` class detects this in its `update` method and will adjust -the player's x position to move along with the platform. - -![player-on-moving-platform.gif](../../../assets/images/player-on-moving-platform.gif) - -Not going to lie, getting this to work was a lot tougher than I expected and required me to rewrite the entire collision handling system -at one point. I'm glad I decided to get this working, as it revealed a lot of flaws in the original collision handling code, due -to decimals screwing everything up (I'm telling you, it's a lot harder than it sounds!!!). - -Also, if the platform's tile type is changed to `NOT_PASSABLE`, it will push the player if the player is in the way of it while it moves. - -The image file for the green platform is `GreenPlatform.png`. - -### End Level Box - -![end-level-box.gif](../../../assets/images/end-level-box.gif) - -This enhanced map tile is defined by the `EndLevelBox` class. Its job is simple upon being touched by the player, it will -set the player's "level state" to `LEVEL_COMPLETED`, which tells the player to do its win animation and whatever else may follow -the level being completed afterwards. - -```java -if (intersects(player)) { - player.setLevelState(LevelState.LEVEL_COMPLETED); -} -``` - -The image file for the end level box is `GoldBox.png`. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-camera.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-camera.md deleted file mode 100644 index 5fca911..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-camera.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -layout: default -title: Map Camera -nav_order: 4 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/MapCamera ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Map Camera - -## What is the map camera? - -The map camera, which is represented by the `Camera` class in the `Level` package, is the class responsible for keeping track of -which section of a map needs to be shown/updated during a specific time period. If you have ever played a 3D video game, you will have an -idea of what a game map's camera is -- often times you are given the ability in these games to rotate it yourself to see different -parts of the map as you play. In a 2D world, the camera is a lot simpler, as there are only four movement directions -- up, down, left, and right. - -Take a look at the below gif: - -![playing-level.gif](../../../assets/images/playing-level.gif) - -As the player moves across the map, it's the `Camera` class's job to change what pieces of the map are shown/updated. This is used to give -the appearance that the map is "scrolling" as the player moves. In reality, once the player hits the half way point on the screen (both x direction and y direction), -instead of the player moving forward, the camera actually moves to show more of the map. Once the player reaches an end of the screen, -the camera just stays in place, and in that time the player can move freely until the need to move the camera comes up again. - -## How does camera movement work? - -Think of the camera as a defined rectangle, with x, y, width, and height attributes. The width and height of the camera are set to the -entire size of the screen. - -Below is the entire map image, which is made up of individual tiles. The entire map does NOT fit on the screen all at once. -![entire-map.PNG](../../../assets/images/entire-map.PNG) - - -The camera starts at x and y (0, 0) when the map is first loaded. Upon starting the game, the camera moves -to match where the player's start tile is (the player is the cat). The screen size is around 800x600, so you can think of the camera -as the rectangle on this map image. The rectangle shows that a piece of the entire map is being shown to the player on the screen -at a time: - -![entire-map-with-camera-1.png](../../../assets/images/entire-map-with-camera-1.png) - -As the player moves throughout the map, the camera follows it to show different pieces of the map. - -![entire-map-with-camera-1.png](../../../assets/images/entire-map-with-camera-2.png) - - -## Active Map Resources - -The camera is also responsible for determining which map tiles, enemies, enhanced map tiles (like the floating platform), and npcs -are a part of the current map that is being shown, meaning those items need to be a part of the `update` and `draw` cycle. In order to not -waste computing resources, the camera is constantly checking if a map item is not in the "updatable" area (such as an enemy has gone too far off-screen). -This is important as wasting time updating and drawing items that do not affect the player can affect FPS and cause the game to slow down, -which is never ideal. - -Map items that are to be included in the `update` and `draw` cycle at a given time are considered "active". -The `Map` class exposes three methods for `getActiveEnemies`, `getActiveEnhancedMapTiles` and `getActiveNPCs` that allow other classes -to retrieve this information. These contain a subsection of each map resource that are currently active, and can be used -for things like collision checking to prevent from having to check EVERY resource in the game. Looking at the above images of the camera example on the entire map image, -it's more apparent how only certain enemies that are in the camera's range need to be included in the `update` and `draw` cycles at -any given time. - -The variable `UPDATE_OFF_SCREEN_RANGE` determines the "tile range" that a map resource can be off screen until it is considered inactive. -It is currently set to 4, so if any map resource is more than 5 tiles away off screen, it will be removed from the game cycle until -it comes back into range. Some resources (specifically enemies) have the ability to respawn, meaning they will go back to their -starting location if they were previously inactive and then became active again. - -The `Camera` class's `loadActiveEnemies`, `loadActiveEnhancedMapTiles`, and `loadActiveNPCs` methods are called each game loop cycle (each frame) -to determine which map entities are currently active and which ones are not. Frankly, the code for these methods is an abomination -because I couldn't find an easy way to combine them all, so it's three long-ish separate methods that all do relatively the same exact thing -and contain identical code (just on different entity lists). \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-entities.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-entities.md deleted file mode 100644 index 56acd69..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-entities.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -layout: default -title: Map Entities -nav_order: 5 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/MapEntities ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Map Entities - -## What is a map entity? - -A map entity, represented by the `MapEntity` class in the `Level` package, is predictably any game object that is a part of a map. -This includes [map tiles](./map-tiles-and-tilesets.md) (`MapTile` class), [enemies](./enemies.md) (`Enemy` class), -[enhanced map tiles](./enhanced-map-tiles.md) (`EnhancedMapTiles` class), and [NPCs](./npcs.md) (`NPC` class). - -## What is the purpose of this class? - -The main reason for the `MapEntity` class, which extends from `GameObject`, is that the `MapEntity` class adds a couple of instance variables -made for map entities other than the player -- this includes `isRespawnable` and `isUpdateOffScreen`, which mostly applies to enemies (and can be read about further -on the [enemies](./enemies.md) page), as well as an `initialize` method that will properly "reset" an entity on a map -back to its original position if necessary (such as for respawning an enemy). - -## Initialize Method - -When subclassing the `MapEntity` class, an `initialize` method will be available to be overridden. This method should "setup" the enemy, -as the `Camera` class will call this `initialize` method whenever an enemy becomes "active" on screen. When subclassing -`MapEntity`, it is important to remember to call the super class's `intitialize` method in order to execute its integral logic -for things like respawning. - -## Map Entity Status - -All map entities have an instance variable `mapEntityStatus` which the map's `Camera` uses to determine if the entity is -"active" or not. An active entity means it should be included in the level's `update`/`draw` cycle for a current frame. Entities -that are too far offscreen for example will often be removed from the `Camera's` `update`/`draw` cycle until they are back on screen, -as the game does not want to waste resources on entities that at that current frame have no affect on the level or the player. - -The `MapEntityStatus` enum in the `Level` package defines three different possible statuses: `ACTIVE`, `INACTIVE`, and `REMOVED`. -An entity generally doesn't have to mess with this value as the `Camera` handles the logic for checking active vs inactive entities, -however an entity may set its own status to `REMOVED` to have it permanently removed from the level with no ability to respawn. -The `Fireball` class sets itself to `REMOVED` after a few seconds in order for it to fully disappear from the level. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-tiles-and-tilesets.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-tiles-and-tilesets.md deleted file mode 100644 index c3b2945..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-tiles-and-tilesets.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -layout: default -title: Map Tiles and Tilesets -nav_order: 2 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/MapTilesAndTilesets ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Map Tiles and Tilesets - -## Map Tiles - -In order to understand how the [map file](./map-file.md) is structured/read in, the concept of a "map tile" must be understood first. - -Think of a map as a 2D grid. Each spot on the grid contains what is known as a "tile", which can be thought of as a smaller graphic -that fits right into the grid. A map is made up by joining these tile graphics together to create one large graphic representing the entire map. -For example, take a look at the below screenshot of a piece of a level: - -![tile-map-example-original.png](../../../assets/images/tile-map-example-original.png) - -This image is actually made up of individual tiles, such as the grass tile and the sky tile: - -![grass-tile.png](../../../assets/images/grass-tile.png) -![sky-tile.png](../../../assets/images/sky-tile.png) - -So the image seen above of the grass, tree, flower and sky is actually composed together by smaller individual tiles, which is known as a tile map: - -![tile-map-example.png](../../../assets/images/tile-map-example.png) - -Tiles make it very easy to build up a 2D map. The Map Editor makes this process of using tiles very obvious since the editor -allows tiles to be placed down on a grid in a desired location of the map. Each tile will have a specified index in the map at an x and y -location, e.g. the first tile in the top left corner would have an index of (0,0), the tile to the right of it would be (1,0) and the tile below it -would be (0,1). - -The class `MapTile` in the `Level` package represents a map tile, which is really just a standard sprite with an attribute -for `TileType`, which determines how the tile is treated by the game -- for example, a tile type of `NOT_PASSABLE` means -that the `Player` cannot walk or fall through the tile, which is used on tiles like grass in order to have the player walk on it. -The available tile types are included in the `TileType` enum in the `Level` package. - -## Map Tileset - -A tileset is a collection of map tiles. Easy enough. - -Graphic wise, a tileset defines each tile in one image. Below is the `CommonTileset.png` which the (only) level in the game uses -to construct its map. You will notice that it's literally one image with each map tile defined. Note that each map tile in a tileset -must be the SAME width and height. - -![common-tileset.png](../../../assets/images/common-tileset.png) - -The `Tileset` class in the `Level` package represents a tileset, which contains a collection of `MapTile` object. The way to define -a tileset in this game is to create a class that extends from this `Tileset` class, such as the `CommonTileset` class in the `Tilesets` package. -From there, the extended `Tileset` class (such as `CommonTileset`) must call its super class with the following: -- The tileset image file to be used -- `CommonTileset` uses the `CommonTileset.png` file shown above -- The width and height for every tile in the tileset -- `CommonTileset` specifies that each tile graphic in the above image is 16x16 -- The tileset scale, which is how much each tile in the tileset should be scaled by when drawn to the screen -- `CommonTileset` specifies a scale of 3, meaning each tile will be 48x48 pixels on screen - -Additionally, the `Tileset` class method `defineTiles` must be overridden and have actual defined `MapTiles` added to it. - -### Adding a map tile to a tileset - -The setup for overriding the `defineTiles` method in a `Tileset` subclass looks like this: - -```java -@Override -public ArrayList defineTiles() { - ArrayList mapTiles = new ArrayList<>(); - return mapTiles; -} -``` - -From here, `MapTiles` can be added to the `ArrayList` -- kind of. This method actually defines a type of `MapTileBuilder`. -The general idea is that the class `MapTileBuilder`, which can be found in the `Builders` -package, essentially defines a `MapTile` but does not instantiate it yet. It's not too important to fully understand if you are unfamiliar with -the builder pattern (you can read more about it [here](../game-patterns.md#builder-pattern)), ideally you can use the `CommonTileset` class as a reference to guide you -in adding a new `MapTile` to a tileset. - -The following example in the `defineTiles` method adds the grass tile to the tileset, which is the first graphic -in the top left corner of the above shown `CommonTileset.png` file: - -![grass-tile.png](../../../assets/images/grass-tile.png) - -```java -@Override -public ArrayList defineTiles() { - ArrayList mapTiles = new ArrayList<>(); - - // grass - Frame grassFrame = new FrameBuilder(getSubImage(0, 0), 0) - .withScale(tileScale) - .build(); - - MapTileBuilder grassTile = new MapTileBuilder(grassFrame) - .withTileType(TileType.NOT_PASSABLE); - - mapTiles.add(grassTile); - - return mapTiles; -} -``` - -Whew, that's a bit confusing to look at I know! But the formatting here can be copied for every subsequent tile, -so it can be treated as a template. - -To start, a new `Frame` (details on `Frame` class [here](../game-object.md#animatedsprite-class)) must be created to represent the grass tile's graphic (`grassFrame`. The `FrameBuilder` -class is used to build a `Frame` instance. In the constructor, `getSubImage(0, 0)` takes a piece of the tileset image (`CommonTileset.png`), -in this case the piece of the image at index (0, 0) which is the top left corner. Since `CommonTileset` defines the tile width and tile height -as 16x16, the `getSubImage` method will start at location (0, 0) on the image and then take 16 pixels in both directions and return -the resulting subimage, which is how the individual grass tile graphic gets returned. The next number (which here is also a 0) is the delay, -which only comes into play for [animated tiles](#adding-an-animated-map-tile-to-a-tileset). - -Then, a `MapTileBuilder` class instance must be created to represent the actual `MapTile` (although it won't instantiate the `MapTile` at this time). -Here is where the grass tile (`grassTile`) is given the `TileType` `NOT_PASSABLE`, meaning the player cannot walk or fall through it (it is "solid"). - -Finally, the tile is added to the `mapTiles` list. - -Let's do one for the sky tile now: - -![grass-tile.png](../../../assets/images/sky-tile.png) - -In the `CommmonTileset.png` image shown earlier, the sky tile is located to the left of the grass tile at index (0, 1). -With that slight difference in mind, nearly everything else will be the same for the sky tile as the grass tile: - -```java -@Override -public ArrayList defineTiles() { - ArrayList mapTiles = new ArrayList<>(); - - // grass - Frame grassFrame = new FrameBuilder(getSubImage(0, 0), 0) - .withScale(tileScale) - .build(); - - MapTileBuilder grassTile = new MapTileBuilder(grassFrame) - .withTileType(TileType.NOT_PASSABLE); - - mapTiles.add(grassTile); - - return mapTiles; - - // sky - Frame skyFrame = new FrameBuilder(getSubImage(0, 1), 0) - .withScale(tileScale) - .build(); - - MapTileBuilder skyTile = new MapTileBuilder(skyFrame) - - mapTiles.add(skyTile); - - return mapTiles; - -} -``` - -Unlike with the grass tile, the sky tile is not "solid" and can be passed through by the player, so its `TileType` needs to be -`PASSABLE`. This is the default tile type, so it does not need to be specified, although you could just for clarity place -`.withTileType(TileType.PASSABLE)` there. - -### Adding an animated map tile to a tileset - -Tiles can be animated! Currently, the `CommonTileset` as three animated tiles: the yellow flower, the purple flower, and the shining sun. -This is really easy to do. First, the tileset image file must have a separate image for each frame in the tile's animation. - -For example, below are the frame tiles used for the flower (three different frame images): - -![purple-flower-image-1.png](../../../assets/images/purple-flower-image-1.png) -![purple-flower-image-1.png](../../../assets/images/purple-flower-image-2.png) -![purple-flower-image-1.png](../../../assets/images/purple-flower-image-3.png) - -The end goal is the following animation: - -![purple-flower-animation.gif](../../../assets/images/purple-flower-animation.gif) - -The code for this animated tile looks like this: - -```java -// purple flower -Frame[] purpleFlowerFrames = new Frame[] { - new FrameBuilder(getSubImage(0, 3), 500) - .withScale(tileScale) - .build(), - new FrameBuilder(getSubImage(0, 4), 500) - .withScale(tileScale) - .build(), - new FrameBuilder(getSubImage(0, 3), 500) - .withScale(tileScale) - .build(), - new FrameBuilder(getSubImage(0, 5), 500) - .withScale(tileScale) - .build() -}; - -MapTileBuilder purpleFlowerTile = new MapTileBuilder(purpleFlowerFrames); - -mapTiles.add(purpleFlowerTile); -``` - -Instead of just one `Frame` being created, instead an array of `Frames` are defined. Each `Frame` specifies its -own subimage location, and its delay value (milliseconds) before the animation moves on to the next frame in the animation. -Each frame in this animation has a delay of 500 milliseconds (half a second). This animation defines these four frames in the following order: - -![purple-flower-image-1.png](../../../assets/images/purple-flower-image-1.png) -![purple-flower-image-1.png](../../../assets/images/purple-flower-image-2.png) -![purple-flower-image-1.png](../../../assets/images/purple-flower-image-1.png) -![purple-flower-image-1.png](../../../assets/images/purple-flower-image-3.png) - -After each animation cycle, the animation will loop back to the beginning again (unless the delay is set to -1, in which case -it will never move on from an animation frame without something else explicitly telling it to). - -### Tile Types - -The available tile types are defined in the `TileType` enum, and include: - -- **NOT_PASSABLE** -- player cannot pass through it, they are "solid", such as the grass tiles -- **PASSABLE** -- player can pass through it, such as the sky tiles -- **JUMP_THROUGH_PLATFORM** -- all platformers have these types of platforms, the player can walk on top of it and cannot pass through it -when coming downwards from above, but can pass through it when coming upwards from below; the tree branch tiles are jump through platforms for example -- **LETHAL** -- player dies if they touch a lethal tile diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/npcs.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/npcs.md deleted file mode 100644 index e3ebae8..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/npcs.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -layout: default -title: NPCs -nav_order: 8 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/NPCs ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# NPCs - -## What is an NPC? - -An NPC (**n**on-**p**layer **c**haracter) (represented by the `NPC` class in the `Level` package) is a `MapEntity` subclass. This class adds -a `checkTalkedTo` method that any `NPC` subclass can override, which handles the code that is run if an NPC is talked to by the Player (the player -character has to be within the NPC's range and then press space to talk to it). An NPC can be given its own animation and graphics information, -as well as its own `update` cycle which defines its behavior. - -NPCs in a game tend to act as a neutral character, meaning they usually do not harm the player and are meant to supplement the traversal through a level and also -improve overall game immersion. - -As of now, once an NPC is talked to, they can display a message until their `talkedToTime`, runs out, at which point the message -disappears and they can be re-talked to again. - -## NPC Subclass - -In the `NPCs` package, there is currently only one subclass of the `NPC` class -- `Walrus`. -This `Walrus` NPC can be seen in the game's one level. - -## Adding a new NPC to the game - -This is simple -- create a new class in the `NPCs` package, subclass the `NPC` class, and then just implement -desired logic from there. I recommend copying an existing npc class as a "template" of sorts to help set up and design the npc. - -## Adding an NPC to a map - -In a map subclass's `loadNPCs` method, NPCs can be defined and added to the map's NPC list. For example, in `TestMap`, -a `Walrus` class instance is created added to the NPC list: - -```java -@Override -public ArrayList loadNPCs() { - ArrayList npcs = new ArrayList<>(); - - npcs.add(new Walrus(getPositionByTileIndex(30, 10).subtract(new Point(0, 13)))); - - return npcs; -} -``` - -## NPCs currently in game - -### Walrus - -![walrus.png](../../../assets/images/walrus.png) - -This NPC is defined by the `Walrus` class. In addition being the best made piece of art you have ever laid your eyes on, -the walrus is able to be talked to when the player is in range of its "hurtbox" and presses space. Upon doing so, the `Walrus` class -will draw a box with text inside to the screen: - -![walrus-talking.png](../../../assets/images/walrus-talking.png) - -The code to bring up the "speech box" during the `draw` cycle just creates a rectangle, then a sprite font, and then places -them in the right location. An `NPCs` `drawMessage` method will automatically be called when the `NPC` is talked to. -This is what the overridden `drawMessage` method looks like in the `Walrus` class: - -```java -@Override -public void drawMessage(GraphicsHandler graphicsHandler) { - graphicsHandler.drawFilledRectangleWithBorder(Math.round(getCalibratedXLocation() - 2), Math.round(getCalibratedYLocation() - 24), 40, 25, Color.WHITE, Color.BLACK, 2); - message.setLocation(getCalibratedXLocation() + 2, getCalibratedYLocation() - 8); - message.draw(graphicsHandler); -} -``` - -Lining the rectangle up with the text and the NPC is a total PITA. Those random `+ 2s` and `- 8s` was just me moving the graphics -pixel by pixel until I was happy with the position. - -The image file for the walrus is `Walrus.png`. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/collision-detection.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/collision-detection.md deleted file mode 100644 index 20d64dc..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/collision-detection.md +++ /dev/null @@ -1,191 +0,0 @@ ---- -layout: default -title: Collision Detection -nav_order: 3 -parent: Player -grand_parent: Game Code Details -permalink: /GameCodeDetails/Player/CollisionDetection ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Collision Detection - -## What is collision detection? - -Collision detection is the ability for entities in the game, such as the player, to be able to detect "collisions" with other map entities, -such as map tiles, enemies, etc. This is generally done through checking if two entities "intersect" each other, at which point a collision -has occurred. Games are built around collision detection -- think of any of your favorite video games and tell me if the game would still work -if entities could not detect and react to colliding with other entities, and I guarantee you it cannot. - -The hard part about collision detection is there are A LOT of different "scenarios" where a collision can occur between two entities, -and each entity needs to react accordingly to the collision in their designed way. All map entities in this game engine are rectangles (x, y, width, height), -which thankfully lessens a lot of the complexity that other shapes would introduce. Rectangles are the easiest shape to work with by far due to -"rectangle math" being relatively simple and they works out very nicely in a 2D space where only the x and y axis exist. - -Also, this is always the hardest part about creating a game, and anyone will tell you that writing your own collision detection is the WORST when it comes to platformers -and especially with 3D games -- it is difficult, math is involved, and tiny errors randomly creep up just when you finally think you've gotten everything down pact. -In this game's case, getting proper decimal movement caused me a whole lot of trouble -- and I've even written code like this before for another game! It's just hard stuff, -but I believe this game's collision detection is in a good state right now (it better be after all the time I put into it). - -## Player collision detection with Map Tiles - -**tl;dr**: This is probably the most complicated part of this game. Each frame the player moves by a specified amount unless it collides -with a solid map tile, in which case it is told to stop moving. Use the `moveXHandleCollision` and `moveYHandleCollision` methods for this functionality -instead of the generic `moveX` and `moveY` methods which don't account for collision. - -The main reason for needing extremely precise collision detecting in a platformer game is for the player to be able to traverse the map -without being able to fall through the floor or run through obstacles. - -The `GameObject` class contains two special move methods named `moveXHandleCollisions` and `moveYHandleCollision`. These methods -will move the player by a specified amount, and the `Player` calls these methods each frame to move it by a set amount based on whatever -actions the player took (such as walking forward). Unlike the plain `moveX` and `moveY` methods which simply move a player by a specified amount no questions asked, -the `moveXHandleCollision` and `moveYHandleCollision` methods will stop the player from moving if it collides with a "solid" entity, -such as a `NON_PASSABLE` map tile ("solid"). Each `MapTile` has an assigned [tile type](../MapSubSections/map-tiles-and-tilesets.md#tile-types), and the move handle collisions -methods will take these into account when moving the player to determine if a player is allowed to move to the new location or not. - -Decimal movement makes this a bit more complex. While a player can be at a decimal location in game logic, in draw logic it cannot -since a graphic cannot be physically drawn on half of a monitor pixel. For this reason, while decimal precision is still possible to implement, -it just won't always apply a graphics update if the decimal movement is small enough to not change the round up or down to the nearest whole number. -For example, a decimal amount of `1.49` will round down to `1`, but `1.5` will round up to `2`. - -So, how exactly do the `moveXHandleCollision` and `moveYHandleCollision` methods work? First thing they do is identify the amount of pixels the player is about to move, -the direction to move in, and lastly calculate decimal values to figure out if a potential round up or round down change will occur. From there, -the methods will move the player one pixel at a time over and over again until it reaches the total amount the player is supposed to move by. -During each one pixel move, collisions are checked against the map tiles, and if a collision is detected the movement is stopped and all leftover -movement is thrown away. Lastly, decimal values are recalculated and figured out (if a round up occurs, one more pixel is moved and collision detection -is checked one last time). - -The `Player` calls these two methods from the `GameObject` class each frame in its `update` logic: - -```java -super.moveYHandleCollision(moveAmountY); -super.moveXHandleCollision(moveAmountX); -``` - -The class `MapTileCollisionHandler` is used by the move handle collisions methods to determine if a collision occurred, and if a collision -did occur the player's position is adjusted to be right in front of the solid map tile it collided with. - -The `MapTileCollisionHandler` contains three methods: `getAdjustedPositionAfterCollisionCheckX`, `getAdjustedPositionAfterCollisionCheckY`, -and `hasCollidedWithMapTile`. - -The first two methods `getAdjustedPositionAfterCollisionCheckX` and `getAdjustedPositionAfterCollisionCheckY` do some "rectangle math" to determine which tiles in the map need to be checked for collisions. -A common mistake newer game developers make is writing collision checking code that checks against every single tile in the map every single time -- this is a huge -waste of resources and can actually harm FPS as the game logic step can end up taking too long. Instead, this code will determine the direction the player is moving, -and based on that only check the immediate tile(s) in the vicinity. Just like with sorting algorithms, lowering the amount of comparisons -is key to have a smoothly running game! From there, they will call the third method `hasCollidedWithMapTile` on each map tile that is determined to intersect with the player -and see if that is determined a collision based on the map tile's tile type. - -The `hasCollidedWithMapTile` method is very simple compared to the monstrous other two adjust position methods. It determines -if the tile a player intersects with is considered a "collision" or not. This is 100% based on the tile's tile type. A tile type of `NOT_PASSABLE` -would return true to there being a collision. `PASSABLE` tiles cannot be collided with and would always return false. -`JUMP_THROUGH_PLATFORM` tiles have a bit of a more complex check, because for a collision to occur the player has to be moving downwards and their -bottom hurtbox has to be overlapping the jump through platform's top hurtbox. `LETHAL` tiles kill the player and restart the level. Overall though this method is very simple when looking at it: - -```java -private static boolean hasCollidedWithMapTile(GameObject gameObject, MapTile mapTile, Direction direction) { - switch (mapTile.getTileType()) { - case PASSABLE: - return false; - case NOT_PASSABLE: - return gameObject.intersects(mapTile); - case JUMP_THROUGH_PLATFORM: - return direction == Direction.DOWN && gameObject.intersects(mapTile) && - Math.round(gameObject.getScaledBoundsY2() - 1) == Math.round(mapTile.getScaledBoundsY1()); - case LETHAL: - return gameObject.intersects(mapTile); - default: - return false; - } -} -``` - -More tile types can easily be added to this method to determine if a collision occurred or not. The other two methods don't really have to be -modified at all, new tiles types added to this `hasCollidedWithMapTile` method will work just as the existing three tile types do. - -## Player collision detection with Enhanced Map Tiles - -Additionally, these methods check against all active [enhanced map tiles](../MapSubSections/enhanced-map-tiles.md) in the map, as they -are to be treated like regular map tiles in terms of collision detection (enhanced map tiles have a tile type attribute!). So for example, -the game's green horizontal moving platform would also be checked against during this code. - -Although standard collision detection on enhanced map tiles is done here, `EnhancedMapTile` classes can run their own -`update` logic to do other actions when intersecting with a player. For example, the green horizontal moving platform's class carries out -its own logic upon determining that it overlaps with a player in order to move the player as it moves. - -![player-on-moving-platform.gif](../../../assets/images/player-on-moving-platform.gif) - -## Player collision detection with Enemies - -The `Player` actually does not seek out collision detection with enemies -- instead each `Enemy` class will seek out a collision -detection aganist the player. This allows an `Enemy` to specify to the `Player` class what to do upon being touched. For example, -the enemies in the game (coming from the generic `Enemy` class) call the `Player` class's `hurtPlayer` method upon intersecting with the player, -and the `Player` can then determine how it "hurts" itself (as of now, the player dies and it's a game over). - -## Player collision detection with NPCs - -Just like with enemies, the `NPC` detects when it intersects with a player, which is how it determines the player is in radius and -is able to be talked to. - -## Enemy collision detection with the Map - -Enemies often follow collision detection rules, and are free to use the same `GameObject` methods `moveXHandleCollision` and `moveYHandleCollision` -to abide by tile type rules. - -## Player class reacting to a collision - -The `GameObject` method provides two methods that are intended to be overridden by a subclass: `onEndCollisionCheckX` and `onEndCollisionCheckY`. -After a collision check has been done, the collision methods will let the `Player` class (or any other `GameObject` subclass like the `Enemy` class that moves -while checking for collisions) know if a collision occurred and what direction the collision happend from (left, right, up, or down). - -The `Player` class uses the `onEndCollisionCheckY` method to determine its `airGroundState` -- if a the player collides -with a map tile downwards, it means that the player is landed on the ground and is no longer in the air. This causes -the player to set its `airGroundState` to `GROUND`. Additionally, the reverse is true -- if the player is not colliding -with a map tile downwards, it means the player is in the air, which causes its `airGroundState` to be set to `AIR`. The method also -stops the player from jumping upwards if it collides with a map tile upwards. - -```java -@Override -public void onEndCollisionCheckY(boolean hasCollided, Direction direction) { - if (direction == Direction.DOWN) { - if (hasCollided) { - momentumY = 0; - airGroundState = AirGroundState.GROUND; - } else { - playerState = PlayerState.JUMPING; - airGroundState = AirGroundState.AIR; - } - } else if (direction == Direction.UP) { - if (hasCollided) { - jumpForce = 0; - } - } -} -``` - -The `BugEnemy` class also uses this feature. It uses the `onEndCollisionCheckX` to determine if a collision happend, and if so it turns itself -around and walks the other direction. - -```java -@Override -public void onEndCollisionCheckX(boolean hasCollided, Direction direction) { - if (hasCollided) { - if (direction == Direction.RIGHT) { - facingDirection = Direction.LEFT; - currentAnimationName = "WALK_LEFT"; - } else { - facingDirection = Direction.RIGHT; - currentAnimationName = "WALK_RIGHT"; - } - } -} -``` diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-states.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-states.md deleted file mode 100644 index 787b0bd..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-states.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -layout: default -title: Player States -nav_order: 2 -parent: Player -grand_parent: Game Code Details -permalink: /GameCodeDetails/Player/PlayerStates ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Player States - -The `Player` has several different states it can be in based on the value of its `playerState` instance variable. -Each state can lead to another state based on the player's actions each cycle of the game loop. The states also dictate -which animation/frames the player should switch to (e.g. player walking animation when in the `WALKING` state) -The `PlayerState` enum in the `Level` package define the following states that the player can be in: -- **STANDING** -- player is standing still -- **WALKING** -- player is walking -- **JUMPING** -- player is jumping (or falling) -- **CROUCHING** -- player is crouching -- **ATTACKING** -- player is shooting lasers beams out of his eyes - -## Player Standing State - -![player-standing.PNG](../../../assets/images/player-standing.PNG) - -If the player is on the ground and no keys are pressed, the player will enter its `STANDING` state, where it does nothing -and just waits for another key to be pressed which will activate another state. The `playerStanding` method contains -the simple logic for the `STANDING` state, which looks like this: - -```java -protected void playerStanding() { - currentAnimationName = facingDirection == Direction.RIGHT ? "STAND_RIGHT" : "STAND_LEFT"; - if (Keyboard.isKeyDown(MOVE_LEFT_KEY) || Keyboard.isKeyDown(MOVE_RIGHT_KEY)) { - playerState = PlayerState.WALKING; - } else if (Keyboard.isKeyDown(JUMP_KEY) && !keyLocker.isKeyLocked(JUMP_KEY)) { - keyLocker.lockKey(JUMP_KEY); - playerState = PlayerState.JUMPING; - } else if (Keyboard.isKeyDown(CROUCH_KEY)) { - playerState = PlayerState.CROUCHING; - } -} -``` - -It really just checks for key presses and if so sets the player to a new state. Not much else to do when you're standing still. - -While standing, the player uses either the `STAND_RIGHT` or `STAND_LEFT` animation. - -### Player Walking State - -![player-walking.gif](../../../assets/images/player-walking.gif) - -If the player is on the ground and either the right or left arrow key is pressed, the player will enter its `WALKING` state, -where it will move either left or right in the level. If no keys are pressed while in this state, the player will stop moving and go -back into its `STANDING` state. How fast the player walks is determined by the `walkSpeed` instance variable. The `playerWalking` method -contains the logic for the `WALKING` state. The player will change the direction it's facing based on which arrow key is pressed. - -While walking, the player uses either the `WALK_RIGHT` or `WALK_LEFT` animation. - -### Player Jumping State - -![player-jumping.gif](../../../assets/images/player-jumping.gif) - -This state's logic is a bit more complicated from the others. -If the player is on the ground and presses the up arrow key, the player will enter its `JUMPING` state. The player's `airGroundState` will -be changed to `AIR`, and the player will start rising until gravity catches up, at which point the player will fall downwards until it -hits the ground again, at which point the player's `airGroundState` will be changed back to `GROUND` and the player will transition -into either a standing or walking state. While in the air in the `JUMPING` state, the player can move left and right, but will not change -the direction it is facing. The player is still considered in `JUMPING` state while it is falling downwards, and if the player runs off the edge -of a cliff and ends up in the air, that is also considered `JUMPING` state -- basically if the player is in the air, it is put in -`JUMPING` state. I probably should have made a separate falling state but...meh it's not really needed. - -The player's jumping physics are all determined through instance variable values(`gravity`, `jumpHeight`, `jumpDegrade`, `terminalVelocityY`, and `momentumYIncrease) -which alter how high the player jumps, how fast the player falls, and what the peak of the jump looks like in terms of motion (e.g. the player can go up and then go right down after reaching -the peak of the jump, or the player can reach the peak and wait a bit and slowly begin to pick up falling speed). - -The actual jump algorithm doesn't do all that much. When up is pressed, the user's y velocity is decreased (moving upwards) by the value of `jumpHeight` -each frame. Each frame, `jumpHeight` is lowered slightly by `jumpDegrade`, until eventually no more upwards y velocity is applied to the player. -When this happens, the user starts to fall as gravity takes over. Each frame, gravity is always added to the player (just like in real life, -gravity is always in effect!). Also, each frame the user's momentum downwards is slightly increased by the value of `momentumYIncrease`. This gives off the effect -that the player is falling faster over time, until it reaches its fall speed reaches the value of `terminalVelocityY`, at which point it can no longer increase in fall speed. -Once the player touches the ground, the jump/fall ends. - -While rising upwards (jumping), the player uses either the `JUMP_RIGHT` or `JUMP_LEFT` animation. While falling downwards, -the player uses either the `FALL_RIGHT` or `FALL_LEFT` animation. - -### Player Crouching State - -![player-crouching.gif](../../../assets/images/player-crouching.gif) - -If the player is on the ground and presses the down arrow key, the player will enter its `CROUCHING` state. Basically, the player goes -lower to the ground to shrink its hurtbox, but that's all it does (and the player cannot walk out of `CROUCHING` state, but they can jump out of it). - -### Player Attacking State - -![ezgif.com-gif-maker.gif](../../../assets/images/ezgif.com-gif-maker.gif) - -If the player presses the 'E' key, the cat will enter its `ATTACKING` state and shoots a laser bolt out of its eyes. If the player holds down 'E', the cat shoots a continuous laser beam out of its eyes. Both attacks can be used to kill enemies. When the laser beam touches an enemy, the enemy disappears. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-win-or-lose-level-logic.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-win-or-lose-level-logic.md deleted file mode 100644 index 4ca965b..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-win-or-lose-level-logic.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -layout: default -title: Player Win or Lose Level Logic -nav_order: 4 -parent: Player -grand_parent: Game Code Details -permalink: /GameCodeDetails/Player/PlayerWinOrLoseLevelLogic ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Player Win or Lose Level Logic - -The `Player` class has a `levelState` variable which it uses to determine if it is currently playing the level, completed the level, -or lost the level (died). Upon the `levelState` being set to either the `LEVEL_COMPLETED` or `PLAYER_DEAD` state, -different `update` logic will be run instead of the usual player traversing through the level. - -## Player Win Logic -- Level Completed - -When the player touches a `LevelEndBox` enhanced map tile, it will result in the player's `levelState` being set to `LEVEL_COMPLETED`. -When this happens, the `update` logic changes to use the `updateLevelCompleted` method. This method is what performs the "animation" where after the player hits -the `LevelEndBox`, it will fall to the ground, and then walk to the right until it goes off screen. Once it goes off screen, it will signify -to the `PlayLevelScreen` that the level has been won. - -![level-completed.gif](../../../assets/images/completing-level.gif) - -## Player Lose Logic -- Player Dead - -When the player dies from touching an enemy, it will result in the player's `levelState` being set to `PLAYER_DEAD`. When this hapens, -the `update` logic changes to use the `updatePlayerDead` method. This method is what performs the death animation where the player falls down until they go -off screen, at which point the `PlayLevelScreen` is notified that the player has died. - -![losing-level.gif](../../../assets/images/losing-level.gif) diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/credits-screen.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/credits-screen.md deleted file mode 100644 index 8ea831f..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/credits-screen.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -layout: default -title: Credits Screen -nav_order: 3 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/CreditsScreen ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Credits Screen - -The screen handles the logic and graphics related to the credits screen that is loaded when the "CREDITS" option is selected form the game's main menu. - -![credits-screen.png](../../../assets/images/credits-screen.png) - -The class file for it is `CreditsScreen.java` which can be found in the `Screens` package. - -## Functionality - -This screen is pretty limited, all it does is display the graphics shown above in the screenshot. When the space -button is pressed, `CreditsScreen` will change `ScreenCoordinator's` game state back to MENU to load the -`MenuScreen` back up. - -```java -if (Keyboard.isKeyUp(Key.SPACE)) { - keyLocker.unlockKey(Key.SPACE); -} -if (!keyLocker.isKeyLocked(Key.SPACE) && Keyboard.isKeyDown(Key.SPACE)) { - screenCoordinator.setGameState(GameState.MENU); -} -``` - -Super simple. You will notice that it first checks if the space key is not pressed, -and if so will "unlock" the key (check out the `KeyLocker` class documentation [here](../game-patterns.md#key-locker) for more information on how that works). -Then if the space key is unlocked and pressed, the screen returns back to the menu screen. The reason for this is that the space button is pressed in order to intially go from the menu screen to this credits screen. Because -of how fast the game loop iterates, before the space key is released, it would detect it as multiple presses -- one space press -would go back and forth from menu to credits and back extremely quickly. By "locking" the space key when the `CreditsScreen` is initialized, -and then only unlocking it when the space key is released, it forces the player to release and re-press the space key again rather than -being able to hold it down. The `MenuScreen` also has this same functionality implemented with the space key for this reason. - -## Graphics - -Like the `MenuScreen` class, the `CreditsScreen` class uses a `Map` (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which -is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. - -All of the text shown on screen is created with various `SpriteFont` graphics defined in the class, which are setup in the -`initialize` method. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/instructions-screen.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/instructions-screen.md deleted file mode 100644 index 045e579..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/instructions-screen.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: default -title: Instructions Screen -nav_order: 8 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/InstructionsScreen ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Instructions Screen - -The screen handles the logic related to how to play the game. Many users were confused with the functionality -of keys, so this screen will fix the confusion. Also, if the users click 'X' the instructions screen appears. - -![instructions-screen.PNG](../../../assets/images/instructions-screen.PNG) - -The class file for it is `InstructionsScreen.java` which can be found in the `Screens` package. - -## Functionality - -The instructions screen's only real job is to show the player how to play the game. It displays how the user -can walk, jump, and run throughout the game while avoiding enemies. - -## Graphics - -The background of the screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which -is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/menu-screen.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/menu-screen.md deleted file mode 100644 index 6dd081d..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/menu-screen.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -layout: default -title: Menu Screen -nav_order: 2 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/MenuScreen ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Menu Screen - -NOTE: WHILE SOME FOLLOWING DOCUMENTATION IS VALID, MANY SPECIFICS HAVE BEEN SIMPLIFIED BY FALL 2021 TEAM A2. PLEASE LOOK AT MENU CLASSES AND SCREEN CLASSES FOR COMMENTS EXPLAINING THE INNER WORKINGS OF THE NEW SYSTEM - -The screen handles the logic and graphics related to the menu that is loaded upon the game starting up. - -![main-menu.PNG](../../../assets/images/main-menu.PNG) - -The class file for it is `MenuScreen.java` which can be found in the `Screens` package. - -## Functionality - -The menu screen's only real job is to allow the player to select between its two options "Play Game" and "Credits". -Upon selecting an option, `MenuScreen` will change `ScreenCoordinator's` game state which will force it to load the appropriate screen based -on the option selected. - -```java -// if down key is pressed, add one to current menu item hovered -// "Play Game" option is menu item 0, pressing down will add 1 to the current menu item hovered, -// which changes it to the "Credits" option -if (Keyboard.isKeyDown(Key.DOWN) && keyTimer.isTimeUp()) { - keyTimer.reset(); - currentMenuItemHovered++; -} -// if up key is pressed, subtract one to current menu item hovered -// "Credits" option is menu item 1, pressing up will sbutract 1 to the current menu item hovered, -// which changes it to the "Play Game" option -} else if (Keyboard.isKeyDown(Key.UP) && keyTimer.isTimeUp()) { - keyTimer.reset(); - currentMenuItemHovered--; -} -``` - -The `MenuScreen` class's update cycle mainly checks if the user has pressed the down or up keys and if so will move the little blue square from one -option to the other and make it clear which option is currently being "hovered" over. Pressing the space bar will select the option and is the trigger -that leads to `ScreenCoordinator's` game state being changed. - -```java -// if space is pressed, item is selected -if (!keyLocker.isKeyLocked(Key.SPACE) && Keyboard.isKeyDown(Key.SPACE)) { - menuItemSelected = currentMenuItemHovered; - - // if first menu item is selected "PLAY GAME", set ScreenCoordinator game state to LEVEL - if (menuItemSelected == 0) { - screenCoordinator.setGameState(GameState.LEVEL); - - // if secpmd menu item is selected "CREDITS, set ScreenCoordinator game state to CREDITS - } else if (menuItemSelected == 1) { - screenCoordinator.setGameState(GameState.CREDITS); - } -} -``` - -Additionally, a `Timer` is used (from the `Utils` package) to specify how long in between key presses of the up and down key are allowed. Since key presses -are so sensitive (every 10ms the `update` cycle is run), pressing the down key or up key just one time would move the selection MANY different times, since -the next `update` cycle would come so fast it would still think the key was held down as the player did not have any time to release the key yet. To prevent this, -the `keyTimer` variable is used to force 200ms to go by before either the up or down key can be detected again after it has already been pressed, which feels a lot more -natural for the common key press. The milliseconds for the `keyTimer` to wait is set in the screen's `initialize` method. The above code already showed how the `keyTimer` integrates -with the checks for the up or down keys being pressed, but here is a snippet of it again just to illustrate its functionality: - -```java -// if down key is pressed -// and 200 milliseconds have passed by since the last down key press -if (Keyboard.isKeyDown(Key.DOWN) && keyTimer.isTimeUp()) { - // reset keyTimer to wait out its set wait time, - // which in this class is set to 200ms - keyTimer.reset(); - - // move currentMenuItemHovered to the next option - currentMenuItemHovered++; -} -``` - -## Graphics - -The background of the menu screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which -is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. - -The little blue square moves based on the value of `currentMenuItemHovered` (which changes value when up or down is pressed) to be in front -of the currently hovered item's text. The text that is hovered over will also change color to gold while the one not hovered will change color to black. - -The menu item text ("Play Game" and "Credits") use the `SpriteFont` class and are defined in the `initialize` method (variables `playGame` and `credits`. - -## How to add a new menu item - -Honestly, this class was not made the best; it was neglected because the vast majority of my development time went into the actual platformer game. The menu works fine, -but it is not as modular as it could be. Regardless, adding a new menu item is easy, but it will be a bit tedious to space everything out correctly, add -the necessary logic checks for when that menu item is selected, and getting the little blue square graphic to move to the correct spot on hover. - -^^ This was done by Fall 2021 Team A2 <3 \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/play-level-screen.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/play-level-screen.md deleted file mode 100644 index cc4ae65..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/play-level-screen.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -layout: default -title: Play Level Screen -nav_order: 4 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/PlayLevelScreen ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Play Level Screen - -The screen handles the logic and graphics related to playing the actual platformer game that is loaded when the "PLAY GAME" option is selected form the game's main menu. - -![Play Level Screen](../../../assets/images/game-screen-1.png) - -The class file for it is `PlayLevelScreen.java` which can be found in the `Screens` package. - -## Functionality - -This is the screen where the platformer game is actually played! The map and player are loaded and the game carries out from here until the level -is beaten or the player dies. Despite the `PlayLevelScreen` class having to seemingly do so much, a vast majority of the platformer game code -is abstracted away from it (mostly residing in the `Player` and `Map` classes) which keeps the screen's code pretty simple and easy to follow. - -The `PlayLevelScreenState` enum defined in the class is used to determine what the `PlayLevelScreen` should be doing at a specific point in time -- -its "current state" is stored in the `playLevelScreenState` instance variable. There are several different states that the `PlayLevelScreen` can be in: -- **RUNNING** -- platformer game is currently running (map is loaded, player can move around/jump, etc.) -- **LEVEL_COMPLETED** -- the level has been completed (beaten), which happens when the player hits the golden box at the end of the level -- **PLAYER_DEAD** -- the player has lost the level by being killed by an enemy (which happens if you touch an enemy) -- **LEVEL_WIN_MESSAGE** - after the player has beaten the level, a "level win message" is shown to the player -- this state is used after the LEVEL_COMPLETED state -- **LEVEL_LOSE_MESSAGE** - after the player has lost the level by being killed by an enemy, a "level lose message" is shown to the player -- this state is used after the PLAYER_DEAD state - -### Running State - -The `RUNNING` state is the default state that the `PlayLevelScreen` is set to when it first loads (this is done in the `initialize` method) - -As mentioned earlier, while this state does have the most going on considering it's the actual game itself being run, -all the game code is abstracted away to the `Map` and `Player` classes, meaning this is the only thing `PlayLevelScreen` has to do for this state: - -`update` method: -```java -player.update(); -map.update(player); -``` - -and - -`draw` method: -```java -map.draw(graphicsHandler); -player.draw(graphicsHandler); -``` - -Basically, the `Map` and `Player` classes are updated and drawn each cycle, and they handle the rest of the work. -The specific `Map` and `Player` class instance used for the level is defined in the `initialize` method -- at the moment -this game currently only has one playable map (`TestMap.java` file in the `Map` package) and one player type (`Cat.java` file in the `Players` package). -From there, the `PlayLevelScreen` just continually calls their `update` and `draw` methods to carry out the platformer game. The documentation -for the `Map` class is located [here](../map.md), and for the `Player` class is located [here](../player.md). - -When in this state, the platformer game can be played! - -![game-screen-1.gif](../../../assets/images/playing-level.gif) - -### Level Completed State - -When the player reaches the end of the level and hits the gold block, the level is "completed" and the `PlayLevelScreen's` state -is changed to `LEVEL_COMPLETED`. Note that this state change is actually triggered by the `Player`, which calls the `PlayLevelScreen's` `onLevelCompleted` -method when it has beaten the level in order for `PlayLevelScreen` to know to change states. From there, `PlayLevelScreen` changes state again to -the `LEVEL_WIN_MESSAGE` state and shows the "Level Cleared" screen for a short amount of time before bringing the player back -to the game's main menu. - -![completing-level.gif](../../../assets/images/completing-level.gif) - -The "Level Cleared" screen is a separate `Screen` class (`LevelClearedScreen.java`) whose only job is to -paint the screen black and show the "Level Cleared" text. The `PlayLevelScreen` sets up and loads the `LevelClearedScreen` from within itself, -rather than making a separate entry in the `ScreenCoordinator` class -- this is important in order to not bloat the `ScreenCoordinator` class, as it should -really only be used for the "core" screens of the game. While it may seem to not make much sense to have created an entire separate screen class for `LevelClearedScreen` -for such a tiny amount of functionality, it's important for keeping the game code organized -- if in the future the graphics for the level cleared -screen were to get more complex and involved, it's best to keep it separate and out of the `PlayLevelScreen` class to make the graphic drawing easier to work with and -prevent `PlayLevelScreen` from becoming bloated. - -### Player Dead State - -When the player dies in the level from touching an enemy, the level is "lost" and the `PlayLevelScreen's` state is changed to `PLAYER_DEAD`. -Note that this state change is actually triggered by the `Player`, which calls the `PlayLevelScreen's` `onDeath` -method when it has died in a level in order for `PlayLevelScreen` to know to change states. From there, `PlayLevelScreen` changes state again to -the `LEVEL_LOSE_MESSAGE` state and shows the "You lose!" screen. The "You lose" screen allows the player to press the space key to restart the level -or the escape key to go back to the main menu. - -![losing-level.gif](../../../assets/images/losing-level.gif) - -The "You lose!" screen is a separate `Screen` class (`LevelLoseScreen.java`) which paints the screen black and shows the "You lose!" text, -as well as the text with instructions for what the player can do from this screen (press the space key to restart the level, or press -the escape key to go back to the main menu). The `LevelLoseScreen` class handles detecting those key inputs and sets `ScreenCoordinator's` game state -accordingly based on what the user presses -- which is essentially just this: - -```java -if (Keyboard.isKeyDown(Key.SPACE)) { - playLevelScreenOld.resetLevel(); -} else if (Keyboard.isKeyDown(Key.ESC)) { - playLevelScreenOld.goBackToMenu(); -} -``` - -The `PlayLevelScreen` class instance is passed into the `LevelLoseScreen` class as well, and as you can see above the -`PlayLevelScreen` exposes methods for `resetLevel` and `goBackToMenu` to make things easier. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/screen-coordinator.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/screen-coordinator.md deleted file mode 100644 index 066544e..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/screen-coordinator.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -layout: default -title: Screen Coordinator -nav_order: 1 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/ScreenCoordinator ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Screen Coordinator - -## What is the Screen Coordinator? - -The `ScreenCoordinator` class in the `Game` package is integral for bringing the entirety of the game's code together. -Its job is to dictate which section (screen) of the game to load at a given time. This class is attached to the engine's game loop -in the `Game.java` file, and from there it coordinates what happens and when it happens. Each screen contains code for updating game logic -and drawing graphics for a piece of the game. - -## What Screens can the Screen Coordinator currently load? - -Currently, there are three main screens that the `ScreenCoordinator` class can load: the menu screen, the credits screen, -and the play level screen. The menu screen is always loaded at the start of the game (which you see when the game first starts up): - -![menu-screen.png](../../../assets/images/menu-screen.png) - -When the "Play Game" option on the menu screen is selected, the `ScreenCoordinator` class responds by loading the play level screen (which starts the platformer game level): - -![game-screen-1.png](../../../assets/images/game-screen-1.png) - -And finally, when the "Credits" option on the menu screen is selected, the `ScreenCoordinator` class responds by predictably loading the credits screen: - -![credits-screen.png](../../../assets/images/credits-screen.png) - -The `ScreenCoordinator` can support as many screens as necessary -- there is no limit! - -## Game State - -The `ScreenCoordinator` class has an instance variable for keeping track of the current game state. This `gameState` variable -can be of any type defined in the `GameState` enum, which can be found in the `GameState.java` file located in the `Game` package. - -The current states defined in the `GameState` enum are `MENU`, `LEVEL`, `CREDITS`, which all coincide with a specific screen. -Based on the value of the `gameState` instnace variable, the `ScreenCoordinator` will choose to load its corresponding screen. How -this is done can be seen in the below snippet of `ScreenCoordinator's` `update` method: - -```java -switch(gameState) { - case MENU: - currentScreen = new MenuScreen(this); - break; - case LEVEL: - currentScreen = new PlayLevelScreen(this); - break; - case CREDITS: - currentScreen = new CreditsScreen(this); - break; -} -``` - -The `ScreenCoordinator` will only change screens when it detects that its `gameState` has been changed. The class exposes -a method `setGameState` which any other class can use to change the current game state and force `ScreenCoordinator` to load a different -screen. As you can see in the above code snippet, `ScreenCoordinator` is passing an instance of itself into each screen instance (e.g. `MenuScreen`). -This allows those screen classes to set the game state of `ScreenCoordinator` when necessary. An example of where this is used is when the `MenuScreen` is loaded -and the player selects the "Play Game" option -- this causes the `MenuScreen` to set `ScreenCoordinator's` game state to `LEVEL`, which triggers it to load -the `PlayLevelScreen` class (as shown in the above `switch` statement). - - - diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/game-object.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/game-object.md deleted file mode 100644 index eb39a63..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/game-object.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -layout: default -title: Game Object -nav_order: 2 -parent: Game Code Details -has_children: true -permalink: /GameCodeDetails/GameObject ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Game Object - -## What is a Game Object? - -Inspired by the [Unity](https://unity.com/) game engine, the class `GameObject` (along with the entire package `GameObject`) was -created as a sort of "one stop shop" for creating game entities. The `GameObject` package contains a series of classes that all work -together which ultimately build up to the `GameObject` class containing all necessary information and functionality required for a map entity, -which makes it quick and easy to use without requiring the code logic used to be rewritten again and again. - -In this game, the `GameObject` class is subclassed by every map entity, which includes the [player](./player.md), -and any subclasses of `MapEntity` which include [enemies](./MapSubSections/enemies.md), [npcs](./MapSubSections/npcs.md), and [map tiles](/GameDetails/Map/MapTilesAndTilesets) (as well as -[enhanced map tiles](./MapSubSections/enhanced-map-tiles.md)). That means that all of these subclasses (and their subclasses) include -all functionality of the `GameObject` class under the hood. - -## Features of the GameObject class - -The `GameObject` class provides the following features for every subclass: - -1. Sprite logic (loading in images, scaling images, flipping images, defining a bounding box which can be thought of as a "hurtbox") -2. Animation support and logic (and the ability to switch between different animations) -3. Graphical support -- will automatically handle drawing graphics in correct location based on map/camera position -4. Collision detection with other GameObjects, including collision detection with map set pieces (like map tiles) - -## Subclassing GameObject - -The `GameObject` class can be subclassed by any entity regardless of what it is or what it will be doing in game, -and it contains many constructors to support a variety of needs. Generally, each `GameObject` at the very least contains -an image (sprite) that is to be attached to a rectangle (x, y, width, height) to be displayed and moved around in game. - -## GameObject Package Overview - -As mentioned earlier, the classes in the `GameObject` package work together and build up to the `GameObject` class. - -### Rectangle class - -The `Rectangle` class is the "base", which is a means to implement the (x, y, width, height) instance variables which every -`GameObject` needs (it also contains some simple "rectangle" math such as detecting collision and moving a rectangle in different directions). - -### Sprite class - -Then, the `Sprite` class extends the `Rectangle` class. The `Sprite` class adds functionality on for storing an image (one image). -it also optionally allows defining a bounding box `bounds` which is a `Rectangle` used in collision detection -- often times when working with collision detection, -a sprite doesn't want its entire image to be able to be "touched". An example of this can be seen in the following Megaman sprites, -where the bounding box where collisions can be detected is much smaller than Megaman itself: - -![megaman-bounds.png](../../assets/images/megaman-bounds.png) - -When working with 2D games, it's common to leave off limbs like in the above picture and have the core body be able to be -detected for collision. - -### AnimatedSprite class - -Then, the most complex class of the group pops up -- `AnimatedSprite`. This does NOT extend `Sprite`. Instead, it contains a `HashMap` -which maps a string value (animation name) to an array of `Frame` type (animation data and graphics). - -An array of `Frames` is what defines an animation. Each `Frame` in the array is one frame of an animation, -and the game will cycle through that animation continuously (and upon reaching the last frame will go back to the first). -The `Frame` class extends `Sprite` with an added `delay` instance variable which specifies how long an animation frame is to -last before switching over to the next frame. `Frames` in the same animation can have different `delay` values. - -This class provides functionality for any `GameObject` to define their own animations as desired, give the animations names, -play out the `GameObject's` current animation during the game, and allow for the current animation to be changed at any time. Any subclass -of `GameObject` that overrides `getAnimations` (which is where animations are defined) utilize the `AnimatedSprite` class's functionality. - -Something important to note about this class is that even though it doesn't directly extend `Sprite`, it is structured to be treated -in game exactly as a `Sprite` would. At any given time, the `AnimatedSprite` class will have a current animation with a current frame, -and this current frame is a `Sprite` with a location, collision potential, etc. While an `AnimatedSprite` is a "collection" of various -`Sprites` at the end of the day, it does only have one "active" `Sprite` out at a time. As a result, the `AnimatedSprite` class -redefines nearly every method from the `Sprite` and `Rectangle` classes to have them utilize the `currentFrame` variable's information. -As a result, an `AnimatedSprite` has all the behavior of the `Sprite` class without needing to extend it. I know this sounds a bit silly, -but I originally did have it extend from the `Sprite` class and after some time I found that the drawbacks for doing that greatly -outweigh the minor inconvenience of having to redefine some methods. - -### GameObject class - -And finally, the `GameObject` class extends from the `AnimatedSprite` class. The `GameObject` is to be given an instance -of the current `Map` being used through its `setMap` method after creation in order for it to provide functionality based on the `Map`, -including move methods that handle map tile collisions (`moveXHandleCollision` and `moveYHandleCollision`) and special `draw` logic to convert "map coordinates" to "screen coordinates". -The `GameObject` class also does some sneaky constructor crafting in order to allow it to be instantiated both as an animated sprite (nearly every entity uses this such as the `Player` and enemies like `BugEnemy`) -or just as one sole sprite (which is done in the `HorizontalMovingPlatform` class). - -More details on `GameObject's` collision detection and handling can be found [here](./PlayerSubSections/collision-detection.md). - -Finally, `GameObject` adds two methods `getCalibratedXLocation` and `getCalibratedYLocation` which are used in the `draw` cycle -to properly draw the `GameObject` to the screen in the correct location with account to how much the map's camera has moved. `GameObjects` should -utilize these methods in their `draw` methods to ensure other graphics are drawn relative to them and still maintain proper draw location -integrity. The `Walrus` class does this when drawing its speech bubble when talked to, which ensures as the camera moves the speech bubble -graphics will remain in the correct spot relative to the walrus game object's location. - -Note that if no `Map` class instance is passed in a `GameObject` class using the `setMap` method, the `GameObject` will draw itself -at its exact location on the screen rather than accounting for the map camera moving. This can be desired behavior for certain graphics, -while not for others. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/map.md b/docs/GameCodeDetails/GameCodeDetailsSubSections/map.md deleted file mode 100644 index db0766b..0000000 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/map.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: default -title: Map -nav_order: 5 -parent: Game Code Details -has_children: true -permalink: /GameCodeDetails/Map ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Map - -## What does the Map class do? - -The `Map` class (found in the `Level` package) is responsible for everything on the screen during the platformer game except for the player character, which includes the map tile graphics, -the camera (current view -- which part of the map is being shown on the screen), the enemies, and the npcs. It has a lot of duties to carry out, -but a vast majority of the `Map` class is just "setup" -- once everything is ready to go, the actual game logic is pretty simple (it pretty much just calls `update` on every class and -lets them decide what they want to do). - -Everything you see in the below gif except for the player character (the cat) is handled/coordinated by the `Map` class. - -![game-screen-1.gif](../../assets/images/playing-level.gif) diff --git a/docs/GameEngine/GameEngineSubSections/config.md b/docs/GameEngine/GameEngineSubSections/config.md deleted file mode 100644 index 30e0756..0000000 --- a/docs/GameEngine/GameEngineSubSections/config.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -layout: default -title: Config -parent: Game Engine -nav_order: 6 -permalink: /GameEngine/Config ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Config - -## What is a config? - -Most applications have a concept of a "config file", which is just a configuration file that is separated out from program logic. -Configuration files generally contain global constant variable values that an application references as needed, and treats them as "read only" -(they should not be changed while the application is running). The values chosen to be included in a configuration file differ depending on the application, -but by having one it makes it possible to tweak/change values without having to go digging through class files, and also makes it very easy -to test out how value changes affect an application. Often times applications are used by multiple people on various platforms -- having -certain values strategically separated out like this can make a program a lot more flexible to work for other people's use cases. You can -literally think of these variable values as "configuration options", just like hitting the "settings" button on any other application and personalizing -your experience while using it. - -## Where is this game's config file located? - -The `Config.java` file in the `Engine` package contains several configuration variables that can be easily changed while -the application is not running in order to modify a "global" aspect of it. The following variable values are defined in the `Config` class: - -- **FPS** -- how many game loop cycles per second are run (FPS = **F**rames **P**er **S**econd) -- **RESOURCES_PATH** -- root folder path to where all game assets will be stored (image files, etc) -- **MAP_FILES_PATH** -- root folder path to where all map files will be stored -- **GAME_WINDOW_WIDTH** -- width of the game's JFrame window -- **GAME_WINDOW_HEIGHT** -- height of the game's JFrame window -- **TRANSPARENT_COLOR** -- default transparent color the `ImageLoader` class will use when loading an image into the game - -## How do you access a `Config` class variable from other classes? - -Any class can access a `Config` class variable since all variables are defined as `public` `static`. - -```java -public void update() { - System.out.println("The game window width is: " + Config.GAME_WINDOW_WIDTH); - System.out.println("The game window height is: " + Config.GAME_WINDOW_HEIGHT); -} -``` - -## How do you decide if a variable should be added to the Config class? - -It's honestly up to the programmer which values they want to have exposed separately as a "configuration" option. The only -real "rule" is that it must be a global read-only variable, meaning the value of the variable CANNOT be modified while the game is running, -and every class must be able to access it. I pretty much only use it for "game engine setup" values. \ No newline at end of file diff --git a/docs/GameEngine/GameEngineSubSections/game-loop.md b/docs/GameEngine/GameEngineSubSections/game-loop.md deleted file mode 100644 index 07c5d13..0000000 --- a/docs/GameEngine/GameEngineSubSections/game-loop.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -layout: default -title: Game Loop -parent: Game Engine -nav_order: 1 -permalink: /GameEngine/GameLoop ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Game Loop - -## What is a game loop? - -A game loop is the overall flow control for a game. Generally, it's an infinite loop that exists while the game application is -running, and will continually do the same actions over and over again. Each game loop iteration is known as a frame. The number -of game loop iterations that are completed per second is how frames per second (FPS) is calculated. - -## How does this engine's game loop work? - -The game loop is created using the `Timer` class from Java Swing. The timer is set up to continually "tick" after a predefined number -of milliseconds have elapsed. While it's not perfect, it does the job well and for the most part maintains its tick cycle to match -the desired FPS. To adjust the FPS of the game, the `Config` class has a variable named `FPS` which can be changed as desired. It currently -is set to 100 as I found that the timer lags a bit when set to a standard 60 FPS, but YMMV based on the computer being used. - -Each "tick" of the game loop (each time the loop iterates), it does two things: updates game logic and then updates the graphics -that are rendered to the screen based on the updated game logic. That's it. Here is a very detailed diagram illustrating the game loop: - -![game-loop-diagram.png](../../assets/images/game-loop-diagram.png) - -All jokes aside, at the end of the day that is all the game loop is doing -- continually cycling between `update` and `draw` calls. - -## What code belongs in the update cycle? - -Game logic is any code that has to do with the game being carried out, which includes anything from detecting key presses, moving something to a new location, -collision detection, etc. All game logic belongs in the `update` section of the game loop. Each iteration of the game loop will -force the game to re-run its game logic, which is essential to making the game run continually. If you write game logic that -moves a player one pixel to the right when the right arrow key is held down, the `update` cycle will continually check for the -right arrow key being pressed and if so move the player to the right one pixel each time. Without the game loop, there'd be no way -to apply this logic continually. - -Anywhere in the codebase you see an `update` method, and you will see it in nearly every class, it is where game logic is being run -by the game loop while an instance of that class is active. - -## What code belongs in the draw cycle? -Any code that is explicitly rendering ("painting") a graphic (usually an image) to the JPanel belongs in the `draw` section. -Each iteration of the game loop will force the game to "repaint" itself, meaning all graphics on the screen are updated (even if they haven't moved -since the previous cycle). This means that any changes to the "state" of the graphics (such as if a new image needs to be rendered or -an existing image has moved) are immediately updated on screen. - -Anywhere in the codebase you see a `draw` method, and you will see it in nearly every class, it is where graphics are being -told where to be displayed on screen while an instance of that class is active. - -You can think of this step in the cycle as the "output" to the player of the game. Technically a game can work without -ever displaying any graphics, since all the game logic still plays out each cycle and the computer itself doesn't actually need -graphics on screen to do its job -- just like how a console app can technically run a program just fine without ever printing -anything to the console. It is a game developer's job to determine what output to show to the user as the game progresses, however -the draw cycle should NOT be relied on to actually perform any critical game logic. Code in the draw cycle should NEVER -be mutating (changing the value of) any class instance variable. Consider it in "read-only" mode. - -### Lag - -The `draw` section of the game loop should only contain code to draw graphics to the screen and should NOT -include any kind of game logic, such as moving graphics to a new location. Not only is this best practice as mentioned above -(treat graphics as the "output" of the application), but it can actually break your game or cause unexplainable bugs otherwise. -This is because of the way the Java JPanel's graphics painting is set up. While the game can call to the JPanel (which this game engine uses as a buffer to display graphics) -and tell it to update its graphics (also known as a "repaint"), this merely "schedules" the repaint instead of carrying it out right away. The JPanel -can then carry out the repainting whenever it damn well pleases outside of your program's control, and there's a possibility that it doesn't carry out the repaint at all. -When draw calls are skipped like this, it is known as lag, because the game is updating its game logic but not representing those logic changes -with updated graphics on the screen. Lag is unavoidable at the end of the day, but it's imperative to not place game logic updating in the `draw` section of the game loop -because having logic "skipped" or ran "out of order" can completely break a game, while the concept of "lag" will at least maintain game logic integrity -to keep the game working. This issue can entirely be avoided if instance variables (from any class) are NEVER mutated during -the `draw` cycle. - -## Example of game loop running - -Say I have the following class for an image: - -```java -import javax.imageio.ImageIO; -import java.io.File; - -public class CatImage { - private BufferedImage image = ImageIO.read(new File("pusheen.png")); - private int xLocation = 0; - private int yLocation = 0; - - public int getXLocation() { - return xLocation; - } - - public int getYLocation() { - return yLocation; - } - - // this would be called during the update cycle - public void update() { - xLocation += 1; - } - - // this would be called during the draw cycle - public void draw(Graphics2D g) { - g.drawImage(image, xLocation, yLocation, null); - } -} -``` - -During the game loop's `update` cycle, this class's `update` method will move its position 1 pixel to the right (adding 1 to `xLocation`). -During the game loop's `draw` cycle, the image will be drawn to the screen at the position defined by its `xLocation` and `yLocation` variables. -As the game loop iterates and continuously calls the `update` and `draw` methods over and over again, the `update` cycle will continually move the image's position to the right, -and the `draw` will continually "repaint" the image to the screen at its updated location. - -Click the button below for a live simulation of how this example `CatImage` class would behave during the game loop cycle: - - - -
- pusheen.png -
- - \ No newline at end of file diff --git a/docs/GameEngine/GameEngineSubSections/gui-components.md b/docs/GameEngine/GameEngineSubSections/gui-components.md deleted file mode 100644 index 6c2f9d0..0000000 --- a/docs/GameEngine/GameEngineSubSections/gui-components.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -layout: default -title: GUI Components -parent: Game Engine -nav_order: 2 -permalink: /GameEngine/GUI Components ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# GUI Components - -## Java Swing Library - -This game uses components from Java's Swing library for its GUI (graphic user interface). All that's really used -is a JFrame for the application window and a JPanel inside the JFrame which is where graphics are "painted" on to. - -## Game Window - -The `GameWindow` class in the `Engine` package extends the Java Swing `JFrame` class and just sets up the application window -as needed. Otherwise, it's pretty uneventful. This JFrame does hold the all-important `GamePanel` class, but otherwise -it's only job is just to exist. - -Essentially, this is all the `GameWindow` brings up on its own: - -![jframe.png](../../assets/images/jframe.png) - -## Game Panel - -The `GamePanel` class in the `Engine` package extends the Java Swing `JPanel` class and is responsible for rendering the game's graphics. -Additionally (although in retrospect this wasn't a great design decision), the `GamePanel` class also sets up various other -essential game resources like the `graphicsHandler` and the Java Swing `Timer` that runs the game loop. - -The `GamePanel` class is also home to the universal pause function (which is another poor design decision to include in this class). Pressing the `P` -key at any point while the game is running will immediately stop the game loop's `update` cycle but will continue the game's `draw` cycle, -which essentially "pauses" the game. It will also show the [sprite font](../../GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md) text "PAUSE" in the middle of the screen while the game is paused. -The `pauseLabel` variable is what defines that "PAUSE" text, and the `update` method and `draw` method contain the pause logic. - -While the `GamePanel` appears to be doing a lot, at the end of the day it's just a `JPanel` that's doing far more work -than it should be doing. Classic case of a bloated class. - -Every `draw` cycle, the `JPanel's` `repaint` method is called, which schedules a re-painting of all graphics to the `JPanel`. As mentioned in -the [game loop](./game-loop.md) documents, this act of "scheduling" a repaint does not immediately execute the re-painting -- the `JPanel` will -carry it out when it's ready to -- this call to `repaint` is asynchronous, meaning Java will carry out the task for painting in a separate thread -from the one currently running the rest of the application. This is not something you have to worry aabout as long as you follow what is said -in the *game-loop* documentation and do not include any game logic during the game loop's `draw` cycle. diff --git a/docs/GameEngine/GameEngineSubSections/loading-images.md b/docs/GameEngine/GameEngineSubSections/loading-images.md deleted file mode 100644 index 94af04e..0000000 --- a/docs/GameEngine/GameEngineSubSections/loading-images.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -layout: default -title: Loading Images -parent: Game Engine -nav_order: 4 -permalink: /GameEngine/LoadingImages ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Loading Images - -## Reading images into the game - -The engine's `ImageLoader` class has methods that will read an image into the game into Java's `BufferedImage` data type. - -The Image Loader provides two methods: -- **load** -- reads an image into the game -- **loadSubImage** -- reads a piece of an image into the game - -Pass the method an image file and it will read it in, no questions asked. - -## Resources Directory - -The `ImageLoader` class will look for image files relative to the directory defined as the `RESOURCES_PATH`, -which is set in the `Config` class. By default, it is set to a `Resources/` directory at the root of the project. -If an image is placed in that `Resources/` directory named "cat.png", the file name can be supplied directly -to the `ImageLoader` methods. - -```java -BufferedImage catImage = ImageLoader.load("cat.png"); -``` - -It is fine to add subfolders to the `Resource` directory, just be sure to include that in the path to the image. -For example, if there was a folder named `CatPics` inside the `Resources` folder, the path would look like this: - -```java -BufferedImage catImage = ImageLoader.load("CatPics/cat.png"); -``` - -### Changing resources directory - -You are free to change the value of the `RESOURCE_PATH` variable in the `Config` class to change the location -of the folder that the `ImageLoader` will look for images in. It is not advisable to remove this restriction as a game -generally needs to guarantee that all of its assets are where they should be in order to run properly, and keeping them in one -base location limits room for error. - -## Image Transparency - -The "transparent" color of an image is a particular color in an image that is desired to be "invisible", meaning it will not be included -when the image is loaded into the game. Every sprite in the base game uses the color magenta (RGB 255 0 255) as its transparent color, -as that color is ugly and very rarely, if ever, used by any image. This magenta color is set as the default transparent color by -the `TRANSPARENT_COLOR` variable in the `Config` class. - -For example, the following cat image used in game has its background set as magenta: - -![cat-sprite.png](../../assets/images/cat-sprite.png) - -When loading this image into the game, the transparent color of magenta is set, meaning in game, the magenta color will not be shown, -allowing it to blend in with whatever color is shown in the background of it: - -![game-screen-3.png](../../assets/images/game-screen-3.png) - -Before you ask "why can't you just use a tool like photoshop to add a transparency to an image", the answer is that it is not advisable to do this -for games because it bloats the file size (leading to longer load times), takes additional time, and does not always work out as expected (especially with Java's limited image loading capabilities). - -If you have an image file that requires a different transparent color, the `ImageLoader` class has alternate methods -to allow for a transparent color to be specified instead of just using the default one. - -```java -// sets transparent color to blue -BufferedImage catImage = ImageLoader.load("cat.png", Color.blue); -``` - -### Changing default transparent color - -A new color can be set as the default transparent color by changing the `TRANSPARENT_COLOR` variable in the `Config` class. \ No newline at end of file diff --git a/docs/GameEngine/game-engine.md b/docs/GameEngine/game-engine.md deleted file mode 100644 index 9b563ba..0000000 --- a/docs/GameEngine/game-engine.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: default -title: Game Engine -has_children: true -nav_order: 4 -permalink: /GameEngine ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Game Engine - -## What is the difference between a game and a game engine? - -The difference between a particular game and a game engine (also called a game framework) is that "game logic" is specific to one game while a game engine is created -to be reused. The game engine is designed to create an environment from which a game can be made. Game engines typically have built in -functionality for loading resources (such as images), detecting user input (such as checking if a key on the keyboard is pressed), -and running the game through a cycled game loop that continually updates game logic and graphics rendered to the screen. - -Most game developers do not write their own game engine, and instead use an existing one that has the features they require to -create their desired game, such as [Unity](https://unity.com/) or [Unreal](https://www.unrealengine.com/en-US/). Some game companies -have even written their own game engines which they reuse for many different games and continually improve upon it. - -For this game, the engine was created from scratch using components from the Java Swing library (really just the JPanel) and everything else was -done using vanilla Java. While the engine itself is not "specific" to the platformer game genre, several other components were included -that fit nicely with platformer game logic. - -## Overview of this game engine - -The code architecture and design for this engine was inspired by [XNA](https://en.wikipedia.org/wiki/Microsoft_XNA), Microsoft’s old game programming framework that they supported for -anyone to use to write games for the Xbox 360. Now it has been ported and released open source under the title [Monogame](https://www.monogame.net/) -since Microsoft discontinued support for it. - -Below are some features of the game engine, which are defined and created in the `Engine` package. - -{% comment %} - The Jekyll theme used [just-the-docs](https://pmarsceill.github.io/just-the-docs/) will auto populate this file with a table of contents -{% endcomment %} diff --git a/docs/HowToUseThisSite/how-to-use-this-site.md b/docs/HowToUseThisSite/how-to-use-this-site.md deleted file mode 100644 index f2d6116..0000000 --- a/docs/HowToUseThisSite/how-to-use-this-site.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -layout: default -title: How To Use This Site -nav_order: 2 -permalink: /HowToUseThisSite -search_exclude: true ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# How To Use This Site - -This site contains documentation for various facets of this game created for Quinnipiac's SER225 class, mainly going over -how the game is made and how the code works. Games tend to be more complicated than the average application due to how much is going on, -the constant cycling of the game loop that has to always be looking out for user input, and performance mattering due a slow -game directly affecting the player. For that reason, it is important to understand (at least at a high-level) the game engine and game logic -code in order to successfully develop this game further. - -Click a link from the sidebar on the left of this site to be taken to its respective webpage. - -The [Game Engine](../GameEngine/game-engine.md) section contains documents that detail the game engine code (how the game loop cycle works, etc.). - -The [Game Code Details](../GameCodeDetails/game-code-details.md) section contains documents that detail the game logic code (map, player, enemies, etc). - -Other pages should be self-explanatory. - -The search bar at the top of this site is your best friend. Without it, you will waste a lot of time combing through pages of text -trying to find what you're looking for. CTRL+F for keywords is another necessity for finding what you're looking for. If you are looking -at code and do not understand something, try typing it into the search of this website and see what comes up. Looking through subsections on the sidebar -can also help you find the correct pages you are looking for if in need of adding a feature where you have a general idea of how to do it but -aren't sure of which class contains the logic that needs to be added to/changed. Please do not attempt to read through all the pages -in this site all at once -- this site is designed to be used as a "look up" reference similar to situations when you need to consult google to find an answer. - -## How was this site made? - -GitHub has a platform called [GitHub Pages](https://pages.github.com/) that gives every GitHub user one free domain, -and each repository is given one free project site route. GitHub Pages will then, for free, host a website specified in that repo. -In the `docs` folder of this project is where GitHub Pages is set to look to in order to build and host the website code. - -While normally websites are written using HTML, GitHub pages uses a web framework called [Jekyll](https://jekyllrb.com/) that -transforms plain text into HTML which can then be rendered in a web browser. -In particular, Jekyll will translate [markdown](https://www.markdownguide.org/basic-syntax/) formatted text files into HTML, -which is AWESOME because writing in markdown is MUCH easier than dealing with HTML tags. There is a bit of a learning curve to use Jekyll -properly (something I didn't know how to use before attempting to make this site), -but once I got it all figured out, things went smoothly and the website came out pretty good in my opinion. I used a Jekyll theme called [Just the Docs](https://pmarsceill.github.io/just-the-docs/) for this website, which gave me access to various features -and already implemented code that made it a lot easier to build up the site. - -You are also allowed to embed HTML right into the markdown files, which I did once in a while if I needed do to an animation or something, -but otherwise there wasn't much of a need to do so as this is a static site and doesn't need much logic to fulfill its purpose. - -## Changing website information - -Each page of the website has an associated `.md` (markdown) file inside the `docs` folder. It's just a matter of finding the correct one -for the page to edit and then actually changing the text to update the site. Only changes to the `master` branch will be applied to the website. - -If you forked this repo and want GitHub to create a new web page for your version of the game (that will not conflict with this -existing website), you need to go to your GitHub repo's Settings, go to the -GitHub Pages section, and set the source to point to the `docs` folder instead of `none`. That setting should also tell you the new -url that will lead to your project's site, which will be separate from mine. - -![github-pages-source.png](../assets/images/github-pages-source.png) - -If you are creating a new web page, I recommend copying an existing page's first several lines and edit them to make sure -it fits properly in the site (the theme will automatically add properly added pages to the sidebar). \ No newline at end of file diff --git a/docs/MapEditor/map-editor.md b/docs/MapEditor/map-editor.md deleted file mode 100644 index 1f6b093..0000000 --- a/docs/MapEditor/map-editor.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -layout: default -title: Map Editor -nav_order: 6 -permalink: /MapEditor ---- - -# Navigation Structure -{: .no_toc } - -## Table of contents -{: .no_toc .text-delta } - -1. TOC -{:toc} - ---- - -# Map Editor - -## What is a map editor? - -In this project, there is another "sub" project called the Map Editor (all located in the `MapEditor` package) that will load up -a GUI for designing game [tile maps](/GameDetails/Map/MapTilesAndTilesets) with a visual display instead of having to do so through -code and not being able to see the map that is being built. Nearly every video game uses a map editor (level editor, level maker, etc.) -that game developers use to design the game's maps. Games like [Mario Maker](https://www.youtube.com/watch?v=tZ5g5n-6OFg) are literally -just one big map editor (a REALLY good one). - -## This game's map editor - -The inspiration behind the design of the map editor for this game engine came from the old CD-ROM game [Speedy Eggbert](https://en.wikipedia.org/wiki/Speedy_Eggbert), -which was a staple of my childhood. It was a standard platformer game (with some VERY odd design choices), but the level editor it included with it -was the same one used by the game developers to actually make the game -- it was so fun as a child to be able to make levels -for a game I really liked with ease. - -While this game's map editor is limited compared to other commercial map editors for other video games you may have seen/used -(it is only for placing map tiles -- things like enemies must be created and added through code), it is still very useful for designing -a map and being able to see what you are doing as you go. - -To run this game's map editor, the `main` method in the `MapEditor` class must be run. Remember this is a separate program -from the actual game code, although both make use of the same classes in this project. You can have both the map editor and the game -running at the same time. - -## Map Editor features - -This Map Editor allows you to do the following: - -- Add a new Map to the game -- Change the individual tiles of a map (which maintain their tile logic defined in code) -- Change the dimensions of a map (how many tiles the map contains width and height wise) - -![map-editor.png](../assets/images/map-editor.PNG) - -## How to use the map editor - -It should be pretty self-explanatory how to use the map editor after playing around with it for 5 minutes. - -On the left-hand sidebar are the tiles that can be used for the map. Since each `Map` subclass (such as `TestMap`) must define -its own `Tileset` (such as `CommonTileset`) (if this doesn't make sense to you, you can read more about the `Map` class [here](/GameDetails/Map) and -about map tiles and tilesets [here](/GameDetails/Map/MapTilesAndTilesets)). From here, any tile on the left-hand sidebar can be clicked to "select" it -(you will know it has been selected when the tile is highlighted with a yellow box). Afterwards, going to the map and clicking will -replace whichever tile was hovered by the mouse with the selected tile. Below is a gif of this process. - -![map-editor-usage.gif](../assets/images/map-editor-usage.gif) - -The map being edited can be changed using the dropdown on the top left. To save any changes to a map, hit the "Save Map" -button. The "Set Map Dimensions" button will allow you to change the size of the map (number of tiles width and height). It'll also -allow you to choose which side of the map to apply changes to when growing/shrinking a map's dimensions -- for example, if increasing -the width of the map, more tile space can be added either to the right side or the left of the map. - -Something to keep in mind is that in a `Tileset's` `defineTiles` method, after a map has been created using that `Tileset`, -you should not rearrange the order the tiles are defined and added to the list. This will break existing maps. You should ALWAYS -just append tiles on even though it may feel disorganized. - -## Adding a new map to the map editor - -Adding a new map to the map editor (and essentially creating a new map entirely) is a relatively easy process. - -First start by creating a new class in the `Maps` package that extends the `Map` class. You can look at `TestMap` as a reference for how to do this. -Then for now, all that's needed is a constructor to define the map file name, tileset, and player starting position. An example is below: - -```java -public class MyMap extends Map { - public MyMap() { - super("my_map.txt", new CommonTileset(), new Point(1, 11)); - } -} -``` - -In the above example, the map's file will be named "my_map.txt" (which has not been created yet -- the Map Editor will create it for you once its added), -the tilset will use the `CommonTileset` (which can be found in the `Tileset` package), and the player start tile is set to tile index (1, 11). Be sure -that two maps do not share the same map file name, or one will overwrite the other! - -Now, in the `MapEditor` package, go to the `EditorMaps.java` file. In here, you will need to add your new map class to these methods. - -In `getMapNames`, just add an entry to the list for the name you would want the map to be recognized by -- for example, for this map -I could use "MyMap": - -{% raw %} -```java -public static ArrayList getMapNames() { - return new ArrayList() {{ - add("TestMap"); - add("TitleScreen"); - add("MyMap"); - }}; -} -``` -{% endraw %} - -Then, in the `getMapByName` method, add another switch case for your new map name and return an instance of your new Map class. -For this example, I would add a switch case for "MyMap" (that is the name it was given in the `getMapNames` method) and then -I would return `new MyMap()`: - -```java -public static Map getMapByName(String mapName) { - switch(mapName) { - case "TestMap": - return new TestMap(); - case "TitleScreen": - return new TitleScreenMap(); - case "MyMap": - return new MyMap(); - default: - throw new RuntimeException("Unrecognized map name"); - } -} -``` - -The last thing you have to do is open the Map Editor and in the drop down select your new map. The Map Editor will create a new -blank map file for it. From there, you can change the map dimensions as desired (it will start at a width and height of (0,0), so -you have to change them to actually add tiles) and start designing the map! - -Map files can be found in the project's `MapFiles` folder. This folder location can be changed in the [game config](/GameEngine/Config) if desired. - -## How does the map editor work? - -I used Java Swing components for the GUI and control functionality (such as buttons, drop down menus, etc.). -The problem with creating complex GUIs purely through code is that A LOT of code and logic is required and it is very easy for it -to get unruly, as many shortcuts/hacky solutions have to be used to have all components take in the correct input and communicate with one-another. -As you can imagine, there is quite a bit of code for the map editor broken up between several different classes, and they aren't -the easiest code files to digest, and as a working unit it isn't immediately obvious which classes work with others -- that will happen -when using any GUI library, and especially Java Swing due to how old it is. - -I will say that this was NOT easy to code up and get working. It's also a bit fragile as all map editors are, -since it has major dependencies on game logic classes that it has no control over such as the `Map`, `MapTile`, and `Tileset` classes, -and if any of those class change, it can easily break the map editor. Such is life. - -While I do not plan on going over the entirety of the map editor code here because it would take me forever to type up and at the end of the day -I would just be re-describing the entire Java Swing Library, I will instead leave it be and give some generic guidance if you are looking -to develop it further. - -The `EditorWindow` class is the JFrame. It sets the `EditorMainPanel` as its content pane. - -The `EditorMainPanel` is a JFrame that holds two other JFrame components: the `EditorControlPanel` (map name selection drop down menu and -save map button are there, etc.) and the `MapBuilder`, which is the right side where the map is displayed -and tiles in it can be replaced. - -The `EditorControlPanel` utilizes the `TilePicker`, which is ANOTHER JPanel that actually display the tile graphics that can be selected and is where -that selection logic takes place. The `SelectedTileIndexHolder` class is used to store the currently selected tile. - -The `MapBuilder` JPanel defines the scroll pane and map info labels (like "width" and "height" at the bottom of the map display). -Then inside the scroll pane, it places the `TileBuilder` JPanel. The `TileBuilder` class is where tiles can be replaced -by the selected tile from the `TilePicker` based on the value in `SelectedTileIndexHolder`. - -Both `TilePicker` and `TileBuilder` have listeners for mouse click and hover inputs. - -The `ChangeMapSizeWindow` class is specifically for the window that pops up after clicking the "Change Map Size" button -in the `EditorControlPanel`. This class lets you change the size of the map. - -The "Save Map" button will overwrite a map's assigned map file with what it looks like in the editor. - -I apologize deeply that there are no comments for the map editor classes, I will go back sometime and fix that \ No newline at end of file diff --git a/docs_archive/Advice/advice.md b/docs_archive/Advice/advice.md new file mode 100644 index 0000000..8dd741c --- /dev/null +++ b/docs_archive/Advice/advice.md @@ -0,0 +1,57 @@ +--- +layout: default title: Advice nav_order: 9 permalink: /Advice search_exclude: true +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Advice + +This page contains some advice and tips from myself for developers looking to expand upon this game in a creative direction of their choice. + +## How to start working on this game + +In the workplace, a majority of the time you will find yourself being put to work on an existing project where you don't understand what it is doing, how the code works, or sometimes what the goal of the project even is. It is normal to feel overwhelmed when this happens. I used to get really anxious during times like these (and sometimes still do), but truthfully there is nothing to be anxious about. You can only do as much as you can, and coding is really hard, so my advice for you to start working on this game is to just take things slow. + +Start by playing around with the code and changing minor things. Running the game and seeing your changes in action will help you identify and confirm where different pieces of code logic is located. Maybe head over to the `Cat` class and change its `jumpHeight` instance variable, or play around in the map editor. Just get a feel for the project and what you're going to be working with. Also, don't be afraid to consult your peers, google, or this site for answers to your questions. + +For your first couple tasks you should pick up something on the small side. No reason to dive head first into things before you understand how anything works. Whenever I am placed on a new project at work, I always look at the list of upcoming tasks and take one or two easier ones (usually bug fixes) that will give me a chance to get a feel for how the project is laid out. Once the initial "training" tasks are completed, take on a bigger one if you feel comfortable. Sometimes tasks that you think will take an hour will take multiple days; that's just the way programming is. :man_shrugging: :woman_shrugging: + +Understanding how the game loop's `update` and `draw` cycles work is the key to success! + +## Copy and paste and the art of a template + +If you are creating something, such as a new enemy, you (almost) always have a starting point: existing code! +Using something else as a template is the key to working within an already defined logic flow. Most of the code written for this game is "setting up" resources, and there's no need to reinvent the wheel if you don't have to. Go ahead and find an existing class that has similar functionality to the thing you want to create, copy and paste the entire thing, and then start modifying values/adding code as you see fit. You also have a perfectly working "clone" of the old class at first, so it will be set up and able to be tested in game right away. + +While developing this program, I continually copy and pasted my own code to get things setup correctly. There's no need to memorize things like that when the answers are all available to you -- use that saved brain space for memorizing more song lyrics! + +## Identify classes you will need to work with for a specific task + +There are tons of classes in this game (most game codebases are pretty large and complex), but you will find that for each task you will rarely come into contact with more than two or three of them at a time. Identifying which classes you don't have to touch for a specific task lets you really narrow down your focus to a much smaller unit of code. + +Once you have decided on a task to work on (whether it be adding a new feature, updating an existing feature, or fixing a bug), take a few minutes to identify where in the project (which files/methods) will need to be modified/worked with in order to successfully complete the task. While this may take a longer time at first, having a plan will make the task feel less daunting, give you a place to at least start from, and also makes it easier for others to help you if you have some sort of "logical reasoning" that you can explain to them for how you got to the point where you ended up stuck. Once you get more comfortable with the codebase, this pre-planning step will take far less time to do, to the point where you'll probably just dive right into the code and ask questions later. + +## Creating Game Art + +As a developer that has never attempted to make my own game art before creating this game, I have to say it was a lot of fun (even though it's not very good). I recommend that everyone give a shot at creating some game art, even if it's just editing the colors on existing sprites. While the logic that goes into running the game is extremely important for obvious reasons, the visuals of a game are also very important as they are the "output" to the user of the program, and it never hurts to get involved a bit with the other side of things. + +With that said, the [spriters resource](https://www.spriters-resource.com/) is an amazing website full of graphics from any game on any console that you can think of. It also has a companion site [sounds resource](https://www.sounds-resource.com/) for looking up music/sound files +(something that this game desperately needs). + +## Have fun + +Making a game is fun, so have fun! Be creative. There's no "right" or "wrong" way for you to expand upon this game, just choose a direction that you will enjoy working on. I remember when I took this class back when I was a student at Quinnipiac, my team added Professors Hoffman, Blake, and Duncan as enemies into the game because it was hilarious, and we ended up having a lot of fun just making each other laugh while improving our overall coding skills. + +These were the professor graphics in case you were wondering (I miraculously found them on my old google drive account). + +![judgement-professor-graphics.png](../assets/images/judgement-professor-graphics.png) \ No newline at end of file diff --git a/docs_archive/BugReport/bug-report.md b/docs_archive/BugReport/bug-report.md new file mode 100644 index 0000000..791bb76 --- /dev/null +++ b/docs_archive/BugReport/bug-report.md @@ -0,0 +1,35 @@ +--- +layout: default title: Bug Report nav_order: 7 permalink: /BugReport search_exclude: true +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Bug Report + +![bug-enemy.gif](../assets/images/bug-enemy.gif) + +Below is a list of known bugs and oddities in the game code. There are definitely more bugs than what is listed here...they just haven't been discovered yet. + +## Options Menu cannot change aspect ratio + +During this Project Our team created an options menu with the hopes to adjust the volume and the Aspect Ratio or the size of the game. After attempting to implement the aspect ratio adjuster into the game we soon realized that to change the size of the game window we would also have to change the size of every object in the game. Because of this the aspect ration option was left unfinished. Our team sees this as a bug or an unfinished improvment and would love to see it finished by another team. + +## Narrative Screen only lasts 15 seconds + +On the main menu there is a option for narrative this will take you to a narrative screen but only for 15 seconds onece the timer runs out it will return you to the main menu. To fix this bug we recommend making the narrative screen and actual screen object and placing a back button on it so the player can say on this screen as long as they want. + +## White color rgb(255,255,255) is transparent + +I am not quite sure why this is, but if an image uses the color white with the rgb (255,255,255), the color will be transparent when loaded in. I think it has to do with the way the `transformColorToTransparency` method works (which I lifted from a StackOverflow answer) that I use in order to set a transparent color in an image when loading it in (the default transparent color is magenta rgb (255,0,255)). Maybe there's a better transparency method out there that doesn't have this adverse effect on the color white? + +The good news is this is super easy to work around -- just use a white color with a different rgb in your images, such as (255,255,254) (which will look no different in game than (255,255,255) I promise). Off the top of my head, the `Walrus.png` image uses this technique for its tusks because pure white wouldn't show, which is how I found out about this bug. diff --git a/docs_archive/EnhancementIdeas/enhancement-ideas.md b/docs_archive/EnhancementIdeas/enhancement-ideas.md new file mode 100644 index 0000000..887011e --- /dev/null +++ b/docs_archive/EnhancementIdeas/enhancement-ideas.md @@ -0,0 +1,47 @@ +--- +layout: default title: Enhancement Ideas nav_order: 8 permalink: /EnhancementIdeas search_exclude: true +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Enhancement Ideas + +Below is a list of potential enhancement ideas that could be made to this game. + +## More levels + +Right now this game only contains five levels. We think that having a game have up to 20+ levels would add lots of dimension to the game. Users enjoy working through challenging levels. + +## Increased ways to kill enemies + +We implemented a way to kill the enemies but many games feature the ability to kill AI by jumping on their head. We think this would be a great addition to our game. + +## Lives/Health + +Name a single platformer game where you get a game over after getting touched by an enemy one time...yeah none of them do this because it's not very fun. Most games have a concept of "lives" where you can try a level again before a game over occurs, and they are booted back to the main menu. Some games implore the idea of health, where the player can be hurt by enemies multiple times in a level before dying. + +## More ways to die + +Enemies work great to hinder the player's progression through a level, but most games have other means of doing so like falling in a bottomless pit, landing on spikes, etc. Would definitely spice things up a bit. We included dying by jumping into the water but using lava or other barriers would be interesting to see. + +## Game music/sounds + +We added sound to the game but having sound effects for jumping, walking, swimming, or dying would add a lot to this simple game. + +## Slopes + +Just putting this here to warn anyone that is interested in implementing slope map tile types into the game that it is HARD and I don't recommend doing it without a solid understanding of how the game's map tile system works. There are so many ways to implement them with their own different pros and cons, and even games known for their slopes like 2D Sonic the Hedgehog games have some bugs (you just don't notice them because in those games you move very fast). + +## Adjusting aspect ratio + +During Scrum Sprint 4, we struggled to complete the task of changing aspect ratio in the options screen. Many expert gamers love this ability and adding this to the game would be very time consuming. We thought this would be a great additon as well. diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/creating-a-simple-game-object.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/creating-a-simple-game-object.md new file mode 100644 index 0000000..8b4508f --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/creating-a-simple-game-object.md @@ -0,0 +1,42 @@ +--- +layout: default title: Creating a Simple Game Object nav_order: 1 parent: Game Object grand_parent: Game Code Details permalink: /GameCodeDetails/CreatingSimpleGameObject +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Creating a Simple Game Object + +A `GameObject` class can be used for just a single image (one graphic). Even though `GameObject` extends from +`AnimatedSprite`, if no animation is desired for a specific game object, it provides certain constructors to essentially treat itself as a one frame animation that never changes (which results in one image being used). + +For example, the following `GameObject` constructor can be used to define just a simple one image sprite: + +```java +public GameObject(BufferedImage image,float x,float y){ + // logic + } +``` + +And can be instantiated like this: + +```java +GameObject(ImageLoader.load("my_image.png"),30,100); +``` + +More details on using the `ImageLoader` class to read in images [here](../../../GameEngine/GameEngineSubSections/loading-images.md). An image file can contain any graphic, and it will be loaded into the game and utilized by the `GameObject` in its `draw` cycle to "display" itself on screen. + +From there, calling its `draw` method will display it to the screen at its x and y location. + +## Setting the Map instance in a Game Object + +In the above example, the `GameObject` will always be drawn at its x and y position relative to the screen's coordinates. Many classes however, such as the `Player` and `Enemy` classes, need to have their drawing logic changed based on where the map's camera has moved (this creates that "scrolling" level effect). To add the `Map` instance to the `GameObject` for it to automatically apply that draw logic, the `setMap` method can be used. \ No newline at end of file diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/game-object-with-animations.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/game-object-with-animations.md new file mode 100644 index 0000000..d67177c --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/GameObjectSubSections/game-object-with-animations.md @@ -0,0 +1,172 @@ +--- +layout: default title: Game Object With Animations nav_order: 2 parent: Game Object grand_parent: Game Code Details permalink: /GameCodeDetails/GameObjectWithAnimations +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Game Object With Animations + +Every `GameObject` subclass automatically has support for animations. An animation is an array of `Frame` class data. Different graphics for each frame should come from a sprite sheet image file. Generally, if you are creating a `GameObject` +with animations, a new class that extends of `GameObject` should be created to separate out the animation data. + +## Sprite Sheet + +The `SpriteSheet` class is used to define a sprite sheet that the `AnimatedSprite` class uses for loading animation graphics. A sprite sheet is one image file that contains multiple graphics (frames images) for a sprite. For example, below is the sprite sheet image file for the cat player character (the image file is `Cat.png`: + +![cat-spritesheet.png](../../../assets/images/cat-spritesheet.png) +![blueCat.png](../../../assets/images/blueCat.png) +![greenCat.png](../../../assets/images/greenCat.png) + +Each sprite image on the sprite sheet must be the same size, and each image must have one pixel in between it and another image that would come next to or below it (to keep track of separate images, I drew black squares around each image, which also adds the one pixel buffer between each image). For the cat sprite sheet above, each sprite is 24x24 pixels. Animations for each sprite are shown in a horizontal line, so the cat walking animation for example is four different frames. This is not a requirement, images on a sprite sheet can be placed anywhere, but I find it easier to manage animations by organizing them like this. + +From here, a `SpriteSheet` class instance can be created, which will be passed into the `GameObject` class to be used to setup animations. Here is an example of creating a `SpriteSheet` instance that the `Cat` class uses with the above cat sprite sheet image: + +```java +new SpriteSheet(ImageLoader.load("Cat.png"),24,24) +``` + +The 24x24 is the size of each sprite in the sheet (height and width). + +## Defining animations in a `GameObject` subclass + +Any class that extends `GameObject` can override `GameObject's` `getAnimations` method, which will load defined animations into the class upon initialization. The setup to override this method looks like this: + +{% raw %} + +```java +@Override public HashMapgetAnimations(SpriteSheet spriteSheet){ + return new HashMap(){{ + + }}; + } +``` + +{% endraw %} + +The `AnimatedSprite` class keeps track of animations with a `HashMap` that maps the name of an animation (String) to an array of `Frame` data (which can be thought of as an array of sprites with their own information like graphics, bounding collision rectangle, etc.). In this method, animations can easily be added directly to this `HashMap` through the use of the earlier defined `SpriteSheet`. + +Since every image in a sprite sheet must be the same size, the `SpriteSheet` class is able to provide a `getSprite` method that will grab a particular graphic from the sheet based on row and column index. For example, in the above cat sprite sheet image, sprite index (0, 0) would be the standing cat in the top left corner. Sprite indexes (1, 0), (1, 1), (1, 2), and (1, 3) would equate to each of the cat walking images (the cat walking sprites are in row 1 of the sprite sheet, and there are then four columns with one image in each. + +![cat-spritesheet-with-indexes.png](../../../assets/images/cat-spritesheet-with-indexes.png) + +### One frame animation + +Using this `getSprite` method from the `SpriteSheet` class, it makes defining each animation easy. Below is an example from the `Cat` class which defines a one frame animation of the cat standing still. There are two separate versions of this animation defined: a stand right animation, and a stand left animation (which will flip the image horizontally -- no need to do it manually when it can be done through code): + +{% raw %} + +```java +@Override public HashMapgetAnimations(SpriteSheet spriteSheet){ + return new HashMap(){{ + + // adds STAND_RIGHT animation + put("STAND_RIGHT",new Frame[]{ + new FrameBuilder(spriteSheet.getSprite(0,0),0) + .withScale(3) + .withBounds(8,9,8,9) + .build() + }); + + // adds STAND_LEFT animation + put("STAND_LEFT",new Frame[]{ + new FrameBuilder(spriteSheet.getSprite(0,0),0) + .withScale(3) + .withImageEffect(ImageEffect.FLIP_HORIZONTAL) + .withBounds(8,9,8,9) + .build() + }); + }}; + } +``` + +{% endraw %} + +This uses the [builder pattern](../game-patterns.md#builder-pattern) with the `FrameBuilder` class to build a `Frame` object instance. The animations are put into the `HashMap` by first specifying a String for the animation's name (e.g. "STAND_RIGHT" and "STAND_LEFT"). + +Notice that both standing animations specify the sprite sheet image index of (0, 0) (cat at top left corner standing still), a delay value of 0 (since this animation is one frame there is no need to specify a delay value), and then several attributes ar eset such as `scale`, `imageEffect`, and `bounds`. These are all optional parameters and have default values if not specifically set (for example, if no `withImageEffect` is specified, the image by default will not have any image effects like flipping horizontally applied to it). + +### Animations with multiple frames + +While one frame animations are fine at times, most animations have multiple frames, such as in the above cat sprite sheet where there are four frames for the cat walking. The same method for adding one frame animations also works for multiple frame animations, just instead of just one `Frame` in the array, multiple `Frames` will be included. + +Below is an example from the `Cat` class which defines the "WALK_RIGHT" animation, which has four frames image indexes (1,0), (1,1), (1,2), and (1,3): + +{% raw %} + +```java +@Override public HashMapgetAnimations(SpriteSheet spriteSheet){ + return new HashMap(){{ + + // add WALK_RIGHT animation + put("WALK_RIGHT",new Frame[]{ + new FrameBuilder(spriteSheet.getSprite(1,0),200) + .withScale(3) + .withBounds(8,9,8,9) + .build(), + new FrameBuilder(spriteSheet.getSprite(1,1),200) + .withScale(3) + .withBounds(8,9,8,9) + .build(), + new FrameBuilder(spriteSheet.getSprite(1,2),200) + .withScale(3) + .withBounds(8,9,8,9) + .build(), + new FrameBuilder(spriteSheet.getSprite(1,3),200) + .withScale(3) + .withBounds(8,9,8,9) + .build() + }); + + }}; + } +``` + +{% endraw %} + +That's it! A `Frame` array is used, and multiple `FrameBuilders` are then used to build each `Frame`. Notice the `delay` +for each `Frame` is set to 200 -- this means that if "WALK_RIGHT" is the current animation, every 200 milliseconds the game will change the current frame to the next frame in the array. Once the last frame is reached and completed, it will wrap back around to the first. The result in game would look like this: + +![cat-walking.gif](../../../assets/images/cat-walking-right.gif) + +Any number of `Frames` can be added to an animation, and any number of animations can be added to a `GameObject`. + +Also, putting a delay value of `-1` will prevent a frame from transitioning to the next frame, so this can be useful to place in the last frame of an animation to prevent the animation from wrapping around. + +## Changing a Game Object's current animation while the game is running + +It is common to want to change a `GameObject's` current animation (and as a result current frame) that is shown while the game is running. For example, with the player character `Cat`, the animation should change from "STAND_RIGHT" to "WALK_RIGHT" when the right key is pressed. The `AnimatedSprite` class provides some instance variables that can be used to manipulate animation/frame data: + +- **currentAnimationName** -- the current animation that is being used, changing this to a new animation name will switch to that new animation +- **currentFrameIndex** -- the current frame index of an animation +- **hasAnimationLooped** -- will be true if an animation has looped at least one time (gone from the last frame index back to the first) + +For example, in the `Player` class's `update` logic for when the player is stnading and the right key is pressed, it will change the `currentAnimationName` to "WALK_RIGHT". + +```java +public void update(){ + + // if right key is pressed, set animation name to WALK_RIGHT and move game object to the right + if(Keyboard.isKeyDown(Key.RIGHT)){ + currentAnimationName="WALK_RIGHT"; + moveRight(1); + + // if right key is not pressed, set animation name to STAND_RIGHT + }else{ + currentAnimationName="STAND_RIGHT"; + } + + // this super call is important as otherwise the AnimatedSprite class can't run its logic to + // change animations/update frames + super.update(); + } +``` diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enemies.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enemies.md new file mode 100644 index 0000000..39fa992 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enemies.md @@ -0,0 +1,114 @@ +--- +layout: default title: Enemies nav_order: 6 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/Enemies +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Enemies + +## What is an enemy? + +An enemy (represented by the `Enemy` class in the `Level` package) is a `MapEntity` subclass. Enemies in a platformer game serve to hinder the player from completing a level. For example, in Mario games, enemies like goombas and koopas can kill Mario if Mario intersects with them in a certain way. + +The `Enemy` class adds a `touchedPlayer` method that any `Enemy` subclass can override. An enemy can be given its own animation and graphics information, as well as its own `update` cycle which defines its behavior. There really isn't any limit to what an enemy can be made to do, it's just up to the implementer's coding skills! + +As of right now, the `Enemy` classes base `touchedPlayer` method will hurt the player (which in turn kills the player) if the player touches the enemy. If this behavior is not desired, you can remove that line of code, or override the `touchedPlayer` method in an enemy class and include logic to determine if the enemy should be hurt or not. + +Most enemies adhere to similar collision detection to the player, and can follow those collision rules using the `GameObject` class's collision methods just like the player does. More details on collision detection and handling can be found [here](../PlayerSubSections/collision-detection.md). + +## Enemy Subclass + +In the `Enemies` package, there are currently three subclasses of the `Enemy` class -- `BugEnemy`, `DinosaurEnemy`, and `Fireball`. Each one of these classes defines an enemy in the game, which can be seen in the game's one level. + +Enemies can also set a few attributes such as: + +- **isRespawnable** -- if the enemy respawns when it becomes inactive and then active again or not; if set to false, the enemy will be "left in place" next time it becomes active +- **isUpdateOffScreen** -- if the enemy should be updated even when it would not be technically considered "active" by the camera + +## Adding a new enemy to the game + +This is simple -- create a new class in the `Enemies` package, subclass the `Enemy` class, and then just implement desired logic from there. I recommend copying an existing enemy class as a "template" of sorts to help set up and design the enemy. + +## Adding an enemy to a map + +In a map subclass's `loadEnemies` method, enemies can be defined and added to the map's enemy list. For example, in `TestMap`, a `BugEnemy` and `DinosaurEnemy` are created and added to the enemy list: + +```java +@Override public ArrayList loadEnemies(){ + ArrayList enemies=new ArrayList<>(); + enemies.add(new BugEnemy(getPositionByTileIndex(15,9),Direction.LEFT)); + enemies.add(new DinosaurEnemy(getPositionByTileIndex(19,1).addY(2),getPositionByTileIndex(22,1).addY(2),Direction.RIGHT)); + return enemies; + } +``` + +## Enemies currently in game + +### Bug Enemy + +![bug-enemy.gif](../../../assets/images/bug-enemy.gif) + +This enemy is defined by the `BugEnemy` class. I tried to replicate a typical goomba's movement patterns from Mario. Essentially, the bug enemy will continually walk forward. If it hits a wall, it will turn around. If it walks off the edge of a cliff, it will fall down until it touches the ground again before it starts walking forward again. + +The image file for the bug enemy is `BugEnemy.png`. + +### Dinosaur Enemy + +![dinosaur-enemy-walk.gif](../../../assets/images/dinosaur-enemy-walk.gif) + +This enemy is defnied by the `DinosaurEnemy` class. It will walk back and forth between two specific points. Every few seconds, it will stop, turn red, and shoot out a fireball. + +![dino-enemy-shoot.png](../../../assets/images/dino-enemy-shoot.png) + +![fireball.png](../../../assets/images/fireball.png) + +This enemy is good to reference to see how to have an enemy create another enemy (the dinosaur enemy in this case creates a fireball enemy and adds it to the map): + +```java +// determine fireball starting x location (relative to dinosaur enemy's current location), speed and direction +// based on the direction the dinosaur is facing, the fireball's direction is chosen (either right or left) +int fireballX; + float movementSpeed; + if(facingDirection==Direction.RIGHT){ + fireballX=Math.round(getX())+getScaledWidth(); + movementSpeed=1.5f; + }else{ + fireballX=Math.round(getX()); + movementSpeed=-1.5f; + } + +// determine fireball starting y location (relative to dinosaur enemy's current location)) + int fireballY=Math.round(getY())+4; + +// create fireball enemy + Fireball fireball=new Fireball(new Point(fireballX,fireballY),movementSpeed,1000,map); + +// add the fireball enemy to the map's enemy list + map.addEnemy(fireball); +``` + +The image file for the dinosaur enemy is `DinosaurEnemy.png`. + +### Fireball + +![fireball.png](../../../assets/images/fireball.png) + +As mentioned in the previous [dinosaur enemy](#dinosaur-enemy) section, the dinosaur enemy will shoot out a fireball every so often. The fireball will travel straight for a few seconds before disappearing (it will also disappear if it hits a solid tile). Notice that this enemy sets itself to `REMOVED` when it should disappear -- a map entity with a `REMOVED` status will be permanently removed from the map enemies list and will not ever respawn. + +```java +if(existenceTimer.isTimeUp()){ + this.mapEntityStatus=MapEntityStatus.REMOVED; + } +``` + +The image file for the fireball is `Fireball.png`. diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enhanced-map-tiles.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enhanced-map-tiles.md new file mode 100644 index 0000000..d783c05 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/enhanced-map-tiles.md @@ -0,0 +1,93 @@ +--- +layout: default title: Enhanced Map Tiles nav_order: 7 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/EnhancedMapTiles +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Enhanced Map Tiles + +## What is an enhanced map tile? + +An enhanced map tile (represented by the `EnhancedMapTile` class in the `Level` package) is a `MapEntity` subclass. The idea behind this class is that it acts just like a `MapTile` does in every way, but with the ability to define its own `update` and `draw` logic instead of just going with the `MapTile's` default logic. This allows a map tile to essentially do whatever it wants while still being counted as a map tile, meaning the `Player` will still consider it during its collision checks based on the `EnhancedMapTile's` tile type. Like every `MapEntity` subclass, an `EnhancedMapTile` during its `update` cycle will be given a reference to the `Player` instance, so it is able to interact with the player directly. + +And yes, I know the name "enhanced map tile" is dumb, I couldn't think of a better name to describe these and now I'm over it. + +## Enhanced Map Tile Subclass + +In the `EnhancedMapTiles` package, there are currently two subclasses of the `EnhancedMapTile` class -- `HorizontalMovingPlatform` and `EndLevelBox`. Each one of these classes defines an enhanced map tile in the game, which can be seen in the game's one level. + +## Adding a new enhanced map tile to the game + +This is simple -- create a new class in the `EnhancedMapTiles` package, subclass the `EnhancedMapTile` class, and then just implement desired logic from there. I recommend copying an existing enhanced map tile class as a "template" of sorts to help set up and design the enhanced map tile. + +## Adding an enhanced map tile to a map + +In a map subclass's `loadEnhancedMapTiles` method, enhanced map tiles can be defined and added to the map's enhanced map tile list. For example, in `TestMap`, a `HorizontalMovingPlatform` and `EndLevelBox` are created and added to the enhanced map tile list: + +```java +@Override public ArrayList loadEnhancedMapTiles(){ + ArrayList enhancedMapTiles=new ArrayList<>(); + + enhancedMapTiles.add(new HorizontalMovingPlatform( + ImageLoader.load("GreenPlatform.png"), + getPositionByTileIndex(24,6), + getPositionByTileIndex(27,6), + TileType.JUMP_THROUGH_PLATFORM, + 3, + new Rectangle(0,6,16,4), + Direction.RIGHT + )); + + enhancedMapTiles.add(new EndLevelBox( + getPositionByTileIndex(32,7), + )); + + return enhancedMapTiles; + } +``` + +The horizontal moving platform has a pretty beefy constructor, but don't let that overwhelm you, it's all just information needed to set it up correctly. + +## Enhanced map tiles currently in game + +### Horizontal Moving Platform + +![horizontal-moving-platform.png](../../../assets/images/horizontal-moving-platform.png) + +This enhanced map tile is defined by the `HorizontalMovingPlatform` class. It is what it sounds like -- a platform that moves horizontally back and forth. The constructor for this class definitely has too much going on, but the goal was to create a platform a more generic class where any image could be used as the platform. The level currently uses the above simple green platform image. + +For this class, a start and stop position are passed in, and the platform will just continually go back and forth. The platform in game is set to a tile type of `JUMP_THROUGH_PLATFORM` which allows the player to stand on it but also allows the player to jump through it from below. + +While the player is standing on the platform, the `HorizontalMovingPlatform` class detects this in its `update` method and will adjust the player's x position to move along with the platform. + +![player-on-moving-platform.gif](../../../assets/images/player-on-moving-platform.gif) + +Not going to lie, getting this to work was a lot tougher than I expected and required me to rewrite the entire collision handling system at one point. I'm glad I decided to get this working, as it revealed a lot of flaws in the original collision handling code, due to decimals screwing everything up (I'm telling you, it's a lot harder than it sounds!!!). + +Also, if the platform's tile type is changed to `NOT_PASSABLE`, it will push the player if the player is in the way of it while it moves. + +The image file for the green platform is `GreenPlatform.png`. + +### End Level Box + +![end-level-box.gif](../../../assets/images/end-level-box.gif) + +This enhanced map tile is defined by the `EndLevelBox` class. Its job is simple upon being touched by the player, it will set the player's "level state" to `LEVEL_COMPLETED`, which tells the player to do its win animation and whatever else may follow the level being completed afterwards. + +```java +if(intersects(player)){ + player.setLevelState(LevelState.LEVEL_COMPLETED); + } +``` + +The image file for the end level box is `GoldBox.png`. diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-camera.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-camera.md new file mode 100644 index 0000000..c09db76 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-camera.md @@ -0,0 +1,53 @@ +--- +layout: default title: Map Camera nav_order: 4 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/MapCamera +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Map Camera + +## What is the map camera? + +The map camera, which is represented by the `Camera` class in the `Level` package, is the class responsible for keeping track of which section of a map needs to be shown/updated during a specific time period. If you have ever played a 3D video game, you will have an idea of what a game map's camera is -- often times you are given the ability in these games to rotate it yourself to see different parts of the map as you play. In a 2D world, the camera is a lot simpler, as there are only four movement directions -- up, down, left, and right. + +Take a look at the below gif: + +![playing-level.gif](../../../assets/images/playing-level.gif) + +As the player moves across the map, it's the `Camera` class's job to change what pieces of the map are shown/updated. This is used to give the appearance that the map is "scrolling" as the player moves. In reality, once the player hits the half way point on the screen (both x direction and y direction), instead of the player moving forward, the camera actually moves to show more of the map. Once the player reaches an end of the screen, the camera just stays in place, and in that time the player can move freely until the need to move the camera comes up again. + +## How does camera movement work? + +Think of the camera as a defined rectangle, with x, y, width, and height attributes. The width and height of the camera are set to the entire size of the screen. + +Below is the entire map image, which is made up of individual tiles. The entire map does NOT fit on the screen all at once. +![entire-map.PNG](../../../assets/images/entire-map.PNG) + +The camera starts at x and y (0, 0) when the map is first loaded. Upon starting the game, the camera moves to match where the player's start tile is (the player is the cat). The screen size is around 800x600, so you can think of the camera as the rectangle on this map image. The rectangle shows that a piece of the entire map is being shown to the player on the screen at a time: + +![entire-map-with-camera-1.png](../../../assets/images/entire-map-with-camera-1.png) + +As the player moves throughout the map, the camera follows it to show different pieces of the map. + +![entire-map-with-camera-1.png](../../../assets/images/entire-map-with-camera-2.png) + +## Active Map Resources + +The camera is also responsible for determining which map tiles, enemies, enhanced map tiles (like the floating platform), and npcs are a part of the current map that is being shown, meaning those items need to be a part of the `update` and `draw` cycle. In order to not waste computing resources, the camera is constantly checking if a map item is not in the "updatable" area (such as an enemy has gone too far off-screen). This is important as wasting time updating and drawing items that do not affect the player can affect FPS and cause the game to slow down, which is never ideal. + +Map items that are to be included in the `update` and `draw` cycle at a given time are considered "active". The `Map` class exposes three methods for `getActiveEnemies`, `getActiveEnhancedMapTiles` and `getActiveNPCs` that allow other classes to retrieve this information. These contain a subsection of each map resource that are currently active, and can be used for things like collision checking to prevent from having to check EVERY resource in the game. Looking at the above images of the camera example on the entire map image, it's more apparent how only certain enemies that are in the camera's range need to be included in the `update` and `draw` cycles at any given time. + +The variable `UPDATE_OFF_SCREEN_RANGE` determines the "tile range" that a map resource can be off screen until it is considered inactive. It is currently set to 4, so if any map resource is more than 5 tiles away off screen, it will be removed from the game cycle until it comes back into range. Some resources (specifically enemies) have the ability to respawn, meaning they will go back to their starting location if they were previously inactive and then became active again. + +The `Camera` class's `loadActiveEnemies`, `loadActiveEnhancedMapTiles`, and `loadActiveNPCs` methods are called each game loop cycle (each frame) +to determine which map entities are currently active and which ones are not. Frankly, the code for these methods is an abomination because I couldn't find an easy way to combine them all, so it's three long-ish separate methods that all do relatively the same exact thing and contain identical code (just on different entity lists). \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-class-overview.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-class-overview.md similarity index 50% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-class-overview.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-class-overview.md index 8d8e2ec..bb62d92 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-class-overview.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-class-overview.md @@ -1,20 +1,16 @@ --- -layout: default -title: Map Class Overview -nav_order: 1 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/MapClassOverview +layout: default title: Map Class Overview nav_order: 1 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/MapClassOverview --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -22,17 +18,17 @@ permalink: /GameCodeDetails/Map/MapClassOverview ## Map Resources Setup -The `Map` class does A LOT of setup upon instantiation for resources such as the camera, enemies, map tile structure, etc. -Since the `Map` class handles so many different aspects of the platformer game logic, getting the setup step perfect is key -for a successful platformer level. Most of the game logic in the `Map` class goes towards this setup step. +The `Map` class does A LOT of setup upon instantiation for resources such as the camera, enemies, map tile structure, etc. Since the `Map` class handles so many different aspects of the platformer game logic, getting the setup step perfect is key for a successful platformer level. Most of the game logic in the `Map` class goes towards this setup step. -The `Map` class is `abstract`, meaning it cannot be instantiated directly (but a subclass can). The constructor for the `Map` +The `Map` class is `abstract`, meaning it cannot be instantiated directly (but a subclass can). The constructor for the `Map` class has three key parameters defined: + - **mapFileName** -- the map file name that defines the tile layout and map dimensions, which are located in the `MapFiles` folder of this project - **tileset** -- which tiles this map uses (along with the map file will determine which map graphics are loaded in which locations) - **playerStartTile** -- the x and y index of the map tile the player should start on at the beginning of a level From there, the map does several setup steps to get all of its resources in order and ready to go for the platformer game: + 1. Read in map file correctly lay out the map tile graphics to the appropriate locations (process detailed [here](./map-tiles-and-tilesets.md)) 1. Setup enemies (read more about enemies [here](./enemies.md)) 1. Setup enhanced map tiles (read more about enhanced map tiles [here](./enhanced-map-tiles.md)) @@ -41,34 +37,25 @@ From there, the map does several setup steps to get all of its resources in orde ## Map Class Methods -The `Map` class has A LOT of methods, but most of them are very simple (also many of them are just getters). -Nearly all of these methods will be covered in the other setup pages linked above, as most are directly involved with -the setup step. Something to note is that the methods `loadEnemies`, `loadEnhancedMapTiles`, and `loadNPCs` are all intended -to be overridden by a subclass of the `Map` class (aka a class that extends from the `Map` class). +The `Map` class has A LOT of methods, but most of them are very simple (also many of them are just getters). Nearly all of these methods will be covered in the other setup pages linked above, as most are directly involved with the setup step. Something to note is that the methods `loadEnemies`, `loadEnhancedMapTiles`, and `loadNPCs` are all intended to be overridden by a subclass of the `Map` class (aka a class that extends from the `Map` class). -The `update` method is very simple, as the `Camera` class does most of the work updating the map, however -there are two very important methods it does call itself: `adjustMovementX` and `adjustMovementY`. These are covered -in the `Player` class documentation [here](../player.md), and are critical to allowing the map to properly "scroll" as the player moves. -These methods were honestly such a PITA to get working correctly in conjunction with the `Player` class's movement and the `Camera` class's -game logic so I highly recommend not trying to edit them because one tiny change will most likely cause the entire platformer game to break. +The `update` method is very simple, as the `Camera` class does most of the work updating the map, however there are two very important methods it does call itself: `adjustMovementX` and `adjustMovementY`. These are covered in the `Player` class documentation [here](../player.md), and are critical to allowing the map to properly "scroll" as the player moves. These methods were honestly such a PITA to get working correctly in conjunction with the `Player` class's movement and the `Camera` class's game logic so I highly recommend not trying to edit them because one tiny change will most likely cause the entire platformer game to break. The `draw` method just tells the `Camera` class to `draw` what should be shown of the map to the screen. ## Map Subclasses -Game maps are defined by subclassing the `Map` class. These are found in the `Map` package. -The level's map class (the only level in the game) is `TestMap`. +Game maps are defined by subclassing the `Map` class. These are found in the `Map` package. The level's map class (the only level in the game) is `TestMap`. Each `Map` subclass must satisfy the super class and specify the map file name, tileset, and player start tile (as mentioned earlier in the [map resources setup](#map-resources-setup)) section. -`TestMap` defines a map file of `test_map.txt`, the `CommonTileset` class as its tileset choice (more on tilesets [here](./map-tiles-and-tilesets.md)), -and a player start tile as tile index (1, 11). +`TestMap` defines a map file of `test_map.txt`, the `CommonTileset` class as its tileset choice (more on tilesets [here](./map-tiles-and-tilesets.md)), and a player start tile as tile index (1, 11). ```java -public TestMap() { - super("test_map.txt", new CommonTileset(), new Point(1, 11)); -} +public TestMap(){ + super("test_map.txt",new CommonTileset(),new Point(1,11)); + } ``` Each map subclass can also override the `loadEnemies`, `loadEnhancedMapTiles`, and `loadNPCs` in order to define @@ -77,15 +64,12 @@ Each map subclass can also override the `loadEnemies`, `loadEnhancedMapTiles`, a For example, in `TestMap`, the `loadEnemies` override method looks like this: ```java -@Override -public ArrayList loadEnemies() { - ArrayList enemies = new ArrayList<>(); - enemies.add(new BugEnemy(getPositionByTileIndex(15, 9), this, Direction.LEFT)); - enemies.add(new DinosaurEnemy(getPositionByTileIndex(19, 1).addY(2), getPositionByTileIndex(22, 1).addY(2), this, Direction.RIGHT)); - return enemies; -} +@Override public ArrayList loadEnemies(){ + ArrayList enemies=new ArrayList<>(); + enemies.add(new BugEnemy(getPositionByTileIndex(15,9),this,Direction.LEFT)); + enemies.add(new DinosaurEnemy(getPositionByTileIndex(19,1).addY(2),getPositionByTileIndex(22,1).addY(2),this,Direction.RIGHT)); + return enemies; + } ``` -This adds two enemies to the map -- the [bug enemy](./enemies.md#bug-enemy) (`BugEnemy` class) and the [dinosaur enemy](./enemies.md#dinosaur-enemy) (`DinosaurEnemy` class). -The `getPositionByTileIndex` `Map` method is used to make it easy to place the enemy on a specific tile index in the map by -getting the tile at that index's absolute position in terms of pixels so it is placed in the right spot. \ No newline at end of file +This adds two enemies to the map -- the [bug enemy](./enemies.md#bug-enemy) (`BugEnemy` class) and the [dinosaur enemy](./enemies.md#dinosaur-enemy) (`DinosaurEnemy` class). The `getPositionByTileIndex` `Map` method is used to make it easy to place the enemy on a specific tile index in the map by getting the tile at that index's absolute position in terms of pixels so it is placed in the right spot. \ No newline at end of file diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-entities.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-entities.md new file mode 100644 index 0000000..267b7c0 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-entities.md @@ -0,0 +1,38 @@ +--- +layout: default title: Map Entities nav_order: 5 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/MapEntities +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Map Entities + +## What is a map entity? + +A map entity, represented by the `MapEntity` class in the `Level` package, is predictably any game object that is a part of a map. This includes [map tiles](./map-tiles-and-tilesets.md) (`MapTile` class), [enemies](./enemies.md) (`Enemy` class), +[enhanced map tiles](./enhanced-map-tiles.md) (`EnhancedMapTiles` class), and [NPCs](./npcs.md) (`NPC` class). + +## What is the purpose of this class? + +The main reason for the `MapEntity` class, which extends from `GameObject`, is that the `MapEntity` class adds a couple of instance variables made for map entities other than the player -- this includes `isRespawnable` and `isUpdateOffScreen`, which mostly applies to enemies (and can be read about further on the [enemies](./enemies.md) page), as well as an `initialize` method that will properly "reset" an entity on a map back to its original position if necessary (such as for respawning an enemy). + +## Initialize Method + +When subclassing the `MapEntity` class, an `initialize` method will be available to be overridden. This method should "setup" the enemy, as the `Camera` class will call this `initialize` method whenever an enemy becomes "active" on screen. When subclassing +`MapEntity`, it is important to remember to call the super class's `intitialize` method in order to execute its integral logic for things like respawning. + +## Map Entity Status + +All map entities have an instance variable `mapEntityStatus` which the map's `Camera` uses to determine if the entity is +"active" or not. An active entity means it should be included in the level's `update`/`draw` cycle for a current frame. Entities that are too far offscreen for example will often be removed from the `Camera's` `update`/`draw` cycle until they are back on screen, as the game does not want to waste resources on entities that at that current frame have no affect on the level or the player. + +The `MapEntityStatus` enum in the `Level` package defines three different possible statuses: `ACTIVE`, `INACTIVE`, and `REMOVED`. An entity generally doesn't have to mess with this value as the `Camera` handles the logic for checking active vs inactive entities, however an entity may set its own status to `REMOVED` to have it permanently removed from the level with no ability to respawn. The `Fireball` class sets itself to `REMOVED` after a few seconds in order for it to fully disappear from the level. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-file.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-file.md similarity index 53% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-file.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-file.md index c13db2c..475ce51 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-file.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-file.md @@ -1,20 +1,16 @@ --- -layout: default -title: Map File -nav_order: 3 -parent: Map -grand_parent: Game Code Details -permalink: /GameCodeDetails/Map/MapFile +layout: default title: Map File nav_order: 3 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/MapFile --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -22,11 +18,10 @@ permalink: /GameCodeDetails/Map/MapFile ## What is a map file? -A map file is a text file that contains information on the map dimensions as well as the map tile structure (which tiles go where). -Map files can be created with the Map Editor, which you can find information on [here](../../../MapEditor/map-editor.md), and are located in the `MapFiles` folder -in this project. +A map file is a text file that contains information on the map dimensions as well as the map tile structure (which tiles go where). Map files can be created with the Map Editor, which you can find information on [here](../../../MapEditor/map-editor.md), and are located in the `MapFiles` folder in this project. ## Map file structure + There are two parts to a map file. If you did the `Maze` project from CSC111, this file is very similar to the maze files. Below is an example map file: @@ -49,21 +44,14 @@ Below is an example map file: The first two numbers (in this case 17 and 12) are the width and height of the map. -Afterwards, each number in the "grid" corresponds to a specific map tile from a tileset. The Map Editor handles for you -which index maps to which map tile, so that is not something that has to be worried about. For example, the Map Editor may have mapped -the grass tile to the number 0, the sky tile to the number 1, etc. +Afterwards, each number in the "grid" corresponds to a specific map tile from a tileset. The Map Editor handles for you which index maps to which map tile, so that is not something that has to be worried about. For example, the Map Editor may have mapped the grass tile to the number 0, the sky tile to the number 1, etc. ## Reading the map file into the game -The `Map` class's `loadMapFile` method handles reading in a map file. It starts by opening up the map file and reading in the -width and height values. +The `Map` class's `loadMapFile` method handles reading in a map file. It starts by opening up the map file and reading in the width and height values. -The `Map` class then defines an array of `MapTile` of length `width * height`. This is a regular array and NOT -a 2D array, however it is being used in a way where it still has the concept of "rows" and "columns". While a 2D array would be fine to use, they have slower accessing speeds (especially when taking into account caching) and use more memory, -both things that a game doesn't want in order to get the best possible FPS. For this reason, a regular array is used, and items -are retrieved from the array as if it were a 2D array using `x + width * y`. +The `Map` class then defines an array of `MapTile` of length `width * height`. This is a regular array and NOT a 2D array, however it is being used in a way where it still has the concept of "rows" and "columns". While a 2D array would be fine to use, they have slower accessing speeds (especially when taking into account caching) and use more memory, both things that a game doesn't want in order to get the best possible FPS. For this reason, a regular array is used, and items are retrieved from the array as if it were a 2D array using `x + width * y`. Afterwards, each tile index of the map is read in one at a time and is given to the map's `Tileset` instance. The `Tileset` -then returns a `MapTileBuilder` that maps to that tile index (the `MapTilBuilder` is basically a pre-setup `MapTile` defined in the `Tileset`, however -it has not been instantiated yet). It is at this point that the `Map` class actually instantiates and initializes each `MapTile` +then returns a `MapTileBuilder` that maps to that tile index (the `MapTilBuilder` is basically a pre-setup `MapTile` defined in the `Tileset`, however it has not been instantiated yet). It is at this point that the `Map` class actually instantiates and initializes each `MapTile` and sets its location to the correct position to construct the entire "map image". \ No newline at end of file diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-tiles-and-tilesets.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-tiles-and-tilesets.md new file mode 100644 index 0000000..14b46c1 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/map-tiles-and-tilesets.md @@ -0,0 +1,195 @@ +--- +layout: default title: Map Tiles and Tilesets nav_order: 2 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/MapTilesAndTilesets +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Map Tiles and Tilesets + +## Map Tiles + +In order to understand how the [map file](./map-file.md) is structured/read in, the concept of a "map tile" must be understood first. + +Think of a map as a 2D grid. Each spot on the grid contains what is known as a "tile", which can be thought of as a smaller graphic that fits right into the grid. A map is made up by joining these tile graphics together to create one large graphic representing the entire map. For example, take a look at the below screenshot of a piece of a level: + +![tile-map-example-original.png](../../../assets/images/tile-map-example-original.png) + +This image is actually made up of individual tiles, such as the grass tile and the sky tile: + +![grass-tile.png](../../../assets/images/grass-tile.png) +![sky-tile.png](../../../assets/images/sky-tile.png) + +So the image seen above of the grass, tree, flower and sky is actually composed together by smaller individual tiles, which is known as a tile map: + +![tile-map-example.png](../../../assets/images/tile-map-example.png) + +Tiles make it very easy to build up a 2D map. The Map Editor makes this process of using tiles very obvious since the editor allows tiles to be placed down on a grid in a desired location of the map. Each tile will have a specified index in the map at an x and y location, e.g. the first tile in the top left corner would have an index of (0,0), the tile to the right of it would be (1,0) and the tile below it would be (0,1). + +The class `MapTile` in the `Level` package represents a map tile, which is really just a standard sprite with an attribute for `TileType`, which determines how the tile is treated by the game -- for example, a tile type of `NOT_PASSABLE` means that the `Player` cannot walk or fall through the tile, which is used on tiles like grass in order to have the player walk on it. The available tile types are included in the `TileType` enum in the `Level` package. + +## Map Tileset + +A tileset is a collection of map tiles. Easy enough. + +Graphic wise, a tileset defines each tile in one image. Below is the `CommonTileset.png` which the (only) level in the game uses to construct its map. You will notice that it's literally one image with each map tile defined. Note that each map tile in a tileset must be the SAME width and height. + +![common-tileset.png](../../../assets/images/common-tileset.png) + +The `Tileset` class in the `Level` package represents a tileset, which contains a collection of `MapTile` object. The way to define a tileset in this game is to create a class that extends from this `Tileset` class, such as the `CommonTileset` class in the `Tilesets` package. From there, the extended `Tileset` class (such as `CommonTileset`) must call its super class with the following: + +- The tileset image file to be used -- `CommonTileset` uses the `CommonTileset.png` file shown above +- The width and height for every tile in the tileset -- `CommonTileset` specifies that each tile graphic in the above image is 16x16 +- The tileset scale, which is how much each tile in the tileset should be scaled by when drawn to the screen -- `CommonTileset` specifies a scale of 3, meaning each tile will be 48x48 pixels on screen + +Additionally, the `Tileset` class method `defineTiles` must be overridden and have actual defined `MapTiles` added to it. + +### Adding a map tile to a tileset + +The setup for overriding the `defineTiles` method in a `Tileset` subclass looks like this: + +```java +@Override public ArrayList defineTiles(){ + ArrayList mapTiles=new ArrayList<>(); + return mapTiles; + } +``` + +From here, `MapTiles` can be added to the `ArrayList` -- kind of. This method actually defines a type of `MapTileBuilder`. The general idea is that the class `MapTileBuilder`, which can be found in the `Builders` +package, essentially defines a `MapTile` but does not instantiate it yet. It's not too important to fully understand if you are unfamiliar with the builder pattern (you can read more about it [here](../game-patterns.md#builder-pattern)), ideally you can use the `CommonTileset` class as a reference to guide you in adding a new `MapTile` to a tileset. + +The following example in the `defineTiles` method adds the grass tile to the tileset, which is the first graphic in the top left corner of the above shown `CommonTileset.png` file: + +![grass-tile.png](../../../assets/images/grass-tile.png) + +```java +@Override public ArrayList defineTiles(){ + ArrayList mapTiles=new ArrayList<>(); + + // grass + Frame grassFrame=new FrameBuilder(getSubImage(0,0),0) + .withScale(tileScale) + .build(); + + MapTileBuilder grassTile=new MapTileBuilder(grassFrame) + .withTileType(TileType.NOT_PASSABLE); + + mapTiles.add(grassTile); + + return mapTiles; + } +``` + +Whew, that's a bit confusing to look at I know! But the formatting here can be copied for every subsequent tile, so it can be treated as a template. + +To start, a new `Frame` (details on `Frame` class [here](../game-object.md#animatedsprite-class)) must be created to represent the grass tile's graphic (`grassFrame`. The `FrameBuilder` +class is used to build a `Frame` instance. In the constructor, `getSubImage(0, 0)` takes a piece of the tileset image (`CommonTileset.png`), in this case the piece of the image at index (0, 0) which is the top left corner. Since `CommonTileset` defines the tile width and tile height as 16x16, the `getSubImage` method will start at location (0, 0) on the image and then take 16 pixels in both directions and return the resulting subimage, which is how the individual grass tile graphic gets returned. The next number (which here is also a 0) is the delay, which only comes into play for [animated tiles](#adding-an-animated-map-tile-to-a-tileset). + +Then, a `MapTileBuilder` class instance must be created to represent the actual `MapTile` (although it won't instantiate the `MapTile` at this time). Here is where the grass tile (`grassTile`) is given the `TileType` `NOT_PASSABLE`, meaning the player cannot walk or fall through it (it is "solid"). + +Finally, the tile is added to the `mapTiles` list. + +Let's do one for the sky tile now: + +![grass-tile.png](../../../assets/images/sky-tile.png) + +In the `CommmonTileset.png` image shown earlier, the sky tile is located to the left of the grass tile at index (0, 1). With that slight difference in mind, nearly everything else will be the same for the sky tile as the grass tile: + +```java +@Override public ArrayList defineTiles(){ + ArrayList mapTiles=new ArrayList<>(); + + // grass + Frame grassFrame=new FrameBuilder(getSubImage(0,0),0) + .withScale(tileScale) + .build(); + + MapTileBuilder grassTile=new MapTileBuilder(grassFrame) + .withTileType(TileType.NOT_PASSABLE); + + mapTiles.add(grassTile); + + return mapTiles; + + // sky + Frame skyFrame=new FrameBuilder(getSubImage(0,1),0) + .withScale(tileScale) + .build(); + + MapTileBuilder skyTile=new MapTileBuilder(skyFrame) + + mapTiles.add(skyTile); + + return mapTiles; + + } +``` + +Unlike with the grass tile, the sky tile is not "solid" and can be passed through by the player, so its `TileType` needs to be +`PASSABLE`. This is the default tile type, so it does not need to be specified, although you could just for clarity place +`.withTileType(TileType.PASSABLE)` there. + +### Adding an animated map tile to a tileset + +Tiles can be animated! Currently, the `CommonTileset` as three animated tiles: the yellow flower, the purple flower, and the shining sun. This is really easy to do. First, the tileset image file must have a separate image for each frame in the tile's animation. + +For example, below are the frame tiles used for the flower (three different frame images): + +![purple-flower-image-1.png](../../../assets/images/purple-flower-image-1.png) +![purple-flower-image-1.png](../../../assets/images/purple-flower-image-2.png) +![purple-flower-image-1.png](../../../assets/images/purple-flower-image-3.png) + +The end goal is the following animation: + +![purple-flower-animation.gif](../../../assets/images/purple-flower-animation.gif) + +The code for this animated tile looks like this: + +```java +// purple flower +Frame[]purpleFlowerFrames=new Frame[]{ + new FrameBuilder(getSubImage(0,3),500) + .withScale(tileScale) + .build(), + new FrameBuilder(getSubImage(0,4),500) + .withScale(tileScale) + .build(), + new FrameBuilder(getSubImage(0,3),500) + .withScale(tileScale) + .build(), + new FrameBuilder(getSubImage(0,5),500) + .withScale(tileScale) + .build() + }; + + MapTileBuilder purpleFlowerTile=new MapTileBuilder(purpleFlowerFrames); + + mapTiles.add(purpleFlowerTile); +``` + +Instead of just one `Frame` being created, instead an array of `Frames` are defined. Each `Frame` specifies its own subimage location, and its delay value (milliseconds) before the animation moves on to the next frame in the animation. Each frame in this animation has a delay of 500 milliseconds (half a second). This animation defines these four frames in the following order: + +![purple-flower-image-1.png](../../../assets/images/purple-flower-image-1.png) +![purple-flower-image-1.png](../../../assets/images/purple-flower-image-2.png) +![purple-flower-image-1.png](../../../assets/images/purple-flower-image-1.png) +![purple-flower-image-1.png](../../../assets/images/purple-flower-image-3.png) + +After each animation cycle, the animation will loop back to the beginning again (unless the delay is set to -1, in which case it will never move on from an animation frame without something else explicitly telling it to). + +### Tile Types + +The available tile types are defined in the `TileType` enum, and include: + +- **NOT_PASSABLE** -- player cannot pass through it, they are "solid", such as the grass tiles +- **PASSABLE** -- player can pass through it, such as the sky tiles +- **JUMP_THROUGH_PLATFORM** -- all platformers have these types of platforms, the player can walk on top of it and cannot pass through it when coming downwards from above, but can pass through it when coming upwards from below; the tree branch tiles are jump through platforms for example +- **LETHAL** -- player dies if they touch a lethal tile diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/npcs.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/npcs.md new file mode 100644 index 0000000..a2024c0 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/MapSubSections/npcs.md @@ -0,0 +1,71 @@ +--- +layout: default title: NPCs nav_order: 8 parent: Map grand_parent: Game Code Details permalink: /GameCodeDetails/Map/NPCs +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# NPCs + +## What is an NPC? + +An NPC (**n**on-**p**layer **c**haracter) (represented by the `NPC` class in the `Level` package) is a `MapEntity` subclass. This class adds a `checkTalkedTo` method that any `NPC` subclass can override, which handles the code that is run if an NPC is talked to by the Player (the player character has to be within the NPC's range and then press space to talk to it). An NPC can be given its own animation and graphics information, as well as its own `update` cycle which defines its behavior. + +NPCs in a game tend to act as a neutral character, meaning they usually do not harm the player and are meant to supplement the traversal through a level and also improve overall game immersion. + +As of now, once an NPC is talked to, they can display a message until their `talkedToTime`, runs out, at which point the message disappears and they can be re-talked to again. + +## NPC Subclass + +In the `NPCs` package, there is currently only one subclass of the `NPC` class -- `Walrus`. This `Walrus` NPC can be seen in the game's one level. + +## Adding a new NPC to the game + +This is simple -- create a new class in the `NPCs` package, subclass the `NPC` class, and then just implement desired logic from there. I recommend copying an existing npc class as a "template" of sorts to help set up and design the npc. + +## Adding an NPC to a map + +In a map subclass's `loadNPCs` method, NPCs can be defined and added to the map's NPC list. For example, in `TestMap`, a `Walrus` class instance is created added to the NPC list: + +```java +@Override public ArrayList loadNPCs(){ + ArrayList npcs=new ArrayList<>(); + + npcs.add(new Walrus(getPositionByTileIndex(30,10).subtract(new Point(0,13)))); + + return npcs; + } +``` + +## NPCs currently in game + +### Walrus + +![walrus.png](../../../assets/images/walrus.png) + +This NPC is defined by the `Walrus` class. In addition being the best made piece of art you have ever laid your eyes on, the walrus is able to be talked to when the player is in range of its "hurtbox" and presses space. Upon doing so, the `Walrus` class will draw a box with text inside to the screen: + +![walrus-talking.png](../../../assets/images/walrus-talking.png) + +The code to bring up the "speech box" during the `draw` cycle just creates a rectangle, then a sprite font, and then places them in the right location. An `NPCs` `drawMessage` method will automatically be called when the `NPC` is talked to. This is what the overridden `drawMessage` method looks like in the `Walrus` class: + +```java +@Override public void drawMessage(GraphicsHandler graphicsHandler){ + graphicsHandler.drawFilledRectangleWithBorder(Math.round(getCalibratedXLocation()-2),Math.round(getCalibratedYLocation()-24),40,25,Color.WHITE,Color.BLACK,2); + message.setLocation(getCalibratedXLocation()+2,getCalibratedYLocation()-8); + message.draw(graphicsHandler); + } +``` + +Lining the rectangle up with the text and the NPC is a total PITA. Those random `+ 2s` and `- 8s` was just me moving the graphics pixel by pixel until I was happy with the position. + +The image file for the walrus is `Walrus.png`. \ No newline at end of file diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/collision-detection.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/collision-detection.md new file mode 100644 index 0000000..b2e9d25 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/collision-detection.md @@ -0,0 +1,137 @@ +--- +layout: default title: Collision Detection nav_order: 3 parent: Player grand_parent: Game Code Details permalink: /GameCodeDetails/Player/CollisionDetection +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Collision Detection + +## What is collision detection? + +Collision detection is the ability for entities in the game, such as the player, to be able to detect "collisions" with other map entities, such as map tiles, enemies, etc. This is generally done through checking if two entities "intersect" each other, at which point a collision has occurred. Games are built around collision detection -- think of any of your favorite video games and tell me if the game would still work if entities could not detect and react to colliding with other entities, and I guarantee you it cannot. + +The hard part about collision detection is there are A LOT of different "scenarios" where a collision can occur between two entities, and each entity needs to react accordingly to the collision in their designed way. All map entities in this game engine are rectangles (x, y, width, height), which thankfully lessens a lot of the complexity that other shapes would introduce. Rectangles are the easiest shape to work with by far due to +"rectangle math" being relatively simple and they works out very nicely in a 2D space where only the x and y axis exist. + +Also, this is always the hardest part about creating a game, and anyone will tell you that writing your own collision detection is the WORST when it comes to platformers and especially with 3D games -- it is difficult, math is involved, and tiny errors randomly creep up just when you finally think you've gotten everything down pact. In this game's case, getting proper decimal movement caused me a whole lot of trouble -- and I've even written code like this before for another game! It's just hard stuff, but I believe this game's collision detection is in a good state right now (it better be after all the time I put into it). + +## Player collision detection with Map Tiles + +**tl;dr**: This is probably the most complicated part of this game. Each frame the player moves by a specified amount unless it collides with a solid map tile, in which case it is told to stop moving. Use the `moveXHandleCollision` and `moveYHandleCollision` methods for this functionality instead of the generic `moveX` and `moveY` methods which don't account for collision. + +The main reason for needing extremely precise collision detecting in a platformer game is for the player to be able to traverse the map without being able to fall through the floor or run through obstacles. + +The `GameObject` class contains two special move methods named `moveXHandleCollisions` and `moveYHandleCollision`. These methods will move the player by a specified amount, and the `Player` calls these methods each frame to move it by a set amount based on whatever actions the player took (such as walking forward). Unlike the plain `moveX` and `moveY` methods which simply move a player by a specified amount no questions asked, the `moveXHandleCollision` and `moveYHandleCollision` methods will stop the player from moving if it collides with a "solid" entity, such as a `NON_PASSABLE` map tile ("solid"). Each `MapTile` has an assigned [tile type](../MapSubSections/map-tiles-and-tilesets.md#tile-types), and the move handle collisions methods will take these into account when moving the player to determine if a player is allowed to move to the new location or not. + +Decimal movement makes this a bit more complex. While a player can be at a decimal location in game logic, in draw logic it cannot since a graphic cannot be physically drawn on half of a monitor pixel. For this reason, while decimal precision is still possible to implement, it just won't always apply a graphics update if the decimal movement is small enough to not change the round up or down to the nearest whole number. For example, a decimal amount of `1.49` will round down to `1`, but `1.5` will round up to `2`. + +So, how exactly do the `moveXHandleCollision` and `moveYHandleCollision` methods work? First thing they do is identify the amount of pixels the player is about to move, the direction to move in, and lastly calculate decimal values to figure out if a potential round up or round down change will occur. From there, the methods will move the player one pixel at a time over and over again until it reaches the total amount the player is supposed to move by. During each one pixel move, collisions are checked against the map tiles, and if a collision is detected the movement is stopped and all leftover movement is thrown away. Lastly, decimal values are recalculated and figured out (if a round up occurs, one more pixel is moved and collision detection is checked one last time). + +The `Player` calls these two methods from the `GameObject` class each frame in its `update` logic: + +```java +super.moveYHandleCollision(moveAmountY); + super.moveXHandleCollision(moveAmountX); +``` + +The class `MapTileCollisionHandler` is used by the move handle collisions methods to determine if a collision occurred, and if a collision did occur the player's position is adjusted to be right in front of the solid map tile it collided with. + +The `MapTileCollisionHandler` contains three methods: `getAdjustedPositionAfterCollisionCheckX`, `getAdjustedPositionAfterCollisionCheckY`, and `hasCollidedWithMapTile`. + +The first two methods `getAdjustedPositionAfterCollisionCheckX` and `getAdjustedPositionAfterCollisionCheckY` do some "rectangle math" to determine which tiles in the map need to be checked for collisions. A common mistake newer game developers make is writing collision checking code that checks against every single tile in the map every single time -- this is a huge waste of resources and can actually harm FPS as the game logic step can end up taking too long. Instead, this code will determine the direction the player is moving, and based on that only check the immediate tile(s) in the vicinity. Just like with sorting algorithms, lowering the amount of comparisons is key to have a smoothly running game! From there, they will call the third method `hasCollidedWithMapTile` on each map tile that is determined to intersect with the player and see if that is determined a collision based on the map tile's tile type. + +The `hasCollidedWithMapTile` method is very simple compared to the monstrous other two adjust position methods. It determines if the tile a player intersects with is considered a "collision" or not. This is 100% based on the tile's tile type. A tile type of `NOT_PASSABLE` +would return true to there being a collision. `PASSABLE` tiles cannot be collided with and would always return false. +`JUMP_THROUGH_PLATFORM` tiles have a bit of a more complex check, because for a collision to occur the player has to be moving downwards and their bottom hurtbox has to be overlapping the jump through platform's top hurtbox. `LETHAL` tiles kill the player and restart the level. Overall though this method is very simple when looking at it: + +```java +private static boolean hasCollidedWithMapTile(GameObject gameObject,MapTile mapTile,Direction direction){ + switch(mapTile.getTileType()){ + case PASSABLE: + return false; + case NOT_PASSABLE: + return gameObject.intersects(mapTile); + case JUMP_THROUGH_PLATFORM: + return direction==Direction.DOWN&&gameObject.intersects(mapTile)&& + Math.round(gameObject.getScaledBoundsY2()-1)==Math.round(mapTile.getScaledBoundsY1()); + case LETHAL: + return gameObject.intersects(mapTile); +default: + return false; + } + } +``` + +More tile types can easily be added to this method to determine if a collision occurred or not. The other two methods don't really have to be modified at all, new tiles types added to this `hasCollidedWithMapTile` method will work just as the existing three tile types do. + +## Player collision detection with Enhanced Map Tiles + +Additionally, these methods check against all active [enhanced map tiles](../MapSubSections/enhanced-map-tiles.md) in the map, as they are to be treated like regular map tiles in terms of collision detection (enhanced map tiles have a tile type attribute!). So for example, the game's green horizontal moving platform would also be checked against during this code. + +Although standard collision detection on enhanced map tiles is done here, `EnhancedMapTile` classes can run their own +`update` logic to do other actions when intersecting with a player. For example, the green horizontal moving platform's class carries out its own logic upon determining that it overlaps with a player in order to move the player as it moves. + +![player-on-moving-platform.gif](../../../assets/images/player-on-moving-platform.gif) + +## Player collision detection with Enemies + +The `Player` actually does not seek out collision detection with enemies -- instead each `Enemy` class will seek out a collision detection aganist the player. This allows an `Enemy` to specify to the `Player` class what to do upon being touched. For example, the enemies in the game (coming from the generic `Enemy` class) call the `Player` class's `hurtPlayer` method upon intersecting with the player, and the `Player` can then determine how it "hurts" itself (as of now, the player dies and it's a game over). + +## Player collision detection with NPCs + +Just like with enemies, the `NPC` detects when it intersects with a player, which is how it determines the player is in radius and is able to be talked to. + +## Enemy collision detection with the Map + +Enemies often follow collision detection rules, and are free to use the same `GameObject` methods `moveXHandleCollision` and `moveYHandleCollision` +to abide by tile type rules. + +## Player class reacting to a collision + +The `GameObject` method provides two methods that are intended to be overridden by a subclass: `onEndCollisionCheckX` and `onEndCollisionCheckY`. After a collision check has been done, the collision methods will let the `Player` class (or any other `GameObject` subclass like the `Enemy` class that moves while checking for collisions) know if a collision occurred and what direction the collision happend from (left, right, up, or down). + +The `Player` class uses the `onEndCollisionCheckY` method to determine its `airGroundState` -- if a the player collides with a map tile downwards, it means that the player is landed on the ground and is no longer in the air. This causes the player to set its `airGroundState` to `GROUND`. Additionally, the reverse is true -- if the player is not colliding with a map tile downwards, it means the player is in the air, which causes its `airGroundState` to be set to `AIR`. The method also stops the player from jumping upwards if it collides with a map tile upwards. + +```java +@Override public void onEndCollisionCheckY(boolean hasCollided,Direction direction){ + if(direction==Direction.DOWN){ + if(hasCollided){ + momentumY=0; + airGroundState=AirGroundState.GROUND; + }else{ + playerState=PlayerState.JUMPING; + airGroundState=AirGroundState.AIR; + } + }else if(direction==Direction.UP){ + if(hasCollided){ + jumpForce=0; + } + } + } +``` + +The `BugEnemy` class also uses this feature. It uses the `onEndCollisionCheckX` to determine if a collision happend, and if so it turns itself around and walks the other direction. + +```java +@Override public void onEndCollisionCheckX(boolean hasCollided,Direction direction){ + if(hasCollided){ + if(direction==Direction.RIGHT){ + facingDirection=Direction.LEFT; + currentAnimationName="WALK_LEFT"; + }else{ + facingDirection=Direction.RIGHT; + currentAnimationName="WALK_RIGHT"; + } + } + } +``` diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-class-overview.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-class-overview.md similarity index 57% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-class-overview.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-class-overview.md index abb27cd..66c8b29 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-class-overview.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-class-overview.md @@ -1,20 +1,16 @@ --- -layout: default -title: Player Class Overview -nav_order: 1 -parent: Player -grand_parent: Game Code Details -permalink: /GameCodeDetails/Player/PlayerClassOverview +layout: default title: Player Class Overview nav_order: 1 parent: Player grand_parent: Game Code Details permalink: /GameCodeDetails/Player/PlayerClassOverview --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -24,8 +20,7 @@ permalink: /GameCodeDetails/Player/PlayerClassOverview The `Player` class has a ton of instance variables that need to be setup upon creation, but they are all relatively straightforward. -First, let's start with the "movement" based variables -- the following define how the `Player` character is able to move throughout a level. -These are all float values, so decimal precision for movement is available. +First, let's start with the "movement" based variables -- the following define how the `Player` character is able to move throughout a level. These are all float values, so decimal precision for movement is available. - **walkSpeed** - how fast the player walks - **gravity** - how fast the player falls when in air @@ -41,6 +36,7 @@ These are all float values, so decimal precision for movement is available. Generally, `Player` subclasses will set those values as desired. The `Player` has several other important variables it uses to keep track of its current state: + - **playerState** - based on the `PlayerState` enum in the `Level` package, a `Player` can be in a certain state which affects its game logic; currently, the supported states are `STANDING`, `WALKING`, `JUMPING`, `CROUCHING`, `ATTACKING` - **facingDirection** - which direction the player is facing; can be either `LEFT` or `RIGHT` - **airGroundState** - if the player is currently on the ground `GROUND` or in the air `AIR` (what a horrible variable name, what was I thinking...) @@ -48,46 +44,38 @@ The `Player` has several other important variables it uses to keep track of its ## Player Class Methods -The player's `update` cycle handles a ton of different cases based on the current `PlayerState`, and the `Player` class has split up -most of that state logic into separate methods. For example, walking logic is in a separate method as well as jumping logic and crouching logic. The +The player's `update` cycle handles a ton of different cases based on the current `PlayerState`, and the `Player` class has split up most of that state logic into separate methods. For example, walking logic is in a separate method as well as jumping logic and crouching logic. The `handlePlayerState` method determines which game logic method to go to based on the `playerState` instance variable: ```java -protected void handlePlayerState() { - switch (playerState) { +protected void handlePlayerState(){ + switch(playerState){ case STANDING: - playerStanding(); - break; + playerStanding(); + break; case WALKING: - playerWalking(); - break; + playerWalking(); + break; case CROUCHING: - playerCrouching(); - break; + playerCrouching(); + break; case JUMPING: - playerJumping(); - break; + playerJumping(); + break; case ATTACKING: - playerAttacking(); - break; - } -} + playerAttacking(); + break; + } + } ``` ## Player Subclasses -Game players are defined by subclassing the `Player` class. These are found in the `Player` package. -The cat's player class (the only player in the game) is `Cat`. +Game players are defined by subclassing the `Player` class. These are found in the `Player` package. The cat's player class (the only player in the game) is `Cat`. -The subclass doesn't have to do much, as the `Player` class contains standard player functionality. If a change is going to be -specific to one player, then it can be added to a subclass, but any changes that would affect all players should be done in the -base `Player` class. - -The `Cat` subclass sets up by setting various movement related attributes to desired values and also defines the player's animations. -The `Player` class expects certain animations be included in a player subclass by name, all of which are included in the `Cat` subclass, -although this can be easily changed and additional animations can be added freely. Currently, the `Player` class expects the following animations. Most -animations have a "left" and "right" counterpart, which are used based on the way the player is facing. +The subclass doesn't have to do much, as the `Player` class contains standard player functionality. If a change is going to be specific to one player, then it can be added to a subclass, but any changes that would affect all players should be done in the base `Player` class. +The `Cat` subclass sets up by setting various movement related attributes to desired values and also defines the player's animations. The `Player` class expects certain animations be included in a player subclass by name, all of which are included in the `Cat` subclass, although this can be easily changed and additional animations can be added freely. Currently, the `Player` class expects the following animations. Most animations have a "left" and "right" counterpart, which are used based on the way the player is facing. 1. `STAND_RIGHT` and `STAND_LEFT` -- player standing still 1. `WALK_RIGHT` and `WALK_LEFT` -- player walking @@ -99,52 +87,46 @@ animations have a "left" and "right" counterpart, which are used based on the wa Looking at the `Cat` classes constructor is a good reference point for making an additional player in terms of how to setup the class: ```java -public Cat(float x, float y, Map map) { - // set sprite sheet, location, and default animation - super(new SpriteSheet(ImageLoader.load("Cat.png"), 24, 24), x, y, "STAND_RIGHT"); - - // set movement values - gravity = .5f; - terminalVelocityY = 6f; - jumpHeight = 14.5f; - jumpDegrade = .5f; - walkSpeed = 2.1f; - momentumYIncrease = .5f; - - // set keys to check for - JUMP_KEY = Key.UP; - MOVE_LEFT_KEY = Key.LEFT; - MOVE_RIGHT_KEY = Key.RIGHT; - CROUCH_KEY = Key.DOWN; - rightKey = Key.D; - leftKey = Key.A; - upKey = Key.W; - downKey = Key.S; - spaceKey = Key.SPACE; - attackKey = Key.E; -} +public Cat(float x,float y,Map map){ + // set sprite sheet, location, and default animation + super(new SpriteSheet(ImageLoader.load("Cat.png"),24,24),x,y,"STAND_RIGHT"); + + // set movement values + gravity=.5f; + terminalVelocityY=6f; + jumpHeight=14.5f; + jumpDegrade=.5f; + walkSpeed=2.1f; + momentumYIncrease=.5f; + + // set keys to check for + JUMP_KEY=Key.UP; + MOVE_LEFT_KEY=Key.LEFT; + MOVE_RIGHT_KEY=Key.RIGHT; + CROUCH_KEY=Key.DOWN; + rightKey=Key.D; + leftKey=Key.A; + upKey=Key.W; + downKey=Key.S; + spaceKey=Key.SPACE; + attackKey=Key.E; + } ``` The image file for the cat player is `Cat.png`. ## Player Moving -When the player moves through the level, you may notice that most of the time it is not actually moving on screen, and instead -stays in the middle of the screen while the map's camera moves to show more of the map. The only time the player actaully moves is when -the map camera reaches the end of the screen and has no more map to show. You can see it in the below gif (notice the player is kept in the middle -of the screen while the camera continually moves). +When the player moves through the level, you may notice that most of the time it is not actually moving on screen, and instead stays in the middle of the screen while the map's camera moves to show more of the map. The only time the player actaully moves is when the map camera reaches the end of the screen and has no more map to show. You can see it in the below gif (notice the player is kept in the middle of the screen while the camera continually moves). ![game-screen-1.gif](../../../assets/images/playing-level.gif) -While the player is in charge of the overall movement of the game, the map class's `adjustMovementX` and `adjustMovementY` methods are called -each frame which adjusts the player's position and the camera's position as needed. Usually, what happens is the player will move forward -while at the center of the screen, and then the adjust methods will move the player back to the center of the screen and convert -that movement amount to the camera to show more of the map. This gives the appearance that the map is "scrolling". And yes, nearly all games do this, -99% of the time your player character is not actually moving, instead the map camera is. +While the player is in charge of the overall movement of the game, the map class's `adjustMovementX` and `adjustMovementY` methods are called each frame which adjusts the player's position and the camera's position as needed. Usually, what happens is the player will move forward while at the center of the screen, and then the adjust methods will move the player back to the center of the screen and convert that movement amount to the camera to show more of the map. This gives the appearance that the map is "scrolling". And yes, nearly all games do this, 99% of the time your player character is not actually moving, instead the map camera is. ## Player Update Cycle Each `update` cycle, the `Player` class will do a few things: + 1. Apply gravity (downward force) 2. Handle player state (more details on player state [here](./player-states.md)) 3. Update player animation and see if a switch is needed (`super.update()` does this) diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-states.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-states.md new file mode 100644 index 0000000..8a26bbc --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-states.md @@ -0,0 +1,85 @@ +--- +layout: default title: Player States nav_order: 2 parent: Player grand_parent: Game Code Details permalink: /GameCodeDetails/Player/PlayerStates +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Player States + +The `Player` has several different states it can be in based on the value of its `playerState` instance variable. Each state can lead to another state based on the player's actions each cycle of the game loop. The states also dictate which animation/frames the player should switch to (e.g. player walking animation when in the `WALKING` state) +The `PlayerState` enum in the `Level` package define the following states that the player can be in: + +- **STANDING** -- player is standing still +- **WALKING** -- player is walking +- **JUMPING** -- player is jumping (or falling) +- **CROUCHING** -- player is crouching +- **ATTACKING** -- player is shooting lasers beams out of his eyes + +## Player Standing State + +![player-standing.PNG](../../../assets/images/player-standing.PNG) + +If the player is on the ground and no keys are pressed, the player will enter its `STANDING` state, where it does nothing and just waits for another key to be pressed which will activate another state. The `playerStanding` method contains the simple logic for the `STANDING` state, which looks like this: + +```java +protected void playerStanding(){ + currentAnimationName=facingDirection==Direction.RIGHT?"STAND_RIGHT":"STAND_LEFT"; + if(Keyboard.isKeyDown(MOVE_LEFT_KEY)||Keyboard.isKeyDown(MOVE_RIGHT_KEY)){ + playerState=PlayerState.WALKING; + }else if(Keyboard.isKeyDown(JUMP_KEY)&&!keyLocker.isKeyLocked(JUMP_KEY)){ + keyLocker.lockKey(JUMP_KEY); + playerState=PlayerState.JUMPING; + }else if(Keyboard.isKeyDown(CROUCH_KEY)){ + playerState=PlayerState.CROUCHING; + } + } +``` + +It really just checks for key presses and if so sets the player to a new state. Not much else to do when you're standing still. + +While standing, the player uses either the `STAND_RIGHT` or `STAND_LEFT` animation. + +### Player Walking State + +![player-walking.gif](../../../assets/images/player-walking.gif) + +If the player is on the ground and either the right or left arrow key is pressed, the player will enter its `WALKING` state, where it will move either left or right in the level. If no keys are pressed while in this state, the player will stop moving and go back into its `STANDING` state. How fast the player walks is determined by the `walkSpeed` instance variable. The `playerWalking` method contains the logic for the `WALKING` state. The player will change the direction it's facing based on which arrow key is pressed. + +While walking, the player uses either the `WALK_RIGHT` or `WALK_LEFT` animation. + +### Player Jumping State + +![player-jumping.gif](../../../assets/images/player-jumping.gif) + +This state's logic is a bit more complicated from the others. If the player is on the ground and presses the up arrow key, the player will enter its `JUMPING` state. The player's `airGroundState` will be changed to `AIR`, and the player will start rising until gravity catches up, at which point the player will fall downwards until it hits the ground again, at which point the player's `airGroundState` will be changed back to `GROUND` and the player will transition into either a standing or walking state. While in the air in the `JUMPING` state, the player can move left and right, but will not change the direction it is facing. The player is still considered in `JUMPING` state while it is falling downwards, and if the player runs off the edge of a cliff and ends up in the air, that is also considered `JUMPING` state -- basically if the player is in the air, it is put in +`JUMPING` state. I probably should have made a separate falling state but...meh it's not really needed. + +The player's jumping physics are all determined through instance variable values(`gravity`, `jumpHeight`, `jumpDegrade`, `terminalVelocityY`, and `momentumYIncrease) +which alter how high the player jumps, how fast the player falls, and what the peak of the jump looks like in terms of motion (e.g. the player can go up and then go right down after reaching the peak of the jump, or the player can reach the peak and wait a bit and slowly begin to pick up falling speed). + +The actual jump algorithm doesn't do all that much. When up is pressed, the user's y velocity is decreased (moving upwards) by the value of `jumpHeight` +each frame. Each frame, `jumpHeight` is lowered slightly by `jumpDegrade`, until eventually no more upwards y velocity is applied to the player. When this happens, the user starts to fall as gravity takes over. Each frame, gravity is always added to the player (just like in real life, gravity is always in effect!). Also, each frame the user's momentum downwards is slightly increased by the value of `momentumYIncrease`. This gives off the effect that the player is falling faster over time, until it reaches its fall speed reaches the value of `terminalVelocityY`, at which point it can no longer increase in fall speed. Once the player touches the ground, the jump/fall ends. + +While rising upwards (jumping), the player uses either the `JUMP_RIGHT` or `JUMP_LEFT` animation. While falling downwards, the player uses either the `FALL_RIGHT` or `FALL_LEFT` animation. + +### Player Crouching State + +![player-crouching.gif](../../../assets/images/player-crouching.gif) + +If the player is on the ground and presses the down arrow key, the player will enter its `CROUCHING` state. Basically, the player goes lower to the ground to shrink its hurtbox, but that's all it does (and the player cannot walk out of `CROUCHING` state, but they can jump out of it). + +### Player Attacking State + +![ezgif.com-gif-maker.gif](../../../assets/images/ezgif.com-gif-maker.gif) + +If the player presses the 'E' key, the cat will enter its `ATTACKING` state and shoots a laser bolt out of its eyes. If the player holds down 'E', the cat shoots a continuous laser beam out of its eyes. Both attacks can be used to kill enemies. When the laser beam touches an enemy, the enemy disappears. diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-win-or-lose-level-logic.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-win-or-lose-level-logic.md new file mode 100644 index 0000000..f6edece --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/PlayerSubSections/player-win-or-lose-level-logic.md @@ -0,0 +1,31 @@ +--- +layout: default title: Player Win or Lose Level Logic nav_order: 4 parent: Player grand_parent: Game Code Details permalink: /GameCodeDetails/Player/PlayerWinOrLoseLevelLogic +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Player Win or Lose Level Logic + +The `Player` class has a `levelState` variable which it uses to determine if it is currently playing the level, completed the level, or lost the level (died). Upon the `levelState` being set to either the `LEVEL_COMPLETED` or `PLAYER_DEAD` state, different `update` logic will be run instead of the usual player traversing through the level. + +## Player Win Logic -- Level Completed + +When the player touches a `LevelEndBox` enhanced map tile, it will result in the player's `levelState` being set to `LEVEL_COMPLETED`. When this happens, the `update` logic changes to use the `updateLevelCompleted` method. This method is what performs the "animation" where after the player hits the `LevelEndBox`, it will fall to the ground, and then walk to the right until it goes off screen. Once it goes off screen, it will signify to the `PlayLevelScreen` that the level has been won. + +![level-completed.gif](../../../assets/images/completing-level.gif) + +## Player Lose Logic -- Player Dead + +When the player dies from touching an enemy, it will result in the player's `levelState` being set to `PLAYER_DEAD`. When this hapens, the `update` logic changes to use the `updatePlayerDead` method. This method is what performs the death animation where the player falls down until they go off screen, at which point the `PlayLevelScreen` is notified that the player has died. + +![losing-level.gif](../../../assets/images/losing-level.gif) diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/credits-screen.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/credits-screen.md new file mode 100644 index 0000000..baa2780 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/credits-screen.md @@ -0,0 +1,46 @@ +--- +layout: default title: Credits Screen nav_order: 3 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/CreditsScreen +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Credits Screen + +The screen handles the logic and graphics related to the credits screen that is loaded when the "CREDITS" option is selected form the game's main menu. + +![credits-screen.png](../../../assets/images/credits-screen.png) + +The class file for it is `CreditsScreen.java` which can be found in the `Screens` package. + +## Functionality + +This screen is pretty limited, all it does is display the graphics shown above in the screenshot. When the space button is pressed, `CreditsScreen` will change `ScreenCoordinator's` game state back to MENU to load the +`MenuScreen` back up. + +```java +if(Keyboard.isKeyUp(Key.SPACE)){ + keyLocker.unlockKey(Key.SPACE); + } + if(!keyLocker.isKeyLocked(Key.SPACE)&&Keyboard.isKeyDown(Key.SPACE)){ + screenCoordinator.setGameState(GameState.MENU); + } +``` + +Super simple. You will notice that it first checks if the space key is not pressed, and if so will "unlock" the key (check out the `KeyLocker` class documentation [here](../game-patterns.md#key-locker) for more information on how that works). Then if the space key is unlocked and pressed, the screen returns back to the menu screen. The reason for this is that the space button is pressed in order to intially go from the menu screen to this credits screen. Because of how fast the game loop iterates, before the space key is released, it would detect it as multiple presses -- one space press would go back and forth from menu to credits and back extremely quickly. By "locking" the space key when the `CreditsScreen` is initialized, and then only unlocking it when the space key is released, it forces the player to release and re-press the space key again rather than being able to hold it down. The `MenuScreen` also has this same functionality implemented with the space key for this reason. + +## Graphics + +Like the `MenuScreen` class, the `CreditsScreen` class uses a `Map` (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. + +All of the text shown on screen is created with various `SpriteFont` graphics defined in the class, which are setup in the +`initialize` method. \ No newline at end of file diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/instructions-screen.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/instructions-screen.md new file mode 100644 index 0000000..787a9b1 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/instructions-screen.md @@ -0,0 +1,31 @@ +--- +layout: default title: Instructions Screen nav_order: 8 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/InstructionsScreen +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Instructions Screen + +The screen handles the logic related to how to play the game. Many users were confused with the functionality of keys, so this screen will fix the confusion. Also, if the users click 'X' the instructions screen appears. + +![instructions-screen.PNG](../../../assets/images/instructions-screen.PNG) + +The class file for it is `InstructionsScreen.java` which can be found in the `Screens` package. + +## Functionality + +The instructions screen's only real job is to show the player how to play the game. It displays how the user can walk, jump, and run throughout the game while avoiding enemies. + +## Graphics + +The background of the screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/levelselect-screen.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/levelselect-screen.md similarity index 52% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/levelselect-screen.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/levelselect-screen.md index ddcce8f..6a6927b 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/levelselect-screen.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/levelselect-screen.md @@ -1,20 +1,16 @@ --- -layout: default -title: Level Select Screen -nav_order: 5 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/LevelSelectScreen +layout: default title: Level Select Screen nav_order: 5 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/LevelSelectScreen --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -28,14 +24,10 @@ The class file for it is `LevelSelect.java` which can be found in the `Screens` ## Functionality -The level select screen's only real job is to allow the player to select between the five levels of the game. -Upon selecting an option, `LevelSelectScreen` will change `ScreenCoordinator's` game state which will force it to load the appropriate screen based -on the option selected. - +The level select screen's only real job is to allow the player to select between the five levels of the game. Upon selecting an option, `LevelSelectScreen` will change `ScreenCoordinator's` game state which will force it to load the appropriate screen based on the option selected. ## Graphics -The background of the screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which -is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. +The background of the screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/menu-screen.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/menu-screen.md new file mode 100644 index 0000000..368f63e --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/menu-screen.md @@ -0,0 +1,93 @@ +--- +layout: default title: Menu Screen nav_order: 2 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/MenuScreen +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Menu Screen + +NOTE: WHILE SOME FOLLOWING DOCUMENTATION IS VALID, MANY SPECIFICS HAVE BEEN SIMPLIFIED BY FALL 2021 TEAM A2. PLEASE LOOK AT MENU CLASSES AND SCREEN CLASSES FOR COMMENTS EXPLAINING THE INNER WORKINGS OF THE NEW SYSTEM + +The screen handles the logic and graphics related to the menu that is loaded upon the game starting up. + +![main-menu.PNG](../../../assets/images/main-menu.PNG) + +The class file for it is `MenuScreen.java` which can be found in the `Screens` package. + +## Functionality + +The menu screen's only real job is to allow the player to select between its two options "Play Game" and "Credits". Upon selecting an option, `MenuScreen` will change `ScreenCoordinator's` game state which will force it to load the appropriate screen based on the option selected. + +```java +// if down key is pressed, add one to current menu item hovered +// "Play Game" option is menu item 0, pressing down will add 1 to the current menu item hovered, +// which changes it to the "Credits" option +if(Keyboard.isKeyDown(Key.DOWN)&&keyTimer.isTimeUp()){ + keyTimer.reset(); + currentMenuItemHovered++; + } +// if up key is pressed, subtract one to current menu item hovered +// "Credits" option is menu item 1, pressing up will sbutract 1 to the current menu item hovered, +// which changes it to the "Play Game" option + }else if(Keyboard.isKeyDown(Key.UP)&&keyTimer.isTimeUp()){ + keyTimer.reset(); + currentMenuItemHovered--; + } +``` + +The `MenuScreen` class's update cycle mainly checks if the user has pressed the down or up keys and if so will move the little blue square from one option to the other and make it clear which option is currently being "hovered" over. Pressing the space bar will select the option and is the trigger that leads to `ScreenCoordinator's` game state being changed. + +```java +// if space is pressed, item is selected +if(!keyLocker.isKeyLocked(Key.SPACE)&&Keyboard.isKeyDown(Key.SPACE)){ + menuItemSelected=currentMenuItemHovered; + + // if first menu item is selected "PLAY GAME", set ScreenCoordinator game state to LEVEL + if(menuItemSelected==0){ + screenCoordinator.setGameState(GameState.LEVEL); + + // if secpmd menu item is selected "CREDITS, set ScreenCoordinator game state to CREDITS + }else if(menuItemSelected==1){ + screenCoordinator.setGameState(GameState.CREDITS); + } + } +``` + +Additionally, a `Timer` is used (from the `Utils` package) to specify how long in between key presses of the up and down key are allowed. Since key presses are so sensitive (every 10ms the `update` cycle is run), pressing the down key or up key just one time would move the selection MANY different times, since the next `update` cycle would come so fast it would still think the key was held down as the player did not have any time to release the key yet. To prevent this, the `keyTimer` variable is used to force 200ms to go by before either the up or down key can be detected again after it has already been pressed, which feels a lot more natural for the common key press. The milliseconds for the `keyTimer` to wait is set in the screen's `initialize` method. The above code already showed how the `keyTimer` integrates with the checks for the up or down keys being pressed, but here is a snippet of it again just to illustrate its functionality: + +```java +// if down key is pressed +// and 200 milliseconds have passed by since the last down key press +if(Keyboard.isKeyDown(Key.DOWN)&&keyTimer.isTimeUp()){ + // reset keyTimer to wait out its set wait time, + // which in this class is set to 200ms + keyTimer.reset(); + + // move currentMenuItemHovered to the next option + currentMenuItemHovered++; + } +``` + +## Graphics + +The background of the menu screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. + +The little blue square moves based on the value of `currentMenuItemHovered` (which changes value when up or down is pressed) to be in front of the currently hovered item's text. The text that is hovered over will also change color to gold while the one not hovered will change color to black. + +The menu item text ("Play Game" and "Credits") use the `SpriteFont` class and are defined in the `initialize` method (variables `playGame` and `credits`. + +## How to add a new menu item + +Honestly, this class was not made the best; it was neglected because the vast majority of my development time went into the actual platformer game. The menu works fine, but it is not as modular as it could be. Regardless, adding a new menu item is easy, but it will be a bit tedious to space everything out correctly, add the necessary logic checks for when that menu item is selected, and getting the little blue square graphic to move to the correct spot on hover. + +^^ This was done by Fall 2021 Team A2 <3 \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/narrative-screen.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/narrative-screen.md similarity index 66% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/narrative-screen.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/narrative-screen.md index f3e7f0b..086b6f5 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/narrative-screen.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/narrative-screen.md @@ -1,20 +1,16 @@ --- -layout: default -title: Narrative Screen -nav_order: 7 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/NarrativeScreen +layout: default title: Narrative Screen nav_order: 7 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/NarrativeScreen --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -32,5 +28,4 @@ The narrative screen's only real job is to allow the player to see the story for ## Graphics -The background of the narrative screen uses a `Map` specifically made for it in the `Maps` package, which is the same type of `Map` class which -is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. +The background of the narrative screen uses a `Map` specifically made for it in the `Maps` package, which is the same type of `Map` class which is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/options-screen.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/options-screen.md similarity index 58% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/options-screen.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/options-screen.md index d8d70ce..f592ffd 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/options-screen.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/options-screen.md @@ -1,20 +1,16 @@ --- -layout: default -title: Options Screen -nav_order: 6 -parent: Screens -grand_parent: Game Code Details -permalink: /GameCodeDetails/Screens/OptionsScreen +layout: default title: Options Screen nav_order: 6 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/OptionsScreen --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -28,12 +24,10 @@ The class file for it is `OptionsScreen.java` which can be found in the `Screens ## Functionality -The options screen's only real job is to allow the player to select between updating volume level and the image the cat displays. -Upon selecting an option, `OptionsScreen` will change `ScreenCoordinator's` game state which will force it to update the volume/cat. +The options screen's only real job is to allow the player to select between updating volume level and the image the cat displays. Upon selecting an option, `OptionsScreen` will change `ScreenCoordinator's` game state which will force it to update the volume/cat. ## Graphics -The background of the screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which -is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. +The background of the screen uses a `Map` specifically made for it (`TitleScreenMap.java` in the `Maps` package), which is the same type of `Map` class which is used when actually playing the platformer game. While any image could have been used, I thought it'd be more fun to use a map as the background. diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/play-level-screen.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/play-level-screen.md new file mode 100644 index 0000000..a48d3fd --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/play-level-screen.md @@ -0,0 +1,93 @@ +--- +layout: default title: Play Level Screen nav_order: 4 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/PlayLevelScreen +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Play Level Screen + +The screen handles the logic and graphics related to playing the actual platformer game that is loaded when the "PLAY GAME" option is selected form the game's main menu. + +![Play Level Screen](../../../assets/images/game-screen-1.png) + +The class file for it is `PlayLevelScreen.java` which can be found in the `Screens` package. + +## Functionality + +This is the screen where the platformer game is actually played! The map and player are loaded and the game carries out from here until the level is beaten or the player dies. Despite the `PlayLevelScreen` class having to seemingly do so much, a vast majority of the platformer game code is abstracted away from it (mostly residing in the `Player` and `Map` classes) which keeps the screen's code pretty simple and easy to follow. + +The `PlayLevelScreenState` enum defined in the class is used to determine what the `PlayLevelScreen` should be doing at a specific point in time -- its "current state" is stored in the `playLevelScreenState` instance variable. There are several different states that the `PlayLevelScreen` can be in: + +- **RUNNING** -- platformer game is currently running (map is loaded, player can move around/jump, etc.) +- **LEVEL_COMPLETED** -- the level has been completed (beaten), which happens when the player hits the golden box at the end of the level +- **PLAYER_DEAD** -- the player has lost the level by being killed by an enemy (which happens if you touch an enemy) +- **LEVEL_WIN_MESSAGE** - after the player has beaten the level, a "level win message" is shown to the player -- this state is used after the LEVEL_COMPLETED state +- **LEVEL_LOSE_MESSAGE** - after the player has lost the level by being killed by an enemy, a "level lose message" is shown to the player -- this state is used after the PLAYER_DEAD state + +### Running State + +The `RUNNING` state is the default state that the `PlayLevelScreen` is set to when it first loads (this is done in the `initialize` method) + +As mentioned earlier, while this state does have the most going on considering it's the actual game itself being run, all the game code is abstracted away to the `Map` and `Player` classes, meaning this is the only thing `PlayLevelScreen` has to do for this state: + +`update` method: + +```java +player.update(); + map.update(player); +``` + +and + +`draw` method: + +```java +map.draw(graphicsHandler); + player.draw(graphicsHandler); +``` + +Basically, the `Map` and `Player` classes are updated and drawn each cycle, and they handle the rest of the work. The specific `Map` and `Player` class instance used for the level is defined in the `initialize` method -- at the moment this game currently only has one playable map (`TestMap.java` file in the `Map` package) and one player type (`Cat.java` file in the `Players` package). From there, the `PlayLevelScreen` just continually calls their `update` and `draw` methods to carry out the platformer game. The documentation for the `Map` class is located [here](../map.md), and for the `Player` class is located [here](../player.md). + +When in this state, the platformer game can be played! + +![game-screen-1.gif](../../../assets/images/playing-level.gif) + +### Level Completed State + +When the player reaches the end of the level and hits the gold block, the level is "completed" and the `PlayLevelScreen's` state is changed to `LEVEL_COMPLETED`. Note that this state change is actually triggered by the `Player`, which calls the `PlayLevelScreen's` `onLevelCompleted` +method when it has beaten the level in order for `PlayLevelScreen` to know to change states. From there, `PlayLevelScreen` changes state again to the `LEVEL_WIN_MESSAGE` state and shows the "Level Cleared" screen for a short amount of time before bringing the player back to the game's main menu. + +![completing-level.gif](../../../assets/images/completing-level.gif) + +The "Level Cleared" screen is a separate `Screen` class (`LevelClearedScreen.java`) whose only job is to paint the screen black and show the "Level Cleared" text. The `PlayLevelScreen` sets up and loads the `LevelClearedScreen` from within itself, rather than making a separate entry in the `ScreenCoordinator` class -- this is important in order to not bloat the `ScreenCoordinator` class, as it should really only be used for the "core" screens of the game. While it may seem to not make much sense to have created an entire separate screen class for `LevelClearedScreen` +for such a tiny amount of functionality, it's important for keeping the game code organized -- if in the future the graphics for the level cleared screen were to get more complex and involved, it's best to keep it separate and out of the `PlayLevelScreen` class to make the graphic drawing easier to work with and prevent `PlayLevelScreen` from becoming bloated. + +### Player Dead State + +When the player dies in the level from touching an enemy, the level is "lost" and the `PlayLevelScreen's` state is changed to `PLAYER_DEAD`. Note that this state change is actually triggered by the `Player`, which calls the `PlayLevelScreen's` `onDeath` +method when it has died in a level in order for `PlayLevelScreen` to know to change states. From there, `PlayLevelScreen` changes state again to the `LEVEL_LOSE_MESSAGE` state and shows the "You lose!" screen. The "You lose" screen allows the player to press the space key to restart the level or the escape key to go back to the main menu. + +![losing-level.gif](../../../assets/images/losing-level.gif) + +The "You lose!" screen is a separate `Screen` class (`LevelLoseScreen.java`) which paints the screen black and shows the "You lose!" text, as well as the text with instructions for what the player can do from this screen (press the space key to restart the level, or press the escape key to go back to the main menu). The `LevelLoseScreen` class handles detecting those key inputs and sets `ScreenCoordinator's` game state accordingly based on what the user presses -- which is essentially just this: + +```java +if(Keyboard.isKeyDown(Key.SPACE)){ + playLevelScreenOld.resetLevel(); + }else if(Keyboard.isKeyDown(Key.ESC)){ + playLevelScreenOld.goBackToMenu(); + } +``` + +The `PlayLevelScreen` class instance is passed into the `LevelLoseScreen` class as well, and as you can see above the +`PlayLevelScreen` exposes methods for `resetLevel` and `goBackToMenu` to make things easier. \ No newline at end of file diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/screen-coordinator.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/screen-coordinator.md new file mode 100644 index 0000000..fe009f9 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/ScreensSubSections/screen-coordinator.md @@ -0,0 +1,62 @@ +--- +layout: default title: Screen Coordinator nav_order: 1 parent: Screens grand_parent: Game Code Details permalink: /GameCodeDetails/Screens/ScreenCoordinator +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Screen Coordinator + +## What is the Screen Coordinator? + +The `ScreenCoordinator` class in the `Game` package is integral for bringing the entirety of the game's code together. Its job is to dictate which section (screen) of the game to load at a given time. This class is attached to the engine's game loop in the `Game.java` file, and from there it coordinates what happens and when it happens. Each screen contains code for updating game logic and drawing graphics for a piece of the game. + +## What Screens can the Screen Coordinator currently load? + +Currently, there are three main screens that the `ScreenCoordinator` class can load: the menu screen, the credits screen, and the play level screen. The menu screen is always loaded at the start of the game (which you see when the game first starts up): + +![menu-screen.png](../../../assets/images/menu-screen.png) + +When the "Play Game" option on the menu screen is selected, the `ScreenCoordinator` class responds by loading the play level screen (which starts the platformer game level): + +![game-screen-1.png](../../../assets/images/game-screen-1.png) + +And finally, when the "Credits" option on the menu screen is selected, the `ScreenCoordinator` class responds by predictably loading the credits screen: + +![credits-screen.png](../../../assets/images/credits-screen.png) + +The `ScreenCoordinator` can support as many screens as necessary -- there is no limit! + +## Game State + +The `ScreenCoordinator` class has an instance variable for keeping track of the current game state. This `gameState` variable can be of any type defined in the `GameState` enum, which can be found in the `GameState.java` file located in the `Game` package. + +The current states defined in the `GameState` enum are `MENU`, `LEVEL`, `CREDITS`, which all coincide with a specific screen. Based on the value of the `gameState` instnace variable, the `ScreenCoordinator` will choose to load its corresponding screen. How this is done can be seen in the below snippet of `ScreenCoordinator's` `update` method: + +```java +switch(gameState){ + case MENU: + currentScreen=new MenuScreen(this); + break; + case LEVEL: + currentScreen=new PlayLevelScreen(this); + break; + case CREDITS: + currentScreen=new CreditsScreen(this); + break; + } +``` + +The `ScreenCoordinator` will only change screens when it detects that its `gameState` has been changed. The class exposes a method `setGameState` which any other class can use to change the current game state and force `ScreenCoordinator` to load a different screen. As you can see in the above code snippet, `ScreenCoordinator` is passing an instance of itself into each screen instance (e.g. `MenuScreen`). This allows those screen classes to set the game state of `ScreenCoordinator` when necessary. An example of where this is used is when the `MenuScreen` is loaded and the player selects the "Play Game" option -- this causes the `MenuScreen` to set `ScreenCoordinator's` game state to `LEVEL`, which triggers it to load the `PlayLevelScreen` class (as shown in the above `switch` statement). + + + diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/game-object.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/game-object.md new file mode 100644 index 0000000..c720202 --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/game-object.md @@ -0,0 +1,77 @@ +--- +layout: default title: Game Object nav_order: 2 parent: Game Code Details has_children: true permalink: /GameCodeDetails/GameObject +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Game Object + +## What is a Game Object? + +Inspired by the [Unity](https://unity.com/) game engine, the class `GameObject` (along with the entire package `GameObject`) was created as a sort of "one stop shop" for creating game entities. The `GameObject` package contains a series of classes that all work together which ultimately build up to the `GameObject` class containing all necessary information and functionality required for a map entity, which makes it quick and easy to use without requiring the code logic used to be rewritten again and again. + +In this game, the `GameObject` class is subclassed by every map entity, which includes the [player](./player.md), and any subclasses of `MapEntity` which include [enemies](./MapSubSections/enemies.md), [npcs](./MapSubSections/npcs.md), and [map tiles](/GameDetails/Map/MapTilesAndTilesets) (as well as +[enhanced map tiles](./MapSubSections/enhanced-map-tiles.md)). That means that all of these subclasses (and their subclasses) include all functionality of the `GameObject` class under the hood. + +## Features of the GameObject class + +The `GameObject` class provides the following features for every subclass: + +1. Sprite logic (loading in images, scaling images, flipping images, defining a bounding box which can be thought of as a "hurtbox") +2. Animation support and logic (and the ability to switch between different animations) +3. Graphical support -- will automatically handle drawing graphics in correct location based on map/camera position +4. Collision detection with other GameObjects, including collision detection with map set pieces (like map tiles) + +## Subclassing GameObject + +The `GameObject` class can be subclassed by any entity regardless of what it is or what it will be doing in game, and it contains many constructors to support a variety of needs. Generally, each `GameObject` at the very least contains an image (sprite) that is to be attached to a rectangle (x, y, width, height) to be displayed and moved around in game. + +## GameObject Package Overview + +As mentioned earlier, the classes in the `GameObject` package work together and build up to the `GameObject` class. + +### Rectangle class + +The `Rectangle` class is the "base", which is a means to implement the (x, y, width, height) instance variables which every +`GameObject` needs (it also contains some simple "rectangle" math such as detecting collision and moving a rectangle in different directions). + +### Sprite class + +Then, the `Sprite` class extends the `Rectangle` class. The `Sprite` class adds functionality on for storing an image (one image). it also optionally allows defining a bounding box `bounds` which is a `Rectangle` used in collision detection -- often times when working with collision detection, a sprite doesn't want its entire image to be able to be "touched". An example of this can be seen in the following Megaman sprites, where the bounding box where collisions can be detected is much smaller than Megaman itself: + +![megaman-bounds.png](../../assets/images/megaman-bounds.png) + +When working with 2D games, it's common to leave off limbs like in the above picture and have the core body be able to be detected for collision. + +### AnimatedSprite class + +Then, the most complex class of the group pops up -- `AnimatedSprite`. This does NOT extend `Sprite`. Instead, it contains a `HashMap` +which maps a string value (animation name) to an array of `Frame` type (animation data and graphics). + +An array of `Frames` is what defines an animation. Each `Frame` in the array is one frame of an animation, and the game will cycle through that animation continuously (and upon reaching the last frame will go back to the first). The `Frame` class extends `Sprite` with an added `delay` instance variable which specifies how long an animation frame is to last before switching over to the next frame. `Frames` in the same animation can have different `delay` values. + +This class provides functionality for any `GameObject` to define their own animations as desired, give the animations names, play out the `GameObject's` current animation during the game, and allow for the current animation to be changed at any time. Any subclass of `GameObject` that overrides `getAnimations` (which is where animations are defined) utilize the `AnimatedSprite` class's functionality. + +Something important to note about this class is that even though it doesn't directly extend `Sprite`, it is structured to be treated in game exactly as a `Sprite` would. At any given time, the `AnimatedSprite` class will have a current animation with a current frame, and this current frame is a `Sprite` with a location, collision potential, etc. While an `AnimatedSprite` is a "collection" of various +`Sprites` at the end of the day, it does only have one "active" `Sprite` out at a time. As a result, the `AnimatedSprite` class redefines nearly every method from the `Sprite` and `Rectangle` classes to have them utilize the `currentFrame` variable's information. As a result, an `AnimatedSprite` has all the behavior of the `Sprite` class without needing to extend it. I know this sounds a bit silly, but I originally did have it extend from the `Sprite` class and after some time I found that the drawbacks for doing that greatly outweigh the minor inconvenience of having to redefine some methods. + +### GameObject class + +And finally, the `GameObject` class extends from the `AnimatedSprite` class. The `GameObject` is to be given an instance of the current `Map` being used through its `setMap` method after creation in order for it to provide functionality based on the `Map`, including move methods that handle map tile collisions (`moveXHandleCollision` and `moveYHandleCollision`) and special `draw` logic to convert "map coordinates" to "screen coordinates". The `GameObject` class also does some sneaky constructor crafting in order to allow it to be instantiated both as an animated sprite (nearly every entity uses this such as the `Player` and enemies like `BugEnemy`) +or just as one sole sprite (which is done in the `HorizontalMovingPlatform` class). + +More details on `GameObject's` collision detection and handling can be found [here](./PlayerSubSections/collision-detection.md). + +Finally, `GameObject` adds two methods `getCalibratedXLocation` and `getCalibratedYLocation` which are used in the `draw` cycle to properly draw the `GameObject` to the screen in the correct location with account to how much the map's camera has moved. `GameObjects` should utilize these methods in their `draw` methods to ensure other graphics are drawn relative to them and still maintain proper draw location integrity. The `Walrus` class does this when drawing its speech bubble when talked to, which ensures as the camera moves the speech bubble graphics will remain in the correct spot relative to the walrus game object's location. + +Note that if no `Map` class instance is passed in a `GameObject` class using the `setMap` method, the `GameObject` will draw itself at its exact location on the screen rather than accounting for the map camera moving. This can be desired behavior for certain graphics, while not for others. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/game-patterns.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/game-patterns.md similarity index 51% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/game-patterns.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/game-patterns.md index b3f9b1d..9752c2f 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/game-patterns.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/game-patterns.md @@ -1,19 +1,16 @@ --- -layout: default -title: Game Patterns -nav_order: 7 -parent: Game Code Details -permalink: /GameCodeDetails/GamePatterns +layout: default title: Game Patterns nav_order: 7 parent: Game Code Details permalink: /GameCodeDetails/GamePatterns --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -23,27 +20,19 @@ Here are details on some common design patterns that can be seen around the code ## Key Locker -The game engine provides a `KeyLocker` class to help keep track of locked keys. This doesn't actually lock a key from being pressed -on the keyboard entirely throughout the entire game, it is instead just a "management" tool for a class to determine if it should listen for a key -press or not. The concept of "locking" a key means it should no longer count as a key press until it is "unlocked". It's a lot simpler than it sounds -I promise, it's hard to word this correctly. +The game engine provides a `KeyLocker` class to help keep track of locked keys. This doesn't actually lock a key from being pressed on the keyboard entirely throughout the entire game, it is instead just a "management" tool for a class to determine if it should listen for a key press or not. The concept of "locking" a key means it should no longer count as a key press until it is "unlocked". It's a lot simpler than it sounds I promise, it's hard to word this correctly. -Generally, the `KeyLocker` class is useful for forcing the player to press and release a key each time to perform a specific action -rather than being able to hold the key down. For example, the player jumping in game requires that the up key be pressed each time, -and does not allow you to hold it down for continuous jumping. +Generally, the `KeyLocker` class is useful for forcing the player to press and release a key each time to perform a specific action rather than being able to hold the key down. For example, the player jumping in game requires that the up key be pressed each time, and does not allow you to hold it down for continuous jumping. -The pattern for setting this up is really simple. First, a `KeyLocker` class instance is declared as an instance variable so it -can be used anywhere in the class during an `update` cycle: +The pattern for setting this up is really simple. First, a `KeyLocker` class instance is declared as an instance variable so it can be used anywhere in the class during an `update` cycle: ```java -protected KeyLocker keyLocker = new KeyLocker(); +protected KeyLocker keyLocker=new KeyLocker(); ``` This can be seen in the `Player` class, `MenuScreen` class, and the `CreditsScreen` class. -Then, to "lock" a key, you can use `KeyLocker's` `lockKey` method to add the key to the locker. The below example will "lock" the space key. Remember -that this does NOT "lock" it throughout the entire engine or stop keyboard detection, it is just a management tool for the class -to keep track of which keys it has personally locked. +Then, to "lock" a key, you can use `KeyLocker's` `lockKey` method to add the key to the locker. The below example will "lock" the space key. Remember that this does NOT "lock" it throughout the entire engine or stop keyboard detection, it is just a management tool for the class to keep track of which keys it has personally locked. ```java keyLocker.lockKey(Key.SPACE); @@ -60,60 +49,54 @@ To check if a key is locked, the `isKeyLocked` method can be used: ```java keyLocker.lockKey(Key.SPACE); -if (keyLocker.isKeyLocked(Key.SPACE)) { + if(keyLocker.isKeyLocked(Key.SPACE)){ -} + } ``` -Put all together, and this is how the `KeyLocker` can be used to force a key be pressed and released rather than detecting while -the key is held down: +Put all together, and this is how the `KeyLocker` can be used to force a key be pressed and released rather than detecting while the key is held down: ```java -public void update() { - // if space key is released, unlock space key - if (Keyboard.isKeyUp(Key.SPACE)) { +public void update(){ + // if space key is released, unlock space key + if(Keyboard.isKeyUp(Key.SPACE)){ keyLocker.unlockKey(Key.SPACE); - } - - // if space is pressed and space is not locked - if (Keyboard.isKeyDown(Key.SPACE) && !keyLocker.isKeyLocked(Key.SPACE) { + } + + // if space is pressed and space is not locked + if(Keyboard.isKeyDown(Key.SPACE)&&!keyLocker.isKeyLocked(Key.SPACE){ player.jump(); - + // lock space key keyLocker.locKey(Key.SPACE); - } -} + } + } ``` -The menu screens use `KeyLocker` a lot to prevent spamming certain keys, as it can make the menu really hard to navigate if a button can be held down. -This is especially true due to how quickly (in real time) each game loop iteration happens -- without the `KeyLocker` pattern shown above, one key press -can easily count as multiple key presses due to the game loop iterating quicker than a human can lift their finger off the key. Sometimes this is fine, -like when moving a player throughout the level, but sometimes this is not desired which is what this pattern resolves. +The menu screens use `KeyLocker` a lot to prevent spamming certain keys, as it can make the menu really hard to navigate if a button can be held down. This is especially true due to how quickly (in real time) each game loop iteration happens -- without the `KeyLocker` pattern shown above, one key press can easily count as multiple key presses due to the game loop iterating quicker than a human can lift their finger off the key. Sometimes this is fine, like when moving a player throughout the level, but sometimes this is not desired which is what this pattern resolves. ## Stopwatch -The `Stopwatch` class, located in the `Utils` folder, will act as a stopwatch in game to allow for timed events. This is used -in several different classes, and is also used for the game's overall animation process to move from frame to frame based on a set delay time. +The `Stopwatch` class, located in the `Utils` folder, will act as a stopwatch in game to allow for timed events. This is used in several different classes, and is also used for the game's overall animation process to move from frame to frame based on a set delay time. -Like the `KeyLocker`, `Stopwatch` classes tend to be instantiated as an instance variable, and many of them can be used in the same class -if there are multiple processes that need to be timed. +Like the `KeyLocker`, `Stopwatch` classes tend to be instantiated as an instance variable, and many of them can be used in the same class if there are multiple processes that need to be timed. ```java -Stopwatch timer = new Stopwatch(); +Stopwatch timer=new Stopwatch(); ``` -Upon initializing a `Stopwatch` class, the method `setWaitTime` is where the number of milliseconds to time is specified. 1000 milliseconds in a second -in case you forgot. +Upon initializing a `Stopwatch` class, the method `setWaitTime` is where the number of milliseconds to time is specified. 1000 milliseconds in a second in case you forgot. ```java timer.setWaitTime(10000); // wait for 10 seconds ``` The `isTimeUp` method will return true or false based on if the amount of seconds since calling `setWaitTime` have passed by. + ```java -if (timer.isTimeUp()) { - -} +if(timer.isTimeUp()){ + + } ``` The `reset` method will reset the timer: @@ -130,26 +113,21 @@ Sometimes, the `Stopwatch` class will be used in a way similar to `KeyLocker`, b // some constructor somewhere timer.setWaitTime(1000); -public void update() { - if (Keyboard.isKeyDown(Key.SPACE) && timer.isTimeUp()) { +public void update(){ + if(Keyboard.isKeyDown(Key.SPACE)&&timer.isTimeUp()){ timer.reset(); // other action logic... - } -} + } + } ``` The above forces 1 second to pass by each time for the space key detection to cause an action to occur even if it is held down. ## Builder Pattern -I use the Builder Pattern around the code base, and all builder classes are defined in the `Builders` package. Below goes -into detail on what the Builder Pattern is and how it makes Java programming life easier. +I use the Builder Pattern around the code base, and all builder classes are defined in the `Builders` package. Below goes into detail on what the Builder Pattern is and how it makes Java programming life easier. -The Builder Pattern is a code pattern that is used in the Java programming language a lot out of necessity because there are no -default parameters in the language. Java is one of the only modern day programming languages to not have this feature. Default parameters (also often called -named parameters or optional parameters) are the ability to specify a method parameter and give it a default value, -and allow any class to optionally pass in a value for that value -- if no value is passed in, that parameter will just use its default value. -An example of how this looks in the C# programming language is below: +The Builder Pattern is a code pattern that is used in the Java programming language a lot out of necessity because there are no default parameters in the language. Java is one of the only modern day programming languages to not have this feature. Default parameters (also often called named parameters or optional parameters) are the ability to specify a method parameter and give it a default value, and allow any class to optionally pass in a value for that value -- if no value is passed in, that parameter will just use its default value. An example of how this looks in the C# programming language is below: ```cs public void printMessage(String message = "Hello") { @@ -163,47 +141,36 @@ It can then be called with an argument for message: printMessage(message: "HELLO THERE!"); ``` -It can also be called with no arguments. When doing this, the `printMessage` method will use the default value for `message`, -which is "Hello". +It can also be called with no arguments. When doing this, the `printMessage` method will use the default value for `message`, which is "Hello". ```cs printMessage(); ``` -Such a nice powerful tool but Java does not have it. This causes a lot of problems, mostly because there are A LOT of situations where -default parameters really help make code more clear, and also prevent having to make 1000 of the same named methods with one parameter difference -each. Lastly, when constructors in Java get really long, the lack of being able to specify the parameter name to the value when calling it -makes it REALLY hard to know which value goes well. For example: +Such a nice powerful tool but Java does not have it. This causes a lot of problems, mostly because there are A LOT of situations where default parameters really help make code more clear, and also prevent having to make 1000 of the same named methods with one parameter difference each. Lastly, when constructors in Java get really long, the lack of being able to specify the parameter name to the value when calling it makes it REALLY hard to know which value goes well. For example: ```java // constructor -public Cat(String name, String nickname, int age, String color, int weight, boolean hasRabiesShot, boolean hasDistemperShot) { - -} +public Cat(String name,String nickname,int age,String color,int weight,boolean hasRabiesShot,boolean hasDistemperShot){ + + } ``` -Look what happens when I call this constructor -- it's impossible just by looking at the line to determine which value is what, -especially if you haven't looked at the code in a hot minute: +Look what happens when I call this constructor -- it's impossible just by looking at the line to determine which value is what, especially if you haven't looked at the code in a hot minute: ```java -Cat cat = new Cat("Callie", "Beeboo", 2, "tortoiseshell", 7, true, true); +Cat cat=new Cat("Callie","Beeboo",2,"tortoiseshell",7,true,true); ``` -Which argument is age and which one is weight? Name vs nickname? Rabies shot vs distemper shot? -It's hard to tell without looking back at the `Cat` class, and even then lining each argument up is a pain. Imagine -if these were all more complicated data types as well...it really is a huge issue with this programming language. +Which argument is age and which one is weight? Name vs nickname? Rabies shot vs distemper shot? It's hard to tell without looking back at the `Cat` class, and even then lining each argument up is a pain. Imagine if these were all more complicated data types as well...it really is a huge issue with this programming language. -The builder pattern attempts to alleviate this issue by creating a class called a "builder" class which will "build" and then -instantiate/initialize a class for you. For my `Cat` class example, I would create a `CatBuilder` class SEPARATE from the `Cat` class. -The `CatBuilder` class's job is to help setup and instantiate/intialize a `Cat` object, and additionally this class can define its own -default values so not every single parameter needs to be passed in. Lastly, each parameter is set using a specific method call, -meaning it's super obvious to determine which argument goes where. +The builder pattern attempts to alleviate this issue by creating a class called a "builder" class which will "build" and then instantiate/initialize a class for you. For my `Cat` class example, I would create a `CatBuilder` class SEPARATE from the `Cat` class. The `CatBuilder` class's job is to help setup and instantiate/intialize a `Cat` object, and additionally this class can define its own default values so not every single parameter needs to be passed in. Lastly, each parameter is set using a specific method call, meaning it's super obvious to determine which argument goes where. -Below is an example of what a `CatBuilder` class can look like. It is okay if you don't understand this class in its entirety, the code -is a bit strange: +Below is an example of what a `CatBuilder` class can look like. It is okay if you don't understand this class in its entirety, the code is a bit strange: ```java public class CatBuilder { + private String name; private int age; @@ -241,7 +208,7 @@ public class CatBuilder { this.weight = weight; return this; } - + // setter for has rabies shot public CatBuilder hasRabiesShot(boolean hasShot) { this.hasRabiesShot = hasShot; @@ -260,116 +227,89 @@ public class CatBuilder { } ``` -Whew, what a weird looking class. What is going on here? Well, the result is that the actual call to the `Cat` class's -constructor is now abstracted away behind this `build` method. This class as a result of its structure allows us to create a new +Whew, what a weird looking class. What is going on here? Well, the result is that the actual call to the `Cat` class's constructor is now abstracted away behind this `build` method. This class as a result of its structure allows us to create a new `Cat` in a much cleaner way: ```java -Cat cat = new CatBuilder("Callie", 2) - .withNickname("Beeboo") - .withColor("tortoiseshell") - .withWeight(7) - .hasRabiesShot(true) - .hasDistemperShot(true) - .build(); +Cat cat=new CatBuilder("Callie",2) + .withNickname("Beeboo") + .withColor("tortoiseshell") + .withWeight(7) + .hasRabiesShot(true) + .hasDistemperShot(true) + .build(); ``` -With this pattern, it's very obvious which argument goes to which parameter. Additionally, the actual call to the `Cat` constructor is abstracted -away behind the `build` method, so the messy call is hidden. Finally, default parameters now exist -- if I left off `hasRabiesShot(true)`, -the `CatBuilder` class will just default it to false and construct the `Cat` using that. Even just immediately doing a `build` without -any of the other optional parameters works, as all those values have default values (e.g. `nickname` would be "N/A", `weight` would be -1, etc.). +With this pattern, it's very obvious which argument goes to which parameter. Additionally, the actual call to the `Cat` constructor is abstracted away behind the `build` method, so the messy call is hidden. Finally, default parameters now exist -- if I left off `hasRabiesShot(true)`, the `CatBuilder` class will just default it to false and construct the `Cat` using that. Even just immediately doing a `build` without any of the other optional parameters works, as all those values have default values (e.g. `nickname` would be "N/A", `weight` would be -1, etc.). ```java -Cat cat = new CatBuilder("Callie", 2).build(); +Cat cat=new CatBuilder("Callie",2).build(); ``` -One more thing this pattern does is removes the need for multiple constructors, which as you may have seen from the `GameObject` class -and its subclasses that they have A LOT of constructors due to Java having no default parameters. Unfortunately it wasn't feasible to make -a builder class for every single subclass that extends from `GameObject` so constructor hell exists over there. +One more thing this pattern does is removes the need for multiple constructors, which as you may have seen from the `GameObject` class and its subclasses that they have A LOT of constructors due to Java having no default parameters. Unfortunately it wasn't feasible to make a builder class for every single subclass that extends from `GameObject` so constructor hell exists over there. -The builder pattern is used in the code base where the constructor for a class was getting too crazy, which commonly happens -with game code due to how much setup is required for resources like graphics. It really isn't scary once you know what it's doing. +The builder pattern is used in the code base where the constructor for a class was getting too crazy, which commonly happens with game code due to how much setup is required for resources like graphics. It really isn't scary once you know what it's doing. -A nice side effect of the bulider pattern also is it allows an object's instantiation to be delayed, as the `build` method -can be called whenever to actually instantiate the object. This technique is used in the `Map` class when loading a map file -- the `Tileset` defines -all tiles using the `MapTileBuilder`, but it doesn't `build` them and instead lets the `Map` class bulid them since they require information from the `Map` class -to finish their creation. +A nice side effect of the bulider pattern also is it allows an object's instantiation to be delayed, as the `build` method can be called whenever to actually instantiate the object. This technique is used in the `Map` class when loading a map file -- the `Tileset` defines all tiles using the `MapTileBuilder`, but it doesn't `build` them and instead lets the `Map` class bulid them since they require information from the `Map` class to finish their creation. In hte `Builders` package, there are four builders defined, but the only two that you will really see come up are `FrameBuilder` (for creating a `Frame` object instance) and `MapTileBuilder` (for creating a `MapTile` object instance). ## Observer Pattern -I use the Observer Pattern one time to enable the `Player` class to trigger events in the `PlayLevelClass` when the `Player` has -either completed a level or died, which then lets the `PlayerLevelClass` react to those events. +I use the Observer Pattern one time to enable the `Player` class to trigger events in the `PlayLevelClass` when the `Player` has either completed a level or died, which then lets the `PlayerLevelClass` react to those events. -I'm not going to go into much detail on this pattern since it's only used one time and it's not all that complex. Basically, -an interface named `PlayerListener` is defined in the `Level` class, and any class that implements this interface must implement the methods +I'm not going to go into much detail on this pattern since it's only used one time and it's not all that complex. Basically, an interface named `PlayerListener` is defined in the `Level` class, and any class that implements this interface must implement the methods `onLevelCompleted` and `onDeath`. A class would implement this interface to "listen" to events from the `Player` class. Then in the `Player` class, an `ArrayList` instance variable of type `PlayerListener` is defined (the variable's name is `listeners`). There is also an -`addListener` method which will add a `PlayerListener` to the player's list of listeners. The `PlayLevelScreen` implements `PlayerListener` and -then passes itself in to the `Player` using the `addListener` method: +`addListener` method which will add a `PlayerListener` to the player's list of listeners. The `PlayLevelScreen` implements `PlayerListener` and then passes itself in to the `Player` using the `addListener` method: ```java // create new player -this.player = new Cat(map.getPlayerStartPosition().x, map.getPlayerStartPosition().y); -this.player.setMap(map); +this.player=new Cat(map.getPlayerStartPosition().x,map.getPlayerStartPosition().y); + this.player.setMap(map); // PlayLevelScreen adds itself to the Player class as a "listener" -this.player.addListener(this); + this.player.addListener(this); ``` That's all the setup that's needed. Now whenever the player wants, it can "trigger" events for its listeners by calling their -`onLevelCompleted` or `onDeath` method where appropriate. For example, the `Player` does this in its `updateLevelCompleted` method once it -has finished playing out the "win" animation to let the `PlayLevelScreen` know that a level has been completed: +`onLevelCompleted` or `onDeath` method where appropriate. For example, the `Player` does this in its `updateLevelCompleted` method once it has finished playing out the "win" animation to let the `PlayLevelScreen` know that a level has been completed: ```java -for (PlayerListener listener : listeners) { - listener.onLevelCompleted(); -} +for(PlayerListener listener:listeners){ + listener.onLevelCompleted(); + } ``` -And then in the `PlayLevelScreen`, which HAS to have an `onLevelCompleted` method because it implements `PlayerListner`, the method -looks like this: +And then in the `PlayLevelScreen`, which HAS to have an `onLevelCompleted` method because it implements `PlayerListner`, the method looks like this: ```java -@Override -public void onLevelCompleted() { - playLevelScreenState = PlayLevelScreenState.LEVEL_COMPLETED; -} +@Override public void onLevelCompleted(){ + playLevelScreenState=PlayLevelScreenState.LEVEL_COMPLETED; + } ``` -Essentially it just updates its own level state here and then its `update` cycle logic will see that change and perform the -desired actions (in this case, will load the level win message screen). +Essentially it just updates its own level state here and then its `update` cycle logic will see that change and perform the desired actions (in this case, will load the level win message screen). -Now, while I could have just passed in the `PlayLevelScreen` instance to the `Player` class, I didn't want to do that because it really -didn't belong in the `Player` class. The `PlayLevelScreen` has nothing to do with the `Player` class, and if I ever wanted to use the `Player` class -somewhere else/in another program, that dependency on the `PlayLevelScreen` would not be welcomed. Additionally, if the `Player` were -to be used on a different screen (like maybe the menu screen, like how some games show the character walking around), the `PlayLevelScreen` +Now, while I could have just passed in the `PlayLevelScreen` instance to the `Player` class, I didn't want to do that because it really didn't belong in the `Player` class. The `PlayLevelScreen` has nothing to do with the `Player` class, and if I ever wanted to use the `Player` class somewhere else/in another program, that dependency on the `PlayLevelScreen` would not be welcomed. Additionally, if the `Player` were to be used on a different screen (like maybe the menu screen, like how some games show the character walking around), the `PlayLevelScreen` would be unavailable to be passed in. Instead, with this pattern, ANY class can be a `PlayerListener` and have events for `onLevelCompleted` and `onPlayerDeath`, and the `Player` is free to trigger them by just calling those methods on its `listeners` when its ready to. -Like I said, this pattern is only used in that one place, so you won't see it or have to interact with it much. The Observer Pattern -is actually really easy once you take a look at it (assuming you understand how interfaces work) and is very commonly used elsewhere in programming, -notably Android development relies heavily on it. +Like I said, this pattern is only used in that one place, so you won't see it or have to interact with it much. The Observer Pattern is actually really easy once you take a look at it (assuming you understand how interfaces work) and is very commonly used elsewhere in programming, notably Android development relies heavily on it. -[This video](https://www.youtube.com/watch?v=WRkw0l72BL4) provides a good overview of the Observer Pattern and why it's important -using real-world examples. +[This video](https://www.youtube.com/watch?v=WRkw0l72BL4) provides a good overview of the Observer Pattern and why it's important using real-world examples. ## Utility Enums There are a two enums defined in the `Utils` package that several classes use: `Direction` and `AirGroundState`. -`Direction` has four possible values: `LEFT`, `RIGHT`, `UP`, and `DOWN`. It is used in various places around the program. Since -a 2D space only has those four directions, it's a very nice data type to have available. +`Direction` has four possible values: `LEFT`, `RIGHT`, `UP`, and `DOWN`. It is used in various places around the program. Since a 2D space only has those four directions, it's a very nice data type to have available. -The `AirGroundState` enum is used mainly for map entities that can have a concept of being on ground vs in the air (such as the `Player` when -jumping/falling or various enemies). This enum only has two possible values: `GROUND` and `AIR`. Nice and simple but very handy! +The `AirGroundState` enum is used mainly for map entities that can have a concept of being on ground vs in the air (such as the `Player` when jumping/falling or various enemies). This enum only has two possible values: `GROUND` and `AIR`. Nice and simple but very handy! ## Utility Point There is a `Point` class in the `Utils` package (do not confuse this with Java's built in `Point` class -- this is a custom `Point` class!) -that will store "point" data (x, y) as floats and also contains several methods for performing "point math" such as adding -and subtracting points. It's used in several places over Java's standard `Point` class so these additional methods are always available -whenever needed. \ No newline at end of file +that will store "point" data (x, y) as floats and also contains several methods for performing "point math" such as adding and subtracting points. It's used in several places over Java's standard `Point` class so these additional methods are always available whenever needed. \ No newline at end of file diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/level.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/level.md similarity index 60% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/level.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/level.md index 2c55178..a3bc8dc 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/level.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/level.md @@ -1,19 +1,16 @@ --- -layout: default -title: Level -nav_order: 4 -parent: Game Code Details -permalink: /GameCodeDetails/Level +layout: default title: Level nav_order: 4 parent: Game Code Details permalink: /GameCodeDetails/Level --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -21,9 +18,7 @@ permalink: /GameCodeDetails/Level ## How does the platformer level work? -The `PlayLevelScreen` class (documentation for that class found [here](./ScreensSubSections/play-level-screen.md)) continually runs `update` and `draw` calls -on its instantiated `Map` and `Player` classes, which carries out the platformer level. The `Map` and `Player` classes -hold all the game code that brings the level together -- this is the most complicated aspect of this application (as expected) +The `PlayLevelScreen` class (documentation for that class found [here](./ScreensSubSections/play-level-screen.md)) continually runs `update` and `draw` calls on its instantiated `Map` and `Player` classes, which carries out the platformer level. The `Map` and `Player` classes hold all the game code that brings the level together -- this is the most complicated aspect of this application (as expected) but it isn't so bad once you get the hang of how the `Map` and `Player` classes work together and how the classes are structured. ![game-screen-1.gif](../../assets/images/playing-level.gif) @@ -36,6 +31,5 @@ Documentation for usage of the Map Editor can be found [here](). ## The Level package -The `Level` package in this project contains all the "core" classes and game logic necessary for the platformer game to play out. Many of the classes -found in here exist solely to be extended from by a different class to be built upon, so you can think of them as a template -- examples of this include the +The `Level` package in this project contains all the "core" classes and game logic necessary for the platformer game to play out. Many of the classes found in here exist solely to be extended from by a different class to be built upon, so you can think of them as a template -- examples of this include the `Player`, `Enemy`, `NPC`, `EnhancedMapTile`, and `Tileset` classes. \ No newline at end of file diff --git a/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/map.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/map.md new file mode 100644 index 0000000..e421b2a --- /dev/null +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/map.md @@ -0,0 +1,25 @@ +--- +layout: default title: Map nav_order: 5 parent: Game Code Details has_children: true permalink: /GameCodeDetails/Map +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Map + +## What does the Map class do? + +The `Map` class (found in the `Level` package) is responsible for everything on the screen during the platformer game except for the player character, which includes the map tile graphics, the camera (current view -- which part of the map is being shown on the screen), the enemies, and the npcs. It has a lot of duties to carry out, but a vast majority of the `Map` class is just "setup" -- once everything is ready to go, the actual game logic is pretty simple (it pretty much just calls `update` on every class and lets them decide what they want to do). + +Everything you see in the below gif except for the player character (the cat) is handled/coordinated by the `Map` class. + +![game-screen-1.gif](../../assets/images/playing-level.gif) diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/player.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/player.md similarity index 84% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/player.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/player.md index 8165a3c..f10a53b 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/player.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/player.md @@ -1,20 +1,16 @@ --- -layout: default -title: Player -nav_order: 6 -parent: Game Code Details -has_children: true -permalink: /GameCodeDetails/Player +layout: default title: Player nav_order: 6 parent: Game Code Details has_children: true permalink: /GameCodeDetails/Player --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -22,7 +18,7 @@ permalink: /GameCodeDetails/Player ## What does the Player class do? -The `Player` class (found in the `Level` package) is responsible for everything to do with the player (for this game, the player is the cat) +The `Player` class (found in the `Level` package) is responsible for everything to do with the player (for this game, the player is the cat) during the platformer game except for the player character, which includes the player's graphics, movement, and interactions with the map (such as collision detection). In the gif below, everything the cat is doing and how it is interacting with the map is handled by the `Player` class. diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/screens.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/screens.md similarity index 55% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/screens.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/screens.md index b75f3b8..1951b8d 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/screens.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/screens.md @@ -1,20 +1,16 @@ --- -layout: default -title: Screens -nav_order: 1 -parent: Game Code Details -has_children: true -permalink: /GameCodeDetails/Screens +layout: default title: Screens nav_order: 1 parent: Game Code Details has_children: true permalink: /GameCodeDetails/Screens --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -22,17 +18,13 @@ permalink: /GameCodeDetails/Screens ## What is a Screen? -This engine uses a concept of "screens" to split up sections of a game into logical separate classes. Games tend to be heavy on code, -so it is important to use screens as a means to organize different pieces of the game to keep the codebase maintainable and navigable. -Screens have their own instance variables and `update`/`draw` methods that are crafted specifically for its owned part of the game code. +This engine uses a concept of "screens" to split up sections of a game into logical separate classes. Games tend to be heavy on code, so it is important to use screens as a means to organize different pieces of the game to keep the codebase maintainable and navigable. Screens have their own instance variables and `update`/`draw` methods that are crafted specifically for its owned part of the game code. -The `ScreenCoordinator` class, which details on can be found [here](./ScreensSubSections/screen-coordinator.md), is the most important `Screen` in the codebase, -as it's job is to "coordinate" which other `Screens` are loaded at which times based on what needs to happen in the game. +The `ScreenCoordinator` class, which details on can be found [here](./ScreensSubSections/screen-coordinator.md), is the most important `Screen` in the codebase, as it's job is to "coordinate" which other `Screens` are loaded at which times based on what needs to happen in the game. ## What is the Screen class? -The `Screen` class is an abstract class provided by the engine, and all screens defined in the game are to extend it and implement its required methods -to ensure they are set up correctly. +The `Screen` class is an abstract class provided by the engine, and all screens defined in the game are to extend it and implement its required methods to ensure they are set up correctly. ## Screen class structure @@ -41,33 +33,31 @@ to be implemented: ```java public abstract class Screen { + public abstract void initialize(); + public abstract void update(Keyboard keyboard); + public abstract void draw(GraphicsHandler graphicsHandler); } ``` -The `ScreenCoordinator` class handles calling the `initialize`, `update`, and `draw` methods for you at the appropriate times in the game loop -for screens that it handles. `Screen` classes can load other screen themselves, in which case it is up to that `Screen` class to add it to -the game loop cycle properly (usually just by calling its `update` and `draw` calls as a part of its own cycle). The `initialize` method -"sets up" a screen and is usually called immediately after the screen is instantiated -- it is very similar to a constructor in this regard, however constructors -cannot be called again while it is often useful to call the `initialize` method again to "reset" a screen. An example of this is resetting a level from the beginning -by calling its `initialize` method and letting it handle re-setting everything up. +The `ScreenCoordinator` class handles calling the `initialize`, `update`, and `draw` methods for you at the appropriate times in the game loop for screens that it handles. `Screen` classes can load other screen themselves, in which case it is up to that `Screen` class to add it to the game loop cycle properly (usually just by calling its `update` and `draw` calls as a part of its own cycle). The `initialize` method +"sets up" a screen and is usually called immediately after the screen is instantiated -- it is very similar to a constructor in this regard, however constructors cannot be called again while it is often useful to call the `initialize` method again to "reset" a screen. An example of this is resetting a level from the beginning by calling its `initialize` method and letting it handle re-setting everything up. The `update` method is passed the `Keyboard` class so it can always be used to detect keyboard input, and the `draw` method is passed the `GraphicsHandler` class in order to paint to the engine's backing JPanel.] ## How to add a new Screen class? -Start by making a new class in the `Screens` package, where all game screens are defined. From there, extend the engine's base `Screen` class and -implement its methods. Below is an example if an `InstructionsScreen` were to be created: +Start by making a new class in the `Screens` package, where all game screens are defined. From there, extend the engine's base `Screen` class and implement its methods. Below is an example if an `InstructionsScreen` were to be created: ```java public class InstructionsScreen extends Screen { public void initialize() { - - } + + } public void update(Keyboard keyboard) { @@ -75,16 +65,13 @@ public class InstructionsScreen extends Screen { public void draw(GraphicsHandler graphicsHandler) { - } + } } ``` -From there, the code added to those methods should handle setting up the instructions screen resources, -the game logic for the instructions screen, and finally what graphics should be shown on the JPanel while the instruction screen -is loaded. +From there, the code added to those methods should handle setting up the instructions screen resources, the game logic for the instructions screen, and finally what graphics should be shown on the JPanel while the instruction screen is loaded. -A way for this screen to be loaded will also need to be added to the game logic, either adding it to the `ScreenCoordniator` or setting up an existing screen to handle it. -The `PlayLevelScreen` is the most bulky screen in this game, but it's a good example of how a screen can be loaded from another screen +A way for this screen to be loaded will also need to be added to the game logic, either adding it to the `ScreenCoordniator` or setting up an existing screen to handle it. The `PlayLevelScreen` is the most bulky screen in this game, but it's a good example of how a screen can be loaded from another screen (upon beating a level or dying in a level, the `LevelClearedScreen` or `LevelLoseScreen` is loaded up while the `PlayLevelScreen` is still active). diff --git a/docs/GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md similarity index 54% rename from docs/GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md rename to docs_archive/GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md index f5eb112..9330ee1 100644 --- a/docs/GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md +++ b/docs_archive/GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md @@ -1,19 +1,16 @@ --- -layout: default -title: Sprite Font -nav_order: 3 -parent: Game Code Details -permalink: /GameCodeDetails/SpriteFont +layout: default title: Sprite Font nav_order: 3 parent: Game Code Details permalink: /GameCodeDetails/SpriteFont --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -22,15 +19,13 @@ permalink: /GameCodeDetails/SpriteFont ## What is a sprite font? A sprite font is what it sounds like -- a "sprite" (graphic) of a font (like Times New Roman, Arial, etc). The `SpriteFont` -class in the `SpriteFont` package allows for easily creating and customizing a sprite font that can be displayed on screen. An example of sprite fonts in action -are on the menu screen -- both the "PLAY GAME" and "CREDITS" menu options are separate `SpriteFont` classes defined in the `MenuScreen` class: +class in the `SpriteFont` package allows for easily creating and customizing a sprite font that can be displayed on screen. An example of sprite fonts in action are on the menu screen -- both the "PLAY GAME" and "CREDITS" menu options are separate `SpriteFont` classes defined in the `MenuScreen` class: ![menu-screen.png](../../assets/images/menu-screen.png) ## SpriteFont class -The `SpriteFont` class has already created `draw` logic that will properly draw any sprite font as desired. The following -values must be defined at instantiation time (calling the constructor): +The `SpriteFont` class has already created `draw` logic that will properly draw any sprite font as desired. The following values must be defined at instantiation time (calling the constructor): - **text** -- the text of the sprite font - **x** -- the x location on screen of the sprite font @@ -41,7 +36,7 @@ values must be defined at instantiation time (calling the constructor): Additionally, there are a couple other values that can be set after instantiation with the appropriate setter method: -- **setFontStyle**-- using Java's built in `Font` enum, you can set the sprite font to have a style such as bold or italic; you would do so like this: `setFontStyle(Font.BOLD);` +- **setFontStyle**-- using Java's built in `Font` enum, you can set the sprite font to have a style such as bold or italic; you would do so like this: `setFontStyle(Font.BOLD);` - **setOutlineColor** -- when this is set, the sprite font will be drawn with an outline around it with the chosen color - **setOutlineThickness** -- the thickness of the outline drawn around the sprite font (will only take effect if outline color has also been set) @@ -50,9 +45,9 @@ The `SpriteFont` class is used all across the application, from the menus to var Below is the code for creating the "PLAY GAME" sprite font for the `MenuScreen`: ```java -SpriteFont playGame = new SpriteFont("PLAY GAME", 200, 150, "Comic Sans", 30, new Color(49, 207, 240)); -playGame.setOutlineColor(Color.black); -playGame.setOutlineThickness(3); +SpriteFont playGame=new SpriteFont("PLAY GAME",200,150,"Comic Sans",30,new Color(49,207,240)); + playGame.setOutlineColor(Color.black); + playGame.setOutlineThickness(3); ``` Pretty easy to use! Don't forget to add it to the `draw` cycle: @@ -63,23 +58,16 @@ playGame.draw(); ## Using sprite fonts in game objects -Using a `SpriteFont` inside a `GameObject` to have the sprite font text show on the map works just like normal, simply -set up the sprite font and add it to the game object's `draw` cycle. One thing to look out for is that a `SpriteFont` will not automatically -calibrate its draw location based on the map's camera unlike a `GameObject` does, since `GameObjects` had extra logic added for that. To -resolve this issue, just be sure to update the `SpriteFont's` location relative to the `GameObject's` location. This is done in the NPC `Walrus` class -to create its speech bubble when taking to it: +Using a `SpriteFont` inside a `GameObject` to have the sprite font text show on the map works just like normal, simply set up the sprite font and add it to the game object's `draw` cycle. One thing to look out for is that a `SpriteFont` will not automatically calibrate its draw location based on the map's camera unlike a `GameObject` does, since `GameObjects` had extra logic added for that. To resolve this issue, just be sure to update the `SpriteFont's` location relative to the `GameObject's` location. This is done in the NPC `Walrus` class to create its speech bubble when taking to it: ![walrus-talking.PNG](../../assets/images/walrus-talking.PNG) -The draw method for the `Walrus` will make sure the "Hello!" sprite font message (variable name is `message` is always drawn -at a location relative to the walrus's so it follows map camera draw logic: +The draw method for the `Walrus` will make sure the "Hello!" sprite font message (variable name is `message` is always drawn at a location relative to the walrus's so it follows map camera draw logic: ```java -message.setLocation(getCalibratedXLocation() + 2, getCalibratedYLocation() - 8); -message.draw(graphicsHandler); +message.setLocation(getCalibratedXLocation()+2,getCalibratedYLocation()-8); + message.draw(graphicsHandler); ``` -Each draw cycle, the location of `message` is set to the calibrated location of the `Walrus` class (read more about the "calibrated" methods [here](./game-object.md#gameobject-class)). This -ensures its location stays relative to the walrus's. It also adds 2 to the walrus's x location and subracts 8 from the walrus's y location -to better position itself (this is what leads to the speech bubble being set inside the rectangle which is above the walrus's head). +Each draw cycle, the location of `message` is set to the calibrated location of the `Walrus` class (read more about the "calibrated" methods [here](./game-object.md#gameobject-class)). This ensures its location stays relative to the walrus's. It also adds 2 to the walrus's x location and subracts 8 from the walrus's y location to better position itself (this is what leads to the speech bubble being set inside the rectangle which is above the walrus's head). diff --git a/docs/GameCodeDetails/game-code-details.md b/docs_archive/GameCodeDetails/game-code-details.md similarity index 51% rename from docs/GameCodeDetails/game-code-details.md rename to docs_archive/GameCodeDetails/game-code-details.md index f9585df..30a4a85 100644 --- a/docs/GameCodeDetails/game-code-details.md +++ b/docs_archive/GameCodeDetails/game-code-details.md @@ -1,19 +1,16 @@ --- -layout: default -title: Game Code Details -nav_order: 5 -has_children: true -permalink: /GameCodeDetails +layout: default title: Game Code Details nav_order: 5 has_children: true permalink: /GameCodeDetails --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -21,14 +18,10 @@ permalink: /GameCodeDetails ## How is this game made and how does it work? -Utilizing the supplied game engine, the game code "hooks" on to the engine's game loop through the use of the `ScreenCoordinator` class, -and from there the game is directed by the various `Screen` classes (which exist in the `Screens` package). The subsections -of this document will go over how this game is made and how everything works. +Utilizing the supplied game engine, the game code "hooks" on to the engine's game loop through the use of the `ScreenCoordinator` class, and from there the game is directed by the various `Screen` classes (which exist in the `Screens` package). The subsections of this document will go over how this game is made and how everything works. Before reading on, it is important to understand how the game engine works at a surface level as described in the documentation [here](../GameEngine/game-engine.md). Below are documents which go over difference pieces of the game code: -{% comment %} - The Jekyll theme used [just-the-docs](https://pmarsceill.github.io/just-the-docs/) will auto populate this file with a table of contents -{% endcomment %} \ No newline at end of file +{% comment %} The Jekyll theme used [just-the-docs](https://pmarsceill.github.io/just-the-docs/) will auto populate this file with a table of contents {% endcomment %} \ No newline at end of file diff --git a/docs_archive/GameEngine/GameEngineSubSections/config.md b/docs_archive/GameEngine/GameEngineSubSections/config.md new file mode 100644 index 0000000..7d6f5ac --- /dev/null +++ b/docs_archive/GameEngine/GameEngineSubSections/config.md @@ -0,0 +1,48 @@ +--- +layout: default title: Config parent: Game Engine nav_order: 6 permalink: /GameEngine/Config +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Config + +## What is a config? + +Most applications have a concept of a "config file", which is just a configuration file that is separated out from program logic. Configuration files generally contain global constant variable values that an application references as needed, and treats them as "read only" +(they should not be changed while the application is running). The values chosen to be included in a configuration file differ depending on the application, but by having one it makes it possible to tweak/change values without having to go digging through class files, and also makes it very easy to test out how value changes affect an application. Often times applications are used by multiple people on various platforms -- having certain values strategically separated out like this can make a program a lot more flexible to work for other people's use cases. You can literally think of these variable values as "configuration options", just like hitting the "settings" button on any other application and personalizing your experience while using it. + +## Where is this game's config file located? + +The `Config.java` file in the `Engine` package contains several configuration variables that can be easily changed while the application is not running in order to modify a "global" aspect of it. The following variable values are defined in the `Config` class: + +- **FPS** -- how many game loop cycles per second are run (FPS = **F**rames **P**er **S**econd) +- **RESOURCES_PATH** -- root folder path to where all game assets will be stored (image files, etc) +- **MAP_FILES_PATH** -- root folder path to where all map files will be stored +- **GAME_WINDOW_WIDTH** -- width of the game's JFrame window +- **GAME_WINDOW_HEIGHT** -- height of the game's JFrame window +- **TRANSPARENT_COLOR** -- default transparent color the `ImageLoader` class will use when loading an image into the game + +## How do you access a `Config` class variable from other classes? + +Any class can access a `Config` class variable since all variables are defined as `public` `static`. + +```java +public void update(){ + System.out.println("The game window width is: "+Config.GAME_WINDOW_WIDTH); + System.out.println("The game window height is: "+Config.GAME_WINDOW_HEIGHT); + } +``` + +## How do you decide if a variable should be added to the Config class? + +It's honestly up to the programmer which values they want to have exposed separately as a "configuration" option. The only real "rule" is that it must be a global read-only variable, meaning the value of the variable CANNOT be modified while the game is running, and every class must be able to access it. I pretty much only use it for "game engine setup" values. \ No newline at end of file diff --git a/docs/GameEngine/GameEngineSubSections/drawing-graphics.md b/docs_archive/GameEngine/GameEngineSubSections/drawing-graphics.md similarity index 58% rename from docs/GameEngine/GameEngineSubSections/drawing-graphics.md rename to docs_archive/GameEngine/GameEngineSubSections/drawing-graphics.md index e57a4c5..44f129d 100644 --- a/docs/GameEngine/GameEngineSubSections/drawing-graphics.md +++ b/docs_archive/GameEngine/GameEngineSubSections/drawing-graphics.md @@ -1,19 +1,16 @@ --- -layout: default -title: Drawing Graphics -parent: Game Engine -nav_order: 5 -permalink: /GameEngine/DrawingGraphics +layout: default title: Drawing Graphics parent: Game Engine nav_order: 5 permalink: /GameEngine/DrawingGraphics --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -21,10 +18,10 @@ permalink: /GameEngine/DrawingGraphics ## Painting an image to the JPanel -The `GraphicsHandler` class is passed into the `draw` method of every class, and is used to paint an image to the engine's backing JPanel. -The engine sets it up on creation, and from that point on it can be used in any `draw` method in any class that needs it. +The `GraphicsHandler` class is passed into the `draw` method of every class, and is used to paint an image to the engine's backing JPanel. The engine sets it up on creation, and from that point on it can be used in any `draw` method in any class that needs it. The `GraphicsHandler` class has many methods, but the following methods are the most commonly used: + - **drawRectangle** -- draws a hollow rectangle to the screen, can supply border color can optionally supply border thickness - **drawFilledRectangle** -- draws a filled rectangle to the screen, can optionally set border color and border thickness - **drawImage** -- draws an image to the screen, can optionally flip the image horizontally, vertically, or both @@ -34,24 +31,21 @@ The `GraphicsHandler` class has many methods, but the following methods are the Most of the time in game, the `drawImage` method is what will be used: ```java -public void draw(GraphicsHandler graphicsHandler) { - // draw image at specified location - graphicsHandler.drawImage(image, xLocation, yLocation, imageWidth, imageHeight); +public void draw(GraphicsHandler graphicsHandler){ + // draw image at specified location + graphicsHandler.drawImage(image,xLocation,yLocation,imageWidth,imageHeight); - // draw image at specified location flipped horizontally - graphicsHandler.drawImage(image, xLocation, yLocation, imageWidth, imageHeight, ImageEffect.FLIP_HORIZONTAL); -} + // draw image at specified location flipped horizontally + graphicsHandler.drawImage(image,xLocation,yLocation,imageWidth,imageHeight,ImageEffect.FLIP_HORIZONTAL); + } ``` The `ImageEffect` enum contains all the other image effects that can be applied to an image besides flipping it horizontally. ## Draw Order -Something important to keep in mind is that the order that graphics are drawn to the JPanel matters, because each graphic -is "pasted" on over whatever was there beforehand. +Something important to keep in mind is that the order that graphics are drawn to the JPanel matters, because each graphic is "pasted" on over whatever was there beforehand. -For an in game example, the map tiles (light blue sky, tree, etc) in the below image are drawn BEFORE the cat image to ensure -that the map tiles do not cover up the cat making it not visible. This also ensures that every time the cat moves, the map tiles -continue to stay behind it. +For an in game example, the map tiles (light blue sky, tree, etc) in the below image are drawn BEFORE the cat image to ensure that the map tiles do not cover up the cat making it not visible. This also ensures that every time the cat moves, the map tiles continue to stay behind it. ![game-screen-3.png](../../assets/images/game-screen-3.png) diff --git a/docs_archive/GameEngine/GameEngineSubSections/game-loop.md b/docs_archive/GameEngine/GameEngineSubSections/game-loop.md new file mode 100644 index 0000000..d37b2e1 --- /dev/null +++ b/docs_archive/GameEngine/GameEngineSubSections/game-loop.md @@ -0,0 +1,130 @@ +--- +layout: default title: Game Loop parent: Game Engine nav_order: 1 permalink: /GameEngine/GameLoop +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Game Loop + +## What is a game loop? + +A game loop is the overall flow control for a game. Generally, it's an infinite loop that exists while the game application is running, and will continually do the same actions over and over again. Each game loop iteration is known as a frame. The number of game loop iterations that are completed per second is how frames per second (FPS) is calculated. + +## How does this engine's game loop work? + +The game loop is created using the `Timer` class from Java Swing. The timer is set up to continually "tick" after a predefined number of milliseconds have elapsed. While it's not perfect, it does the job well and for the most part maintains its tick cycle to match the desired FPS. To adjust the FPS of the game, the `Config` class has a variable named `FPS` which can be changed as desired. It currently is set to 100 as I found that the timer lags a bit when set to a standard 60 FPS, but YMMV based on the computer being used. + +Each "tick" of the game loop (each time the loop iterates), it does two things: updates game logic and then updates the graphics that are rendered to the screen based on the updated game logic. That's it. Here is a very detailed diagram illustrating the game loop: + +![game-loop-diagram.png](../../assets/images/game-loop-diagram.png) + +All jokes aside, at the end of the day that is all the game loop is doing -- continually cycling between `update` and `draw` calls. + +## What code belongs in the update cycle? + +Game logic is any code that has to do with the game being carried out, which includes anything from detecting key presses, moving something to a new location, collision detection, etc. All game logic belongs in the `update` section of the game loop. Each iteration of the game loop will force the game to re-run its game logic, which is essential to making the game run continually. If you write game logic that moves a player one pixel to the right when the right arrow key is held down, the `update` cycle will continually check for the right arrow key being pressed and if so move the player to the right one pixel each time. Without the game loop, there'd be no way to apply this logic continually. + +Anywhere in the codebase you see an `update` method, and you will see it in nearly every class, it is where game logic is being run by the game loop while an instance of that class is active. + +## What code belongs in the draw cycle? + +Any code that is explicitly rendering ("painting") a graphic (usually an image) to the JPanel belongs in the `draw` section. Each iteration of the game loop will force the game to "repaint" itself, meaning all graphics on the screen are updated (even if they haven't moved since the previous cycle). This means that any changes to the "state" of the graphics (such as if a new image needs to be rendered or an existing image has moved) are immediately updated on screen. + +Anywhere in the codebase you see a `draw` method, and you will see it in nearly every class, it is where graphics are being told where to be displayed on screen while an instance of that class is active. + +You can think of this step in the cycle as the "output" to the player of the game. Technically a game can work without ever displaying any graphics, since all the game logic still plays out each cycle and the computer itself doesn't actually need graphics on screen to do its job -- just like how a console app can technically run a program just fine without ever printing anything to the console. It is a game developer's job to determine what output to show to the user as the game progresses, however the draw cycle should NOT be relied on to actually perform any critical game logic. Code in the draw cycle should NEVER be mutating (changing the value of) any class instance variable. Consider it in "read-only" mode. + +### Lag + +The `draw` section of the game loop should only contain code to draw graphics to the screen and should NOT include any kind of game logic, such as moving graphics to a new location. Not only is this best practice as mentioned above +(treat graphics as the "output" of the application), but it can actually break your game or cause unexplainable bugs otherwise. This is because of the way the Java JPanel's graphics painting is set up. While the game can call to the JPanel (which this game engine uses as a buffer to display graphics) +and tell it to update its graphics (also known as a "repaint"), this merely "schedules" the repaint instead of carrying it out right away. The JPanel can then carry out the repainting whenever it damn well pleases outside of your program's control, and there's a possibility that it doesn't carry out the repaint at all. When draw calls are skipped like this, it is known as lag, because the game is updating its game logic but not representing those logic changes with updated graphics on the screen. Lag is unavoidable at the end of the day, but it's imperative to not place game logic updating in the `draw` section of the game loop because having logic "skipped" or ran "out of order" can completely break a game, while the concept of "lag" will at least maintain game logic integrity to keep the game working. This issue can entirely be avoided if instance variables (from any class) are NEVER mutated during the `draw` cycle. + +## Example of game loop running + +Say I have the following class for an image: + +```java +import javax.imageio.ImageIO; +import java.io.File; + +public class CatImage { + + private BufferedImage image = ImageIO.read(new File("pusheen.png")); + private int xLocation = 0; + private int yLocation = 0; + + public int getXLocation() { + return xLocation; + } + + public int getYLocation() { + return yLocation; + } + + // this would be called during the update cycle + public void update() { + xLocation += 1; + } + + // this would be called during the draw cycle + public void draw(Graphics2D g) { + g.drawImage(image, xLocation, yLocation, null); + } +} +``` + +During the game loop's `update` cycle, this class's `update` method will move its position 1 pixel to the right (adding 1 to `xLocation`). During the game loop's `draw` cycle, the image will be drawn to the screen at the position defined by its `xLocation` and `yLocation` variables. As the game loop iterates and continuously calls the `update` and `draw` methods over and over again, the `update` cycle will continually move the image's position to the right, and the `draw` will continually "repaint" the image to the screen at its updated location. + +Click the button below for a live simulation of how this example `CatImage` class would behave during the game loop cycle: + + + +
+ pusheen.png +
+ + \ No newline at end of file diff --git a/docs_archive/GameEngine/GameEngineSubSections/gui-components.md b/docs_archive/GameEngine/GameEngineSubSections/gui-components.md new file mode 100644 index 0000000..a1d4a87 --- /dev/null +++ b/docs_archive/GameEngine/GameEngineSubSections/gui-components.md @@ -0,0 +1,40 @@ +--- +layout: default title: GUI Components parent: Game Engine nav_order: 2 permalink: /GameEngine/GUI Components +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# GUI Components + +## Java Swing Library + +This game uses components from Java's Swing library for its GUI (graphic user interface). All that's really used is a JFrame for the application window and a JPanel inside the JFrame which is where graphics are "painted" on to. + +## Game Window + +The `GameWindow` class in the `Engine` package extends the Java Swing `JFrame` class and just sets up the application window as needed. Otherwise, it's pretty uneventful. This JFrame does hold the all-important `GamePanel` class, but otherwise it's only job is just to exist. + +Essentially, this is all the `GameWindow` brings up on its own: + +![jframe.png](../../assets/images/jframe.png) + +## Game Panel + +The `GamePanel` class in the `Engine` package extends the Java Swing `JPanel` class and is responsible for rendering the game's graphics. Additionally (although in retrospect this wasn't a great design decision), the `GamePanel` class also sets up various other essential game resources like the `graphicsHandler` and the Java Swing `Timer` that runs the game loop. + +The `GamePanel` class is also home to the universal pause function (which is another poor design decision to include in this class). Pressing the `P` +key at any point while the game is running will immediately stop the game loop's `update` cycle but will continue the game's `draw` cycle, which essentially "pauses" the game. It will also show the [sprite font](../../GameCodeDetails/GameCodeDetailsSubSections/sprite-font.md) text "PAUSE" in the middle of the screen while the game is paused. The `pauseLabel` variable is what defines that "PAUSE" text, and the `update` method and `draw` method contain the pause logic. + +While the `GamePanel` appears to be doing a lot, at the end of the day it's just a `JPanel` that's doing far more work than it should be doing. Classic case of a bloated class. + +Every `draw` cycle, the `JPanel's` `repaint` method is called, which schedules a re-painting of all graphics to the `JPanel`. As mentioned in the [game loop](./game-loop.md) documents, this act of "scheduling" a repaint does not immediately execute the re-painting -- the `JPanel` will carry it out when it's ready to -- this call to `repaint` is asynchronous, meaning Java will carry out the task for painting in a separate thread from the one currently running the rest of the application. This is not something you have to worry aabout as long as you follow what is said in the *game-loop* documentation and do not include any game logic during the game loop's `draw` cycle. diff --git a/docs/GameEngine/GameEngineSubSections/keyboard-detection.md b/docs_archive/GameEngine/GameEngineSubSections/keyboard-detection.md similarity index 58% rename from docs/GameEngine/GameEngineSubSections/keyboard-detection.md rename to docs_archive/GameEngine/GameEngineSubSections/keyboard-detection.md index 8631de9..e89e29d 100644 --- a/docs/GameEngine/GameEngineSubSections/keyboard-detection.md +++ b/docs_archive/GameEngine/GameEngineSubSections/keyboard-detection.md @@ -1,19 +1,16 @@ --- -layout: default -title: Keyboard Detection -parent: Game Engine -nav_order: 3 -permalink: /GameEngine/KeyboardDetection +layout: default title: Keyboard Detection parent: Game Engine nav_order: 3 permalink: /GameEngine/KeyboardDetection --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- @@ -22,12 +19,12 @@ permalink: /GameEngine/KeyboardDetection ## Keyboard Input There is a `Keyboard` class in the engine that handles detecting if a key is currently pressed (or not pressed) -on the keyboard at any point while the game is running. The class is a "static" class (meaning it can't be instantiated) and exists -solely to poll the global keyboard state, which is generally used in `update` methods to be used to check if a certain key is currently being pressed or not. -Every class has the ability to detect keyboard input and multiple classes can detect keyboard input at the same time and react to it as desired. +on the keyboard at any point while the game is running. The class is a "static" class (meaning it can't be instantiated) and exists solely to poll the global keyboard state, which is generally used in `update` methods to be used to check if a certain key is currently being pressed or not. Every class has the ability to detect keyboard input and multiple classes can detect keyboard input at the same time and react to it as desired. ## Key Detection Methods + The `Keyboard` class supplies the following methods: + - **isKeyDown** -- check if a key is currently being pressed - **isKeyUp** -- check if a key is not currently being pressed - **areKeysDown** -- check if multiple keys are being pressed at the same time @@ -35,31 +32,30 @@ The `Keyboard` class supplies the following methods: Since these methods are all static methods, they can be referenced directly from the `Keyboard` type (examples for this below). -Using these methods are really easy. There is a `Key` enum in the engine that has predefined keys set up to be checked for. -These can be used as arguments for the `Keyboard` class's methods. +Using these methods are really easy. There is a `Key` enum in the engine that has predefined keys set up to be checked for. These can be used as arguments for the `Keyboard` class's methods. ```java -public void update() { - // check if LEFT arrow key pressed - if (Keyboard.isKeyDown(Key.LEFT)) { - - } - - // check if A key is pressed - if (Keyboard.isKeyDown(Key.A)) { - - } - - // if 1 key is not pressed - if (Keyboard.isKeyUp(Key.ONE)) { - - } - - // if both shift and space are pressed at the same time - if (Keyboard.areKeysDown(new Key[] { Key.SHIFT, Key.SPACE })) { - - } -} +public void update(){ + // check if LEFT arrow key pressed + if(Keyboard.isKeyDown(Key.LEFT)){ + + } + + // check if A key is pressed + if(Keyboard.isKeyDown(Key.A)){ + + } + + // if 1 key is not pressed + if(Keyboard.isKeyUp(Key.ONE)){ + + } + + // if both shift and space are pressed at the same time + if(Keyboard.areKeysDown(new Key[]{Key.SHIFT,Key.SPACE})){ + + } + } ``` ## Supported Keys @@ -115,8 +111,6 @@ public void update() { ## Adding More Supported Keys -If additional keys are needed to be supported for key detection, an entry for it must be added to the `Key` enum (`Key.java`), -and then an entry also needs to be added to the `Keyboard` class's `buildKeyMap` `EnumMap` method mapping the enum entry with its -key code. You can look up each key's key code in the StackOverflow answer [here](https://stackoverflow.com/a/31637206). Note: -key codes for Java differ slightly from those in JavaScript, which most other websites will give you when googling for key codes -- please -use the provided link for correct key codes for Java. \ No newline at end of file + +If additional keys are needed to be supported for key detection, an entry for it must be added to the `Key` enum (`Key.java`), and then an entry also needs to be added to the `Keyboard` class's `buildKeyMap` `EnumMap` method mapping the enum entry with its key code. You can look up each key's key code in the StackOverflow answer [here](https://stackoverflow.com/a/31637206). Note: +key codes for Java differ slightly from those in JavaScript, which most other websites will give you when googling for key codes -- please use the provided link for correct key codes for Java. \ No newline at end of file diff --git a/docs_archive/GameEngine/GameEngineSubSections/loading-images.md b/docs_archive/GameEngine/GameEngineSubSections/loading-images.md new file mode 100644 index 0000000..2e41e13 --- /dev/null +++ b/docs_archive/GameEngine/GameEngineSubSections/loading-images.md @@ -0,0 +1,71 @@ +--- +layout: default title: Loading Images parent: Game Engine nav_order: 4 permalink: /GameEngine/LoadingImages +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Loading Images + +## Reading images into the game + +The engine's `ImageLoader` class has methods that will read an image into the game into Java's `BufferedImage` data type. + +The Image Loader provides two methods: + +- **load** -- reads an image into the game +- **loadSubImage** -- reads a piece of an image into the game + +Pass the method an image file and it will read it in, no questions asked. + +## Resources Directory + +The `ImageLoader` class will look for image files relative to the directory defined as the `RESOURCES_PATH`, which is set in the `Config` class. By default, it is set to a `Resources/` directory at the root of the project. If an image is placed in that `Resources/` directory named "cat.png", the file name can be supplied directly to the `ImageLoader` methods. + +```java +BufferedImage catImage=ImageLoader.load("cat.png"); +``` + +It is fine to add subfolders to the `Resource` directory, just be sure to include that in the path to the image. For example, if there was a folder named `CatPics` inside the `Resources` folder, the path would look like this: + +```java +BufferedImage catImage=ImageLoader.load("CatPics/cat.png"); +``` + +### Changing resources directory + +You are free to change the value of the `RESOURCE_PATH` variable in the `Config` class to change the location of the folder that the `ImageLoader` will look for images in. It is not advisable to remove this restriction as a game generally needs to guarantee that all of its assets are where they should be in order to run properly, and keeping them in one base location limits room for error. + +## Image Transparency + +The "transparent" color of an image is a particular color in an image that is desired to be "invisible", meaning it will not be included when the image is loaded into the game. Every sprite in the base game uses the color magenta (RGB 255 0 255) as its transparent color, as that color is ugly and very rarely, if ever, used by any image. This magenta color is set as the default transparent color by the `TRANSPARENT_COLOR` variable in the `Config` class. + +For example, the following cat image used in game has its background set as magenta: + +![cat-sprite.png](../../assets/images/cat-sprite.png) + +When loading this image into the game, the transparent color of magenta is set, meaning in game, the magenta color will not be shown, allowing it to blend in with whatever color is shown in the background of it: + +![game-screen-3.png](../../assets/images/game-screen-3.png) + +Before you ask "why can't you just use a tool like photoshop to add a transparency to an image", the answer is that it is not advisable to do this for games because it bloats the file size (leading to longer load times), takes additional time, and does not always work out as expected (especially with Java's limited image loading capabilities). + +If you have an image file that requires a different transparent color, the `ImageLoader` class has alternate methods to allow for a transparent color to be specified instead of just using the default one. + +```java +// sets transparent color to blue +BufferedImage catImage=ImageLoader.load("cat.png",Color.blue); +``` + +### Changing default transparent color + +A new color can be set as the default transparent color by changing the `TRANSPARENT_COLOR` variable in the `Config` class. \ No newline at end of file diff --git a/docs_archive/GameEngine/game-engine.md b/docs_archive/GameEngine/game-engine.md new file mode 100644 index 0000000..f7cb79d --- /dev/null +++ b/docs_archive/GameEngine/game-engine.md @@ -0,0 +1,34 @@ +--- +layout: default title: Game Engine has_children: true nav_order: 4 permalink: /GameEngine +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Game Engine + +## What is the difference between a game and a game engine? + +The difference between a particular game and a game engine (also called a game framework) is that "game logic" is specific to one game while a game engine is created to be reused. The game engine is designed to create an environment from which a game can be made. Game engines typically have built in functionality for loading resources (such as images), detecting user input (such as checking if a key on the keyboard is pressed), and running the game through a cycled game loop that continually updates game logic and graphics rendered to the screen. + +Most game developers do not write their own game engine, and instead use an existing one that has the features they require to create their desired game, such as [Unity](https://unity.com/) or [Unreal](https://www.unrealengine.com/en-US/). Some game companies have even written their own game engines which they reuse for many different games and continually improve upon it. + +For this game, the engine was created from scratch using components from the Java Swing library (really just the JPanel) and everything else was done using vanilla Java. While the engine itself is not "specific" to the platformer game genre, several other components were included that fit nicely with platformer game logic. + +## Overview of this game engine + +The code architecture and design for this engine was inspired by [XNA](https://en.wikipedia.org/wiki/Microsoft_XNA), Microsoft’s old game programming framework that they supported for anyone to use to write games for the Xbox 360. Now it has been ported and released open source under the title [Monogame](https://www.monogame.net/) +since Microsoft discontinued support for it. + +Below are some features of the game engine, which are defined and created in the `Engine` package. + +{% comment %} The Jekyll theme used [just-the-docs](https://pmarsceill.github.io/just-the-docs/) will auto populate this file with a table of contents {% endcomment %} diff --git a/docs/GameOverview/game-overview.md b/docs_archive/GameOverview/game-overview.md similarity index 65% rename from docs/GameOverview/game-overview.md rename to docs_archive/GameOverview/game-overview.md index 2b68a27..f28d942 100644 --- a/docs/GameOverview/game-overview.md +++ b/docs_archive/GameOverview/game-overview.md @@ -1,32 +1,26 @@ --- -layout: default -title: Game Overview -nav_order: 3 -permalink: /GameOverview -search_exclude: true +layout: default title: Game Overview nav_order: 3 permalink: /GameOverview search_exclude: true --- # Navigation Structure + {: .no_toc } ## Table of contents + {: .no_toc .text-delta } -1. TOC -{:toc} +1. TOC {:toc} --- # What is this game? -This game (which has no title as of now) is a traditional platformer where you play as a cat and have to traverse from the beginning to the end of -a level while avoiding enemies. Upon hitting the gold box at the end of a level, the level is completed. Hitting an enemy results in a game over and the level must be restarted. +This game (which has no title as of now) is a traditional platformer where you play as a cat and have to traverse from the beginning to the end of a level while avoiding enemies. Upon hitting the gold box at the end of a level, the level is completed. Hitting an enemy results in a game over and the level must be restarted. -Inspiration for this game came from popular 2D platformers such as Mario and Megaman. The player currently can walk around, jump, and duck as standard to the genre, -and enemy/level design and graphics were heavily influenced by the games [Super Mario World](https://www.youtube.com/watch?v=ta7ufW0Prws) and [Megaman 2](https://www.youtube.com/watch?v=vuJ8Qr-3_zg). +Inspiration for this game came from popular 2D platformers such as Mario and Megaman. The player currently can walk around, jump, and duck as standard to the genre, and enemy/level design and graphics were heavily influenced by the games [Super Mario World](https://www.youtube.com/watch?v=ta7ufW0Prws) and [Megaman 2](https://www.youtube.com/watch?v=vuJ8Qr-3_zg). -The art direction was intended to create a "happy" vibe, and follows a blocky minimalistic style ~~because I am a horrible artist~~. -The sun shines, the flowers dance, and a bright color palette is used ~~which is totally not just the result of me only using Microsoft Paint default colors~~. +The art direction was intended to create a "happy" vibe, and follows a blocky minimalistic style ~~because I am a horrible artist~~. The sun shines, the flowers dance, and a bright color palette is used ~~which is totally not just the result of me only using Microsoft Paint default colors~~. # Features @@ -45,33 +39,26 @@ The sun shines, the flowers dance, and a bright color palette is used ~~which is The menu screen can be navigated using the arrow keys and the space bar to select an option. -The player character (cat) can walk left and right using the left arrow key or the letter 'A' and right arrow key or the letter 'D'. Pressing the letter 'W' or the up arrow key jumps, and pressing the letter 'S' or the down arrow key ducks. Note that depending on the type of keyboard your computer has, you may be limited to how many keys you can press at the same time. -If you find yourself being unable to jump when multiple other arrow keys are pressed at once, it is a keyboard problem, not a coding/Java problem. +The player character (cat) can walk left and right using the left arrow key or the letter 'A' and right arrow key or the letter 'D'. Pressing the letter 'W' or the up arrow key jumps, and pressing the letter 'S' or the down arrow key ducks. Note that depending on the type of keyboard your computer has, you may be limited to how many keys you can press at the same time. If you find yourself being unable to jump when multiple other arrow keys are pressed at once, it is a keyboard problem, not a coding/Java problem. If you touch the gold box at the end of a level, the level will be completed and you will continue to the next level. Once you touch the gold box on level 5, you win the game. If you touch an enemy, you will die and be forced to restart the level. If you fall in the water, you also die. Most of the map tiles -(grass, dirt, trees) are solid and can be jumped on but not jumped through. There are a few tiles like the tree branches and the moving platform which -can be jumped through and stood on (like typical jump through platforms that are common in the platforming genre). +(grass, dirt, trees) are solid and can be jumped on but not jumped through. There are a few tiles like the tree branches and the moving platform which can be jumped through and stood on (like typical jump through platforms that are common in the platforming genre). -Clearly, this game is still not finished, in fact it's a more developed and interesting version of the original game.There are five levels, two enemy types (that can be killed), -and the player character's movement options are limited. There are many different directions the game can be taken in from here, -and the backing game engine was built to support a wide variety of options that could be feasibly implemented. +Clearly, this game is still not finished, in fact it's a more developed and interesting version of the original game.There are five levels, two enemy types (that can be killed), and the player character's movement options are limited. There are many different directions the game can be taken in from here, and the backing game engine was built to support a wide variety of options that could be feasibly implemented. # Tools used to make this game -This game was written in Java (version 8 or later) and uses no external libraries. It was developed using the IntelliJ IDE and Eclipse, -although it should work out of the box with any other Java IDEs. +This game was written in Java (version 8 or later) and uses no external libraries. It was developed using the IntelliJ IDE and Eclipse, although it should work out of the box with any other Java IDEs. All of the game's art was created from scratch using the almighty Microsoft Paint. # Running the game -The game can be run like any other Java application by executing the program starting from the file with the main method. I recommend using -the IntelliJ IDE to run the application, however the Eclipse IDE will work fine as well. +The game can be run like any other Java application by executing the program starting from the file with the main method. I recommend using the IntelliJ IDE to run the application, however the Eclipse IDE will work fine as well. The `main` method for the game is in the `Game` package > `Game.java` file. -The `main` method for the map editor is in the `MapEditor` package > `MapEditor.java` file. This is a separate program -from the game. +The `main` method for the map editor is in the `MapEditor` package > `MapEditor.java` file. This is a separate program from the game. # Game Screenshots diff --git a/docs/Gemfile b/docs_archive/Gemfile similarity index 100% rename from docs/Gemfile rename to docs_archive/Gemfile diff --git a/docs/Gemfile.lock b/docs_archive/Gemfile.lock similarity index 100% rename from docs/Gemfile.lock rename to docs_archive/Gemfile.lock diff --git a/docs/Home/home.md b/docs_archive/Home/home.md similarity index 92% rename from docs/Home/home.md rename to docs_archive/Home/home.md index 506f7a7..2647f80 100644 --- a/docs/Home/home.md +++ b/docs_archive/Home/home.md @@ -1,9 +1,5 @@ --- -layout: default -title: Home -nav_order: 1 -permalink: / -search_exclude: true +layout: default title: Home nav_order: 1 permalink: / search_exclude: true --- # SER-225-Game Documentation @@ -12,10 +8,10 @@ search_exclude: true cat-walking-right.gif -Welcome to the unnamed SER-225-Game's website! Here you can find information on the game, documentation on how the code works, -and other miscellaneous notes. +Welcome to the unnamed SER-225-Game's website! Here you can find information on the game, documentation on how the code works, and other miscellaneous notes. ## Notes from Fall 2021 Team A2 + Some legacy code was completely revised during Team A2's development, and thus some of this documentation is obsolete. Documentation will be kept here as it was well written, however please look at the specific code. Javadoc's comments have been included, as well as other comments, to try and explain what is happening. ## Table of contents diff --git a/docs_archive/HowToUseThisSite/how-to-use-this-site.md b/docs_archive/HowToUseThisSite/how-to-use-this-site.md new file mode 100644 index 0000000..e8c983b --- /dev/null +++ b/docs_archive/HowToUseThisSite/how-to-use-this-site.md @@ -0,0 +1,47 @@ +--- +layout: default title: How To Use This Site nav_order: 2 permalink: /HowToUseThisSite search_exclude: true +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# How To Use This Site + +This site contains documentation for various facets of this game created for Quinnipiac's SER225 class, mainly going over how the game is made and how the code works. Games tend to be more complicated than the average application due to how much is going on, the constant cycling of the game loop that has to always be looking out for user input, and performance mattering due a slow game directly affecting the player. For that reason, it is important to understand (at least at a high-level) the game engine and game logic code in order to successfully develop this game further. + +Click a link from the sidebar on the left of this site to be taken to its respective webpage. + +The [Game Engine](../GameEngine/game-engine.md) section contains documents that detail the game engine code (how the game loop cycle works, etc.). + +The [Game Code Details](../GameCodeDetails/game-code-details.md) section contains documents that detail the game logic code (map, player, enemies, etc). + +Other pages should be self-explanatory. + +The search bar at the top of this site is your best friend. Without it, you will waste a lot of time combing through pages of text trying to find what you're looking for. CTRL+F for keywords is another necessity for finding what you're looking for. If you are looking at code and do not understand something, try typing it into the search of this website and see what comes up. Looking through subsections on the sidebar can also help you find the correct pages you are looking for if in need of adding a feature where you have a general idea of how to do it but aren't sure of which class contains the logic that needs to be added to/changed. Please do not attempt to read through all the pages in this site all at once -- this site is designed to be used as a "look up" reference similar to situations when you need to consult google to find an answer. + +## How was this site made? + +GitHub has a platform called [GitHub Pages](https://pages.github.com/) that gives every GitHub user one free domain, and each repository is given one free project site route. GitHub Pages will then, for free, host a website specified in that repo. In the `docs` folder of this project is where GitHub Pages is set to look to in order to build and host the website code. + +While normally websites are written using HTML, GitHub pages uses a web framework called [Jekyll](https://jekyllrb.com/) that transforms plain text into HTML which can then be rendered in a web browser. In particular, Jekyll will translate [markdown](https://www.markdownguide.org/basic-syntax/) formatted text files into HTML, which is AWESOME because writing in markdown is MUCH easier than dealing with HTML tags. There is a bit of a learning curve to use Jekyll properly (something I didn't know how to use before attempting to make this site), but once I got it all figured out, things went smoothly and the website came out pretty good in my opinion. I used a Jekyll theme called [Just the Docs](https://pmarsceill.github.io/just-the-docs/) for this website, which gave me access to various features and already implemented code that made it a lot easier to build up the site. + +You are also allowed to embed HTML right into the markdown files, which I did once in a while if I needed do to an animation or something, but otherwise there wasn't much of a need to do so as this is a static site and doesn't need much logic to fulfill its purpose. + +## Changing website information + +Each page of the website has an associated `.md` (markdown) file inside the `docs` folder. It's just a matter of finding the correct one for the page to edit and then actually changing the text to update the site. Only changes to the `master` branch will be applied to the website. + +If you forked this repo and want GitHub to create a new web page for your version of the game (that will not conflict with this existing website), you need to go to your GitHub repo's Settings, go to the GitHub Pages section, and set the source to point to the `docs` folder instead of `none`. That setting should also tell you the new url that will lead to your project's site, which will be separate from mine. + +![github-pages-source.png](../assets/images/github-pages-source.png) + +If you are creating a new web page, I recommend copying an existing page's first several lines and edit them to make sure it fits properly in the site (the theme will automatically add properly added pages to the sidebar). \ No newline at end of file diff --git a/docs_archive/MapEditor/map-editor.md b/docs_archive/MapEditor/map-editor.md new file mode 100644 index 0000000..8d5a327 --- /dev/null +++ b/docs_archive/MapEditor/map-editor.md @@ -0,0 +1,135 @@ +--- +layout: default title: Map Editor nav_order: 6 permalink: /MapEditor +--- + +# Navigation Structure + +{: .no_toc } + +## Table of contents + +{: .no_toc .text-delta } + +1. TOC {:toc} + +--- + +# Map Editor + +## What is a map editor? + +In this project, there is another "sub" project called the Map Editor (all located in the `MapEditor` package) that will load up a GUI for designing game [tile maps](/GameDetails/Map/MapTilesAndTilesets) with a visual display instead of having to do so through code and not being able to see the map that is being built. Nearly every video game uses a map editor (level editor, level maker, etc.) +that game developers use to design the game's maps. Games like [Mario Maker](https://www.youtube.com/watch?v=tZ5g5n-6OFg) are literally just one big map editor (a REALLY good one). + +## This game's map editor + +The inspiration behind the design of the map editor for this game engine came from the old CD-ROM game [Speedy Eggbert](https://en.wikipedia.org/wiki/Speedy_Eggbert), which was a staple of my childhood. It was a standard platformer game (with some VERY odd design choices), but the level editor it included with it was the same one used by the game developers to actually make the game -- it was so fun as a child to be able to make levels for a game I really liked with ease. + +While this game's map editor is limited compared to other commercial map editors for other video games you may have seen/used +(it is only for placing map tiles -- things like enemies must be created and added through code), it is still very useful for designing a map and being able to see what you are doing as you go. + +To run this game's map editor, the `main` method in the `MapEditor` class must be run. Remember this is a separate program from the actual game code, although both make use of the same classes in this project. You can have both the map editor and the game running at the same time. + +## Map Editor features + +This Map Editor allows you to do the following: + +- Add a new Map to the game +- Change the individual tiles of a map (which maintain their tile logic defined in code) +- Change the dimensions of a map (how many tiles the map contains width and height wise) + +![map-editor.png](../assets/images/map-editor.PNG) + +## How to use the map editor + +It should be pretty self-explanatory how to use the map editor after playing around with it for 5 minutes. + +On the left-hand sidebar are the tiles that can be used for the map. Since each `Map` subclass (such as `TestMap`) must define its own `Tileset` (such as `CommonTileset`) (if this doesn't make sense to you, you can read more about the `Map` class [here](/GameDetails/Map) and about map tiles and tilesets [here](/GameDetails/Map/MapTilesAndTilesets)). From here, any tile on the left-hand sidebar can be clicked to "select" it +(you will know it has been selected when the tile is highlighted with a yellow box). Afterwards, going to the map and clicking will replace whichever tile was hovered by the mouse with the selected tile. Below is a gif of this process. + +![map-editor-usage.gif](../assets/images/map-editor-usage.gif) + +The map being edited can be changed using the dropdown on the top left. To save any changes to a map, hit the "Save Map" +button. The "Set Map Dimensions" button will allow you to change the size of the map (number of tiles width and height). It'll also allow you to choose which side of the map to apply changes to when growing/shrinking a map's dimensions -- for example, if increasing the width of the map, more tile space can be added either to the right side or the left of the map. + +Something to keep in mind is that in a `Tileset's` `defineTiles` method, after a map has been created using that `Tileset`, you should not rearrange the order the tiles are defined and added to the list. This will break existing maps. You should ALWAYS just append tiles on even though it may feel disorganized. + +## Adding a new map to the map editor + +Adding a new map to the map editor (and essentially creating a new map entirely) is a relatively easy process. + +First start by creating a new class in the `Maps` package that extends the `Map` class. You can look at `TestMap` as a reference for how to do this. Then for now, all that's needed is a constructor to define the map file name, tileset, and player starting position. An example is below: + +```java +public class MyMap extends Map { + + public MyMap() { + super("my_map.txt", new CommonTileset(), new Point(1, 11)); + } +} +``` + +In the above example, the map's file will be named "my_map.txt" (which has not been created yet -- the Map Editor will create it for you once its added), the tilset will use the `CommonTileset` (which can be found in the `Tileset` package), and the player start tile is set to tile index (1, 11). Be sure that two maps do not share the same map file name, or one will overwrite the other! + +Now, in the `MapEditor` package, go to the `EditorMaps.java` file. In here, you will need to add your new map class to these methods. + +In `getMapNames`, just add an entry to the list for the name you would want the map to be recognized by -- for example, for this map I could use "MyMap": + +{% raw %} + +```java +public static ArrayList getMapNames(){ + return new ArrayList(){{ + add("TestMap"); + add("TitleScreen"); + add("MyMap"); + }}; + } +``` + +{% endraw %} + +Then, in the `getMapByName` method, add another switch case for your new map name and return an instance of your new Map class. For this example, I would add a switch case for "MyMap" (that is the name it was given in the `getMapNames` method) and then I would return `new MyMap()`: + +```java +public static Map getMapByName(String mapName){ + switch(mapName){ + case"TestMap": + return new TestMap(); + case"TitleScreen": + return new TitleScreenMap(); + case"MyMap": + return new MyMap(); +default: + throw new RuntimeException("Unrecognized map name"); + } + } +``` + +The last thing you have to do is open the Map Editor and in the drop down select your new map. The Map Editor will create a new blank map file for it. From there, you can change the map dimensions as desired (it will start at a width and height of (0,0), so you have to change them to actually add tiles) and start designing the map! + +Map files can be found in the project's `MapFiles` folder. This folder location can be changed in the [game config](/GameEngine/Config) if desired. + +## How does the map editor work? + +I used Java Swing components for the GUI and control functionality (such as buttons, drop down menus, etc.). The problem with creating complex GUIs purely through code is that A LOT of code and logic is required and it is very easy for it to get unruly, as many shortcuts/hacky solutions have to be used to have all components take in the correct input and communicate with one-another. As you can imagine, there is quite a bit of code for the map editor broken up between several different classes, and they aren't the easiest code files to digest, and as a working unit it isn't immediately obvious which classes work with others -- that will happen when using any GUI library, and especially Java Swing due to how old it is. + +I will say that this was NOT easy to code up and get working. It's also a bit fragile as all map editors are, since it has major dependencies on game logic classes that it has no control over such as the `Map`, `MapTile`, and `Tileset` classes, and if any of those class change, it can easily break the map editor. Such is life. + +While I do not plan on going over the entirety of the map editor code here because it would take me forever to type up and at the end of the day I would just be re-describing the entire Java Swing Library, I will instead leave it be and give some generic guidance if you are looking to develop it further. + +The `EditorWindow` class is the JFrame. It sets the `EditorMainPanel` as its content pane. + +The `EditorMainPanel` is a JFrame that holds two other JFrame components: the `EditorControlPanel` (map name selection drop down menu and save map button are there, etc.) and the `MapBuilder`, which is the right side where the map is displayed and tiles in it can be replaced. + +The `EditorControlPanel` utilizes the `TilePicker`, which is ANOTHER JPanel that actually display the tile graphics that can be selected and is where that selection logic takes place. The `SelectedTileIndexHolder` class is used to store the currently selected tile. + +The `MapBuilder` JPanel defines the scroll pane and map info labels (like "width" and "height" at the bottom of the map display). Then inside the scroll pane, it places the `TileBuilder` JPanel. The `TileBuilder` class is where tiles can be replaced by the selected tile from the `TilePicker` based on the value in `SelectedTileIndexHolder`. + +Both `TilePicker` and `TileBuilder` have listeners for mouse click and hover inputs. + +The `ChangeMapSizeWindow` class is specifically for the window that pops up after clicking the "Change Map Size" button in the `EditorControlPanel`. This class lets you change the size of the map. + +The "Save Map" button will overwrite a map's assigned map file with what it looks like in the editor. + +I apologize deeply that there are no comments for the map editor classes, I will go back sometime and fix that \ No newline at end of file diff --git a/docs/_config.yml b/docs_archive/_config.yml similarity index 100% rename from docs/_config.yml rename to docs_archive/_config.yml diff --git a/docs/assets/images/blueCat.png b/docs_archive/assets/images/blueCat.png similarity index 100% rename from docs/assets/images/blueCat.png rename to docs_archive/assets/images/blueCat.png diff --git a/docs/assets/images/bug-enemy.gif b/docs_archive/assets/images/bug-enemy.gif similarity index 100% rename from docs/assets/images/bug-enemy.gif rename to docs_archive/assets/images/bug-enemy.gif diff --git a/docs/assets/images/cat-sprite.png b/docs_archive/assets/images/cat-sprite.png similarity index 100% rename from docs/assets/images/cat-sprite.png rename to docs_archive/assets/images/cat-sprite.png diff --git a/docs/assets/images/cat-spritesheet-with-indexes.png b/docs_archive/assets/images/cat-spritesheet-with-indexes.png similarity index 100% rename from docs/assets/images/cat-spritesheet-with-indexes.png rename to docs_archive/assets/images/cat-spritesheet-with-indexes.png diff --git a/docs/assets/images/cat-spritesheet.png b/docs_archive/assets/images/cat-spritesheet.png similarity index 100% rename from docs/assets/images/cat-spritesheet.png rename to docs_archive/assets/images/cat-spritesheet.png diff --git a/docs/assets/images/cat-walking-left.gif b/docs_archive/assets/images/cat-walking-left.gif similarity index 100% rename from docs/assets/images/cat-walking-left.gif rename to docs_archive/assets/images/cat-walking-left.gif diff --git a/docs/assets/images/cat-walking-right.gif b/docs_archive/assets/images/cat-walking-right.gif similarity index 100% rename from docs/assets/images/cat-walking-right.gif rename to docs_archive/assets/images/cat-walking-right.gif diff --git a/docs/assets/images/common-tileset.png b/docs_archive/assets/images/common-tileset.png similarity index 100% rename from docs/assets/images/common-tileset.png rename to docs_archive/assets/images/common-tileset.png diff --git a/docs/assets/images/completing-level.gif b/docs_archive/assets/images/completing-level.gif similarity index 100% rename from docs/assets/images/completing-level.gif rename to docs_archive/assets/images/completing-level.gif diff --git a/docs/assets/images/credits-screen.png b/docs_archive/assets/images/credits-screen.png similarity index 100% rename from docs/assets/images/credits-screen.png rename to docs_archive/assets/images/credits-screen.png diff --git a/docs/assets/images/dino-enemy-shoot.png b/docs_archive/assets/images/dino-enemy-shoot.png similarity index 100% rename from docs/assets/images/dino-enemy-shoot.png rename to docs_archive/assets/images/dino-enemy-shoot.png diff --git a/docs/assets/images/dinosaur-enemy-walk.gif b/docs_archive/assets/images/dinosaur-enemy-walk.gif similarity index 100% rename from docs/assets/images/dinosaur-enemy-walk.gif rename to docs_archive/assets/images/dinosaur-enemy-walk.gif diff --git a/docs/assets/images/end-level-box.gif b/docs_archive/assets/images/end-level-box.gif similarity index 100% rename from docs/assets/images/end-level-box.gif rename to docs_archive/assets/images/end-level-box.gif diff --git a/docs/assets/images/entire-map-with-camera-1.png b/docs_archive/assets/images/entire-map-with-camera-1.png similarity index 100% rename from docs/assets/images/entire-map-with-camera-1.png rename to docs_archive/assets/images/entire-map-with-camera-1.png diff --git a/docs/assets/images/entire-map-with-camera-2.png b/docs_archive/assets/images/entire-map-with-camera-2.png similarity index 100% rename from docs/assets/images/entire-map-with-camera-2.png rename to docs_archive/assets/images/entire-map-with-camera-2.png diff --git a/docs/assets/images/entire-map.PNG b/docs_archive/assets/images/entire-map.PNG similarity index 100% rename from docs/assets/images/entire-map.PNG rename to docs_archive/assets/images/entire-map.PNG diff --git a/docs/assets/images/ezgif.com-gif-maker.gif b/docs_archive/assets/images/ezgif.com-gif-maker.gif similarity index 100% rename from docs/assets/images/ezgif.com-gif-maker.gif rename to docs_archive/assets/images/ezgif.com-gif-maker.gif diff --git a/docs/assets/images/fireball.png b/docs_archive/assets/images/fireball.png similarity index 100% rename from docs/assets/images/fireball.png rename to docs_archive/assets/images/fireball.png diff --git a/docs/assets/images/game-loop-diagram.png b/docs_archive/assets/images/game-loop-diagram.png similarity index 100% rename from docs/assets/images/game-loop-diagram.png rename to docs_archive/assets/images/game-loop-diagram.png diff --git a/docs/assets/images/game-screen-1.png b/docs_archive/assets/images/game-screen-1.png similarity index 100% rename from docs/assets/images/game-screen-1.png rename to docs_archive/assets/images/game-screen-1.png diff --git a/docs/assets/images/game-screen-2.png b/docs_archive/assets/images/game-screen-2.png similarity index 100% rename from docs/assets/images/game-screen-2.png rename to docs_archive/assets/images/game-screen-2.png diff --git a/docs/assets/images/game-screen-3.png b/docs_archive/assets/images/game-screen-3.png similarity index 100% rename from docs/assets/images/game-screen-3.png rename to docs_archive/assets/images/game-screen-3.png diff --git a/docs/assets/images/game-screen-4.png b/docs_archive/assets/images/game-screen-4.png similarity index 100% rename from docs/assets/images/game-screen-4.png rename to docs_archive/assets/images/game-screen-4.png diff --git a/docs/assets/images/github-pages-source.png b/docs_archive/assets/images/github-pages-source.png similarity index 100% rename from docs/assets/images/github-pages-source.png rename to docs_archive/assets/images/github-pages-source.png diff --git a/docs/assets/images/grass-tile.png b/docs_archive/assets/images/grass-tile.png similarity index 100% rename from docs/assets/images/grass-tile.png rename to docs_archive/assets/images/grass-tile.png diff --git a/docs/assets/images/greenCat.png b/docs_archive/assets/images/greenCat.png similarity index 100% rename from docs/assets/images/greenCat.png rename to docs_archive/assets/images/greenCat.png diff --git a/docs/assets/images/horizontal-moving-platform.png b/docs_archive/assets/images/horizontal-moving-platform.png similarity index 100% rename from docs/assets/images/horizontal-moving-platform.png rename to docs_archive/assets/images/horizontal-moving-platform.png diff --git a/docs/assets/images/instructions-screen.PNG b/docs_archive/assets/images/instructions-screen.PNG similarity index 100% rename from docs/assets/images/instructions-screen.PNG rename to docs_archive/assets/images/instructions-screen.PNG diff --git a/docs/assets/images/jframe.png b/docs_archive/assets/images/jframe.png similarity index 100% rename from docs/assets/images/jframe.png rename to docs_archive/assets/images/jframe.png diff --git a/docs/assets/images/judgement-professor-graphics.png b/docs_archive/assets/images/judgement-professor-graphics.png similarity index 100% rename from docs/assets/images/judgement-professor-graphics.png rename to docs_archive/assets/images/judgement-professor-graphics.png diff --git a/docs/assets/images/level-select-screen.PNG b/docs_archive/assets/images/level-select-screen.PNG similarity index 100% rename from docs/assets/images/level-select-screen.PNG rename to docs_archive/assets/images/level-select-screen.PNG diff --git a/docs/assets/images/losing-level.gif b/docs_archive/assets/images/losing-level.gif similarity index 100% rename from docs/assets/images/losing-level.gif rename to docs_archive/assets/images/losing-level.gif diff --git a/docs/assets/images/main-menu.PNG b/docs_archive/assets/images/main-menu.PNG similarity index 100% rename from docs/assets/images/main-menu.PNG rename to docs_archive/assets/images/main-menu.PNG diff --git a/docs/assets/images/map-editor-usage.gif b/docs_archive/assets/images/map-editor-usage.gif similarity index 100% rename from docs/assets/images/map-editor-usage.gif rename to docs_archive/assets/images/map-editor-usage.gif diff --git a/docs/assets/images/map-editor.PNG b/docs_archive/assets/images/map-editor.PNG similarity index 100% rename from docs/assets/images/map-editor.PNG rename to docs_archive/assets/images/map-editor.PNG diff --git a/docs/assets/images/megaman-bounds.png b/docs_archive/assets/images/megaman-bounds.png similarity index 100% rename from docs/assets/images/megaman-bounds.png rename to docs_archive/assets/images/megaman-bounds.png diff --git a/docs/assets/images/menu-screen.png b/docs_archive/assets/images/menu-screen.png similarity index 100% rename from docs/assets/images/menu-screen.png rename to docs_archive/assets/images/menu-screen.png diff --git a/docs/assets/images/narrative-screen.PNG b/docs_archive/assets/images/narrative-screen.PNG similarity index 100% rename from docs/assets/images/narrative-screen.PNG rename to docs_archive/assets/images/narrative-screen.PNG diff --git a/docs/assets/images/options-screen.PNG b/docs_archive/assets/images/options-screen.PNG similarity index 100% rename from docs/assets/images/options-screen.PNG rename to docs_archive/assets/images/options-screen.PNG diff --git a/docs/assets/images/pause-game-1.PNG b/docs_archive/assets/images/pause-game-1.PNG similarity index 100% rename from docs/assets/images/pause-game-1.PNG rename to docs_archive/assets/images/pause-game-1.PNG diff --git a/docs/assets/images/pause-game-2.PNG b/docs_archive/assets/images/pause-game-2.PNG similarity index 100% rename from docs/assets/images/pause-game-2.PNG rename to docs_archive/assets/images/pause-game-2.PNG diff --git a/docs/assets/images/player-crouching.gif b/docs_archive/assets/images/player-crouching.gif similarity index 100% rename from docs/assets/images/player-crouching.gif rename to docs_archive/assets/images/player-crouching.gif diff --git a/docs/assets/images/player-falling-off-map.gif b/docs_archive/assets/images/player-falling-off-map.gif similarity index 100% rename from docs/assets/images/player-falling-off-map.gif rename to docs_archive/assets/images/player-falling-off-map.gif diff --git a/docs/assets/images/player-jumping.gif b/docs_archive/assets/images/player-jumping.gif similarity index 100% rename from docs/assets/images/player-jumping.gif rename to docs_archive/assets/images/player-jumping.gif diff --git a/docs/assets/images/player-on-moving-platform.gif b/docs_archive/assets/images/player-on-moving-platform.gif similarity index 100% rename from docs/assets/images/player-on-moving-platform.gif rename to docs_archive/assets/images/player-on-moving-platform.gif diff --git a/docs/assets/images/player-standing.PNG b/docs_archive/assets/images/player-standing.PNG similarity index 100% rename from docs/assets/images/player-standing.PNG rename to docs_archive/assets/images/player-standing.PNG diff --git a/docs/assets/images/player-walking.gif b/docs_archive/assets/images/player-walking.gif similarity index 100% rename from docs/assets/images/player-walking.gif rename to docs_archive/assets/images/player-walking.gif diff --git a/docs/assets/images/playing-level.gif b/docs_archive/assets/images/playing-level.gif similarity index 100% rename from docs/assets/images/playing-level.gif rename to docs_archive/assets/images/playing-level.gif diff --git a/docs/assets/images/purple-flower-animation.gif b/docs_archive/assets/images/purple-flower-animation.gif similarity index 100% rename from docs/assets/images/purple-flower-animation.gif rename to docs_archive/assets/images/purple-flower-animation.gif diff --git a/docs/assets/images/purple-flower-image-1.png b/docs_archive/assets/images/purple-flower-image-1.png similarity index 100% rename from docs/assets/images/purple-flower-image-1.png rename to docs_archive/assets/images/purple-flower-image-1.png diff --git a/docs/assets/images/purple-flower-image-2.png b/docs_archive/assets/images/purple-flower-image-2.png similarity index 100% rename from docs/assets/images/purple-flower-image-2.png rename to docs_archive/assets/images/purple-flower-image-2.png diff --git a/docs/assets/images/purple-flower-image-3.png b/docs_archive/assets/images/purple-flower-image-3.png similarity index 100% rename from docs/assets/images/purple-flower-image-3.png rename to docs_archive/assets/images/purple-flower-image-3.png diff --git a/docs/assets/images/pusheen.png b/docs_archive/assets/images/pusheen.png similarity index 100% rename from docs/assets/images/pusheen.png rename to docs_archive/assets/images/pusheen.png diff --git a/docs/assets/images/sky-tile.png b/docs_archive/assets/images/sky-tile.png similarity index 100% rename from docs/assets/images/sky-tile.png rename to docs_archive/assets/images/sky-tile.png diff --git a/docs/assets/images/tile-map-example-original.png b/docs_archive/assets/images/tile-map-example-original.png similarity index 100% rename from docs/assets/images/tile-map-example-original.png rename to docs_archive/assets/images/tile-map-example-original.png diff --git a/docs/assets/images/tile-map-example.png b/docs_archive/assets/images/tile-map-example.png similarity index 100% rename from docs/assets/images/tile-map-example.png rename to docs_archive/assets/images/tile-map-example.png diff --git a/docs/assets/images/walrus-talking.PNG b/docs_archive/assets/images/walrus-talking.PNG similarity index 100% rename from docs/assets/images/walrus-talking.PNG rename to docs_archive/assets/images/walrus-talking.PNG diff --git a/docs/assets/images/walrus.png b/docs_archive/assets/images/walrus.png similarity index 100% rename from docs/assets/images/walrus.png rename to docs_archive/assets/images/walrus.png diff --git a/Resources/0 Hearts.png b/resources/0 Hearts.png similarity index 100% rename from Resources/0 Hearts.png rename to resources/0 Hearts.png diff --git a/Resources/1 Heart.png b/resources/1 Heart.png similarity index 100% rename from Resources/1 Heart.png rename to resources/1 Heart.png diff --git a/Resources/2 Hearts.png b/resources/2 Hearts.png similarity index 100% rename from Resources/2 Hearts.png rename to resources/2 Hearts.png diff --git a/Resources/3 Hearts.png b/resources/3 Hearts.png similarity index 100% rename from Resources/3 Hearts.png rename to resources/3 Hearts.png diff --git a/Resources/Bone.png b/resources/Bone.png similarity index 100% rename from Resources/Bone.png rename to resources/Bone.png diff --git a/Resources/BugEnemy.png b/resources/BugEnemy.png similarity index 100% rename from Resources/BugEnemy.png rename to resources/BugEnemy.png diff --git a/Resources/Cat.png b/resources/Cat.png similarity index 100% rename from Resources/Cat.png rename to resources/Cat.png diff --git a/Resources/CatBlue.png b/resources/CatBlue.png similarity index 100% rename from Resources/CatBlue.png rename to resources/CatBlue.png diff --git a/Resources/CatGreen.png b/resources/CatGreen.png similarity index 100% rename from Resources/CatGreen.png rename to resources/CatGreen.png diff --git a/Resources/CheckpointFlag.png b/resources/CheckpointFlag.png similarity index 100% rename from Resources/CheckpointFlag.png rename to resources/CheckpointFlag.png diff --git a/Resources/CommonTileset.png b/resources/CommonTileset.png similarity index 100% rename from Resources/CommonTileset.png rename to resources/CommonTileset.png diff --git a/Resources/CyborgEnemy.png b/resources/CyborgEnemy.png similarity index 100% rename from Resources/CyborgEnemy.png rename to resources/CyborgEnemy.png diff --git a/Resources/DefaultTile.png b/resources/DefaultTile.png similarity index 100% rename from Resources/DefaultTile.png rename to resources/DefaultTile.png diff --git a/Resources/DinosaurEnemy.png b/resources/DinosaurEnemy.png similarity index 100% rename from Resources/DinosaurEnemy.png rename to resources/DinosaurEnemy.png diff --git a/Resources/Dog.png b/resources/Dog.png similarity index 100% rename from Resources/Dog.png rename to resources/Dog.png diff --git a/Resources/Fireball.png b/resources/Fireball.png similarity index 100% rename from Resources/Fireball.png rename to resources/Fireball.png diff --git a/Resources/GoldBox.png b/resources/GoldBox.png similarity index 100% rename from Resources/GoldBox.png rename to resources/GoldBox.png diff --git a/Resources/GreenPlatform.png b/resources/GreenPlatform.png similarity index 100% rename from Resources/GreenPlatform.png rename to resources/GreenPlatform.png diff --git a/Resources/Lazer Beam.png b/resources/Lazer Beam.png similarity index 100% rename from Resources/Lazer Beam.png rename to resources/Lazer Beam.png diff --git a/MapFiles/BossBattle.txt b/resources/MapFiles/BossBattle.txt similarity index 100% rename from MapFiles/BossBattle.txt rename to resources/MapFiles/BossBattle.txt diff --git a/MapFiles/level_select_map.txt b/resources/MapFiles/level_select_map.txt similarity index 100% rename from MapFiles/level_select_map.txt rename to resources/MapFiles/level_select_map.txt diff --git a/MapFiles/test_map.txt b/resources/MapFiles/test_map.txt similarity index 100% rename from MapFiles/test_map.txt rename to resources/MapFiles/test_map.txt diff --git a/MapFiles/test_map3.txt b/resources/MapFiles/test_map3.txt similarity index 100% rename from MapFiles/test_map3.txt rename to resources/MapFiles/test_map3.txt diff --git a/MapFiles/test_map4.txt b/resources/MapFiles/test_map4.txt similarity index 100% rename from MapFiles/test_map4.txt rename to resources/MapFiles/test_map4.txt diff --git a/MapFiles/test_map5.txt b/resources/MapFiles/test_map5.txt similarity index 100% rename from MapFiles/test_map5.txt rename to resources/MapFiles/test_map5.txt diff --git a/MapFiles/test_map6.txt b/resources/MapFiles/test_map6.txt similarity index 100% rename from MapFiles/test_map6.txt rename to resources/MapFiles/test_map6.txt diff --git a/MapFiles/test_map7.txt b/resources/MapFiles/test_map7.txt similarity index 100% rename from MapFiles/test_map7.txt rename to resources/MapFiles/test_map7.txt diff --git a/MapFiles/test_map_2.txt b/resources/MapFiles/test_map_2.txt similarity index 100% rename from MapFiles/test_map_2.txt rename to resources/MapFiles/test_map_2.txt diff --git a/MapFiles/test_tutorial.txt b/resources/MapFiles/test_tutorial.txt similarity index 100% rename from MapFiles/test_tutorial.txt rename to resources/MapFiles/test_tutorial.txt diff --git a/MapFiles/title_screen_map.txt b/resources/MapFiles/title_screen_map.txt similarity index 100% rename from MapFiles/title_screen_map.txt rename to resources/MapFiles/title_screen_map.txt diff --git a/Resources/Music/music.mp3 b/resources/Music/music.mp3 similarity index 100% rename from Resources/Music/music.mp3 rename to resources/Music/music.mp3 diff --git a/Resources/Music/music.wav b/resources/Music/music.wav similarity index 100% rename from Resources/Music/music.wav rename to resources/Music/music.wav diff --git a/Resources/Walrus.png b/resources/Walrus.png similarity index 100% rename from Resources/Walrus.png rename to resources/Walrus.png diff --git a/src/Enemies/BugEnemy.java b/src/Enemies/BugEnemy.java index 83dca38..3704001 100644 --- a/src/Enemies/BugEnemy.java +++ b/src/Enemies/BugEnemy.java @@ -14,9 +14,9 @@ import java.util.HashMap; /** - * This class is for the black bug enemy - * enemy behaves like a Mario goomba -- walks forward until it hits a solid map tile, and then turns around - * if it ends up in the air from walking off a cliff, it will fall down until it hits the ground again, and then will continue walking + * This class is for the black bug enemy + * enemy behaves like a Mario goomba -- walks forward until it hits a solid map tile, and then turns around + * if it ends up in the air from walking off a cliff, it will fall down until it hits the ground again, and then will continue walking */ public class BugEnemy extends Enemy { diff --git a/src/Enemies/CyborgEnemy.java b/src/Enemies/CyborgEnemy.java index c30572c..7ba86cc 100644 --- a/src/Enemies/CyborgEnemy.java +++ b/src/Enemies/CyborgEnemy.java @@ -16,9 +16,9 @@ import java.util.HashMap; /** - * This class is for the cyborg - * It walks back and forth between two set points (startLocation and endLocation) - * Every so often (based on shootTimer) the cyborg will shoot lazers from its arms + * This class is for the cyborg + * It walks back and forth between two set points (startLocation and endLocation) + * Every so often (based on shootTimer) the cyborg will shoot lazers from its arms */ public class CyborgEnemy extends Enemy { diff --git a/src/Enemies/DinosaurEnemy.java b/src/Enemies/DinosaurEnemy.java index 417286e..359aedc 100644 --- a/src/Enemies/DinosaurEnemy.java +++ b/src/Enemies/DinosaurEnemy.java @@ -16,9 +16,9 @@ import java.util.HashMap; /** - * This class is for the green dinosaur enemy that shoots fireballs - * It walks back and forth between two set points (startLocation and endLocation) - * Every so often (based on shootTimer) it will shoot a Fireball enemy + * This class is for the green dinosaur enemy that shoots fireballs + * It walks back and forth between two set points (startLocation and endLocation) + * Every so often (based on shootTimer) it will shoot a Fireball enemy */ public class DinosaurEnemy extends Enemy { diff --git a/src/Enemies/Dog.java b/src/Enemies/Dog.java index 92cb793..0635cf6 100644 --- a/src/Enemies/Dog.java +++ b/src/Enemies/Dog.java @@ -15,9 +15,9 @@ import java.util.HashMap; /** - * This class is for the green dog enemy that shoots bones - * It walks back and forth between two set points (startLocation and endLocation) - * Every so often (based on shootTimer) it will shoot a bone enemy + * This class is for the green dog enemy that shoots bones + * It walks back and forth between two set points (startLocation and endLocation) + * Every so often (based on shootTimer) it will shoot a bone enemy */ public class Dog extends Enemy { diff --git a/src/Engine/Config.java b/src/Engine/Config.java index 0040910..4b107fa 100644 --- a/src/Engine/Config.java +++ b/src/Engine/Config.java @@ -14,11 +14,11 @@ public class Config { /** * Path for the Resources folder */ - public static final String RESOURCES_PATH = "Resources/"; + public static final String RESOURCES_PATH = "resources/"; /** * Path for the Map Files folder */ - public static final String MAP_FILES_PATH = "MapFiles/"; + public static final String MAP_FILES_PATH = RESOURCES_PATH + "MapFiles/"; /** * Width of the window */ diff --git a/src/Engine/GamePanel.java b/src/Engine/GamePanel.java index 9c9de94..4ae9f39 100644 --- a/src/Engine/GamePanel.java +++ b/src/Engine/GamePanel.java @@ -166,10 +166,10 @@ public void startGame() { gameThread.start(); try { - music("Resources/Music/music.wav", .05); + music(Config.RESOURCES_PATH + "Music/music.wav", .05); } catch (Exception e) { try { - music("Resources/Music/music.mp3", .05); + music(Config.RESOURCES_PATH + "Music/music.mp3", .05); } catch (Exception ignored) { } diff --git a/src/Game/ScreenCoordinator.java b/src/Game/ScreenCoordinator.java index 84aeb3b..c5d2705 100644 --- a/src/Game/ScreenCoordinator.java +++ b/src/Game/ScreenCoordinator.java @@ -10,6 +10,7 @@ /** * Displays the screen based on the current Game State + * * @author Thomas Kwashnak */ public class ScreenCoordinator extends Screen { @@ -44,7 +45,7 @@ public void update() { // this triggers ScreenCoordinator to bring up a new Screen based on what the gameState is if (previousGameState != gameState) { //This will return an error if there are unimplemented game states - currentScreen = switch(gameState) { + currentScreen = switch (gameState) { case MENU -> new MenuScreen(); case LEVEL -> new PlayLevelScreen(initialMap); case CREDITS -> new CreditsScreen(); @@ -75,6 +76,7 @@ public void draw(GraphicsHandler graphicsHandler) { /** * Delegates the mouse clicked event to the current screen + * * @param e Mouse Event to Delegate */ public void mouseClicked(MouseEvent e) { diff --git a/src/Game/TimeTracker.java b/src/Game/TimeTracker.java index 99ad9e4..bb9bd61 100644 --- a/src/Game/TimeTracker.java +++ b/src/Game/TimeTracker.java @@ -97,6 +97,7 @@ public void stop() { * Calculates the total elapsed time over all levels. This will be more accurate than the given total time as it removes the potential wait * time between stopping one level timer and starting another. This method should be used sparingly, as it requires adding every level-specific * timer together. + * * @return a TimeParser object of the total time */ public TimeParser getElapsedTime() { diff --git a/src/Players/Cat.java b/src/Players/Cat.java index a28760a..ab721b3 100644 --- a/src/Players/Cat.java +++ b/src/Players/Cat.java @@ -12,8 +12,8 @@ import java.util.HashMap; /** - * This is the class for the Cat player character - * basically just sets some values for physics and then defines animations + * This is the class for the Cat player character + * basically just sets some values for physics and then defines animations */ public class Cat extends Player { diff --git a/src/Projectiles/Bone.java b/src/Projectiles/Bone.java index c5f6cba..e015f8a 100644 --- a/src/Projectiles/Bone.java +++ b/src/Projectiles/Bone.java @@ -16,9 +16,9 @@ import java.util.HashMap; /** - * This class is for the bone enemy that the dog class shoots out - * it will travel in a straight line (x axis) for a set time before disappearing - * it will disappear early if it collides with a solid map tile + * This class is for the bone enemy that the dog class shoots out + * it will travel in a straight line (x axis) for a set time before disappearing + * it will disappear early if it collides with a solid map tile */ public class Bone extends Projectile implements Collidable.PreventJump { diff --git a/src/Projectiles/Fireball.java b/src/Projectiles/Fireball.java index 05a4aa4..9057c37 100644 --- a/src/Projectiles/Fireball.java +++ b/src/Projectiles/Fireball.java @@ -14,8 +14,8 @@ /** * This class is for the fireball enemy that the DinosaurEnemy class shoots out - * it will travel in a straight line (x axis) for a set time before disappearing - * it will disappear early if it collides with a solid map tile + * it will travel in a straight line (x axis) for a set time before disappearing + * it will disappear early if it collides with a solid map tile */ public class Fireball extends Projectile { diff --git a/src/Projectiles/LazerBeam.java b/src/Projectiles/LazerBeam.java index a542ddf..c6f2134 100644 --- a/src/Projectiles/LazerBeam.java +++ b/src/Projectiles/LazerBeam.java @@ -15,8 +15,8 @@ /** * This class is for the lazer beam enemy that the DinosaurEnemy class shoots out - * it will travel in a straight line (x axis) for a set time before disappearing - * it will disappear early if it collides with a solid map tile + * it will travel in a straight line (x axis) for a set time before disappearing + * it will disappear early if it collides with a solid map tile */ public class LazerBeam extends Projectile { diff --git a/src/Projectiles/Projectile.java b/src/Projectiles/Projectile.java index 561193f..56d5c1a 100644 --- a/src/Projectiles/Projectile.java +++ b/src/Projectiles/Projectile.java @@ -12,7 +12,7 @@ import java.util.HashMap; /** - * This class is a base class for all projectiles in the game -- all projectiles should extend from it + * This class is a base class for all projectiles in the game -- all projectiles should extend from it */ public class Projectile extends MapEntity implements Collidable.Damage { diff --git a/src/Screens/DifficultySelectScreen.java b/src/Screens/DifficultySelectScreen.java index c041eca..e1fa289 100644 --- a/src/Screens/DifficultySelectScreen.java +++ b/src/Screens/DifficultySelectScreen.java @@ -21,9 +21,11 @@ public class DifficultySelectScreen extends Menu { public DifficultySelectScreen() { MenuOption[][] menu = new MenuOption[][]{ { - new MenuOption("Normal", 100, 150, () -> GamePanel.setDifficulty(NORMAL), CloseOnExecute.CLOSE), - new MenuOption("Hard", 320, 150, () -> GamePanel.setDifficulty(HARD), CloseOnExecute.CLOSE), - new MenuOption("Hardcore", 500, 150,() -> GamePanel.setDifficulty(HARDCORE),CloseOnExecute.CLOSE) + new MenuOption("Normal", 100, 150, () -> GamePanel.setDifficulty(NORMAL), CloseOnExecute.CLOSE), new MenuOption( + "Hard", 320, 150, () -> GamePanel.setDifficulty(HARD), CloseOnExecute.CLOSE), new MenuOption("Hardcore", 500, 150, + () -> GamePanel.setDifficulty( + HARDCORE), + CloseOnExecute.CLOSE) }, { new MenuOption("Back to Main Menu", 225, 375, () -> GamePanel.getScreenCoordinator().setGameState(GameState.MENU)) } diff --git a/src/Screens/GameScoreScreen.java b/src/Screens/GameScoreScreen.java index 017bc40..0933b01 100644 --- a/src/Screens/GameScoreScreen.java +++ b/src/Screens/GameScoreScreen.java @@ -14,6 +14,7 @@ /** * Displays the game score after the player completes the game, showing the time they took to complete each level, the total time, and the * difficulty that the player completed on. + * * @author Thomas Kwashnak */ public class GameScoreScreen extends Menu { @@ -44,7 +45,9 @@ public GameScoreScreen(TimeTracker timeTracker) { /** * Converts the list of level-specific times to a single string, separating each level by a newline character ('\n') + * * @param timeTracker Time Tracker containing the list of level-specific maps used to render + * * @return Displayed list of newlines as a String */ private String levelsToString(TimeTracker timeTracker) { diff --git a/src/Screens/OptionsScreen.java b/src/Screens/OptionsScreen.java index a3698f2..722a6d6 100644 --- a/src/Screens/OptionsScreen.java +++ b/src/Screens/OptionsScreen.java @@ -18,6 +18,7 @@ /** * Gives the user the ability to modify settings regarding the game, including setting the volume and the player color. This class demonstrates an * example of extending the {@link Menu} class properly. + * * @author Thomas Kwashnak */ public class OptionsScreen extends Menu { @@ -37,17 +38,17 @@ Menu class can handle most of the linking between MenuOptions (see the menu opti */ MenuOption[][] items = new MenuOption[][]{ { - //Each of these curly brackets ("{}") describes a line, or elements that will be displayed next to each other + //Each of these curly brackets ("{}") describes a line, or elements that will be displayed next to each other new MenuOption("Volume Control:", 75, 150), - /* - "GamePanel::setVolumeOff" is a new java syntax that references a specific method. In simple terms, "GamePanel::setVolumeOff" - creates an anonymous class based on the Runnable interface (which contains a run() method), and implements the run() method to - just call GamePanel.setVolumeOff(). Lambdas and manual anonymous classes can also be used here - */ + /* + "GamePanel::setVolumeOff" is a new java syntax that references a specific method. In simple terms, "GamePanel::setVolumeOff" + creates an anonymous class based on the Runnable interface (which contains a run() method), and implements the run() method to + just call GamePanel.setVolumeOff(). Lambdas and manual anonymous classes can also be used here + */ new MenuOption("Off", 350, 150, GamePanel::setVolumeOff), new MenuOption("Low", 450, 150, GamePanel::setVolumeLow), - new MenuOption("Medium", 550, 150, GamePanel::setVolumeMed), - new MenuOption("High", 680, 150, GamePanel::setVolumeHigh) + new MenuOption("Medium", 550, 150, GamePanel::setVolumeMed), + new MenuOption("High", 680, 150, GamePanel::setVolumeHigh) }, { new MenuOption("Player", 100, 300), @@ -79,6 +80,7 @@ creates an anonymous class based on the Runnable interface (which contains a run /** * Custom drawing instructions to display the cat based on the chosen option + * * @param handler Graphics Handler used for the current frame. */ public void draw(GraphicsHandler handler) { diff --git a/src/Screens/PlayLevelScreen.java b/src/Screens/PlayLevelScreen.java index a63e3f0..082459f 100644 --- a/src/Screens/PlayLevelScreen.java +++ b/src/Screens/PlayLevelScreen.java @@ -16,7 +16,6 @@ * Probably the most important screen of all. This is the screen that handles rendering and updating the game, along with the sub-screens of the * game, such as the instructions menu and the pause menu. This also contains the time tracker and handles the loading of individual maps * - * * @author Thomas Kwashnak */ /* diff --git a/src/Utils/TimeParser.java b/src/Utils/TimeParser.java index db6349b..85e82f6 100644 --- a/src/Utils/TimeParser.java +++ b/src/Utils/TimeParser.java @@ -24,6 +24,7 @@ public TimeParser(long time) { /** * Adds time to the TimeParser + * * @param time Time, in milliseconds, to add to the timer */ public void addTime(long time) { @@ -32,6 +33,7 @@ public void addTime(long time) { /** * Removes time from the TimeParser + * * @param time Time, in milliseconds, to remove from the timer */ public void subtractTime(long time) { @@ -82,6 +84,7 @@ public String toString() { /** * Gets the current time. Override this method in order to modify what the {@link #toString()} method outputs + * * @return Time, in milliseconds, to display */ public long getTime() {