Scrolling Rooms
Feb 02, 2015
Another Thimbleweed Monday and time for a new video and some source code.
I spent last week working on getting rooms implemented and it closely follows how they were set up in SCUMM. In SCUMM I had the luxury of writing my own compiler, so I could customize the syntax to my liking.
Since we're using Squirrel for Thimbleweed Park, I needed to figure how to do something similar, yet retain what I liked about how rooms were set up in SCUMM. I've come pretty close, and I might make some small changes to the Squirrel compiler to make the syntax a little clearer. By and large, it's working quite well.
There is probably a better way to do all this, but this is a trip down memory lane for me as well, so I wanted to have it be as close as I could to making a classic SCUMM game. I even pulled up the original Maniac Mansion SCUMM engine source code to see how it handled the camera.
Cameras are always something you spend the entire project tweaking and I spent an afternoon working on it, trying to get it to feel right. I'm not 100% sold yet. This camera drifts a little too much and could use a slight acceleration when it starts moving. The camera in the early SCUMM games are pretty crude. Everything had to scroll on 8-pixel boundaries due to the C64 character set and then later on due to the compression we used in the PC. We could have jumped through some hoops to get it working, but performance was a huge issue and we were pushing it as it was.
There is no pathfinding at the moment (it relies on the honor system) and I'm not sure how I'm going to implement it. I could do an old SCUMM style walk-box system, or maybe bitmap based with A*. I probably won't get to pathfinding for a month, it's not critical at this point.
The video is driven by the script below with minimal hand waving.
{
background = "Bank" // the data file to load (Bank.json)
// Called when the room is entered, after objects are inited.
enter = function()
{
}
// Called when exiting, before objects are destroyed and new room's enter code is run
exit = function()
{
}
objects =
{
bankPainting =
{
name = "Cheap Painting"
lookAt = function()
{
currentActor.say("I'd think a bank could afford a better painting.") // .say() not working yet
}
}
bankToMainStreetDoor =
{
name = "Door"
quickExit = true // Actor exits before fully reaching the door
walkTo = function()
{
}
}
}
}
newRoom(Bank);
detective <- Actor("DetectiveFemaleWalk")
detective.room(Bank)
detective.at(160,10)
cameraFollow(detective)
One thing you'll notice is there are raw strings in the source code and for translation reasons this is a really bad idea. I'll do a full post in a few months talking about strings, rapid development, translations and how we're going to handle it. Until then, hold your eye rolling and self-righteous distain.
In addition to the above source code, each room has an additional file that defines it's visual characteristics, including images, hotspots, and walk areas. The files are in JSON format, which I find amusing since JSON was created by Doug Crockford, who Gary and I worked with at Lucasfilm and he produced the NES port of Maniac Mansion. In addition to JSON being a great data format, it's my little thanks and nod to Doug.
Right now I edit these files by hand, but I'll be writing a GUI editor in April that will allow the game programmers and artists to define everything visually. The editor will read and write these files as I find it very convenient to have editor data files in a format that I can bring into a text editor and made big changes or massive find/replaces. Sometimes the best tool for the job is notepad.
background: "Bank"
objects: {
bankPainting: {
usepos: "{83,26}"
usedir: FACE_BACK
hotspot: "{{40,120},{119,82}}"
}
bankMainStreetDoor: {
usepos: "{25,10}"
hotspot: "{{0,10},{20,120}}"
cursor: CURSOR_EXIT_LEFT
}
}
props: {
bankRope: {
image: "BankRope"
pos: "{300,32}"
zsort: 6
}
}
Some of you may have noticed this isn't valid JSON. I wrote my own JSON parser for Scurvy Scallywags that can read compliant JSON, but also relaxes some of the rules just a little. Key's don't need to be in quotes if they are made from alpha+digits, commas are optional if they are not syntactically confusing, and the opening "{" is optional as the file is assumed to be a dictionary. Small changes, but I find them convenient.
There might not be a new video next Monday since I'm going to be gone for two days (family stuff) and then focusing on the design and getting ready for the "Big Brainstorm" with David Fox.
But the week after that, I plan on having a first pass at the UI, selecting verbs, building sentences and clicking on objects.
It really is 1987 all over again. In a whole lot of ways.
- Ron
If I was going to use walk-boxes today, I'd do them as full n-sided polygons, not simple rectangles like in Maniac Mansion (or trapezoids in Monkey Island). Just painting the walk areas down is also appealing. Like everything we did back then, it was driven by how slow the processors were. That just isn't a realistic concern these days, we can do things like make our lives easier.
I'm wondering if you ever plan on open sourcing some of the engine. Specifically I'm thinking of the bridge between C++ and Squirrel scripting. There are so many questions online about how to implement scripting on games and your solutions seems really elegant!
Nice stuff, Ron - very exciting to see these posts.
Otherwise, great read as always. Keep up the excellent work.
Keeping the maps and the art in sync should be easier if you can get a pipeline where you do them from the same source, and it gives you a little more wiggle room to get funky with the art too, given that you'll have a 1-1 mapping between the "collision" and visuals.
You can even use more bits/more maps to cheaply flag all kinds of things, like multiple walking planes at different scales, or a the-floor-is-lava map. Because the floor is sometimes lava, except where it isn't.
1. I'm a programmer, and I'm simultaneously awed and inspired by the simplicity of the scripting thus far... does it ever get much more complicated, or is this really the meat of it, barring a few tricks to accommodate visuals and walk boxes? Also, I should clarify that the tone of my question is sincere awe, not a snide "that's all there is?" remark.
2. Do you ever have to create your own placeholder art, in Gary's absence, and if so does it embarrass you to work with it? I have that problem in my game development efforts, so I'm just hoping that I'm not alone in that regard. :D
Thanks!
-Gary
Btw, regarding the "cheap painting" of the mountains, the late Bob Ross of PBS fame would approve of that!
Whenever I reached some kind of milestone (like in the video shown here, ie being able to walk an actor around in a room) in my own projects (not necessarily game programming), I feel the urgent need to click around the screen for hours and just watch it happen :-)
Along with your source-code sample, I could watch this video over and over again
let's say Bank would be a subclass of MajorRoom. You'd define quickExit = true once on the MajorRoom class, and quickExit = false on MinorRoom. both of these rooms could be subclasses of Room. The Room class itself could have an array of Exits that display some default behavior, and via the infernal gift of subclassing this magically makes every MajorRoom and MinorRoom work sensibly..
The upside would be that you'd be able to focus on the specific details of a Room, and be guaranteed that the 'world' still works, unless you specifically change it for a specific room - in which case the code is there because the game needs it. Only the code that's required for the specific room would be in the specific room class file. Which is awesome, in terms of overview and separation of concern.
I find it quite annoying when game areas behave differently from eachother for no reason, except -ahem- sloppy programming. This ruins the illusion.
Everything is very early, so I'm still figuring out what's going to work best.
BTW: The 'quickExit' flag is so a character doesn't have to walk all the way to the door before leaving the room. They might walk 25% of the way, then the room transition will happen. This was a change we made in some the latter Humongous Entertainment games, and it felt really good. It eliminates a lot of walking time and since the screen is going to cut anyway, it's not jarring.
On the other hand, if you don't use subclassing, you will end up fixing the same bug in 50 files and only doing it in 49 because of a phone call or a picture of a kitten on the internet...
quickExit seems like an excellent idea. This shows that you recognize the player's pain in moving back and forth a lot, and that simple pat on the virtual shoulder helps a lot to keep the game fun even though some parts can be repetetive.
Pick up metal post.
Great work and love the "storyline" type of script as you mentioned many times in your blog when you/your team were creating Scumm you wanted it to be more like a movie script (not exactly in those lines but still)..
One thing only bothered me in the video and don't know if it bothered me in a good or bad way still trying to figure it out. At 00.06 seconds when you make the first stop as the camera scrolls , at the stop point it continues smoothly to scroll just a bit more and then stops. This was not achievable in Scumm I suppose but with this engine it works or doesn't work. I am confused about how I feel about it. I am not sure whether it removes the retroness of the game or not. It is such a smooth move for a game this old! :) What do you and Gary feel about it?
P.S KEEP UP THE EXCELLENT WORK!!
Regards
Demetris
Is mooving! Great!
now for a poke
Are you planning to make the engine accessible for other developers, so they can create their own adventures?
I prefer it in this way, even if in 1987 the scroll was 8-pixel wide, due to hardware restriction.
(Maybe you can start thinking on a special "1987" settings page, someone could prefer to view strict 1987 look and feel...)
I would be great if each actor could have a little variation in it or maybe (Don't remember what game did this) when you click multple times on the same object the character would get frustrated and say something different.
I didn't like the "jagged" scrolling of the 80's in Kickstarter video. I don't think the smooth scrolling takes anything away from the authenticity of the old style.
If you make it as a option, please consider setting the smooth scrolling as default. Wider audience doesn't want to see jagged scrolling as a default. Those wishing to torture themselves can set the setting to false.
And I wonder, why the scroll register of the C64's VIC wasn't used back then? It surely is there because of the fixed 8-width pixel chars.
Since there is a lack of quality 2D, open-source, point and click adventure engines in the wild, is there any chance (even remote) to see it someday on GitHub? :)
That would make me (and I guess many developers) very happy. :)