Anteru's blog
  • Consulting
  • Research
    • Assisted environment probe placement
    • Assisted texture assignment
    • Edge-Friend: Fast and Deterministic Catmull-Clark Subdivision Surfaces
    • Error Metrics for Smart Image Refinement
    • High-Quality Shadows for Streaming Terrain Rendering
    • Hybrid Sample-based Surface Rendering
    • Interactive rendering of Giga-Particle Fluid Simulations
    • Quantitative Analysis of Voxel Raytracing Acceleration Structures
    • Real-time Hybrid Hair Rendering
    • Real-Time Procedural Generation with GPU Work Graphs
    • Scalable rendering for very large meshes
    • Spatiotemporal Variance-Guided Filtering for Motion Blur
    • Subpixel Reconstruction Antialiasing
    • Tiled light trees
    • Towards Practical Meshlet Compression
  • About
  • Archive

Gameplay scripting

July 11, 2006
  • Programming
approximately 3 minutes to read

For my engine, I want to use a scripting language. The primary purpose for employing a scripting language is to simplify the development of gameplay code and leverage the power of multi-core systems. I’ve been lately a bit more serious about it (and yes, the packages are still in development, good stuff takes time) and did some more research. Read on for design goals, parser generators and general rambling on programming languages. By the way, this is the 100th entry in my blog :) Hooray!

Design goals

The scripting language should first and foremost allow designers and artists to write gameplay code, not more, not less. It does not have to be a fully features scripting language like Python. The focus is on event-driven, mostly parallel gameplay code. This brings several requirements with it:

  • Event-driven code has to be supported natively. Basically, there has to be some keyword for events as everything in niven is going to be event-driven.
  • Parallel execution. The resulting code should run well on a parallel ScriptVM.

Of course, if most of your code is event-driven, parallel execution comes for free as long as your objects are independent of each other. The most simple solution is to stack up all events for an object and then process the objects in parallel. This is completely transparent to the user and does not even require special language support. This means also that event raising will be asynchronous, but that seems rather logical to me. More design goals will probably emerge as soon as I get a prototype working, but these are the primary ones for now.

Syntax

A word on syntax, what kind of syntax do you prefer given the following choices?

  • C with function function int a (int b) { ... }
  • C, trailing types function a (b: int) : int { ... }

or a slight variation, with a more math-inspired syntax:

function (a:int, b:int) -> int { ... }

  • A really verbose variant: function name:a params:{a:int, b:int} return:int { ... }

More verbose, but very friendly to annotations. Imagine if you could add other “attributes” to a function like

function name:qsort implements:sort-algorithm complexity:nlogn ...

and get it via sort-algorithm.get("complexity","nlogn")?

A note on the last one: I have some idea of something I call a “Rich metadata engine” which would allow adding attributes to each parameter, variable or function. For example, const and reference would become attributes and passed over to the compiler. This would allow compilers to support new attributes (say, checks like [>10]) which could lead to better optimization and better compile-time checks. Image the example

function a (
        q:array [const,ref],
        i:int [const,=size(q)])

Parser generators

To make all this stuff happen, I have to write a powerful script VM and a good compiler, and both tasks are not simple.

Pyl

For the compiler, I was playing around with the idea to write it in Python, but unfortunately parser generators for Python are not very powerful (the best I’ve found was Pyl, a yacc port to Python) and transforming ASTs in Python is painful because Python does not know call-by-reference. Anyway, for quick prototyping it’s really good, I managed to get a compiler with constant-folding up & running in 2 hours for a simple test “language” (which understood assignment to variables and basic maths).

Boost.Spirit

Next try was Boost.Spirit, truly impressive, took me also around 2 hours to get a sample running (this time not written from scratch though) and write a simple compiler. Spirit is probably better suited for simpler parsing tasks, although I’ve seen some impressive parsers written with it. I wouldn’t do it because although there is something beautiful about embedding EBNF in C++ I still see the advantage of writing real EBNF and transforming to ugly C++ ;) which leads me to the last tool I’ve tried, ANTLR.

ANTLR

It comes with a truly amazing IDE (yeah, you read right. It really has an IDE for writing parsers), it supports LL(*) grammars (no left-recursion though, but the IDE has a built-in function to refactor left-recursive rules) and it generates ASTs if you want it to. Unfortunately, the current version is under heavy development and not finished yet, meaning I didn’t have the chance to play with the C++ AST yet. Anyway, I’ve been able to implement a rather large grammar already in no time, and I’ll stick with ANTLR until something better pops up which is kinda unlikely.

Previous post
Next post

Recent posts

  • Data formats: Why CSV and JSON aren't the best
    Posted on 2024-12-29
  • Replacing cron with systemd-timers
    Posted on 2024-04-21
  • Open Source Maintenance
    Posted on 2024-04-02
  • Angular, Caddy, Gunicorn and Django
    Posted on 2023-10-21
  • Effective meetings
    Posted on 2022-09-12
  • Older posts

Find me on the web

  • GitHub
  • GPU database
  • Projects

Follow me

Anteru NIV_Anteru
Contents © 2005-2025
Anteru
Imprint/Impressum
Privacy policy/Datenschutz
Made with Liara
Last updated February 03, 2019