JavaScript EditorFree JavaScript Editor     Ajax Editor 



Main Page
  Previous Section Next Section

Implementing Sight, Sound, and Smell

The implementation of the human senses in a text game is the most challenging of all since the player can't really see, hear, or smell. This means that apart from the algorithmic considerations, the output descriptions must be full of adjectives, descriptives, qualifiers, and so forth to create mental images. Implementing sounds and smells are the easiest since they are not focused senses. By focused, I mean that if a specific room has a general smell, then the player should smell it at any location of the room. Similarly, the same goes for sounds. If the room has music playing, then the music will be heard in any part of the room. Vision is the most complex because it is more focused than sound and smell and is much harder to implement. Let's take a look at all three senses and see how to implement them.

Sound

There are two types of sounds in a text game: ambient and dynamic. Ambient sounds are the sounds that are always in a room, and dynamic sounds are sounds that can enter and leave a room. First, let's talk about the ambient sounds. To implement ambient sounds, there should be a data structure that contains a set of descriptive strings for each room. Then when the player asks to "listen," these strings are printed out by testing the room the player is standing within and using this information to select the correct set of strings. For example, if the player types in listen in a machine shop, then this might be the monologue he/she is presented with:

What do you want to do? Listen

…You hear the sounds of large machines all around you. The sounds are so strong and piercing you feel them in your teeth. However, beyond all the sounds of the large machines, somewhere in the background you here a peculiar hum, but you're not sure what it is…

The data structure containing these static strings is up to you, but I suggest either an array of strings with a field that describes which room the string is for, or an array of structures with a structure for each room. For example, Listing 14.10 is the ambient sound string data structure used for Shadow Land along with the sounds for the game.

Listing 14.10 The Static Data Structure Used to Contain the Informational Strings in Shadow Land Along with the Ambient Sounds in the Game
// this is the structure used to hold a single string that is used to
// describe something in the game like a smell, sight, sound...

typedef struct info_string_typ
        {
        char type;        // the type of info string i.e. what does it describe
        char string[100]; // the actual description string
        }  info_string, *info_string_ptr;

