Guide to the Expression Editor

tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
edited January 2015 in Community Tutorials
One of my clients asked me how people figure out how to come up with expressions in GameSalad such as 768+(1/2)*self.Size.Width. Is there some guide they follow? Well as far as I can tell there is no guide, so this is my attempt to make one.

First, the short answer: there are three ways that people figure out expressions: (1) Copycatting - using someone else's expression and making small changes to see how it affects the actor. This is generally how I approach trig functions such as sin & cos because I have only a very basic understanding of them. This tool is a good example of a way to tinker with a trig expression; (2) Trial & Error - this is my favorite method because well, I like math, and I like a good challenge. If I know what I want to accomplish (e.g. keep the actor within the boundaries of the screen), I will try out different expressions until I hit upon the correct one. Sometimes this is quick and sometimes it takes hours or days of thinking and calculating and trying and making mistakes before it works; (3) Asking for help - the forums are a great equalizer. If you know what you want to do but not how to do it, ask!

And now the long answer: okay, first an explanation of the expression editor. Here's what the expression editor looks like (it's accessed from that little 'e' symbol in behaviors such as Change Attribute and Constrain Attribute, but also in many other behaviors):

image
image

The expression editor is how GameSalad incorporates mathematical functions*. These functions can be as straightforward as 3+4 or they can be as complex as a recent one I created 176+self.Size.Width*floor(log10(game.currentScore))-(log10(self.divisor)-1)). If you're curious about that one, see this thread for details.

You're probably thinking "why would I ever put 3+4 into the expression editor?" And you might not. But here's an example of when I use a simple expression like that. Let's say that I want to place an actor on the edge of the screen. I could just drag it into place and hope it's in the right spot or I could manually edit its position attributes in the attribute inspector for that actor, but if I want to automate it a bit and save myself time, I can use the expression editor. I know that an iPad Portrait scene is 768 pixels wide. If I do this:

image

And preview the scene, the actor gets cut off at the edge of the scene:

image

The reason for this is that GameSalad calculates the position of an actor from its center. If an actor is 100x40 pixels, then its x position is 50 pixels (half of 100) from the right or left edge of the actor. So placing an actor at x=768 means that its center is at the edge of the screen. Half of the actor is on the screen and half of it is off. To fix this, I have to move the actor half of its width to the left. So I can put 718 (768-50) in the expression editor and it will work correctly (although in that case I don't actually need the expression editor since it's just a single value):

image

I like to keep my coding as clear as possible so I would actually put 768-50 or 768-100/2 into the expression editor instead:

image

That way when I come back to look at the code, I will be able to quickly understand what I've done. If I see 718, I'm initially confused and have to work backwards to figure out where that came from (although the value of the Note behavior cannot be overstated). But what if I decide to resize the actor so that it is 75x30 pixels? I then have to go back in and change the expression to 730.5 or 768-37.5 or 768-75/2. There has to be a better way! And there is. I would enter 768- and then add self.Size.Width from the attribute drop-down menu (attributes MUST be selected in this way and cannot be typed in directly) and then type in /2 for divide by two, leaving me with this:

image

[As an aside, if you ever need to take a screenshot of a behavior that includes an expression, make sure to click the 'e' first to display the full expression. You can even move the expression window that pops up to position it better for the screenshot.]

No matter what the width of the actor is, that expression will place it on the edge of the scene. Try it for yourself! You can make the width 205.7168 if you like. Because it's based in math, it will always place the actor at the edge of the screen minus half of its width.

Let's take it one step further in terms of complexity. Sometimes it's useful to be able to keep an actor from falling off of the edge of the screen while allowing the player to drag the actor. The basic rule for dragging an actor is:

image

