Utviklingsverktøyene
Ikke prøv å finne opp hjulet (helt) på nytt!

Avgjørelser
I artikkelen om begynnelsen har du kanskje lest at jeg hadde bestemt meg for å lage et spill.
Det finnes utrolig mange måter å gripe an noe slikt på, og hver situasjon krever en annen tilnærming. Det eneste jeg visste 100 %, var at det skulle bli et oldschool pek og klikk eventyrspill i 2D.
Dermed falt mange utviklingsverktøy bort med en gang, men det var fortsatt noen åpne spørsmål:
- Med hvilket programmeringsspråk?
- På hvilken plattform skal spillet fungere?
- Hvilken motor eller hvilket rammeverk skal du bruke?
- Supportforum?
Programmeringsspråk
Jeg kunne allerede bruke PHP, C#, Python, Javascript, Node, Blitz 3D, Monkey X, Monkey 2. Med de to siste språkene hadde jeg allerede laget en prototype. Men utvikleren av språket sluttet, og dermed hadde jeg ikke lenger tro på et godt utfall og sluttet selv også den gangen.
Det måtte altså være et enkelt og pålitelig språk. Jeg liker enkelhet og hadde heller ikke tid til å lære et nytt språk skikkelig.
Motor
En spillmotor som Unreal Engine finnes for å gjøre arbeidet mye enklere. Derfor begynte letingen min med å studere hele listen https://en.wikipedia.org/wiki/List_of_game_engines.
Mye falt bort med en gang, siden jeg ikke ville bruke penger på en motor, i hvert fall var en engangsbetaling ikke noe problem. Men absolutt ingen rare, uklare lisenskonstruksjoner som noen motorer hadde funnet på. Jeg ville også at den fortsatt ble aktivt utviklet og at det ikke var C eller C++. Og selvfølgelig 2D.
Ja, Unreal Engine kan også 2D, men det går imot prinsippet mitt ’less is more’. Slike motorer inneholder altfor mange funksjoner jeg ikke kommer til å bruke, og som bare kan gå i stykker. Derfor faller slike motorer bort.
- Adventure Game Studio
- Windows, og jeg kan ikke gjøre alt jeg hadde tenkt der.
- Construct
- Lisensproblemer, for dyrt og månedlig betaling
- Flixel
- Jeg vil ikke bruke ActionScript
- GameMaker Studio
- For gammelt, lisensproblemer og syntes ikke det var kult nok haha
- LÖVE
- Nice! Men jeg holder fortsatt på å undersøke
- Monkey X
- Her begynte jeg en gang, men det har ikke support lenger
- Pygame
- Interessant, men det ble det ikke til slutt
- HaxeFlixel
- Nei, bare nei
- Cocos2d
- Interessant, men nei, i mine øyne ikke enkelt nok
- Godot
- Interessant, men nei, i mine øyne ikke enkelt nok
- Unity
- Lisensproblemer
Konklusjonen var at jeg tydeligvis virkelig ville lage noe selv. Det måtte ha en åpen MIT-lisens eller noe lignende og være allsidig. Undersøkelser viste at noen av motorene over bruker SDL2, og det måtte jeg vite mer om.
SDL2
Når du undersøker hvordan programvare og maskinvare samarbeider for å få et bilde til å bevege seg på skjermen via skjermkortet (og lyd), ser du at Windows bruker DirectX, Ubuntu OpenGL og Mac Metal/Vulkan. Og Windows og Mac kan også bare bruke OpenGL…
Så hver plattform har sitt eget bibliotek, som igjen er skrevet i sitt eget språk. Veldig upraktisk, for du vil ikke skrive kode på nytt for hver plattform. Det du vil, er å skrive koden 1 gang og at den fungerer overalt.
Løsningen på dette problemet er biblioteket https://www.libsdl.org. Det er gratis under riktig lisens, og med den samme koden kan du bruke alle plattformer. SDL2 er skrevet i C, og det finnes en rekke bindings som gjør at du kan bruke SDL2 i et annet programmeringsspråk.
Siden jeg ikke ville bruke C, gikk jeg gjennom hele listen over bindings for å teste litt:
- C
- Virket ikke praktisk
- C#
- Falt bort, for hjemme brukte jeg en Mac
- GO
- Likte det egentlig og det var enkelt, men du måtte kompilere hver gang
- Haskell
- Skrekk, dette var absolutt ikke det jeg lette etter
- Rust
- Øh, dette var for vanskelig, plutselig måtte jeg programmere funksjonelt
- Lua
- Interessant og enkelt
- Python
- På en eller annen måte passet det ikke, og jeg gidder heller ikke sette en TAB overalt.
- Nim
- Ingen så ut til å bruke det
På dette tidspunktet gjensto altså GO og Lua, og i et tidligere søk etter en motor hadde jeg allerede sett LÖVE.
LÖVE
Rammeverket https://love2d.org oppfylte alle kravene mine.
Jeg kaller det ikke en motor, men et rammeverk, siden det bare inneholder funksjoner som bruker SDL2 på en enkel måte. Og det er akkurat det jeg ser etter, det oppfyller alle punktene mine:
- Det finnes et forum der alle fortsatt er hjelpsomme og hyggelige.
- Rammeverket er enkelt (less is more).
- Språket Lua er enkelt og har vært brukt i mange år.
- Targets for plattformene spillet skal fungere på.
function love.draw()
love.graphics.print("Hello: Signal From The Stars", 400, 300)
end
Lua

