Increasing animation rate in keyframe method apparently disables the feature - animation

I have searched and found nothing. I am trying to get a simple bouncing ball to speed up on a continuous basis. The code below does not speed the ball up at all (even with a mouse click), however the getRate() property does increase as expected. If I comment out the increaseSpeed() method call in moveBall() the mouse click will speed up the ball.
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
public class MyApp extends Application {
#Override
public void start(Stage primaryStage) {
Bounce bouncePane = new Bounce();
bouncePane.setOnMousePressed(e -> bouncePane.increaseSpeed());
Scene scene = new Scene(bouncePane, 250, 150);
primaryStage.setTitle("Bouncing Ball");
primaryStage.setScene(scene);
primaryStage.show();
bouncePane.requestFocus();
}
}
public class Bounce extends Pane {
public final double rad = 25;
private double x = rad, y = rad;
private double dx = 1, dy = 1;
private Circle ball = new Circle(x, y, rad);
private Timeline anim;
public Bounce() {
ball.setFill(Color.BLUE);
getChildren().add(ball);
anim = new Timeline(
new KeyFrame(Duration.millis(50), e -> moveBall()));
anim.setCycleCount(Timeline.INDEFINITE);
anim.play();
}
public void increaseSpeed() {
anim.setRate(anim.getRate() + 0.5);
System.out.println(anim.getRate());
}
protected void moveBall() {
if (x < rad || x > getWidth() - rad) {
dx *= -1;
}
if (y < rad || y > getHeight() - rad) {
dy *= -1;
}
x += dx;
y += dy;
ball.setCenterX(x);
ball.setCenterY(y);
increaseSpeed();
}
}

Your application actually works (kinda)
If you set the duration of your KeyFrame to Duration.millis(1_000) instead of Duration.millis(50), then you will see the animation speed up (well up to a factor of 1_000/60).
Huh? What's going on
By default JavaFX is capped at 60 frames per second. The key frames in your timeline will not be invoked any more often than that, no matter what you set the playback rate to. Because in your example you set the initial duration of the animation to 50 milliseconds, then increase rate by 50 percent of the original rate in each frame, the animation callback for the key frame quickly (in a fraction of time imperceptible to the human eye) reaches the maximum frame rate of 60 frames per second and once it does that, you can't run it any faster.
What you can do
For this kind of problem, perhaps don't rely on animation playback rate to control the speed of your object. Instead, implement a "game loop" using an AnimationTimer and associate a velocity with your objects and move the object to the required position based upon the loop such as is done in this AnimationTimerTest by JamesD.

Related

How to rotate a rigidBody2d in a fixed period?

I'm new in Unity development. I have a ball with RigidBody 2D on it and i want it to rotate for 1 second. Does not matter the speed. The speed can be automatically.
I want just : at second 0 to be in initial position and at second 1 to be in final position. The rotation can be : 180, 360, 720.. etc degree.
I tried with angularVelocity but never stops. I tried with add torque but same. I don't know to handle it.
rb.angularVelocity = 180;
or
rb.AddTorque(90);
If you want to reach a precise rotation after a certain amount of time, it means your rotation speed will be computed automatically. To achieve something like this I'd recommend using a Coroutine :
public class TestScript : MonoBehaviour
{
public float targetAngle;
public float rotationDuration;
void Update()
{
//This is only to test the coroutine
if(Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(RotateBall(targetAngle, rotationDuration));
}
}
private IEnumerator RotateBall(float a_TargetAngle, float a_Duration)
{
Vector3 startLocalEulerAngles = GetComponent<RectTransform>().localEulerAngles;
Vector3 deltaLocalEulerAngles = new Vector3(0.0f, 0.0f, a_TargetAngle - startLocalEulerAngles.z);
float timer = 0.0f;
while(timer < a_Duration)
{
timer += Time.deltaTime;
GetComponent<RectTransform>().localEulerAngles = startLocalEulerAngles + deltaLocalEulerAngles * (timer / a_Duration);
yield return new WaitForEndOfFrame();
}
GetComponent<RectTransform>().localEulerAngles = startLocalEulerAngles + deltaLocalEulerAngles;
}
}

changing rate of Animation/Transition

