Jump to content

[WIP] RELIC, retro action-adventure 2D (desktop, web)


Refeuh
 Share

Recommended Posts

Hi all,

I am here to talk about RELIC (working title, release title TBD), a project I've been working on for a few months. The game is currently in development ; a portion is fully playable but the product requires additional testing, content and beautification.

  • OVERVIEW

RELIC is an action-adventure game in line with 2D Zelda (TLoZ, ALLTP, DX, OoA, OoS). Details of the scenario are still to be defined, there is a semi-opened outdoor exploration area, interior dungeons with a mix of puzzles and challenging fights, and the need to collect items in order to progress.

  • SCREENSHOTS

1463328544-hud.png

1463328551-dungeon1.png


1463328551-dungeon2.png

1463328551-cave.png

Visuals are temporary, captured from the latest game build

  • KEY FEATURES

- Retro style but smooth gameplay with modern accessibility
- Dynamic action including real-time combats
- 4 outdoor environments with distinctive themes
- 5 dungeons with their share of puzzles, monsters and exclusive boss
- 16 items to collect, each granting special abilities or aptitudes (8 active, 8 passive)
- Explorable "onion skin" outdoor world that unlocks progressively
- Diversified bestiary with 20+ types of enemies and behaviours
- Rich interactions, including traditional switches, teleporters, obstacles, secrets, etc.

  • INFOS

- Estimated play-time : ~10h, +~5h completionist
- Size of the playable area : ~150 game screens
- Team : me, all by myself !
- Development time : ~6 months starting from scratch (~4 done, ~2 left)
- Platforms : desktop (Windows, Linux, MacOS), web (HTML5)
- Release : ~summer 2016 TBC on Steam and itch.io

  • DEVELOPPMENT

I work alone and I do all the programming, game design, level design, production, QA, and I also create additional resources and art assets when I cannot find/buy what I need.

The game is fully playable in terms of "flow" (player progress, items, gameplay mechanics, types of interactions, etc.) but only ~20% of the content is really completed at the moment (level design, puzzle variety, monsters balancing, beautification).
The "finished" portion works as a "vertical slice" I intend to use to start focus tests as soon as possible, in order to collect player feedback and drive the upcoming iterations before moving on with the rest of the game.

The work left is mostly filling and dressing, but also integrating all the secondary content : scenario, tutorial, audio, etc. ; as well as some late features : options, customisable controls, graphical filters, etc.

  • BIO

A few words about myself, in case anyone cares  - I worked for almost 12 years in the game industry as lead programmer and producer for AAA PC and console titles. I've now been creating my own games and learning about game design for about ~1 year, with a first small project released on Steam a few months ago.

My activity is not profitable yet, but it's a good way to gain experience with the "business" and "publication"-sides. My goal is to capitalise on my developments and reach a larger market/public with time as I progress, while finding other like-minded creators to join me in bigger productions.

PARASITE on Steam

  • TECHNOLOGY

For those interested in the technical side - The game is developed in TypeScript, offering a good balance of productivity and maintainability. 

The underlying engine is Phaser 2.4.4 and I used Tiled for all the level design. This is a simple yet robust and efficient and tool-chain.

In parallel of Phaser, I have developed my own custom ECS framework (Entity-Component-System) to maximise the reusability of all logic elements and blocks of gameplay. I intend to build on this framework and expand it with future projects, such as a dungeon-crawler and a coop-shooter, which I expect to be shorter to complete starting from this base.

My ECS allows a fully data-driven approach, where all the gameplay is specified in the entity definitions (.json) and the instance data from the level file. It is therefore possible to integrate content, create new feature combinations and balance existing elements without the need to "re-program" any gameplay. It takes longer to get results than coding the gameplay directly, but in the long run it is a lot more flexible and reusable.
Still in the interest of factorisation, my entity definition files (prototypes or blueprints, depending on the terminology various frameworks use) support multiple inheritance.

  • CONTRIBUTORS

