0.15.0
Indigo 0.15.0
There are quite a few changes an improvements in this release, the two big ones to note (as they are breaking changes) are:
- New game resizing behaviour
- Indigo plugin overhaul.
Read on for more details.
Warning! New game resizing behaviour!
An excellent piece of work has been done that allows you to set the resizing behaviour of your game in your game config, and which has enabled us to deprecate the old (and rather suspect) debounce script.
You can now set resizing your game's resizing policy, like this:
// Resizes to fill the available space
GameConfig.default.withResizePolicy(ResizePolicy.Resize)
// Stays at a fixed size
GameConfig.default.withResizePolicy(ResizePolicy.NoResize)
// Resizes to fill the available space, but retains the original aspect ratio
GameConfig.default.withResizePolicy(ResizePolicy.ResizePreserveAspect)
When building or running an Indigo game via the plugin, the new behaviour will be dealt with automatically.
However, if you have an existing web-page, or you are embedding your game manually, then you'll need to do some additional set up to avoid the game trying to aggressively fill an ever expanding screen!
There are two ways to handle the new behaviour:
- Change your
GameConfig
by calling the.noResize
method, which is the same as.withResizePolicy(ResizePolicy.Resize)
. - Copy (and adjust) the following CSS (included by default by the plugin):
#indigo-container {
display: flex;
align-items: center;
justify-content: center;
padding:0px;
margin:0px;
width: 100vw;
height: 100vh;
}
New Features
Focus events
New events have been added to let you know when the application or canvas has gained or lost focus.
ApplicationGainedFocus
- The application is in focusApplicationLostFocus
- The application has lost focusCanvasGainedFocus
- The game canvas is in focusCanvasLostFocus
- The game canvas has lost focus
Online / Offline network events
New events have been added to indicate whether your game is on/offline. Beware that this is not a complete test, since your machine will be considered online, if your local network is available.
Online
Offline
Mouse and Pointer events now have a lot more detail
The main example of this is MouseEvent.Click
which used to take a simple argument "position", but now has all of the following, inherited from the browser.:
MouseEvent.Click(position, buttons, isAltKeyDown, isCtrlKeyDown, isMetaKeyDown, isShiftKeyDown, movementPosition, button)
Indigo plugin overhaul
This release includes an unusually large amount of work on Indigo's Mill / sbt plugins. The motivation behind the work stems from the question: "Does indigo need an editor?"
Indigo is a code-first engine for people who like making games by writing code. However, writing code alone can be inconvenient, boring, and slow when there is a lot of manual wiring or repeat tasks to perform, and this is where a game editor typically comes in. Your game is ultimately code, but you get a load of tools to speed up the dev process.
Is it possible to make the plugin do more? To reduce the delta between having and not having a full editor by providing more tools in the plugin?
One thing that the Scala community has become somewhat allergic to is a framework. Frameworks however, provide convenience and speed at the cost of having to conform to how they work ...but that sort of sounds like what we want.
Indigo can now be thought of as a library that can optionally be upgraded to a framework:
- Indigo itself is (pretty much) a library - you can combine it with any other web targeted Scala.js project or framework.
- The Indigo plugin - Now adds more functionality and takes Indigo into framework territory for game building speed and convenience.
There is a lot more to do in this space, but this release contains two specific changes:
IndigoOptions
- A simple builder object that replaces all the old sbt / mill settings with a standard interface and expanded options. (When you upgrade an existing project to Indigo 0.15.0, you'll likely get a build error here.)IndigoGenerators
- A set of code generators that help perform/automate a variety of tasks.
Indigo Options
The experience of setting up an Indigo game in Mill and sbt has now been unified with the new IndigoOptions
type, which provides a fluent API for IDE support, examples below:
Mill
The MillIndigo
trait requires the presence of an indigoOptions
instance.
val indigoOptions: IndigoOptions =
IndigoOptions.defaults
.withTitle("My Game")
.withWindowWidth(720)
.withWindowHeight(516)
.withBackgroundColor("black") // defaults to 'white'
.excludeAssets {
case p if p.startsWith(os.RelPath("data")) => true
case _ => false
}
sbt
It is recommended that you do NOT inline your game options, because you will probably want to reference them in you generators later.
import indigoplugin.IndigoOptions
// In sbt, you must use a lazy val.
lazy val myGameOptions: IndigoOptions =
IndigoOptions.defaults
.withTitle("My Game")
.withWindowSize(1280, 720)
.withBackgroundColor("#21293f") // You can use valid CSS colors, like 'black', hex values, or rgb() / rgba()
.excludeAssetPaths {
case p if p.endsWith(".json") => true
case p if p.startsWith("shaders") => true
}
lazy val mygame =
project
.enablePlugins(ScalaJSPlugin, SbtIndigo)
.settings(
name := "mygame",
indigoOptions := myGameOptions
)
Asset paths and filtering
For the most part, the new IndigoOptions
type is simply a re-organisation of the old settings, however, assets paths have changed.
You can set the path to your asset directory using withAssetDirectory
, as follows:
IndigoOptions.defaults
.withAssetDirectoy(os.RelPath.rel / "assets")
The argument is a relative path and can be expressed with a String
, os-lib / Mill RelPath
(recommended, even in sbt), or File
. The path is now relative to the project directory, not the module.
You can also use (as above) excludeAssets
or includeAssets
to filter your assets (these methods use os.Path
s, if you'd prefer a String path, use excludeAssetPaths
and includeAssetPaths
instead). For example you could exclude a whole directory of in-development assets / files, but include one specific file from that folder.
The rules are, in order:
- If there is a specific include rule that covers the file, include it.
- If there is a specific exclude rule that covers the file, exclude it.
- Otherwise, include it.
This behaviour is important when used in conjunction with the asset listing generator (see below).
Indigo Generators
The purpose of these generators is to automate some of the less glamorous work, or fiddly tasks. Much of this work was originally prototyped and validated in the Roguelike demo.
The generators are once again based on a fluent API that you can explore with the assistance of your IDE, however here are simple Mill / sbt examples and a list of the generators available:
Mill
val indigoGenerators: IndigoGenerators =
IndigoGenerators
.mill("com.mygame") // Full qualified package for generated code to live under.
.listAssets("Assets", indigoOptions.assets) // Generate asset information
.generateConfig("MyConfig", indigoOptions) // Generate Config sync'd to build settings.
sbt
Please refer to sbt documentation for ways to tell sbt how to cache these values, if desired.
Compile / sourceGenerators += Def.task {
IndigoGenerators
.sbt((Compile / sourceManaged).value, "com.mygame")
.listAssets("Assets", pirateOptions.assets)
.generateConfig("Config", myGameOptions)
.toSourceFiles
}
Generator list
There are variations of most of the generators to allow you to supply paths as either
String
,File
, oros.Path
/os.RelPath
(as appropriate).
Wiring generators:
listAssets
- takes in yourIndigoOptions
assets configuration and generates a module that contains details of all of your assets, respecting asset filter rules, ready to be loaded in your game. Names that would generate duplicates / collisions will be reported (and will also fail compilation).generateConfig
- takes in youIndigoOptions
settings and generates a game config instance that conforms toGameConfig.default
with your build details embedded, namely the background colour and viewport size.
Data embedding helpers:
embedText
- Read any text file and embeds it into code.embedGLSLShaders
- Reads a pair of GLSL shader files (vertex and fragment), and embeds them as an Indigo shader, optionally validates the code (requires local install of glslValidator).embedAseprite
- Reads an Aseprite json export (array type), and converts it directly into anAseprite
instance and adds it to your source code. This means you do not need to load the separate JSON at runtime. Handy for loading screens.
Data generators:
Data generators generate data! The intended use case here is for when you have a table of data that you'd like to edit conveniently, say, a table of item stats in a spreadsheet that you export to CSV, and then put into your game. Clearly you could load these as a text file, but that means (at runtime) loading the data, parsing it, validating it, and all look ups being subject to things like null checks. The data generators by pass all of that by embedding your data as compile time values you can get full IDE / typechecking assistance on.
A word of caution: It may not be a good idea to do this with really big data sets.
Step one, tell the plugin what format your tabular data is in using one of the following:
.embedCSV
- Comma separated values.embedTSV
- Tab separated values.embedMarkdownTable
- Data in a Markdown table.embedSeparatedData
- Data separated by some other delimiter
The Rules:
- The first row are the headers / fields names
- The first column is the keys
- All rows must be the same length and cannot be empty
- The generator will attempt to guess the type:
Int
,Double
,Boolean
, orString
only. - The generator will choose the lowest common type for each column, and fallback to a
String
.
Then you say how you would like the data to be embedded:
.asEnum(..)
- as a Scala 3 enum
.asMap(..)
- as a Map()
.asCustom(..)
- in some other form that you will supply as a function.
Here is a simple example based on the roguelike demo:
IndigoGenerators
.sbt((Compile / sourceManaged).value, "roguelike.model.gamedata")
.embedMarkdownTable
.asEnum(
"Armour",
os.pwd / "roguelike-generated" / "gamedata" / "armour.md",
"roguelike.model.items.Item"
)
.embedCSV
.asCustom("KeyMapping", os.pwd / "roguelike-generated" / "gamedata" / "key-mappings.md")(
KeyMappingsGen.present("KeyMapping")
)
.toSourceFiles
Bug fixes & Improvements
- Fixed #580: Fixing path finding bug
- Storage events now have error states
- Added
take
,takeRight
, andtakeWhile
operations toBatch
- Focus Events by @hobnob in #578
- Adds online/offline events by @hobnob in #579
- fix: fixing path finding method for close impassable staying by @cheesterx3 in #581
- Storage Event Errors by @hobnob in #582
- Mouse and Pointer Events by @hobnob in #577
- Auto-Resize by @hobnob in #584
- Laika website by @davesmith00000 in #588
- add take operations to Batch (#583) by @AraneusRota in #589
- Create an IndigoOptions type for the plugin by @davesmith00000 in #590
- Plugin config and asset improvements by @davesmith00000 in #593
- Plugin generators by @davesmith00000 in #595
- Update sbt to 1.9.6 by @davesmith00000 in #599
New Contributors
- @cheesterx3 made their first contribution in #581
Full Changelog: v0.15.0-RC3...v0.15.0