javafx8 timeline - how to get the source of the animation - animation

I want to have 3 objects doing different things, so I want to get the source of the caller.
I am using lambda for that, and I am not using key frame, the animation is the same and cyclic so I don't need to specify different behavior for a different key frame.
Maybe I am doing something wrong with the lambda?
this is my code:
class MyClock extends ClockPane //clockpane extends pane
{
Timeline animation;
int id;
EventHandler<ActionEvent> eventHandler = e ->
{//startAnimationById();
System.out.println(e.getSource()==clockControl1.myclock.animation);
};
public MyClock(int c,int id)
{
super(c);
this.id=id;
animation = new Timeline(new KeyFrame(Duration.millis(1000),eventHandler));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play();
}
the startanimationbyid method works using the id I defined to avoid this problem, but it surely does bug me.
I have 3 different objects of this type, each one is nested ina clockcontrol class
(in other words I have clockcontrol1 clockcontrol2 and 3 each one of these having a MyClock myclock in them)
The print I have there returns false for all the the clocks I have (total of 3) while as it is currently written I'd expect to get true for the first clock...I tried different variations of this, the one I post here is only the last variation, but I got false for all of my attempts.
Where did I mess things up?

The API for KeyFrame doesn't specify what the source of the ActionEvent will be. You could just do System.out.println(e.getSource()) to see (it seems that the source of the event is the KeyFrame object, not the animation), but there's no real guarantee it would always be what you expect.
If you want different instances of MyClock to behave differently, you can provide a parameter for the different behavior. In this case, you could use a Runnable to execute different code in the event handler:
class MyClock extends ClockPane //clockpane extends pane
{
Timeline animation;
// int id;
public MyClock(int c, Runnable handler)
{
super(c);
EventHandler<ActionEvent> eventHandler = e -> handler.run() ;
animation = new Timeline(new KeyFrame(Duration.millis(1000),eventHandler));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play();
}
}
Then you can instantiate these as you need:
public class Controller1 {
// ...
MyClock clock = new MyClock(..., () -> {
// code to execute for this clock
});
}
and
public class Controller2 {
// ...
MyClock clock = new MyClock(..., () -> {
// code to execute for this clock
});
}

Related

how to destroy an object in unity

how to destroy an object in unity?
I know you have to type the command
Destroy();
but what I'm saying is that I don't know what to put between brackets.
I have tried many different ways:
public GameObject motor;
Destroy(motor);
but it does not work
using System.Collections;
using UnityEngine;
public class LogRotation : MonoBehaviour
{
[System.Serializable] //this will allow us to edit it in the editor
//a custom class representing a single rotation "element" of the log's rotation pattern
private class RotationElement
{
//to get rid of an obnoxious warning about these fields not being initialized
#pragma warning disable 0649
public float Speed;
public float Duration;
#pragma warning restore 0649
}
[SerializeField] //attribute making private fields editable in the Unity Editor
//the aforemention full rotation pattern of the log
private RotationElement[] rotationPattern;
//this will be set to the Wheel Joint 2D from the LogMotor object
private WheelJoint2D wheelJoint;
//something has to actually apply a force to the log through the Wheel Joint 2D
private JointMotor2D motor;
private void Awake()
{
//setting fields
wheelJoint = GetComponent<WheelJoint2D>();
motor = new JointMotor2D();
//starting an infinitely looping coroutine defined below right when this script loads (awakes)
StartCoroutine("PlayRotationPattern");
}
private IEnumerator PlayRotationPattern()
{
int rotationIndex = 0;
//infinite coroutine loop
while (true)
{
//working with physics, executing as if this was running in a FixedUpdate method
yield return new WaitForFixedUpdate();
motor.motorSpeed = rotationPattern[rotationIndex].Speed;
//hard coded 10000, feel free to experiment with other torques if you wish
motor.maxMotorTorque = 10000;
//set the updated motor to be the motor "sitting" on the Wheel Joint 2D
wheelJoint.motor = motor;
//let the motor do its thing for the specified duration
yield return new WaitForSecondsRealtime(rotationPattern[rotationIndex].Duration);
rotationIndex++;
//infinite loop through the rotationPattern
rotationIndex = rotationIndex < rotationPattern.Length ? rotationIndex : 0;
}
}
}
TLDR; Destroy(motor.gameObject), but it will not work if JointMotor2D doesn't inherit MonoBehaviour.
Destroy(obj) can be used to destroy a component too.You need reference it to the game-object to destroy it.
Destroy(GetComponent<RigidBody>()) would remove the RigidBody out of the game-object, rather than removing the object itself.
Destroy(motor.gameObject) should do the trick.
But upon seeing your code, it may not.As it seems like JointMotor2D isn't a MonoBehaviour, aka it doesn't exists in the game-world, hence you can't destroy it.
Depending on what your trying to destroy, you have to find a reference to it.
Simplest way is to reference it in the inspector. Or destroy itself if that is already the object you want to destroy:
// Drag-drop the object in the inspector
[SerializeField]
private GameObject toDestroyLater;
private void DestroyObject() {
Destroy(toDestroyLater);
// Destroys self
// (Aka, whatever game-object this script is attached to)
Destroy(this.gameObject);
}

How to ignore one OnTrigger Collider in an Object?

I have 3(Three) BoxCollider2D components where 2(two) have OnTrigger checked in my Object and both have different functions. Due to having OnTrigger on both, the projectiles I am casting collide with the wrong collider and instead activate that function. Is there a way to ignore 1(one) OnTrigger collider?
I have already tried Layer-based collision detection and set up a layer. Unfortunately, the object now collides with the collider which eliminates the player on collision
However, there are several ways to solve this problem. All kinds of physics.checks as well as raycasts but this code helps you to ignore the obstacle collider.
public Collider2D playerCollider;
public Collider2D obstacleCollider;
public void Start() => Physics2D.IgnoreCollision(playerCollider, obstacleCollider);
I have a very simple script I use to handle collisions in my games.
It's very easy to setup because it makes everything drop and draggable, which is a much easier way to program.
Below is the script and below that is instructions on how to use. Note it is a layer based system, but you can select multiple layers.
[System.Serializable]
public class TriggerEvent : UnityEvent<Collider> { }
[System.Serializable]
public class CollisionEvent : UnityEvent<Collision>{ }
public class EnterEvent: MonoBehaviour
{
public TriggerEvent TriggerEnteredEvent;
public CollisionEvent CollisionEnteredEvent;
[SerializedField]private LayerMask validLayers;
// Initalize Event System
void Awake()
{
if (OnTriggerEnter == null)
{
TriggerEnteredEvent = new TriggerEvent();
}
if (OnCollisionEnter == null)
{
CollisionEnteredEvent = new CollisionEvent();
}
}
// Called if transform is a trigger
void OnTriggerEnter(Collider collider)
{
if (validLayers == (validLayers | 1 << collider.gameObject.layer))
{
TriggerEnteredEvent?.Invoke(collider);
}
}
// Called if transform is not a trigger
void OnCollisionEnter(Collision collision)
{
if (validLayers == (validLayers | 1 << collision.gameObject.layer))
{
CollisionEnteredEvent?.Invoke(collision);
}
}
}
This is how it would work, and in this example I will be "coding" it from the perspective of a Bullet.
Basically I want to check if I (The Bullet) hits either the terrain or an enemy then call the relevent funcitons in the Bullet class respectively.
Obviously if I hit an enemy I want to deal damage.
So this will be my example bullet class
public class Bullet : MonoBehaviour
{
public int BulletDamage = 10;
public int BulletSpeed = 5;
void FixedUpdate() => transform.position = Vector3.Lerp(transform.position, transform.position + transform.forward * speed * Time.deltaTime, 1f);
public void OnEnemyHit(Collision collision)
{
// Try to get the enemy script
Enemy enemy = collision.gameObject.transform.GetComponent<Enemy>();
if (enemy != null)
{
enemy.DealDamage(this.BulletDamage);
}
}
public void OnTerrainHit(Collision collision)
{
Destroy(this.gameObject);
}
}
Add the EnterEvent script to the bullet.
Add the Bullet Script to the bullet.
There will be a space on the Inspecter where you can add your events. It should have a Plus and Minus in the top right corner. Press the plus.
From the inspector drag the bullet in game GameObject to the open space provider.
In the dropdown to the right, click on it, look for the Bullet Script, and select the OnEnemyHit function from it.
Create another event, do the exact same, but this time select the OnTerrainHit funciton instead - now but would be called in the order you added them.
Just underneath the event system should be the be able to see a dropdown for the Layers. Select all the layers you want your bullet to interact with. In this case it will be the Enemy and Terrain.
Finally remember to setup your layers properly. Ensure the Enemy has an Enemy Layer, the Terrain has a Terrain Layer, Bullet bullet layer and Player has a Player Layer

Update TextArea while executing processes

i found out that my GUI starts to freeze after 3-4 seconds when I click the "start" button like "no response". When I keep on click the App, it's forced to shut down.
Now I want to prevent this but I got no clue how. Just as far as I know JavaFX runs in a Single Thread, therefore, to update my TextArea while the Methods are executing, I need to run these Methods in another Thread.
I hope someone can help me.
How does my project look like?
I got a FXML, a Controller, a Handler, a Transformer , also a Writer and a Reader class (which are used in the Handler class).
When I click the button, which is bind to a method in the Controller, an instance of Handler is created and this one calls the Reader to read in a text file, transformed to a List of Strings (line by line).
In addition, the lines are getting manipulated. After this, the Writer is used to creat a new file and write the new manipulated lines to this file.
It is also allowed to the user to refer to more than just one file.
What I want is that the textarea shows whenever the reader starts to read a file like "The file ... is being read".
Then append "The file ... is being manipulated" when Transformer comes to action and then
"The file ... is being written" when the new lines are written to the new file.
Here is some code..
Controller:
public class FXMLDocumentController implements Initializable {
#FXML
private TextArea console;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
Handler hand = new Handler();
hand.handle(files);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Handler:
public class Handler {
public void handle(List<String> files) {
for (String s : files) {
List<String> ls = Reader.readFile(s);
Writer.writeFile(Transformer.transform(ls));
}
}
How should I change my code to update the TextArea whenever a file is read, manipulated or written?
Info: I know the class Handler won't compile as I erased the initialization of the List "files" which contains Strings of file paths.
If I left out relevant information, feel free to ask.
I thank you in advance!
You should execute the handle() method in a background thread:
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
Thread thread = new Thread(() -> {
Handler hand = new Handler();
hand.handle(files);
});
// this line means the background thread will not prevent application exit:
thread.setDaemon(true);
thread.start();
}
If you want to update the text area with the current status, you need to schedule that back on the FX Application Thread using Platform.runLater(). Probably the cleanest way to do this is not to have Platform.runLater() in the Handler class, but define a field in Handler for "consuming" status messages:
public class Handler {
private final Consumer<String> statusMessageProcessor ;
public Handler(Consumer<String> statusMessageProcessor) {
this.statusMessageProcessor = statusMessageProcessor ;
}
// default processor does nothing:
public Handler() {
this(s -> {});
}
public void handle(List<String> files) {
for (String s : files) {
// similarly for other status updates:
statusMessageProcessor.accept("The file "+s+" is being read");
List<String> ls = Reader.readFile(s);
Writer.writeFile(Transformer.transform(ls));
}
}
}
and then
Handler hand = new Handler() ;
can become
Handler hand = new Handler(message ->
Platform.runLater(() -> console.appendText(message + "\n")));
in the button handler method.

Get EditText data on swipe to next Fragment

I have three fragments in a view pager:
A -> B -> C
I would like to get the strings of my two edittexts in Fragment A on swipe to Fragment B to show them in Fragment B. The edittext data may be changed up until the swipe.
Someone has suggested listening for typing and sending data after each one, but the callbacks I know for that change state EVERY key click (which can be expensive). How do I this without using buttons, since their right next to each other, for a more delightful experience?
You can check the data of the EditText on swipe ; if it's not null, then you can send it to any other fragment using Bundle since you are dealing with fragments
With help from #I. Leonard I found a solution here.
It was deprecated so I used the newer version. I put the below code in my fragment class because I needed access to the data without complicating things. It works like a charm!
On the page listener callback, I suggest, calling an interface for inter-fragment communication to do your actions on your home activity or to call the appropriate fragment that can do the work, from the activity.
// set content to next page on scroll start
vPager = (ViewPager) getActivity().findViewById(R.id.viewpager);
vPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_SETTLING) {
// ViewPager is slowing down to settle on a page
if (vPager.getCurrentItem() == 1) {
// The page to be settled on is the second (Preview) page
if (!areFieldsNull(boxOne.getText().toString(), boxTwo.getText().toString()))
// call interface method in view pager activity using interface reference
communicator.preview(boxOne.getText().toString(), boxTwo.getText().toString());
}
}
}
});