Den offisielle Lua-logoen
Jeg måtte bare lære Lua, så hva gjør du da? Du drar til foreldrene dine for å skrive ut hele Lua-manualen… Det var de ikke så glade for.
Til slutt viste det seg at språket var så enkelt at jeg ikke trengte manualen.
Lua kan man vel se som en wrapper som snakker med C. Det er også en fordel at du ikke hele tiden må vente på en kompilator etter hver kodeendring.
Det finnes selvfølgelig også noen ulemper, det er for eksempel ikke like raskt som C, men det er ikke noe problem.
Targets
Spillet Signal From The Stars må minst fungere på Mac, Windows og iOS (iPad). Senere kommer Nintendo Switch, Ubuntu og Android til. Det kan sannsynligvis også fungere på Xbox og PlayStation, men ikke rett out of the box.
Bilder
Spillet får også bilder, og til det brukte jeg først programmet Aseprite, siden det har en pixel perfect modus. Det er en bestemt stil jeg bruker. Senere gikk jeg likevel over til Adobe Photoshop. Jeg skal skrive en artikkel om det senere.
Alle bildene behandles deretter optimalt via TexturePacker https://www.codeandweb.com/texturepacker, og det resultatet behandles så på nytt med et skript.
Skript
Det trengs noen enkeltstående skript for å få orden på visse ting. Det meste er i Lua, men etter hvert kommer det mer og mer.
Shell
#!/bin/bash
cd ../
luacheck --no-cache --config scripts/.luacheckrc src
cd src/lib/engine3/scripts/
echo "UNIT TEST"
./unittest.sh
echo "INTEGRATION TEST"
./integrationtest.sh
Node
Valget av Node skyldes bare at det fantes en modul som kunne lese Adobe Photoshop-filer (https://github.com/meltingice/psd.js). Den bruker jeg fortsatt, og den gjør om Photoshop-filen til ressurser og en Atlas for spillet.
Hvis jeg den gangen hadde funnet det samme i Python, hadde jeg ikke brukt Node. På et vis passer ikke Node med sine milliarder av biblioteker helt til det jeg ser etter. Det minner meg litt om DLL-helvetet i Windows.
Python
Jeg angrer på at jeg ikke har skrevet mer Python. Under står akkurat nå det eneste, men viktige, skriptet jeg bruker i Python. Med det kan jeg finne omrisset av en gjennomsiktig .png-fil og forenkle koordinatene.
# python3 outline.py
# pip3 install pillow
# pip3 install opencv-python
import sys
import cv2
import numpy as np
import json
import os
import argparse
def find_outer_contours(image_path, simplify=0):
img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
mask = img[:, :, 3] > 0
contours, _ = cv2.findContours(mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
min_contour_size = 5
filtered_contours = [contour for contour in contours if cv2.contourArea(contour) > min_contour_size]
if simplify > 0:
simplified_contours = []
for contour in filtered_contours:
epsilon = simplify * cv2.arcLength(contour, True)
simplified_contour = cv2.approxPolyDP(contour, epsilon, True)
simplified_contours.append(simplified_contour)
return simplified_contours
else:
return filtered_contours
def draw_and_save_contours(contours, output_path, original_size):
img = np.zeros((original_size[1], original_size[0]), dtype=np.uint8)
cv2.drawContours(img, contours, -1, (255), thickness=1)
cv2.imwrite(output_path, img)
def contour_coordinates_to_json(contours):
contour_coordinates_list = []
for contour in contours:
contour_coordinates = []
for point in contour:
x, y = point[0]
contour_coordinates.append([int(x), int(y)])
if len(contour_coordinates) % 2 != 0:
contour_coordinates.append([contour_coordinates[-1][0], contour_coordinates[-1][1]])
contour_coordinates_list.append(contour_coordinates)
return json.dumps(contour_coordinates_list)
def apply_convex_hull(contours):
combined_contour = np.concatenate(contours)
hull = cv2.convexHull(combined_contour)
return [hull]
def create_rectangles(contours):
rectangles = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
rectangles.append((x, y, x + w, y + h))
return rectangles
def rectangles_to_json(rectangles):
json_output = json.dumps(rectangles)
return json_output
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Find outer contours of a transparent image.')
parser.add_argument('image_path', type=str, help='Path to the image file.')
parser.add_argument('--simplify', type=float, default=0.01, help='Simplify factor for contour approximation. Set to 0 to disable simplification.')
parser.add_argument('--hull', action='store_true', help='Apply convex hull to the contours.')
parser.add_argument('--rects', action='store_true', help='Return contour x1,y2,x2,y2 outline rects')
parser.add_argument('--output_path', type=str, default='', help='Path to save the output image. If not provided, the image will not be saved.')
args = parser.parse_args()
image_path = args.image_path
original_img = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
original_size = (original_img.shape[1], original_img.shape[0])
contours = find_outer_contours(image_path, simplify=args.simplify)
if args.hull:
contours = apply_convex_hull(contours)
if args.output_path:
draw_and_save_contours(contours, output_path, original_size)
if args.rects:
rectangles = create_rectangles(contours)
json_output = rectangles_to_json(rectangles)
print(json_output)
else:
json_output = contour_coordinates_to_json(contours)
print(json_output)
Git
Alt spores og lagres i git og på BitBucket, siden den har flere (gratis) private repoer.
Editor
Jeg har testet flere editorer, men personlig fungerte Visual Studio Code best for meg. Riktignok i kombinasjon med ulike utvidelser.
- Code Runner
- Love2D Support
- Markdown Preview Enhanced
- reStructuredText Syntax highlighting
- vscode-lua
Da kan jeg starte spillet på nytt med ett tastetrykk og eventuelt feilsøke. Og syntaksuthevingen er også en lettelse for meg. Autofullføring bruker jeg egentlig aldri.
Å skrive historien
Og nå kommer det rare. Du skulle tro Word, LibreOffice, Markdown… Men jeg skriver den i reStructuredText-filer (.rst).
===========================
Scene 20: Nederland - Intro
===========================
.. image:: ../project/story/scene/20/resources/scene20.png
:width: 100%
.. contents::
:depth: 1
.. raw:: pdf
PageBreak
Omschrijving
============
Een bos in Nederland in de len...
Alle .rst-filene ligger i ulike mapper, og med skriptet under lager jeg en eksport til PDF. For så å skrive den ut.
Om dette eventyret skal jeg selvfølgelig skrive en artikkel.
#!/bin/bash
set -e
source config
if [ -n "$1" ]; then
TARGET_SCENE="$1"
TARGET_PDF="scene_${TARGET_SCENE}.pdf"
echo "Generating PDF for scene $TARGET_SCENE"
else
echo "Generating all PDF files"
fi
mkdir -p ../temp/docs/
for dir in ../project/story/scene/*; do
SCENE_NUMBER=$(basename "$dir")
PDF_NAME="scene_${SCENE_NUMBER}.pdf"
if [ -z "$TARGET_PDF" ] || [ "$PDF_NAME" = "$TARGET_PDF" ]; then
echo -e "Create: $PDF_NAME"
# merge *.rst scene files
first_iteration=true
TEMP_FILE=$(mktemp tempfile.XXXXXX)
for md_file in $(ls "${dir}"/*.rst | sort); do
TEMP_CONTENT=$(mktemp /tmp/tempcontent.XXXXXX)
if $first_iteration; then
first_iteration=false
else
item_number=$(basename "$md_file" .rst | cut -d'-' -f1)
item_description=$(basename "$md_file" .rst | sed -E 's/^[0-9]+-//; s/-/ /g')
title=""
# inventory item ends with 9
if [ ${#item_number} -eq 6 ] && [[ "$item_number" == *"9" ]]; then
title="$item_number $item_description (inventory)"
else
title="$item_number $item_description"
fi
title_length=${#title}
separator=$(printf '=%.0s' $(seq 1 $title_length))
echo -e "$title\n$separator\n" >> "$TEMP_CONTENT"
fi
cat "$md_file" >> "$TEMP_CONTENT"
echo -e "\n\n.. raw:: pdf\n\n PageBreak\n" >> "$TEMP_CONTENT"
cat "$TEMP_CONTENT" >> "$TEMP_FILE"
rm "$TEMP_CONTENT"
done
rst2pdf --footer="###Page### | ###Title###" "$TEMP_FILE" --output="../temp/docs/${PDF_NAME}"
rm "$TEMP_FILE"
if [ $# -eq 1 ]; then
echo "Opening $PDF_NAME..."
open "../temp/docs/${PDF_NAME}"
fi
fi
done
if [ $# -eq 0 ]; then
echo -e 'Create: concept_document.pdf'
rst2pdf --footer="###Page### | ###Title###" ../project/story/concept-document.rst --output="../temp/docs/concept_document.pdf"
echo -e 'Create: story_notes.pdf'
rst2pdf --footer="###Page### | ###Title###" ../project/story/notes.rst --output="../temp/docs/story_notes.pdf"
fi
Diagram
Hele historien inneholder selvfølgelig gåter, og dem gjør jeg synlige for meg selv med det gratis programmet (https://app.diagrams.net).
Yaml
For å sette alt enkelt sammen i første omgang valgte jeg å ikke bruke tid på en spilleditor. Hele spillet er satt sammen av .yaml-filer som gjøres om til json og brukes i spillet.
Oversikt
Når du ser på alt jeg har skrevet så langt, ender du opp med skjemaet under.
Her ser du engine3 og game, som jeg skal forklare i et annet innlegg.
Diagram over de endelige verktøyene