Graphical resources used so far are created by 3 artists. All assets are licensed for commercial use and/or are distributed under CC0/CC-BY(-SA)

Henrique "7Soul" Lazarini
Environments, enemies
http://7soul.tumblr.com/

Michele "Buch" Bucelli
Environnements, various entities
https://www.patreon.com/buch/

Carl "Surt" Olsson
Additional characters
http://uninhabitant.com/
 

Link to comment
Share on other sites

Thanks - here are some technical details that will hopefully explain things a bit more -

My ECS is fairly traditional in terms of architecture :

- Entities are abstract high-level objects made of collections of Components
- Components are a hybrid mix of data and logic (as opposed to another ECS philosophy where Components are pure data and all logic goes into Systems ; personal preference only)
- Systems handle all cross-components and entity-level logic (controls, display, etc.). I don't have that many since quite a lot is delegated to Phaser (collisions, etc.)

In parallel, I use a messaging system to handle all entity or component communication.

Where it gets more interesting is when we look at the Components factory ; for each Component there are 3 types of data:
- prototype data (or blueprint), coming from definition files (json). These are common to all instances of the same Entity (e.g. this type of switch uses these sprites)
- instance data, coming from the level file. These are specific to each instance of an Entity (e.g. the position of the switch in the world)
- runtime data, extra info required by the logic of the game. Again, specific to each instance but driven by gameplay, not files (e.g. the current state of the switch, activated or not)

Therefore the process of building an Entity is non-trivial. I rely on a Component factory to create partial information from the Entity json files, add the runtime data, and finally assemble everything into the final Entity. TypeScript makes it very easy, but still it's a bit of work. Also even though I use .json files, I do "parse" my Entity definitions, mostly to ensure my Components are well-formed.

The coupling with Phaser is done via a "SpriteEntity" wrapper ; basically an object that combines both framework. This justifies why simple data like "position" are Components as well, as the logic is non-intuitive - for example when the player steps onto a teleporter, my framework tell Phaser what to do (ECS -> Phaser), but after a collision, Phaser tells my framework what to do (Phaser -> ECS). So some Components only exist to sync the data and and minimise the coupling between the two frameworks.

A couple of json Entity definition to help visualise it all :

Here, a base definition for Obstacles ; an Obstacle being an object the player cannot walk through but can remove with specific interactions

{
	"id" : "Obstacle",
	"base" : [ "FullHitbox", "Resettable", "HealthDrop" ],
	"components" :
	[
		{
			"type" : "PositionAttributeComponent"
		},
		{
			"type" : "CollidedPhysicsComponent",
			"group" : "Object"
		},
		{
			"type" : "OverlappedPhysicsComponent",
			"group" : "Object"
		},
		{
			"type" : "HealthStatComponent",
			"health" : 1
		}
	]
}

This inherits from "FullHitbox" (defines various physics attributes required for collisions, etc.), "Resettable" (a collection of Components, with logic so that the Entity is not destroyed but disabled and respawned when out-of-screen) and "HealthDrop" (another collection of Components, with logic so that this Entity has a chance to drop pickups when removed). Also some additional Components : groups/messages for collisions/overlaps, position (but no data ! it will come from the level file), etc.

And now a "specialisation" of an Obstacle : a Grass Entity, which can be removed by attacking it with a Sword

{
	"id" : "Grass",
	"base" : [ "Obstacle" ],
	"components" :
	[
		{
			"type" : "VulnerabilityStatComponent",
			"entries" :
			[
				{ "prototype" : "SwordUse", "damage" : 1 }
			],
			"invulnerabilityTime" : 0
		},
		{
			"type" : "StateDisplayComponent",
			"texture" : "Obstacles",
			"animations" :
			[
				{ "name" : "Default", "frames" : [ 3 ], "framerate" : 0, "loop" : false }
			]
		}
	]
}

We inherit from everything and only specify what's missing : some data for the display system to know what to show on screen, and some data required by the combat logic to handle damage.

