Skip to content

0.15.0

Compare
Choose a tag to compare
@davesmith00000 davesmith00000 released this 25 Sep 19:24
· 197 commits to main since this release

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:

  1. New game resizing behaviour
  2. 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:

  1. Change your GameConfig by calling the .noResize method, which is the same as .withResizePolicy(ResizePolicy.Resize).
  2. 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 focus
  • ApplicationLostFocus - The application has lost focus
  • CanvasGainedFocus - The game canvas is in focus
  • CanvasLostFocus - 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:

  1. 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.)
  2. 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.Paths, 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:

  1. If there is a specific include rule that covers the file, include it.
  2. If there is a specific exclude rule that covers the file, exclude it.
  3. 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, or os.Path/os.RelPath (as appropriate).

Wiring generators:

  • listAssets - takes in your IndigoOptions 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 you IndigoOptions settings and generates a game config instance that conforms to GameConfig.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 an Aseprite 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:

  1. The first row are the headers / fields names
  2. The first column is the keys
  3. All rows must be the same length and cannot be empty
  4. The generator will attempt to guess the type: Int, Double, Boolean, or String only.
  5. 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, and takeWhile operations to Batch

New Contributors

Full Changelog: v0.15.0-RC3...v0.15.0