Les raccourcis d'écriture

Écrire moins : comment les commandes sont nommées, les blocs WITH sur les objets du moteur, la notation par point, l'interpolation $"…" et les graphies PureBasic que le compilateur accepte.

En un mot#

L'identifiant d'un sprite est le premier argument de chaque commande de sprite, et son nom figure dans chaque nom de commande. Les raccourcis de cette page suppriment cette répétition. Aucun n'est une nouvelle API : le compilateur réécrit chacun en BASIC ordinaire (appels du catalogue, concaténations avec Str(), FUNCTION, TYPE) avant de compiler le programme.

RaccourciVous écrivezLe compilateur voit
WITH <famille>\position 100, 200SpritePosition(s, 100, 200)
notation par points.position(100, 200)SpritePosition(s, 100, 200)
interpolation$"score: {points}"("score: " + Str(points))
mots PureBasicProcedure … EndProcedureFUNCTION … ENDFUNCTION

Comment les commandes sont nommées#

Les deux raccourcis de bloc reconstruisent des noms de commande ; ils reposent donc sur une règle.

L'objet d'abord, la propriété ensuite, ni Set ni Get. SpritePosition, SpriteX, MusicVolume, CameraPosition.

Sorte de commandeGraphieExemple
une propriété qu'on écrit<Objet><Propriété>(id, valeur…)SpritePosition(s, 10, 20)
une propriété qu'on lit<Objet><Propriété>(id)SpriteWidth(s)
les deux, sous un seul nomle nombre d'arguments trancheSpriteX(s) lit, SpriteX(s, 10) écrit
quelque chose naît ou disparaîtle verbe reste en têteCreateSprite, LoadImage, CloneSprite, DeleteSprite

C'est la convention de PureBasic, pas celle d'AGK. AGK écrit SetSpritePosition et GetSpriteX ; ici, ce sont deux commandes inconnues, donc une erreur de compilation. Retirez le Set ou le Get.

Les exceptions, comptées dans le catalogue :

  • Aucune commande ne commence par Get.
  • 4 commandes commencent par Set, parce que le nom nu est déjà une autre commande avec le même nombre d'arguments, que le compte ne peut donc pas départager : SetRandomSeed (à côté de RandomSeed), SetMemblockString (à côté de MemblockString, qui lit), SetCloudDataVariable et SetTerrainBiome.
  • 29 commandes finissent par Set (sans compter LSet et RSet, qui complètent des chaînes). 18 sont les écritures de Text : TextPositionSet, TextStringSet, TextXSet, TextSizeSet, TextVisibleSet et treize autres, tandis que TextX, TextString et TextSize lisent. Les onze autres sont SoundInstanceVolumeSet, SoundInstanceRateSet, MusicOggLoopCountSet, FilePositionSet, HttpHostSet, XmlNodeNameSet, XmlNodeTextSet, XmlAttributeSet, AtmosphereSet, WeatherSet et DayNightSet.

En cas de doute, la référence des commandes est engendrée depuis le catalogue et a sa propre recherche.

Les blocs WITH <famille>#

WITH <FAMILLE> <expression>
  \<propriété> [<arguments>]     ' en tête d'instruction : écrit
  … \<propriété> …               ' partout ailleurs : lit
ENDWITH                          ' ou 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

La place décide du sens. En tête d'instruction, \prop écrit, et ce qui le suit sur la ligne sont les arguments, sans parenthèses. Partout ailleurs, \prop lit. Il n'y a pas de seconde notation à retenir.

  • Le nom est reconstruit, puis cherché. WITH SPRITE plus \position donne SpritePosition. Une propriété que le catalogue n'a pas est une erreur de compilation sur-le-champ : Unknown property in WITH SPRITE: \positon.
  • L'expression peut être quelconque : WITH SPRITE ships[3], WITH CAMERA 1.
  • WITH hero, en un seul mot, reste le bloc d'enregistrement décrit dans Enregistrements et méthodes. Deux mots, dont le premier est une famille, sélectionnent le catalogue.
  • Les lignes qui ne commencent pas par \ sont des instructions ordinaires.
  • Pas d'imbrication. Un WITH dans un WITH est refusé, enregistrement ou famille.

