Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning CSharp Game Programming (2005) [eng]

.pdf
Скачиваний:
151
Добавлен:
16.08.2013
Размер:
4.97 Mб
Скачать

210 Chapter 8 DirectInput

Force Feedback

When I was a kid, there was an arcade game named After Burner. It was this really cool machine that you could sit in, and when you moved the controller around, the whole machine would move around, making you feel like you were actually flying the plane. Sort of. It may not have exactly replicated the 6Gs you’d get by pulling up as hard as you could while flying at a thousand miles an hour in a fighter jet, but even the limited movement was far more realistic than anything I’d ever felt or seen before. When you got hit by missiles or bullets, the whole machine would rumble and you really felt like you just hit something. This was why arcades were so much better than going home and playing the game on the Sega Genesis—you got that whole immersion experience.

Games you played at home on your computer and video game consoles didn’t have anything like this full experience until years later, when Nintendo made the Rumble Pak, a little device that would sit in your N64 controller and vibrate whenever certain events occurred, like collisions. Nintendo made the N64 able to send data back to the controller, so that it became more than just an input device.

This rumble effect was the beginning of a whole revolution in game input technology, called force feedback. The idea of force feedback is to make the controller interactive—to send data back to the user. If you’re flying a jet and your rudders are damaged, then the joystick may give you some resistance when you are trying to pull it up. If you’re flying in a turbulent patch of air, the controls will shake; or if you’re driving on a bumpy road, your steering wheel will pop up and down.

Force feedback makes your computer games much more realistic, and DirectInput makes it incredibly easy to use.

The Effect Editor

The DirectX SDK comes with a program called fedit.exe, which allows you to create various force feedback effects and save them to disk. It’s really a simple program, and it makes your life easier; it even comes with a whole bunch of built-in effects, as well. The effects are stored in files called FFE files, for force feedback effects.

Figure 8.5 is a screenshot of the program in action.

The editor supports a number of different kinds of effects, the simplest of which is the constant force effect, which will apply a single constant force in the direction of your choosing.

If you insert one of these effects, it will point north by default and use the maximum power. If you press the Play button, you’ll see the stick of your device move backward immediately and stay there until the effect ends. You can change the direction of the effect by going into its properties and messing around with the options in there.

Force Feedback

211

Figure 8.5 The DXSDK Force Feedback Editor program

There are a bunch of other effect types as well, such as a ramp effect that will linearly move the stick from one direction to another based on time, or various repeating patterns (sine, sawtooth, square) that move the stick back and forth according to a particular algorithm.

Then there are effects that make it hard for you to move the handle around, like friction, damper, and so on. Just play around with them and see what you can do.

Loading Effects

DirectInput allows you to load in effects from a file and play them, but the process is somewhat difficult, so I’ve created a helper class to help you along. The class is called AdvancedFramework.ForceEffect, and it can be found in the directory of Demo 8.4.

Essentially, an FFE file contains a list of effects, where each effect can be any of the effects I mentioned previously, like a sine wave effect, or a constant force effect. So the ForceEffect class will store an array of these effects, as well as the name of the effect:

public class ForceEffect

{

System.Collections.ArrayList effectlist;

string name;

public string Name

{

212 Chapter 8 DirectInput

get { return name; }

}

<code snipped>

}

Once you have all that, you can pass the filename of an FFE file into the constructor, as well as the name of the effect, and the device that you’re going to be using it on:

public ForceEffect( string filename, string name, Device device )

{

// save the name

this.name = name;

effectlist = new System.Collections.ArrayList();

This just initializes the name and the list of effects.

Next, you load a list of EffectFiles from disk:

EffectList effects;

effects = device.GetEffects( filename, FileEffectsFlags.ModifyIfNeeded );

An EffectList is just a list of EffectFiles, which are structures that describe an effect inside an FFE file. Once you have that list, you need to look at each one and load it into an

EffectObject object:

foreach( FileEffect e in effects )

{

EffectObject effect = new EffectObject( e.EffectGuid, e.EffectStruct, device );

effectlist.Add( effect );

}

}