Triggering event in Activity using MvvmCross

I have an MvxFragmentActivity which loads a google map and places markers on the map. The code to create the map and markers is very Droid specific so it is in the Activity. The markers are created based on objects in the ViewModel which each contain lat/long coordinates. This worked fine as long as I loaded the objects in my Init method. I have since moved the load objects method to a service and call it on a different thread. This way the UI is responsive. However, how do I call the method in the Activity when the load is completed?
Here is my current code in the Activity (this code shouldn't change, just how it is called):
private void InitMapFragment()
{
foreach (var item in viewModel.Items)
{
var icon = BitmapDescriptorFactory.FromResource(Resource.Drawable.place_img);
var markerOptions = new MarkerOptions()
.SetPosition(new LatLng(item.Latitude, item.Longitude))
.InvokeIcon(icon)
.SetSnippet(item.DistanceText)
.SetTitle(item.Name);
var marker = _map.AddMarker(markerOptions);
_markerIds.Add(marker.Id, item.Id);
}
}
Code in my viewModel:
private void BeginLoadItems()
{
_loadItemsService.Load();
}
// This is triggered by a message
private void OnLoadItemsComplete(LoadCompleteMessage message)
{
Items = message.Items;
}
Code in my Service:
public void Load()
{
ThreadPool.QueueUserWorkItem(state =>
{
var results = _repository.Retrieve();
_messenger.Publish(new LoadCompleteMessage(this, results));
});
}
You're already triggering an event when you set:
Items = message.Items;
This triggers PropertyChanged with a property name of "Items"
For more on map binding, see Using MvvmCross how can I bind a list of annotations to a MapView? - although with Droid you'll need to use markers instead of annotations.

Resources