FastJ 1.5.0 is finally here! With over 300 commits since version 1.4.0, the FastJ game engine has grown in maturity and gained new skills.
This version of FastJ includes a revamped Drawable system with gradients and outlines support for polygons, more logic manager options, better keyboard input support, the addition of an audio engine, and much much more! Read on to find out what's new.
#
Discord ServerI'm proud to announce that the FastJ Discord server is now open -- join here!
#
New Example ProgramsWith the popularity and feature list of FastJ growing, 13 new example programs have been added! They cover a range of topics like getting started, engine configuration, keyboard and mouse input, text, polygons, models, and more.
#
Examples Source Code- Hello, FastJ -- Creating a Window in FastJ
- FastJEngine Logging -- Logging information and errors in FastJ
- FastJEngine Configuration -- Configuring FastJ's FPS, UPS, and viewer/game window size
- Polygon2D -- Creating and using
Polygon2D
objects (polygons) in FastJ - Text2D -- Creating and using
Text2D
objects (text) in FastJ - Model2D -- Creating and using
Model2D
objects (models) in FastJ - Model File Read/Write -- Reading and writing
Model2D
objects from/to files in FastJ - Display Rendering Settings -- changing a
Display
's global rendering settings - Keyboard Input -- Getting keyboard input in FastJ
- Mouse Input -- getting mouse input in FastJ
Furthermore, the following examples have been removed:
- "Hello World!" has been removed, in favor of "Hello, FastJ!"
#
Code ImprovementsThe full list of new things in FastJ is so long, I couldn't possibly put it all here! Instead, I'll give you a run-down of all the new features you'll see in this version of FastJ.
#
Audio EngineBetween 1.4.0 and 1.5.0, FastJ sprouted an audio engine! Thanks to the availability of Java's standard audio library, no external dependencies were needed to create this engine.
Its API is straightforward for simpler cases...
AudioManager.playSound(Path.of("path/to/audio"));
...but can handle more complex needs as well!
StreamedAudio audio = AudioManager.loadStreamedAudio(Path.of("path/to/audio"));
FloatControl panControl = audio.panControl();panControl.setValue(pancontrol.getMaximum());
audio.getAudioEventListener().setOnPlayAction(() -> FastJEngine.log("Now Playing -- this sound!"));audio.play();
The new audio engine supports the following:
- Loading audio into memory
- Streaming audio from a file
- Play/Pause/Resume/Stop options
- Configurable Audio Events (audio stream open/close, audio play/pause/resume/stop)
- (memory-loaded audio only) Looping and Playback Position options
- (streamed audio only) Gain/Pan/Balance/Mute controls
For the best information, check out the audio usage example.
#
Better Camera TransformationsWith the transformation improvements made for all Drawables
(more on that here), the Camera
class has also been upgraded to be able to use these transformations:
// This example assumes "this" to be a SceneManager, SimpleManager, or other LogicManager// that contains a "getCamera" method.Camera camera = this.getCamera();camera.translate(new Pointf(50f));camera.rotate(90f);camera.scale(2.5f);
Here, the camera has been translated (50, 50)
, rotated 90 degrees, and zoomed in to 250%.
#
Display Render Settings ImprovementsThe Display
class' old render setting class was fine, but not very intuitive or informative for newer users -- combing through the AWT documentation is slightly daunting just from how much content there is.
To combat this, a RenderSettings
class has been created. It contains all the different available rendering options, while also compiling the AWT documentation into smaller, more readable sentences.
display.modifyRenderSettings(RenderSettings.AntiAliasing.Enable);
#
DrawUtil -- Paint Comparingjava.awt.Paint
classes can now be compared using DrawUtil.paintEquals
.
#
Keyboard ImprovementsFastJ's keyboard input API has improved by leaps and bounds since 1.4.0. Read on!
#
Better Keys HandlingIn 1.4.0, checking for key equality was fine, but not very intuitive for situations such as determining if the right shift key is pressed:
if (Keyboard.isKeyDown(Keys.Shift)) { // not what we want -- if no key location is specified, this type of key defaults to the left shift.}
if (Keyboard.isKeyDown(Keys.Shift, KeyLocation.Left)) { // this is what we want, but it makes life for beginners and the unsuspecting harder.}
With FastJ 1.4.0, Keys.Shift
is just an integer, meaning it can't safely store the key's location on the keyboard. In FastJ 1.5.0, Keys
is now an enum and handles this well:
if (Keyboard.isKeyDown(Keys.RightShift)) { // simpler to understand, and still achieves what we want!}
Furthermore, the new Keys enum allows you to get a Keys
instance based on a provided java.awt.KeyEvent
or provided key name, key code, and key location. We can use this to our advantage when dealing with raw KeyEvent
instances, or building a key from scratch.
// assume a KeyEvent keyEvent is presentKeys keyfromKeyEvent = Keys.get(keyEvent);
Keys keyFromScratch = Keys.get("Enter", KeyEvent.VK_ENTER, KeyEvent.KEY_LOCATION_STANDARD);
#
FastJ-Defined Keyboard EventsFastJ's KeyboardActionListener
no longer directly passes KeyEvent
instances -- FastJ is now equipped with its own KeyboardEvent
classes!
These classes (KeyboardStateEvent
and KeyboardTypedEvent
) provide easy access to the key event and key name, and KeyboardStateEvent
provides a getKey
method which returns the corresponding Keys
instance for the event.
Both KeyboardStateEvent
and KeyboardTypedEvent
also provide a fromKeyEvent
method which allows you to create one from a KeyEvent
:
// assume KeyEvent keyEvent exists// assume KeyEvent keyTypedEvent exists
KeyboardStateEvent keyboardStateEvent = KeyboardStateEvent.fromKeyEvent(keyEvent);KeyboardTypedEvent keyboardTypedEvent = KeyboardTypedEvent.fromKeyEvent(keyTypedEvent);
#
LogicManager RevampThe LogicManager
class has been repurposed into an interface containing the basic needs for all logic managers. Two new managers implement this class:
SceneManager
-- the oldLogicManager
class.SimpleManager
-- the manager for creating programs without the use of scenes.
#
Old LogicManager becomes SceneManagerWhat was once named LogicManager
is now called SceneManager
. It functions the same as before -- this was changed to allow the LogicManager
name to represent what all game management classes would be based on.
#
SimpleManager -- the Return of One-File GamesIn the early days of FastJ, long before it became a reasonable game engine, there was only the option to create games using a very simple logic manager -- no scenes, just general control from a single file. Before long, this was replaced with the scene system and soon forgotten... but not for long.
Now, the manager returns with a new name and one thing in mind -- single-"scene" programs. With the SimpleManager
class, the starting code needed for a window in FastJ was reduced from this:
import io.github.lucasstarsz.fastj.engine.FastJEngine;import io.github.lucasstarsz.fastj.graphics.Display;
import io.github.lucasstarsz.fastj.systems.control.LogicManager;
public class Main extends LogicManager {
@Override public void setup(Display display) { GameScene scene = new GameScene(); addScene(scene); setCurrentScene(scene); loadCurrentScene(); }
public static void main(String[] args) { FastJEngine.init("Hello, FastJ!", new Main()); FastJEngine.run(); }}
import io.github.lucasstarsz.fastj.graphics.Display;
import io.github.lucasstarsz.fastj.systems.control.Scene;
public class GameScene extends Scene {
public ButtonScene(String sceneName) { super(sceneName); }
@Override public void load(Display display) { }
@Override public void unload(Display display) { }
@Override public void update(Display display) { }}
to this:
import tech.fastj.engine.FastJEngine;import tech.fastj.graphics.display.Display;
import tech.fastj.systems.control.SimpleManager;
public class Main extends SimpleManager {
@Override public void init(Display display) { }
@Override public void update(Display display) { }
public static void main(String[] args) { FastJEngine.init("Hello, FastJ!", new Main()); FastJEngine.run(); }}
#
Math ImprovementsSomewhat similar to the version 1.4.0 release, the math section of FastJ has received several new functionalities:
- Inverse Lerp -- Thanks once again to @YeffyCodeGit, FastJ's
Maths
class now contains aninverseLerp
function for all your interpolation needs. - Normalization -- number normalization and denormalization are now both available in the
Maths
class, asnormalize
anddenormalize
respectively. - Range Bounding -- Having use in color linear interpolation, range bounding methods have been added:
withinRange
andwithinIntegerRange
. - Pointf Lerping -- As inverse lerp was added to the
Maths
class, it also became apparent that the Pointf class was also missing linear interpolation! It now contains bothlerp
andinverseLerp
static methods. - Color Lerping -- From inside the
DrawUtil
class, colors were given staticlerp
andinverseLerp
methods as well.
#
Revamped Drawable SystemDrawables were one of the main focus areas for this update. As such, several useful improvements, upgrades, and standardizations have been made!
#
Drawable BuildersFirst off, all Drawable
-based classes now have a corresponding builder that aids in creation of the class. This allows for much more configuration than the addition of more and more constructor overloads, as well as a generally simpler API for both the developer(s) and users.
Assume Pointf[] boxCoordinates = DrawUtil.createBox(10f, 10f, 50f);
has been declared in each example.
For Polygon2D
, an array of points is all you need:
Polygon2D box = Polygon2D.fromPoints(boxCoordinates);
but if you want to configure more options, the extended builder can help you out!
BasicStroke outlineStroke = new BasicStroke(5.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
Pointf translation = new Pointf(20f, 10f);float rotation = 30f;Pointf scale = new Pointf(0.5f, 0.5f);
Polygon2D box = Polygon2D.create(boxCoordinates, RenderStyle.FillAndOutline) .withFill(Color.blue) .withOutline(outlineStroke, Color.black) .withTransform(translation, rotation, scale) .build();
This creates a box with a rendering style of both fill
and outline
variants, meaning it has both an outline color/stroke and a fill color.
- Its fill color is
blue
. - The outline color is
black
. - The outline stroke (
outlineStroke
) is 5 pixels thick and had rounded edges. - It also contains a translation of
(20, 10)
, a 30 degree rotation, and has its size scaled down to 50%.
Beyond that, the provided setter methods can still be used post-creation to modify the main values of the Polygon2D
.
Both Text2D
and Model2D
have received builders as well. The attributes of each of the above three are explained in the following example programs:
#
Transforms for All!The transformation system has been massively updated, as briefly referenced here.
With this massive update, all Drawable
s can now be translated, rotated, and scaled as much as needed!
Here's an example using the Text2D
class, which was previously unable to rotate or scale.
Text2D text = Text2D.fromText("Text2D has transformations!");text.translate(new Pointf(50f));text.rotate(90f);text.scale(2.5f);
Here, the text has been translated (50, 50)
, rotated 90 degrees, and scaled up to 250% size.
These options are available for all Drawable
-based classes, even UIElement
s! Feel free to check it out.
#
GradientsAll Drawable
classes can now also have their fill colors as gradients! (no outline support just yet). There are two types of gradients available -- linear gradients, and radial gradients. Both will be explained here.
These builders both have a few limitations that will be expanded in later versions:
- No support for repeating gradients
These builders also have some limitations that will likely not be expanded upon in later versions, due to performance issues:
- Gradients may not contain more than 8 colors.
#
Linear GradientsCreating a linear gradient takes a few things:
- Starting and Ending points
- These can either both be
Pointf
-based points, orBoundary
values. Using boundary values requires the inclusion of aDrawable
object to evaluate the boundaries for.
- These can either both be
- Colors! There are lots of premade colors to choose from (
Color.cyan
andColor.yellow
, to name a few) and you can create your own -- both are supported.
Here's two examples of linear gradients, one using Pointf
s and one using a Drawable
and specified boundaries.
/* Linear Gradient using Pointfs */Pointf startingPoint = Pointf.Origin.copy();Pointf endingPoint = new Pointf(100, 100);LinearGradientPaint linearGradient = Gradients.linearGradient(startingPoint, endingPoint) .withColors(Color.red, Color.green, Color.black) .withColor(Color.blue) .withColor(new Color(255, 255, 0, 100)) .build();
/* Linear Gradient using Drawables */LinearGradientPaint linearGradient = Gradients.linearGradient(box, Boundary.TopLeft, Boundary.BottomRight) .withColors(Color.red, Color.green, Color.black) .withColor(Color.blue) .withColor(new Color(255, 255, 0, 100)) .build();
#
Radial GradientsOn the other hand, creating radial gradients takes a few things:
- A center and radius
- this can either be a
Pointf
-defined centerpoint andfloat
radius, or just aDrawable
which will handle both.
- this can either be a
- Colors! There are lots of premade colors to choose from (
Color.lightGray
andColor.blue
, to name a few) and you can create your own -- both are supported.
Here's two examples of radial gradients, one using the center and radius, and the other using just the drawable.
/* Radial Gradient using Center and Radius */Pointf center = new Pointf(50f, 100f);float radius = 25f;RadialGradientPaint radialGradient = Gradients.radialGradient(center, radius) .withColors(Color.lightGray, Color.cyan, Color.darkGray) .withColor(Color.pink) .withColor(new Color(255, 255, 0, 100)) .build();
/* Linear Gradient using Drawables */RadialGradientPaint radialGradient = Gradients.radialGradient(box) .withColors(Color.lightGray, Color.cyan, Color.darkGray) .withColor(Color.pink) .withColor(new Color(255, 255, 0, 100)) .build();
#
Polygon2D Render StylesAs briefly shown in the Drawable
builders section, the Polygon2D
class has received an attribute called RenderStyle
. This attribute allows you to control how and what of a specific Polygon2D
should be rendered -- its outline, its fill, or both.
There are specific options for each possibility:
RenderStyle.Fill
RenderStyle.Outline
RenderStyle.FillAndOutline
Keeping consistent with the older API, the default is RenderStyle.Fill
-- polygons will just have their fill rendered. This can be changed using the builder, as well as through a setter method. Both are shown below.
/* Changing RenderStyle through the builder */Polygon2D box = Polygon2D.create(boxCoordinates, RenderStyle.Outline) .build();
/* Changing RenderStyle through setRenderStyle method */box.setRenderStyle(RenderStyle.FillAndOutline);