Now effectlist has a bunch of EffectObjects in it, and that represents the entire FFE file.

Playing Effects

Once you have an effect list loaded, you need some way to actually play the effects; you do so with the Play function:

public void Start( int iterations, bool restart )

{

foreach( EffectObject effect in effectlist )

{

if( !effect.EffectStatus.Playing || restart )

{

Force Feedback

213

effect.Start( iterations, 0 );

}

}

}

The function takes two parameters: the number of iterations to make, and whether or not the effect should be restarted. For each EffectObject inside the list, the function checks to see if the effect is already playing. If it is, and you don’t want it reset, then the effect isn’t played; otherwise it is. Calling the Start function on an effect automatically resets it, so that’s why you need to check it.

The Start function takes the number of times you want to repeat the effect as its first parameter, and a flag as its second. There are only two valid flags: EffectStartFlags.NoDownload, which disables auto-downloading of effects into the device (I’m not sure why anyone would want that), and EffectStartFlags.Solo, which stops everything the device is playing and starts the new effect. You probably won’t want to use either of those flags, but it’s good to know that they exist.

Stopping Effects

The next thing the class manages is the ability to stop playing an effect; this is an extremely simple process:

public void Stop()

{

foreach( EffectObject effect in effectlist )

{

effect.Stop();

}

}

Demo 8.4

Demo 8.4 is designed to show you how to use force feedback effects. It’s pretty simple, as most of the hard work is already done within the ForceEffect class.

The first thing the demo does is declare some new variables:

ForceEffect[] effects;

string instructions;

int currenteffect;

bool lastfiring = false;

bool lastchanging = false;

The first is an array of effects, which will store the effects. Next are the instructions, which tell the user how to use the demo.

214Chapter 8 DirectInput

The currenteffect variable stores the index of the current effect that is being used, and the last two Booleans are used to determine whether a button state has changed since the last frame. You’ll see how these work in just a bit.

Setting Up the Device

There are two changes you need to make to your code that sets up a DirectInput device: you must make it search for force-feedback devices, and make it acquire the device in exclusive mode. This should probably be obvious to you, but I’ll say it anyway: trying to run force feedback effects on a non-force feedback device will cause errors.

DirectInput requires that force feedback effects be used only on exclusive devices—two programs can’t be using a device at the same time if you’re using feedback effects. So here’s the new setup code:

foreach( DirectInput.DeviceInstance i in

DirectInput.Manager.GetDevices(

DirectInput.DeviceClass.GameControl,

DirectInput.EnumDevicesFlags.AttachedOnly |

DirectInput.EnumDevicesFlags.ForceFeeback ) )

{

gameinput = new DirectInput.Device( i.InstanceGuid );

gameinput.SetCooperativeLevel(

this,

DirectInput.CooperativeLevelFlags.Background |

DirectInput.CooperativeLevelFlags.Exclusive );

gameinput.SetDataFormat( DirectInput.DeviceDataFormat.Joystick );

<code snipped>

}

The two important changes from Demo 8.3 are in bold.

Initializing the Effects

Thanks to the handy-dandy ForceEffect class, loading FFE files is a snap!

effects = new ForceEffect[3];

 

 

effects[0] = new ForceEffect(

“gatling.ffe”, “Gatling

Gun”, gameinput );

effects[1]

=

new

ForceEffect(

“pistol.ffe”, “Pistol”,

gameinput );

effects[2]

=

new

ForceEffect(

“shotgun3.ffe”, “Shotgun”, gameinput );

That loads three FFE files meant to emulate Gatling gun fire, pistol fire, and shotgun fire. All three of these came with the DXSDK, and there are plenty more where they came from.

FLY

Force Feedback 215

TEAM

 

Gathering Input and Playing Output

 

The final step is to actually gather input and play the feedback out to the device, which is accomplished inside ProcessInput:

DirectInput.JoystickState state = gameinput.CurrentJoystickState;

