UniverCity - Change log
The previous post including an introduction to this game can be found [here]({% post_url 2017-05-24-may-change-log %}).
This post is not as long as the last one as it only covers one month and quite a lot of time has been spent on stylish.
Stylish⌗
One of the big things I’ve been working on this month is Stylish. Stylish is a simple ui system that revolves around styles (hence the name) and is mainly targeted at games. Elements are just names with key/value pairs and optionally children; by their own they do nothing.
Styles control everything: positioning, appearance and even events (in the
case of UniverCity). Stylish itself only cares about a few style rules:
layout
to set what layout engine controls the children of an element and then
scroll_x/y
and clip_overflow
which are used for positioning when querying.
Every other style rule is able to be used by either the user of the
library or the renderer. Stylish comes with a crate stylish_webrender
which uses Servo’s webrender to render via OpenGL (which
is what I use for my game). In theory, however, anything can be used. For
example, I put together a simple web version that uses
a HTML canvas to render (incomplete because I didn’t want to spent too long on
it).
General⌗
For stylish itself the main source of control is via layout engines.
Layout engines position child elements within an element and can
also resize the element to fit the children. By default every element
uses the layout type absolute
which uses the x
, y
, width
and
height
style values to position the element relative to the parent
element. stylish_webrender
also provides two layout types:
grid
which aligns children within a fixed sized grid controlled byrows
andcolumns
lined
which aligns elements along one or multiple lines. This is currently the only layout that will correctly position and size text (although absolute can still be used if sized manually).
Stylish was mainly designed for elements and styles to be loaded from files, but I did create a macro that can be used to create elements inline.
manager.add_node(node!{
dragable(x=200, y=60) {
@text("Drag me!")
}
});
I haven’t focused on optimization yet, but with webrender it currently performs well enough for my uses.
UniverCity⌗
Embedding stylish + webrender into my game ended up being pretty simple. The only pain point was working out what state webrender expected opengl to be in before rendering and then to reset the changes it made to the state afterwards. This is what ended up being enough for me after watching webrender in apitrace:
gl::clear(gl::DEPTH_BUFFER_BIT);
gl::disable(gl::Flag::CullFace);
ui_renderer.render(&mut ui_manager, width, height);
gl::enable(gl::Flag::CullFace);
gl::enable(gl::Flag::DepthTest);
gl::disable(gl::Flag::Blend);
gl::depth_mask(true);
I’m slightly worried about what future versions of webrender will do but I don’t think it’ll be too much of an issue. I have webrender pinned to a commit right now anyway.
I’m currently in the act of converting all the old UI stuff from json to Stylish; progress has been good so far.
One thing that UniverCity required was a way to interact with the UI via scripts (lua) and events. I decided to build events into the style rules. This had the benefit of allowing things like buttons and textboxes to be implemented purely as style rules instead of specifically handling them in stylish.
textbox {
background_color = rgb(200, 200, 200),
border_width = border_width(2.0, 2.0),
border = border(bside("#000000", "solid")),
can_focus = true,
layout = "center",
on_mouse_up = "textbox#node:focus()",
on_focus = "textbox#focused(node)",
on_unfocus = "textbox#unfocused(node)",
on_update = "textbox#update(node, evt.delta)",
on_char_input = "textbox#
local txt = node:query():text():matches()(nil, nil)
txt.text = txt.text .. string.char(evt.input)
",
on_key_up = "textbox#key_up(node, evt)",
on_key_down = "textbox#key_down(node, evt)",
}
The event handlers are lua scripts. You can, however, by abusing the custom value feature of Stylish, also use rust closures by setting them as properties.
fn from_value(val: stylish::Value) -> Option<Vec<MethodDesc<E>>> {
if let Some(val) = val.get_custom_value::<Vec<MethodDesc<E>>>() {
Some(val.clone())
} else if let Some(val) = val.get_custom_value::<MethodDesc<E>>() {
Some(vec![val.clone()])
} else if let Some(desc) = val.get_value::<String>() {
// The @ here is a hack because stylish doesn't
// support arrays yet.
Some(desc.split('@')
.map(|desc| Self::from_format(ModuleKey::new("base"), &desc))
.collect())
} else {
None
}
}
Mouse events use the query_at
method to find an element to fire the
event. Key events use a ‘focused’ element to fire their events at
and (de)init/update work on every element.
I’ve only just started using Stylish with UniverCity and so far it seems to work well (a large improvement over what I was doing before). It’s possible I’ll start finding issues as I finish moving everything over to it.
Gameplay⌗
New path and brick wall textures⌗
The old path texture was one of the first textures drawn for the game and was showing its age as I’ve improved with my texture work.
The brick texture was also pretty old, and when the SSAO changes happened (see below) I updated the wall texture to have a simpler look.
The textures are still just simple shapes with noise and some basic shading, but they fit in much better with the rest of the textures.
SSAO and lighting improvements⌗
Click the images for a larger view.
Given the angle of the game, having depth be clear is important. The old rendering was bright and colorful, but the only visual clue about depth was from the shadows from walls and objects, which in the case of walls couldn’t be seen from every angle. The game did have some normal based lighting, but it wasn’t very visible.
To improve this, I fixed up the normal lighting so it was more noticeable and then added SSAO. With the new pass system (detailed below in Internals) this ended up being somewhat simple to add (apart from tracking down some gpu specific bugs). SSAO comes with a peformance hit, but it isn’t noticable on high end machines and I plan to add an option to disable it on low end machines.
Students got a model⌗
The old blue box model will be missed, but the new model is much more fitting. It is fully set up for the tinting system that was made previously, like the professor.
The student uses modified animations from the professor with an additional sitting animation.
I still need to add other student models (female, other looks etc.), but it takes a lot of time. I am therefore deferring that to a later date.
Skin color fixes⌗
Minor tweak to the underlying texture of professors/students so that skin colors don’t look as washed out.
Shops⌗
Shops and queuing somewhat working #gamedev #indiedev pic.twitter.com/fRG1MorjIT
— Thinkofname (@thinkofdeath) June 7, 2017
Background for the main menu⌗
The main menu now renders a dummy game instance to use as the background.
Internal⌗
Threaded pathfinding⌗
Whilst it’s still not done in the background, I have used rayon to at least compute a few paths in parallel.
Render passes⌗
Implemented a new pipeline system for handling render passes. The pipeline is defined using a builder. This made adding the passes for SSAO much easier.