Bonus Movie and Mr Spock
Feb 27, 2015
First of all, I usually don't get choked up when celebrities die. I enjoyed their work, I will miss their new work, but I didn't know them. I feel for their families and friends, but it's not a loss I internalize.
There have been two exceptions. The first was John Candy. I don't know why his death hit me like it did. The second is Leonard Nimoy, which I found out about today on Twitter while I was compiling (no, really, I was compiling).
Star Trek is an indescribable part of my life. Spock really does feel like a friend. I loved Next Gen, but Star Trek will always be Kirk, Spock, Uhura, McCoy and Scotty. It's what I watched as a kid; everyday after school. I even went to a Star Trek convention back in '89.
This is as close as Gary and I have to something to memorialize Mr Spock. Live Long and Prosper.
Now on to the fun stuff.
Here is this week's video, complete with animationing objects.
There are three reasons I didn't get as much done as I wanted to this week:
1) I goofed around too much. Monday and Tuesday are just days I want back.
2) I decide to refactor a bunch of code having to do with how an object's images are represented in the room data files. Animated objects were being kept in separate .json files and it was getting unwieldy, so I moved animated objects into the room's .json file. They are never seen outside the room, so they can just live in there.
3) I ran into a odd Squirrel bug that took me a couple of days to figure out. It wasn't a bug IN squirrel, more of how I was using the API (many thanks to Vincent Hamm for helping and listening to me debug and gripe on IM). There are a lot of really great things about Squirrel, documentation isn't one of them. One thing I worry with Squirrel is that I'm really bending it to get it to do what I want and someday I'm going to break it.
The managing of object states is pretty verbose right now. As time goes on, I'll streamline a lot of it so less scripting code is needed.
Here is the code:
{
background = "Bank"
enter = function()
{
if (bankClock.state == OPEN) {
playAnimation(bankClock, "state_open")
} else {
playAnimation(bankClock, "state_closed")
}
}
exit = function()
{
}
bankPainting =
{
look_count = 0
name = "Cheap Painting"
verbLookAt = function()
{
if (look_count == 0) { currentActor.say("You'd think a bank could afford a better painting.") }
if (look_count == 1) { currentActor.say("This looks like a knockoff.") }
if (look_count == 2) { currentActor.say("I think I can see the numbers behind the paint.") }
if (look_count >= 3) { currentActor.say("I'm done looking at this painting.") }
look_count++
}
}
bankClock =
{
name = "Clock"
state = CLOSED
verbLookAt = function()
{
if (state == CLOSED) {
currentActor.say("It's who cares'o clock.")
} else {
currentActor.say("It's gears all the way down.")
}
}
verbOpen = function()
{
if (state == OPEN) {
currentActor.say("That is already open.")
} else {
state = OPEN
playAnimation(bankClock, "do_open")
}
}
verbClose = function()
{
if (state == CLOSED) {
currentActor.say("That is already closed.")
} else {
state = CLOSED
playAnimation(bankClock, "do_close")
}
}
}
bankToMainStreetDoor =
{
name = "Door"
verbWalkTo = function()
{
print("here")
enterRoomFromDoor(TestStreet.TestStreetToBankDoor)
}
}
}
sheet: "BankSheet"
background: "bank"
objects: {
bankPainting: {
usepos: "{83,26}"
usedir: 8
hotspot: "{{40,120},{119,82}}"
}
bankClock: {
animations: [
{
name: "do_open"
frames: ["bank_clock1", "bank_clock2", "bank_clock3"]
}
{
name: "do_close"
frames: ["bank_clock3", "bank_clock2", "bank_clock1"]
}
{
name: "state_closed"
frames: ["bank_clock1"]
}
{
name: "state_open"
frames: ["bank_clock3"]
}
]
pos: "{158,101}"
zsort: 158
usepos: "{161,26}"
usedir: 8
hotspot: "{{146,114},{171,88}}"
}
bankToMainStreetDoor: {
usepos: "{17,11}"
usedir: 2
hotspot: "{{0,125},{12,0}}"
}
}
props: {
bankRope: {
image: "bank_rope"
pos: "{300,32}"
zsort: 6
}
}
- Ron
I had a nice chuckle at "it's who cares o'clock", haha.
Also: LLAP. \\//_
- you manage the character to be depending on its position in front or behind the ropes
- how you handle when you have animations of two different objects interacting (may be it's not there in the video, but will happen)
- what the zsort is
- what use dir is
love the blog
As a side note I do actually like the current usage of structures of verbLookAt. Even though it might be simplified (or "optimized") with simple lists for currentActor.say() some of the cool stuff in the adventure games was when other stuff happened when you were looking at something. For instance your character were looking at an item in the shop, saying out loud his observation and the shop owner would chime in with further informationat this current setup it would be easy to add further events or actions when somebody is looking at something. My favourite was Bernard in Maniac Mansion reading the security door warning and then trying to crackpot the code by himself.
These opportunities make for a more "realistic" approach where Looked isn't simply a suspension of disbelief of why the character actually has to say stuff out loud for the person playing the game to read. Every time I looked at an item in the old adventure games and other characters in the scene/room reacted I was positively surprised and felt more immersed in the game.
const int kDirNone = 0x00000000;
const int kDirRight = 0x00000001;
const int kDirLeft = 0x00000002;
const int kDirFront = 0x00000004;
const int kDirBack = 0x00000008;
At some point, I'll replace the numbers in the .json file with symbols so I can changes them in the engine code and not have to go into 100 .json files and change the values.
lookAtStrings = {
"You'd think a bank could afford a better painting."
, "This looks like a knockoff."
, "I think I can see the numbers behind the paint."
, "I'm done looking at this painting." }
buildVerbLookAt = function( responseStrings , actor )
{
cycleIndex = 0
buildVerb = function()
{
actor.say( responseStrings [cycleIndex])
if ( cycleIndex = < ( responseStrings.len() - 1) )
{ cycleIndex++}
}
return buildVerb
}
verbLookAt = buildVerbLookAt( lookAtStrings , currentActor )
Since verbLookAt at is build via currying , it's completly decoupled from response strings and a actor , that way it is possible to "mix and match " different response strings with different actors , besides , once created , verbLookAt automatically manages response without need for a clumsy if branching.
I'm also seeing allot of "quality of life" features like "hold to move" in-staid of "point and click".. click......click*
This is refreshing to see in such early development, "2.D sprites with a touch of modern tech" Nostalgia overload.
One thing that caught my eye was how well you captured the speed and fluid movement of the models *feels like 60fps.
You are truly adding a gem to the 1987-1990 era of PC gaming that has been lost in time.
Unfortunately my heart lies in the 1993-1995 years of Day of the Tentacle and Full Throttle. When Lucas Arts
and Sierra really pushed the more intricate MS Paint style Sprites and animation that peaked just before 3.D models.
But that's more personal taste than critique, like comparing 1964 vs 1972 mustang body styles.
Why is the initialization for the clock inside the enter method of the bank and not within an "onEnterRoom" method of the clock object?
Having played Maniac Mansion just recently (as recent as "last week") what I want to say is that it just doesn't "feel" right. Oh, speaking about the right (look) and feel, the rest is just perfect! I totally love it! :-D
@Ron: Could you maybe make a video with characters moving at "half speed" some day, please?
Over the next few months, you're gong to see a lot of crappy and unfinished art go into the game. Games aren't built by polishing one thing until it's right then moving to the next. I get things in, make sure the code is flexible, then move on the next. There will be a point where the whole game looks like a giant piece of garbage. That's totally normal, then we'll go though and fix everything in multiple passes, each time making it a little better. We do this to better hit a dead line. If we're polishing in layers, we can stop at some point and know the whole game exists at the same level of quality. It's how I've always made games and the way most people do.
Hopefully this blog is a glimpse into that process. I love getting all the comments, but ofter the response is "yeah, I know".
Sorry for being nit-picky. Please take it as a compliment. "Wow, everything looks so great, almost perfect already! Just that one little aspect though, how could they let that slip?" Sounds better? Because that"s what I was trying to say. I'm really amazed by the progress so far that I'm thinking "that looks almost done, what could they possibly be doing another 12 months?" from time to time.
Gee, I think I'm bad at giving compliments...
All that said, this is just a first pass. I can now tune all the variables I need so I'm on to the next task. As Gary does more animation we'll tune and adjust. The animation of her walking was a first pass. Nothing is final at this point.
I have thought about the verbosity in this sample script and want to add my 2 cents to this:
Why not give each actor a general purpose state flag (32bit Int or whatever). The states can be represtend by bits of the flag (and therefore constants) which means you would check a state like this:
if ( state & OPEN ) {...}
if ( state & CLOSED ) {...}
To make state handling even easier I would add some nice helper methods:
sateIs( toCheck): Returns the current object if the state flag is set, otherwise returns dummy actor (see my comment in "UI Action").
sateSwitchTo( toSet ): If the state is already set, return the dummy actor, otherwise set the state flag and return the current object
stateElse(): Returns the current object if no "stateSwitchTo" or "stateIs" has succeeded since calling the last event handler (i.e. "onVerb"), otherwise returns the dummy actor
Also in the follwing sample "animationPlay" is a mehtod of the actor class and the say-method
was tweaked so that it performs the following check:
function say( text, [...] ) {
if ( this == actorDummy ) return(actorDummy)
if ( !this.isPlayable ) {
return( currentActor.say( text, [...] ) )
}
[...]
}
This way "say" can used by used on the object but will execute on the actorCurrent. This makes scripting with daisy-chaning methods easier.
Also I moved the intial animation from the "enter" method of the room to an "init" method of the object
Bank <-
{
background = "Bank"
bankPainting =
{
name = "Cheap Painting"
onLookAt = function()
{
actorCurrent.say("You'd think a bank could afford a better painting.",
"This looks like a knockoff.",
"I think I can see the numbers behind the paint.",
I'm done looking at this painting.")
}
onPickUp = function()
{
who(Ransome).doThis(function() {
say("Sure, why not....")
pickUp(bankPainting)
delay(500)
facePlayer()
say("I'm now the proud owner of this painting.")
delay(100)
say("What the hell am I going to do with this?")
delay(100)
say("NO, I'm not carrying this thing around all day!")
faceObject(bankPainting)
putBack(bankPainting)
})
whoElse.say("I don't want that stupid painting")
}
}
bankClock =
{
name = "CLOCK"
// State constants
CLOSED = 1
OPEN = 0
init = function()
{
stateIs(OPEN).animationPlay("open");
stateIs(CLOSED).animationPlay("closed");
}
onLookAt = function()
{
stateIs(CLOSED).say("It's who cares o'clock")
stateIs(OPEN).say("It's gears all the way down")
}
onOpen = function()
{
stateSwitchTo(OPEN).animationPlay("do_open")
stateElse().say("That is already open.")
}
onClose = function()
{
stateSwitchTo(CLOSED).animationPlay("do_close").say("Great, now what?")
stateElse().say("It doesn't get more closed than this.");
}
}
}
Sorry for the long post but reading your blog really get my gears going! It's very interesting to read and a nice brain teaser to think about how one I would approach this.
Ron, maybe you could do them a favor and document Squirrel?
I hope this post isn't stupid.
He played so many loveable characters on screen, such as the dad from Summer Rental, the security guard from Vacation, Del from Planes, Trains & Automobiles (my favorite performance of his, and wow was he great at being a dramatic actor especially when giving his character's sad confession at the end), the Polka King who offers Kevin's mom a ride back to Chicago in Home Alone, etc. etc. All of these super nice, super funny characters that he played, by all accounts everyone who knew him and worked with him said that off-screen in real life, he was just as nice and wonderful of a guy. Any time he appeared on screen, even if it was a bad movie, he instantly made the movie 100x more enjoyable to watch because he was just that incredibly likeable.
John Candy, Chris Farley, John Pinette, Mitch Hedberg, Patrice O'Neal, there's just way too many super nice comedians who've died way too young who's deaths could've been prevented if they just took care of their health a little bit better. Easier said than done I know, the entertainment business puts an intense amount of pressure on those in it to stay relevant which often leads to maintaining health as being at the bottom of one's priority list.
I'd have a question regarding the verb interface: is it implemented in the engine code or in Squirrel code?
I ask this because I remember you showed a clickedAt(x,y) Squirrel function: being the code in Squirrel would allow you to define an override to the interfaces e.g. if the player needs to do some sort of click puzzle.
Is that the case?
Keep up the great job!
One small comment on these test videos is that when the actor stops in front of the object there should be a very small delay before the subtitles come up. Of course is the newer processing which makes everything faster but that is my opinion. Not having also the animated talking heads makes the whole subtitle thing a bit odd as well. Just my opinion.
Demetris
I love the classics, the artist's equivalent is
"I was rendering..."
Although the artist's sounds like it could have more sinister connotation and involve beef tallow or quick lime.