// these info strings hold the smells in each room
info_string smells[]={

{'l',"You smell the sweet odor of Jasmine with/
 an undertone of potpourri. "} ,
{'b',"The sweet smell of perfume dances within/
 your nostrils...Realities possibly. "} ,
{'k',"You take a deep breath and your senses/
 are tantalized with the smell of"} ,
{'k',"tender breasts of chicken marinating in/
 a garlic sauce. Also, there is "} ,
{'k',"a sweet berry smell emanating from the oven./
                          "} ,
{'w',"You are almost overwhelmed by the smell of/
 bathing fragrance as you"} ,
{'w',"inhale.                                    /
                          "} ,
{'h',"You smell nothing to make note of. "} ,
{'r',"Your nose is filled with steam and the smell/
 of baby oil... "} ,
{'e',"You smell pine possible from the air coming/
 through a small orifice near"} ,
{'e',"the front door.                            /
                                "} ,
{'o',"You are greeted with the familiar odor of /
burning electronics. As you inhale"} ,
{'o',"a second time, you can almost taste the/
 rustic smell of aging books.        "} ,
{'X',""} , // terminate
} ;

As you can see, there are strings for each room in the game and the end of the strings is delineated with an 'X' character. This is just one way to do things, but it works for me!

Dynamic sounds are more complex to implement than static ones since they can move around the environment. Shadow Land has no dynamic objects, but I will explain how to implement dynamic sound. Each object that can move around the universe has a sound attached to it, which is just a string that describes the sound the object makes. Hence, when a player asks to listen to the sounds in a room, first the static sound is printed out, and then it is determined what objects are in the room. Their "sounds" are printed out as well after the static portion of the text. Of course, you should try and make the sentences "connect" together with some kind of conjunction so that the sounds don't look like a bullet list!

Smell

The sense of smell is programmed in the exact same way sounds are, except that the text strings should describe the smells in the room instead of the sounds. Moreover, dynamic smells are implemented in the same way as described above: as an "attachment" to the dynamic objects. For example, an ogre might have a bad smell attached to it that is described along with the static smell of the room. This would be determined by testing if the ogre is in the same room as the player. That is easy since we can look at the position of the ogre and of the player, index into the universe map, and see if they are on the same cell type. Before moving on, there is one artistic aspect of describing smells we should touch on. If something smells bad, try to make it sound as if it smells bad in a good way, get my drift?

Sight

Implementing vision in a text game is an interesting problem. We want to make a virtual character "see" in a virtual world that isn't even visible to the player! Well, being the software sorcerers that we are, this is no problem at all if the correct data structures and approach are used. First, the easiest data structure to implement sight with is the map world since it is a 2-D version of the world the player is walking around in. The algorithm is easy also; all we have to do is understand how we (ourselves) see, and then implement a version of our sight within the text game that is based on the data structures in the game.

To begin with, there is static vision and dynamic object base vision. The static vision is taken care of as we have seen and is very general. If a player asks to "look" at a room, then the first part of the description would be a static one that is always the same. The second part of the description might focus on what's within the player's virtual field of view or VFOV (I like acronyms). This is the hard part. We must somehow scan in front of the player and detect if objects are within this scan space. Unlike the smell and sound test, we just can't check if the dynamic object is within the room since it might be behind the player. We must instead see if it's within the room in conjunction with being within the VFOV.

To test if an object is within the VFOV of view of the player, we need to know five things:

  1. The position of the player

  2. The direction of the player

  3. The positions of all the dynamic objects

  4. The depth of the scan (distance)

  5. The view angle of the scan

Numbers 1, 2, and 3 are easy. We can find that information by looking at the data structures for the player and all the dynamic objects. The only question arises when we consider numbers 4 and 5. First, the depth means how far should the player be able to see? This is relative, but as a rule of thumb, the player should be able to see at least the length of the largest room. The second factor, "view angle," is really the field of view and simply means the angle at which the scan will take place (see Figure 14.6). Now, since the player can only be facing four directions (North, South, East, and West), the scan becomes very simple. All that is needed are two for loops to accomplish the scan.

Figure 14.6. The virtual vision system in action.

graphics/14fig06.gif

The scan will have a shape of an upside-down pyramid, and the view and or field of view will be 90 degrees. This is close enough to a normal human's field of view and is realistic for a game. Hence, the scan will emanate out from the player's position and test each cell in the scan (see Figure 14.7). In general, one for loop will control the X-axis deflection of the scan and one for loop will control the Y-axis deflection. The scan will continue until a specified depth is reached (which is the distance) and then the vision scan will be complete. Now, as the vision scan is running, tests are made to see if any of the blocks within vision contain a dynamic object. If this is true, then each object is "tagged" and placed in a list. When the vision strings print out, the list is referred to and the specific visual strings for each dynamic object are printed out along with the general static view.

Figure 14.7. A close up of the player's vision scan.

graphics/14fig07.gif

Shadow Land uses this technique to "see" in a room. As an example, Listing 14.11 is the vision code that takes care of vision in the northern direction. Pay close attention to the structure of the for loops and the tests. Don't worry if some of the variables or defines are not visible; just try to understand the overall operation of the code fragment.

Listing 14.11 A Code Fragment from Shadow Lands That Produces a Vision Scan in the Northern Direction
case NORTH:
{
// scan like this
//  .....
//   ...
//    P
for (y=you.y,scan_level=0; y>=(you.y-depth); y--,scan_level++)
    {
    for (x=you.x-scan_level; x<=you.x+scan_level; x++)
        {
        // x,y is test point, make sure it is within the universe
        // boundaries and within the same room
        if (x>=1 && x<NUM_COLUMNS-1 &&
           y>=1 && x<NUM_ROWS-1 &&
           universe_geometry[y][x]==universe_geometry[you.y][you.x])
           {
           // test to see if square has an object in it
           if (universe_objects[y][x]!=' ')
              {
              // insert the object into object list
              stuff[*num_objects].thing = universe_objects[y][x];
              stuff[*num_objects].x     = x;
              stuff[*num_objects].y     = y;
              // increment the number of objects
             (*num_objects)++;

             }  // end if an object was found
            }  // end if in boundaries
          }  // end for x
      }  // end for y
// return number of objects found
return(*num_objects);

}  break;

The function works by scanning the area in front of the player and testing each block in the scan to see if it is within the universe. If the block is within the universe, then the object's data structure is referred to see if an object is sitting on the block. If so, then the object is inserted into a list and the code proceeds until the scan is done. You should pay close attention to the bounding code that determines if the scanned block is within the universe. This is absolutely needed since the scan takes place at a given distance, the code doesn't have any way of determining if it has gone out of bounds of the array or in the negative direction. This is why a "filter" is needed to condition and test each test block position that is generated by the double for loops. Finally, when you print out the dynamic objects after the static visual, make sure that they read in a fluid manner!

      Previous Section Next Section
    



    JavaScript EditorAjax Editor     JavaScript Editor