Coroutine Callbacks

I’m working on a cutscene/dialogue graph editor plugin for the Godot game engine (I mentioned using it for the Ludum Dare in a previous post). When I came to deciding how to actually process the graphs it creates in order to display the dialogue and perform actions in the game, I discovered an interesting use for coroutines that I haven’t seen described elsewhere. I’m not all that used to working with coroutines so maybe what I’ve done is completely normal and everybody does it all the time, or indeed it might be ill-advised for some reason. Either way, I will describe it here and you can ignore me if I’m being an idiot!

Graph editor

Processing a graph that results in activity in-game is a task which frequently needs to be put on hold - waiting for text to be displayed in the UI in a juicy manner, moving characters around, waiting for input etc. This could be achieved in a Node by implementing _process() and maintaining state about whether you’re currently walking a graph or waiting on something, and having methods that other nodes can use to indicate when processing can continue. I have found such methods to be quite messy in the past.

I decided instead to process the graph in a while loop and yield as necessary while other nodes perform tasks initiated by a number of signals. Where tasks are expected to take some time to complete, a coroutine state object is passed to the signal which the listener can use to indicate that processing should continue, or in some cases return values.

Let’s See Some Code

Below is an abridged version of the main method that processes the graphs. Processing a dialogue node yields so that the UI that displays the text can wait for player input to continue. Processing a branch node and others like it just proceed directly to the next iteration of the loop.

func process_cutscene(cutscene):
    _local_store = {}
    _current_graph = cutscene
    _current_node = _current_graph.root_node
    while _current_node != null:
        if _current_node is DialogueTextNode:
            yield(_process_dialogue_node(), "completed")
        elif _current_node is BranchNode:
            _process_branch_node()
    ...

Here is the code that processes dialogue nodes and raises the signal for their text to be displayed in-game. A coroutine is put in a frozen state to be passed with the signal, and the call to emit the signal is deferred so we can make sure we are waiting for the coroutine to complete before any signal listeners have a chance to resume it.

func _await_response():
    return yield()


func _emit_dialogue_signal(
    text,
    character_name,
    character_variant,
    process
):
    emit_signal(
        "dialogue_display_requested",
        text,
        character_name,
        character_variant,
        process
    )


func _process_dialogue_node():
    var text = _current_node.text

    var process = _await_response()
    call_deferred(
        "_emit_dialogue_signal",
        text,
        _current_node.character.character_name,
        _current_node.character_variant.variant_name,
        process
    )
    yield(process, "completed")

    _current_node = _current_node.next

The code that consumes the signal can take however long it likes to do its thing, and when it’s ready it can just call resume() on the coroutine state and the graph processing will proceed to the next node.

func _on_CutsceneController_dialogue_display_requested(
    text,
    character_name,
    character_variant,
    process
):
    _process = process
    ...


func _on_ContinueButton_pressed():
    if _process != null:
        _process.resume()

If a node in the graph needs a response in order to know what to do next (e.g. a choice between multiple dialogue options), we can just accept the return value of the coroutine passed in the signal. The consumer can just pass the value to the resume() call.

func _process_choice_node():
  ...
    var process = _await_response()
    call_deferred(
        "_emit_choices_signal",
        choices,
        process
    )
    var choice = yield(process, "completed")
    _current_node = _current_node.branches[choice]

Potential Pitfalls

There are two pitfalls that come to mind with this approach, but they’re not really any different than what could occur with a _process() based approach like the one I described above.

  1. Processing of another graph could be triggered while one is still in progress. If the previous one is yielding it would end up processing the new graph as well when it resumes. This is quite easy to avoid by just noping out of the method if there is already work in progress.
  2. If there are multiple listeners connected to the signals there could be ambiguity about which one is responsible for resuming. If there are multiple resume calls, all but the first will result in a runtime exception.

In the event that you actually want multiple listeners to report in before resuming, it might be possible to craft a different coroutine that takes the number of listeners and yields until they have all resumed. I think it would look something like this:

func _await_many(count):
  while count > 0:
    yield()
    count -= 1


func _await_many_responses(count):
  var responses = []
  while count > 0:
    responses.append(yield())
    count -= 1
  return responses

I haven’t tested that though.

Conclusion

I don’t really have one, I just thought it was a neat pattern that doesn’t require the listeners to keep a reference to the node doing the processing or have any knowledge of it aside from the signals it emits. You can check out the full code on GitHub as it stands currently. I haven’t tested it in a game yet, but I will probably enter the Ludum Dare later this month and I might get a chance to use it then. I will update afterwards whether it is a disaster or a triumph!

America Wins Again

Chess lives here
Chess lives here

This post contains spoilers for the TV show “The Queen’s Gambit”

