游戏手柄
可参考的官方例子:
gamepad_input
,
gamepad_input_events
.
Bevy 支持游戏手柄输入硬件:游戏机控制器、游戏手柄等。许多不同种类的硬件应该都能工作,但如果你的设备不被支持,你应该向 gilrs 项目提交一个 issue。
手柄 ID
Bevy 为每个连接的游戏手柄分配一个唯一的 ID(Gamepad
)。这可以让你把设备与特定的玩家联系起来,并区分你的输入是来自哪一个玩家。
你可以使用 Gamepads
资源 来列出所有当前连接的手柄设备的 ID,或者检查某个特定设备的状态。
为了检测游戏手柄何时连接或断开,你可以使用 GamepadEvent
事件。
展示如何记住第一个连接成功的手柄 ID 的例子:
/// Simple resource to store the ID of the connected gamepad.
/// We need to know which gamepad to use for player input.
struct MyGamepad(Gamepad);
fn gamepad_connections(
mut commands: Commands,
my_gamepad: Option<Res<MyGamepad>>,
mut gamepad_evr: EventReader<GamepadEvent>,
) {
for GamepadEvent(id, kind) in gamepad_evr.iter() {
match kind {
GamepadEventType::Connected => {
println!("New gamepad connected with ID: {:?}", id);
// if we don't have any gamepad yet, use this one
if my_gamepad.is_none() {
commands.insert_resource(MyGamepad(*id));
}
}
GamepadEventType::Disconnected => {
println!("Lost gamepad connection with ID: {:?}", id);
// if it's the one we previously associated with the player,
// disassociate it:
if let Some(MyGamepad(old_id)) = my_gamepad.as_deref() {
if old_id == id {
commands.remove_resource::<MyGamepad>();
}
}
}
// other events are irrelevant
_ => {}
}
}
}
处理手柄输入
你可以用 Axis<GamepadAxis>
(Axis
, GamepadAxis
)来处理模拟摇杆和触发开关。按键可以用 Input<GamepadButton>
(Input
,
GamepadButton
) 来处理,类似于鼠标按键或键盘按键。
注意,GamepadButton
中的按键名称是厂商中立的(比如使用 South
和 East
,而非 X/O
和 A/B
)。
fn gamepad_input(
axes: Res<Axis<GamepadAxis>>,
buttons: Res<Input<GamepadButton>>,
my_gamepad: Option<Res<MyGamepad>>,
) {
let gamepad = if let Some(gp) = my_gamepad {
// a gamepad is connected, we have the id
gp.0
} else {
// no gamepad is connected
return;
};
// The joysticks are represented using a separate axis for X and Y
let axis_lx = GamepadAxis(gamepad, GamepadAxisType::LeftStickX);
let axis_ly = GamepadAxis(gamepad, GamepadAxisType::LeftStickY);
if let (Some(x), Some(y)) = (axes.get(axis_lx), axes.get(axis_ly)) {
// combine X and Y into one vector
let left_stick_pos = Vec2::new(x, y);
// Example: check if the stick is pushed up
if left_stick_pos.length() > 0.9 && left_stick_pos.y > 0.5 {
// do something
}
}
// In a real game, the buttons would be configurable, but here we hardcode them
let jump_button = GamepadButton(gamepad, GamepadButtonType::South);
let heal_button = GamepadButton(gamepad, GamepadButtonType::East);
if buttons.just_pressed(jump_button) {
// button just pressed: make the player jump
}
if buttons.pressed(heal_button) {
// button being held down: heal the player
}
}
你也可以使用 GamepadEvent
事件 来处理游戏手柄的输入:
fn gamepad_input_events(
my_gamepad: Option<Res<MyGamepad>>,
mut gamepad_evr: EventReader<GamepadEvent>,
) {
let gamepad = if let Some(gp) = my_gamepad {
// a gamepad is connected, we have the id
gp.0
} else {
// no gamepad is connected
return;
};
for GamepadEvent(id, kind) in gamepad_evr.iter() {
if id.0 != gamepad.0 {
// event not from our gamepad
continue;
}
use GamepadEventType::{AxisChanged, ButtonChanged};
match kind {
AxisChanged(GamepadAxisType::RightStickX, x) => {
// Right Stick moved (X)
}
AxisChanged(GamepadAxisType::RightStickY, y) => {
// Right Stick moved (Y)
}
ButtonChanged(GamepadButtonType::DPadDown, val) => {
// buttons are also reported as analog, so use a threshold
if *val > 0.5 {
// button pressed
}
}
_ => {} // don't care about other inputs
}
}
}
手柄设置
你可以使用 GamepadSettings
资源 来配置各个轴和按键的死区和其他参数。你可以设置全局默认值,也可以单独设置每个轴和按键。
下面是一个例子,展示了如何用自定义设置来配置游戏手柄(不一定是好的设置,请不要盲目地复制这些设置):
// this should be run once, when the game is starting
// (transition entering your in-game state might be a good place to put it)
fn configure_gamepads(
my_gamepad: Option<Res<MyGamepad>>,
mut settings: ResMut<GamepadSettings>,
) {
let gamepad = if let Some(gp) = my_gamepad {
// a gamepad is connected, we have the id
gp.0
} else {
// no gamepad is connected
return;
};
// add a larger default dead-zone to all axes (ignore small inputs, round to zero)
settings.default_axis_settings.negative_low = -0.1;
settings.default_axis_settings.positive_low = 0.1;
// make the right stick "binary", squash higher values to 1.0 and lower values to 0.0
let right_stick_settings = AxisSettings {
positive_high: 0.5, // values 0.5 to 1.0, become 1.0
positive_low: 0.5, // values 0.0 to 0.5, become 0.0
negative_low: -0.5, // values -0.5 to 0.0, become 0.0
negative_high: -0.5, // values -1.0 to -0.5, become -1.0
// the raw value should change by at least this much,
// for Bevy to register an input event:
threshold: 0.01,
};
// make the triggers work in big/coarse steps, to get fewer events
// reduces noise and precision
let trigger_settings = AxisSettings {
threshold: 0.2,
// also set some conservative deadzones
positive_high: 0.8,
positive_low: 0.2,
negative_high: -0.8,
negative_low: -0.2,
};
// set these settings for the gamepad we use for our player
settings.axis_settings.insert(
GamepadAxis(gamepad, GamepadAxisType::RightStickX),
right_stick_settings.clone()
);
settings.axis_settings.insert(
GamepadAxis(gamepad, GamepadAxisType::RightStickY),
right_stick_settings.clone()
);
settings.axis_settings.insert(
GamepadAxis(gamepad, GamepadAxisType::LeftZ),
trigger_settings.clone()
);
settings.axis_settings.insert(
GamepadAxis(gamepad, GamepadAxisType::RightZ),
trigger_settings.clone()
);
// for buttons (or axes treated as buttons), make them less sensitive
let button_settings = ButtonSettings {
// require them to be pressed almost all the way, to count
press: 0.9,
// require them to be released almost all the way, to count
release: 0.1,
};
settings.default_button_settings = button_settings;
}