1 of 40

Unity… but differently

Alkemi - ADDON - May 2018

2 of 40

Who we are

Alkemi - ADDON - May 2018

  • A team of 5
  • 1 full dev + 3 tech artists + 1 3D artist
  • Mostly ex-Flash people
  • Game developers for 5 to 15 years
  • Unity users since 2009
  • Producing self financed weird games with high production values

3 of 40

What we do

Alkemi - ADDON - May 2018

Transcripted - 2012 (2016)

a.k.a geometry wars meets Zuma

Drifting Lands - 2017

a.k.a Diablo meets R-Type

4 of 40

Disclaimer

Alkemi - ADDON - May 2018

We’re not married to Unity and we keep our eyes open to all solutions.

The following may not be relevant for all teams and all kind of projects. We mainly do 2.5D games.

We know a bit about making games but since we’re unable to sell them decently, maybe we’re not that good...

5 of 40

Why the choices, I’m going to present:

Alkemi - ADDON - May 2018

To keep our design patterns

& capitalize on our experience which is older than Unity

To stay relatively independent

We like Unity, but none of our tools or process would be complicated to port on some other engine.

Get rid of as much useless-under-the-hood process as possible

In a rich tool like Unity, often a lot more happens than what is actually required for simple things.

6 of 40

Unity ‘by the book’

Alkemi - ADDON - May 2018

Unity is usually presented and demonstrated with a

Scene > GameObject > Component paradigm

  • Game are made of levels = Scenes
  • Levels are made of objects = GameObjects
  • Objects are driven by behaviours = Components (Scripts)

Once your level is finished, you load a new scene to replace most of the current one...

7 of 40

Unity ‘by the book’ - Pros

Alkemi - ADDON - May 2018

  • Very easy to quickly prototype small concepts
  • Friendly with less experienced developers
    • easier to understand relations between objects and behaviours
    • Easily compatible with visual scripting solutions (Playmaker, uscript, …)
  • Makes the most of some aspects of Unity, like physics which is way more natural (less painful) to use like this!

8 of 40

Unity ‘by the book’ - Cons

Alkemi - ADDON - May 2018

  • With the size of project it becomes exponentially more difficult to:
    • Track all eventing / messaging between Components and Objects
    • Discover or keep in mind the global architecture of a project

  • Different jobs manipulate a lot of common resources & assets with very strong links
  • Often difficult to handle team work without conflicts on Scenes and Prefabs (may be somewhat improved by coming nested prefabs at the end of 2018)

9 of 40

Unity ‘by the book’ - Cons(2)

Alkemi - ADDON - May 2018

You can have code called from:

  • Scripts placed on multiple Prefabs at any place in the hierarchy

But also…

  • State machine behaviours for Animators
  • UI visual components !
  • particle system triggers !!
  • Animation timelines !!!

And you have to manage the order of execution for ALL OF THIS!

10 of 40

It’s a bit like Flash ‘frame script’!

Alkemi - ADDON - May 2018

  • It was quick
  • It was artist friendly
  • But it was getting messy really fast!

In those days larger projects used Flex Builder or Flash Develop

to work on logic code isolated from animation stuff.

Logic code placed directly in the timeline of any Flash MovieClip.

11 of 40

Unity “by Alkemi”

Alkemi - ADDON - May 2018

One lib to rule them all, one lib to find them,

One lib to bring them all and in Unity bind them.

12 of 40

Unity “by Alkemi” - Rule #1

Alkemi - ADDON - May 2018

All our code is mostly contained in one (or a few) DLL

compiled with Visual Studio

One entry point, a MonoBehaviour Class

assigned to a persistent “Boot” GameObject.

  • Its Start() is like the constructor of the Main Class in a C# project
  • Its Update(), FixedUpdate(), LateUpdate() run the main loops of the project

13 of 40

Unity “by Alkemi” - Rule #2

Alkemi - ADDON - May 2018

Our Main and only scene is pretty much empty except for this boot script and our camera setup.

14 of 40

Unity “by Alkemi” - Rule #2

Alkemi - ADDON - May 2018

Our Main and only scene is pretty much empty except for this boot script and our camera setup.

As much as possible we try to make this boot script able to initialize the game in any state and even to switch to any state in Play Mode.

15 of 40

Unity “by Alkemi” - Rule #3

Alkemi - ADDON - May 2018

We use as often as possible Unity as a view, not really a provider of logic which is concentrated in our main DLL.

All gameplay and UI visual elements are instantiated with Resources.Load calls or procedurally generated and destroyed when no longer necessary.

No loading of scenes is involved.

16 of 40

Unity “by Alkemi” - Rule #3

Alkemi - ADDON - May 2018

Example of a Drifting Lands Scene

sky

prefab

1 prefab

per rock

1 prefab

per ship

island

prefab

Everything else (bullets, VFX, UI) is procedurally generated

17 of 40

Unity “by Alkemi” - Rule #3

Alkemi - ADDON - May 2018

3D prefab example

18 of 40

Unity “by Alkemi” - Rule #4

Alkemi - ADDON - May 2018

Do not use what you don’t need. A good example : the physics engine.

Drifting Lands is a shoot’em’up action-RPG. It needs collisions.

But does it need physics ?

Built in collision detection of Unity for any other purpose than a physic driven gameplay is a no go for us.

NO.

19 of 40

Case study: strategies for simple collisions

Alkemi - ADDON - May 2018

  • 500 moving Bullets (our player shoots very rapidly)
  • 500 static Enemies (yes it’s a lot too)
  • All entities can be represented by circles
  • All 1000 prefabs are instantiated from a script
  • Test the 500 Bullets against all Enemies : 250 000 collision tests

20 of 40

Case study: strategies for simple collisions

Alkemi - ADDON - May 2018

  • Bullets have a RigidBody2D and a CircleCollider2D

Strategy #1 (aka classic Unity)

  • Enemies have a CircleCollider2D declared as Trigger
  • Enemies have a script to broadcast a message in OnTriggerStay2D to count each collision

Result : 30fps (Collisions are done at a Fixed Timestep : 0.02s)

21 of 40

Case study: strategies for simple collisions

Alkemi - ADDON - May 2018

  • Bullets & Enemies do not have a Collider, RigidBody, nor any script.

Strategy #2 (aka our first typical solution)

  • We create logic entities in a C# class to store and manipulate positions for both bullets and enemies.
  • All collision tests are done with simple distance to the square comparisons

Result : 60fps (collisions done in Update and not FixedUpdate)

  • Prefabs of Bullets & Enemies are updated once per Update

22 of 40

Case study: strategies for simple collisions

Alkemi - ADDON - May 2018

  • Enemies have a Collider and Bullets nothing.

Strategy #3 (the best hybrid solution I could find...)

  • Collisions are tested with Physics2D.CircleCastAll between a circle for each Bullet (not a Collider) and all Colliders of Enemies

Result : 120fps (collisions done in Update and not FixedUpdate)

  • Results are roughly the same whether the test is done by a script on each bullet or centralized in a manager.

23 of 40

Case study: strategies for simple collisions

Alkemi - ADDON - May 2018

  • Enemies and Bullets have a Collider.

Strategy #4 (the worst solution I could find...)

  • Collisions are tested between both Colliders with Physics2D.Distance

Result : <1fps ¯\_(ツ)_/¯

24 of 40

Why do you even use Unity?

Alkemi - ADDON - May 2018

  • Very good asset importer and converter to multiple platforms
  • Very easy to use library to instantiate and manipulate graphical or sound assets
  • Very easy to use API to generate geometry or textures procedurally
  • Very powerful and customizable renderer through Surface Shaders

Watch out for the new 2018 feature : Scriptable render pipeline

25 of 40

Our practices : object pools