(The attribute game.Mouse.Position.X can be found in the Devices section of the attributes drop-down menu, which is accessed by clicking the 'e' and then clicking the triangle right below the expression editor's text entry area.)

But if you try this, you'll quickly learn that the actor can be dragged beyond the edges of the scene. I want the actor to stop at the edge and I should be able to use what I learned above. But I need an additional function: min() or max(). These two functions and many others can be found in the functions drop-down menu:

image

Most, if not all, of the functions in GameSalad are based in traditional mathematical functions used outside of game development. To learn more about a particular function, see the Cookbook definitions or just google something like mod function, which returns this Wikipedia entry:
In computing, the modulo (sometimes called modulus) operation finds the remainder of division of one number by another.

Given two positive numbers, a (the dividend) and n (the divisor), a modulo n (abbreviated as a mod n) is the remainder of the Euclidean division of a by n. For instance, the expression "5 mod 2" would evaluate to 1 because 5 divided by 2 leaves a quotient of 2 and a remainder of 1, while "9 mod 3" would evaluate to 0 because the division of 9 by 3 has a quotient of 3 and leaves a remainder of 0; there is nothing to subtract from 9 after multiplying 3 times 3. (Notice that doing the division with a calculator won't show you the result referred to here by this operation, the quotient will be expressed as a decimal fraction.)
Adding gamesalad to the search term can result in much more useful information such as tutorials, demos, and videos:

image

But let's get back to what I was doing. The min() function returns the minimum value of two inputs. So min(3,18) = 3 and min(-22,0) = -22. The max() function returns the maximum value of two inputs. Note that inputs can be attributes, functions, or numbers. To keep the actor from falling off the screen, I need to make its maximum x position equal to 768-self.Size.Width/2 (look familiar?). I can do this by constraining the actor's x position as follows:

image

(Note that functions can either be selected from the drop-down menu or typed in directly... your choice! Just make sure to count your parentheses because you must always have an equal number of left-facing and right-facing parentheses, just like in this note).

The Constrain Attribute behavior keeps the attribute that value indefinitely, whereas the Change Attribute behavior changes the value once and allows it to be changed again later. So the above expression will constrain/keep the actor's self.Position.X attribute equal to the smaller value of the current mouse position or the edge of the scene (minus half of the actor's width). If the mouse position is greater than 768-self.Size.Width/2, the actor will remain there. If the mouse position is less than 768-self.Size.Width/2, the actor will move to whatever the mouse position is. But that's only half of the solution. If I drag the actor to the left side of the screen, there is no constraint in place and the actor will fall off of the edge of the screen. If I want to prevent this, I can do:

image

In this case, I am constraining the x position to the left edge of the scene (x=0) plus half of the actor's width.

So I can have those two Constrain Attribute behaviors in place and they should keep my actor on the screen. But unfortunately, in practice that doesn't work. The two constraints work independently and the result is that only one seems to constrain at any given time. So I need to combine them into one function. Plus, I like to simplify rules and behaviors as much as possible (and by simplify I don't mean "keep it simple and easy" but rather "keep it concise"):

image

That may seem complex because... it is! I've placed one function, min inside of another function, max. To evaluate this expression, I have to start with the innermost function, the min function. That function starts to the right of the first right-facing parenthesis and ends at the first left-facing parenthesis. This function returns the smaller value of the mouse position and the right edge of the screen. But as I move the actor/mouse to the left edge of the screen, the smallest value of that function is going to be wherever the mouse position is (e.g. 200, 100, 0, -100, etc.; it's not constrained yet). Next, I evaluate the outermost function, the max function. This returns the smaller value of the innermost function (which is somewhere between the mouse position and the right edge of the screen) and the left edge of the screen (0+self.Size.Width/2). So wherever the mouse position is, it's going to be no further left than the left edge of the screen (plus half of the actor's width).

In conclusion: I took you through my thought process for creating one complex expression. You may be thinking, "but I wasted half an hour of my time reading through that wall of text and all I can do is constrain the actor's x position to the boundaries of the screen!" And you would be right. :) My intention wasn't to teach you everything there is to know about using the expression editor. That would be the equivalent of teaching you all there is to know about math. But hopefully you have a grasp of how the expression editor can be used to build expressions based on what you decide you need for your game.


*I focused on mathematical functions but the expression editor can also be used for text expressions (e.g. "Hello and welcome, "..game.Name; two periods in a row will contactentate or join text strings and attributes; use option/alt+space to display a space within quotes) and even certain logical expressions (e.g. NOT game.boolean). Those are outside of the scope of this post but perhaps I will tackle them in a later post.

New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

«1

