Testing my Game Design with... Svelte? (or: "is it fun?")

Photo by Colin Watts on Unsplash

Testing my Game Design with... Svelte? (or: "is it fun?")

·

6 min read

It'd been quite a while, actually longer if you consider all kinds of efforts and re-writes and dreams over the years since I started writing the current version of my online game. For the current version, I knew I had to get it into a minimal playable phase and then start testing it out on real people to see if it's fun, as they tell you in all the Beginning Game Development tutorials. A POC.

Two nights ago I realized I was barking up the wrong tree with my POC's mission. Testing if waiting for a long time for some thing to happen (colonizing a planet, building a jump gate to get to the next system, etc.) was fun, but it was not correct in retrospect.

At this stage in my persistent-universe single/co-op player online space game you can basically build two kinds of ships, one of which does nothing, colonize planets, build a jump gate to get to another system (where you have to build another jump gate just to return) and mine some almost infinite amount of resources for building all of these stuff.

While playing it for myself it had some sense of fun for expansion and maybe even proving that waiting can be some sort of an interesting factor, along the 'always-running' persistence of the universe for the player, which was me, was also fun (I was actually checking my phone on my commute to see my colonies). It was nearing a point where I would send the game over to some friends for feedback... but there were not enough "things to do" in it to get any serious feedback before having them leave the game to not play it again after a month or two when I address any comments they might have given to this poor experience lacking interest 🤷‍♂️

So back on to the drawing board. Some kind of "game" should be made and put over the client/server architecture and mobile friendliness efforts. Here's some sketch for your eye-candy (done with my Lenovo Yoga laptop's pen, fun!).

Whiteboard.png

I was playing with the thought of resource production balancing, creating "advanced" resources from them, pollution, trees, population, people dying from things, this kind of stuff.

But is it fun?

Let there be code!

First: ugly Python for the interpreter

On the first night I hacked a really ugly Python script that created a small game class with a game.tick() and actions likeupgrade_mining which would spit out things to the terminal. It was ugly but gave a sense to some of the resource production balance when selecting which of two main resources you would like to mine more. It is meant to be run in the interpreter. It looks something like this:

BEWARE OF UGLY AND QUICK TEST POC CODE.

class Game:
    def __init__(self):
        self.raw1 = 0
        self.raw2 = 0
        self.special = 0

        self.mining_ratio = 0.5  # 0 -> full raw1, 1 -> full raw2
        self.mining_power = 10
        self.special_power = 0
        self.special_ratio = 1

    def tick(self):
        self.mine_raw()
        self.produce_special()
        return self

    def mine_raw(self):
        raw1_income = self.mining_power * (1 - self.mining_ratio)
        raw2_income = self.mining_power * self.mining_ratio
        self.raw1 += raw1_income
        self.raw2 += raw2_income

    def produce_special(self):
        cost = self.special_ratio * self.special_power * 5
        if self.require_and_consume_resources(0, cost):
            self.special += self.special_ratio * self.special_power

    def __repr__(self):
        return f"r1 {self.raw1}      r2 {self.raw2}      special: {self.special}"

    def improve_mining(self):
        cost = self.mining_power ** 1.5
        if self.require_and_consume_resources(cost, cost):
            self.mining_power += 1
        return self.tick()

    def improve_special(self):
        cost = self.special_power ** 2
        if self.require_and_consume_resources(cost, cost):
            self.special_power += 1
        return self.tick()

    def require_and_consume_resources(self, raw1, raw2):
        errors = []
        if self.raw1 < raw1:
            errors.append(f"insufficient raw1, need {raw1}")
        if self.raw2 < raw2:
            errors.append(f"insufficient raw2, need {raw2}")
        if len(errors) > 0:
            print(", ".join(errors))
            return False
        self.raw1 -= raw1
        self.raw2 -= raw2
        return True


g = Game()

It looks like this after some rounds:

Screenshot from 2021-12-09 09-03-36.png

With tkinter

After that, I decided the terminal is ugly, so I wrote an even uglier Python script with tkinter and made a similar simulation app.

BEWARE OF UGLIER AND QUICKER TEST POC CODE.

from tkinter import *

top = Tk()
top.geometry("300x400")

raw1 = 0
raw2 = 0
special = 0

mining_power = 20
mining_ratio = DoubleVar(value=0.5)