I loved pretty much everything about The Queen’s Gambit, and as you’ve probably gathered if you’ve read any of my other posts, I don’t have much to say about things if I’m not complaining about them!

What I do feel obligated to talk about, however, are the differences between this show and “For All Mankind” in terms of how they deal with the Soviet Union. I’ve written previously about how “For All Mankind” re-imagines the space race such that the USA remains the underdog after several successful Soviet moon landings - erasing real Soviet accomplishments in favour of fictional ones, and providing an impetus for the US to include women in its space program, something that it didn’t do in reality until the 1980’s.

The Queen’s Gambit does not rewrite history to the same extent. The chess world was dominated by Soviet players in the 50s, 60s and beyond, and the show acknowledges that readily, with “The Russian”, Vasily Borgov, being Beth Harmon’s ultimate opponent. Her breakthrough somewhat reflects that of American prodigy Bobby Fischer, so it’s not unprecedented. However, where For All Mankind featured a number of real-life historical figures, all the competitors in The Queen’s Gambit are fictional.

Team USA
Team USA

In a way, however, Beth’s real opponent is not Borgov or any of the other men she defeats on the road to face him, but herself, her emotional problems, and her addictions. The games she plays are almost entirely without malice, with just a touch of smug arrogance on occasion, and everybody she faces ends up with enormous admiration for her. This is especially true of the Soviet players she faces, who almost seem happier to have been beaten by her than they would have been to have won. What cold war political animosity is present comes mostly from her CIA handler, and is treated as a ridiculous, petty distraction by Beth. Doesn’t he know that there’s chess to be played??

For All Mankind, in contrast, has that animosity at its core. Its the motivation behind all of the American government’s actions in the show, though not necessarily of everybody at NASA, while the one Soviet character seems to validate their suspicions.

The Queen’s Gambit’s portrayal of the Soviet Union is actually extraordinarily sympathetic as a result of being viewed through the lens of chess enthusiasm. Whereas at home chess is a niche interest, when Beth arrives in the USSR she discovers that it is a national obsession. At home her prospects are probably akin to those of Benny Watts, the US chess champion before her - obscurity, and a dingy basement apartment at best. In the USSR she is mobbed by an adoring crowd after every match, which she plays in a dedicated chess hall instead of whatever spaces are available. She even has to adopt their strategy of cooperating during adjournments before she is able to achieve victory, with a team of all the chess friends she’s acquired on her journey advising her on how to approach the rest of the game - an apparent admission of the superiority of collective cooperation over competition and individualism.

Welcome home
Welcome home

The final scene sees Beth slipping her handler’s grasp on the way to the airport. She wanders the streets of Moscow unmolested to find the old guys playing chess in the park, and they greet her warmly before inviting her to play. There’s a distinct sense that she has found a home here, where the game she loves is played openly in the park instead of hidden away in the basement, as in the orphanage where she grew up.

Although this series is still about an American character and mostly set in America, it is much closer to being the view from “the other side” that I hoped for in my previous post.

Cybercentrism

You're a wizard Wade!
You're a wizard Wade!

This post contains spoilers for the movie “Ready Player One”

I finally gave in and watched Ready Player One, prompted by seeing the Internet dump on the sequel to the novel. I avoided it before now because the commentary on the novel made it sound like pandering nonsense, and I assumed the movie would be much the same. But, I wanted to see for myself.

Perhaps as a result of those lowered expectations, I actually found it quite entertaining on a surface level. The pop culture references were certainly omnipresent, but maybe not as tiresome as having them explained in writing would be. In movie form, a lot of them that are not plot-relevant just form a visual backdrop that actually help define a plausible metaverse, albeit one with a culture that is inexplicable stuck 30 years in the past.

But on the other hand, the centrality of pop culture to the plot, and why it is central, is also what leaves a bad taste in my mouth. In the universe of Ready Player One, the online world is dominated by a metaverse-like virtual reality game called the OASIS, a game owned and controlled by a single corporation, and ultimately a single man, its creator, James Halliday. When he dies, he decrees that his successor will be decided by a tripartite fetch quest within the virtual world. In a monumental act of narcissism, the quest’s challenges are not based on the skills required to run a massive company or a piece of essential infrastructure, or tests of morality or wisdom, but on intimate knowledge of his own life and his pop cultural obsessions. This might be an interesting setup if Halliday were the antagonist, but he’s not, he’s practically worshipped by the users of the OASIS.

Wow!! Cool Future!!

This movie has the trappings of cyberpunk, but little of the critique. Victory in this story is not the smashing of a corporate monopoly or frustrating of its goals, but the passing of the reins of power from a virtuous benevolent founder to a handful of virtuous disciples, through an absurd faux-meritocratic process where merit means liking the right books and games and movies.