We then have to fetch the instance bits from the level file (position, name, etc.) and assemble everything into the final Entity. Finally, we have a Grass object, with a collection of behaviours (physics, reset out-of-screen, drops health, etc.), and the ability to take damage from the combat system when overlapped by a Sword attack. And all of this data-driven ; obviously it means knowing how the logic for Components and Systems work, but it makes it easy to add/remove behaviour, and change bits like redefine messages, balance numbers, etc. Also, given working examples of base entities, designers can easily combine things to achieve the result they want (teleporter + moving monster = moving teleporter, etc.)

Same goes for more complex objects, such as Monsters ; they have more animations, they have control components for their inputs/AI, etc. So bigger definitions, but same principle.

I might release the code of my ECS framework in the future, but I don't think it's mature enough to be published yet (e.g. I don't have a nice elegant way to sort component initialisation order in case of dependencies, and other functionalities). But I'm happy to help / answer questions for people considering a similar approach.

'Hope that helps.

Link to comment
Share on other sites

The visual is shining, I love it. Surprisingly to see a game made with assets from art store can be so attracting. Can't wait to play it now :D

The design of your ECS sounds pretty cool, that is what I am recently trying to bring to my own engine(LesserPanda).

What I've done is to make things like render/physics systems and update them in a customizable order. For the "Entity" side(I call it an Actor), the visual and physics part can be fully created from JSON data, and configs can be passed to the instances in the runtime. And that's all, still far from what you have achieved since it requires the user to inherit Actor to add behaviors.

I've searched a lot to find out whether it is possible to add behaviors to objects using pure data, and never succeed. I end up with introducing the Behavior from Stencyl, which still requires inheriting from Actor and create objects as classes. It's not perfect but I was satisfied until read your post just now. Can you share a simple example of how to create a Component and do the "hybrid mixing of data and logic"?

BTW. the entity defination seems a little bit complex, do you use any homebrew data editor for that?

Link to comment
Share on other sites

It's nice to see something a bit broader in scope compared to a lot of html5 games.
You ment
ioned that you haven't really come up with a plot yet, though. I would recommend you start thinking about that, because I'm not sure about others, but I find a rushed or bad plot extremely difficult to endure.

Link to comment
Share on other sites

Thanks for all the feedback -

Oh I do have story elements, plots and leads - though it's difficult to find art asserts to really support a proper scenarisation ; I think I'll have to stick to something relatively simple, at least in terms of story-telling, and keep just the few good bits that make the core of it.

Regarding the ECS, I'm not familiar with Stencyl and its concept of Actors so I can't really explain how it differs from my own infrastructure.

Basically, a Component in my framework has 6 core elements : 3 data bits (template, instance, runtime, cf. previous definitions), and 3 methods :
- Init()
- Advance(_time)
- Receive(_parcel)

Every specialised Component (e.g. "HealthStatComponent") inherits from the base Component interface. An Entity is then an abstract object, with just a collection to store Components, and a couple of methods (Advance/Receive) to propagate/dispatch the updates/messages to its Components.

For each Component I also have a few "helper" classes to help organise the data when parsing/building definitions : ComponentTemplateData, ComponentInstanceData.

When parsing the prototype definitions files and level file, it's just matter of building each data bits for each Component (=> partial Components with only the template data), then parsing the level files (=> partial Components with only the instance data) and finally instanciating the required Components once all the required data is known. In some languages this would rely on some form of custom-typing or RTTI-like mechanism, in TypeScript this is trivial : .json specifies Components name, and the Component factory relies on "eval" string evaluations, and also heavily on instanceof tests when assembling Components (template + instance) to ensure Entities are well-formed

Said a bit differently, the construction "flow" goes like this :
- read .json definition files (= specifies all components types for every entity, with the common data) => build partial Components, i.e. ComponentTemplateData ; group these into what I call Prototypes (= a collection of partial Components with template data only ; can also be seen as a "partial" Entity, it's an Entity without all the instance data)
- store all the Prototypes into a simple database/manager for future look-up
- apply Prototype inheritance by propagating partial Components to derived definitions
- read the level file => given the Entity type, build partial Components, the instance bits this time, i.e. ComponentInstanceData.
- use a factory to assemble it all together : Prototype (= collection of ComponentTemplateData) + collection of ComponentInstanceData = Entity. This also checks for possible mismatch, e.g. trying to assemble partial Components that don't go together, or where bits of data are missing (e.g. no instance x/y specified for PositionComponent). This can happen with typos or incomplete definitions.

The process is the same when creating an Entity dynamically during gameplay ; look-up the Prototype (= all the template bits), provide the instance data required by the Entity you're creating (position, etc.), and use the same factory to do the assembling

Link to comment
Share on other sites

This should help to visualise the relationships a bit better ; the factory logic itself isn't really visible, since it's what's happening when assembling a Prototype (= partial Components with template bits) with a group of ComponentInstanceData (= partial Components with instance bits)

ECS.png

Link to comment
Share on other sites

Correct - though you never target a Component directly, you always sent to an Entity ; the Entity dispatches to its Component, and the ones interested in that message can process it (so 1 message can trigger multiple changes, on various Components in parallel)

Also, for cases where a Component needs to target its parent Entity, I have a "this.self" initialised when I construct Entities. In my Post system, a Message is a type (via instanceof) and additional data (e.g. amount of damage, etc.), and a Parcel is just a wrapper for a Message (sender, target, delivery method, etc.)

So everything is done through chains of message :

Entity receives Damage > HealthComponent processes Damage, sends Death message to itself > Entity receives Death > AudioComponent triggers death sound + VisualComponent triggers death particles > Entity removes itself

 

Link to comment
Share on other sites

Not usually a problem regarding performance ; if certain types of messages tend to be used A LOT it might interesting to put a recycling pool mechanism in place, to save on the construction of objects - but usually VMs and script engines have some of this built-in already. In a native engine, we'd also compile everything, messages, strings, identifiers, etc. to a binary format. If it ever becomes a problem for performance, it's always possible to refactor specifically bits of the components once a perf killer has actually been identified. I always favor flexibility at first - the time saved in development will be better used for optimisations later on, once we know what the problems are.

Alternatively, some people prefer to have all their Components as POD structs (data only, all public, no methods), and use Systems for all the logic. This reduces the messaging to some extent. All variations are viable.

2 "special" Components I really like to have :
- a "ScriptComponent" ; basically it's a dummy that just calls custom defined Init/Advance/Receive. Incredibly useful for exclusive puzzles, boss logic, etc. Trivial in JavaScript, just "eval" "objectname" + naming convention (ObjectName_Init, etc.)
- a "MapperComponent" ; something to "translate" a message into a different one. It has a mapping table (A1 => B1, A2 => B2, etc.). It can help to reduce coupling between Components messages. Instead of having the AudioComponent process the Death message, just re-map Death message to, say, a "play sfx 'death' " message and have the AudioComponent process PlaySFX messages only

Link to comment
Share on other sites

  • 2 weeks later...

Quick update regarding progress -

 

I have just completed a first round of focus testing last week (thanks to all the participants !). While still quite far from an actual "demo", this allowed me to answer 2 critical questions :

- in spite of the "work in progress" nature of the project, is there sufficient potential to complete & release the game ?

- how to prioritise the remaining work in order to maximise the time/player experience ratio ?

 

In light of the feedback, I have recalibrated the production tracking and I plan 3 major iterations :

- gameplay : tweak existing elements, redesign weak mechanisms, add missing functionalities, etc.

- content : balance existent areas, redesign some puzzles, integrate additional zones, etc.

- presentation : scenario / tutorial, graphical polish, options and controls customisation, etc.

 

Including publication delays, I am still planning for a release around ~late Summer 2016

 

'Will post updates soon with actual concrete progress !

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...