formatting changes and stuff :3

This commit is contained in:
Rowan 2024-12-03 15:15:45 -06:00
parent 0d18bb4f83
commit 50b7737df5
10 changed files with 42 additions and 23 deletions

View file

@ -2,8 +2,6 @@
{% import "../../../layouts/macros.html" as macros %}
{% block article %}
# 45 Day Rogulike Postmortem
I worked on a Roguelike game from 8 March 2022 to 22 April 2022. My motivations were to teach myself new things, develop my existing skills, and have fun with the development process. Two out of three isn't bad. [Give it a try.](https://mochancrimthann.itch.io/45dr)
Starting a new project is always exciting, so I put in many hours in the first couple of weeks. There's no shortage of inspiring roguelike games, but most of my inspiration was directly from [DCSS](https://crawl.develz.org/). I completed procedural generation, movement, tile visibility, combat, items, enemy AI, and a character controller within the first week and a half. Most of the development was straightforward to reason. I decided early in development to create a new component for each feature to avoid breaking previous ones. Nothing drains my motivation like a week-long bug-fixing marathon.

View file

@ -1,7 +1,6 @@
{% extends "../../../layouts/post.html" %}
{% block article %}
# A Query about Game State
## Preface
I'm an indie game dev that has an interest in making tools that I think would improve my life as a game developer in the hopes that it can help others. I'm not an expert in any of the topics that this article covers. I have no formal education in game engine development or architecture, systems programming, or anything else. I went to university for Computer Science with a specialization in game design & development when I was in my early 20s and the only thing I learned is that there are better ways to spend tens of thousands of dollars.
@ -39,7 +38,7 @@ Seasoned game devs, disciplined object-oriented programmers, and existing ECS de
How do we add a Spiked Shield item -- one which derives the behaviors of `Weapon` and `Armor`? If we were using a language which supports multiple inheritance this could potentially be a nonissue: except that multiple inheritance is often [purposefully missing](https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem) in many languages. We could decide that the item belongs more to one class than the other and just duplicate the missing behavior but these types of decisions often introduce unforeseen complexity: will the damage algorithm need to do a specific type check for `SpikedShield`? What about the equipment screen? And of course, what happens when we need to implement a damaging potion?
Perhaps the most appropriate solution this case would be to forgo the inheritance pattern in favor of [composition](https://en.wikipedia.org/wiki/Composition_over_inheritance). In C#, this could be achieved with interfaces and default implementations.
Perhaps the most appropriate solution for this case would be to forgo the inheritance pattern in favor of [composition](https://en.wikipedia.org/wiki/Composition_over_inheritance). In C#, this could be achieved with interfaces and default implementations.
```cs
interface ICollectable {
@ -80,12 +79,17 @@ public class DamageEventArgs : EventArgs {
public readonly float Value;
}
public delegate void DamageEventHandler(object sender, DamageEventArgs args);
// only one of these should exist and should be globally
// accessible so we make it a singleton
class DamageEvent {
private static readonly DamageEvent instance = new Instance();
public static DamageEvent Instance => instance;
class DamageEventQueue {
private PriorityQueue<DamageEventArgs, int> queue = new();
private void OnHandleDamageEvent(object sender, DamageEventArgs args) {
private void Raise(DamageEventArgs args) {
// before enqueuing, determine the priority
// based on the object dealing damage
var priority = args.Source switch {
Player => 2,
Enemy => 1,
@ -96,18 +100,26 @@ class DamageEventQueue {
}
private void OnTick(float deltaTime) {
// for simplicity we'll dequeue everything each frame
// and pass it to the damage handler
while(queue.TryDequeue(out var e, out int priority)) {
e.Source.ApplyDamage(e.Target, e.Value);
}
}
}
DamageEvent.Invoke(this, new DamageEventArgs(bossEnemy, player, 10));
DamageEvent.Invoke(this, new DamageEventArgs(player, bossEnemy, 10));
// bossEnemy's damage should be processed after player's damage
// if they're raised during the same frame
DamageEvent.Raise(new DamageEventArgs(bossEnemy, player, 10));
DamageEvent.Raise(new DamageEventArgs(player, bossEnemy, 10));
```
There's a lot of issues with this code that I'm going to pretend were deliberate decisions for brevity. The point of this example is to show that we can ingest events and sort them arbitrarily based on the requirements of our games. We've made a step in the right direction by identifying a need to explicitly order these events. Even if this implementation isn't ideal, it properly encodes the requirements of the design (ie. player damage should be processed before all other types).
There's a lot of issues with this code that I'm going to pretend were deliberate decisions for brevity. The point of this example is to show that we can ingest events and sort them arbitrarily based on the requirements of the game. We've made a step in the right direction by identifying a need to explicitly order these events. Even if this implementation isn't ideal, it properly encodes the requirements of the design (ie. player damage should be processed before all other types).
There is still a timing issue with this approach however. Events can be raised at any point: before, during, or after `DamageEvent` queue has already done its work for the frame. If `bossEnemy` raises its damage event before `DamageEvent` processes its queue but `player` raises their event after, we still have the original issue.
Depending on the engine and implementation, there may be a few options for solving this. Rather than using `OnTick`, the damage can be handled in `OnLateTick` which runs after all `OnTick` systems have been processed (Unity's version of these methods are `Update` and `LateUpdate`). In Unity the `DamageEvent` singleton script could have its order explicitly modified in the settings or with the `DefaultExecutionOrder` attribute. In Godot, this would likely be resolved by moving the `DamageEvent` node lower in the tree since nodes are processed from top to bottom while resolving children first.
## In search of loot
Managing game state is difficult. As a game grows, so too does the amount of objects active at any given time. Dozens, hundreds, or even thousands of entities each with their own behaviors, goals, and concerns that interact with each other in different ways. This explosion in complexity can be exceedingly hard to manage even in games that are small in scope. Games, even at their simplest, tend to be *highly* complex.

View file

@ -1,8 +1,6 @@
{% extends "../../../layouts/post.html" %}
{% block article %}
# A Roxy Update
I released a new version of Roxy over at [fem.mint.lgbt](https://fem.mint.lgbt/kitsunecafe/roxy-cli). I've been working on it for a few months now, one part because it's more complex than the original, and another part because I had to learn a lot. I want to make a quick rundown of the changes.
## Philosophy

View file

@ -2,8 +2,6 @@
{% import "../../../layouts/macros.html" as macros %}
{% block article %}
# Convenient Unity Attributes
Unity offers a range of convenient ways to manipulate and hack its inspector. Unfortunately, thanks to the size of Unity's documentation, these types of methods go unnoticed.
I won't attempt to iterate every useful attribute in Unity's library. I encourage you to explore Unity's documentation.

View file

@ -1,7 +1,6 @@
{% extends "../../../layouts/post.html" %}
{% block article %}
# Making a Static Site Generator (Also a New Site)
I decided to give my site a much-needed redesign, so in the spirit of making things harder than they need to be, I decided to make a static site generator. You might see "Made with Roxy" at the bottom of this page (unless you're reading this so far in the future that I've changed it again). Roxy is what I've decided to call this generator. I wanted to write a little about my experiences creating it.
## The Plan

View file

@ -1,8 +1,6 @@
{% extends "../../../layouts/post.html" %}
{% block article %}
# Simplifying Code with Components
Unity is a component-based game engine. Without any context, "component" is a nebulous and vague term. Understanding what a component *is* becomes crucial to understanding Unity. In Unity's [Introduction to components](https://docs.unity3d.com/Manual/Components.html), they provide an unsatisfactory definition of a component.
> Components define the behaviour of that GameObject.

View file

@ -1,7 +1,17 @@
{% extends "page.html" %}
{% block content %}
{% block article %}
{% endblock %}
{% endblock %}
{% set date_format = "%d %B, %Y" %}
<article>
<header>
<h1 class="header">{{ this.title }}</h1>
<small class="muted">
<time datetime="{{ this.created_date }}">
Posted on {{ this.created_date | date(format=date_format) }}
</time>
</small>
</header>
{% block article %}
{% endblock %}
</article>
{% endblock %}

View file

@ -5,8 +5,9 @@
"scripts": {
"copy:static": "ln -sfn $(realpath static) dist/static 2>/dev/null",
"build": "roxy_cli content dist && npm run copy:static",
"watch": "./dev",
"dev": "npm run build && npm run watch & wrangler pages dev --live-reload && fg",
"watch": "./watch",
"serve": "wrangler pages dev --live-reload",
"dev": "parallel -u -j3 \"npm run\" ::: build watch serve",
"deploy": "wrangler pages deploy --project-name kitsunecafe"
},
"keywords": [],

View file

@ -152,6 +152,11 @@ nav li::before {
content: '';
}
article header {
margin-top: 1rem;
margin-bottom: 1rem;
}
@media only screen and (min-width: 740px) {
nav.main-nav {
flex-direction: column;

View file