diff --git a/docs/01-main_class_width_user_input.md b/docs/01-main_class_width_user_input.md index 1de0d71..3e9c5bc 100644 --- a/docs/01-main_class_width_user_input.md +++ b/docs/01-main_class_width_user_input.md @@ -1,44 +1,35 @@ # Main class and user input -Building a game can be a game itself; or a nightmare. Depending on the tools, the team, the skills, … a lot of thing can -interact badly with the process of creating a game. And this is exactly what we won't talk about!:) +Building a game can be a game itself; or a nightmare. Depending on the tools, the team, the skills, … a lot of thing can interact badly with the process of creating a game. And this is exactly what we won't talk about!:) -Here I will talk about creating a A-B-AB game with the only weapons provided by the nice Java JDK 19:) And no, I won't -use JavaFX (beuarrk:)) I'll use the good old AWT and Swing (yes, I am maybe a 50 years old man…). +Here I will talk about creating a A-B-AB game with the only weapons provided by the nice Java JDK 19:) And no, I won't use JavaFX (beuarrk:)) I'll use the good old AWT and Swing (yes, I am maybe a 50 years old man…). Anyway let's try it through a series of posts to design some code. ## Creating a simple class The basic Main class will support the famous "triptic" of the game loop: manage input, update objects, and draw -everything on screen! -But do you know why this is the basic default pattern for a game ? +everything on screen! But do you know why this is the basic default pattern for a game ? ## A bit of video game history -Why the game loop exists ? This is a very good question and the reason why is a historically based answer. Everything -starts from the first ever video game: PONG. +Why the game loop exists ? This is a very good question and the reason why is a historically based answer. Everything starts from the first ever video game: PONG. ![Pong, where all begin !](https://cdn-images-1.medium.com/max/800/0*ySNC72GHeT19Nq3N "wikipedia PONG screenshot") -_figure 1 - Pong where all begins (ref:[https://fr.wikipedia.org/wiki/Pong](https://fr.wikipedia.org/wiki/Pong))_ +_figure 1 - Pong where all begins ( +ref:[Pong on wikipedia](https://fr.wikipedia.org/wiki/Pong))_ -The original Pong video game from wikipediaAt this very beginning time, the processor to execute tasks is a very a slow -on, almost some hundreds of Khz as CPU frequency. To understand the scale we are talking about, current processor are -running at 2 to 4 GHz! -So processor are very slow, each cycle of CPU is a precious one. So every line of code is very optimized and clearly -dedicated to some precise tasks. +The original Pong video game from wikipediaAt this very beginning time, the processor to execute tasks is a very a slow on, almost some hundreds of Khz as CPU frequency. To understand the scale we are talking about, current processor are running at 2 to 4 GHz! +So processor are very slow, each cycle of CPU is a precious one. So every line of code is very optimized and clearly dedicated to some precise tasks. -And another element must be taken in account: the display process. At this time, screen where not flat one with a bunch -of LCD, but CRT ones. CRT display screen are based on ionic flow started from a cathode (electronic gun) and moving to -the anode (the screen grid) to excite fluorescent layer in the intern face of the glass bulb. +And another element must be taken in account: the display process. At this time, screen where not flat one with a bunch of LCD, but CRT ones. CRT display screen are based on ionic flow started from a cathode (electronic gun) and moving to the anode (the screen grid) to excite fluorescent layer in the +intern face of the glass bulb. -And swiping the all surface of the screen has a time cost: to display 25 frame per seconds, we need 16ms to swipe a -frame. -A CRT tube with its ions gun!The CRT Tube is nothing more than a big bubble light. (3) the cathode emits ions (1) and -(2) are anodes, deflecting ion ray to screen, lighting a fluorescent dot. +And swiping the all surface of the screen has a time cost: to display 25 frame per seconds, we need 16ms to swipe a frame. A CRT tube with its ions gun!The CRT Tube is nothing more than a big bubble light. (3) the cathode emits ions (1) and (2) are anodes, deflecting ion ray to screen, lighting a fluorescent dot. -![A CRT diagram with ions gun and anodes deflectors](illustrations/figure-crt.jpg "A CRT diagram with ions gun and anodes deflectors (c) myself with my own hands !") +![A CRT diagram with ions gun and anodes deflectors](images/figure-crt.jpg "A +CRT diagram with ions gun and anodes deflectors (c) myself with my own hands !") _figure 2 - A CRT diagram with ions gun and anodes deflectors_ @@ -48,20 +39,22 @@ So capturing input, moving things and displaying things must be done in 16ms. An So the game process is a LOOP. that's why we talk about a Main Loop: -![The basic Main loop explained with a pencil: the method to keep a fixed frame rate !](illustrations/figure-game-loop.jpg "the basic Main loop explained with a pencil: the method to keep a fixed frame rate ! (c) myself with my own hands !") +![The basic Main loop explained with a pencil: the method to keep a fixed +frame rate !](images/figure-game-loop.jpg "the basic Main loop explained with a +pencil: the method to keep a fixed frame rate ! (c) myself with my own hands !") _figure 3 - The basic Main loop explained with a pencil: the method to keep a fixed frame rate !_ -There is also some advanced version of the Main Loop, where multiple update can be performed between each rendering -phase, the timer is around the update methods only: +There is also some advanced version of the Main Loop, where multiple update can be performed between each rendering phase, the timer is around the update methods only: -![The advanced method to keep a fixed update rate](illustrations/figure-game-loop-fixed.jpg "The advanced method to keep a fixed update rate (c) myself with my own hands !") +![The advanced method to keep a fixed update rate](images/figure-game-loop-fixed.jpg "The advanced method to keep a fixed update rate (c) myself with my own hands !") _figure 4 - The advanced method to keep a fixed update rate_ -I can only invite you to read the fantastic book from Robert Nystrom for details about the Main loop. +I can only invite you to read the fantastic book "Game Programming Pattern" from Robert Nystrom[¹] for details about the Main loop. -> **Note:** diagram are largely inspired by the Robert Nystrom book, thanks to him to had shared his own knowledge! +> **Note:** diagram are largely inspired by the Robert Nystrom book, thanks to +> him to had shared his own knowledge! Anyway, I need to implement my own. As a good diagram is better than word: @@ -92,10 +85,12 @@ public class Main { public void loop() { while (!isExit()) { + // --- simplified version input(); update(); render(); waitUntilNextFrame(); + // --- } } @@ -110,45 +105,72 @@ public class Main { } ``` -So, what do you think of my (too?) basic class ? nothing fancy, nothing "bling bling". only useful and mandatory. +So, what do you think of my (too?) basic class ? nothing fancy, nothing "bling-bling". only useful and mandatory. + +So yes, as you may be now, the game loop is the heart of any old school game, and even some Big game engine keep running such old loop. -So yes, as you maybe now, the gameloop is the heart of any old school game. and even some Big game engine keep running -such old loop. Let's dive into some details. The Loop is certainly the most important one, so let get some code: ```java public class Main { - //... - public void loop() { - int frames = 0; - int fps = getTargetFps(); - double internalTime = 0; - double previousTime = System.currentTimeMillis(); - double currentTime = System.currentTimeMillis(); - double elapsed = currentTime - previousTime; - create(this); - while (!isExitRequested()) { - currentTime = System.currentTimeMillis(); - input(this); - elapsed = currentTime - previousTime; - if (!isPaused()) { - update(this, elapsed); - } - render(this, fps); - frames += 1; - internalTime += elapsed; - if (internalTime > 1000.0) { - fps = frames; - frames = 0; - internalTime = 0; - } - waitUntilNextFrame(elapsed); - previousTime = currentTime; - } + //... + public void loop() { + int frames = 0; + int fps = getTargetFps(); + double internalTime = 0; + double previousTime = System.currentTimeMillis(); + double currentTime = System.currentTimeMillis(); + double elapsed = currentTime - previousTime; + create(this); + while (!isExitRequested()) { + currentTime = System.currentTimeMillis(); + input(this); + elapsed = currentTime - previousTime; + if (!isPaused()) { + update(this, elapsed); + } + render(this, fps); + frames += 1; + internalTime += elapsed; + if (internalTime > 1000.0) { + fps = frames; + frames = 0; + internalTime = 0; + } + waitUntilNextFrame(elapsed); + previousTime = currentTime; } + } + + default void waitUntilNextFrame(double elapsed) { + try { + double timeFrame = 1000.0 / getTargetFps(); + int wait = (int) (elapsed < timeFrame ? timeFrame - elapsed : 1); + Thread.sleep(wait); + } catch (InterruptedException ie) { + System.err.println("error while trying to wait for sometime"); + } + } + //... +} +``` + +This implementation of the game-loop is named "Fixed Frame game loop". To maintain a sustainable frequency for that loop, I use the default current traditional FPS: 60 frames per second, this is now an internal configuration: +```java +double timeFrame=1000.0/getTargetFps(); +``` + +> **INFO**
We will discover the configuration mechanism on the 3rd +> chapter. + +So starting with that , defining the waiting time is a simple calculus int the wait method to compute the rest of the timeframe minus elapsed time: + +```java +public class Main { + //... default void waitUntilNextFrame(double elapsed) { try { double timeFrame = 1000.0 / getTargetFps(); @@ -157,32 +179,55 @@ public class Main { } catch (InterruptedException ie) { System.err.println("error while trying to wait for sometime"); } - }} + } //... } ``` -So to maintain a sustainable frequency for that loop, I use the default current traditional FPS: 60 frames per second. - -So starting with that , defining the waiting time is a simple calculus. +and the rest of the loop process is list of calls: ```java -int timeFrame=1000/60; - int wait=timeFrame-elapsed; -``` - -and the rest of the loop process is simple calls: +public class Main { + //... + public void loop() { + currentTime = System.currentTimeMillis(); + + // (1) + input(this); + elapsed = currentTime - previousTime; + // (2) + if (!isPaused()) { + update(this, elapsed); + } + // (3) + render(this, fps); + + // compute next wait time until + // FPS time frame is reach. + frames += 1; + internalTime += elapsed; + if (internalTime > 1000.0) { + fps = frames; + frames = 0; + internalTime = 0; + } + waitUntilNextFrame(elapsed); -```java -elapsed=currentTime-previousTime; - input(); - update(elapsed); - render(); + previousTime = currentTime; + } + //... +} ``` -- input() will manage all the player's device input like keyboard, mouse or joystick, -- update(elapsed) will compute all the moves and animations for all the game objects, -- render() will draw the resulting objects and display that on screen. +1. `input()` will manage all the player's device input like keyboard, mouse or + joystick, +2. `update(long)` will compute all the moves and animations for all the game + objects, +3. `render(Game, long)` will draw the resulting objects and display that on + screen. And when you execute this incredible class… nothing will happen. -BUT… we now have the framework of our future game. I'm not joking, this is a fact ;) +BUT… we now have the framework of our future game. I'm not joking, this is a +fact ;) + +[^1]: Game programming Patterns, Robert Nystrom, 2014, Genever Benning, 520 pages - http://gameprogrammingpatterns.com/ \ No newline at end of file diff --git a/docs/02-adding_entity_and_entitytype.md b/docs/02-adding_entity_and_entitytype.md index 82f9d80..ee05ccc 100644 --- a/docs/02-adding_entity_and_entitytype.md +++ b/docs/02-adding_entity_and_entitytype.md @@ -1,13 +1,26 @@ # Adding Entity and EntityType -The `Entity` is a class to define all the moving (or not) object on screen. +The `Entity` is the basics for every object in the game. From the simple spark of fire to the full animated main +character, all are `Entity`. A `Node` hierarchical tree definition will structure all the Entity in the game. + The `EntityType` define the type of rendering and management. +here is an illustration of the required classes, detailed in the bellow sections. + ```plantuml @startuml !theme plain +hide Node attributes +interface Node<>{ + + getName():String + + long getId():long + + setParent(T p):T + + getParent():T + + addChild(T c):T + + getChild():List +} hide Entity methods -class Entity{ +class Entity implements Node{ - id:long - name:String - x:double @@ -19,6 +32,8 @@ class Entity{ } hide EntityType methods enum EntityType{ + - DOT, + - LINE, - RECTANGLE, - ELLIPSE, - IMAGE @@ -27,10 +42,91 @@ Entity --> EntityType:type @enduml ``` -## the EntityType enumeration +## The Entity class + +These entities are defined through a class, grouping all the required attributes to make it movable, drawable as some +basic geometric shapes or image, and displayed at the right place. + +An entity must be identifiable with a unique ID, and for a more common access and retrievable, with a name. +This ID is auto-generated by an incremental counter, and default name is generated based on this counter, to be sure +that every Entity is uniquely identified. + +```java +public class Entity { + private static long index = 0; + private long id = ++index; + private String name = "default_" + id; + + EntityType type = EntityType.RECTANGLE; + Vector2D position; + Vector2D velocity; + + double width = 16, height = 16; + + BufferedImage image = null; + Color borderColor = Color.WHITE; + Color fillColor = Color.BLUE; + int direction = 0; + + private Entity parent; + private List child = new ArrayList<>(); + + Map attributes = new HashMap<>(); + +} +``` + +The 3 first attributes are managing identifier: + +- `index` is the internal static counter of entity generated in the game, +- `id` is the unique identifier for that new entity, initialized based on the next value of the counter, +- `name` is the human-readable name, set by default to "default_" + id value; + +The 5 next attributes are dedicated to geometric and physic computation: + +- `type` is defining the global shape of this entity, (see `EntityType`), +- `position` a 2D vector defining the position of the entity in the play area, +- `velocity` a 2D vector to define current velocity on the play area of this entity, +- `width` and `height` define the size (as a rectangular box) of that entity. + +And the next attributes are used for rendering purpose: + +- `image` define a `BufferedImage` to be drawn as entity at `position`, +- `borderColor` is the color used to draw edge of this Entity's shape, +- `fillColor` is the color defined to fill this shape. +- `direction` is used to draw image left (<0) or right (=>0) oriented. + +As `Entity` can be hierarchically grouped, we need some `Node` management capability. +The 2 next attributes a dedicated to manage this entity's tree: + +- `parent` the parent entity if exists, else null, +- `child` a list of child Entity linked to this one. + +## The Node Interface + +A mentioned on the Entity description, a hierarchical tree is used to organize the game entities. + +This tree is composed of node. here is the interface to be implemented in the tree nodes: + +```java +public interface Node { + String getName(); + + long getId(); + + T setParent(T p); + + T getParent(); + + T addChild(T c); + + List getChild(); +} +``` -TODO +These Entity methods are defined to match this interface, based on the 2 attributes `parent` and `child`. +it will also use `id` and `name`. -## the Entity class +## The EntityType enumeration -TODO +TODO \ No newline at end of file diff --git a/docs/03-configuration.md b/docs/03-configuration.md index df72fef..2a2febb 100644 --- a/docs/03-configuration.md +++ b/docs/03-configuration.md @@ -2,42 +2,46 @@ ## Goals -The COnfiguration principle consists in loading values from a properties file into a map wich keys are enumeratioin entries, and typed values the conversion of properties values into some required objects. +The `Configuration` principle consists in loading values from a properties file into a map which keys are enumeration +entries, and typed values the conversion of properties values into some required objects. ## Proposed design -The enumration entry will be of type : +The enumeration entry will be of type : ```java public enum ConfigAttribute { MY_VALUE( - "game.phusic.gravity", // the property key entry - "gravity,g", // the possible CLI arguments (long and short) - "The gravity value used by physic engine computation and affects every Entity in the World game, defaut is set to no gravity." , // all is in the text ;) - 0.0, // the default value if no entry provided - v->Double:valueOf), // the way to parse the string value and convert it to the a required type (thanks to the Java Function interface). + "game.physic.gravity", // the property key entry + "gravity,g", // the possible CLI arguments (long and short) + "The gravity value used by physic engine computation and affects every Entity in the World game, defaut is set to no gravity.", // all is in the text ;) + 0.0, // the default value if no entry provided + v -> Double::valueOf), // the way to parse the string value and convert it to the required type (thanks to the Java Function interface). //... ; } ``` -You can add any other entry in the enum to define new confguration key/values. + +You can add any other entry in the enum to define new configuration key/values. Anatomy of a `ConfigAttribute` enum entry: -- a `String` fo rthe configuration attribute entry, +- a `String` for the configuration attribute entry, - a `coma separated list of String` to define the possible CLI arguments (long and short) -- a clear and understandable description as a `String` for this configuration entry. this text will be displayed as help when requested. -- an `Object` instance as a default value (eg.0.0 for a double, "blabla" for a String, ttc...) -- a Java `Function` implementation to convert the loaded String form an entry in the properties file or the argument value from the command line to the required data type. +- a clear and understandable description as a `String` for this configuration entry. this text will be displayed as help + when requested. +- an `Object` instance as a default value (eg.0.0 for a double, "blablabla" for a String, ttc...) +- a Java `Function` implementation to convert the loaded String form an entry in the properties file or the + argument value from the command line to the required data type. -And from the config.propeties file, the corresponding entry : +And from the `config.propeties` file, the corresponding entry : ```properties # this configuration key is a double value game.physic.gravity=0.981 ``` -This property value can be overriden through java command line argument:` +This property value can be overridden through java command line argument:` ```bash $> java -jar simplegameclass-1.0.jar gravity=0.981 @@ -46,15 +50,19 @@ $> java -jar simplegameclass-1.0.jar gravity=0.981 ## File Rules 1. The `config.properties` file is provided as default values for the full game framework in the jar itself. -2. if a `my-config.properties` is provided in the same location as the JAR file itself, all entries in this properties file will be used to override already defined values by the new provided ones. -3. As soon a value has been modified through a command line argument a `backup.properties` is saved in the same root path os the jar itself. +2. if a `my-config.properties` is provided in the same location as the JAR file itself, all entries in this properties + file will be used to override already defined values by the new provided ones. +3. As soon a value has been modified through a command line argument a `backup.properties` is saved in the same root + path os the jar itself. ## The `Configuration` service -The service will provide -4. all the required default behavior to satisfy the File rules. +The service will provide + +4. all the required default behavior to satisfy the File rules. 5. the required API to let a specific configuration file to be loaded (to be used for unit test purpose) -6. an entry point to intiialize the internal configuration value object from the file and from the arguments passed from the common Java main method. +6. an entry point to initialize the internal configuration value object from the file and from the arguments passed from + the common Java main method. ### The Configuration class design @@ -73,31 +81,43 @@ class Configuration { @enduml ``` -From the command line, +From the command line, -7. if an unknown attribute is used, the program exit without execution, but with a clear message giving an error message. +7. if an unknown attribute is used, the program exit without execution, but with a clear message giving an error + message. 8. the user can request to display help for the game arguments with the 'help' or '?' first argument ```bash $> java -jar simplegameclass-1.0.jar help ``` -9. when the Configuraton properties file is read and parsed, some clear INFO level messages output the detected values for each entry. -10. If an unkown values is used, the program properly exits without execution, but with a help message giving the error message about the unknown attribute. +9. when the Configuration properties file is read and parsed, some clear INFO level messages output the detected values + for each entry. +10. If an unknown values is used, the program properly exits without execution, but with a help message giving the error + message about the unknown attribute. -To use an initiazed configruation value : +To use an initialized configuration value : ```java -Configuration config = new Configuration( - "/config.properties", - new String[]{} ); - -String windowTitle = config.get( - ConfigAttribute.WINDOW_TITLE); +public class Main { + public void main(String[] args) { + Configuration config = new Configuration( + "/config.properties", + args); + String windowTitle = config.get(ConfigAttribute.WINDOW_TITLE); + System.out.printf("title:%s%n", windowTitle); + } +} ``` -for a properties entry like : +And the default value is set in a properties file entry like : ```properties game.window.title="My title window" ``` + +Ok, we now have a very good configuration utility to manage all the future configuration values fo our game framework +development and its service initialization. + +It is now time to move to the [next chapter](04-physic_with_world_and_material.md "Physic with World and Material") +about Adding some moves and physics computation laws. \ No newline at end of file diff --git a/docs/images/figure-crt.jpg b/docs/images/figure-crt.jpg new file mode 100644 index 0000000..1770fa1 Binary files /dev/null and b/docs/images/figure-crt.jpg differ diff --git a/docs/images/figure-game-loop-fixed.jpg b/docs/images/figure-game-loop-fixed.jpg new file mode 100644 index 0000000..f9daa46 Binary files /dev/null and b/docs/images/figure-game-loop-fixed.jpg differ diff --git a/docs/images/figure-game-loop.jpg b/docs/images/figure-game-loop.jpg new file mode 100644 index 0000000..79fbbb0 Binary files /dev/null and b/docs/images/figure-game-loop.jpg differ