Alat pembangunan
Jangan cuba mencipta semula roda (sepenuhnya)!

Keputusan
Dalam artikel tentang permulaannya mungkin anda sudah membaca bahawa saya telah memutuskan untuk membuat permainan.
Ada sangat banyak cara untuk mendekati perkara sebegini, dan setiap situasi memerlukan pendekatan yang berbeza. Satu-satunya yang saya tahu 100% ialah ia mesti menjadi permainan pengembaraan point and click old school dalam 2D.
Dengan itu banyak alat pembangunan terus tersingkir, tetapi masih ada beberapa soalan:
- Dengan bahasa pengaturcaraan apa?
- Di platform mana permainan ini perlu berfungsi?
- Enjin atau rangka kerja apa yang akan digunakan?
- Forum sokongan?
Bahasa pengaturcaraan
Saya sudah boleh menggunakan PHP, C#, Python, Javascript, Node, Blitz 3D, Monkey X, Monkey 2. Dengan dua bahasa terakhir saya sudah pernah membuat prototaip. Tetapi pembangun bahasa itu berhenti, jadi saya tidak lagi yakin ia akan berakhir baik dan ketika itu saya juga berhenti.
Jadi ia mesti bahasa yang ringkas dan boleh dipercayai. Saya suka kesederhanaan dan juga tiada masa untuk benar-benar menguasai bahasa baharu.
Enjin
Enjin permainan seperti Unreal Engine wujud untuk menjadikan kerja jauh lebih mudah. Sebab itu pencarian saya bermula dengan mengkaji senarai penuh https://en.wikipedia.org/wiki/List_of_game_engines.
Banyak yang terus tersingkir kerana saya tidak mahu berbelanja untuk enjin, sekurang-kurangnya bayaran sekali tidak menjadi masalah. Tetapi jelas bukan binaan lesen yang pelik dan kabur seperti yang dicipta oleh sesetengah enjin. Saya juga mahu ia masih aktif dibangunkan dan bukan C atau C++. Dan sudah tentu 2D.
Ya, Unreal Engine juga boleh 2D, tetapi itu bertentangan dengan prinsip saya ’less is more’. Enjin sebegini mengandungi terlalu banyak fungsi yang saya tidak akan guna dan hanya boleh rosak. Sebab itu enjin sebegini tersingkir.
- Adventure Game Studio
- Windows, dan saya tidak boleh buat semua yang saya bayangkan di sana.
- Construct
- Masalah lesen, terlalu mahal dan bayaran bulanan
- Flixel
- Saya tidak mahu menggunakan ActionScript
- GameMaker Studio
- Terlalu lama, masalah lesen dan saya rasa kurang hebat haha
- LÖVE
- Mantap! Tetapi saya masih menyiasat
- Monkey X
- Saya pernah bermula dengan ini, tetapi sudah tiada sokongan
- Pygame
- Menarik, tetapi akhirnya bukan ini
- HaxeFlixel
- Tidak, memang tidak
- Cocos2d
- Menarik, tetapi tidak, pada pandangan saya kurang asas
- Godot
- Menarik, tetapi tidak, pada pandangan saya kurang asas
- Unity
- Masalah lesen
Kesimpulannya, nampaknya saya memang mahu membuat sesuatu sendiri. Ia mesti mempunyai lesen terbuka MIT atau yang seumpamanya dan serba boleh. Kajian menunjukkan bahawa sesetengah enjin di atas menggunakan SDL2, dan saya perlu tahu lebih lanjut tentangnya.
SDL2
Apabila anda mencari bagaimana perisian dan perkakasan bekerjasama untuk menggerakkan imej pada skrin melalui kad grafik (dan bunyi), anda akan lihat Windows menggunakan DirectX, Ubuntu OpenGL dan Mac Metal/Vulkan. Dan Windows serta Mac juga boleh menggunakan OpenGL…
Jadi setiap platform ada pustaka sendiri yang ditulis dalam bahasanya sendiri. Sangat menyusahkan, kerana anda tidak mahu menulis semula kod untuk setiap platform. Yang anda mahu ialah menulis kod 1 kali dan ia berfungsi di mana-mana.
Penyelesaian masalah ini ialah pustaka https://www.libsdl.org. Ia percuma di bawah lesen yang betul dan dengan kod yang sama anda boleh menggunakan semua platform. SDL2 ditulis dalam C dan ada beberapa binding yang membolehkan SDL2 digunakan dalam bahasa pengaturcaraan lain.
Oleh sebab saya tidak mahu menggunakan C, saya menyemak keseluruhan senarai binding untuk menguji:
- C
- Nampak tidak praktikal
- C#
- Tersingkir, kerana di rumah saya menggunakan Mac
- GO
- Sebenarnya saya suka dan ringkas, tetapi perlu dikompil setiap kali
- Haskell
- Ngeri, langsung bukan apa yang saya cari
- Rust
- Eh, terlalu sukar, tiba-tiba saya perlu memprogram secara fungsian
- Lua
- Menarik dan ringkas
- Python
- Entah kenapa ia tidak serasi, dan saya juga malas meletakkan TAB di mana-mana.
- Nim
- Nampaknya tiada siapa menggunakannya
Pada ketika ini tinggal GO dan Lua, dan dalam pencarian enjin sebelum ini saya sudah nampak LÖVE.
LÖVE
Rangka kerja https://love2d.org memenuhi semua keperluan saya.
Saya tidak memanggilnya enjin tetapi rangka kerja, kerana ia hanya mengandungi fungsi yang menggunakan SDL2 secara asas. Dan itulah yang saya cari, ia memenuhi semua perkara saya:
- Ada forum di mana semua orang masih suka membantu dan peramah.
- Rangka kerjanya asas (less is more).
- Bahasa Lua ringkas dan sudah digunakan bertahun-tahun.
- Sasaran platform tempat permainan ini akan berfungsi.
function love.draw()
love.graphics.print("Hello: Signal From The Stars", 400, 300)
end
Lua

