Friday, 1 May 2015

Sample Game with JSGAM

This is the second in the series where I make a simple game.  Like last time, you are left outside a winter cabin and you have to light a fire to survive.  There is firewood left outside and matches and a fireplace inside.

To play the game: http://www.thecatsweb.com/tutorials/LightMyFire/JSGAM/
To get the source files:http://www.thecatsweb.com/tutorials/LightMyFire/JSGAM/light.zip

This time I am using JSGAM - an open source Javascript game engine focused on classic graphical point-and-click adventure games. From their homepage: http://jsgam.sourceforge.net/ JSGAM provides a great framework for making Point and Click adventures in Javascript.  It handles menus, saving, inventory, and other things.
First thing you need to do is set up a directory that will hold all the necessary files.  The easiest way to do this is to copy an existing project and delete the files you don’t need.
screenshot_directories
Next step is to open your “js\screens\title.js”

function Main()
{
  //Title of the Room
  SetTitle("Light my Life");
 
  CreateScreen("title");
  FirstScreen="exterior";
 
}

This sets up the first screen that people will see and the screen that people go to when they hit the Home button. SetTitle – sets the browser’s title bar. CreateScreen – tells the engine what image to use as a title screen. FirstScreen is the first screen of the game.
Like most Point and Click games, your game is split into different screens.  There is one javascript file for each screen.  This game really only has to locations: inside and outside the cabin.

After the title is set, you can make your first screen called exterior.js:

function Main()
{
    //Title of the Room
    SetTitle("Exterior");
   
    //Image background and walkable area
    CreateScreen("exterior", "152,398,269,223,591,220,706,396");
   
    //Adding the player
    CreatePlayer(450,300);
}

The numbers in CreateScreen lets the engine know what the walkable area is. CreatePlayer uses the Sprites in the Player directory as the player.

You can now run the game.  But it is pretty boring. You can walk around the screen, but nothing else.  Let’s add some stuff to look at:

TheWoods=CreateInvisibleObject("185,9,296,221,596,214,732,396,799,399,797,0,440,4");
TheWoods.description="Lots and lots of snow covered trees.";
TheCabin=CreateInvisibleObject("114,6,219,109,202,132,83,305,0,192,2,129");
TheCabin.description="I think it might be warmer in the cabin.";
 
TheDoor=CreateInvisibleObject("126,377,126,266,159,219,159,319");
TheDoor.description="This is the door into the cabin.";
TheWindow=CreateInvisibleObject("170,205,175,237,195,217,190,181,171,206");
TheWindow.description="It looks dark in there.";

CreateInvisibleObject makes a shape around something in the background.  If the player looks at it, the game will say the description.

Now you might be wondering where I’m getting those numbers.  I am cheating and using an image map generator. Similar to this one: http://imagemap-generator.dariodomi.de/
imagemap_example
Easy peasy!
Now to add the next room.  We make a screen file called interior.js with the following:

function Main()
{
    //Title of the Room
    SetTitle("Interior");
   
    //Image background and walkable area
    CreateScreen("interior", "182,240,641,240,771,399,5,397,29,357,111,356");
   
    //Adding the player
    CreatePlayer(645,260);
     
    TheTable=CreateInvisibleObject("22,325,110,326,181,210,105,209");
    TheTable.description = "A solid wooden table.";
    TheRug=CreateInvisibleObject("231,290,283,263,390,251,496,261,553,293,531,321,448,340,342,341,271,325,232,303");
    TheRug.description="A rustic braided rug.";
    TheDoor=CreateInvisibleObject("665,95,755,120,755,375,667,267,667,97");
    TheDoor.description = "The door back to the frigid outside."
    TheDoor.DoorTo("exterior",150,300);
}
And change the door in exterior.js to:
TheDoor=CreateInvisibleObject("126,377,126,266,159,219,159,319");
TheDoor.description="This is the door into the cabin.";
TheDoor.DoorTo("interior");
This links the two doors and allows the player to walk between them.
Now I’m going to place the wood in exterior.js and the match in interior.js.
Wood=CreateObject("Wood",365,245);
Wood.description="A pile of well dried logs.";
Wood.Takable();
Match=CreateObject("Match",90,237);
Match.description="A pretty match.";
Match.Takable();