The antagonist, Sorrento, is the CEO of a company that runs debtor’s prisons, yet somehow the movie gives the impression that his greater sin is being a poseur who’s only pretending to “get” the stuff that Halliday liked. The denouement even reveals that Halliday could have used his monopoly power to end the practice of indentured servitude at any time, but apparently chose not to, or just didn’t think to, and this is not framed as a critique of monopoly capitalism, but is just a throwaway line to demonstrate how benevolent the protagonist is in his new role.

Remember Tracer? Remember Chun-Li? Remember various skeletons?
Remember Tracer? Remember Chun-Li? Remember various skeletons?

The third act involves an epic battle in the OASIS, with the user-base coming together to fight Sorrento’s forces and prevent them from completing the quest. It’s presented as an empowering grass-roots uprising to save the OASIS from an evil corporation, and fine, they are pretty evil… But the status quo is also intolerable. They’re not fighting for their own empowerment, but merely to keep the unaccountable corporate power that dominates their lives out of even more abusive hands. It’s a pretty uninspiring cause.

The movie’s true feelings on the nature of power, and the relationship of ordinary people to it, is revealed most starkly at its climax. The conflict in the OASIS has spilled over into the real world, with our intrepid heroes being chased through the streets in a van, exposed at every turn by CCTV tracking technology and drones. While Sorrento sets out to confront them personally, they put out a call for aid, for their supporters to gather in the slums and defend them. When Sorrento reaches them, a huge crowd of people stream out of the stacks to oppose him.

It's dangerous to go alone! Take this!
It's dangerous to go alone! Take this!

For a moment, it seems as though the plot really is going to be resolved by collective direct action. People power, woo! Instead, Sorrento pulls out a gun, and the crowd parts. They could easily disarm him, there are hundreds of them all around him. Instead they gawp uselessly at him, right up to the point where he is threatening to shoot some children in the face. It is only the police arriving that prevents him from committing murder right in front of them.

With Sorrento out of the way thanks to the police, a bunch of lawyers arrive to certify the protagonist’s completion of the quest and acquisition of complete control over the OASIS. The crowd are reduced to placid spectators in a boardroom drama that doesn’t involve them or empower them. This is, I feel, the perfect encapsulation of the movie’s worldview. The crowd are the audience - expected to worship and trust benevolent CEOs, to react strongly in defence of a commodified culture where they are otherwise passive consumers, but to look on silently and not interfere as control over that culture changes hands, maintaining the status quo.

Avocados

Avocados
Avocados

I’m not very good at doing these art posts in a timely manner; I did this still-life of a couple of avocados for pixel dailies back in September!

Timelapse

Avocados

Anticipation

Anticipation

I was inspired to create this when I won a postcard and sticker pack giveaway on twitter by my friend Stefanie, and I was eagerly waiting for it to arrive! I actually did this a while ago but I forgot to post it here.

Postcard and stickers
Postcard and stickers

Go check out Stefanie’s work, it’s amazing. Thanks for the goodies Stef!

Timelapse

Anticipation

Stars Trek - Failures of Imagination

Where did it all go wrong?
Where did it all go wrong?

This post contains spoilers for the TV show “Picard”

Star Trek: The Next Generation, and its follow-ups, Deep Space 9 and Voyager, are some of my favourite TV shows. They are set in a future post-scarcity utopia where poverty, disease and war (amongst humans and most of the alien species they encounter, at least) have been largely eliminated.

Although they sometimes deal with deep topics, like the humanity or personhood of artificial intelligences, the economic reality of their universe and how it came about goes largely unexamined. There is no money, we are told (except sometimes, when there is). There is property, apparently, but whether it works the same way as property does today is not discussed. Abundant energy and the technology to conjure most of the essentials of life from thin air mean that nobody goes hungry, but whether everybody can have the opportunity to own a vinyard in France without inheriting one is a question that goes unasked.

The utopianism is usually established instead through a gentle mocking of the preoccupations and vices of present-day society, or lecturing about how humanity has moved past them. A bemused Picard takes a cigarette offered by a 20th century character on the holodeck, and coughs aggressively. Imagine smoking! Even the Ferengi, themselves caricatures of grubby, grasping capitalists, are shocked at our stupidity when they learn about tobacco. Janeway lectures the omnipotent Q about resolving conflicts with diplomacy instead of violence. An arrogant 20th century revival is revealed to be a buffoon when he demands to be allowed to speak to his lawyer. No lawyers here sir - this is the lawyerless utopia of Lionel Hutz’s nightmares.

Occasionally the admonishment is more direct - such as when Sisko time travels to a ghetto in a North American city, in our near future, looks directly into the camera, and says “sort your fucking shit out”.

