5
\$\begingroup\$

Context: I'm making a little "program your robot army" sort of game in Java in which the player writes Lua scripts (which are then run by LuaJ) to program their robots to do stuff. So far there are two objectives:

  1. Keep It Simple, Stupid, and
  2. React to the environment.

To take a simple example of what I currently have, the player should be able to tell their robot to go to a particular location with something like

robot:goTo(5, 8)

Like Colobot, the scripting engine effectively halts (blocks) at that instruction until the robot has reached its destination.

I like this -- it's meets the first requirement nicely, in that you can't do anything until goTo decides to return. It's also simple on the Java side -- I can basically do while (/* condition */) yield() and just resume the coroutine every tick (then maintaining state in local variables is trivial). But: what if that resource at (5,8) disappears, or an enemy comes near, or energy runs low? There's no opportunity to bail -- the robot has to reach (5,8) before it can do anything else.

The main other idea I can think of is to produce some sort of loop:

while not robot:near(5,8) do
    if robot:enemyNearby() then
        break -- and do some other stuff, presumably
    else
        robot:moveTo(5,8)
    end
end

where moveTo this time sets the velocity/acceleration and then immediately returns, without blocking until arrival.

I could have robot:goTo return the actual coroutine (either wrapped, so you just call it, or directly, which requires calling coroutine.resume and passing the coroutine), which might look something like this:

moveTask = robot:moveTo(5,8)
while not robot:near(5,8) do
    if robot:enemyNearby() then
        break
    else
        moveTask()
        -- or perhaps coroutine.resume(moveTask)
        -- or some other syntactic sugar
    end
end

These last two approaches look pretty similar, but reusing a coroutine as in the second example makes managing state (e.g. a path) trivial, at the cost of exposing coroutines to the player.

To reiterate, the goal is to allow for writing simple scripts, in which a set of actions are executed in series (sorta like queued actions in The Sims), while simultaneously allowing more complex scripts with actions that may be interrupted by arbitrary events (nearby enemy, target moved, stats dropping).

Any tips or ideas would be much appreciated!

\$\endgroup\$
4
  • 3
    \$\begingroup\$ Hi Max, please ask a specific question. This is a Q&A site, and open-ended questions aren't a good fit. \$\endgroup\$ Aug 1, 2015 at 6:07
  • \$\begingroup\$ The question is "what's the best way to tackle this situation?" Even though it's somewhat open-ended, there can still be a best, well-thought out, comprehensive answer that could be Accepted. Lastly, is there a recommended alternative place to post, or simply "not here"? \$\endgroup\$
    – Max
    Aug 1, 2015 at 15:57
  • 1
    \$\begingroup\$ "what's the best" never has a single answer. There is no best. :) I think you could reword this question to fit, but "here's two choices, which do you like most?" certainly doesn't. gamedev.net has traditional forums and there's the Chat feature of this site, too. \$\endgroup\$ Aug 1, 2015 at 19:24
  • 3
    \$\begingroup\$ is there a recommended alternative place to post: This list might help. \$\endgroup\$ Aug 2, 2015 at 0:05

3 Answers 3

1
\$\begingroup\$

You can make the moveTo() function a blocking function in the user script, but you should definitely not make it block your script.
This way you should have moveTo() return a boolean telling weather the destination was reached or the action was interrupted.
I suppose this would be the simplest solution for the user.

You cold also have the moveTo() function just sends the destination and actionToTake to your program and return immediately.
Then your program can just move the robot towards that destination unless it gets interrupted, then when/if it reaches destination just execute the action that was assigned (action could be a function call or you could predefine a set of possible actions).

\$\endgroup\$
0
\$\begingroup\$

Multiple solutions:

  1. Pass a callback to the moveTo function that can check other things from inside the loop and optionally stop it in the middle.
  2. Create two new coroutines - one for movement, one for doing things while moving. Yield main thread until either of the two has finished, abort the remaining one afterwards.
  3. Turn movement into state, resolve it in parallel (possibly from outside Lua). robot.targetPos = {5,7}, then do the additional work in the same thread.

Which one's 'best' for this case is up to you.

\$\endgroup\$
0
\$\begingroup\$

When something bad happens while running a Java function from a Lua script, have that function exit by throwing an exception. There are several ways to implement Lua from Java, so refer to the documentation for details about how to throw an exception from a java function back to Lua. The downside is that the player will have to put lots of error checking into their code and wrap everything in pcall, which might be a bit unfun.

Another option is to just do nothing when there is an error on the Java side and return silently to Lua. It's now up to the player to implement error-checking code by calling a getLastError Java-method . If they don't, their program will continue as if nothing had happened and might simply not do what they want to do.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged .