Closed72

Nimを調べてみる

hasturhastur

動機

NimにArc/Orcが導入されて「この、メモリ管理方式なら本格的にゲーム開発に採用できるのでは?」と思ったので調査してみる。

hasturhastur

目的

ゲーム開発に採用できるのか調べる。

仮想のシチュエーション:
1からゲーム開発環境を構築する際にCではキツイがC++は諸々の問題から積極的に採用したくないのでNimを検討する。

※諸々:includeによるライブラリの管理、コンパイル速度、パッケージマネージャの欠落、複雑な構文、メモリ管理の手間

hasturhastur

調査の方向

  1. とにかく速度を出せるか調べる
  2. メモリ管理について調べる
  3. Cとの連携について調べる
  4. Cとの構文の比較
  5. C++で欠如している部分を補えるか
hasturhastur

現状の知識

数年前に触って基本的な構文は知っている(Ver1.0にヒットする前)
細かいところは知らない。

hasturhastur

速度について

ゲーム開発で使うには高速に動作する必要があるので速度が出るか調べる。

高速に動作するだけだと、ざっくりしすぎなので細かく定義する。

  • 実行時の処理速度が高速
    プログラムを実行した後の処理が速いということ
  • 処理全体が高速
    Pythonで一部関数をCに置き換えたから高速だよね、とはならない
  • 安定的に高速
    GCが走ってスパイクが起きたりするのはNG

を満たせると高速であると言える。

hasturhastur

速度測定の定番であるベンチマーク計測

  • 概ねC++のx1.0 ~ x3.0の間
  • メモリ管理をすべて手動にすることでx1.0に近づけられる

気になるのはGCをArcに切り替えた場合のパフォーマンス。
現在(Version 1.4.8)のNimのデフォルトGCはOrc(Arc)では無いので、これらのベントマークもおそらく違う。

https://github.com/kostya/benchmarks
https://github.com/frol/completely-unscientific-benchmarks

hasturhastur

と、書いたけど正直この手のベンチマークはあんまり当てにならないので参考までに考える。
ただ最悪でも3倍遅い程度なので使えないほど遅いわけでは無い。

hasturhastur

言語機能的に高速に動作させる事が出来るかを考えてみる。

言語としてはNimは他言語にコンパイルされる言語なので最終的にはCにコンパイルされCの最適化が受けられる。
「Cに変換されるからCと同等の速度です」は素直過ぎるがCに近い速度が得られる。

コンパイル時に計算できると一部処理を事前に計算したり、そもそも計算しなくて済むようになる。
どのくらい柔軟に扱えるかは分からないがマニュアルを見る限り行える様子(マニュアルではコンパイル時にフィボナッチ数列を定数化している)

Move Semanticsについてもサポートされているのでコピー周りの処理も高速に動作させられる。
https://nim-lang.org/docs/destructors.html

上のベンチでも触れられているがメモリ自体は手動で管理する機能も有るので自身が書くシステム自体は手動メモリ管理による高速化も行える。

hasturhastur

ゼロコスト抽象化を考えてみる。
が、ちょっと分からない。Generics自体は存在していて、おそらくコンパイル時に処理されると思うのだがコンパイル時間に処理されるかの記述をマニュアルから見つけられなかったので確定ではない。

hasturhastur

機能的にはパフォーマンスを意識して書く際に必要な機能はそろっているように感じる。
ベンチでも手動メモリ管理ならCに迫れるので言語自体の処理速度は十分に高速だと言えるのでは。

hasturhastur