The point is that in the Star Trek future, society is better than it is today, but its material basis is vague to the point of absurdity. It’s unfortunate, because that would be an interesting topic to explore, but it is what it is - light-hearted science-fantasy more concerned with the personal growth of characters than with the economic basis of their society.

A Shallow Dystopia

Sacre bleu!
Sacre bleu!

Star Trek: Picard, the latest attempt at continuing the Star Trek franchise on television, is set, in contrast to its predecessors, in a fractured, broken society. The Federation has turned inward, failing to live up to its values of diplomatic and humanitarian outreach. Money has returned with a vengeance, and nobody does anything unless they’re getting paid. There’s a disaffected, under-appreciated working class, who apparently don’t even get the same quality of replicators as other sections of society. Where characters in other series’ have interests and hobbies, in Picard they have vices, addictions and psychological damage. One character constantly has a fat cigar hanging out of his mouth and nobody is shocked about it because he looks, just, so cool.

In a show without an established universe, these aspects would be unremarkable - just a different set of assumptions about human nature and the future development of society, and with different stories to tell. In Picard, the departure from the previous utopianism is not examined, much less explained, and it is jarring.

But the above examples are trivialities compared to a problem that is at the very heart of the show. Toiling alongside the human workers are a class of sentient android slaves whose abolition and genocide by the Federation serves as a major plot motivator.

More than a Fistful

Seriously bro?
Seriously bro?

These are the “Datas on every starship” that Maddox expresses a desire for in the TNG episode “The Measure of a Man”. In that episode (one of the best in the Star Trek canon), Picard defends Data’s personhood and his right to self-determination. He’s ready to sacrifice his career over the possibility that his society would view even one android as property. In Picard, the same character disagrees with the ban on “synthetics”, but hardly comments on their genocide, or their previous condition of slavery. He quits Starfleet over their failure to provide humanitarian aid to the Romulans, not over the fact that the Federation had somehow come to rely on slave labour. Apparently, he would be perfectly happy to return to a situation that he fought to prevent when he was Captain of the Enterprise.

It’s a disappointing missed opportunity. It seems to me that the show wanted to say something about the socio-political situation in America today, but utterly fails to understand that situation, the shows that came before it, or the character of Picard.

If it really wanted to portray the dissolution of a utopia, a crumbling society betraying its ideals, it could have done so by clarifying the nature of the Federation economy, and provided some systemic explanation for the introduction of slave labour, money, and inequality, where those things did not exist before, or at least some believable political force pushing for those things. Picard could have been cast in the role of defending the fundamental rights of the Androids from a society that is determined to exploit them, as he has many times in the past. Perhaps he could even have taken on the new role of defending the rights of human labourers, whatever the reason that they’re suddenly being disenfranchised. It could have been a great opportunity to introduce some economic depth to the Star Trek universe that has long been lacking.

Instead, we get a complete mess where exploitation is ignored, the dissolution of Federation society is apparently due to infiltration by an inherently sinister other, and the androids are the ultimate villain for not being sympathetic to their oppressors or being understanding about the genocide of their race.

Observatory

Observatory

An animation I made for Asteroid day to continue my space-themed art streak! Thanks to my friend Stefanie for the suggestion! Please click that link and check out her absolutely stunning art, you won’t regret it.

Prints

Prints are available through RedBubble, INPRNT and coming soon to Displate.

Timelapse

Observatory Timelapse

Celestial

Celestial 1
Celestial 1

Only a few days after the last inspiring pixel dailies prompt, another one appeared that I couldn’t resist - “Celestial”. I had three ideas for it immediately but I only had time to do the one above on the day. I’m glad I spaced them out anyway because I think I achieved a lot with the extra time.

Flyby

The second idea I worked on was originally only a planet-rise over some mountains, but it evolved quite a bit as I worked on it until it was about spaceships racing over a peaceful alien village.

I’m quite pleased with the palette and animation on this one.

Celestial 2

The final concept I only completed yesterday, a view of the night sky through some trees.

Prints

Prints are available through RedBubble (linked individually above), and Displate.

Timelapses

Celestial 1

Flyby

Available from 2020-06-25:

Celestial 2

Cosmic Eye

Cosmic Eye
Cosmic Eye

I got back into the pixel dailies a couple of days this week. I’ve never used them as an actual daily practice, but when I see a theme I like I jump in. This day, the theme was “Eye”, and I really like eyes.

Unfortunately I was pushed for time that day so the result is a bit rough - however, I think it gets the idea across, which is “eyes which are full of stars and made of stars and also stars were there”.

I started this one out with my drawing tablet, which I don’t usually use for pixel art and amn’t very good with. I’m trying to get away from using so many rigid straight lines, and treat pixel art more like regular painting. I had to switch to the mouse towards the end for the finer details, but it’s a start.

Timelapse

Cosmic Eye