LoginLogin

How do/can you make objects in SmileBasic?

Root / Programming Questions / [.]

cakecatCreated:
For example, in python, you can say
class Apple:
    def __init__(self, color, x, y):
        self.color = color
        self.x = x
        self.y = y

    def beRainbow(self,color):
        self.color = 'Rainbow'

apple1 = Apple('Red',12,23)
apple2 = Apple('Green',14,23)
And then apple1.color is 'Red', and I can make apple1.color equal 'Rainbow' by doing apple1.beRainbow() Is there some sort of workaround to this? Do you have to use arrays or something? Thank you for any help, I really like being able to make programs on my 3ds, I am just having some difficulty with these concepts.

the best option to ease yourself in may be snail's struct library : https://smilebasicsource.com/page?pid=587

You can do that be doing something like this:
ACLS
RAINBOW=TRUE

@MAINLOOP
WAIT 10
GCLS
IF RAINBOW==FALSE THEB GPUTCHR 0,0,"APPLE",RGB(255,0,0) ELSE GPUTCHR 0,0,"APPLE",RGB(RND(255),RND(255),RND(255)
GOTO @MAINLOOP
By setting the rainbow boolean to be false it is going to display the text in red. If there is anything you don't understand feel free to ask I am probaly not avaible for the time being but I will try to answer as soon as possible. Ps. I recomend you to watch petiteprofessor smilebasic tutorials on youtube they are very helpfull to people new to the progamming language.

Okay, so I did some experimenting, and I found a workaround, but it's quite messy I think, hopefully I'll be able to get better at using arrays like this by looking more at the sample programs. So I wanted 10 apples to fall to the ground, each with their own x and y values, so I was able to do this
SIZE = 10 'Number of apples
DIM AX[SIZE], AY[SIZE] 'Arrays for the apple coords
so now that there are variables for each apple. Then I can use a for loop to check them all
FOR I=0 TO SIZE-1
  INC AY[I]
  IF AY[I]<=28 THEN 'If within y limit of screen
    LOCATE AX[I],AY[I]
    PRINT("A")
  ELSE
    resetApple(I) 'At random time puts apple I back to top at random X position
  ENDIF
NEXT
Hopefully I'll be able to get a better grip of this kind of organization, it just seems clunky, but it somewhat resembles how EX7ALIEN is able to manage the enemy invaders without using objects.

You can't make real objects in SB; it isn't a language feature. Using arrays to be like "pools" for object data isn't a bad idea. If you're working with sprites, they actually do have their own features that you can apply to be really similar to "objects" but it depends on what exactly you're doing. The sample programs areโ€ฆ okay. They aren't exactly shining examples of what SB is truly capable of (or even how to write good SB code) since many of them are PTC conversions (and others were slightly botched by localization and updates.)

the best option to ease yourself in may be snail's struct library : https://smilebasicsource.com/page?pid=587
This is a decent approach if you absolutely can't live without something like an object, but they aren't "real" and they aren't very efficient, either. I haven't maintained this library in a while.

You can't make real objects in SB; it isn't a language feature. Using arrays to be like "pools" for object data isn't a bad idea. If you're working with sprites, they actually do have their own features that you can apply to be really similar to "objects" but it depends on what exactly you're doing. The sample programs areโ€ฆ okay. They aren't exactly shining examples of what SB is truly capable of (or even how to write good SB code) since many of them are PTC conversions (and others were slightly botched by localization and updates.)
I know you can't make real objects, but there has to be a way around that, right? People can still make games and stuff, I just don't get what they are doing to work around this incapability. If you wanted multiple entities in a dungeon crawler, how would you manage every entity's X and Y position and HP for example? In any other language I would use a class, but what is the convention for SB? (Sorry for all the questions x.x)

I know you can't make real objects, but there has to be a way around that, right? People can still make games and stuff, I just don't get what they are doing to work around this incapability. If you wanted multiple entities in a dungeon crawler, how would you manage every entity's X and Y position and HP for example? In any other language I would use a class, but what is the convention for SB? (Sorry for all the questions x.x)
An array for entity HPs, an array for entity X positions, and an array for entity Y positions would be the simplest way to do it.

I know you can't make real objects, but there has to be a way around that, right? People can still make games and stuff, I just don't get what they are doing to work around this incapability. If you wanted multiple entities in a dungeon crawler, how would you manage every entity's X and Y position and HP for example? In any other language I would use a class, but what is the convention for SB? (Sorry for all the questions x.x)
An array for entity HPs, an array for entity X positions, and an array for entity Y positions would be the simplest way to do it.
You could do what iโ€™m doing and store it in one string array, and read the data listed above from each string. Much more space efficient.

I know you can't make real objects, but there has to be a way around that, right? People can still make games and stuff, I just don't get what they are doing to work around this incapability. If you wanted multiple entities in a dungeon crawler, how would you manage every entity's X and Y position and HP for example? In any other language I would use a class, but what is the convention for SB? (Sorry for all the questions x.x)
An array for entity HPs, an array for entity X positions, and an array for entity Y positions would be the simplest way to do it.
You could do what iโ€™m doing and store it in one string array, and read the data listed above from each string. Much more space efficient.
But why?

I know you can't make real objects, but there has to be a way around that, right? People can still make games and stuff, I just don't get what they are doing to work around this incapability. If you wanted multiple entities in a dungeon crawler, how would you manage every entity's X and Y position and HP for example? In any other language I would use a class, but what is the convention for SB? (Sorry for all the questions x.x)
An array for entity HPs, an array for entity X positions, and an array for entity Y positions would be the simplest way to do it.
Okay, thank you!

If you want a much cleaner way to do it, you could setup your own object-like system like this:
VAR MAX_ENTITIES=100
DIM ENTITIES[4, MAX_ENTITIES]

'Add a new entity
DEF NEW_ENTITY(X, Y, HP)
  'Search for empty space to store an entity
  VAR I
  FOR I=0 TO MAX_ENTITIES-1
    IF ENTITIES[0, I]==FALSE THEN
      BREAK
    ENDIF
  NEXT
  'If no empty space was found, return -1
  IF I==MAX_ENTITIES THEN
    RETURN -1
  ENDIF
  'Store the entity and return its address.
  ENTITIES[0, I]=TRUE
  ENTITIES[1, I]=X
  ENTITIES[2, I]=Y
  ENTITIES[3, I]=HP
  RETURN I
END

'Destroy an entity
DEF DESTROY_ENTITY E
  ENTITIES[0, E]=FALSE
END

'Accessors
DEF GET_X(E)
  RETURN ENTITIES[1, E]
END
DEF GET_Y(E)
  RETURN ENTITIES[2, E]
END
DEF GET_HP(E)
  RETURN ENTITIES[3, E]
END

'Mutators
DEF SET_X E, X
  ENTITIES[1, E]=X
END
DEF SET_Y E, Y
  ENTITIES[2, E]=Y
END
DEF SET_HP E, HP
  ENTITIES[3, E]=HP
END

'Main code
VAR PLAYER=NEW_ENTITY(25,25,100)
PRINT "Player stats..."
PRINT "   X: " + STR$(GET_X(PLAYER))
PRINT "   Y: " + STR$(GET_Y(PLAYER))
PRINT "  HP: " + STR$(GET_HP(PLAYER))
Here we can create an entity called "PLAYER" with the X and Y values 25, 25, and HP 100. We can get these values with our accessors. Such as "GET_HP(PLAYER)" will return the player's HP, and we can change them with our mutators, such as "SET_Y PLAYER, 5" will change the player's Y value to 5. We can make as many entities as we want up until MAX_ENTITIES. There's a fourth property for each entity here, that is the "alive" property. A created entity has that value initially set to true, and it is always true until the entity is destroyed. When you go to create an entity, it searches for the first slot where a destroyed entity is located by checking their "alive" property, and it stores the entity there. This will be fast enough for most purposes, but if you're dealing with thousands of entities, creating entities this way can be pretty slow. A much quicker way to do it would be to keep a "journal".
VAR MAX_ENTITIES=100
DIM ENTITIES[4, MAX_ENTITIES]
VAR ENTITIES_SIZE=0
VAR JOURNAL[.]

'Add a new entity
DEF NEW_ENTITY(X, Y, HP)
  VAR ID
  IF LEN(JOURNAL)>0 THEN
    ID=POP(JOURNAL)
  ELSEIF ENTITIES_SIZE!=MAX_ENTITIES THEN
    ID=ENTITIES_SIZE
    INC ENTITIES_SIZE
  ELSE
    RETURN -1
  ENDIF

  'Store the entity and return its address.
  ENTITIES[0, ID]=TRUE
  ENTITIES[1, ID]=X
  ENTITIES[2, ID]=Y
  ENTITIES[3, ID]=HP
  RETURN ID
END

'Destroy an entity
DEF DESTROY_ENTITY E
  'Store the entity and return its address.
  ENTITIES[0, E]=FALSE
  IF E==ENTITIES_SIZE-1 THEN
    DEC ENTITIES_SIZE
  ELSE
    PUSH JOURNAL, E
  ENDIF
END
This journal basically logs every time an entity is destroyed, so when you go to create a new entity, you can immediately know where to put it just by looking at the journal. Creating entities the first way is linear time O(n) since it has to search potentially the entire dimension of the array while keeping a journal makes it constant time O(1) since there is no searching, you can just look at the journal and immediately know where to store the next entity.

I know you can't make real objects, but there has to be a way around that, right? People can still make games and stuff, I just don't get what they are doing to work around this incapability. If you wanted multiple entities in a dungeon crawler, how would you manage every entity's X and Y position and HP for example? In any other language I would use a class, but what is the convention for SB? (Sorry for all the questions x.x)
An array for entity HPs, an array for entity X positions, and an array for entity Y positions would be the simplest way to do it.
You could do what iโ€™m doing and store it in one string array, and read the data listed above from each string. Much more space efficient.
But why?
It was really easy, i got learn more about strings, and i could hold all the needed data in one array.

If you want a much cleaner way to do it, you could setup your own object-like system like this:
VAR MAX_ENTITIES=100
DIM ENTITIES[4, MAX_ENTITIES]

'Add a new entity
DEF NEW_ENTITY(X, Y, HP)
  'Search for empty space to store an entity
  VAR I
  FOR I=0 TO MAX_ENTITIES-1
    IF ENTITIES[0, I]==FALSE THEN
      BREAK
    ENDIF
  NEXT
  'If no empty space was found, return -1
  IF I==MAX_ENTITIES THEN
    RETURN -1
  ENDIF
  'Store the entity and return its address.
  ENTITIES[0, I]=TRUE
  ENTITIES[1, I]=X
  ENTITIES[2, I]=Y
  ENTITIES[3, I]=HP
  RETURN I
END

'Destroy an entity
DEF DESTROY_ENTITY E
  ENTITIES[0, E]=FALSE
END

'Accessors
DEF GET_X(E)
  RETURN ENTITIES[1, E]
END
DEF GET_Y(E)
  RETURN ENTITIES[2, E]
END
DEF GET_HP(E)
  RETURN ENTITIES[3, E]
END

'Mutators
DEF SET_X E, X
  ENTITIES[1, E]=X
END
DEF SET_Y E, Y
  ENTITIES[2, E]=Y
END
DEF SET_HP E, HP
  ENTITIES[3, E]=HP
END

'Main code
VAR PLAYER=NEW_ENTITY(25,25,100)
PRINT "Player stats..."
PRINT "   X: " + STR$(GET_X(PLAYER))
PRINT "   Y: " + STR$(GET_Y(PLAYER))
PRINT "  HP: " + STR$(GET_HP(PLAYER))
Here we can create an entity called "PLAYER" with the X and Y values 25, 25, and HP 100. We can get these values with our accessors. Such as "GET_HP(PLAYER)" will return the player's HP, and we can change them with our mutators, such as "SET_Y PLAYER, 5" will change the player's Y value to 5. We can make as many entities as we want up until MAX_ENTITIES. There's a fourth property for each entity here, that is the "alive" property. A created entity has that value initially set to true, and it is always true until the entity is destroyed. When you go to create an entity, it searches for the first slot where a destroyed entity is located by checking their "alive" property, and it stores the entity there. This will be fast enough for most purposes, but if you're dealing with thousands of entities, creating entities this way can be pretty slow. A much quicker way to do it would be to keep a "journal".
VAR MAX_ENTITIES=100
DIM ENTITIES[4, MAX_ENTITIES]
VAR ENTITIES_SIZE=0
VAR JOURNAL[.]

'Add a new entity
DEF NEW_ENTITY(X, Y, HP)
  VAR ID
  IF LEN(JOURNAL)>0 THEN
    ID=POP(JOURNAL)
  ELSEIF ENTITIES_SIZE!=MAX_ENTITIES THEN
    ID=ENTITIES_SIZE
    INC ENTITIES_SIZE
  ELSE
    RETURN -1
  ENDIF

  'Store the entity and return its address.
  ENTITIES[0, ID]=TRUE
  ENTITIES[1, ID]=X
  ENTITIES[2, ID]=Y
  ENTITIES[3, ID]=HP
  RETURN ID
END

'Destroy an entity
DEF DESTROY_ENTITY E
  'Store the entity and return its address.
  ENTITIES[0, E]=FALSE
  IF E==ENTITIES_SIZE-1 THEN
    DEC ENTITIES_SIZE
  ELSE
    PUSH JOURNAL, E
  ENDIF
END
This journal basically logs every time an entity is destroyed, so when you go to create a new entity, you can immediately know where to put it just by looking at the journal. Creating entities the first way is linear time O(n) since it has to search potentially the entire dimension of the array while keeping a journal makes it constant time O(1) since there is no searching, you can just look at the journal and immediately know where to store the next entity.
Thank you for the tip! I never thought of doing it like that!

Here's how I've been taught how to make an array...
ACLS
DIM ARRAY[X,Y]'X AND Y ARE THE NUMBER OF THINGS YOU WANT IN EACH DIMENSION, SO LETS SAY 4 BY 4
RESTORE @ARRAY

FOR I=0 TO X
 FOR J=0 TO Y
 READ A' READS THE DATA
 ARRAY[X,Y]=A
 NEXT
NEXT

@ARRAY
DATA 1,4,77,0
DATA 1,4,2,8
DATA 3,8,7,99
DATA 1,34,22,555


Have you tried lowerdash or N#? Maybe you could use them to simulate OOP.

String Array Storage System (S.A.S.S)
ACLS
DIM OBJ$[100] 

DEF SET_OBJ ID,X,Y
 OBJ$[ID] = STR$(X) + โ€œ,โ€ + STR$(Y)
END

DEF READ_OBJ(TYPE$,ID)
 DIM C[1]
  FOR I=0 TO LEN(OBJ$[ID])
   IF MID$(OBJ$[ID],I,1) == โ€œ,โ€ THEN PUSH C,I+1
  NEXT

  IF TYPE$ == โ€œXโ€ THEN RETURN VAL(MID$(OBJ$[ID],C[0],C[1]))
  IF TYPE$ == โ€œXโ€ THEN RETURN VAL(MID$(OBJ$[ID],C[1],LEN(OBJ$[ID])))
END

DEF EDIT_OBJ TYPE$,ID,VALUE
 DIM CC[0]
   FOR I=0 TO LEN(OBJ$[ID])
   IF MID$(OBJ$[ID],I,1) == โ€œ,โ€ THEN PUSH CC,I+1
  NEXT

   IF TYPE == โ€œXโ€ THEN OBJ$[ID] = SUBST$(OBJ$[ID],0,LEN(STR$(READ_OBJ(โ€œXโ€,ID))),STR$(VALUE))
   IF TYPE == โ€œYโ€ THEN OBJ$[ID] = SUBST$(OBJ$[ID],CC[0],LEN(STR$(READ_OBJ(โ€œYโ€,ID))),STR$(VALUE))
END

From here you can create, edit, and read object data from a single string, no hassle required. The only thing though is that any value over 10 digits will make things not what they are, because SmileBasics way of handling things, but those high of values are usually uncommon for objects. Demo code :
SET_OBJ 1,100,10

PRINT READ_OBJ(โ€œXโ€,1)
 EDIT_OBJ โ€œXโ€,1,999
PRINT READ_OBJ(โ€œXโ€,1)

Whatโ€™s cool about this system is that you can easily add more data to each object and use the same array. If you want have 100 objects defined you donโ€™t need 3 100 slotted arrays, youโ€™ll only need 1 100 slotted arrays. This is super useful if each object needs lots of data attached to it. EDIT : Amiharts system is better (I worked too hard again, even when there was a simple solution.... time to restructure my object orientated system thing...)

String Array Storage System (S.A.S.S)
While this technically works a method like this should only be used for saving/loading data (such as JSON), not manipulating data in real-time in something like a video game. It is pretty slow, and the more complex your object is the slower it will take to parse it.

One other example is this:
DEF Entity()
DIM OBJECT[8]
OBJECT[0]=0'SP#
OBJECT[1]=200'X
OBJECT[2]=120'Y
OBJECT[3]=0'Z
OBJECT[4] = 0'U
OBJECT[5] = 0'V
OBJECT[6] = 16'W & H
OBJECT[7] = 1'SCALING FACTOR
RETURN OBJECT
END
DEF RENDER ENTITY
 SPSET ENTITY[0],ENTITY[4],ENTITY5],ENTITY[6],ENTITY[6]
 SPOFS ENTITY[0],ENTITY[1],ENTITY[2],ENTITY[3]
 SPSCALE ENTITY[0],ENTITY[7],ENTITY[7]
