test
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Layouts;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine.InputSystem.Editor;
|
||||
using UnityEngine.UIElements;
|
||||
#endif
|
||||
|
||||
// Let's say we want to have a composite that takes an axis and uses
|
||||
// it's value to multiply the length of a vector from a stick. This could
|
||||
// be used, for example, to have the right trigger on the gamepad act as
|
||||
// a strength multiplier on the value of the left stick.
|
||||
//
|
||||
// We start by creating a class that is based on InputBindingComposite<>.
|
||||
// The type we give it is the type of value that we will compute. In this
|
||||
// case, we will consume a Vector2 from the stick so that is the type
|
||||
// of value we return.
|
||||
//
|
||||
// NOTE: By advertising the type of value we return, we also allow the
|
||||
// input system to filter out our composite if it is not applicable
|
||||
// to a specific type of action. For example, if an action is set
|
||||
// to "Value" as its type and its "Control Type" is set to "Axis",
|
||||
// our composite will not be shown as our value type (Vector2) is
|
||||
// incompatible with the value type of Axis (float).
|
||||
//
|
||||
// We can customize the way display strings are formed for our composite by
|
||||
// annotating it with DisplayStringFormatAttribute. The string is simply a
|
||||
// list with elements to be replaced enclosed in curly braces. Everything
|
||||
// outside those will taken verbatim. The fragments inside the curly braces
|
||||
// in this case refer to the binding composite parts by name. Each such
|
||||
// instance is replaced with the display text for the corresponding
|
||||
// part binding.
|
||||
//
|
||||
// NOTE: We don't supply a name for the composite here. The default logic
|
||||
// will take the name of the type ("CustomComposite" in our case)
|
||||
// and snip off "Composite" if used as a suffix (which is the case
|
||||
// for us) and then use that as the name. So in our case, we are
|
||||
// registering a composite called "Custom" here.
|
||||
//
|
||||
// If we were to use our composite with the AddCompositeBinding API,
|
||||
// for example, it would look like this:
|
||||
//
|
||||
// myAction.AddCompositeBinding("Custom")
|
||||
// .With("Stick", "<Gamepad>/leftStick")
|
||||
// .With("Multiplier", "<Gamepad>/rightTrigger");
|
||||
[DisplayStringFormat("{multiplier}*{stick}")]
|
||||
public class CustomComposite : InputBindingComposite<Vector2>
|
||||
{
|
||||
// So, we need two parts for our composite. The part that delivers the stick
|
||||
// value and the part that delivers the axis multiplier. Note that each part
|
||||
// may be bound to multiple controls. The input system handles that for us
|
||||
// by giving us an integer identifier for each part that reads a single value
|
||||
// from however many controls are bound to the part.
|
||||
//
|
||||
// In our case, this could be used, for example, to bind the "multiplier" part
|
||||
// to both the left and the right trigger on the gamepad.
|
||||
|
||||
// To tell the input system of a "part" binding that we need for a composite,
|
||||
// we add a public field with an "int" type and annotated with an [InputControl]
|
||||
// attribute. We set the "layout" property on the attribute to tell the system
|
||||
// what kind of control we expect to be bound to the part.
|
||||
//
|
||||
// NOTE: These part binding need to be *public fields* for the input system
|
||||
// to find them.
|
||||
//
|
||||
// So this is introduces a part to the composite called "multiplier" and
|
||||
// expecting an "Axis" control. The value of the field will be set by the
|
||||
// input system. It will be some internal, unique numeric ID for the part
|
||||
// which we can then use with InputBindingCompositeContext.ReadValue to
|
||||
// read out the value of just that part.
|
||||
[InputControl(layout = "Axis")]
|
||||
public int multiplier;
|
||||
|
||||
// The other part we need is for the stick.
|
||||
//
|
||||
// NOTE: We could use "Stick" here but "Vector2" is a little less restrictive.
|
||||
[InputControl(layout = "Vector2")]
|
||||
public int stick;
|
||||
|
||||
// We may also expose "parameters" on our composite. These can be configured
|
||||
// graphically in the action editor and also through AddCompositeBinding.
|
||||
//
|
||||
// Let's say we want to allow the user to specify an additional scale factor
|
||||
// to apply to the value of "multiplier". We can do so by simply adding a
|
||||
// public field of type float. Any public field that is not annotated with
|
||||
// [InputControl] will be treated as a possible parameter.
|
||||
//
|
||||
// If we added a composite with AddCompositeBinding, we could configure the
|
||||
// parameter like so:
|
||||
//
|
||||
// myAction.AddCompositeBinding("Custom(scaleFactor=0.5)"
|
||||
// .With("Multiplier", "<Gamepad>/rightTrigger")
|
||||
// .With("Stick", "<Gamepad>/leftStick");
|
||||
public float scaleFactor = 1;
|
||||
|
||||
// Ok, so now we have all the configuration in place. The final piece we
|
||||
// need is the actual logic that reads input from "multiplier" and "stick"
|
||||
// and computes a final input value.
|
||||
//
|
||||
// We can do that by defining a ReadValue method which is the actual workhorse
|
||||
// for our composite.
|
||||
public override Vector2 ReadValue(ref InputBindingCompositeContext context)
|
||||
{
|
||||
// We read input from the parts we have by simply
|
||||
// supplying the part IDs that the input system has set up
|
||||
// for us to ReadValue.
|
||||
//
|
||||
// NOTE: Vector2 is a less straightforward than primitive value types
|
||||
// like int and float. If there are multiple controls bound to the
|
||||
// "stick" part, we need to tell the input system which one to pick.
|
||||
// We do so by giving it an IComparer. In this case, we choose
|
||||
// Vector2MagnitudeComparer to return the Vector2 with the greatest
|
||||
// length.
|
||||
var stickValue = context.ReadValue<Vector2, Vector2MagnitudeComparer>(stick);
|
||||
var multiplierValue = context.ReadValue<float>(multiplier);
|
||||
|
||||
// The rest is simple. We just scale the vector we read by the
|
||||
// multiple from the axis and apply our scale factor.
|
||||
return stickValue * (multiplierValue * scaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
// Our custom composite is complete and fully functional. We could stop here and
|
||||
// call it a day. However, for the sake of demonstration, let's say we also want
|
||||
// to customize how the parameters for our composite are edited. We have "scaleFactor"
|
||||
// so let's say we want to replace the default float inspector with a slider.
|
||||
//
|
||||
// We can replace the default UI by simply deriving a custom InputParameterEditor
|
||||
// for our composite.
|
||||
#if UNITY_EDITOR
|
||||
public class CustomCompositeEditor : InputParameterEditor<CustomComposite>
|
||||
{
|
||||
public override void OnGUI()
|
||||
{
|
||||
// Using the 'target' property, we can access an instance of our composite.
|
||||
var currentValue = target.scaleFactor;
|
||||
|
||||
// The easiest way to lay out our UI is to simply use EditorGUILayout.
|
||||
// We simply assign the changed value back to the 'target' object. The input
|
||||
// system will automatically detect a change in value.
|
||||
target.scaleFactor = EditorGUILayout.Slider(m_ScaleFactorLabel, currentValue, 0, 2);
|
||||
}
|
||||
|
||||
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
|
||||
public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback)
|
||||
{
|
||||
var slider = new Slider(m_ScaleFactorLabel.text, 0, 2)
|
||||
{
|
||||
value = target.scaleFactor,
|
||||
showInputField = true
|
||||
};
|
||||
|
||||
// Note: For UIToolkit sliders, as of Feb 2022, we can't register for the mouse up event directly
|
||||
// on the slider because an element inside the slider captures the event. The workaround is to
|
||||
// register for the event on the slider container. This will be fixed in a future version of
|
||||
// UIToolkit.
|
||||
slider.Q("unity-drag-container").RegisterCallback<MouseUpEvent>(evt =>
|
||||
{
|
||||
target.scaleFactor = slider.value;
|
||||
onChangedCallback?.Invoke();
|
||||
});
|
||||
|
||||
root.Add(slider);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private GUIContent m_ScaleFactorLabel = new GUIContent("Scale Factor");
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user