Comments

  • FlamingbananaFlamingbanana Member, PRO Posts: 90
    Wow this is great! Thanks a lot :D
  • gyroscopegyroscope I am here.Member, Sous Chef, PRO Posts: 6,598

    Excellent post/article/tutorial, @tatiang. :-)

    ""You are in a maze of twisty passages, all alike." - Zork        temp domain http://spidergriffin.wix.com/alphaghostapps

  • Braydon_SFXBraydon_SFX Member, Sous Chef, PRO, Bowlboy Sidekick Posts: 9,271
    Very nice! Thanks for sharing!
  • ArmellineArmelline Member, PRO Posts: 5,327
    edited December 2013
    Fantastic tutorial! I really wish I'd been able to read it a couple of weeks ago :D Took me longer than I'd like to admit to work out the boundaries things for myself...

    Definitely deserves to be pinned!

    One thing I think would be very beneficial to add would be an explanation of %.
  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
    Thanks, everyone!

    @Armelline The mod() function which can also be written as % can be confusing but I didn't want to include too much that wasn't directly related to the example of setting scene boundaries and the topic of the expression editor in general.

    The best place to start learning about the mod() function is actually by searching or clicking on the links I provided above. But if you have specific questions, feel free to start a new thread. I'd be happy to explain further in a separate thread.

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

  • SocksSocks London, UK.Member Posts: 12,822
    edited December 2013
    @tatiang

    Brilliant stuff as usual ! :)>- Should be 'stickied'.
    First, the short answer: there are three ways that people figure out expressions: (1) Copycatting - using someone else's expression and making small changes to see how it affects the actor.
    Definitely the best way to learn stuff, pull apart stuff made by people who know what they are doing and steal the good bits ! :)
    This is generally how I approach trig functions such as sin & cos because I have only a very basic understanding of them
    I could teach you sin and cos in a few posts, all that stuff is surprisingly simple (probably much more simple than you think) but pretty powerfully, in fact you'd only need a basic understanding of sin (cos is just sin with a slightly offset value).

    @Armelline
    One thing I think would be very beneficial to add would be an explanation of %.
    % = modulus, think of it as looping a value, so if you had a count that went like this [ Change XXX to XXX+1 ] you would get: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 . . . .

    But if you use the '%' function you can get that value to 'loop' at a certain point, so for example: [ Change XXX to (XXX+1)%5 ] would give you this: 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 . . . (basically it loops the first 5 values).

    And [ Change XXX to (XXX+1)%3 ] would give you this: 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 . . . . .



  • natzuurnatzuur Member Posts: 304
    Yes, great information. The expression editor is extremely powerful, and can really push your game into the next level. I would highly recommend users to take the time to research the functions by using wiki and google, the more you understand them the more complex and powerful equations you can create.
  • SnapFireStudiosSnapFireStudios Member Posts: 1,603
    Awesome info, thanks for contributing!
    - Thomas
  • natzuurnatzuur Member Posts: 304
    @Armelline

    As a side note to what socks wrote, if your using the mod function this (XXX+1)%5 would look like this mod(XXX+1,5).
  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
    Both versions of the mod function are interchangeable but GS staff have stated that they are phasing out % and it shouldn't be used.

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

  • Fal01Fal01 Member Posts: 460
    Top post!

    It’s not a bug – it’s an undocumented feature

  • ArmellineArmelline Member, PRO Posts: 5,327
    Thanks for the responses guys. I was suggesting % as something I know confused me at first and I had to do some serious googling to work out what it was for - googling % really isn't easy :D I eventually figured it out, but the explanations given here will hopefully save others the headache I went through :D

    Also @Socks, your quoting makes it look like I wrote the post you're referring to in the first half of your response - tatiang is too gracious to say, I'm sure, but the credit should be his there! :D
  • SocksSocks London, UK.Member Posts: 12,822
    edited December 2013
    @natzuur
    @Armelline
    As a side note to what socks wrote, if your using the mod function this (XXX+1)%5 would look like this mod(XXX+1,5).
    Yep, it's probably best to use your version (mod) because the old version (%) is going to be taken out into the yard and shot / killed off at some stage, best to get with the new version.
  • SocksSocks London, UK.Member Posts: 12,822
    I was suggesting % as something I know confused me at first and I had to do some serious googling to work out what it was for - googling % really isn't easy. . .
    Whoops ! lol, yep, I misread that ;)

    And yes, I can imagine Googling '%' is going to be pretty useless.
    Also @Socks, your quoting makes it look like I wrote the post you're referring to in the first half of your response - tatiang is too gracious to say, I'm sure, but the credit should be his there! :D
    Double whoops ! :D I'll fix that !
  • brettw777brettw777 Member Posts: 24
    I also appreciate @tatiang for taking the time to create this post. There are so many things that can be done in the expression editor but no single resource for learning all the different types of commands and equations and what the result will do. I would like to encourage any of you long time gurus to share as much of your knowledge of the EE as you care to type because for us newbies, that is easily the most difficult part of learning GS. If there were a book explaining every or almost every possible thing about the EE, I would pay crazy money for that book! Does anyone know if there are any good wikipedia links to learning more about this? I would not know what to search for. Thanks.
  • JGary321JGary321 Member Posts: 1,246
    @Socks feel free to throw up a SIN explanation anytime. I certainly don't use/understand much of that one.
  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
    @JGary321 He's working on it as I'm sure he will comment on soon. I think it's best to start new threads for specific discussions about mod, sin, cos, etc. so that the whole community can benefit rather than just people who have chosen to read a "Guide to the Expression Editor" post.

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
    Here's a brief explanation of a use for the mod function: http://forums.gamesalad.com/discussion/comment/440348/#Comment_440348.

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

  • I messed around with mod() a bit but didn't get the intended result. I ended up using some nested conditionals, but the result I was trying to achieve was this:

    -- The higher the level you are on, the more likely an enemy will appear.
    -- I want it to go up by 1% chance every 2 levels, up to maximum of 20% and to start at 5%.

    if you are level 1, the chance is 5% an enemy would appear.
    if you are level 10, the chance would be 10%. 10/2 + original 5% chance.
    If you are level 20, the chance would be 15%
    if you are level 30, the chance would be 20% (max)
    if you are level 99, the chance would still be 20%

    I know I am missing a simple math solution but it has thus far eluded me and to save time, I went with some < and > if statements.
  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
    edited December 2013
    @mattrouse1 Not really a case for mod(), I don't think. Here's how I would do it:

    Constrain attribute game.enemyChancePercentage to max(20,5+ceil(game.Level/2)-1).

    Change attribute game.enemyAttempt to random(1,100)
         this would need to be in a timer or a rule so that it fires more than once

    When attribute game.enemyAttempt ≤ game.enemyChancePercentage
         Spawn [enemy actor]

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

  • pHghostpHghost London, UKMember Posts: 2,342
    edited December 2013
    @tatiang or anyone else

    You mention logical expressions are possible -- how do you implement that?

    I cannot get it to work. Tried:

    When: Attribute game.box is

    NOT..self.open
    NOTself.open
    NOT self.open
    not..self.open
    notself.open
    not self.open
    not..(self.open)
    not(self.open)
    not (self.open)
    !=..self.open
    !=self.open
    != self.open

    But none of them register. Tips?

    Thanks!

    P.S. I did it in the expression editor.
  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
    @pHghost

    That seems like an exhaustive list... hmm... there was a thread where someone mentioned it but I've never actually tried to get it to work. I recall that the person said it only worked for boolean attributes and I just couldn't see the point since you can already create a rule where a boolean is true or one where a boolean is false.

    I don't think it was leading to any sort of more complex logical expression (e.g. NOT(attribute1 OR attribute 2) ).

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949
    edited December 2013
    @pHghost

    I just tried a DisplayText behavior with not(self.attribute) and it worked to display the opposite of the boolean attribute value.

    image

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

  • pHghostpHghost London, UKMember Posts: 2,342
    Didn't try with booleans -- that's probably it. Not usable in that case, shame.

    I have a project in nightly, which I wanted to port to stable, since the GS team is going on break for the holidays and I might be release-ready before they start addressing some of the issues like animations etc. that mean it's impossible to release through the nightly. I make extensive use of 'is not' to check text strings. I'll have to figure out another way round it.
  • motorcycle boymotorcycle boy Member Posts: 429

    just discovered this thread. I will be studying this as i still need to understand this area more. thanks @tatiang

  • floatingwoofloatingwoo Los Angeles, Calif.Member Posts: 393

    Just what I was looking for! Thanks

  • tatiangtatiang Member, Sous Chef, PRO, Senior Sous-Chef Posts: 11,949

    You're welcome @dreichelt and @floatingwoo! I forgot I'd even written this. :#

    New to GameSalad? (FAQs)   |   Tutorials   |   Templates   |   Greenleaf Games   |   Educator & Certified GameSalad User

Sign In or Register to comment.