Send feedback/effect to joystick from .net

2020-03-26 06:40发布

问题:

Thanks to this answer https://stackoverflow.com/a/13734766/637142 I am able to know when a button is pressed or when the steering wheel is rotated. Now my question is how do I send an effect to the device? For example when I am playing a game if I crash the wheel will vibrate. How could I make the steering wheel vibrate?

I belive what I need to do is to Start() an effect (http://sharpdx.org/documentation/api/t-sharpdx-directinput-effect). The SharpDX.DirectInput.Joystick class does not seem to have a method to return me all the effects. There is a method called GetEffects but that method returns a collection of EffectInfo objects. How does a game sends commands to the joystick?

回答1:

The source code is copy-pasted from here.

To use this source you need a "Force Effect file" (C:\MyEffectFile.ffe), to "play" it on the joystick.

According to this book to create the "force effect" file you need to use the "Force Editor" that came with DirectX SDK.

(the same book, alternatively, state that you can create the effect from scratch in the code... down in the answer I've found another piece of code that create and use an effect without loading it from a file :-))

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX;
using Microsoft.DirectX.DirectInput;

namespace JoystickProject
{
    /// <summary>
    /// Summary description for Form1.
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.Label label1;
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;
        private Device device = null;
        private bool running = true;
        private ArrayList effectList = new ArrayList();
        private bool button0pressed = false;
        private string joyState = "";

        public bool InitializeInput()
        {
            // Create our joystick device
            foreach(DeviceInstance di in Manager.GetDevices(DeviceClass.GameControl,
                EnumDevicesFlags.AttachedOnly | EnumDevicesFlags.ForceFeeback))
            {
                // Pick the first attached joystick we see
                device = new Device(di.InstanceGuid);
                break;
            }
            if (device == null) // We couldn't find a joystick
                return false;

            device.SetDataFormat(DeviceDataFormat.Joystick);
            device.SetCooperativeLevel(this, CooperativeLevelFlags.Exclusive | CooperativeLevelFlags.Background);
            device.Properties.AxisModeAbsolute = true;
            device.Properties.AutoCenter = false;
            device.Acquire();

            // Enumerate any axes
            foreach(DeviceObjectInstance doi in device.Objects)
            {
                if ((doi.ObjectId & (int)DeviceObjectTypeFlags.Axis) != 0)
                {
                    // We found an axis, set the range to a max of 10,000
                    device.Properties.SetRange(ParameterHow.ById,
                        doi.ObjectId, new InputRange(-5000, 5000));
                }
            }

            // Load our feedback file
            EffectList effects = null;
            effects = device.GetEffects(@"C:\MyEffectFile.ffe",
                FileEffectsFlags.ModifyIfNeeded);
            foreach(FileEffect fe in effects)
            {
                EffectObject myEffect = new EffectObject(fe.EffectGuid, fe.EffectStruct,
                    device);
                myEffect.Download();
                effectList.Add(myEffect);
            }

            while(running)
            {
                UpdateInputState();
                Application.DoEvents();
            }

            return true;
        }

        private void PlayEffects()
        {
                // See if our effects are playing.
                foreach(EffectObject myEffect in effectList)
                {
                    //if (button0pressed == true)
                    //{
                        //MessageBox.Show("Button Pressed.");
                    //  myEffect.Start(1, EffectStartFlags.NoDownload);
                    //}

                    if (!myEffect.EffectStatus.Playing)
                    {
                        // If not, play them
                        myEffect.Start(1, EffectStartFlags.NoDownload);
                    }
                }
                //button0pressed = true;
        }

        protected override void OnClosed(EventArgs e)
        {
            running = false;
        }

        private void UpdateInputState()
        {
            PlayEffects();

            // Check the joystick state
            JoystickState state = device.CurrentJoystickState;
            device.Poll();
            joyState = "Using JoystickState: \r\n";

            joyState += device.Properties.ProductName;
            joyState += "\n";
            joyState += device.ForceFeedbackState;
            joyState += "\n";
            joyState += state.ToString();

            byte[] buttons = state.GetButtons();
            for(int i = 0; i < buttons.Length; i++)
                joyState += string.Format("Button {0} {1}\r\n", i, buttons[i] != 0 ? "Pressed" : "Not Pressed");

            label1.Text = joyState;

            //if(buttons[0] != 0)
                //button0pressed = true;

        }

        public Form1()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            //
            // TODO: Add any constructor code after InitializeComponent call
            //
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.BackColor = System.Drawing.SystemColors.ActiveCaptionText;
            this.label1.Location = new System.Drawing.Point(8, 8);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(272, 488);
            this.label1.TabIndex = 0;
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.BackColor = System.Drawing.SystemColors.ControlText;
            this.ClientSize = new System.Drawing.Size(288, 502);
            this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                          this.label1});
            this.Name = "Form1";
            this.Text = "Joystick Stuff";
            this.ResumeLayout(false);

        }
        #endregion

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() 
        {
            using (Form1 frm = new Form1())
            {
                frm.Show();
                if (!frm.InitializeInput())
                    MessageBox.Show("Couldn't find a joystick.");
            }
        }
    }
}

I've just found here another piece of code that maybe useful. This sample seem to create the effect from scratch, so you shouldn't need an "effect file".

   DeviceList xDeviceList = Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly); 
            DeviceInstance someDeviceInstance; 
            foreach (DeviceInstance deviceInstance in xDeviceList) 
            { 
                someDeviceInstance = deviceInstance; 
                break; 
            } 

            Device someDevice = new Device(someDeviceInstance.InstanceGuid); 
            someDevice.SetCooperativeLevel(this.Handle, CooperativeLevelFlags.Exclusive | CooperativeLevelFlags.Background); 
            int[] axis = new int[0]; 
            foreach (DeviceObjectInstance doi in someDevice.Objects) 
            { 
                if((doi.Flags & (int)ObjectInstanceFlags.Actuator) != 0) 
                { 
                    axis = new int[axis.Length + 1]; 
                    axis[axis.Length - 1] = doi.Offset; 
                } 
            } 

            someDevice.Acquire(); 

            Effect effect = new Effect(); 
            effect.SetDirection(new int[axis.Length]); 
            effect.SetAxes(new int[axis.Length]); 
            effect.ConditionStruct = new Condition[axis.Length]; 

            effect.Flags = EffectFlags.Cartesian | EffectFlags.ObjectOffsets; 
            effect.Duration = int.MaxValue; 
            effect.SamplePeriod = 0; 
            effect.Gain = 10000; 
            effect.TriggerButton = (int)Microsoft.DirectX.DirectInput.Button.NoTrigger; 
            effect.TriggerRepeatInterval = 0; 
            effect.UsesEnvelope = false; 
            effect.EffectType = Microsoft.DirectX.DirectInput.EffectType.ConstantForce; 
            effect.StartDelay = 0; 
            effect.Constant = new Microsoft.DirectX.DirectInput.ConstantForce(); 
            effect.Constant.Magnitude = -5000; 
            EffectObject effectObject = null; 
            foreach (EffectInformation ei in someDevice.GetEffects(EffectType.ConstantForce)) 
            { 
                effectObject = new EffectObject(ei.EffectGuid, effect, someDevice); 
            } 

            effectObject.SetParameters(effect, EffectParameterFlags.Start ); 

And here is another link then may be useful Force feedback sample