Alkemi - ADDON - May 2018

  • Instantiate() & Destroy() a minimum of managed Objects and Unity GameObjects during action phases for smooth performances.
  • Pools of managed Objects & GameObjects
  • GameObjects in the “free” list are just disabled and cost nearly nothing. You might want to parent them to container to keep a tidy hierarchy
  • Pools of complex GameObjects but also very simple generated geometries like quads.

26 of 40

Our practices : preloading with Resources.Load

Alkemi - ADDON - May 2018

  • All instantiated GameObjects must first be loaded with all dependencies (geometry, textures, etc.)
  • The first Resources.Load() for a Prefab which is not in memory can be long. Consecutive Load() are often instant.
  • Preload assets before a level actually starts with Resources.Load()
  • Free memory with Resources.UnloadUnusedAssets() after each level.
  • Requires to generate lists of useful assets for each level.

Or use asset bundles

27 of 40

Our practices : limit accesses to Unity properties

Alkemi - ADDON - May 2018

GameObject.Find(MyGO).transform.position.x ;

Go.transform.position = new Vector3 ( Go.transform.position.x + 10, transform.position.y + 10, transform.position.z ) ;

:(

:’(

Collision example from earlier (strategy #2) with worst possible implementation (no logic entities stored in managed code) => 11fps (vs 60)

Since we use Unity as a View, we often set properties only once per frame and barely ever need to read them.

28 of 40

Our practices : use custom geometry

Alkemi - ADDON - May 2018

Custom UI system why?

  • Because there wasn’t anything decent when we started
  • To stay relatively MonoBehaviour-free
  • We reproduced an implementation similar to Flash because… we still have to find better

29 of 40

Our practices : use custom geometry

Alkemi - ADDON - May 2018

Custom UI system recipe

  • Pooled quads
  • Texture2D.PackTextures
  • Maintain a display list with parenting
  • Detect, propagate and broadcast pointer events

30 of 40

Our practices : use custom geometry

Alkemi - ADDON - May 2018

Custom VFX system why?

  • Because we sometimes use custom geometry data for some effects
  • We tend to use “richer” particles or meshes requiring more assets
  • We need a custom pipeline to import assets and keep them tidy

31 of 40

Our practices : use custom geometry

Alkemi - ADDON - May 2018

Custom VFX system recipe

  • Pooled quads & modeled meshes
  • Texture2D.PackTextures
  • MaterialPropertyBlock
  • Custom Import Pipeline

> To read a full presentation google : “Alkemi FX”

32 of 40

Alkemi - ADDON - May 2018

33 of 40

Our practices : dependency injection

Alkemi - ADDON - May 2018

Class App {

App(IPlayerController playerController) {

_playerController = playerController;

}

}

Interface IPlayerController;

Class TutorialController;

Class GamepadController;

34 of 40

Our practices : dependency injection

Alkemi - ADDON - May 2018

// Configuration

Var container = new Container();

container.Register<App>();

If (tutorial)

container.Register<IPlayerController, TutorialController>();

Else

container.Register<IPlayerController, GamepadController>();

// App construction

Var app = container.Resolve<App>();

35 of 40

Our practices : dependency injection

Alkemi - ADDON - May 2018

  • Delegate class instantiation to a factory
  • Configure different compositions based on context
    • Create special setups for tutorials
    • Create external unit tests with mockup classes
  • Avoid static classes

36 of 40

Our practices : data models with protobuf

Alkemi - ADDON - May 2018

  • Open source data descriptor initiated by Google
  • Binary format for optimal network communication
  • Compatible with multiple languages (untyped or typed)
  • Handle versioning by design
  • Used in Drifting Lands for items / stats database & saves

37 of 40

Our practices : data editing with .NET server

Alkemi - ADDON - May 2018

38 of 40

Our practices : data editing with .NET server

Alkemi - ADDON - May 2018

39 of 40

Our practices : data editing with .NET server

Alkemi - ADDON - May 2018

40 of 40

Our practices : data editing with .NET server

Alkemi - ADDON - May 2018

  • Web data editors are quick & easy to do (loads of existing libs)
  • Programmed in C#, so typed...
  • Can execute C# lib of the game to run parts of the same code