SCI1.1 Animating Your Static Props
Hopefully you noticed when you added view 110 in the last chapter that there were a couple of loops that had a few extra cels. For the most part, these are the ones that we are now going to animate. Looking in the view, take note of loop 4, the ships wake, loop 6, the fisherman, and loop 7 the fishing pole. You'll also notice that there are also several cels in loop 1, but that was really just for organization, those are the stars that we placed in the sky, and each cel was meant to be a static view.
Props are quite frankly the bread and butter of your game, besides adding detail, you can also animate them as well as add logic to them. If you're crafty enough, you can even add a bit of motion to them which is something that is usually reserved for Actors. Let's begin by first taking a look at the fisherman, we'll animate him and then we'll go ahead and add a bit of logic to control when he animates. My goal with the fisherman was to make it look like he was breathing... I am not quite sure that I accomplished that, but he will still serve as a good first example.
The first thing I am going to want you to do, is scroll down to the spot where you initialized the fisherman
(fisherMan init:)
Now, I am going to have you place a call to set his animation cycle to Forward, change your statement to this and run your game to see what happens.
(fisherMan init: setCycle:Forward)
Ok, that certainly doesn't look right, not right at all. One thing we could try to fix this is controlling the speed at which he cycles. This next statement we are going to add is actually a bit of a misnomer. Using the cycle speed command, the higher we set the number, the slower the speed actually is. The number that we set, is actually the cycle delay, the number used, is the number of cycles that pass before the loop moves to the next cel. So now, let's try setting the cycle speed. Change the fisherman's init statement to this and try running your game again.
(fisherMan init: setCycle:Forward cycleSpeed:25)
Well, that is a little bit better but still far from good. Let's face it, unless you are hyperventilating, then nobody actually breathes like that. What we want is for the fisherman to cycle once, then wait a bit, and then cycle again. Just judging from the description of what we want, we are going to need a changeState method for this. It allows us to step through different command statements, setting either cycles or seconds as a delay if we want before stepping on to the next step. In order to do this, we will be adding a new instance of script and then set the fisherman to use that script.
Just like in the last chapter, we are going to want to scroll down to the end of the script and right click and select insert object, only this time we want to add a script instead of a prop. Just like before, the first thing we need to do is give the instance a name, something like fishingScript would seem appropriate. With any luck, you should now have something that looks like this.
(instance fishingScript of Script
(properties)
(method (changeState newState)
(= state newState)
(switch state
(0)
)
)
)
Here's a little tip for you, as the number of methods and instances grow you are going to find that it becomes really easy to misplace a closing bracket from time to time so whenever you can a good comment or two can really save your ass. The first thing that I want you to do is add a comment to each closing bracket. I would suggest doing "; end instance", "; end method", and "; end switch". With that out of the way, lets go ahead and look at the first case in the changeState method. By default, it is going to be 0 and it will be triggered as soon as the script is called. For our purposes, we want to pause the fisherman for breathing so lets just add a statement to wait 4 seconds in case 0. Next we will want to add a statement in case 1 to set the fisherman's cycle to End. Unlike our first attempt where we set the cycle to forward which cycles through all the cels and then starts over and repeats, end just plays till the end then stops. Now, since the end cel of the loop is actually the fisherman inhaled cel, we are going to want him to exhale so it doesn't look like he is just holding his breath, add in a slight delay to this current state, lets say 1 second and then in a third state we set the fisherman back to cel 0, and also since we are now at the end of our change state, we are going to want to jump back to the beginning in order to repeat the process the whole time we are here. Now that it has all been described, here is what that should look like in code.
(instance fishingScript of Script
(method (changeState newState)
(= state newState)
(switch state
(0
(= seconds 4)
)
(1
(fisherMan setCycle: EndLoop)
(= seconds 1)
)
(2
(fisherMan cel: 0)
(self changeState:0)
)
); end switch
); end method
); end instance
Oh, and we can't forget, now where we initialized the fisherMan, we are going to need to set him to this new script, so change your init statement to this and then run your game again to see what we think of that.
(fisherMan init: setScript:fishingScript)
Alright, now that is probably about as good as we can get it for what I have drawn. One thing that bugs me about it right now is that while the fisherMan moves, the fishing pole that he is holding does not so let's go ahead and address that. For this, yes we could create another script instance if we wanted and just mirror what we did with the fisherman, but there really is no need to. Since we have the fisherman's script up and running we can control everything from there. Since we want the fishing pole to mirror what the fisherman is doing, let's just go ahead and add in a couple of calls to the fishingPole whenever it is that the fisherman moves. That happens in state 1 and in state 2. Since the fishing pole is only a couple of cels, this time, let's just change the cel value instead of setting an animation cycle. Yes we could have done this with the fisherman too but I am trying to show you a couple of different ways to do things. Anyway, in state 1 where the fisherman inhales, lets just set the fishingPole to cel 1. Then in state 2 where the fisherman exhales and returns to his original state, let's set the fishingPole cel to 0. Adding those in, our fishingScript would now look like this.
(instance fishingScript of Script
(method (changeState newState)
(= state newState)
(switch state
(0
(= seconds 4)
)
(1
(fisherMan setCycle: EndLoop)
(fishingPole cel:1)
(= seconds 1)
)
(2
(fisherMan cel: 0)
(fishingPole cel:0)
(self changeState:0)
)
); end switch
); end method
); end instance
That is just about as much as we can do with the fisherman and the fishing pole. Now let's move on the ship's wake. This is going to be an easy one. For the ship's wake, I am just going to have you set the cycle forward, and add set the speed somewhere around 15. If you remember from above, this is all set where we initialized the shipWaves. Try adding in that bit of code and running your game to see what you think.
(shipWaves init: setCycle:Forward cycleSpeed:15)
Alright, that is great and all, but with the ship's wake animated, just doesn't seem right with the ship just sitting there. Let's do a little bit of creative coding and add motion to the ship. This isn't something that is usually done with a prop, there are several motion classes built into the template game but those are all reserved for actors. We could change our ship to an actor but I don't want to, we aren't covering actors yet that is later in the tutorial. Let's just talk through what it is that we want to do. We want the ship, every few cycles to move to the right a few pixels since it is supposed to look like it is in the distance we don't really want to move it very fast. After the ship is off the screen, we may as well dispose of it so it isn't burning up any more of our memory resources since we can't see it anyway.
Now for the first part of what we want, the ship to wait a few cycles and then move to the right, we can handle just like we did with the fisherman, with a changeState method. Once again, we will need to create a new script instance. This time let's call it the shipScript. Just like with the fisherMan, the first state (0) or our changeState method, we are simply going to want to wait a few cycles so this time instead of using seconds, let's try cycles. In the second state, let's reposition the ship one pixel to the right, or another way to say that, would be to add 1 to the current x position. Just like with the fishing pole where we set the cel directly, we can also set the x or y position directly. Really we can set any of it's properties as we go, but right now we want to play with the x position. Now, the only real trick here is that we need to know the current x position in order to change it right? Nope, not at all, even though we don't know what it is, the interpreter does and we have a way to just access it directly. This bit of code corresponds to the ships current x postion.
(distantShip x?)
With that in mind, let's create our first run through of our changeState, remember case 0, we simply want to pause for a bit. In case 1, we want to set the x position of the ship to the current position plus one. We may as well say it now too, just like in our fisherman, state 2 we want to jump back up to case 0. So with all of that said let's start with this as our changeState in the shipScript you just made.
(method (changeState newState)
(= state newState)
(switch state
(0
(= cycles 25)
)
(1
(distantShip x: (+ (distantShip x?) 1))
(= cycles 1)
)
(2
(shipScript changeState:0)
)
); end switch
); end method
... and now, finally our ship script in all it's glory
(instance shipScript of Script
(method (doit)
(super doit:)
(if (> (distantShip x?) 340)
(shipWaves dispose:)
(distantShip dispose:)
)
); end method
(method (changeState newState)
(= state newState)
(switch state
(0
(= cycles 350)
)
(1
(distantShip posn: (+ (distantShip x?) 1) (+ (distantShip y?) 1))
(shipWaves x: (- (distantShip x?) 8))
(= cycles 1)
)
(2
(= cycles 350)
)
(3
(distantShip posn: (+ (distantShip x?) 1) (distantShip y?))
(shipWaves x: (- (distantShip x?) 8))
(= cycles 1)
)
(4
(= cycles 350)
)
(5
(distantShip posn: (+ (distantShip x?) 1) (- (distantShip y?) 1))
(= cycles 1)
)
(6
(shipScript changeState:0)
)
); end switch
); end method
); end instance