Lua Scripting

I started off early with Lua scripting. This was mainly because the Ray Wenderlich RPG samples were using it and this gave me a head start and a direction. I think Lua is a beautiful scripting language, and it has real potential (see the Love2D framework).

It’s what I can teach my 9 year old son. So it’s pretty nice.

I am still in pre-production and I wanted to make coding the actor (NPC and player) actions as easy as possible. If you settle to early for something simple to progress quickly, it can be really cumbersome when you need to make a lot of changes to rooms and actors.

I made a pickup animation sequence completely in Objective C, but I knew this was wrong.
If I had to change or extends this I had to change about 40 lines of code and it wasn’t flexibal.
This was wrong because the behavior of actors is data but in the form of scripts. I have one script per room.
As you can see this is much nicer and flexible :

local seqOpen = cs.SequenceFactory.new("dave");
seqOpen.walkTo("{6,5}");
seqOpen.animationBackAndForth("pickupsideways");
seqOpen.addInventory("hammer");
seqOpen.wait("0.8");
seqOpen.script("passHammerToDave");
seqOpen.start();

This script will :

  • Walk the main character to the item.
  • He will kneel to pick it up and will rise again. This is a back and forth animation
  • Add the item to the inventory of the player
  • Wait for about a second and run a lua method called passHammerToDave that alters the state of the character. Dave will ‘own’ the item now.

Now it passes all these actions to the CCActionSequence that is supported by the Cocos2D framework. The seqOpen will be passed as an array of commands to the Objective C bridging code, and this will execute this in sequence as CCAction’s! Many actions in cocos2d are finite time actions, that means that walking to a certain tile with the help of an A* (Astar) algorithm does not fit easily into an action. So I splitted all separate CCAction sequences and let every sequence end with an action to fire an event to a sort of action manager class that starts the next sequence.

This looks like this :

    NSMutableArray *preppedActions = [NSMutableArray array];

    for (NSString * scriptingLine in luaStringTableArray) {
        if ([scriptingLine hasPrefix:@"walkto"]) {
            id blockToExecute = [CCActionCallBlock actionWithBlock:^{
                CCLOG(@"scripted walkto reported");

                NSString * coords = [LuaUtility firstArgFromScriptLine:scriptingLine];
                player.moving = YES;
                [player moveTowardsTileCoordWithAi:CGPointFromString(coords)];
            }];
            [preppedActions addObject:blockToExecute];
        }else if([scriptingLine hasPrefix:@"wait"]){
            NSString *delay = [LuaUtility firstArgFromScriptLine:scriptingLine];
            CCTime timedDelay = [delay floatValue];
            id actionDelay = [CCActionDelay actionWithDuration:timedDelay];
            id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                [player.characterAnimationManager eventReportedOn:player event:@"waiting finished"];
            }];

            id sequenceToExecute = [CCActionSequence actionOne:actionDelay two:blockToFireEvent];
            [preppedActions addObject:sequenceToExecute];
        }
        else if([scriptingLine hasPrefix:@"animate"]){
            NSString *animationName = [LuaUtility firstArgFromScriptLine:scriptingLine];

            id blockToStartAnimation;
            if([scriptingLine hasPrefix:@"animateonce"]){
                blockToStartAnimation = [CCActionCallBlock actionWithBlock:^{
                    [player startOneTimeAnimation:animationName];
                }];
            }else if ([scriptingLine hasPrefix:@"animatebackandforth"]){
                blockToStartAnimation = [CCActionCallBlock actionWithBlock:^{
                    [player startBackAndForthAnimation:animationName];
                }];
            }

            if(blockToStartAnimation){
                CGFloat animationDuration = [player animationDuration:animationName];
                id delayFromPickingUpAnimation = [CCActionDelay actionWithDuration:animationDuration];
                id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                    [player.characterAnimationManager eventReportedOn:player event:@"animation finished"];
                }];

                id sequenceToExecute = [CCActionSequence actions:blockToStartAnimation,delayFromPickingUpAnimation, blockToFireEvent,nil];
                [preppedActions addObject:sequenceToExecute];
            } else{
                CCLOG(@"WARN : syntax of the animate command was not correct, it needs to be animateonce or animatebackandforth it was %@",scriptingLine);
            }

        }else if([scriptingLine hasPrefix:@"script"]){
            id blockToExecute = [CCActionCallBlock actionWithBlock:^{
                [luaScriptProcessor runVoidScript:[LuaUtility firstArgFromScriptLine:scriptingLine]];
            }];
            id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                [player.characterAnimationManager eventReportedOn:player event:@"script finished"];
            }];
            id sequenceToExecute = [CCActionSequence actionOne:blockToExecute two:blockToFireEvent];
            [preppedActions addObject:sequenceToExecute];
        } else if([scriptingLine hasPrefix:@"move"]){
            id moveAnInch = [CCActionMoveBy actionWithDuration:0.2f position:CGPointMake(20,0)];
            id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                [player.characterAnimationManager eventReportedOn:player event:@"script finished"];
            }];
            id sequenceToExecute = [CCActionSequence actionOne:moveAnInch two:blockToFireEvent];
            [preppedActions addObject:sequenceToExecute];
        }  else if([scriptingLine hasPrefix:@"playsound"]){
            id blockToPlaySound = [CCActionCallBlock actionWithBlock:^{
                [[AudioManager sharedManager] playSoundEffect:[LuaUtility firstArgFromScriptLine:scriptingLine]];
            }];
            id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                [player.characterAnimationManager eventReportedOn:player event:@"script finished"];
            }];

            id sequenceToExecute = [CCActionSequence actionOne:blockToPlaySound two:blockToFireEvent];
            [preppedActions addObject:sequenceToExecute];
        }
        else if([scriptingLine hasPrefix:@"addinventory"]){
            id blockToAddInventory = [CCActionCallBlock actionWithBlock:^{
                NSString *sceneItemName = [LuaUtility firstArgFromScriptLine:scriptingLine];
                [[InventoryManager sharedManager] addInventoryItemByName:sceneItemName];
                [tilemapTool removeObjectFromMap:sceneItemName];
            }];
            id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                [player.characterAnimationManager eventReportedOn:player event:@"script finished"];
            }];

            id sequenceToExecute = [CCActionSequence actionOne:blockToAddInventory two:blockToFireEvent];
            [preppedActions addObject:sequenceToExecute];
        }


        else if([scriptingLine hasPrefix:@"gotoscene"]){
            id blockToGotoScene = [CCActionCallBlock actionWithBlock:^{
                //TODO Pass specific roomid in case of roomchange
                NSString *roomId = [LuaUtility firstArgFromScriptLine:scriptingLine];
                SwitchRoomScreen *scene = [[SwitchRoomScreen alloc] init];
                [[CCDirector sharedDirector] pushScene:scene];
            }];
            [preppedActions addObject:blockToGotoScene];
        } else if([scriptingLine hasPrefix:@"tileon"]){
            id blockToPutTileOn = [CCActionCallBlock actionWithBlock:^{
                NSString *tileCoords = [LuaUtility firstArgFromScriptLine:scriptingLine];
                CCTiledMapLayer *wallLayer = [tilemapTool.tileMap layerNamed:@"collisiontiles"];
                [wallLayer setTileGID:1 at:CGPointFromString(tileCoords)];
            }];
            id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                [player.characterAnimationManager eventReportedOn:player event:@"script finished"];
            }];
            id sequenceToExecute = [CCActionSequence actionOne:blockToPutTileOn two:blockToFireEvent];
            [preppedActions addObject:sequenceToExecute];
        } else if([scriptingLine hasPrefix:@"tileoff"]){
            id blockToPutTileOn = [CCActionCallBlock actionWithBlock:^{
                NSString *tileCoords = [LuaUtility firstArgFromScriptLine:scriptingLine];
                CCTiledMapLayer *wallLayer = [tilemapTool.tileMap layerNamed:@"collisiontiles"];
                [wallLayer setTileGID:0 at:CGPointFromString(tileCoords)];
            }];
            id blockToFireEvent = [CCActionCallBlock actionWithBlock:^{
                [player.characterAnimationManager eventReportedOn:player event:@"script finished"];
            }];
            id sequenceToExecute = [CCActionSequence actionOne:blockToPutTileOn two:blockToFireEvent];
            [preppedActions addObject:sequenceToExecute];
        }
    }

    [player.characterAnimationManager loadActionsFromArray:preppedActions];
    [player.characterAnimationManager startAnimations:player];
    player.characterAnimationManager.continuous = continuous;

