6
\$\begingroup\$

I've been adding Google Play support into my game. For the most part I'm getting on swimmingly, but I'm having a hell of a time understanding exactly how the submission calls to submit score and achievements work.

It's the => operator and surrounding method that confuse me, such as this one that I have in my code. It all works, I just don't know how. I've read up a bit and I think these are called Lambda expressions.

But I want to know why it is this way, and why it isn't made so we can just use the standard typical bool instead of making this strange way to send the result of whether or not our player is logged in.

Hope that makes sense. Here's the code in question:

Social.localUser.Authenticate((bool success) => {
        if (success)
            {
                Debug.Log("score authenticed successfully");
            }
            else
            {
                Debug.Log("score not authenticed, check user is signed in");
            }
     });
    Social.ReportScore(score, GPGSIds.leaderboard_pinball_wizards, (bool success) => {});

Particularly, the bottom line really has me stuck.

\$\endgroup\$
  • 14
    \$\begingroup\$ This doesn't really have anything to do with game developement \$\endgroup\$ – Bálint Jul 25 '17 at 19:39
  • 1
    \$\begingroup\$ Whatever function you pass into that argument is then later called when ReportScore has succeeded or failed. If you have a void result(bool s) function in your class you can just pass result without parenthesis. In this case you have an anonymous function which doesn't have a purpose elsewhere other than handling the result of ReportScore. Also in this case your lambda function doesn't do anything, but it is eventually called. ALSO this is not gamedev related. \$\endgroup\$ – Sidar Jul 25 '17 at 19:47
  • 1
    \$\begingroup\$ Google PLAY services requires this operator to be used to make use of their GAME service libraries. And I am developing a game. But hey I got some great answers from this site so I'm happy as always :) \$\endgroup\$ – Big T Larrity Jul 26 '17 at 10:51
10
\$\begingroup\$

In this particular case, lambda expressions are used to handle the result of your Authenticate and ReportScore calls. Both of these calls are asynchronous, meaning they will be processed in the background while your game continues to run. Internally these calls will have to communicate with Google over the network which will take a while. It may be dozens of frames between the time you are making the calls and the time you are receiving the results.

See this example:

Debug.Log("Frame " + Time.frameCount + " - Sending authentication request.");
Social.localUser.Authenticate((bool success) =>
{
    Debug.Log("Frame " + Time.frameCount + " - Received authentication result. success=" + success);
});

Once you execute this, you will see that the call to Authenticate, and the execution of your lambda expression happen at entirely different times.

So how does this work? The signature of Authenticate is

public void Authenticate(Action<bool> callback); 

so this method does not immediately return a result, because it simply is unable to immediately tell whether the authentication will succeed (because it has to wait for network communication to complete). So how can it tell you about the result once it is known? By allowing you to pass in a callback handler. Authenticate will call that callback handler as soon as it knows the result. That handler is passed in as the first parameter: Action<bool> callback, meaning, any method that can be called with a single boolean parameter and that does not return a result (=void).

So both these calls will be correct:

void Start()
{
    Social.localUser.Authenticate((bool success) => { Debug.Log("" + success); });
    Social.localUser.Authenticate(AuthenticationCallback);
}

void AuthenticationCallback(bool success)
{
    Debug.Log("" + success);
}
\$\endgroup\$
  • \$\begingroup\$ thank you both for this. I understand the reasons why now and hopefully can remember how it works and add this to my knowledge. I wish I could Tick both answers \$\endgroup\$ – Big T Larrity Jul 25 '17 at 19:51
14
\$\begingroup\$

You are correct, these are lambdas. A lambda is basically a nameless function that you can write (almost) anywhere. It looks like this, in code

(parameters here) => { body of function here }

where "parameters here" are a list of parameters to the function as you would write parameters to any function definition, and similar for "body of function." The => token is devoid of any other meaning except indication that the whole expression is a lambda. In particular is has nothing to do with equality or greater-than, in this context. It just as easily could have been some other arbitrary token, the language designers simply chose that one.

The alternative to passing a lambda is usually not a fixed value (that's why you can't just "pass a bool" instead. Lambdas are functions, so if you aren't passing a lambda you're instead passing a delegate (or the name of a function). That function needs to exist elsewhere.

Lambdas are useful for a variety of things, such as for specifying execution logic in a place that is nearby the surround logic that will use it. One often sees them employed as sorting predicates to a list, or as callbacks for asynchronous operations (which is what your use appears to be, on the surface).

Lambdas also have this neat ability to capture local variables, which means they can use those locals inside their bodies. This is useful because if a sort predicate requires a function taking two objects to compare against, that's all well and good: you could write a lambda or just supply the name of a SortObjectsByWhatever function you've written elsewhere. But if you want to also employ some other bit of runtime data in the body of that sort, it's often easier to do that using a lambda, because you can't always change the signature of the delegate type the sort function expects.

The MSDN has a comprehensive write-up on lambdas in C#.

\$\endgroup\$
  • \$\begingroup\$ thanks very much for all the answers. Most helpful as always. I decided to give the Ticked answer to the guy with low rep as his answer was also very helpful. \$\endgroup\$ – Big T Larrity Jul 25 '17 at 19:54
3
\$\begingroup\$

What is Lambda?

You can think of a lambda expression as just shorthand for an anonymous function:

void myFunction(bool value) { 
    Console.WriteLine("my function({0})", value);;
} 

// Action<bool> is the type of a function that accepts a bool parameter and returns void
Action<bool> f;
f = myFunction;
f(true);
f = (bool value) => { 
    Console.WriteLine("my lambda({0})", value);;
};
f(true);

Result:

my function(True)
my lambda(True)

Why do functions take lambda parameters?

Welcome to the world of first-class functions! Being able to pass around functions in your variables opens up many possibilities. In this particular case, it is one pattern for writing nonblocking request-response code. This way, your game code doesn't just pause while waiting for the network request to finish. In essense, you tell the library "go run this request on the Internet, and take your time -- when you're done, do (this thing) with the response". that (this thing) part is the lambda you provide it.

For example, if I had a service SDK that exposed my functions this way, and you wrote the following code,

Console.WriteLine("1");
myService.Send(parameter, (string result) => Console.WriteLine("3. {0}", result));
Console.WriteLine("2");

You could conceivably get this order of output, where my server took 3 seconds to respond but your game didn't have to pause for 3 seconds while it was waiting for a response like it would have to if it was written synchronously

1:00:00.000 PM: "1"
1:00:00.001 PM: "2"
1:00:03.000 PM: "3. response from server"

Other ways of asynchronicity

In .NET, there are other async patterns, like async/await, or event callbacks for writing asychronous code. In some platforms (like [earlier] javascript+node.js), the lambda-based approach is the only option)

\$\endgroup\$
  • \$\begingroup\$ thank you both for this. I understand the reasons why now and hopefully can remember how it works and add this to my knowledge. I wish I could Tick both answers \$\endgroup\$ – Big T Larrity Jul 25 '17 at 19:51
3
\$\begingroup\$

In terms of general use, a Lambda is just an anonymous function. The main advantage is that they're easy to make anonymous / pass around to other frameworks or API's.

They generally consist of three parts:

  1. arguments, usually wrapped in a parenthesis (not always),
  2. => Lets you know that this IS a lambda not just an expression
  3. a scope. Much like an if statement / loop you can follow it with a single line, or wrap you function in a scope using {}

take this function:

void foo(int bar){Debug.Log(bar);}
//called as
foo(5); //Outputs 5;

as a lambda it would looks like this:

var foo = (int bar) => {Debug.Log(bar);}
//called as foo(5); //Outputs 5

Other uses: As Josh Petrie mentioned in his answer, lambdas are very good at capturing local data.

This makes them very useful for tasks like threading. Rather than packing all your relevant data into an args struct, you can just use the vars straight into the function.

As you've seen already, they're also widely used as callbacks. Lambdas provide a clean and simple way for developers to leverage the power of a framework without needing to drastically refactor their own code.


Now, to use a Lambda in your situation is as simple as adding code to included scope

void customHandler(bool success){//insert code here}
//sends the lambda through the callback.
Social.ReportScore(score, GPGSIds.leaderboard_pinball_wizards, (bool success) => {customHandler(success);});
\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.