⚠️ Une écriture ne prend pas de =. Écrivez \x 50. La ligne \x = 50 n'est pas refusée, mais le programme qu'elle produit ne démarre pas.

⚠️ Une écriture doit commencer l'instruction. Dans IF … THEN \visible 1, le \visible n'est pas en tête : il est pris pour une lecture, et le programme ne démarre pas. Utilisez un IF en bloc :

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         ' refusé : la famille n'a pas cette propriété
ENDWITH

Quelles familles existent#

Une famille n'est pas une liste figée. Tout mot par lequel commencent des noms de commande du catalogue est une famille : Sprite à cause de SpriteX, Camera à cause de CameraPosition. Celles qui servent le plus :

FamilleQuelques propriétésUne variable la reçoit de
SPRITEposition, x, y, size, angle, depth, visible, colorCreateSprite, LoadSprite, CloneSprite
TEXTlecture : x, string, size ; écriture : positionSet, stringSet, sizeSetCreateText
OBJECT (3D)position, positionY, rotationLoadObject, ou AS Object
CAMERAposition, rotation, xAS Camera
PARTICLESposition, frequency, life, sizeCreateParticles
IMAGEwidth, heightLoadImage
MUSICvolumeLoadMusic
EDITBOXposition, textCreateEditBox
MEMBLOCKsize, byte, int, floatCreateMemblock
DICTIONARYsize, hasCreateDictionary
VIRTUALBUTTON, VIRTUALJOYSTICKposition, sizeAS VirtualButton, AS VirtualJoystick
COMPUTEBUFFER, COMPUTESHADERsize, type, bufferCreateComputeBuffer, CreateComputeShader

⚠️ Quelques familles sont globales, pas par objet : VideoPlaying() et VideoWidth() ne prennent pas d'identifiant, donc une variable de vidéo n'a rien à mettre devant le point.

Dans l'éditeur, taper \ dans un bloc WITH <famille> complète les propriétés de cette famille.

La notation par point#

WITH supprime la répétition dans un bloc. La notation par point la supprime partout : la famille est attachée à la variable.

<var> = Create<Famille>(…)       ' ou Load<Famille>(…), Clone<Famille>(…)
<var> AS <Famille>               ' quand l'identifiant vient d'ailleurs

