I'm creating a database of the images of the lake for further processing using the DJI Mavic Air drone. After flying to the observation point, I'm then use the elements.add(new AircraftYawAction(-70f, true)); with absolute heading against true north and take a series of photos with consequent rotations using elements.add(new AircraftYawAction(20f, false)); with relative heading.
The compass is calibrated and the photos are taken far from any interference sources at 200m altitude so I believe nothing disrupts the compass readings.
Nevertheless, I clearly have very low rotation precision as is seen from two shots below
Photo 1
Photo 2
I've reviewed the SDK documentation and I haven't found any YawAction precision adjustment or any alternative to accomplish this. Can anyone point me to the solution without using the image analysis?
Here is the complete function (without real coordinates, obviously)
public void onClick(View v) {
BaseProduct product = DJISDKManager.getInstance().getProduct();
if (product == null || !product.isConnected()) {
setRunningResultToText("\nNo device is connected");
missionControl = null;
return;
} else {
missionControl = MissionControl.getInstance();
if (product instanceof Aircraft) {
flightController = ((Aircraft) product).getFlightController();
}
}
flightController.setMaxFlightHeight(300, null);
flightController.setMaxFlightRadius(2000, null);
if (!GeneralUtils.checkGpsCoordinate(homeLatitude, homeLongitude))
{
setRunningResultToText("\nCan't get the coordinates");
return;
}
List<TimelineElement> elements = new ArrayList<>();
elements.add(new TakeOffAction());
elements.add(new GoToAction(new LocationCoordinate2D(homeLatitude, homeLongitude), 20f));
elements.add(new GoToAction(new LocationCoordinate2D(00.000000, -000.000000), 25f ));
elements.add(new GoToAction(new LocationCoordinate2D(11.111111, -111.111111), 200f ));
elements.add(new GimbalAttitudeAction(new Attitude(-15,Attitude.NO_ROTATION,Attitude.NO_ROTATION)));
elements.add(new AircraftYawAction(-70f, true));
elements.add(ShootPhotoAction.newShootSinglePhotoAction());
for(int i=0; i<numphoto-1;i++)
{
elements.add(new AircraftYawAction(20f, false));
elements.add(ShootPhotoAction.newShootSinglePhotoAction());
}
elements.add(new GoToAction(new LocationCoordinate2D(00.000000, -000.000000), 25f ));
elements.add(RecordVideoAction.newStopRecordVideoAction());
elements.add(new GoHomeAction());
elements.add(new LandAction());
if (MissionControl.getInstance().scheduledCount() > 0) {
MissionControl.getInstance().unscheduleEverything();
MissionControl.getInstance().removeAllListeners();
setRunningResultToText("\nCleaning previous");
}
MissionControl.getInstance().scheduleElements(elements);
MissionControl.getInstance().startTimeline();
setRunningResultToText("\nStarted sequence");
});
Related
Okay so I'm making a photography game where when you 'take a photo', Unity sends a few raycasts forward to check if certain tagged items are in the photo (all within the cameras FOV). My problem is, this seems to work intermittently! Sometimes it finds the tagged objects, other times it will be right in front of the view yet it will miss it completely! Can anyone advise about what I'm doing wrong?
public static Transform target;
public static GameObject[] targetName;
public static float length = 250f;
public static Transform thisObject;
// Start is called before the first frame update
void Start()
{
thisObject = GameObject.Find("Main Camera").GetComponent<Transform>();
//target = GameObject.FindGameObjectWithTag("Trees").transform;
}
// Update is called once per frame
void Update()
{
//InFront();
//HasLineOfSight("Trees");
}
public static bool InFront(Transform target1)
{
Vector3 directionToTarget = thisObject.position - target1.position;
float angleOnXAxis = Vector3.Angle(thisObject.right, directionToTarget);
float angleOnYAxis = Vector3.Angle(thisObject.up, directionToTarget);
//Debug.Log(angleOnYAxis);
if (Mathf.Abs(angleOnXAxis) < 130 && Mathf.Abs(angleOnXAxis) > 50
&& Mathf.Abs(angleOnYAxis) < 115 && Mathf.Abs(angleOnYAxis) > 62)
{
//Debug.DrawLine(transform.position, target.position, Color.green);
return true;
}
return false;
}
public static bool HasLineOfSight(string objectTag)
{
RaycastHit hit;
Vector3 direction = target.position - thisObject.position;
//Debug.Log(direction);
if (Physics.Raycast(thisObject.position, direction, out hit, length))
{
if (hit.transform.tag == objectTag)
{
Debug.DrawRay(thisObject.position, direction * 0.96f, Color.red);
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public static GameObject SortObjects(string objectTag)
{
targetName = GameObject.FindGameObjectsWithTag(objectTag);
GameObject closestObject = null;
for (int i = 0; i < targetName.Length; i++)
{
if (Vector3.Distance(thisObject.position,
targetName[i].transform.position) <= length)
{
if (InFront(targetName[i].transform))
{
if (closestObject == null)
{
closestObject = targetName[i];
}
else
{
if (Vector3.Distance(targetName[i].transform.position, thisObject.position) <= Vector3.Distance(closestObject.transform.position, thisObject.position))
{
closestObject = targetName[i];
}
}
}
}
}
return closestObject;
}
public static bool ObjectCheck(string objectTag)
{
//Debug.Log(SortObjects(objectTag));
if (SortObjects(objectTag) != null)
{
target = SortObjects(objectTag).transform;
//Debug.Log(target);
if (InFront(target) && HasLineOfSight(objectTag))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
I'm essentially calling ObjectCheck() with the tag I want to check for to get the closest, visible, object with that tag. What is wrong with this code??
In your script, only the closest object to the main camera gets checked. SortObjects() determines the closest tagged object, and then you only handle that single object in ObjectCheck(). - That object might be obstructed by something else, so the method returns false. And other tagged objects that are actually visible, are not picked up this way...
So, you could rename and change your SortObjects() function to check for both conditions right in the loop (InFront(target) && HasLineOfSight(objectTag)), and filter the objects out right in there, since only those objects are of interest.
Also, your HasLineOfSight() method checks the tag of the hit object, but what you probably wanted to do, is to check if the raycast actually hits that exact object. So it should instead compare the hit's gameObject to the target's gameObject, ignoring the tag, since a correct tag alone isn't enough. (Side note: it would make sense to place all "photographable objects" on a "photo layer", and set the layer mask in the Physics.Raycast() call accordingly, it's more efficient that way in larger scenes.)
The way the angles are calculated in the InFront() method is probably causing issues, because the direction vector to the target is really in 3D. To calculate the angles, you could try to use Vector3.Project() or Vector3.ProjectOnPlane(), but that will also be problematic, because of perspective camera issues.
This check is strongly related to the topic of "frustum culling", a technique usually used for rendering. But it's similar to what you need, to filter out all the (possibly) visible objects in the camera's field of view (frustum culling doesn't handle obstruction, it is just a geometric check to see if a point lies within the camera's frustum space). See:
https://en.wikipedia.org/wiki/Viewing_frustum
https://en.wikipedia.org/wiki/Hidden-surface_determination#Viewing-
http://www.lighthouse3d.com/tutorials/view-frustum-culling/
https://docs.unity3d.com/Manual/UnderstandingFrustum.html
If you want to dig deeper and optimize this, there are a couple of ways this can be done. But luckily, Unity comes with many useful related functions already built into the Camera class. So instead, you could use Camera.WorldToScreenPoint() (or Camera.WorldToViewportPoint()), and compare the resulting screen coordinates to the screen size or viewport, like discussed in Unity forum. (The frustum math is hidden behind these compact functions, but beware that this is probably not the optimal way to do this.)
Instead of calling FindGameObjectsWithTag() every time, you could do it only once in Start(), assuming objects do not get created/destroyed while the game is running.
I've tried to modify your script, since I'm also learning Unity again... The script can be dragged to the main camera, and it should show the "focus object" in the Scene view with the green debug line. I hope this helps:
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class PhotoCast : MonoBehaviour
{
public float maxDistance = 250.0f;
public string objectTag = "photo";
protected GameObject[] objs;
protected GameObject objFocus;
protected Camera cam;
public void Start() {
objs = GameObject.FindGameObjectsWithTag(objectTag);
cam = GetComponent<Camera>();
}
public void Update() {
if (Input.GetButtonDown("Fire1")) {
objFocus = CheckObjects();
if (objFocus) {
Debug.Log("closest object in view: " + objFocus.name);
/* TODO: take actual photo here */
}
}
if (objFocus) {
Debug.DrawLine(transform.position,
objFocus.transform.position, Color.green);
}
}
GameObject CheckObjects() {
GameObject obj_closest = null;
float dist_closest = float.MaxValue;
foreach (GameObject o in objs) {
float dist = Vector3.Distance(
o.transform.position, transform.position);
if (dist < maxDistance && dist < dist_closest
&& InViewport(o.transform.position)
&& HasLineOfSight(o.transform)) {
dist_closest = dist;
obj_closest = o;
}
}
return obj_closest;
}
bool InViewport(Vector3 worldPos) {
Vector3 p = cam.WorldToViewportPoint(worldPos);
return (p.x > 0.0f && p.x <= 1.0f && p.y > 0.0f && p.y <= 1.0f
&& p.z > cam.nearClipPlane);
}
bool HasLineOfSight(Transform target) {
RaycastHit hit;
Vector3 dir = target.position - transform.position;
if (Physics.Raycast(transform.position, dir, out hit, maxDistance)) {
if (hit.collider.gameObject == target.gameObject) {
return true;
}
}
return false;
}
}
Side notes:
Another issue with this technique is, that there can be tagged objects right in front of the camera, but other tagged objects that are closer on the side will be picked up instead of the obvious one. Many small issues to fine-tune until the scripts fits the game, I guess. Instead of only using one Raycast per object, you could use multiple ones, and take the bounding box or the actual collider shape into account.
An improved version of the script could make use of the Physics.Overlap*() or Physics.*Cast*() functions, documented here.
I'm trying to make a realistic transition between a walking animation and the falling process with a ragdoll. Basically when calling the "DoRagdoll" function, I'm adding the previous force from the walking animation to all rigidbody components in respect of their masses. Then I deactivate the animator component and turn on gravity for all rigidbodies. It works but it still kinda looks weird. I already tried a lot like the Unity documentation site for problems with ragdolls and I also played around with the joints of the different bodyparts. I used the buildin system to create the ragdoll. Any Ideas on how to improve the transition or how to create a more realistic ragdoll?
void Awake()
{
rbs = GetComponentsInChildren<Rigidbody>();
DoRagdoll(false);
foreach (Rigidbody rb in rbs)
{
rb.useGravity = false;
//rb.isKinematic = true;
}
}
public void DoRagdoll (bool isRagdoll)
{
if (isRagdoll)
{
foreach (Rigidbody rb in rbs)
{
rb.AddForce(rb.mass * rb.velocity, ForceMode.Impulse);
}
}
GetComponent<Animator>().enabled = !isRagdoll;
if (isRagdoll)
{
foreach (Rigidbody rb in rbs)
{
rb.maxDepenetrationVelocity = 0.01f;
rb.useGravity = true;
//rb.isKinematic = false;
}
NavMeshAgent agent = GetComponent<NavMeshAgent>();
//GameObject.FindGameObjectWithTag("pelvis").GetComponent<PelvisPush>().PushPelvis();
rbSlowdown = true;
ragdollOn = true;
}
else
{
GetComponent<NavMeshAgent>().speed = GameObject.FindGameObjectWithTag("root").GetComponent<OldmanMovement>().movSpeed;
foreach (Rigidbody rb in rbs)
{
rb.useGravity = false;
//rb.isKinematic = true;
}
ragdollOn = false;
}
}
When trying to start a new waypoint mission while one is already in progress, I usually get a OpenProtocol::ErrorCode::MissionACK::WayPoint::DATA_NOT_ENOUGH error when I try to upload the first waypoint. Strangely, if I stop the mission afterwards and try uploading again, it works.
Unfortunately, I couldn't find any detailed documentation on what this error means.
Here's my code (note that it's the onboard SDK, not the mobile SDK; the code is Java calling into native code; interface generated by SWIG):
public void uploadWaypointMissionImpl(#NonNull DroneMission mission) throws DroneException{
WayPointInitSettings wpis = newMissionSettings(mission);
MissionManager missionManager = vehicle.getMissionManager();
WaypointMission wptMission;
int wpcount = missionManager.getWayptCounter();
if (wpcount > 0){
wptMission = missionManager.getWpMission();
wptMission.stop(REQUEST_TIMEOUT_SECONDS);
wptMission.init(wpis, REQUEST_TIMEOUT_SECONDS);
} else{
missionManager.initWaypoint(wpis, REQUEST_TIMEOUT_SECONDS));
wptMission = missionManager.getWpMission();
}
if (wptMission == null)
throw new DroneException("Failed to initialize mission manager");
int pointCount = mission.getNavPoints().size();
logger.i("Waypoint mission created, uploading %d points", pointCount);
for (int i = 0; i < pointCount; ++i){
WayPointSettings wp = newWayPointSettings(mission, i);
wptMission.uploadIndexData(wp, REQUEST_TIMEOUT_SECONDS).getAck(); // <-- ERROR HERE
}
}
private static WayPointInitSettings newMissionSettings(#NonNull DroneMission mission){
WayPointInitSettings wpis = new WayPointInitSettings();
wpis.setMaxVelocity(mission.getVelocityMps());
wpis.setIdleVelocity(mission.getVelocityMps());
wpis.setTraceMode((short)(!mission.isInPlace() && Prefs.drone.traceMode().get() ? 1 : 0));
wpis.setRCLostAction((short)(Prefs.drone.rcLostMode().get() ? 1 : 0));
wpis.setGimbalPitch((short)(Prefs.drone.gimbalPitchAutoMode().get() ? 1 : 0));
wpis.setYawMode((short)0);
wpis.setExecutiveTimes((short)1);
wpis.setFinishAction((short)4);
wpis.setLatitude(0.0);
wpis.setLongitude(0.0);
wpis.setAltitude(0.0f);
wpis.setIndexNumber((short)mission.getNavPoints().size());
Uint8Array reserved = new Uint8Array(16);
for (int i = 0; i < 16; ++i){
reserved.setitem(i, (short)0);
}
wpis.setReserved(reserved.cast());
return wpis;
}
private static WayPointSettings newWayPointSettings(#NonNull DroneMission mission, int index){
WayPointSettings wp = new WayPointSettings();
wp.setDamping(Prefs.drone.dampingDistanceM().get());
wp.setGimbalPitch(Prefs.drone.gimbalPitch().get().shortValue());
wp.setYaw(Prefs.drone.yawDeg().get().shortValue());
wp.setTurnMode((short)(Prefs.drone.turnModeCounterClockwise().get() ? 1 : 0));
wp.setActionTimeLimit(100);
wp.setHasAction((short)0);
wp.setActionNumber((short)0);
wp.setActionRepeat((short)0);
Uint8Array commandList = new Uint8Array(16);
Uint16Array commandParameter = new Uint16Array(16);
for (int i = 0; i < 16; ++i){
commandList.setitem(i, (short)0);
commandParameter.setitem(i, 0);
}
wp.setCommandList(commandList.cast());
wp.setCommandParameter(commandParameter.cast());
NavPoint point = mission.getNavPoints().get(index);
wp.setLongitude(point.getLocation().getLngRad());
wp.setLatitude(point.getLocation().getLatRad());
wp.setAltitude((float)Math.ceil(point.getCruisingAltitude() - mission.getTakeoffAltitudeMeters()));
wp.setIndex((short)index);
return wp;
}
Waypoint mission needs a minimum of 3 waypoints when i tried it on an android device, try adding 2 more points but note that they must have 1 meter distance between each point
Edit: I added code for making waypoints and adding them to a list
// Create a waypoint instance
Waypoint mWaypoint1 = new Waypoint(Latitude, Longitude, altitude);
//check if the mission's builder is null
if (waypointMissionBuilder != null) {
waypointList.add(mWaypoint1);
waypointMissionBuilder.waypointList(waypointList).
waypointCount(waypointList.size());
}
else {
waypointMissionBuilder = new WaypointMission.Builder();
waypointList.add(mWaypoint1);
waypointMissionBuilder.waypointList(waypointList).
waypointCount(waypointList.size());
}
You need to make a waypoint instance which has Lat,Long,Alt. give it the correct parameters and add it to the list then use the builder to config, upload and start the mission. In case the list's size is 2 or less the mission will give you a djiError when trying to use the config function.
Hi,maybe you need to call the "wptMission.stop"before you start a new waypoint mission.All the data of this mission you write is uploaded to the flight controller,only when you call the "wptMission.stop",the flight controller will know you want to call other commands.
the code below is for a fundraiser dinner to purchase a land, the purpose is to show the progress of the square meter of land purchased (around 2976m2). everytime a square meter is purchased, the application adds an image tile which corresponds to an acctual 1m2. eventually the tiles (~2976 of them) fill up like in a grid to complete the land once fully purchased.
The size of each tiles is around 320bytes, there are 2976 tiles in total.
I have also showing below an image example.
The thing that drives me crazy with this code (in javafx) is that it consumes around 90 to 100% of 1 of my processors and the memory usage keeps increasing as the tiles add up until the code buffer run out of memory and the program crashes after a while. this is not desirable during the fundraising dinner.
the full code is available for testing at
you will need to change boolean split to true false, which will split the images for you, (around 3000 images);
https://github.com/rihani/Condel-Park-Fundraiser/tree/master/src/javafxapplication3
The main culprit that uses all the memory and CPU is the AnimationTimer() function shown below and I am wondering if anyone can help me reduce memory and CPU usage in this code.
to briefly explain how the code below is used, the land is divided into 2 panes, when the first one grid_pane1 is filled up the second pane grid_pane2 starts to then fill up.
also a flashing tile is used to show the current progress.
I am using total_donnation ++; to test the code, but would normally use mysql to pull the new value raised during the findraising dinner
AnimationTimer() Code:
translate_timer = new AnimationTimer() {
#Override public void handle(long now) {
if (now > translate_lastTimerCall + 10000_000_000l)
{
old_total_donnation = total_donnation;
try
{
// c = DBConnect.connect();
// SQL = "Select * from donations";
// rs = c.createStatement().executeQuery(SQL);
// while (rs.next())
// {total_donnation = rs.getInt("total_donnation");}
// c.close();
total_donnation ++;
if(total_donnation != old_total_donnation)
{
System.out.format("Total Donation: %s \n", total_donnation);
old_total_donnation = total_donnation;
if (!pane1_full)
{
grid_pane1.getChildren().clear();
grid_pane1.getChildren().removeAll(imageview_tile1,hBox_outter_last);
}
grid_pane2.getChildren().clear();
grid_pane2.getChildren().removeAll(imageview_tile2,hBox_outter_last);
for(i=0; i<=total_donnation; i++)
{
if (pane1_full){ System.out.println("Pane 1 has not been redrawn"); break;}
file1 = new File("pane1_img"+i+".png");
pane1_tiled_image = new Image(file1.toURI().toString(),image_Width,image_Height,false,false);
imageview_tile1 = new ImageView(pane1_tiled_image);
grid_pane1.add(imageview_tile1, current_column_pane1,current_row_pane1);
current_column_pane1 = current_column_pane1+1;
if (current_column_pane1 == max_columns_pane1 )
{
current_row_pane1 = current_row_pane1+1;
current_column_pane1 = 0;
}
if (i == max_donnation_pane1 ){ pane1_full = true; System.out.println("Pane 1 full"); break;}
if (i == total_donnation)
{
if (i != max_donnation_pane1)
{
hBox_outter_last = new HBox();
hBox_outter_last.setStyle(style_outter);
hBox_outter_last.getChildren().add(blink_image);
ft1 = new FadeTransition(Duration.millis(500), hBox_outter_last);
ft1.setFromValue(1.0);
ft1.setToValue(0.3);
ft1.setCycleCount(Animation.INDEFINITE);
ft1.setAutoReverse(true);
ft1.play();
grid_pane1.add(hBox_outter_last, current_column_pane1,current_row_pane1);
}
}
}
if (i < total_donnation)
{
total_donnation_left = total_donnation - max_donnation_pane1;
for(j=0; j<=total_donnation_left; j++)
{
file2 = new File("pane2_img"+j+".png");
pane2_tiled_image = new Image(file2.toURI().toString(),image_Width,image_Height,false,false);
imageview_tile2 = new ImageView(pane2_tiled_image);
grid_pane2.add(imageview_tile2, current_column_pane2,current_row_pane2);
current_column_pane2 = current_column_pane2+1;
if (current_column_pane2 == max_columns_pane2 )
{
current_row_pane2 = current_row_pane2+1;
current_column_pane2 = 0;
}
if (j == max_donnation_pane2 ){ System.out.println("Pane 2 full"); break;}
if (j == total_donnation_left)
{
if (j != max_donnation_pane2)
{
hBox_outter_last = new HBox();
hBox_outter_last.setStyle(style_outter);
hBox_outter_last.getChildren().add(blink_image);
ft = new FadeTransition(Duration.millis(500), hBox_outter_last);
ft.setFromValue(1.0);
ft.setToValue(0.3);
ft.setCycleCount(Animation.INDEFINITE);
ft.setAutoReverse(true);
ft.play();
grid_pane2.add(hBox_outter_last, current_column_pane2,current_row_pane2);
}
}
}
}
current_column_pane1 =0;
current_row_pane1=0;
current_column_pane2=0;
current_row_pane2=0;
}
}
catch (Exception ex) {}
translate_lastTimerCall = now;
}
}
};
First and foremost, you create a lot of indefinite FadeTransitions that are never stopped. These add up over time and cause both memory and CPU leaks. You should stop() the transition before starting a new one. Alternatively, you only need one transition to interpolate the value of a DoubleProperty and then bind node's opacity to this property:
DoubleProperty opacity = new SimpleDoubleProperty();
Transition opacityTransition = new Transition() {
protected void interpolate(double frac) {
opacity.set(frac);
}
};
// elsewhere
hBox_outter_last.opacityProperty().bind(opacity);
You may want to preload all the image tiles beforehand, so that you avoid reading from disk in the loop.
You unnecessarily destroy and recreate large part of the scene in every cycle. You should modify your code to only add the new tiles and not drop them all and recreate them from scratch.
Finally, when you actually query the database, you should do it from a different thread and not the JavaFX application thread, because your UI will be unresponsive for the time of the query (e.g. not animating your fade transitions).
I have a suggestion:
Do not split the image instead using 2 panels. One for displaying the whole image. The second will be a grid pane overlapping the first pane. Therefore, when a square meter is purchased, the background of corresponding grid-cell will become transparent.
Hey guys I am in the process of building this simple billiard game, and I want the black ball, labeled bBall, to go the same distance as the white ball, labeled wBall, and no farther. ie, if the white ball travels 20 pixels before it hits the black ball, I want the black ball to travel 20 pixels and then stop. How might I go about accomplishing this? Thanks for the help guys.
processing 2.0.3
ball wBall, bBall;
int click;
String msg;
Boolean moving = false;
float difx, dify;
float cdistance;
int steps = 40;
void setup(){
click=0;
size(800,400);
background(16,77,27);
wBall = new ball(35,#ffffff);
bBall = new ball(35,#000000);
msg="";
}
void mouseClicked(){
if(!moving){
click++;
}
}
void draw(){
background(16,77,27);
String msg;
fill(0,0,0);
ellipse(15,15,30,30);
ellipse(785,15,30,30);
ellipse(15,385,30,30);
ellipse(785,385,30,30);
ellipse(410,15,30,30);
ellipse(410,385,30,30);
msg="the count is "+click;
println("the count is "+click);
//Moving Balls\\
fill(255,255,255);
noStroke();
if(click==0){
wBall.xpos=mouseX;
wBall.ypos=mouseY;
}else if(click==1){
bBall.xpos=mouseX;
bBall.ypos=mouseY;
}else if(click==2){
difx = wBall.xpos-bBall.xpos;
dify = wBall.ypos-bBall.ypos;
}
else if(click==3){
cdistance = dist(wBall.xpos,wBall.ypos,bBall.xpos,bBall.ypos);
if (cdistance>bBall.ballDiam/2){
moving = true;
wBall.xpos-=difx/steps;
wBall.ypos-=dify/steps;
}
else{
moving = false;
click=4;
println("click"+click);
}
}else if(click==4){
if(cdistance<bBall.ballDiam){
moving = true;
bBall.xpos-=difx/steps;
bBall.ypos-=dify/steps;
}
}
wBall.update();
bBall.update();
}
class ball{
float xpos, ypos;
color myColor;
int ballDiam;
boolean visible = true;
ball(int tempdiam, color tempColor){
myColor=tempColor;
ballDiam=tempdiam;
}
void update(){
if(visible){
fill(myColor);
ellipse(xpos,ypos,ballDiam,ballDiam);
}
}
}
void keyPressed(){
if (key =='c'){
setup();
}
}
One way would be to make your
else if (click==4) {
if (cdistance<bBall.ballDiam) {
moving = true;
bBall.xpos-=difx/steps;
bBall.ypos-=dify/steps;
}
}
to
else if (click==4) {
if (cdistance<bBall.ballDiam) {
if (dist(wBall.xpos, wBall.ypos, bBall.xpos, bBall.ypos) < sqrt(sq(difx)+sq(dify))) {
moving = true;
bBall.xpos-=difx/steps;
bBall.ypos-=dify/steps;
}
}
}
essentially only moving the ball as long as its distance from the white one is less than the original difference...
Despite this though I feel you are approaching this the hard way. Maybe take a look to introduce speed and acceleration (and maybe even friction) calculations to your game and things will make more sense...