I am using a code I got from a site for a heartbeat sensor. The signal, when displayed by this code looks something like this:
Could you help me add a check which will increment an integer every time a signal goes above a certain threshold? This needs to happen for 10 seconds only, after 10 seconds the check stops and then gets multiplied by 6 to display the amount of beats per minute.
The code I'm using gets the imaging done, I would like to add the beats per minute onto it.
import processing.serial.*;
Serial myPort; // The serial port
int xPos = 1; // horizontal position of the graph
float oldHeartrateHeight = 0; // for storing the previous reading
void setup () {
// set the window size:
size(600, 400);
frameRate(25);
// List available serial ports.
println(Serial.list());
// Setup which serial port to use.
// This line might change for different computers.
myPort = new Serial(this, Serial.list()[1], 9600);
// set inital background:
background(0);
}
void draw () {
}
void serialEvent (Serial myPort) {
// read the string from the serial port.
String inString = myPort.readStringUntil('\n');
if (inString != null) {
// trim off any whitespace:
inString = trim(inString);
// convert to an int
println(inString);
int currentHeartrate = int(inString);
// draw the Heartrate BPM Graph.
float heartrateHeight = map(currentHeartrate, 0, 1023, 0, height);
stroke(0,255,0);
line(xPos - 1, height - oldHeartrateHeight, xPos, height - heartrateHeight);
oldHeartrateHeight = heartrateHeight;
// at the edge of the screen, go back to the beginning:
if (xPos >= width) {
xPos = 0;
background(0);
} else {
// increment the horizontal position:
xPos++;
}
}
}
Disclaimer: Of course, it goes without saying, this is only a guideline. Don't hook it up to someone's heart without thorough testing!
The simplest of checks is to look out for whenever the signal crosses a virtual line - typically the midpoint, like so:
That immediately makes things easier - we just need to check when our latest value is above the line, and the previous one is below it; whenever that happens, our signal must've crossed the line. That's as simple as this, using 750 as your midpoint:
int currentHeartrate = int(inString);
int midpoint=750;
if(currentHeartrate >= midpoint && oldHeartrateHeight < midpoint){
// It crossed the line!
beats++;
}
Looking closer at your signal, it's really noisy, which means we might get pretty unlucky with a sample which goes above the line then immediately drops below it giving us a false reading. To deal with that, you could add a moving average to your currentHeartrate value - that'll smooth out the fine noise for you. Add this to your project:
public class MovingAverage {
private final float[] window;
private float sum = 0f;
private int fill;
private int position;
public MovingAverage(int size) {
this.window=new float[size];
}
public void add(float number) {
if(fill==window.length){
sum-=window[position];
}else{
fill++;
}
sum+=number;
window[position++]=number;
if(position == window.length){
position=0;
}
}
public float getAverage() {
return sum / fill;
}
}
Rather than using currentHeartrate and oldHeartrateHeight, you'd instead first obtain the moving average - let's call it averageHeartrate - then cache that in oldAverageHeartrate and perform the same comparison with these two values instead.
Taking this a little further, you could make your BPM indicator realtime by counting the number of samples between these beat marks. As you've got a fixed number of samples per second, you then divide those and apply another moving average to this time reading. That then gives us this:
public int samplesSinceBeat; // Tracks the # of samples since the prev beat
public float oldAverageHeartrate; // Renamed
public int samplesPerSecond=10000; // However many samples/sec
public float midpoint=750; // The midpoint
public MovingAverage averageSamples=new MovingAverage(10); // Averaging over 10 samples
public MovingAverage beatTimeAverage=new MovingAverage(4); // Averaging over 4 beats
..
int currentHeartrate = int(inString);
// Add to your moving average buffer:
averageSamples.add(currentHeartrate);
float averageHeartrate=averageSamples.getAverage();
// Bump up the number of samples since the last beat:
samplesSinceBeat++;
if(averageHeartrate >= midpoint && oldAverageHeartrate < midpoint){
// It crossed the line - we have a beat!
// The time since the last beat is therefore:
float timeBetweenBeats=(float)samplesSinceBeat / (float)samplesPerSecond;
// Add time between beats to another moving average:
beatTimeAverage.add(timeBetweenBeats);
// Reset samples since beat:
samplesSinceBeat=0;
}
oldAverageHeartrate=averageHeartrate;
// The BPM is now this:
int realtimeBPM= (int)(60f / beatTimeAverage.getAverage() );
Some signals are evil and have a moving midpoint too. If this is the case, I would approach that by recording:
The largest value seen since the previous beat
The smallest value seen since the previous beat
Then simply take the midpoint from those. Essentially, you'd end up tracking the midpoint as you go:
if( currentHeartrate > maxHeartrateValue){
maxHeartrateValue=currentHeartrate;
}
if( currentHeartrate < minHeartrateValue){
minHeartrateValue=currentHeartrate;
}
// This line is unchanged:
if(averageHeartrate >= midpoint && oldAverageHeartrate < midpoint){
// It crossed the line - we have a beat!
midpoint=(minHeartrateValue + maxHeartrateValue) / 2;
// Clear!
minHeartrateValue=Integer.MAX_VALUE;
maxHeartrateValue=Integer.MIN_VALUE;
..
Related
Background
Many similar questions seem to have been asked on SO before (most notably android google maps not loading the map when using GoogleMap.AnimateCamera() and How can I smoothly pan a GoogleMap in Android?), but none of the answers or comments posted throughout those threads have given me a firm idea of how to do this.
I initially thought that it would be as simple as just calling animateCamera(CameraUpdateFactory.newLatLng(), duration, callback) but like the OP of the first link above, all I get is a gray or very blurry map until the animation completes, even if I slow it down to tens of seconds long!
I've managed to find and implement this helper class that does a nice job of allowing the tiles to render along the way, but even with a delay of 0, there is a noticeable lag between each animation.
Code
OK, time for some code. Here's the (slightly-modified) helper class:
package com.coopmeisterfresh.googlemaps.NativeModules;
import android.os.Handler;
import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.GoogleMap;
import java.util.ArrayList;
import java.util.List;
public class CameraUpdateAnimator implements GoogleMap.OnCameraIdleListener {
private final GoogleMap mMap;
private final GoogleMap.OnCameraIdleListener mOnCameraIdleListener;
private final List<Animation> cameraUpdates = new ArrayList<>();
public CameraUpdateAnimator(GoogleMap map, GoogleMap.
OnCameraIdleListener onCameraIdleListener) {
mMap = map;
mOnCameraIdleListener = onCameraIdleListener;
}
public void add(CameraUpdate cameraUpdate, boolean animate, long delay) {
if (cameraUpdate != null) {
cameraUpdates.add(new Animation(cameraUpdate, animate, delay));
}
}
public void clear() {
cameraUpdates.clear();
}
public void execute() {
mMap.setOnCameraIdleListener(this);
executeNext();
}
private void executeNext() {
if (cameraUpdates.isEmpty()) {
mOnCameraIdleListener.onCameraIdle();
} else {
final Animation animation = cameraUpdates.remove(0);
new Handler().postDelayed(() -> {
if (animation.mAnimate) {
mMap.animateCamera(animation.mCameraUpdate);
} else {
mMap.moveCamera(animation.mCameraUpdate);
}
}, animation.mDelay);
}
}
#Override
public void onCameraIdle() {
executeNext();
}
private static class Animation {
private final CameraUpdate mCameraUpdate;
private final boolean mAnimate;
private final long mDelay;
public Animation(CameraUpdate cameraUpdate, boolean animate, long delay) {
mCameraUpdate = cameraUpdate;
mAnimate = animate;
mDelay = delay;
}
}
}
And my code to implement it:
// This is actually a React Native Component class, but I doubt that should matter...?
public class NativeGoogleMap extends SimpleViewManager<MapView> implements
OnMapReadyCallback, OnRequestPermissionsResultCallback {
// ...Other unrelated methods removed for brevity
private void animateCameraToPosition(LatLng targetLatLng, float targetZoom) {
// googleMap is my GoogleMap instance variable; it
// gets properly initialised in another class method
CameraPosition currPosition = googleMap.getCameraPosition();
LatLng currLatLng = currPosition.target;
float currZoom = currPosition.zoom;
double latDelta = targetLatLng.latitude - currLatLng.latitude;
double lngDelta = targetLatLng.longitude - currLatLng.longitude;
double latInc = latDelta / 5;
double lngInc = lngDelta / 5;
float zoomInc = 0;
float minZoom = googleMap.getMinZoomLevel();
float maxZoom = googleMap.getMaxZoomLevel();
if (lngInc > 15 && currZoom > minZoom) {
zoomInc = (minZoom - currZoom) / 5;
}
CameraUpdateAnimator animator = new CameraUpdateAnimator(googleMap,
() -> googleMap.animateCamera(CameraUpdateFactory.zoomTo(
targetZoom), 5000, null));
for (double nextLat = currLatLng.latitude, nextLng = currLatLng.
longitude, nextZoom = currZoom; Math.abs(nextLng) < Math.abs(
targetLatLng.longitude);) {
nextLat += latInc;
nextLng += lngInc;
nextZoom += zoomInc;
animator.add(CameraUpdateFactory.newLatLngZoom(new
LatLng(nextLat, nextLng), (float)nextZoom), true);
}
animator.execute();
}
}
Question
Is there a better way to accomplish this seemingly-simple task? I'm thinking that perhaps I need to move my animations to a worker thread or something; would that help?
Thanks for reading (I know it was an effort :P)!
Update 30/09/2021
I've updated the code above in line with Andy's suggestions in the comments and although it works (albeit with the same lag and rendering issues), the final algorithm will need to be a bit more complex since I want to zoom out to the longitudinal delta's half-way point, then back in as the journey continues.
Doing all these calculations at once, as well as smoothly rendering all the necessary tiles simultaneously, seems to be way too much for the cheap mobile phone that I'm testing on. Or is this a limitation of the API itself? In any case, how can I get all of this working smoothly, without any lag whatsoever between queued animations?
Here's my attempt using your utility frame player.
A few notes:
The zoom value is interpolated based on the total steps (set at 500 here) and given the start and stop values.
A Google Maps utility is used to compute the next lat lng based on a fractional distance: SphericalUtil.interpolate.
The fractional distance should not be a linear function to reduce the introduction of new tiles. In other words, at higher zooms (closer in) the camera moves in shorter distances and the amount of camera movement increases exponentially (center-to-center) while zooming out. This requires a bit more explanation...
As you can see the traversal is split into two - reversing the exponential function of the distance movement.
The "max" zoom (bad name) which is the furthest out can be a function of the total distance - computed to encompass the whole path at the midpoint. For now it's hard coded to 4 for this case.
Note the maps animate function cannot be used as it introduces its own bouncing ball effect on each step which is undesirable. So given a fair number of steps the move function can be used.
This method attempts to minimize tile loading per step but ultimately the TileLoader is the limiting factor for viewing which cannot monitored (easily).
animateCameraToPosition
// flag to control the animate callback (at completion).
boolean done = false;
private void animateCameraToPosition(LatLng targetLatLng, float targetZoom) {
CameraPosition currPosition = gMap.getCameraPosition();
LatLng currLatLng = currPosition.target;
//meters_per_pixel = 156543.03392 * Math.cos(latLng.lat() * Math.PI / 180) / Math.pow(2, zoom)
int maxSteps = 500;
// number of steps between start and midpoint and midpoint and end
int stepsMid = maxSteps / 2;
// current zoom
float initz = currPosition.zoom;
//TODO maximum zoom (can be computed from overall distance) such that entire path
// is visible at midpoint.
float maxz = 4.0f;
float finalz = targetZoom;
CameraUpdateAnimator animator = new CameraUpdateAnimator(gMap, () -> {
if (!done) {
gMap.animateCamera(CameraUpdateFactory.
zoomTo(targetZoom), 5000, null);
}
done = true;
});
// loop from start to midpoint
for (int i = 0; i < stepsMid; i++) {
// compute interpolated zoom (current --> max) (linear)
float z = initz - ((initz - maxz) / stepsMid) * i;
// Compute fractional distance using an exponential function such that for the first
// half the fraction delta advances slowly and accelerates toward midpoint.
double ff = (i * (Math.pow(2,maxz) / Math.pow(2,z))) / maxSteps;
LatLng nextLatLng =
SphericalUtil.interpolate(currLatLng, targetLatLng, ff);
animator.add(CameraUpdateFactory.newLatLngZoom(
nextLatLng, z), false, 0);
}
// loop from midpoint to final
for (int i = 0; i < stepsMid; i++) {
// compute interpolated zoom (current --> max) (linear)
float z = maxz + ((finalz - maxz) / stepsMid) * i;
double ff = (maxSteps - ((i+stepsMid) * ( (Math.pow(2,maxz) / Math.pow(2,z)) ))) / (double)maxSteps;
LatLng nextLatLng =
SphericalUtil.interpolate(currLatLng, targetLatLng, ff);
animator.add(CameraUpdateFactory.newLatLngZoom(
nextLatLng, z), false, 0);
}
animator.add(CameraUpdateFactory.newLatLngZoom(
targetLatLng, targetZoom), true, 0);
//
animator.execute();
}
Test Code
I tested with these two points (and code) from Statue Of Liberty to a point on the west coast:
gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40.68924, -74.04454), 13.0f));
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
animateCameraToPosition(new LatLng(33.899832, -118.020450), 13.0f);
}
}, 5000);
CameraUpdateAnimator Mods
I modified the camera update animator slightly:
public void execute() {
mMap.setOnCameraIdleListener(this);
executeNext();
}
private void executeNext() {
if (cameraUpdates.isEmpty()) {
mMap.setOnCameraIdleListener(mOnCameraIdleListener);
mOnCameraIdleListener.onCameraIdle();
} else {
final Animation animation = cameraUpdates.remove(0);
// This optimization is likely unnecessary since I think the
// postDelayed does the same on a delay of 0 - execute immediately.
if (animation.mDelay > 0) {
new Handler().postDelayed(() -> {
if (animation.mAnimate) {
mMap.animateCamera(animation.mCameraUpdate);
} else {
mMap.moveCamera(animation.mCameraUpdate);
}
}, animation.mDelay);
} else {
if (animation.mAnimate) {
mMap.animateCamera(animation.mCameraUpdate);
} else {
mMap.moveCamera(animation.mCameraUpdate);
}
}
}
}
Before Sample
Using
// assume initial (40.68924, -74.04454) z=13.0f
gMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(33.899832,-118.020450), 13.0f), 30000, null);
After Samples
These are recorded from an emulator. I also sideloaded onto my phone (Samsumg SM-G960U) with similar results (using 1000 steps 0 delay).
So I don't think this meets your requirements entirely: there are some "ambiguous tiles" as they are brought in from the west.
Statue of Liberty - to - somewhere near San Diego
500 Steps 0 delay
100 Steps 0 delay
50 Steps 100MS delay
Diagnostics
It is in some ways useful to have insight into what Maps is doing with tiles. Insight can be provided by installing a simple UrlTileProvider and log the requests. This implementation fetches the google tiles though they are lower resolution that is normally seen.
To do this the following is required:
// Turn off this base map and install diagnostic tile provider
gMap.setMapType(GoogleMap.MAP_TYPE_NONE);
gMap.addTileOverlay(new TileOverlayOptions().tileProvider(new MyTileProvider(256,256)).fadeIn(true));
And define the diagnostic file provider
public class MyTileProvider extends UrlTileProvider {
public MyTileProvider(int i, int i1) {
super(i, i1);
}
#Override
public URL getTileUrl(int x, int y, int zoom) {
Log.i("tiles","x="+x+" y="+y+" zoom="+zoom);
try {
return new URL("http://mt1.google.com/vt/lyrs=m&x="+x+"&y="+y+"&z="+zoom);
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
}
You'll notice right away that tile layers are always defined in integral units (int). The fractional zooms which are supplied in the zoom (e.g. LatLngZoom work strictly with the in-memory images - good to know.'
Here's a sample for completeness:
// initial zoom
x=2411 y=3080 zoom=13
x=2410 y=3080 zoom=13
x=2411 y=3081 zoom=13
x=2410 y=3081 zoom=13
x=2411 y=3079 zoom=13
x=2410 y=3079 zoom=13
And at max:
x=9 y=12 zoom=5
x=8 y=12 zoom=5
x=9 y=11 zoom=5
x=8 y=11 zoom=5
x=8 y=13 zoom=5
x=9 y=13 zoom=5
x=7 y=12 zoom=5
x=7 y=11 zoom=5
x=7 y=13 zoom=5
x=8 y=10 zoom=5
x=9 y=10 zoom=5
x=7 y=10 zoom=5
Here's a chart of the zooms (y-axis) at each invocation of tiler (x-axis). Each zoom layer are roughly the same count which imo is what is desired. The full-out zoom appears twice as long because that's the midpoint repeating. There are a few anomalies though which require explaining (e.g. at around 110).
This is a chart of "zoom" as logged by the tile provider. So each x-axis point would represent a single tile fetch.
I am making a paint application and the flood fill tool works, but it takes about two minutes for it to fill a 400x180. What can I do to speed up this process? Here is the code I am currently using for this.
public void gradientSize(int x, int y, int origRGB, int index){
queue = new ArrayList<String>(); //queue is an ArrayList<String> that holds the points
time = System.currentTimeMillis(); // time is a long so I can calculate the time it takes to finish a flood fill
if(new Color(origRGB).equals(foreground)){ //foreground is the color the flood fill is using to fill in. origRGB is the RGB of the color I clicked
return;
}
if(!testFill(x, y, origRGB)){
return;
}
queue.add(pixel(x,y));
while(!queue.isEmpty()){
String pixel = queue.get(0);
int x2 = Integer.parseInt(pixel.substring(0, pixel.indexOf(",")));
int y2 = Integer.parseInt(pixel.substring(pixel.indexOf(",")+1,pixel.length()));
queue.remove(0);
if(testFill(x2, y2, origRGB)){
queue.add(pixel(x2+1, y2));
queue.add(pixel(x2-1,y2));
queue.add(pixel(x2,y2+1));
queue.add(pixel(x2,y2-1));
gradientPoints.add(pixel(x2, y2)); //gradientPoints is an ArrayList<String> that contains all the points for the fill
processed[y*image.getWidth()+x] = true; //processed[] is a boolean array that has a true or false value for each pixel to determine if it has been looked at yet.
}
}
}
public boolean testFill(int x, int y,int origRGB){ //testFill tests if the current pixel is okay to be filled or not
if(x>=0&&x<image.getWidth()&&y>=0&&y<image.getHeight()){
int testRGB = image.getRGB(x, y);
Color orig = new Color(origRGB,true);
Color test = new Color(testRGB,true);
if ((Math.abs(orig.getRed() - test.getRed()) <= difference) && (Math.abs(orig.getGreen() - test.getGreen()) <= difference)&& (Math.abs(orig.getBlue() - test.getBlue()) <= difference)&&(Math.abs(orig.getAlpha() - test.getAlpha()) <= difference)) {
if (!gradientPoints.contains(pixel(x,y))) {
if (!queue.contains(pixel(x,y))) {
if (processed[y*image.getWidth()+x]==false) {
return true;
}
}
}
}
}
return false;
}
public String pixel(int x, int y){//this returns the String value of a pixel's x and y coordinates.
return String.valueOf(x)+","+String.valueOf(y);
}
public void gradientFillSolid(){ //This gets all the points from gradientPoints and fills each pixel from there.
for(String s:gradientPoints){
int x = Integer.parseInt(s.substring(0, s.indexOf(',')));
int y = Integer.parseInt(s.substring(s.indexOf(',')+1,s.length()));
image.setRGB(x, y, foreground.getRGB());
}
System.out.println(System.currentTimeMillis()-time);
repaint();
}
The output for a 400x180 rectangle was 148566 milliseconds. Is there a way for me to speed up this process at all? Any help is appreciated.
Here's your problem:
queue.add(pixel(x2+1, y2));
queue.add(pixel(x2-1,y2));
queue.add(pixel(x2,y2+1));
queue.add(pixel(x2,y2-1));
You're adding every pixel multiple times (once here, and once for every block around that particular pixel) and rechecking it every time it's added again. If you had a 4x4 block, or something, you really wouldn't notice a slowdown, but when we're talking about 400x180 (72,000) pixels being added and checked 3 or 4 times per pixel, it gets to be huge.
My suggestion is very simple: Check before you add. Or even better, make a small little "MyPixel" class that has a boolean value that is flipped to true after you've already checked it. That way, you can skip doing any math on it and you can just do something like this:
if(my_pixel.has_been_checked == false)
queue.add(my_pixel);
You are converting the pixel coordinates to a String, then parsing them back out. I have found in my experience that string concatenation is an expensive action. Instead, just store pixels as java.awt.Point objects and read the coordinates from those.
I'm developing a racing game like http://harmmade.com/vectorracer/ and I have implemented the A* algorithm to use for the AI players. The algorithm is working fine for 1-tile movements, but I don't want the AI players to only move 1 tile at a time (by using only their adjacent points), I need them to be able to accelerate and decelerate when they are closing in on a turn. Their next positions should depend on their previous one, just like Vector Racer.
public boolean createRoute() {
// The list where the points will be added in reverse order (from finish_point)
ArrayList<Track_Point> path = new ArrayList<>();
// The list where the unchecked points will be stored
ArrayList<Track_Point> open = new ArrayList<>();
// The list where the checked points will be stored
ArrayList<Track_Point> closed = new ArrayList<>();
// The starting point is always added as the first point to be checked
open.add(starting_point);
Track_Point current;
while (true) {
current = null;
// If all points from the open list have been removed (be being checked), it means that there isn't a possible path from the starting to the finish point
if (open.isEmpty()) {
System.out.println("no route available");
return false;
}
// Selects the point with the lowest F value from the open list
for (Track_Point temp : open) {
temp.show();
if (current == null || temp.getF() < current.getF()) {
current = temp;
}
}
// If the current point has reached the finish point, break the loop to construct the path
if (current.equals(finish_point)) {
break;
}
// Removes the current point (with the lowest F value) from the open list
open.remove(current);
// Adds the current point (with the lowest F value) to the closed list
closed.add(current);
ArrayList<Track_Point> possible_points = createNextPossibleTrackPoints(current);
//Sets the parent of the possible points
for (Track_Point tp : possible_points) {
if (!tp.equals(current)) {
tp.setParent(current);
}
}
for (Track_Point possible_point : possible_points) {
double nextG = current.getG() + current.distance(possible_point);
if (nextG < possible_point.getG()) {
open.remove(possible_point);
closed.remove(possible_point);
}
if (!open.contains(possible_point) && !closed.contains(possible_point)) {
possible_point.setParent(current);
open.add(possible_point);
}
}
}
//Track_Point current = finish_point;
while (current.getParent() != null) {
path.add(current);
current = current.getParent();
}
// optimalRacingLine is the list where all the points will be held in the correct order
optimalRacingLine.add(starting_point);
for (int k = path.size() - 1; k >= 0; k--) {
optimalRacingLine.add(path.get(k));
}
return true;
}
createPossiblePoints(Point current) so far returns a list of the current point's adjacents.
Each point's H value is calculated in their constructor, as I'm passing the finish point there and it calculates the distance between them.
Each point's G value is calculated when I set a parent to it, the G value is the distance from the new point to their parent + the parent's G value.
How do I modify this code to allow acceleration/deceleration?
The code of Track_Point:
package model;
import javafx.geometry.Point2D;
public class Track_Point extends Point2D {
private Track_Point parent, velocity;
private double f, g, h;
public Track_Point(double x, double y) {
super(x, y);
}
public Track_Point(double x, double y, Track_Point f) { // f is the finish point
super(x, y);
h = distance(f);
}
public void setParent(Track_Point tp) {
parent = tp;
g = distance(tp) + tp.getG();
f = g + h;
velocity = new Track_Point(getX() - parent.getX(), getY() - parent.getY());
}
public Track_Point getParent() {
return parent;
}
public double getG() {
return g;
}
public double getH() {
return h;
}
public double getF() {
return f;
}
public Track_Point getVelocity() {
return velocity;
}
#Override
public String toString() {
return "( " + (int) getX() + " , " + (int) getY() + " )";
}
public void show() {
System.out.println(toString());
}
}
Added some screenshots of my failed attempt and the working simple A* version
http://tinypic.com/r/zlakg2/8 - working version
http://tinypic.com/r/2e3u07o/8 - modified version (uses velocity as a parameter in the createNextPossiblePoints method)
Firstly, don't use an integers for the x/y position. There should be no such thing as '1 tile' in a racing game. Your game world and output can be completely different. For example, consider using doubles to store x and y. Ssh, don't worry, your JFrame doesn't need to know.
Heuristics
You are using A* to run your AI? Consider these additional heuristics:
Prefer high velocities; cost = max velocity - current velocity
Stay near inside edge of turn (imagine the turn as the outside edge of a circle); cost = distance_from(focus of turn)
Avoid walls; cost = isMovable(x, y) ? 0 : infinite/high
EDIT Prefer shortest path to avoid taking unnecessary moves as your second image does (Breadth First search not Djikstra); cost = steps from first node
The way A* works is as follows:
Use Djikstra (distance from origin) + Greedy (distance to target)
Insert your heuristics here
Add them all together and choose the lowest number
There's no such thing as f, g, or h; it's just mathematical nonsense you don't need to know.
Velocity
velocity = Math.abs(position1 - position2); so... position1 + velocity = position2.
You'll need to add the following variables:
int xVelocity
int yVelocity
Each moment, x += xVelocity; y += yVelocity.
The next position will be xf = x + xVelocity; yf = y + yVelocity. Then, you draw a ring around that position as follows:
+yVelocity
\|/
-xVelocity -0- +xVelocity
/|\
-yVelocity
So the center retains the same velocity, any adjacent side changes one velocity, and any diagonal side changes both velocities.
As for using A* the solution space of a turn is small enough that you can brute force it; don't add TrackPoints to the open list if you bump into a wall and prefer the highest velocity.
Really, that's all there is to it; simple stuff, but it can be tedious and difficult the first few times you need to do it.
EDIT: Just played vector racer and it's actually a lot simpler than I expected. I thought you were making a full blown 2d racing game. What I've told you is still very applicable, but you'll need to make a few adjustments, particularly to the way you handle rotation. You'll definitely want to look up the racing line. I don't have the time at the moment to go over the mathematics of the racing line, but this should help you calculate it.
EDIT2: Updated Velocity section. I'll do some calculations to figure out a faster heuristic, but what is present is enough to check 3-10 moves ahead without major performance issues.
I have a method that fills an array and I need to find a way to make it repeat a number of times. The purpose is to iterate and reiterate the density of a planet to narrow its mass,gravity and densities at specific points which are concentric shells. This is my first program but, I have learned a decent amount while working on this I think. Thanks everyone
Here is my code sample of the density calculation. I probably included too much but oh well. So I need to make this iterate selected number of times. Each iteration needs to be put back into the mass calculation which will then be put back into the gravity calculation. And then the show starts again.
public class ItrDensityGrid {
public double itrrho[];
double b = InitialConditions.bmod;
// Iterating grid of densities
public ItrDensityGrid(int shells, double radius, double mass){
GravityGrid gg = new GravityGrid(shells, radius, mass);
for(int k = shells; k >= 0; k--){
itrrho[k] = (itrrho[k]*(1+(gg.alpha[k]*(1.0 / 2)))*(1 / (1-((gg.alpha[k])*(1.0 / 2)))));
}
}
}
This can be achieved with the help of Recursion, or looping.
In recursion, you call the method again from inside of the method itself. Make sure to call (or return) conditionally, otherwise, it may lead to infinite loop!
Here is an example with recursion:
public planetMars (double density, double mass) {
// do your calculations
density = density / 10.05312;
mass = mass / 7.2378;
myArray[] = density; // or whatever you want
// if calculations have not narrowed enough, call recursively
if ( density > 5.2)
planetMars (density, mass);
}
alternatively, with loop, you may do something like:
public planetMars (double density, double mass) {
// loop unless the calculation is not cool
while ( density > 5.2) {
// do your calculations
density = density / 10.05312;
mass = mass / 7.2378;
myArray[] = density; // or whatever you want
}
}
you could make a function which checks if the tolerances of your calculations are already good enough, here is some "pseudocode"
while(toleranceIsGood(planet) == false)
{
planet = calculatePlanet(planet);
}
planet would be the array. of course you can implement things like Endless loop detection etc
I'm working in J2ME, I have my gameloop doing the following:
public void run() {
Graphics g = this.getGraphics();
while (running) {
long diff = System.currentTimeMillis() - lastLoop;
lastLoop = System.currentTimeMillis();
input();
this.level.doLogic();
render(g, diff);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
stop(e);
}
}
}
So it's just a basic gameloop, the doLogic() function calls for all the logic functions of the characters in the scene and render(g, diff) calls the animateChar function of every character on scene, following this, the animChar function in the Character class sets up everything in the screen as this:
protected void animChar(long diff) {
this.checkGravity();
this.move((int) ((diff * this.dx) / 1000), (int) ((diff * this.dy) / 1000));
if (this.acumFrame > this.framerate) {
this.nextFrame();
this.acumFrame = 0;
} else {
this.acumFrame += diff;
}
}
This ensures me that everything must to move according to the time that the machine takes to go from cycle to cycle (remember it's a phone, not a gaming rig). I'm sure it's not the most efficient way to achieve this behavior so I'm totally open for criticism of my programming skills in the comments, but here my problem: When I make I character jump, what I do is that I put his dy to a negative value, say -200 and I set the boolean jumping to true, that makes the character go up, and then I have this function called checkGravity() that ensure that everything that goes up has to go down, checkGravity also checks for the character being over platforms so I will strip it down a little for the sake of your time:
public void checkGravity() {
if (this.jumping) {
this.jumpSpeed += 10;
if (this.jumpSpeed > 0) {
this.jumping = false;
this.falling = true;
}
this.dy = this.jumpSpeed;
}
if (this.falling) {
this.jumpSpeed += 10;
if (this.jumpSpeed > 200) this.jumpSpeed = 200;
this.dy = this.jumpSpeed;
if (this.collidesWithPlatform()) {
this.falling = false;
this.standing = true;
this.jumping = false;
this.jumpSpeed = 0;
this.dy = this.jumpSpeed;
}
}
}
So, the problem is, that this function updates the dy regardless of the diff, making the characters fly like Superman in slow machines, and I have no idea how to implement the diff factor so that when a character is jumping, his speed decrement in a proportional way to the game speed. Can anyone help me fix this issue? Or give me pointers on how to make a 2D Jump in J2ME the right way.
Shouldn't you be adjusting the jumpSpeed based on the elapsed time? That is, perhaps the speed changes by -75/sec, so your diff should be a weight for the amount of change applied to the jumpSpeed.
So pass in diff to checkGrav and do something like... jumpSpeed += (diff * (rate_per_second)) / 1000;
(assuming diff in milliseconds)
(Ideally, this would make it just like real gravity :D)
Why not just scale all constants by diff?
By the way, I'm embarrassed to say this, but I worked on a commercial game where gravity was twice as strong on characters going down as going up. For some reason, people preferred this.
This seems to be more of a question about game design than the math of a jump. It is a common problem that in games running on different processors one game will be executed faster and on other games it will be executed slower (thus changing the entire speed of the game). I'm not sure what common practice is in games, but whenever I made home-brewed 2D games (they were fun to make) I would have the concept of a game-tick. On faster machines
long diff = System.currentTimeMillis() - lastLoop;
lastLoop = System.currentTimeMillis();
Would be lower. A wait time would be derived from the diff so that the game would run at the same speed on most machines. I would also have the render method in a separate thread so that the game speed isn't dependent on the graphics.
I can give a formula like this (I use it everywhere). The X is the parameter of it starting from zero and ending on the length of jump.
if you want someone to jump at some Height (H) and at some Length (L), then function of the jump will look like this (and it won't' be able to ever look different):
y = minus(power(x - Length of Jump divided by two) multiply by 4 and
multiply by Height of the jump) divide by power of Length and add
Height of jump in the very end.
y = -(x-l/2)(x-l/2)*4*h/(l*l) + h
And if you want the jumping object to land on something, then you can check every new X if it's approximately standing on a platform and if it is standing on something, then don't make it just stop, make it's Y position exactly equal to the Y of platform.
If you're using something like Flash or other base which has inverted y axis, then multiply the function output by -1;