I have a 2D game that uses two integer arrays to track x and y coordinates as shown below:
private int gridX[] = { 0,0,0,0,0 }
private int gridY[] = { 0,0,0,0,0 }
The problem is I can have a LOT of objects on the screen that needs to be tracked. Is there a way to add integers / create new blocks as needed? IE in a loop, do something like
gridX[].add(); or something like that. I'm relatively new to java and droid development and I'm having trouble finding a good tutorial or example that shows how to do this without having to initialize the gridX and gridY to sizes of 100 or so.
This is important, as I am about 90% sure that all those unused 0's are causing androids garbage cleanup to lag my application.
Why dont you use an array list instead of an Integer array?
that way you can dynamically add items to the list
ArrayList<Integer> myList = new ArrayList<Integer>();
myList.add(1);
myList.add(2);
Why not also use the Point class?
List<Point> points = new ArrayList<Point>();
points.add(new Point(0, 0));
points.add(new Point(50, 70));
Point point = points.get(1);
Log.d("MyApp", String.format("The point is: (%d, %d)", point.x, point.y);
This way you are keeping track of your x and y coordinates together and there is less opportunity for error.
Related
I am facing some weird problems with my (Line) GraphView!
To explain it a bit better I'll add a pic that shows what happens when I start scrolling horizontal (vertical scrolling is not needed).
The thing is:
The DataPoints are displayed correctly, but not my labels! I wrote the correct label in red color into the pictures above!
Whenever I start scrolling the labels start to "jump" around weirdly.
The DataPoints are received from a SharedPreference file with String/Integer pairs [Strings are always MMMyyyy (f.e. "Mar2020")]
I get the pairs out of my SP-file, put the Strings as static labels into my graph use the DataPoints in this way "logical" way (not the actual code!)
DataPoint[0] = (0, valueOfKey0)
DataPoint[1] = (1, valueOfKey1)
Here is the code how the graph is called for / drawn in the Activity
private void setupGraph(final GraphView graph, String subName){
GraphDataHelper graphDataHelper = new GraphDataHelper(mContext);
LineGraphSeries<DataPoint> series;
/*Have to call .removeAllSeries to draw each graph individually
* Otherwise they will be drawn over another!*/
graph.removeAllSeries();
/*Gets Values from each Sub
* --> Gets values form each key/value pair of specific SharedPreference-file
* --> sets values in order (0, 1, 2,... n)*/
series = new LineGraphSeries<DataPoint>(graphDataHelper.generateGraphData(subName));
series.setDrawDataPoints(true);
series.setColor(getResources().getColor(R.color.casualWhite));
series.setAnimated(true);
graph.addSeries(series);
/*Fill Labels for X-Axis:
* Get the specific Monthly-Keys for the choosen Sub
* In special cases extra rules are declared in .getSpecificSubMonthKeys() method
* Put all entries of ArrayList in Array (StaticLabelsFormatter only works with "normal" Array)*/
ArrayList<String> array_paths = graphDataHelper.getSpecificSubMonthKeys(subName);
int size = array_paths.size();
String[] paths = new String[size];
for (int i=0; i<size; i++){
paths[i] = array_paths.get(i);
}
StaticLabelsFormatter staticLabelsFormatter = new StaticLabelsFormatter(graph);
staticLabelsFormatter.setHorizontalLabels(paths);
graph.getGridLabelRenderer().setLabelFormatter(staticLabelsFormatter);
graph.getViewport().setXAxisBoundsManual(true);
if(size == 5 || size > 5){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(4.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(5);
} else if(size == 4){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(3.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(4);
} else if(size == 3){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(2.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(3);
} else if(size == 2){
graph.getViewport().setMinX(0.0);
graph.getViewport().setMaxX(1.0);
graph.getGridLabelRenderer().setNumHorizontalLabels(2);
}
graph.getViewport().setScrollable(true);
graph.getGridLabelRenderer().setHorizontalLabelsColor(getResources().getColor(R.color.midBlue));
graph.getGridLabelRenderer().setVerticalLabelsColor(getResources().getColor(R.color.midBlue));
graph.getGridLabelRenderer().setGridColor(getResources().getColor(R.color.darkBlue));
graph.getGridLabelRenderer().setPadding(50);
graph.getGridLabelRenderer().setLabelsSpace(30);
I call for .removeAllSeries because the User is able to call for another graph via a dropdown-Spinner.
Also I set different MinX & MaxX Points because there can be the case that an entry doesn't have 5 DataPoints yet and so I change the Viewport in regard of that (didn't find a better solution for that so I am also open for some better code here, but it does work this way). My SharedPreference-file always has a minimun of 2 key/value pairs
Also little extra question: Is there a way to give ONLY the horizontal labels some extra labelspace. If I use setLabelSpace it will also apply on the vertical labels and I do not want that. But the horizontal labels are just so close to the axis.
Also sorry for ugly code. It was my first time using GraphView!
Thanks ahead!
i don't know what im doing wrong...i think im having brain freeze. I am really struggling with converting my spine objects pixel coordinates to world coordinates. I have recently converted all my code to work with Ashley ecs and i cant seem to get my spine object to display in the correct position.
i have a system which handles the rendering and positioning of my spine object but i cant seem to get it displaying in the correct position.
I'm hoping someone can point me in the correct direction!
i have included my code for the spine rendering system...hope you can help!
i want to place the spine object at the same position as my box 2d object which is using world coordinates. but spine is using pixel coordinates. i have also included an image to show you what is happening. (the grey square near the middle right of the screen is where i want my spine object to be!)
Amarino
in game image
public class SpineRenderSystem extends IteratingSystem {
private static final String TAG = com.chaingang.freshstart.systems.SpineRenderSystem.class.getName();
private PolygonSpriteBatch pBatch;
SkeletonMeshRenderer skeletonMeshRenderer;
private boolean process = true;
BodyComponent bodyComp;
Spine2DComponent spineComp;
public SpineRenderSystem(PolygonSpriteBatch pBatch){
super(Family.all(RenderableComponent.class, Spine2DComponent.class, PositionComponent.class).get());
this.pBatch = pBatch;
skeletonMeshRenderer = new SkeletonMeshRenderer();
skeletonMeshRenderer.setPremultipliedAlpha(true);
}
#Override
protected void processEntity(Entity entity, float deltaTime) {
bodyComp = Mappers.body.get(entity);
spineComp = Mappers.spine2D.get(entity);
float offsetX = 100.00f/Gdx.graphics.getWidth(); //100 equal world width
float offsetY = 50.00f/Gdx.graphics.getHeight(); //50 equals world height
pBatch.begin();
spineComp.skeleton.setX((bodyComp.body.getPosition().x / offsetX) );
spineComp.skeleton.setY((bodyComp.body.getPosition().y) / offsetY);
skeletonMeshRenderer.draw(pBatch,spineComp.skeleton);
//spineComp.get(entity).skeleton.setFlipX(player.dir == -1);
spineComp.animationState.apply(spineComp.skeleton);
spineComp.skeleton.updateWorldTransform();
pBatch.end();
}
}
What I do for my spine renders is look at the bounding box size in pixels in spine. This is usually in the order of 100s of pixels. But if you are working with box2d scales, it is recommended that you think of 1 as 1 meter.
With this in mind I will scale a human spine animation with a hip y coordinate of 200 pixels by dividing it by 200, or there about.
Once you have this ratio, then when you build your Spine Skeleton you can do this (sorry, I do all my libgdx stuff in Kotlin now):
val atlasLoader = AtlasAttachmentLoader(atlas)
val skeletonJson = SkeletonJson(atlasLoader)
skeletonJson.scale = 1/200f
Then you might also want to handle an offset for rendering your spine object, as I see you are trying to do, because possibly your root bone is in the center of your spine object (a hip for example). However, you are doing a division operation, which I guess is exploratory as offsets should be an addition or subtraction. Here is how I do it using the spine pixel coordinates (again, sorry for the Kotlin, but I like it):
//In some object or global state we have this stuff
var skeleton: Skeleton
var skeletonRenderer = SkeletonRenderer<PolygonSpriteBatch>()
//Then in the rendering code
val offset = Vector2(0f,-200f)
val position = physicsRoot.position().add(offset)
skeleton.setPosition(position.x, position.y)
skeleton.updateWorldTransform()
skeletonRenderer.draw(batch, skeleton)
That should get your spine stuff working as you expect.
Have you heard of a method called camera.project(world coordinates); it might do what you are looking for. It takes the world coordinates and turns them into screen coordinates. For the opposite you can do camera.unproject(screen coordinates);
I have to move the sprite along the path that is drawn onTouch.For that I'm using Path and PathModifier
case MotionEvent.ACTION_UP:
int historySize = pSceneTouchEvent.getMotionEvent().getHistorySize();
pointX = new float[historySize];
pointY = new float[historySize];
for (int i = 1; i < historySize; i++) {
pointX[i] = pSceneTouchEvent.getMotionEvent().getHistoricalX(i);
pointY[i] = pSceneTouchEvent.getMotionEvent().getHistoricalY(i);
}
path = new path(pointX,pointY);
PathModifier pathModifier = new PathModifier(2.5f, path);
pathModifier.setRemoveWhenFinished(true);
sprite1.clearEntityModifiers();
sprite1.registerEntityModifier(pathModifier);
break;
Its giving me error as path needs at least 2 way points.
Any idea why so?
Normally this shouldn't happen, since a motion event is very often more than just one coordinate. Maybe you should test if the historySize is really bigger than 2. In addition you can add the sprites starting coordinates, otherwise the sprite would "jump" towards the first touch point (but that wasn't your question).
This isn't actually different – just another possibility:
path= new Path(historySize);
for (int i = 0; i < historySize; i++) {
float x = pSceneTouchEvent.getMotionEvent().getHistoricalX(i);
float y = pSceneTouchEvent.getMotionEvent().getHistoricalY(i);
path.to(x,y);
}
In addition I noticed you start your for-loop with int i=1 so if your historySizeis 2, the loop iterates only one times!
EDIT
I couldn't find the problem, but I found a solution:
Instead of using the motionEvent history, save the coordinates of the toucheEvent on the go as the touchEventoccurs:
ArrayList<Float> xCoordinates; // this is where you store all x coordinates of the touchEvents
ArrayList<Float> yCoordinates; // and here will be the y coordinates.
onSceneTouchEvent(TouchEvent sceneTouchEvent){
switch(sceneTouchEvent.getAction()){
case (TouchEvent.ACTION_DOWN):{
// init the list every time a new touchDown is registered
xCoordinates = new ArrayList<Float>();
yCoordinates = new ArrayList<Float>();
break;
}
case (TouchEvent.ACTION_MOVE): {
// while moving, store all touch points in the lists
xCoordinates.add(sceneTouchEvent.getX());
yCoordinates.add(sceneTouchEvent.getY());
break;
}
case (TouchEvent.ACTION_UP): {
// when the event is finished, create the path and make the sprite move
// instead of the history size use the size of your own lists
Path path = new Path(xCoordinates.size());
for (int i = 0; i < xCoordinates.size(); i++) {
path.to(xCoordinates.get(i), yCoordinates.get(i)); // add the coordinates to the path one by one
}
// do the rest and make the sprite move
PathModifier pathModifier = new PathModifier(2.5f, path);
pathModifier.setAutoUnregisterWhenFinished(true);
sprite1.clearEntityModifiers();
sprite1.registerEntityModifier(pathModifier);
break;
}
}
I tested this on my phone (which does not run in debug mode) and it works fine. But to make sure that no Exception will be thrown, you should always test if the xCoordinates list is bigger than 1. Although it is very probable that it is.
Well I hope it helps at least to go around your original problem. I noticed that some methods are named differently (e.g. setAutoUnregisterWhenFinished(true);) I guess you are using AndEngine GLES1 ? I use GLES2, so when a method has another name in my code, don't worry and just look for the equivalent in GLES1 (I didn't rename them because, the code works as it is)
Christoph
I am trying to solve a problem with drawing a path from huge (100k+) set of GeoPoints to a MapView on Android.
Firstly I would like to say, I searched through StackOverflow a lot and haven't found an answer.The bottleneck of my code is not actually drawing into canvas, but Projection.toPixels(GeoPoint, Point) or Rect.contains(point.x, point.y) method..I am skipping points not visible on screen and also displaying only every nth point according to current zoom-level. When the map is zoomed-in I want to display as accurate path as possible so I skipping zero (or nearly to zero) points, so that when finding visible points I need to call the projection method for every single point in the collection. And that is what really takes a lot of time (not seconds, but map panning is not fluid and I am not testing it on HTC Wildfire:)). I tried caching calculated points, but since points be recalculated after every map pan/zoom it haven't helped
at all.
I thought about usage of some kind of prune and search algorithm instead of iterate the array, but I figured out the input data is not sorted (I can't throw away any branch stacked between two invisible points). That could I possible solve with simple sort at the beginning, but I am still not sure even the logarithmic count of getProjection() and Rect.contains(point.x, point.y) calls instead of linear would solve the performance problem.
Bellow is my current code. Please help me if you know how to make this better. Thanks a lot!
public void drawPath(MapView mv, Canvas canvas) {
displayed = false;
tmpPath.reset();
int zoomLevel = mapView.getZoomLevel();
int skippedPoints = (int) Math.pow(2, (Math.max((19 - zoomLevel), 0)));
int mPointsSize = mPoints.size();
int mPointsLastIndex = mPointsSize - 1;
int stop = mPointsLastIndex - skippedPoints;
mapView.getDrawingRect(currentMapBoundsRect);
Projection projection = mv.getProjection();
for (int i = 0; i < mPointsSize; i += skippedPoints) {
if (i > stop) {
break;
}
//HERE IS THE PROBLEM I THINK - THIS METHOD AND THE IF CONDITION BELOW
projection.toPixels(mPoints.get(i), point);
if (currentMapBoundsRect.contains(point.x, point.y)) {
if (!displayed) {
Point tmpPoint = new Point();
projection.toPixels(mPoints.get(Math.max(i - 1, 0)),
tmpPoint);
tmpPath.moveTo(tmpPoint.x, tmpPoint.y);
tmpPath.lineTo(point.x, point.y);
displayed = true;
} else {
tmpPath.lineTo(point.x, point.y);
}
} else if (displayed) {
tmpPath.lineTo(point.x, point.y);
displayed = false;
}
}
canvas.drawPath(tmpPath, this.pathPaint);
}
So I figured out how to make it all much faster!
I will post it here, somebody could possibly found it useful in the future.
It has emerged that usage of projection.toPixels() can really harm application performance. So I figured out that way better than take every single GeoPoint, convert it to Point and then check if it is contained in map viewport is, when I count actuall viewport radius of the map as following:
mapView.getGlobalVisibleRect(currentMapBoundsRect);
GeoPoint point1 = projection.fromPixels(currentMapBoundsRect.centerX(), currentMapBoundsRect.centerY());
GeoPoint point2 = projection.fromPixels(currentMapBoundsRect.left, currentMapBoundsRect.top);
float[] results2 = new float[3];
Location.distanceBetween(point1.getLatitudeE6()/1E6, point1.getLongitudeE6()/1E6, point2.getLatitudeE6()/1E6, point2.getLongitudeE6()/1E6, results2);
The radius is in results2[0]..
Then I can take every single GeoPoint and count the distance between it and the center of the map mapView.getMapCenter(). Then I can compare the radius with computed distance and decide whether ot not diplay the point.
So that's it, hope It will be helpful.
Is there a way to read the points created when drawing a path? It seems silly to me that a path cannot be readable.
Or is it just better to manually write the current finger position to an array?
thanks
You can read as many points as you want from any path.
Example how to read coordinates from the middle of path:
PathMeasure pm = new PathMeasure(myPath, false);
//coordinates will be here
float aCoordinates[] = {0f, 0f};
//get coordinates of the middle point
pm.getPosTan(pm.getLength() * 0.5f, aCoordinates, null);
You can pass any distance from the path start to get point coordinates.
As far as I know, I think that you can't get previously added points, but you can extend Path class and create your own, override add methods, and then store that points in an array or a list or whatever you prefer.
You mentioned finger position in your question. If you are drawing and using motion events, you could add the X and Y positions to an ArrayList during the event where all even indices are X's and odds are Y's. I used this in a couple of drawing apps I created. To recreate the path all you need is a for loop and Path.lineTo().
Also if you have drawn the path to a view with a specific color, say Color.Black, you can use Bitmap.getPixels(...) and create an array {x0,y0,x1,y1,....xn,yn} based off a for loop like
int i = 0;
for(int y = 0; y < bitmap.getHeight(); y++){
for(int x = 0; x < bitmap.getWidth(); x++){
if(pixels[y*bitmap.getWidth()+x] == Color.BLACK){
xy[i] = x;
i++;
xy[i] = y;
i++;
}
}
}
The array xy has all your coordinates.