Shorthands
Write less: how commands are named, WITH blocks on engine objects, dot notation, $"…" interpolation, and the PureBasic spellings the compiler accepts.
In one line#
A sprite's id is the first argument of every sprite command, and its name is
inside every command name. The shorthands on this page remove that repetition.
None of them is a new API: the compiler rewrites each one into ordinary
BASIC (catalogue calls, Str() concatenations, FUNCTION, TYPE) before it
compiles the program.
| Shorthand | You write | The compiler sees |
|---|---|---|
WITH <family> | \position 100, 200 | SpritePosition(s, 100, 200) |
| dot notation | s.position(100, 200) | SpritePosition(s, 100, 200) |
| interpolation | $"score: {points}" | ("score: " + Str(points)) |
| PureBasic words | Procedure … EndProcedure | FUNCTION … ENDFUNCTION |
How commands are named#
The two block shorthands rebuild command names, so they rely on one rule.
The object first, the property second, no
SetorGet.SpritePosition,SpriteX,MusicVolume,CameraPosition.
| Kind of command | Spelling | Example |
|---|---|---|
| a property you write | <Object><Property>(id, value…) | SpritePosition(s, 10, 20) |
| a property you read | <Object><Property>(id) | SpriteWidth(s) |
| both, on one name | the argument count decides | SpriteX(s) reads, SpriteX(s, 10) writes |
| something comes into being or ends | the verb stays first | CreateSprite, LoadImage, CloneSprite, DeleteSprite |
This is PureBasic's convention, not AGK's. AGK writes SetSpritePosition and
GetSpriteX; here both are unknown commands, and a compile error. Drop the
Set or the Get.
The exceptions, counted from the catalogue:
- No command starts with
Get. - 4 commands start with
Set, because the bare name is already another command with the same number of arguments, so the count cannot tell them apart:SetRandomSeed(next toRandomSeed),SetMemblockString(next toMemblockString, which reads),SetCloudDataVariableandSetTerrainBiome. - 29 commands end with
Set(not countingLSetandRSet, which pad strings). 18 are the Text writes:TextPositionSet,TextStringSet,TextXSet,TextSizeSet,TextVisibleSetand thirteen more, whileTextX,TextStringandTextSizeread. The other eleven areSoundInstanceVolumeSet,SoundInstanceRateSet,MusicOggLoopCountSet,FilePositionSet,HttpHostSet,XmlNodeNameSet,XmlNodeTextSet,XmlAttributeSet,AtmosphereSet,WeatherSetandDayNightSet.
When in doubt, the command reference is generated from the catalogue and has a search.
WITH <family> blocks#
WITH <FAMILY> <expression>
\<property> [<arguments>] ' at the head of a statement: writes
… \<property> … ' anywhere else: reads
ENDWITH ' or END WITH
img = LoadImage("hero.png")
s = CreateSprite(img)
WITH SPRITE s
\position 100, 200 ' SpritePosition(s, 100, 200)
\size 64, 64 ' SpriteSize(s, 64, 64)
\x 50 ' SpriteX(s, 50)
x# = \x ' SpriteX(s)
\depth 5 ' SpriteDepth(s, 5)
ENDWITH
Console(Str(x#)) ' 50
The place decides the meaning. At the head of a statement, \prop writes,
and what follows it on the line are the arguments, without parentheses.
Anywhere else, \prop reads. There is no second notation to learn.
- The name is rebuilt, then looked up.
WITH SPRITEplus\positiongivesSpritePosition. A property the catalogue does not have is a compile error on the spot:Unknown property in WITH SPRITE: \positon. - The expression can be anything:
WITH SPRITE ships[3],WITH CAMERA 1. WITH hero, a single word, is still the record block described in Records and methods. Two words, the first one a family, select the catalogue.- Lines that do not start with
\are ordinary statements. - No nesting. A
WITHinside aWITHis refused, record or family.
⚠️ A write has no =. Write \x 50. The line \x = 50 is not refused,
but the program it produces fails to start.
⚠️ A write must start the statement. In IF … THEN \visible 1, the
\visible is not at the head, so it is read as a read, and the program fails to
start. Use a block IF:
s = CreateSprite(0)
WITH SPRITE s
IF \visible = 0
\visible 1 ' SpriteVisible(s, 1)
ENDIF
ENDWITH
s = CreateSprite(0)
WITH SPRITE s
\positon 100, 200 ' refused: the family has no such property
ENDWITH
Which families exist#
A family is not a fixed list. Any word that starts catalogue command names
is a family: Sprite because of SpriteX, Camera because of
CameraPosition. The ones you will use most:
| Family | Some properties | A variable gets it from |
|---|---|---|
SPRITE | position, x, y, size, angle, depth, visible, color | CreateSprite, LoadSprite, CloneSprite |
TEXT | reads: x, string, size; writes: positionSet, stringSet, sizeSet | CreateText |
OBJECT (3D) | position, positionY, rotation | LoadObject, or AS Object |
CAMERA | position, rotation, x | AS Camera |
PARTICLES | position, frequency, life, size | CreateParticles |
IMAGE | width, height | LoadImage |
MUSIC | volume | LoadMusic |
EDITBOX | position, text | CreateEditBox |
MEMBLOCK | size, byte, int, float | CreateMemblock |
DICTIONARY | size, has | CreateDictionary |
VIRTUALBUTTON, VIRTUALJOYSTICK | position, size | AS VirtualButton, AS VirtualJoystick |
COMPUTEBUFFER, COMPUTESHADER | size, type, buffer | CreateComputeBuffer, CreateComputeShader |
⚠️ A few families are global, not per object: VideoPlaying() and
VideoWidth() take no id, so a video variable has nothing to put in front of
the dot.
In the editor, typing \ inside a WITH <family> block completes that
family's properties.
Dot notation#
WITH removes the repetition inside a block. Dot notation removes it
everywhere: the family is attached to the variable.
<var> = Create<Family>(…) ' or Load<Family>(…), Clone<Family>(…)
<var> AS <Family> ' when the id comes from elsewhere
<var>.<property>(<arguments>) ' a call: writes, or reads with arguments
<var>.<property> = <value> ' a write, as a whole statement
… <var>.<property> … ' a read
img = LoadImage("hero.png")
s = CreateSprite(img) ' s now belongs to the Sprite family
s.position(100, 200) ' SpritePosition(s, 100, 200)
s.visible = 1 ' SpriteVisible(s, 1)
s.x(s.x + 10) ' SpriteX(s, SpriteX(s) + 10)
Console(Str(s.x)) ' 110
- The family is deduced from
Create<Family>(,Load<Family>(orClone<Family>(when it is the whole right side of an assignment, or taken from anAS <Family>declaration. - Reads inside call arguments are rewritten too:
s.position(s.x + 1, s.y)becomesSpritePosition(s, SpriteX(s) + 1, SpriteY(s)). - The family follows the variable's name through the whole program,
FUNCTIONbodies included. - An unknown property is a compile error:
Unknown property on sprite: .positon. - Only properties.
s.delete()is refused: writeDeleteSprite(s).
| Rewritten | Left alone |
|---|---|
| a variable whose family is known | a variable with no family |
s.x, s.x(10), s.visible = 1 | a record field: hero.health |
| reads nested in call arguments | a list or map method: scores.insert(1) |
SELF.field inside a METHOD | |
the inside of a string: "sprite.png" |
⚠️ The = write copies its right side as written. In
s.angle = s.angle + 5, the s.angle on the right is not rewritten, and
the sprite receives something that is not a number. No error is shown. Use the
call form, which rewrites its arguments:
s = CreateSprite(0)
s.angle(s.angle + 5) ' SpriteAngle(s, SpriteAngle(s) + 5)
IF s.visible = 0 THEN s.visible(1) ' the call form also works on one line
⚠️ The = write must be a whole statement. After THEN on a one-line
IF, use the call form, as above.
When the family is not deduced#
Some creation commands carry more than the family in their name:
CreateObjectBox, CreatePointLight. They give the variable no family.
Declare it first:
box AS Object
box = CreateObjectBox(1, 1, 1)
box.position(0, 2, 0) ' ObjectPosition(box, 0, 2, 0)
Console(Str(box.positionY)) ' ObjectPositionY(box)
⚠️ CreateObjectSphere is worse: it gives the variable a wrong family
(objectsphere), which wins over AS Object. Use the form that takes an id:
ball AS Object
ball = 1
CreateObjectSphere(ball, 1, 16, 16)
ball.position(0, 1, 0) ' ObjectPosition(ball, 0, 1, 0)
A typed parameter is not recognised#
' refused: Unknown BASIC parameter type: Sprite
FUNCTION Nudge(s AS Sprite)
s.x(s.x + 1)
ENDFUNCTION
A family is not a parameter type. Take the parameter untyped and declare its family on the first line of the body:
FUNCTION Nudge(spr)
spr AS Sprite
spr.x(spr.x + 1) ' SpriteX(spr, SpriteX(spr) + 1)
ENDFUNCTION
hero = CreateSprite(0)
Nudge(hero)$"…" interpolation#
$"<text>{<expression>}<text>…"
{{ and }} ' a literal { and }
points = 1250
name$ = "Ada"
lives = 3
Console($"{name$} has {lives} lives") ' Ada has 3 lives
Console($"next life at {points + 750}") ' next life at 2000
Console($"{{braces}} and {Len(name$)}") ' {braces} and 3
Each hole becomes Str(expression), and the string becomes a concatenation.
Str gives a string back unchanged, so a hole takes a number or a string.
The
$is mandatory, and it is not decoration. Many world commands take JSON written by hand, where the braces are data:
CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")
A string without $ is left untouched, whatever it contains. The $ must
touch the opening quote.
- A hole ends at the first
}. It cannot contain a string literal: a"inside a hole ends the whole string, and the compiler reports an unclosed{. Put the text in a variable first. - A hole can hold a call:
{Upper(name$)},{Str(speed#, 2)}. - An empty hole
{}or an unclosed{is a compile error. A lone}outside a hole is kept as is. - ⚠️ An
AS floatvariable shows every digit of its 32-bit value:0.1prints as0.10000000149011612. Round it in the hole:{Str(f, 2)}.
n = 1
Console($"oops {n") ' refused: Unclosed { in interpolated string
AGK, DarkBASIC and PureBasic have no interpolation: they glue with + and
Str(). The $"…" spelling is C#'s.
PureBasic spellings#
Keywords are case-insensitive, so many PureBasic lines already read as BASIC:
Global, If … ElseIf … EndIf, Repeat … Until, Select …
EndSelect, With … EndWith, ForEach, NewList v.i() (see
Arrays, lists and maps) and the \field
shorthand.
The words below are accepted too. Each one is an alias: it is rewritten into the construct the compiler already knows, so both spellings always behave the same.
| PureBasic | Same as | Notes |
|---|---|---|
Procedure name(…) … EndProcedure | FUNCTION … ENDFUNCTION | a return type is AS <type> after the parentheses |
ProcedureReturn [value] | EXITFUNCTION [value] | |
Structure Name … EndStructure | TYPE … ENDTYPE | also on one line: Structure Vec x y EndStructure |
Wend | ENDWHILE | |
Define, Protected | LOCAL | use them inside a Procedure |
Debug(value) | Console(value) | with parentheses |
x.f = 1.5 | x AS float then x = 1.5 | the type suffix on a variable, see below |
Structure Vec
x AS float
y AS float
EndStructure
Procedure Twice(n AS integer) AS integer
Protected result = n * 2
ProcedureReturn result
EndProcedure
v AS Vec
v.x = 1.5
count.i = 0
While count < 3
count = count + 1
Wend
Debug(Str(Twice(count))) ' 6
The aliases work at the head of a statement only. structure = 3 is
still a variable named structure, and Print("Procedure") prints the word.
The type suffix#
The first name.<suffix> declares the variable with that type. After that,
write the bare name. The types themselves are described in
Variables and types.
| Suffix | Type | Suffix | Type |
|---|---|---|---|
.b | int8 | .a | uint8 |
.w | int16 | .u, .c | uint16 |
.l, .i | int32 | .q | int64 |
.f | float32 | .d | float64 |
.s | string |
⚠️ .i is 32 bits here. In 64-bit PureBasic it is 64 bits. Use .q for
64 bits.
What differs from PureBasic#
| PureBasic | In aukimi |
|---|---|
Procedure.i Twice(n) | refused: write Procedure Twice(n) AS integer |
EndProcedure value | refused: ProcedureReturn value, then EndProcedure |
x.f as a Structure field | refused: write x AS float |
p.Point to declare a record | refused: write p AS Point |
Define.i a, b | refused: write a.i = 0 and b.i = 0 |
n.i on a parameter | not typed: write n AS integer |
EnableExplicit | OPTION EXPLICIT |
Repeat … ForEver | DO … LOOP, or REPEAT … UNTIL |
Default in a Select | CASE DEFAULT. A bare Default is not recognised: it is not refused either, and the Select misbehaves at run time. |
Shared, Static | refused |
; comment | not a comment here: write ' |
⚠️ At the top level of the program, prefer GLOBAL to Define. A
variable declared with Define (or LOCAL) outside any Procedure, then
assigned again, currently produces a program that fails to start. Inside a
Procedure, Define and Protected are fine.
What is deliberately not adopted#
Two DarkBASIC habits and one PureBasic feature stay out, on purpose.
Commands without parentheses. PRINT "hello", SYNC, Debug x: the
parentheses are what tells a command from a variable, and what lets the
compiler catch a misspelt command.
PRINT "hello" ' refused: BASIC commands require parentheses
Commands of several words. DarkBASIC's MAKE OBJECT CUBE 1, 10 or
SYNC ON are not statements here. The catalogue spells each command as one
word: CreateObjectBox(1, 10, 10, 10).
MAKE OBJECT CUBE 1, 10 ' refused: Not a BASIC statement
Macro … EndMacro. A PureBasic macro pastes text at the call site. It is
a preprocessor, not a keyword alias, and half of it would look like the real
thing without behaving like it. Write a FUNCTION, or use the
preprocessor for conditional code.
Macro Square(x) ' refused
x * x
EndMacroLimits and common snags#
SetSpritePositionandGetSpriteXdo not exist. Drop theSetand theGet.- A
WITHwrite has no=, and starts its statement. - A dot write with
=does not rewrite its right side. Uses.x(s.x + 1). CreateObjectBoxand friends give no family. DeclareAS Objectfirst.- A family is not a parameter type. Declare it inside the function.
$before the quote, or no interpolation at all. No string literal inside a hole..iis 32 bits.