SMILE 2.0Learn & build
Language guide

04 / Organize your game

Types, objects & modules

Keep related facts together and give reusable code a clear home. Learn the difference between copying a hero's stats and sharing one object.

Choose the smallest useful container

When your game grows, related values need to stay together. A hero has a name and health. A game has a title screen, a playing state, and a game-over state. Reusable scoring rules belong in their own module.

FeatureThink of it asA useful game example
EnumNamed choices in one categoryTitle, Playing, GameOver
TypeA value made from related fieldsA hero's stats or a position
ClassAn object that several references can shareOne menu or score board shared by callers
ModuleA named home for reusable declarationsScoring calculations or drawing helpers

You can build a small game with variables and routines first. Add these structures when they make a specific part easier to understand.

Enum gives game states meaningful names

Instead of remembering that 0 means “title” and 1 means “playing,” declare the choices. An enum is its own type, so an unrelated number cannot silently become a game state.

Enum AdventureState
    Title
    Playing
    GameOver
End Enum

Dim State As AdventureState

State = AdventureState.Playing

Select Case State
    Case AdventureState.Title
        Print "Press Start"
    Case AdventureState.Playing
        Print "Explore the world"
    Case AdventureState.GameOver
        Print "Try again"
End Select

Write the type and member together, such as AdventureState.Playing. Members begin at zero unless an explicit Number constant is supplied; following implicit members increase by one. Prefer a useful zero member because an unassigned enum defaults to its underlying zero value.

Enums support same-type = and <> comparisons and work in Select Case. Do not use them as ordinary numbers, do arithmetic on them, or pass the whole enum to Print. Print a friendly label from a matching case instead.

Type groups values and copies them

A field is a named value inside another value. Use a dot to reach it. Assigning one Type value to another copies its fields, including nested values, text, and fixed arrays.

Type HeroStats
    Name As Text
    Health As Number

    Public Sub Heal(Optional Amount As Number = 5)

        Me.Health = Min(100, Me.Health + Amount)

    End Sub
End Type

Dim Hero As HeroStats
Dim Backup As HeroStats

Hero.Name = "Nova"
Hero.Health = 80
Backup = Hero

Call Hero.Heal(Amount:=10)

Print Hero.Health
Print Backup.Health
HeroName: Nova · Health: 90
BackupName: Nova · Health: 80
After Backup = Hero, they hold independent values. Healing Hero does not change Backup.

Me means the Type value whose method was called. A method is a Sub or Function inside the Type. Call Hero.Heal() applies the default amount to Hero; Call Hero.Heal(Amount:=10) supplies ten.

A Type can contain other Types and fixed one- or two-dimensional arrays. For example, Waypoints[4] As Point2D inside an actor Type stores four positions. Fields have no declaration-time initializers; assign their starting values in your program.

Methods need a stable Type location.

A Type method or property works on a writable variable, array element, or field. If a Function returns a Type, first store it in a variable, then call its methods. Passing a Type ByVal copies it; passing it ByRef allows the routine to change the caller's value.

Class gives several names one shared object

A Class behaves differently from a Type. New creates an object. Assignment copies the reference to that object, so two variables can reach the same stored state. This complete fragment prints 15 and True.

Class ScoreBoard
    Private StoredScore As Number

    Public Sub New(Optional Start As Number = 0)

        Me.StoredScore = Start

    End Sub

    Public Sub Add(Points As Number)

        Me.StoredScore = Me.StoredScore + Points

    End Sub

    Public Property Total As Number

        Get

            Dim Result As Number

            Result = Me.StoredScore

            Return Result

        End Get

    End Property
End Class

Dim Board As New ScoreBoard(Start:=10)
Dim OtherView As ScoreBoard

OtherView = Board

Call OtherView.Add(5)

Print Board.Total
Print OtherView Is Board
BoardA reference
One ScoreBoardStoredScore: 15
OtherViewA reference
Changing the object through OtherView is visible through Board.

Sub New is the constructor: it runs when the object is created. The Total property has a getter, so callers can read it like a field. The score field is Private, so callers use Add to change it.

A Set accessor can make a property writable. Inside it, the contextual name Value holds the incoming value. For example, a health property could store Max(0, Value) to prevent negative health. A getter-only property cannot be assigned to.

AssignmentWhat is shared?
Backup = Hero for a TypeNothing mutable is aliased by the copy. Each value has its own fields.
OtherView = Board for a ClassBoth references reach the same object.
Class parameter passed ByValThe reference still reaches the same object. Its fields can be changed through methods.
Class parameter passed ByRefThe routine can also replace the caller's reference with a different object or Nothing.