Logo rasmi Lua
Saya cuma perlu belajar Lua, jadi apa yang anda buat? Anda pergi ke rumah ibu bapa untuk mencetak keseluruhan manual Lua… Mereka tidak begitu gembira.
Akhirnya ternyata bahasa itu begitu ringkas sehingga saya tidak perlukan manual.
Saya rasa Lua boleh dilihat sebagai wrapper yang berhubung dengan C. Satu lagi kelebihan ialah anda tidak perlu menunggu pengkompil selepas setiap perubahan kod.
Sudah tentu ada juga kelemahannya, contohnya ia tidak sepantas C, tetapi itu bukan masalah.
Sasaran
Permainan Signal From The Stars mesti sekurang-kurangnya berfungsi pada Mac, Windows dan iOS (iPad). Kemudian Nintendo Switch, Ubuntu dan Android akan ditambah. Mungkin ia juga boleh berfungsi pada Xbox dan PlayStation, tetapi tidak terus out of the box.
Imej
Permainan ini juga akan mempunyai imej, dan untuk itu pada mulanya saya menggunakan program Aseprite, kerana ia ada mod pixel perfect. Itu gaya tertentu yang saya gunakan. Namun kemudian saya beralih kepada Adobe Photoshop. Saya akan menulis artikel tentang ini kemudian.
Semua imej kemudian diproses secara optimum melalui TexturePacker https://www.codeandweb.com/texturepacker, dan output itu kemudian diproses lagi dengan skrip.
Skrip
Beberapa skrip berasingan diperlukan untuk menyusun perkara tertentu. Kebanyakannya dalam Lua, tetapi kemudian akan bertambah lagi.
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
Pilihan Node hanya kerana ada modul yang boleh membaca fail Adobe Photoshop (https://github.com/meltingice/psd.js). Saya masih menggunakannya, dan ia menukar fail Photoshop kepada aset dan Atlas untuk permainan.
Jika ketika itu saya jumpa perkara yang sama dalam Python, saya tidak akan menggunakan Node. Entah kenapa Node dengan berbilion pustakanya tidak begitu sesuai dengan apa yang saya cari. Ia mengingatkan saya sedikit kepada neraka DLL dalam Windows.
Python
Saya menyesal tidak menulis lebih banyak Python. Di bawah ialah satu-satunya skrip yang saya gunakan dalam Python buat masa ini, tetapi ia penting. Dengannya saya boleh mencari garis luar fail .png lutsinar dan meringkaskan koordinat tersebut.
# 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
Semuanya dijejak dan disimpan dalam git dan BitBucket, kerana ia menyediakan beberapa repo peribadi (percuma).
Editor
Saya telah menguji beberapa editor, tetapi secara peribadi Visual Studio Code paling sesuai untuk saya. Tentunya bersama beberapa sambungan.
- Code Runner
- Love2D Support
- Markdown Preview Enhanced
- reStructuredText Syntax highlighting
- vscode-lua
Dengan itu saya boleh memulakan semula permainan dengan satu tekan butang dan menyahpepijat jika perlu. Penonjolan sintaks juga sangat melegakan bagi saya. Autolengkap sebenarnya tidak pernah saya gunakan.
Menulis cerita
Dan sekarang bahagian yang pelik. Anda mungkin fikir Word, LibreOffice, Markdown… Tetapi saya menulisnya dalam fail reStructuredText (.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...
Semua fail .rst berada dalam folder yang berbeza, dan dengan skrip di bawah saya mengeksportnya ke PDF. Kemudian mencetaknya.
Tentang pengembaraan ini sudah tentu saya akan menulis artikel.
#!/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
Rajah
Keseluruhan cerita sudah tentu mengandungi teka-teki, dan saya menjadikannya kelihatan untuk diri sendiri dengan program percuma (https://app.diagrams.net).
Yaml
Untuk menyusun semuanya secara ringkas pada mulanya, saya memilih untuk tidak meluangkan masa membuat editor permainan. Keseluruhan permainan disusun dengan fail .yaml yang ditukar kepada json dan digunakan dalam permainan.
Gambaran keseluruhan
Jika anda melihat semua yang saya tulis setakat ini, anda akan sampai kepada skema di bawah.
Di situ anda nampak engine3 dan game, yang akan saya terangkan dalam catatan lain.
Rajah alat akhir