I'm trying to do some basic animations, but am failing at the most simple things:
Rectangle rect = new Rectangle(100.0, 10.0);
mainPane.getChildren().add(rect); //so the rectangle is on screen
Animation anim = new Timeline(new KeyFrame(Duration.seconds(30.0),
new KeyValue(rect.widthProperty(), 0.0, Interpolator.LINEAR)));
rect.setOnMouseClicked(e -> {
if (anim.getStatus() == Status.RUNNING) {
anim.pause();
} else {
anim.setRate(Math.random() * 5.0);
anim.play();
System.out.println(anim.getRate());
}
});
The problem I am facing is that when I click the rectangle multiple times, the size will randomly jump around, instead of just changing the speed at which it drops. So for example, I let it run to about 50% size at speed ~2.5 and then stop it. When I start it up again, it will jump to a totally different size, smaller for a lower speed, bigger for a higher speed, so for example to ~20% for ~1.0 speed or ~80% for ~4.5 speed.
At first I thought animation was pre-calculated for the new speed and thus jumped to the position at which it would be, had it been played with the new speed from the beginning for the time that it was already playing before the pause, but it's bigger for a smaller speed, which doesn't really make sense then.
How do I change the speed/rate of an animation without having it jump around?
I think your diagnosis is correct: the current value is interpolated given the current time and current rate. If you decrease the rate without changing the current time, you are then earlier in the animation. Since the animation is shrinking this has the effect of making the rectangle bigger.
The easiest way is probably just to start a new animation each time:
import javafx.animation.Animation;
import javafx.animation.Animation.Status;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class VariableRateAnimation extends Application {
private Animation anim ;
#Override
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
Rectangle rect = new Rectangle(100.0, 10.0);
mainPane.getChildren().add(rect); //so the rectangle is on screen
rect.setOnMouseClicked(e -> {
if (anim != null && anim.getStatus() == Status.RUNNING) {
System.out.println("Paused (" + anim.getTotalDuration().subtract(anim.getCurrentTime())+ " remaining)");
anim.pause();
} else {
Duration duration = Duration.seconds(30.0 * rect.getWidth() / (100 * Math.random() * 5.0));
System.out.println("Starting: ("+duration+ " to go)");
double currentWidth = rect.getWidth() ;
if (anim != null) {
anim.stop();
}
anim = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rect.widthProperty(), currentWidth, Interpolator.LINEAR)),
new KeyFrame(duration, new KeyValue(rect.widthProperty(), 0.0, Interpolator.LINEAR)));
anim.play();
}
});
primaryStage.setScene(new Scene(mainPane, 600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Animate/move/translate/tween image in Unity 4.6 from C# code

How can I move/animate/translate/tween an Image from position A to position B using C# code in Unity 4.6?
Assuming Image is a GameObject, so it could be a Button or whatever.
There has to be a one-liner for this, right? I've been googling for a while but all I can see out-of-the-box is stuff done in Update, and I firmly believe doing stuff in Update is not a fast way of scripting things.
maZZZu's method will work, however, if you do NOT want to use the Update function, you can use an IEnumerator/Coroutine like so…
//Target object that we want to move to
public Transform target;
//Time you want it to take before it reaches the object
public float moveDuration = 1.0f;
void Start ()
{
//Start a coroutine (needed to call a method that returns an IEnumerator
StartCoroutine (Tween (target.position));
}
//IEnumerator return method that takes in the targets position
IEnumerator Tween (Vector3 targetPosition)
{
//Obtain the previous position (original position) of the gameobject this script is attached to
Vector3 previousPosition = gameObject.transform.position;
//Create a time variable
float time = 0.0f;
do
{
//Add the deltaTime to the time variable
time += Time.deltaTime;
//Lerp the gameobject's position that this script is attached to. Lerp takes in the original position, target position and the time to execute it in
gameObject.transform.position = Vector3.Lerp (previousPosition, targetPosition, time / moveDuration);
yield return 0;
//Do the Lerp function while to time is less than the move duration.
} while (time < moveDuration);
}
This script will need to be attached to the GameObject that you would like to move. You will then need to create another GameObject in the scene that will be your Target…
The code is commented but if you need clarification on something just post a comment here.
If you want to do the movement yourself you can use something like this:
public Vector3 targetPosition = new Vector3(100, 0, 0);
public float speed = 10.0f;
public float threshold = 0.5f;
void Update () {
Vector3 direction = targetPosition - transform.position;
if(direction.magnitude > threshold){
direction.Normalize();
transform.position = transform.position + direction * speed * Time.deltaTime;
}else{
// Without this game object jumps around target and never settles
transform.position = targetPosition;
}
}
Or you can download for example DOTween package and just start the tween:
public Vector3 targetPosition = new Vector3(100, 0, 0);
public float tweenTime = 10.0f;
void Start () {
DOTween.Init(false, false, LogBehaviour.Default);
transform.DOMove(targetPosition, tweenTime);
}

Having FPS lags when drawing tiles in XNA

I am developing my own tile-based game in xna 4.0, and i suffer big fps lags while drawing the tiles on the screen.
I've read a lot about increasing performance and i've got to this:
I store my tiles in a simple dictionary
private Dictionary<Vector2, IBlock> WorldBlocks;
private Vector2 ExactBlockPosition;
private IBlock ExistingBlock;
public void Draw(SpriteBatch SpriteBatch, Vector2 ScreenStart, Vector2 ScreenEnd)
{
// Run over all the blocks in the screen
for (this.ExactBlockPosition.X = this.GetCurrentBlockXPosition(ScreenStart.X);
this.ExactBlockPosition.X < ScreenEnd.X;
this.ExactBlockPosition.X += Global.BLOCK_WIDTH)
{
for (this.ExactBlockPosition.Y = this.GetCurrentBlockYPosition(ScreenStart.Y);
this.ExactBlockPosition.Y < ScreenEnd.Y;
this.ExactBlockPosition.Y += Global.BLOCK_HEIGHT)
{
// Check if there is a block in the exact block location
if (this.WorldBlocks.TryGetValue(this.ExactBlockPosition, out this.ExistingBlock))
{
// Draw the block on the screen
this.ExistingBlock.Draw(SpriteBatch);
}
}
}
}
private int GetCurrentBlockXPosition(float X)
{
return (int)(X - X % Global.BLOCK_WIDTH);
}
private int GetCurrentBlockYPosition(float Y)
{
return (int)(Y - Y % Global.BLOCK_WIDTH);
}
In short, this code is drawing the blocks that are on the screen only.
But still my fps falls to 31 when there is a lot of tiles on the screen.
The tile width and heigh is 20 pixels.
Any suggestions why my fps falls?

How to control an animation by touch position along a path in Unity3D?

I have a GameObject that I want to animate along a specific path/curve, but the animation should be controlled by mouse/touch position. So when I touch/click on the GameObject and move the finger/mouse on/near the path (or maybe its easier to just move down) the GameObject should follow its defined path.
I like iTween, but I think it is not possible to find a solution using it here, right?
edit: added image:
It's quite a simpler task than what you might think.
Basically it's a question of remapping a function (that takes the input as parameter) to another function (that express a position along a path).
There are several ways of doing that, depending on the precise effect you want to implement.
The most important choices you have to take are:
How the describe the path/curve
How to handle input
Example
For the path an easy and flexible way is to use some sort of spline curves, such as cubic Bézier curve. It's easy to implement and Unity3D provides built-in functions to draw them. Have a look at Handles.DrawBezier.
Basically a Bézier function takes as input a parameter t in the domain [0,1] and return as a result a point in the space (2D or 3D as you prefer). B(0) gives the point at the begin of the curve, B(1) the end point. (Side note: the function is not linear so in the general case incrementing at a constant rate t doesn't produce a movement at constant speed along the curve. This paper might be useful).
For what concern the input the simpler solution that comes up to my mind is the following:
Accumulate somewhere the vector describing the offset from the position when the touch started to the current touch position. (Here's how to handle touches, have a look at deltaPosition).
Something like:
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
offsetFromStartPos += Input.GetTouch(0).deltaPosition;
}
Let's say you want to swipe up/down your finger for moving forward/back an object along a path.Choose a "travel" distance (the domain of the input function) for your finger in order to complete the movement along the curve and normalize the offset using such distance in order to remap the input into the [0,1] domain.
float t = offsetFromStartPos.y / maxDistanceAlongYAxis;
Vector3 pos = CalculateBezier(t);
transform.position = pos;
It's just an hint to put you in the right direction.
I tried with keyboard and its working fine,
but not with mouse or touch
using System;
using UnityEngine;
public class Collector : MonoBehaviour
{
public Transform startPoint;
public Transform middlePoint;
public Transform endPoint;
public float curveSpeed = 0.5f;
//public float speed = 0f;
private int _direction = 1;
private bool _isObjectSelected;
private Vector3 _mouseLastPosition;
private float _journeyLength;
private Vector3 _offsetPos;
private float _currentTime = 0;
private void Start()
{
_journeyLength = Vector3.Distance(startPoint.position,
endPoint.position);
UpdateJourney(0);
}
private void OnMouseDown()
{
if (_isObjectSelected)
return;
_offsetPos = Vector3.zero;
_mouseLastPosition = Input.mousePosition;
_isObjectSelected = true;
}
private void OnMouseUp()
{
_isObjectSelected = false;
}
private void OnMouseExit()
{
_isObjectSelected = false;
}
private void OnMouseDrag()
{
if (_isObjectSelected)
{
Debug.LogError("Mouse drag");
Vector3 currentPosition = Input.mousePosition;
_offsetPos += currentPosition - _mouseLastPosition;
float distCovered = _offsetPos.y / _journeyLength;
UpdateJourney(distCovered);
_mouseLastPosition = currentPosition;
}
}
private void UpdateJourney(float time)
{
if (time < 0)
time = 0;
else if (time > 1)
time = 1;
_currentTime = time;
transform.position =
QuadraticCurve(startPoint.position,
middlePoint.position,
endPoint.position,
_currentTime);
transform.rotation = Quaternion.Euler(
new Vector3(0, 0,
QuadraticCurve(0, 45, 90, _currentTime)));
}
private void Update()
{
// moving on path using keyboard input
float direction = Input.GetAxisRaw("Horizontal");
if (Math.Abs(direction) > 0.1f)
{
_currentTime += Time.deltaTime * curveSpeed * direction;
UpdateJourney(_currentTime);
}
}
private static Vector3 Lerp(Vector3 start, Vector3 end, float time)
{
return start + (end - start) * time;
}
private static Vector3 QuadraticCurve(Vector3 start, Vector3 middle, Vector3 end, float time)
{
Vector3 point0 = Lerp(start, middle, time);
Vector3 point1 = Lerp(middle, end, time);
return Lerp(point0, point1, time);
}
private static float QuadraticCurve(float start, float middle, float end, float time)
{
float point0 = Mathf.Lerp(start, middle, time);
float point1 = Mathf.Lerp(middle, end, time);
return Mathf.Lerp(point0, point1, time);
}
}

Resources