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.
| Feature | Think of it as | A useful game example |
|---|---|---|
Enum | Named choices in one category | Title, Playing, GameOver |
Type | A value made from related fields | A hero's stats or a position |
Class | An object that several references can share | One menu or score board shared by callers |
Module | A named home for reusable declarations | Scoring 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
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.
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
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.
| Assignment | What is shared? |
|---|---|
Backup = Hero for a Type | Nothing mutable is aliased by the copy. Each value has its own fields. |
OtherView = Board for a Class | Both references reach the same object. |
| Class parameter passed ByVal | The reference still reaches the same object. Its fields can be changed through methods. |
| Class parameter passed ByRef | The 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 feature | Useful boundary |
|---|---|
| Type fields | Public; Type methods and properties may be Public or Private. |
| Class fields | Private by default; explicitly Public when intended. Methods and properties default to Public. |
| Class references | Scalar references only. No Class arrays, directly nested Class fields, or Class fields inside Types. |
| Class-owned data | Number, 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 features | No inheritance, interfaces, virtual dispatch, static Class members, indexed/default properties, or user-defined destructors in this feature set. |
| Modules | Imports must form a non-circular dependency graph. An application may use local modules without packaging them. |
| Multiple source files | The selected startup file owns top-level executable statements. Other ordinary support files provide declarations and routines. |
| Native versus Web | Native 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 →