END

I got very similar. but its all very manual. So inside the "class" you have to keep putting the prefix before it... very bloated and tedious. I used an array for each member of the object and a count, and some have strings to help do field to text translation, think I even used a call to var to lookup fields. But seeing as most classes have the same fields such as count/type/name or whatever... I kept the class prefix in the name so kinda useless to try to generalize the field access, smilebasic likes unique named.I gave most objects their own "out" functions so they could format their own debug outs.
F=VAR(TI_$[I])'where i is the field
F[object_index]' such as F[player] or F[T] I use T as "this"
I was working with a bunch of fields, like seconds, minutes, hours that were overflowing into each other so I liked to think of things as F and F2 instead of seconds/minutes and it made debug'ing a lot easier.
I=TI_FIELDS-1
F=VAR(TI_$[I])
F2=VAR(TI_$[I-1])
TI_OFLOW T,F,F2
I=TI_FIELDS-2
F=VAR(TI_$[I])
F2=VAR(TI_$[I-1])
TI_OFLOW T,F,F2
I know its ugly but it let me manually debug things, had to unroll a for loop etc. Hope it makes sense...or shows how not to do things at the least ;) In hindsight it would of been nice to say .sec .min .hour when writing the time handling, made it easier to read etc. I didn't care to put any object limits in, just push things ontop of their array. do a copy for array to array, if I recall, sb wont' push arrays onto arrays, have to mem copy. which means you have to keep track of where an objects portion of the array begins and ends. Its all indices. an index that is used by functions, generally one index will work with all the members. battling between what you can read and type in before forgetting how to read an objects value. all that goes in a separate slot from my main program.
load "PRG1:LIBRARYNAME1",FALSE
USE 1
GOSUB "1:@DEFDT"
VAR CUR=NEWTI(2016,9,20,2,0,0):?"CUR=";CUR
TI_OUT CUR 'will output the time in the "CUR" object
The idea is that being in a separate slot it is out of my way and I can use the time fields with a few update calls or setting new time and now I got timers and a sorta minimal calender support. Programming in SB is always a tradeoff of what inputs fast, holds some value to re-reading the code, and what keeps good namespace.. obviously I'd run out of namespace as it quickly turns into abbreviations for object names but ohwell. No inheritance, but objects can have "references" to other objects.. its just an index afterall. If you are dealing with AX and AY array for aliens. sure that works. and it is very easy to input. You got an "object" or "index" you just need to write the functions on what to do with it. so you get the nice function names that are backwards of the dot operator version
Apple1.set_color("red")
'turns into
AppleSet_color(Apple1,"red")
the goal of object based is to get it so you don't have to type as much, and can forget the nuances of array definition and access... so using the libraries seems the way to go when your objects differ so much from each other that they need their own unique fields that aren't useable by other functions etc.