Nothing means “no object here”

An uninitialized Class variable holds Nothing. Check references using Is or Is Not. The = and <> operators are not Class identity comparisons.

' Fragment using Board and OtherView from the previous example.
OtherView = Nothing

If Board Is Not Nothing Then
    Print Board.Total
End If

Assigning Nothing to OtherView releases that reference. Board still owns a reference to the object, so the object remains available. SMILE releases owned references as scopes end or references are replaced. Do not call a method or read a property through Nothing.

Keep ownership easy to follow: decide which part of your game creates a resource, which part updates it, and which part releases it. A library's Destroy method or resource handle may still need an explicit call; follow that library's contract rather than assuming every resource is a Class.

With saves repeated typing

When several neighboring instructions work with the same Type or Class, With gives leading-dot member names a shared target. This is a fragment after declaring and initializing Hero:

With Hero
    .Name = "Nova the Brave"
    .Health = 100
End With

The target is selected once on entry. For a Type, the block keeps its writable location. For a Class, it keeps the original object identity. Use it for a clear group of related edits, not just to wrap one line.

Module and Import give reusable code a home

Keep your program's main story short. Put reusable declarations in a module, export the useful ones with Public, then import that module with a short alias.

File 1: language-module.smile owns the scoring rule. Inside a module, Option Explicit comes immediately after the Module header and before imports or declarations.

Module Adventure.Scoring

Option Explicit

Public Const PointsPerGem = 10

Public Function GemPoints(Gems As Number) As Number

    Dim Result As Number

    Result = Gems * PointsPerGem

    Return Result

End Function

End Module

File 2: language-import.smile owns the executable flow.

' The startup file for the two-source module example.
Option Explicit

Import Adventure.Scoring As Scoring

Dim Score As Number

Score = Scoring.GemPoints(4)
Print "Four gems: "; Score
Print "One gem: "; Scoring.PointsPerGem

End Program

Scoring is a local alias, so Scoring.GemPoints(4) means “call GemPoints from the imported module.” An Import is local to the physical source file where it appears. Import does not download a library or add a source file to the project for you.

Download all three files into one folder: module source, startup source, and project file. Open the project in Visual Studio. Its source list includes both files. Expected output: Four gems: 40, then One gem: 10.

Compile the two files without a project

From the repository root, with the files in MyFirstSmile:

& ".\artifacts\compiler\smilec.exe" ".\MyFirstSmile\language-import.smile" --source ".\MyFirstSmile\language-module.smile" -o ".\MyFirstSmile\AdventureModules.exe"
& ".\MyFirstSmile\AdventureModules.exe"

Module declarations are Private by default. Export only the constants, routines, and types other files should use. An imported type needs its alias, such as Models.HeroStats. Modules cannot reach into a consumer's global variables; pass the state they need as arguments.

Build within the current language boundaries

Current featureUseful boundary
Type fieldsPublic; Type methods and properties may be Public or Private.
Class fieldsPrivate by default; explicitly Public when intended. Methods and properties default to Public.
Class referencesScalar references only. No Class arrays, directly nested Class fields, or Class fields inside Types.
Class-owned dataNumber, Double, Boolean, Text, Enum and Type fields, including fixed arrays of supported value types. Direct Image fields are not supported; a contained Type can own Image data.
Object-oriented featuresNo inheritance, interfaces, virtual dispatch, static Class members, indexed/default properties, or user-defined destructors in this feature set.
ModulesImports must form a non-circular dependency graph. An application may use local modules without packaging them.
Multiple source filesThe selected startup file owns top-level executable statements. Other ordinary support files provide declarations and routines.
Native versus WebNative support/module scalar Class initializers run before startup. Web adoption of that initialization order is still open. Initialize shared objects explicitly when targeting both.

The language's limitations are useful design information. For a party of several heroes, use a fixed array of Type values. Keep a single Class reference for a shared service when that is the behavior you actually want.

Your mission: predict the copy

Download the complete models example. After Backup = Hero, add Backup.Health = 20. What will the two health lines show after Hero heals?

Reveal the answer

Hero: 90 and Backup: 20. The values are independent. The later score board example still shares one Class object and prints 15.

Next: make a playable Star Collector game → or explore the library toolbox →

Sources & version notes

Checked against the local SMILE 2.0 source on September 25, 2026. Examples target the native Windows toolchain unless stated otherwise. The linked repository may continue to evolve.