本命のメモリ管理(と言いつつこれも実質は速度のお話

ここまで(Move Semanticsは除く)はArc導入前のNimにも言えることだがArcに関しては別。

まず、なぜ以前のNimはNGかといとGCがあるから。

ゲーム開発に使うには基本的にGCはNGで何でかというと速度を落とすから。
ベンチを見る限りGCが有っても高速な言語は多々あるのだがGCが避けられる理由は大きく2つでスパイクと最悪時間の長さが読めないこと。
ベンチで高速でも一定期間動かすたびにGCが走ってフレームレートが低下するとかだと採用できない。

指定した場所にメモリを割り当てたいという要件は多分無い。

ゲームじゃないけどDiscordがGoからRustに移行した理由に似ている。
https://misonln41.hateblo.jp/entry/2020/02/12/232853

特にゲームは16ms(60FPS)ですべての動作を終える必要が有るので極力ランタイムでコストを払いたくない。

hasturhastur

じゃあ、なぜArcなら良いかというとArcはGCじゃないから(--gc:arcで指定するけど)
Arcの中身は単純な参照カウントで使用されなくなったメモリを即破棄する仕組みになっている(C++のスマートポインタと違うのはコンパイル時に決定される点)
なのでGCと違いランタイムで払うコストは限りなく0に近くなる。

弱点は参照カウントそのままに循環参照を検出できない点。

ゲーム開発的にありがたいメリット:

  • ランタイムコスト(ほぼ)なし
  • メモリの開放は即時
  • RAIIが可能
  • 動作がコードから予測可能

気になるポイント:

  • 標準ライブラリはArcでリークしないのか
    フォーラムだと幾つかは使えるみたいな書き方なので全部が全部いけるわけではなさそう

https://nim-lang.org/blog/2020/10/15/introduction-to-arc-orc-in-nim.html
https://forum.nim-lang.org/t/5734

hasturhastur

もう一つOrcというArcベースで循環参照を検出する機構が付いたモノも存在するが、こちらはArcよりもコストを支払うことになるので今回は除外(とはいえ既存のGCよりはかなり少ない様子)

hasturhastur

外部のライブラリのリークについては仕方が無い。
Arcでメモリのリークが無いことを保証するライブラリを使うしかない。

一応、回避策として{.acyclic.}というプラグマも存在するが完全解決するわけでは無い。
(そもそも所有権モデルが循環参照前提のデータ構造に弱い)

Orcのパフォーマンスでも満足できるならOrcで解決することはできるが今回は無視する。

hasturhastur

Arcがランタイムコスト(ほぼ)0だとして動作にどれほど影響するかを考える。
ここでは競合相手としてC++を想定する。

まずC++自体、ヒープにメモリを確保した場合はほぼ間違いなくスマートポインタで管理することになる。
unique_ptr, shared_ptrで管理するので実際にはメモリ管理に全くコストを払わないわけでは無い。

スマートポインタでの管理とArcでの管理のどちらがコストが高いかを比較する必要が有るのだが...正直、分からん。
unique_ptrは別としてshare_ptrは同じく参照カウントなので、どちらかが非常に重いという事は無いはず。またC++にはweak_ptrもあるので一概に比較は出来ない。

C++がshare_ptrを活用して高速に動作しているのを見るにNimのArcも十分高速に動作するのではないだろうか...。

hasturhastur

ランタイムコストについては分からないがメモリ管理の精神的コストで言えばArc搭載のNimの方が優れていると思われる。

hasturhastur

NimはGCを動作させないモードもあるが、それは検討しない。
ほかのGCを切れる言語にも言えることだがGCを切ると標準ライブラリがリークするので標準ライブラリ無しでコードを書く必要があるのが辛い。
そもそもGC切るのは通常の動作から離れたこととされるので資料や言語機能の点でも辛い。

hasturhastur

Arcの仕組みから言って速度面の問題は無さそう。
懸念点はライブラリ側のリーク問題か。

Arc/Orcはヒープのサイズによって処理速度が変わるわけでは無いのでDiscordの例と同じ問題は起きない。
Orcも速そうなんだけどOrcに対して言及されているドキュメントが少なくて判断しかねる。

hasturhastur

GCの辛いところはGCに起因するバグだったり動作を修正したくてもGC自体が言語と深く絡みついているから取り除けないのが辛いところ。通常は自前でGC用意して書き換えるわけにもいかないし。

ヒープサイズに依存せず、ソフト側からも制御出来るしOrcで致命的な問題が起きる未来は見えないが確約はできない。

hasturhastur

メモリ管理まとめ

  • パフォーマンスの問題は無い
  • GCの利点は受けられる(一部)
  • 標準ライブラリもバグなく使える(一部)
  • 外部ライブラリがバグなく使えるかはライブラリによる

Arcだと自分で書く分には問題ないけど外部ライブラリに不安が残る。
Orcだとパフォーマンス面でやや不安が残るがGCの恩恵はすべて受けられる。

hasturhastur

簡単に手元でArc/Orcを切り替えてテストしてみたがメモリ使用量がOrcの方がやや多いくらいで開放のタイミングは同じだった。
なのでOrcを使用しても循環参照が無ければArcと同じようなパフォーマンスを望めるか?

OrcはArcをベースにしたメモリ管理方式で循環参照を処理するためにCycleCollectorが搭載されている。
Cycle collector自体はソフト側で停止させたり動作させるなどの制御も可能で動作タイミングも指定できる。
なので自分で書くコードに関してはなるべく循環参照しない形式にして最低限の動作になるように努める。
標準ライブラリや外部ライブラリへの保険としての役割をCycleCollectorに期待する。

https://nim-lang.org/docs/gc.html

hasturhastur

OrcであってもArcに近い挙動でCycleCollector自体はソフト側から制御できるので極端に問題が起きることは考えにくい。
が、コレは作るソフトの規模にかかわる話なので何とも言えないところもある。

極端なこと言えばソロで作る場合は、どうしたって規模は小さくなるので今議題に挙げているGCが低速な言語でもどうにかなる。
分かりやすい例はC#でMonoGameで作られているゲームは多く有る一方でUnityやカプコンはC#をゴリゴリにカスタマイズして極力GCの影響を少なくしている。

AAA級の大規模開発を想定しているわけでは無いが完全ソロレベルは想定していない。
https://gist.github.com/raysan5/909dc6cf33ed40223eb0dfe625c0de74
で言えばSmall-size Studiosで運用されるレベルを考えている。

hasturhastur

個人的な経験だとC#ではSmall-size StudiosレベルのゲームでもGCが気になると思うので極力GCのコストが小さくなるかを考えたい。

Orcの仕組みを見るにSmall-size Studiosレベルのゲームで問題になる事は考えにくいがNimの経験もGCのハンドリングの経験も全くないので分からん。

hasturhastur

Orcについての所感。

  • 個人で使う分には問題なし
  • Small-size Studiosレベルでも多分問題ない
  • Middle-size Studiosでは謎
  • The BIG Companiesは力技も使えるし逆に問題ないでしょ
hasturhastur

Cとの連携

ゲーム開発のメイン言語としてはC/C++が使われているのでゲーム開発に使えるライブラリの多くはC/C++で記述されている。
またウィンドウ制御やGPUの呼び出しなどはどうしてもCを呼び出す必要が有る。
以上の点からCとの連携が重要なので、どの程度行えるか調べる。
※NimからCを呼び出すパターンをメインに調査

hasturhastur

Nimは他言語にトランスコンパイルする言語でCにもコンパイルできる。
最終的にはCのコードしてコンパイルできる辺りCとの連携は容易そうに思える。

ただしCからNimを呼び出す場合はGCがArcでないとGC関連で面倒なことになりそう。

https://nim-lang.org/docs/backends.html

hasturhastur

CはABIも安定しているし単純な言語なのでCFFIを搭載した言語は多く存在する。
問題はC++で、こっちは期待していない。機能的にも独自のものが多く複雑なので単純にNimに置き換えられないという問題が有る(テンプレートを使った関数をNim側からどう呼び出すのかなど)

実際Cが呼べれば何とかなる部分は多いのでCが呼べれば良しとする。

hasturhastur

enumやstructはnim側で再定義する必要があるもののCの呼び出し自体は難しくなさそう。

hasturhastur

Cとの構文の比較

基本的な構文から何もかも比較するわけでは無い。
Cで有用に使っているテクニックやCで気になる挙動がNimだとどうなるかを調べる。

hasturhastur

関数の値渡し参照の挙動の確認

やりたいこと:

  • 関数の引数は参照で渡す
  • struct(object)を関数に渡す
  • 関数からの戻り値をコピーせずにstruct(object)で返す
hasturhastur

関数に渡す引数は最適化に応じて値渡しか参照渡しかが決まる。

proc value_or_ref(i: int32) = 
  echo i

value_or_ref(0)

効率的に値の受け渡しが出来れば問題ない。
またNimでは関数の引数はデフォルトでimmutableなので使う側からしたら気にすることが無い挙動である。

hasturhastur

上記の通りなので関数に引数を渡す挙動には何の問題も無い。

やりたかったこととしては

void create_enemy(enemy_data_t data) {
    ...
}
create_enemy((enemy_data_t){...});

みたいなコードで、Cだとポインタにするか最適化に期待するかみたいな択が存在したのだがNimでは気にする必要は無さそう。

hasturhastur

構造体のコピー

  • 構造体を素直にコピーしたい

なんで?
メモリ管理の手間を減らすためにコピーで済むデータはコピーして所有したい。

hasturhastur
import strformat

type ref_object_t = ref object
  data: int32

proc struct_copy_test() =
  let r = ref_object_t(data:32)
  let rr = r
  var cr = r[]
  let ccr = cr

  r.data = 100
  cr.data = 99

  echo fmt"r.data = {r.data}"
  echo fmt"rr.data = {rr.data}"
  echo fmt"cr.data = {cr.data}"
  echo fmt"ccr.data = {ccr.data}"

  echo fmt"repr r = {repr r}"
  echo fmt"repr rr = {repr rr}"
  echo fmt"repr cr = {repr cr}"
  echo fmt"repr ccr = {repr ccr}"

struct_copy_test()

出力

r.data = 100
rr.data = 100
cr.data = 99
ccr.data = 32
repr r = ref_object_t(data: 100)
repr rr = ref_object_t(data: 100)
repr cr = ref_object_t:ObjectType(data: 99)
repr ccr = ref_object_t:ObjectType(data: 32)
hasturhastur

ref objectのコピーは参照のコピー、[]で関節参照をコピーすると値自体をコピーできる。

nimはref objectで宣言できるので、objectとの挙動の違いが気になったが単純な参照として扱える様子。
(思えば当たり前の挙動ではある)

hasturhastur

強い型付け

  • 既存の型を別の型として扱う
typedef struct texture_id_t {
    int32_t id;
} texture_id_t;

みたいなやつ。

hasturhastur
type texture_id_t = uint32# typedef相当

type entity_id_t = object# 上の例
  id: int32
type model_id_t = distinct uint32# 完全に別の型として扱う

という書き方が有る。

distinctがやりたいことには一番近い。

# 元の型の関数を適用できるようにする
# proc `+`(x, y: model_id_t): model_id_t {.borrow.}

proc typedef_test() =
  var entity = entity_id_t(id: 0)
  var texture_id: texture_id_t = 0
  var model_id: model_id_t = model_id_t(0)

  texture_id = 1 + 2

  # {.borrow.}プラグマで機能を持ってくると使えるようになる
  # model_id = model_id_t(1) + model_id_t(2)

既存の関数に適用することもできるので、より柔軟性が高い。

hasturhastur

構造体を渡す関数の省略

  • タイプ量を減らしたい
typedef struct sparse_set_o {
    int32_t data;
} hoget_t;

void sparse_set_alloc(sprase_set_o* sset, int32_t val) { sset->data = val; }

sprase_set_o sset = {0};
sprase_set_alloc(&sset, 0);

構造体を渡す関数の名前が長くなりがちなのでタイプ量が増える。
OOP的に.でつないで呼び出したい。

hasturhastur

D言語のUFCSのような機能がNimにも存在する。

type sparse_set_o = object
  data: int32

proc alloc(sset: var sparse_set_o, val: int32) =
  sset.data = val

var sset = sparse_set_o()
sset.alloc(0)

のように書ける。

hasturhastur

またNimは引数が1つなら()を省略できるので

proc get_data(sset: sprase_set_o): int32 =
  return sset.data

sset.get_data

で呼び出せる。

さらに言うとNimは渡す引数が1つなら

sset.alloc 1
get_data sset

みたいな書き方もできる。

hasturhastur

Nimの関数呼び出しはかなりフリーダムなことになっておりキャメルケースとスネークケースを区別しないという仕様も有るので

sset.getData()
sset.getData
getData sset

sset.get_data()
sset.get_data
get_data sset

上記のコードは全て等価になる。

hasturhastur

途中から違う話になっていったがUFCS自体は

  • 元のtypeに手を加えなくてもメソッドを追加できる
  • メソッドチェインしやすい
  • ()のネストが少なくなる
    というメリットが有る

構造体と関数のみでOOPライクに書くには非常に便利な機能だと思う。

hasturhastur

関数ポインタ・高階関数・ラムダ

ジェネリクスとラムダが存在しないのでCだと便利に使うのが難しい機能郡。

import sugar

proc mul_10(x: int32): int32 =
  return x * 10

let mul_10_ptr1: (proc (x:int32): int32) = mul_10
let mul_10_ptr2: (int32) -> int32 = mul_10

echo mul_10(10)
echo mul_10_ptr1(10)
echo mul_10_ptr2(10)
# echo 10.mul_10_ptr1# これでも呼べる

let add_one = proc(val: int32): int32 = val + 1
let add_one_sugar = (x: int) => x + 1

echo 1.add_one
echo 1.add_one_sugar

proc pass1(x: int32, f: (proc (x:int32): int32)): int32 =
  return f(x)

proc pass2(x: int32, f: (int32) -> int32): int32 =
  return f(x)

echo 2.pass1(mul_10)
echo 2.pass1(mul_10_ptr1)
echo 2.pass1((x) => x * 10)
echo 2.pass2((x) => x * 10)

->=>sugarモジュールをインポートしないと使えない。

hasturhastur

procを使って記述する方法はやや冗長だが->=>を使うと簡潔に書ける。
sugarの中身がtemplateかmacroで書かれているのかはわからないが、Nimのメタプログラミング能力の高さが感じられる。

hasturhastur

ついでにジェネリクスも試してみる。

proc pass3[T](x: T, f: (T) -> T): T =
  return f(x)

echo 2.pass3((x) => x * 10)
echo 2.5.pass3((x) => x * 10.0)

素直に書けた。

hasturhastur

関数に副作用が無いことを保証する

Cにもconstは存在するが限定的にしか保証できない。

type
  child_t = object
    data: int32

  parent_o = object
    child: child_t

func no_sideeffect_function(parent: ref parent_o): int32 =
  parent.child.data = 99
  return parent.child.data

var parent = new parent_o
parent.child.data = 100

discard parent.no_sideeffect_function()

echo parent.child.data# 99

procではなくfuncで関数を宣言することで副作用が無いことを保証できる。
のだが上記のコードは普通にコンパイルできるし動く。
これはCの関数でポインタをconstで受け取った場合と同じような挙動。

hasturhastur

以前に少し触ったときに上記のコードが普通にコンパイルできてしまうのはかなり残念だなと思っていたので制限できるようになったのは非常に嬉しい。

hasturhastur

欲しい機能は存在して構文的に気になる部分もない。

調べていて思ったのは

  • 糖衣構文が多い
  • 記述の容易さを意識している

このあたりはZigとかなり違うところだなと感じる。

hasturhastur

とはいえ読みにくいわけでもないし、構造体と関数ベースの記述なので極端に複雑というわけでもないという印象。

hasturhastur

C++で欠如している部分を補えるか

  • モジュール管理
  • 公式のビルドシステム
  • パッケージマネージャー

言語機能というよりは所謂エコシステム周り。

hasturhastur

モジュール

# Module A
type
  T1* = int  # Module A exports the type ``T1``
import B     # the compiler starts parsing B

proc main() =
  var i = p(3) # works because B has been parsed completely here

main()
# Module B
import A  # A is not parsed here! Only the already known symbols
          # of A are imported.

proc p*(x: A.T1): A.T1 =
  # this works because the compiler has already
  # added T1 to A's interface symbol table
  result = x + 1

https://nim-lang.org/docs/manual.html#modules

hasturhastur

ざっと読んだだけだがPythonのモジュールに似ている。
importするとモジュール名を無視してアクセスもできるのだが、個人的にソレは微妙なので

import strutils
echo replace("abc", "a", "z")

ではなく

from strutils import `%`# 一部をimport
from strutils import nil# strutils.でのアクセスを強制できる

echo replace("abc", "a", "z")# compile error
echo strutils.replace("abc", "a", "z")

のようにしてimportする機能を明示するかモジュール名の記述を強制したい。
ただし標準ライブラリはフルで記述すれば呼び出せるようなので上記コードのfrom strutils import nilに意味はない。

https://nim-lang.org/docs/manual.html#modules-from-import-statement

hasturhastur

ビルドシステム&パッケージマネージャー

nimbleというビルドシステム兼パッケージマネージャーがデフォルトで付いている。

https://github.com/nim-lang/nimble

hasturhastur

nimbleのビルド自体はNimScriptというNimに似たスクリプト言語で制御できる。

C++の乱立するビルドシステムの面倒な点はビルドシステムによって使用しているスクリプト言語が違っていちいち新しいスクリプトを覚える必要のあるところだと思っているので、使用している言語に近い言語(NimScriptはNimのサブセット)で記述できるのは良い。

https://nim-lang.org/docs/nims.html

hasturhastur

調べた感想

全体的な印象は良い。

過去に触ったときは

  • GCが面倒
  • funcの制限が微妙
  • Pythonっぽいと言うけどPythonっぽくはないよね(クラス周り)

が微妙だなと思っていたんだけど

  • 新しいGCが導入された
  • funcの制限を厳しくできるようになった
  • 以前に比べてクラス周りを僕が気にしなくなった

ということで気になっていた点が改善されている。

hasturhastur

「効率的で表現豊かで優雅」とか主観的すぎて、どんなプログラミング言語か分からなさすぎるので
調べてみての印象を詳しく書いてみる。

個人的にはNimもBetterCなプログラミング言語だと感じる。
「Cを修正して GC・関数型言語由来の機能・強力なマクロ を搭載した言語」というのが一番しっくり来る紹介文。
まあCを修正のくだりは僕がC/C++とかC系の経験が長いせいで他の言語を触っていたら他の言語が収まる部分なのかもしれない(C云々より手続き型言語という記述を追加したほうが適している気がする)

C++とかPythonらしくないのはOOPへのアプローチでクラスベースじゃなくて構造体をベースに関数の多重ディスパッチで対応しているあたりが、それっぽくないなと感じる。
今回はマクロについては調べてないが強力なマクロというのは所謂Lisp族が持っている同図像性があるマクロという意味。

Pythonっぽいのはインデントの構文とかモジュール周りくらいで言語機能的に何か強く影響されているようには感じなかった。
冗長に書かざる負えない部分をUFCSとか演算子オーバーロードとか関数呼び出しの糖衣構文とかで記述量を少なく自由に書けるところが、Pythonというか動的型付けの言語的な気軽に書ける印象を与えている(でもこれPythonというよりRubyっぽい気がする)

hasturhastur

あと調べていて思ったのは「D言語っぽいな」と。
言語機能がどうとか見た目が似ているとかではなくて

「C++のように柔軟で高速に、Javaのように堅牢に、Pythonのように手軽に」書ける

https://qiita.com/JJ1LIS/items/0c0117f2ac3c362b916d
https://wiki.dlang.org/Programming_in_D_for_Python_Programmers

みたいな所を意識しているのをNimにも感じて似ているなと。
(DもNimも真剣に使ったことはないので両方ちゃんと使ってみたら全然似ていない可能性はある)

hasturhastur

実際のところGCがどれくらい足枷になるかは謎。
仕組み的には軽量だし、ゲームは常にループしているので余裕のあるタイミングで定期的にGCを動作させれば大丈夫じゃないかなという気持ち。
GCのデメリットを極力少なくしてGCのメリットを受けられるのではないかと期待している。

hasturhastur

懸念点は

  • 関数の呼び出し方がフリーダムすぎでは
  • 省略できるところを何でも省略し過ぎでは(enumとかimportとか)
  • 開発規模・コミュニティの小ささ

上2つは、そこまで致命的だとは思ってないし、何なら気楽に書けるほうが好きなので個人的には好きなんだけど久々に見たときとかに何やってるか分からなくなりそうなのが怖い。あとは多人数で書くときとか。

開発規模とかコミュニティはマイナー言語故に致し方なし。小さくて良いことはほぼ無いと思うんだけど、大きくしたいと思ってすぐに大きく出来るものでもないからなぁ。
気になるけど気にしてもしょうがない問題ではある。

hasturhastur

結論としてゲーム開発には使える(んじゃないかな)

  • 低コストで制御可能なGC
  • GCでの容易なメモリ管理
  • 簡潔な記述
  • 豊富な言語機能
  • マクロによる強力なメタプログラミング

など魅力に感じる点は多かった。

hasturhastur

自分で使うかという話。
使いますね。

Nimで大規模な何かを作成するかは分からないがゲームのプロトタイプレベルのものはSDLとNimで作ってみたい。

まずは単純にNimで書く体験が良いかどうかを確認していく。
ソレが良ければマクロによるメタプログラミングを活かしていきたい(DSLの構築とか)

hasturhastur

ゲームボーイアドバンスという特殊な環境ではあるものの、ガチ目なゲームでNimが使われているのでNimでもゲーム開発はイケる!(個人レベルの小規模開発だけど)
https://forum.nim-lang.org/t/8375

hasturhastur

正直ゲーム開発うんぬんよりもゲームボーイアドバンスで動くソフトをNimで書けることに驚き。Cに変換できるから技術的には無理じゃないんだろうけど、それでも実例が出てくるとビックリする。

hasturhastur

ちなみにGCは切って開発している模様。GBAだとリソース的にGCを回す余裕は無いだろうし手動管理以外の選択肢は無かったんだろう(開発スタート時にはArcは実装されていなかったとのこと)

このスクラップは2021/06/28にクローズされました