A lot of code, for sure. But this is what I need to do once… Maybe the code needs some cleaning up, but for now it’s ok. It does the job well. Every action will be perfectly in sequence because each action will fire an event to the characterAnimationManager to signal that the next sequence should be executed.

The next scripts will show the scripting for opening the elevator door and going to another room :

local seqOpen = cs.SequenceFactory.new("dave");
sequence.walkTo(room.objectElevatorDoor.buttonlocation);
sequence.animationOnce("reachcentre");
sequence.playSound("button.caf");
sequence.wait("0.2");
sequence.playSound("slidedoors.caf");
sequence.switchCollision(room.objectElevatorDoor.standingLocation, "off");
sequence.script("openElevatorDoor");

The script does the following steps:

  • The player walks to the button location that is defined in the room he is in.
  • The player will reach for the button when it uses the verb “use” on the control panel
  • Button sound plays (this is alway an asynchronous event)
  • The sound for sliding the elevator is played
  • The elevator has some tile locations that needs to be blocked or unblocked when doors close or open up.
  • The sequence runs the open elevator door animations that is called via lua via function ‘openElevatorDoor’

Well lets see these both scripts in action! (trust me… it’s quite short) :

Scripting from franzzle on Vimeo.

Archived: Uncategorised

App Icon Evolution

Is it too early to look at an icon? I think you need to start early, trying different designs and get into grips with a certain look… It’s these kind of things that, if you wait too long for it to make and when you are trying to finish the game, you will not make a proper icon. Those final crunch days are gruesome because bugs need to be squashed, menu’s have to be made, a settings screen you forgot and maybe the gameplay still lacks some finesse? Eventually  you will not have the time to make it anyway!

One other thing is that you need to know how long your icon could last. Does it bore you? Does it grow on you? Does it stand out? I am on to my third icon now, and it could be the final one.

The icon below was a really early icon I created just to try to lay out the composition and to get off the default Spritebuilder icon, Dave was standing against the wall ready to be fusilladed by german WWII soldiers. I you think this looks familiar, it is! Because a part of the cartoon is actually stolen from the political cartoon for the Peking Olympics

iPad-old@2x

The idea was that Dave would transported back to the present day with a kind of transporter beam. I knew this was only a working icon and I would make a complete overhaul of it. But the transporter thingy, stuck with me though!


itunesIconCharacterWithTimemachine-ipad

A long period after that I thought that the Time Machine had a shape that could be fitted into an icon. So I made a Hires character and put that in the time machine. This was a first draft and the dave character was not recognizable on small icons. The colors were completely off and it did not have the pixelated look I was looking for.

iPad

So I came up with this icon. I still use it today. The character is the Dave character as he will walk around in the game. The colours are bright and playful. The transporter twinkling stars makes it as if Dave is actually being transported back in time! Maybe I should tweak the shadowing a bit?

Well, that’s it for now!

 

Archived: Uncategorised