<var>.<propriété>(<arguments>)   ' un appel : écrit, ou lit avec des arguments
<var>.<propriété> = <valeur>     ' une écriture, en instruction entière
… <var>.<propriété> …            ' une lecture
img = LoadImage("hero.png")
s = CreateSprite(img)              ' s appartient désormais à la famille Sprite
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
  • La famille est déduite de Create<Famille>(, Load<Famille>( ou Clone<Famille>( quand c'est tout le membre droit d'une affectation, ou prise dans une déclaration AS <Famille>.
  • Les lectures dans les arguments d'un appel sont réécrites aussi : s.position(s.x + 1, s.y) devient SpritePosition(s, SpriteX(s) + 1, SpriteY(s)).
  • La famille suit le nom de la variable dans tout le programme, corps de FUNCTION compris.
  • Une propriété inconnue est une erreur de compilation : Unknown property on sprite: .positon.
  • Des propriétés seulement. s.delete() est refusé : écrivez DeleteSprite(s).
RéécritLaissé tel quel
une variable dont la famille est connueune variable sans famille
s.x, s.x(10), s.visible = 1un champ d'enregistrement : hero.health
les lectures imbriquées dans les argumentsune méthode de liste ou de dictionnaire : scores.insert(1)
SELF.champ dans une METHOD
l'intérieur d'une chaîne : "sprite.png"

⚠️ L'écriture avec = recopie son membre droit tel quel. Dans s.angle = s.angle + 5, le s.angle de droite n'est pas réécrit, et le sprite reçoit quelque chose qui n'est pas un nombre. Aucune erreur ne s'affiche. Utilisez la forme d'appel, qui réécrit ses arguments :

s = CreateSprite(0)
s.angle(s.angle + 5)               ' SpriteAngle(s, SpriteAngle(s) + 5)
IF s.visible = 0 THEN s.visible(1) ' la forme d'appel marche aussi sur une ligne

⚠️ L'écriture avec = doit être une instruction entière. Après le THEN d'un IF sur une ligne, utilisez la forme d'appel, comme ci-dessus.

Quand la famille n'est pas déduite#

Certaines commandes de création portent plus que la famille dans leur nom : CreateObjectBox, CreatePointLight. Elles ne donnent aucune famille à la variable. Déclarez-la d'abord :

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 fait pire : elle donne à la variable une mauvaise famille (objectsphere), qui l'emporte sur AS Object. Utilisez la forme qui prend un identifiant :

ball AS Object
ball = 1
CreateObjectSphere(ball, 1, 16, 16)
ball.position(0, 1, 0)             ' ObjectPosition(ball, 0, 1, 0)

Un paramètre typé n'est pas reconnu#

' refusé : Unknown BASIC parameter type: Sprite
FUNCTION Nudge(s AS Sprite)
  s.x(s.x + 1)
ENDFUNCTION

Une famille n'est pas un type de paramètre. Prenez le paramètre sans type, et déclarez sa famille à la première ligne du corps :

FUNCTION Nudge(spr)
  spr AS Sprite
  spr.x(spr.x + 1)                 ' SpriteX(spr, SpriteX(spr) + 1)
ENDFUNCTION

hero = CreateSprite(0)
Nudge(hero)

L'interpolation $"…"#

$"<texte>{<expression>}<texte>…"
{{  et  }}                       ' une accolade { et } littérale
points = 1250
name$ = "Ada"
lives = 3
Console($"{name$} a {lives} vies")             ' Ada a 3 vies
Console($"vie suivante à {points + 750}")      ' vie suivante à 2000
Console($"{{accolades}} et {Len(name$)}")      ' {accolades} et 3

Chaque trou devient Str(expression), et la chaîne devient une concaténation. Str rend une chaîne inchangée : un trou accepte donc un nombre ou une chaîne.

Le $ est obligatoire, et ce n'est pas de la décoration. Beaucoup de commandes de monde prennent du JSON écrit à la main, où les accolades sont des données :

CreateTerrain("island", "{\"preset\":\"island\",\"size\":256}")

Une chaîne sans $ est laissée intacte, quoi qu'elle contienne. Le $ doit toucher le guillemet ouvrant.

  • Un trou s'arrête à la première }. Il ne peut pas contenir de chaîne littérale : un " dans un trou termine toute la chaîne, et le compilateur signale une { non fermée. Mettez d'abord le texte dans une variable.
  • Un trou peut contenir un appel : {Upper(name$)}, {Str(speed#, 2)}.
  • Un trou vide {} ou une { non fermée est une erreur de compilation. Une } seule, hors d'un trou, est gardée telle quelle.
  • ⚠️ Une variable AS float affiche tous les chiffres de sa valeur sur 32 bits : 0.1 s'affiche 0.10000000149011612. Arrondissez dans le trou : {Str(f, 2)}.
n = 1
Console($"oups {n")              ' refusé : Unclosed { in interpolated string

AGK, DarkBASIC et PureBasic n'ont pas d'interpolation : ils recollent avec + et Str(). La graphie $"…" est celle de C#.

Les graphies PureBasic#

Les mots-clés sont insensibles à la casse, si bien que beaucoup de lignes PureBasic se lisent déjà comme du BASIC : Global, If … ElseIf … EndIf, Repeat … Until, Select … EndSelect, With … EndWith, ForEach, NewList v.i() (voir Tableaux, listes et dictionnaires) et le raccourci \champ.

Les mots ci-dessous sont acceptés aussi. Chacun est un alias : il est réécrit dans la construction que le compilateur connaît déjà, donc les deux graphies se comportent toujours pareil.

PureBasicÉquivaut àRemarques
Procedure nom(…) … EndProcedureFUNCTION … ENDFUNCTIONun type de retour s'écrit AS <type> après les parenthèses
ProcedureReturn [valeur]EXITFUNCTION [valeur]
Structure Nom … EndStructureTYPE … ENDTYPEaussi sur une ligne : Structure Vec x y EndStructure
WendENDWHILE
Define, ProtectedLOCALà utiliser dans une Procedure
Debug(valeur)Console(valeur)avec parenthèses
x.f = 1.5x AS float puis x = 1.5le suffixe de type sur une variable, voir plus bas
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

Les alias ne jouent qu'en tête d'instruction. structure = 3 reste une variable nommée structure, et Print("Procedure") affiche le mot.

Le suffixe de type#

Le premier nom.<suffixe> déclare la variable avec ce type. Ensuite, écrivez le nom nu. Les types eux-mêmes sont décrits dans Variables et types.

SuffixeTypeSuffixeType
.bint8.auint8
.wint16.u, .cuint16
.l, .iint32.qint64
.ffloat32.dfloat64
.sstring

⚠️ .i fait 32 bits ici. Dans PureBasic 64 bits, il en fait 64. Utilisez .q pour 64 bits.

Ce qui diffère de PureBasic#

PureBasicDans aukimi
Procedure.i Twice(n)refusé : écrivez Procedure Twice(n) AS integer
EndProcedure valeurrefusé : ProcedureReturn valeur, puis EndProcedure
x.f comme champ de Structurerefusé : écrivez x AS float
p.Point pour déclarer un enregistrementrefusé : écrivez p AS Point
Define.i a, brefusé : écrivez a.i = 0 et b.i = 0
n.i sur un paramètrenon typé : écrivez n AS integer
EnableExplicitOPTION EXPLICIT
Repeat … ForEverDO … LOOP, ou REPEAT … UNTIL
Default dans un SelectCASE DEFAULT. Un Default nu n'est pas reconnu : il n'est pas refusé non plus, et le Select se comporte mal à l'exécution.
Shared, Staticrefusés
; commentairece n'est pas un commentaire ici : écrivez '

⚠️ Au niveau supérieur du programme, préférez GLOBAL à Define. Une variable déclarée avec Define (ou LOCAL) hors de toute Procedure, puis réaffectée, produit pour l'instant un programme qui ne démarre pas. Dans une Procedure, Define et Protected ne posent aucun problème.

Ce qui n'est volontairement pas repris#

Deux habitudes de DarkBASIC et une fonctionnalité de PureBasic restent dehors, exprès.

Les commandes sans parenthèses. PRINT "hello", SYNC, Debug x : les parenthèses sont ce qui distingue une commande d'une variable, et ce qui permet au compilateur de repérer une commande mal orthographiée.

PRINT "hello"                    ' refusé : BASIC commands require parentheses

Les commandes en plusieurs mots. Le MAKE OBJECT CUBE 1, 10 ou le SYNC ON de DarkBASIC ne sont pas des instructions ici. Le catalogue écrit chaque commande en un seul mot : CreateObjectBox(1, 10, 10, 10).

MAKE OBJECT CUBE 1, 10           ' refusé : Not a BASIC statement

Macro … EndMacro. Une macro PureBasic colle du texte à l'endroit de l'appel. C'est un préprocesseur, pas un alias de mot-clé, et une moitié de macro ressemblerait à la vraie sans se comporter comme elle. Écrivez une FUNCTION, ou utilisez le préprocesseur pour le code conditionnel.

Macro Square(x)                  ' refusé
  x * x
EndMacro

Limites et pannes courantes#

  • SetSpritePosition et GetSpriteX n'existent pas. Retirez le Set et le Get.
  • Une écriture WITH ne prend pas de =, et commence son instruction.
  • Une écriture par point avec = ne réécrit pas son membre droit. Utilisez s.x(s.x + 1).
  • CreateObjectBox et consorts ne donnent pas de famille. Déclarez AS Object d'abord.
  • Une famille n'est pas un type de paramètre. Déclarez-la dans la fonction.
  • Le $ contre le guillemet, sinon pas d'interpolation. Pas de chaîne littérale dans un trou.
  • .i fait 32 bits.