mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 03:15:43 +08:00
Added a ButtonComponent (#1146)
* Added a ButtonComponent * Don't import VoidCallback * Change epsilon since test is flaky * Added SpriteButtonComponent * Add changelog entry * Add docs
This commit is contained in:
@ -117,6 +117,19 @@ If you want to act upon the button being pressed (which would be the common thin
|
||||
a callback function as the `onPressed` argument, or you extend the component and override
|
||||
`onTapDown`, `onTapUp` and/or `onTapCancel` and implement your logic there.
|
||||
|
||||
## SpriteButtonComponent
|
||||
|
||||
A `SpriteButtonComponent` is a button that is defined by two `Sprite`s, one that represents
|
||||
when the button is pressed and one that represents when the button is released.
|
||||
|
||||
## ButtonComponent
|
||||
|
||||
A `ButtonComponent` is a button that is defined by two `PositionComponent`s, one that represents
|
||||
when the button is pressed and one that represents when the button is released. If you only want
|
||||
to use sprites for the button, use the [](#SpriteButtonComponent) instead, but this component can be
|
||||
good to use if you for example want to have a `SpriteAnimationComponent` as a button, or anything
|
||||
else which isn't a pure sprite.
|
||||
|
||||
## Gamepad
|
||||
|
||||
Flame has a separate plugin to support external game controllers (gamepads), check
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:flame/input.dart';
|
||||
import 'package:flame/palette.dart';
|
||||
import 'package:flame/sprite.dart';
|
||||
import 'package:flutter/animation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
|
||||
import 'joystick_player.dart';
|
||||
@ -112,6 +113,50 @@ class JoystickAdvancedExample extends FlameGame
|
||||
),
|
||||
);
|
||||
|
||||
// A button, created from a shape, that adds a scale effect to the player
|
||||
// when it is pressed.
|
||||
final buttonComponent = ButtonComponent(
|
||||
button: RectangleComponent(
|
||||
size: Vector2(185, 50),
|
||||
paint: Paint()
|
||||
..color = Colors.orange
|
||||
..style = PaintingStyle.stroke,
|
||||
),
|
||||
buttonDown: RectangleComponent(
|
||||
size: Vector2(185, 50),
|
||||
paint: BasicPalette.magenta.paint(),
|
||||
),
|
||||
position: Vector2(20, size.y - 280),
|
||||
onPressed: () => player.add(
|
||||
ScaleEffect(
|
||||
scale: Vector2.all(1.5),
|
||||
duration: 1.0,
|
||||
isAlternating: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final buttonSprites = await images.load('buttons.png');
|
||||
final buttonSheet = SpriteSheet.fromColumnsAndRows(
|
||||
image: buttonSprites,
|
||||
columns: 1,
|
||||
rows: 2,
|
||||
);
|
||||
|
||||
// A sprite button, created from a shape, that adds a opacity effect to the
|
||||
// player when it is pressed.
|
||||
final spriteButtonComponent = SpriteButtonComponent(
|
||||
button: buttonSheet.getSpriteById(0),
|
||||
buttonDown: buttonSheet.getSpriteById(1),
|
||||
position: Vector2(20, size.y - 360),
|
||||
size: Vector2(185, 50),
|
||||
onPressed: () => player.add(
|
||||
OpacityEffect.fadeOut(
|
||||
isAlternating: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final _regular = TextPaint(
|
||||
style: TextStyle(color: BasicPalette.white.color),
|
||||
);
|
||||
@ -142,6 +187,8 @@ class JoystickAdvancedExample extends FlameGame
|
||||
add(joystick);
|
||||
add(flipButton);
|
||||
add(flopButton);
|
||||
add(buttonComponent);
|
||||
add(spriteButtonComponent);
|
||||
add(shapeButton);
|
||||
add(speedWithMargin);
|
||||
add(directionWithMargin);
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
# CHANGELOG
|
||||
|
||||
## [Next]
|
||||
- Add `ButtonComponent` backed by two `PositionComponent`s
|
||||
- Add `SpriteButtonComponent` backed by two `Sprite`s
|
||||
|
||||
## [1.0.0-releasecandidate.18]
|
||||
- Forcing portrait and landscape mode is now supported on web
|
||||
- Fixed margin calculation in `HudMarginComponent` when using a viewport
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
export 'src/components/input/button_component.dart';
|
||||
export 'src/components/input/hud_button_component.dart';
|
||||
export 'src/components/input/hud_margin_component.dart';
|
||||
export 'src/components/input/joystick_component.dart';
|
||||
export 'src/components/input/sprite_button_component.dart';
|
||||
export 'src/extensions/vector2.dart';
|
||||
export 'src/game/mixins/keyboard.dart';
|
||||
export 'src/gestures/detectors.dart';
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../../../components.dart';
|
||||
import '../../../extensions.dart';
|
||||
import '../../../input.dart';
|
||||
import '../../gestures/events.dart';
|
||||
|
||||
/// The [ButtonComponent] bundles two [PositionComponent]s, one that shows while
|
||||
/// the button is being pressed, and one that shows otherwise.
|
||||
///
|
||||
/// Note: You have to set the [button] in [onLoad] if you are not passing it in
|
||||
/// through the constructor.
|
||||
class ButtonComponent extends PositionComponent with Tappable {
|
||||
late final PositionComponent? button;
|
||||
late final PositionComponent? buttonDown;
|
||||
|
||||
/// Callback for what should happen when the button is pressed.
|
||||
/// If you want to interact with [onTapUp] or [onTapCancel] it is recommended
|
||||
/// to extend [ButtonComponent].
|
||||
void Function()? onPressed;
|
||||
|
||||
ButtonComponent({
|
||||
this.button,
|
||||
this.buttonDown,
|
||||
this.onPressed,
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
Vector2? scale,
|
||||
double? angle,
|
||||
Anchor? anchor,
|
||||
int? priority,
|
||||
}) : super(
|
||||
position: position,
|
||||
size: size ?? button?.size,
|
||||
scale: scale,
|
||||
angle: angle,
|
||||
anchor: anchor,
|
||||
priority: priority,
|
||||
);
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void onMount() {
|
||||
assert(
|
||||
button != null,
|
||||
'The button has to either be passed in as an argument or set in onLoad',
|
||||
);
|
||||
final idleButton = button;
|
||||
if (idleButton != null && !contains(idleButton)) {
|
||||
add(idleButton);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
bool onTapDown(TapDownInfo info) {
|
||||
if (buttonDown != null) {
|
||||
if (button != null) {
|
||||
remove(button!);
|
||||
}
|
||||
add(buttonDown!);
|
||||
}
|
||||
onPressed?.call();
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
bool onTapUp(TapUpInfo info) {
|
||||
onTapCancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
bool onTapCancel() {
|
||||
if (buttonDown != null) {
|
||||
remove(buttonDown!);
|
||||
if (button != null) {
|
||||
add(button!);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import 'package:flutter/rendering.dart' show EdgeInsets, VoidCallback;
|
||||
import 'package:flutter/rendering.dart' show EdgeInsets;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../../../components.dart';
|
||||
@ -18,7 +18,7 @@ class HudButtonComponent extends HudMarginComponent with Tappable {
|
||||
/// Callback for what should happen when the button is pressed.
|
||||
/// If you want to interact with [onTapUp] or [onTapCancel] it is recommended
|
||||
/// to extend [HudButtonComponent].
|
||||
VoidCallback? onPressed;
|
||||
void Function()? onPressed;
|
||||
|
||||
HudButtonComponent({
|
||||
this.button,
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
import '../../../components.dart';
|
||||
import '../../../extensions.dart';
|
||||
import '../../../input.dart';
|
||||
|
||||
enum _ButtonState {
|
||||
up,
|
||||
down,
|
||||
}
|
||||
|
||||
/// The [SpriteButtonComponent] bundles two [Sprite]s, one that shows while
|
||||
/// the button is being pressed, and one that shows otherwise.
|
||||
///
|
||||
/// Note: You have to set the [button] in [onLoad] if you are not passing it in
|
||||
/// through the constructor.
|
||||
class SpriteButtonComponent extends SpriteGroupComponent<_ButtonState>
|
||||
with Tappable {
|
||||
/// Callback for what should happen when the button is pressed.
|
||||
void Function()? onPressed;
|
||||
|
||||
Sprite? button;
|
||||
Sprite? buttonDown;
|
||||
|
||||
SpriteButtonComponent({
|
||||
this.button,
|
||||
this.buttonDown,
|
||||
this.onPressed,
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
Vector2? scale,
|
||||
double? angle,
|
||||
Anchor? anchor,
|
||||
int? priority,
|
||||
}) : super(
|
||||
current: _ButtonState.up,
|
||||
position: position,
|
||||
size: size ?? button?.originalSize,
|
||||
scale: scale,
|
||||
angle: angle,
|
||||
anchor: anchor,
|
||||
priority: priority,
|
||||
);
|
||||
|
||||
@override
|
||||
void onMount() {
|
||||
assert(
|
||||
button != null,
|
||||
'The button sprite has to be set either in onLoad or in the constructor',
|
||||
);
|
||||
sprites = {_ButtonState.up: button!};
|
||||
sprites![_ButtonState.down] = buttonDown ?? button!;
|
||||
super.onMount();
|
||||
}
|
||||
|
||||
@override
|
||||
bool onTapDown(_) {
|
||||
current = _ButtonState.down;
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
bool onTapUp(_) {
|
||||
onTapCancel();
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
bool onTapCancel() {
|
||||
current = _ButtonState.up;
|
||||
onPressed?.call();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,7 @@ void main() {
|
||||
// It should change from 0-255 in 1s so it will change alpha with an
|
||||
// average of 255/100=2.5 per tick, which should not result in a need of
|
||||
// an epsilon value this high.
|
||||
expectDouble(component.getOpacity(), 1.0, epsilon: 6 * _epsilon);
|
||||
expectDouble(component.getOpacity(), 1.0, epsilon: 10 * _epsilon);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user