if( state.GetButtons()[0] != 0 )

{

if( lastfiring == false ) effects[currenteffect].Start( 1, true );

lastfiring = true;

}

else

{

if( lastfiring == true ) effects[currenteffect].Stop();

lastfiring = false;

}

This first chunk of code returns the joystick state and checks to see whether button 0 is pressed. If it is, then it checks to see if lastfiring is false. If lastfiring is false, that means that the last time input was gathered, button 0 was up and this time it is down—meaning the button was just pressed. Therefore, the program tells the current effect to start playing.

The reason for the lastfiring Boolean is to tell exactly when a button is pressed down. Chances are, if you quickly press a button down, about 10–20 frames will be processed before you let go, and without that check you’d end up starting the current effect 10–20 times instead of just once. Likewise, the function figures out when you’ve stopped pressing the button and stops the effect.

The second joystick button is used in a similar manner to change the current effect:

if( state.GetButtons()[1] != 0 )

{

if( lastchanging == false )

{

currenteffect++;

if( currenteffect >= effects.Length ) currenteffect = 0;

}

lastchanging = true;

}

else

{

216 Chapter 8 DirectInput

lastchanging = false;

}

And finally, the instructions string is constructed:

instructions = “Press Button 0 to play an effect\n”; instructions += “Press Button 1 to change the effect\n”; instructions += “Current effect: “ + effects[currenteffect].Name;

This demo produces the output shown in Figure 8.6.

Figure 8.6 The output from Demo 8.4

Summary

Who would have thought there was so much to be learned about input programming? It’s certainly gotten a lot more complex than the olden days, when you simply just asked the keyboard what keys were pressed down. And I’ve got some news for you, too: DirectInput is even more complex than what you’ve seen here. DirectInput supports event-based input gathering, which I didn’t show you because it requires knowledge of threading, which is a topic I didn’t have room to get to in this book. But you should now know enough about DirectInput to be able to write code for almost any input device you want to use.

What You Learned

The main concepts that you should have picked up from this chapter are:

How to get information from keyboards.

How to get information from mice.

Summary 217

How to get information from game devices like joysticks and steering wheels.

How to use force feedback to send data back to a game device.

Review Questions

These review questions test your knowledge on the important concepts exposed to you in this chapter. The answers can be found in Appendix A.

8.1.DirectInput supports two methods of input gathering. What method was shown in this chapter?

8.2.True or False: A keyboard will tell you about every key that is pressed down when you poll it.

8.3.What is the third axis typically used for on a mouse?

8.4.Name the four different types of input objects that a game device can have.

8.5.Can force feedback devices be created non-exclusively?

On Your Own

There were a few major topics that I didn’t get to in this chapter. One of them is action mapping. Action mapping is an advanced concept that allows you to associate certain events on a device to actions in your game, and it makes re-mapping input keys in your game extremely easy to do. You should look into this concept on your own, and play around with it.

You might also want to look into multithreading and using event-based input handling, in order to get an idea of how they work.

This page intentionally left blank

chapter 9

DirectSound

You know how to show graphics and how to get user input, but you still haven’t learned one major portion of game programming: how to use sound in your games. In this chapter, I’ll remedy that situation by teaching you the basics of DirectSound, DirectX’s sound API.

In this chapter, you will learn:

How to create a sound device.

How to load a wave from disk.

How to play a sound buffer.

How to fine-tune your buffers.

How to process sound effects.

How to use 3D sound.

The Sound Device

As with Direct3D and DirectInput, the fundamental class in DirectSound is Device, which represents your sound card.

Creating a sound device is really quite simple:

DirectInput.Device sound;

sound = new DirectSound.Device();

sound.SetCooperativeLevel( this, DirectSound.CooperativeLevel.Priority );

Just create a sound device and set the cooperation level. Remember that the sound device is shared across the entire operating system, so you have to tell it how you want to cooperate with other programs. For a game, you’re going to want to use the Priority level

because it gives you a decent level of access to the sound card.

219