To create the object, you need to make a directory in the Sprites folder the uses the same name.  In this folder, you put an image of the object called “left.gif”. Takable indicates that you can take this item.

Magic! You can run it again and try out taking the items.

Now is the tricky part. Letting the fireplace know about the wood.

    TheFireplace=CreateInvisibleObject("264,76,264,235,515,235,515,76");
    TheFireplace.description = "A lonely empty (and cold) Fireplace.";
    TheFireplace.UsableWith("Wood",UseWood);
   
    Wood=CreateObject("Wood",365,205);
    Wood.description="A pile of well dried logs.";
    Wood.Hide();
    Wood.UsableWith("Match",UseMatch);
   
    if(SearchParameter("LogInFireplace")){
        TheFireplace.description = "A fireplace with logs in it.";
        Wood.Show();   
    }   
[…]
function UseWood(){
    RemoveInventoryObject();
    CreateParameter("LogInFireplace");
    Wood.Show();   
}

UsableWith lets the object know what objects to listen for use actions. Then which function to call if it is used. I make a “ghost object” (that’s what I call it) of the wood in the fireplace and hide it right away. The page then looks to see if the LogInFireplace parameter has been set in place yet.  If so, the page changes the description of the fireplace and shows the wood. The UseWood function removes the wood from the inventory, creates the LogInFireplace parameter, and show the wood object that is hiding in the fireplace.

Halfway there! (Object-wise)

Now for the Match

    Fire=CreateObject("Fire",365,160);
    Fire.description="What a lovely fire!";
    Fire.Hide();

    if(SearchParameter("FireLit")) {
        Fire.Show();
    } else if(SearchParameter("LogInFireplace")){
        TheFireplace.description = "A fireplace with logs in it.";
        Wood.Show();   
    }      
Now I created a ghost object of the Fire that checks for the FireLit parameter. And finally, the UseMatch function that is attached to the Wood object.
function UseMatch(){
    RemoveInventoryObject();
    CreateParameter("FireLit");
    Wood.Hide();
    Fire.Show();
    Player.Say("Now I have a great fire!", 5000,function () { delete_cookie("LastScreen");GotoScreen("title"); } );
}
I had to modify the Say function in the js/engine/action.js file
function Say(txt,time,do_next)
{
    if(this.SayTxt==undefined)
    {
        if(time==undefined) var time=delay;
        this.SayTxt=new CreateText(parseInt(this.style.left),parseInt(this.style.top),this.divname);
        this.SayTxt.ChangeText(txt);
        this.setImage(this.status+"speak");
        var tmpthis=this;
        tmpthis.lockSprite=true;
        setTimeout(function(){
            DestroyText(tmpthis.SayTxt);
            tmpthis.SayTxt=undefined;
            tmpthis.lockSprite=false;
            tmpthis.setImage(tmpthis.status.substring(0,tmpthis.status.length-5));
            if (do_next!=undefined) do_next();            },time);
    }
}

I couldn’t figure out how to do an end screen.  So I modified the Say command to take a function to run after the character is done talking.  So after the Player says "Now I have a great fire!", it waits 5000 ms, clears the LastScreen cookie, and goes to the title screen. The LastScreen cookie is how it determines if there is a current game.

That’s it for now.  Remember you can get the source files here: files:http://www.thecatsweb.com/tutorials/LightMyFire/JSGAM/light.zip

Other articles in this series:
Making a Basic Game with Inform 7
Sample Game with JSGAM
Making a Basic Game in Twine

No comments:

Post a Comment