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
|
a callback function as the `onPressed` argument, or you extend the component and override
|
||||||
`onTapDown`, `onTapUp` and/or `onTapCancel` and implement your logic there.
|
`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
|
## Gamepad
|
||||||
|
|
||||||
Flame has a separate plugin to support external game controllers (gamepads), check
|
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/palette.dart';
|
||||||
import 'package:flame/sprite.dart';
|
import 'package:flame/sprite.dart';
|
||||||
import 'package:flutter/animation.dart';
|
import 'package:flutter/animation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
|
|
||||||
import 'joystick_player.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(
|
final _regular = TextPaint(
|
||||||
style: TextStyle(color: BasicPalette.white.color),
|
style: TextStyle(color: BasicPalette.white.color),
|
||||||
);
|
);
|
||||||
@ -142,6 +187,8 @@ class JoystickAdvancedExample extends FlameGame
|
|||||||
add(joystick);
|
add(joystick);
|
||||||
add(flipButton);
|
add(flipButton);
|
||||||
add(flopButton);
|
add(flopButton);
|
||||||
|
add(buttonComponent);
|
||||||
|
add(spriteButtonComponent);
|
||||||
add(shapeButton);
|
add(shapeButton);
|
||||||
add(speedWithMargin);
|
add(speedWithMargin);
|
||||||
add(directionWithMargin);
|
add(directionWithMargin);
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## [Next]
|
||||||
|
- Add `ButtonComponent` backed by two `PositionComponent`s
|
||||||
|
- Add `SpriteButtonComponent` backed by two `Sprite`s
|
||||||
|
|
||||||
## [1.0.0-releasecandidate.18]
|
## [1.0.0-releasecandidate.18]
|
||||||
- Forcing portrait and landscape mode is now supported on web
|
- Forcing portrait and landscape mode is now supported on web
|
||||||
- Fixed margin calculation in `HudMarginComponent` when using a viewport
|
- 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_button_component.dart';
|
||||||
export 'src/components/input/hud_margin_component.dart';
|
export 'src/components/input/hud_margin_component.dart';
|
||||||
export 'src/components/input/joystick_component.dart';
|
export 'src/components/input/joystick_component.dart';
|
||||||
|
export 'src/components/input/sprite_button_component.dart';
|
||||||
export 'src/extensions/vector2.dart';
|
export 'src/extensions/vector2.dart';
|
||||||
export 'src/game/mixins/keyboard.dart';
|
export 'src/game/mixins/keyboard.dart';
|
||||||
export 'src/gestures/detectors.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 'package:meta/meta.dart';
|
||||||
|
|
||||||
import '../../../components.dart';
|
import '../../../components.dart';
|
||||||
@ -18,7 +18,7 @@ class HudButtonComponent extends HudMarginComponent with Tappable {
|
|||||||
/// Callback for what should happen when the button is pressed.
|
/// Callback for what should happen when the button is pressed.
|
||||||
/// If you want to interact with [onTapUp] or [onTapCancel] it is recommended
|
/// If you want to interact with [onTapUp] or [onTapCancel] it is recommended
|
||||||
/// to extend [HudButtonComponent].
|
/// to extend [HudButtonComponent].
|
||||||
VoidCallback? onPressed;
|
void Function()? onPressed;
|
||||||
|
|
||||||
HudButtonComponent({
|
HudButtonComponent({
|
||||||
this.button,
|
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
|
// 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
|
// average of 255/100=2.5 per tick, which should not result in a need of
|
||||||
// an epsilon value this high.
|
// 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