I have an app where I have an animated check mark made in rive. It is a one shot animation and I want to play it and have it stay in the end of the animation once it's done and then also be able to reverse it when the user clicks on it again. I tried using a SimpleAnimation but I also wasn't able to achieve what I want but it did stay after the animation was done. I don't, however, know how to play an animation and then reverse it or play another animation.
The check mark looks something like this: https://cln.sh/MwLFNs
They are two separate animations but they are copy pasted but in reverse so I can use either one or both.
What I need is to have the animation play and then when the user clicks on the check, I want the animation to change to another one which would then play. How can I achieve this?
Thanks for the help!
you can use State machines, get the state and decide what to do when you want
// Getting state machine
void _onRiveInit(Artboard artboard) async {
final controller =
StateMachineController.fromArtboard(artboard, 'State Machine 1');
if (controller != null) {
artboard.addController(controller);
_lick = controller.findInput<bool>('Lick') as SMITrigger;
}
}
void _onHandlerStateMachine() => _lick?.value = !_lick!.value;
_onRiveInit is a function that is fired when the Rive file is being loading,
after you get the controller to the state machine to modify his state (This is important the name of the State Machine has to be the same of the animation State machine name)
and to modified the value of the trigger your must get the reference on this way
_lick = controller.findInput('Lick') as SMITrigger;
then you can use
void _onHandlerStateMachine() => _lick?.value = !_lick!.value;
to put or remove the animation on any time, also you can have more than one trigger.
If your need that your animation do something and after a time do something else you can use a debouncer function
_debouncer.run(() {
_onHandlerStateMachine()
});
**Remember update the view if are using state management
when the rive file is load
// Loads a Rive file
Future<void> _loadRiveFile() async {
final bytes = await rootBundle.load(riveFileName);
RiveFile rFile = RiveFile.import(bytes);
final artboard = rFile.mainArtboard;
_onRiveInit(artboard);
globalArtboard = artboard
..addController(
_animationController = SimpleAnimation('idle'),
);
}
Here I have and example of a Rive animation of Teddy, using Flutter with provider
https://github.com/alcampospalacios/flutter-rive-animations
Related
I want to make customizable character in rive to change his outfit during runtime in flutter, and because there is no way to change assets during runtime I added them in rive and joined to my amimated bones. But i cant find the way to change them whem my main "anim" playing. I wish if I could do it with using inputs as an IDs of clothes.
workspace
I make a few animations-states which changes opacity and make visible one clothes and transparent others. I can change them with blend state in state machine but can`t play "anim" at the same time. I expect to someany offer me some way to change clothes
I don't know how to solve the problem, I change colors of the animation in this way, maybe I can help you to achieve it.
final bytes = await rootBundle.load(animationPath);
final RiveFile file = RiveFile.import(bytes);
_riveArtboard = file.mainArtboard
..forEachChild((c) {
if (c is Shape) {
if (c.name == 'SKY') {
c.fills.first.paint.colorFilter = ColorFilter.mode(
Customization.variable_6.withOpacity(.9),
BlendMode.lighten);
}
}
return true;
})
..addController(
_controller = SimpleAnimation('Untitled 1'),
);
I use AudioPlayers package.
I have Button1 that plays the sounds. (Code Below)
AudioCache playerCache = new AudioCache(); // you already initialized this
AudioPlayer player = new AudioPlayer();
void _playFile(String yol, String name) async {
player = await playerCache.play(yol);
}
But there is another button which calling "Button2" has to stop all of the sounds at once. I wrote this :
void cancelPlay() {
print("stop");
playSounds.removeRange(0, playSounds.length);
player.stop();
player.stop();
}
However , when user click Button2 , it only stops the last sound. I want that to stop all of the sounds. How to do that ?
I guess The problem is, that every time i call _playFile() (press Button 1) a new instance of AudioPlayer is assigned to the player variable, hence in cancelPlay() the player variable holds only the last instance of AudiPlayer.
How can i do to store the instances in a list.
Thank you.
make a variable
List<AudioPlayer> audioPlayers = [];
every time you tap button1, add new audioPlayer
audioPlayers.add(new AudioPlayer());
then when you tap on button2, do something like this to stop every audioPlayer
audioPlayers.forEach((audioPlayer) => audioPlayer.stop());
or even better would be function to toggle the AudioPlaybackState of audioPlayers, so if they are paused then play and vice versa. Ofcourse this example is based on presumption that every audioPlayer controller has same AudioPlaybackState as first:
void toggleAudioPlayers() {
if (audioPlayers.first.playbackState == AudioPlaybackState.playing) {
audioPlayers.forEach((audioPlayer) => audioPlayer.stop());
} else {
audioPlayers.forEach((audioPlayer) => audioPlayer.play());
}
}
Dont forget to dispose every audioPlayer controller to prevent memory leaks:
audioPlayers.forEach((audioPlayer) => audioPlayer.dispose());
AudioPlayer.players.forEach((key, value) {
value.stop();
});
when you play another audio you need to call this code before
Hi I'm struggling with this issue.I searched a lot before asking here.
So i have programmed an arcade game which when it dies it pops up a menu with a continue option using one life out of five.Like Subway Surfers with the keys.
The issue is that when i use one heart i want to save it.But everytime i restart the game it keeps saying the initial number which is five.Or without using a life,it by itself,subtracts by one without even clicking the "Save me" button.
Here is my code:
public Text heart;
public Text heart2;
public int counter;
void Start () {
heart.text = "" + counter;
heart2.text = "" +counter;
}
// Update is called once per frame
void Update () {
}
public void SaveMe()
{
heart.text = counter.ToString();
heart2.text = counter.ToString();
int heartScore = PlayerPrefs.GetInt("Life2", 0);
heartScore--;
if(heartScore<0)
{
heartScore = counter;
}
PlayerPrefs.SetInt("Life2", heartScore);
heart.text = PlayerPrefs.GetInt("Life2", 0).ToString();
heart2.text = PlayerPrefs.GetInt("Life2", 0).ToString();
}
}
Then i call this method in the button script.
You should need to save life counter in a file or PlayerPrefs in unity so that loading a scene doesn't effect the counters.
Stores and accesses player preferences between game sessions.
When Unity loads a new scene (if not in an additive way) it will destroy everything that belongs to it before loading the new one. If you don't want a GameObject to be destroyed automatically, read the instructions for DontDestroyOnLoad
Also consider generally to separate your game model (in this occasion the variable counter) to a different Component and use scriptable objects for managing it/them. It will make your life easier in the long run.
I want to determine whether the camera changed event was initiated from the user or not. (i have to make different actions based on that). So if the user pans a camera with the finger, i have to close sg, but if i moved the camera with the API, i do not.
Currently i cannot decide it was a user event or not, in my OnCameraChangeListener, because the onCameraChange(CameraPosition var1) method does not provide any kind of information about that.
I also tried to save the last marker position which i animated onto programmatically, and check that in the listener method:
map.setOnCameraChangeListener(new GoogelMap.onCameraChangeListener {
public void onCameraChange(CameraPosition position) {
if (!cameraPosition.target.equals(lastClickedMarker)) {
// this is a user event
}
}
I set the lastClickedMarker with the OnMarkerClickListener. I found out i cannot rely on this, because the cameraPosition and lastClickedMarker coordinates will always differ a little, even if really animate to that marker programmatically with animateCamera().
Is there any way to solve this?
You can set a boolean before you change the camera programatically, and check if it is set (and unset) in the onCameraChange method.
Something like this:
// Moving programmatically
cameraMovedProgrammatically = true;
map.animateCamera(cameraUpdate);
And checking it:
public void onCameraChange(CameraPosition position) {
if (cameraMovedProgrammatically) {
// this is not a user event
cameraMovedProgrammatically = false;
} else {
// this is a user event
}
}
My app has a sliding drawer for notifications. I've managed to get it to function like the android notifications including the "clear all" button.
When the clear all button is clicked my database is cleared, my list adapter is refreshed, and my list adapter gets set to the list. The view updates and the list is cleared.
When I added slide-out animation (just like jelly bean) I got a NullPointerException. The issue crops up when my adapter is set. If I comment out setting the adapter the animation runs without a problem.
// Animation
int count = drawer_list.getCount();
for (int i = 0; i < count; i++) {
View view = drawer_list.getChildAt(i);
if (view != null) {
// create an Animation for each item
Animation animation = AnimationUtils.loadAnimation(this, android.R.anim.slide_out_right);
animation.setDuration(300);
// ensure animation final state is "persistent"
animation.setFillAfter(true);
// calculate offset (bottom ones first, like in notification panel)
animation.setStartOffset(300 * (count - 1 - i));
// animation listener to execute action code
if (i == 0) {
animation.setAnimationListener(new AnimationListener() {
public void onAnimationStart(Animation animation) {
// NOT USED
}
public void onAnimationRepeat(Animation animation) {
// NOT USED
}
public void onAnimationEnd(Animation animation) {
// Clear table
notifications.flushNotificationTempTable_ActiveOnly();
// Update list adapter
refreshNotifications();
// Close drawer
notification_drawer.close();
}
});
}
view.startAnimation(animation);
}
}
The trouble spot is in the refreshNotifications() method when the line drawer_list.setAdapter(notificationAdapter); executes. I use this method and adapter all throughout my app and, as I said above, it works flawlessly without the animations.
I was able to solve this by eliminating the animation listener and adding a delayed runnable after it that ran my post animation code.
I was unable to find a way to set a new adapter (required because i used a custom adapter) inside the animation listener. If anyone knows how to do this I would like to know.