|
© Andy Wilson, 1998
Tips and Tricks: Pop and Push
This month I’ll be looking at a simple technique you can use to manage your data in a Director application. This technique involves managing a data stack. The concept of a data stack is very simple. It means that you think of your data as being arranged in a stack so that when you add data it goes to the top of the stack, and when you retrieve data it is taken from the top. This is know as ‘pushing and popping’: you ‘push’ data onto the top of the stack when you want to store it and ‘pop’ it when you want it back.
The idea is that data is piled up on the stack one item at a time and then popped back out in reverse order. This can be a very handy way to think about your data in some circumstances. For example, assume you have built a game that involves the user navigating their way around a maze of some sort. Assume that each choice the user makes takes them to a new frame in your movie. At any point you want to allow the user to be able to step back to their previous position before moving forward again. A stack would come in very handy here – simply ‘push’ the frame number onto the stack each time the user makes a decision then, when they choose to step back, pop their last position off the top of the stack and take them to that frame. This way, even if the user had been playing the game for hours, visiting hundreds of frames, they would be able to step back through every decision they had made in reverse order.
There are lots of other situations where the same technique would come in handy. For example, let’s say that you build a ‘browser’ that allows users to look at different web pages in your Director application. As long as you pushed the users new URL onto the stack each time they moved to a new location you could build a back button that returned the user to their previous positions just by popping them back off the stack. They would then be able to step back through every page they had visited in that session.
The way we are going to implement this in Director is really very simple, since it only really involves using a combination of parent scripts and lists. To kick off we’ll write a simple parent script to hold the stack data. To do this, open a new script window and type in the following lingo;
property stackList
on new me
set stackList = []
return me
end
Remember to click on the properties button for the script and change it’s type to ‘Parent’ since we are going to use this as a parent script to create multiple stack objects. Remember also to give the script a name when you have entered the code, since you have to call the script by name whenever you create a new object. In this example I’m going to call my script ‘Stack’. If you do this, your cast window should look something like:
All we’ve done so far is create the core of the stack object – just enough to allow us to create a stack without giving it any real functionality. Nevertheless, it allows us to create stacks as follows;
set stack1 = new (script "Stack")
set stack2 = new (script "Stack")
put stack1
-- <offspring "Stack" 2 2321c56>
put stack2
-- <offspring "Stack" 2 2321cb0>
set stack1 = EMPTY
set stack2 = EMPTY
The two objects we create here actually do nothing of any use. The parent script does just enough to allow us to create objects in this way. Let’s take the next step and write the code that allows us to push data up onto the stack. Obviously you will be adding this code to the Stack parent script, thus adding a new method to the objects that are created from this script;
on push me, val
append (stackList, val)
end
Notice that the parent script declares a property stackList, and also that the on new handler that we call when we create a child object initializes this property to [ ], an empty list. This means that each time a new stack is created from the parent script it starts of with a list property which we initialize to [ ]. The push handler we have just added to the object allows us to add a new value to this list. When we call the push handler we pass to it a value, val, which is simply appended to the end of the objects’ stackList property.
The other half of our objects’ functionality involves writing a pop handler that allows us to retrieve any values we have already pushed up on to the stack. The code for the pop handler is as follows;
on pop me
if not count (stackList) then return #error
set var = getLast (stackList)
deleteAt (stackList, count (stackList))
return var
end
This code checks to see if there are any values up on the stack. If not, it returns an error signal. Otherwise it returns the last value pushed on to the stack and deletes that item from the stack.
To push a value up on to the stack and then pop it back out we would do something like this;
set myStack = new (script "Stack")
push (myStack, 100)
put pop (myStack)
-- 100
All I’ve done here is create a new stack object, myStack, pushed up an arbitrary value, the integer 100, then popped it back off the list. Note that each time a value is popped off the stack it is actually removed from the top of the stack completely – that is the whole point of a stack. That means that if, as in this example, we have only pushed one item up onto the stack then we can only pop one item back off before receiving an error;
put pop (myStack)
-- #error
To get a better idea of how our stack works, lets kill off our existing object create a new one, push a series of values onto it then pop them all back out again. In the previous example I pushed an integer onto the stack. In fact, since the stack is implemented as a lingo list, you can push up any type of data supported by lingo;
set myStack = EMPTY
set newStack = new (script "Stack")
push (newStack, 100)
push (newStack, "Lingo")
push (newStack, 2.4023)
push (newStack, [2, "Andy", 4.2])
push (newStack, [#name:"Andy", ¬
#address:"andyw@dircon.co.uk"])
push (newStack, the script of ¬
member "Stack")
put pop (newStack)
-- (script "Stack")
put pop (newStack)
-- [#name: "Andy", ¬
#address: "andyw@dircon.co.uk"]
put pop (newStack)
-- [2, "Andy", 4.2000]
put pop (newStack)
-- 2.4023
put pop (newStack)
-- "Lingo"
put pop (newStack)
-- 100
put pop (newStack)
-- #error
Remember that the code we've written is in the form of a parent script, which means we can use it to create as many child objects as we like. Each child will have it’s own stackList property, which means that each can be used to manage and maintain a separate stack. And that’s really all there is to it. We’ve developed what is potentially a very powerful tool from just a few lines of lingo. Just to remind you, the entire Stack script looks like this;
- - "Stack" parent script
property stackList
on new me
set stackList = []
return me
end
on pop me
if not count (stackList) then return #error
set var = getLast (stackList)
deleteAt (stackList, count (stackList))
return var
end
on push me, val
append (stackList, val)
end
Having explained the core techniques involved in writing a stack object I want to finish by making a few modifications to allow us to use this technique to implement the back button I talked about at the start of the article. To do this you will first need to create a new parent script – call it 'Navigator', with code as follows;
-- "Navigator" parent script
property stackList
on new me, startFrame
set stackList = []
navigateTo me, startFrame
return me
end
on backTo me
if count (stackList) then
set newFrame = getLast (stackList)
if (count (stackList) > 1) then
deleteAt (stackList, count (stackList))
end if
go to frame newFrame
end if
end
on navigateTo me, newFrame
append (stackList, newFrame)
go to frame newFrame
end
You will need to do a few other things too. First, create a movie script as follows;
global navObject
on startMovie
set navObject = new (script "Navigator", 1)
end
on enterFrame
put the frame into field "frameNum"
end
on exitFrame
go to the frame
end
Now, create two buttons, Navigate and Back, and attach cast member scripts to them as follows;
- - code for ‘Navigate’ button
global navObject
on mouseUp
navigateTo (navObject, random(60))
end
- - code for ‘Back’ button
global navObject
on mouseUp
backTo (navObject)
end
Finally, create a field member ‘frameNum’ and place it, along with the Navigate and Back buttons on all frames from 1 to 60 in your movie. What happens here is that the startMovie handler creates a Navigation object which is basically a slightly modified Stack object – the main difference is that it not only pops and pushes the stack but also navigates to a new frame every time it does so. When the Navigate object is created it takes the movie to a frame, in this case frame 1. Each time the Navigate button is clicked the user is taken to a new frame at random. When the Back button is clicked the user is returned to the last frame they visited. I also modified the code so that if the user tries to step back from the first frame nothing happens. Now you have all the elements you’ll need to create a game that allows the user to step back through all the steps they’ve made.
As usual I’ve put this article on the web along with all the previous articles in this series. I’ve also included a version of the code I’ve described here as a zipped file for you to download.
|