special_power = 0
special_ratio = DoubleVar(value=1.0)


def get_resources_text():
    return f"r1 {round(raw1)}, r2 {round(raw2)}, sp. {round(special)}"


def tick():
    raw1_income = mining_power * (1 - mining_ratio.get())
    raw2_income = mining_power * mining_ratio.get()
    global raw1, raw2, special
    raw1 += raw1_income
    raw2 += raw2_income
    resources.config(text=get_resources_text())

    special_cost = special_ratio.get() * special_power
    if require_and_consume_resources(0, special_cost * 10):
        special += special_cost


def require_and_consume_resources(cost_r1, cost_r2):
    global raw1, raw2
    errors = []
    if raw1 < cost_r1:
        errors.append(f"insufficient raw1, need {round(cost_r1)}")
    if raw2 < cost_r2:
        errors.append(f"insufficient raw2, need {round(cost_r2)}")
    if len(errors) > 0:
        errors_label.config(text="\n".join(errors))
        return False
    raw1 -= cost_r1
    raw2 -= cost_r2
    return True


def upgrade_mining():
    global mining_power
    cost = mining_power ** 1.3
    if require_and_consume_resources(cost, cost):
        mining_power += 5
    tick()


def upgrade_special():
    global special_power
    cost = special_power ** 2.1
    if require_and_consume_resources(cost, cost):
        special_power += 1
    tick()


resources = Label(text=get_resources_text())
resources.pack()
mining_slider = Scale(top, orient=HORIZONTAL, var=mining_ratio, to=1, resolution=0.01)
mining_slider.pack()
special_slider = Scale(top, orient=HORIZONTAL, var=special_ratio, to=1, resolution=0.01)
special_slider.pack()
ticker = Button(text="Tick", command=tick)
ticker.pack()
upgrade_mining_button = Button(text="Upgrade mining", command=upgrade_mining)
upgrade_mining_button.pack()
upgrade_special_button = Button(text="Upgrade special", command=upgrade_special)
upgrade_special_button.pack()
errors_label = Label()
errors_label.pack()
top.mainloop()

Which has an output like that, after some gameplay:

Screenshot from 2021-12-09 09-05-46.png

But this was not going anywhere, enter Svelte

Doing 99% of frontend development as part of my day job nowadays, and the web being a good UI creation platform having all kinds of frameworks like Svelte that makes it easy to have bound inputs to actual variables quickly, I decided to make a minimal web app with create-snowpack-app and the TypeScript Svelte template to make my simulations on a web page. It looks like this:

Screenshot from 2021-12-09 09-21-58.png

The funny resource names are wordplay over the names of me and my girlfriend.

So while I don't have any spaceships that I can build or anything to do with my advanced resources at the moment, I do get a sense of urgency when my oxygen or "air quality" drops due to over-production and people die. The next steps would be really adding some ship building and maybe colonization of other planets.

This is not a game. This is a way to balance the game and find fun resource balancing mechanics.

Some excerpts from the code might be:

...
  function lifeTick() {
    oxygen -= population * 0.1 + pollution ** 2;
    const noOxygen = oxygen <= 0;
    population += population * 0.005;
    if (noOxygen) {
      population -= Math.abs(oxygen) * 0.12;
    }
    if (population > planetSize) {
      population = planetSize;
    }
    if (population < 0) {
      population = 0;
    }
    oxygen += trees / 100;
    if (oxygen > planetSize * 1000) {
      oxygen = planetSize * 1000;
    }
  }

  function treesTick() {
    if (
      population > 0 &&
      trees < plannedTrees &&
      requireAndConsumeResourcesIfAnyoneIsAlive(1, 1, 0, 0)
    ) {
      trees += 1;
    }
  }
...
...
  let miningPower = 1;
  let amithium = 100;
  let amirin = 100;
  let mirmiton = 0;
  let refinedAmirin = 0;
  let miningRatio = 0.5;

  $: pollution =
    miningPower * 0.2 +
    productionPower * 0.3 * mirmitonRatio +
    productionPower * refinedAmirinRatio * 0.5;
...

You get the idea.

So that's what I do now. After some balancing and finding the right concepts and ideas where I can build something playable around, I will integrate these ideas to the main game gameplay and then could send it to some friends for initial testing.

After some of these iterations, I plan to open a public alpha for the game some time.

Thanks, bye.