Is my target selection AI efficient? - android

quick question. I am developing a top-down 2d Platformer game with lots of enemies in the map (at least a hundred spawn at the start of each level). Each enemy uses an AI that searches the map for objects with a specified tag, sorts each object into a list based on their distance, then reacts to the object closest to them.
My code works, but the thing is, if the machine my game is running on is slow, then my game lags. I want to be able to port my game to Android and iOS with low end specs.
In pursuit of putting less strain on the CPU, is there a better way to write my AI?
Here is my code:
void Start () {
FoodTargets = new List<Transform>(); // my list
SelectedTarget = null; // the target the enemy reacts to
myTransform = transform;
AddAllFood ();
}
public void AddAllFood()
{
GameObject[] Foods = GameObject.FindGameObjectsWithTag("Object");
foreach (GameObject enemy in Foods)
AddTarget (enemy.transform);
}
public void AddTarget(Transform enemy)
{
if (enemy.GetComponent<ClassRatingScript>().classrating != 1) { // classrating is an attribute each enemy has that determines their identity (like if they are a plant, a herbivore or a carnivore)
FoodTargets.Add (enemy); // adds the object to the list
}
}
private void SortTargetsByDistance() // this is how I sort according to distance, is this the fastest and most efficient way to do this?
{
FoodTargets.Sort (delegate(Transform t1, Transform t2) {
return Vector3.Distance(t1.position, myTransform.position).CompareTo(Vector3.Distance(t2.position, myTransform.position));
});
}
private void TargetEnemy() // this is called every 4 frames
{
if (SelectedTarget == null) {
SortTargetsByDistance ();
SelectedTarget = FoodTargets [1];
}
else {
SortTargetsByDistance ();
SelectedTarget = FoodTargets [1];
}
}
if (optimizer <= 2) { // this is a variable that increments every frame and resets to 0 on the 3rd frame. Only every 3rd frame is the target enemy method is called.
optimizer++;
} else {
TargetEnemy ();
// the rest are attributes that the AI considers when reacting to their target
targetmass = SelectedTarget.GetComponent<MassScript> ().mass;
targetclass = SelectedTarget.GetComponent<ClassRatingScript> ().classrating;
mass = this.GetComponent<MassScript> ().mass;
classrating = this.GetComponent<ClassRatingScript> ().classrating;
distance = Vector3.Distance (transform.position, SelectedTarget.transform.position);
optimizer = 0;
}
Is there a more optimized way of doing this? Your help will be much appreciated. Thanks in advance!

I'm not awfully familiar with C# or Unity but I would look very carefully at what sorting algorithm your sorting method is using. If all you want is the closest Game Object, then sorting isn't necessary.
The fastest sorting algorithms, such as Quicksort, are O(n*log(n)). That is to say that the time it takes to sort a collection of n objects is bounded by some constant multiple of n*log(n). If you just want the k closest objects, where k << n, then you can perform k iterations of the Bubble Sort algorithm. This will have time-complexity O(k*n), which is much better then before.
However, if you only need the single closest object, then just find the closest object without sorting (pseudocode):
float smallestDistance = Inf;
object closestObject = null;
foreach object in objectsWithTag {
float d = distance(object, enemy);
if (d < smallestDistance) {
smallestDistance = d;
closestObject = object;
}
}
This extremely simple algorithm has time complexity O(n).

Related

Android - Google map clustering bug with markers not being reclustered

Edit: Show what the mapManager.filter() method does.
I have an Android app with ~20k markers being draw on the map and with filters option so the markers are being drawn and remove a lot.
I'm currently using this clustering library because it's way more efficient for displaying a large amount of markers than the classic google map library. But it's not supported and hasn't been updated since 3 years and i haven't found any alternatives.
Here is a video of the bug.
Here is my code that is call when i click on an filtering option in my app:
private void filter(){
//currentMapManager contain all the filters option and also the array list
//of the unfiltered markers (~20,000 markers)
//it's filter method retrieve the current filtering option enable (marker type, and other specs)
//And return markers from the full arraylist that match those filters options.
ArrayList<MarkerModel> filteredList = currentMapManager.filter(currentMapManager.getAllMarkers());
if (clusterManager != null) {
clusterManager.setItems(new ArrayList<>());
clusterManager.onCameraIdle();
clusterManager.setItems(filteredList);
clusterManager.onCameraIdle();
}
Here the mapManager.filter() method.
public ArrayList<MarkerModel> filter(List<MarkerModel> currentMarkers) {
ArrayList<Object> filtersArray = filters.getFilters();
/* filtersArray is an array like this :
* ['type', ['typeFilter1', 'typeFilter2'],
* 'date', dateTimestamp,
* 'withPictures', true,
* ....]
*/
return filter(currentMarkers, filtersArray);
}
private ArrayList<MarkerModel> filter(List<MarkerModel> currentMarkers,
ArrayList<Object> fieldsAndValues) {
if (fieldsAndValues.size() % 2 == 1) {
throw new IllegalArgumentException("Missing value in call to filterList().
There must be an even number of arguments that alternate between
field names and values");
} else {
//Here the arraylist that we are returning
ArrayList<MarkerModel> filteredMarkers = new ArrayList<>();
List<Object> argumentList = new ArrayList();
Collections.addAll(argumentList, fieldsAndValues);
if (!currentMarkers.isEmpty()) {
for (int j = 0; j < currentMarkers.size(); j++) {
MarkerModel marker = currentMarkers.get(j);
boolean isInFilter = true;
//Check fieldsAndValue filters...
//If isInFilter stay to true we
filteredMarkers.add(marker);
}
}
return filteredMarkers;
}
}
I think the problem is that the cluster library is not supposed to have so much redrawn and it broke at some point from reading and working on 20k items arraylist.
I have look through all the forks branch of this library and look/try to understand the library source code but hasn't find any solution at my problem...
If you need other part of my source code to understand more tell me i will add it.
Thanks a lot if you can help me i have been struggling a lot with this issue...

Android Run Time Array Size Efficiency

My tide prediction application uses 8 double arrays for tide height calculations. Literally every tide station in the United States requires these to have 37 elements, EXCEPT Anchorage, Alaska which requires 124 elements.
Here is a declaration example
final int NUM_C = 37; //all stations except anchorage use 37
//final int NUM_C = 124; //anchorage uses 124
double a[] = new double[NUM_C + 1];
Can I efficiently specify the array size at the start up of the app? I can determine which is needed. I don't want to burden the application with inefficiency for 99% + of the users to handle this one case. The difference is only about 3K bytes.
Why don't you instantiate the variable in the constructor? It gives you more freedom to do programatic manipulation.
public class Station {
double a[];
public Station(String location) {
if(location.equals("Anchorage")) {
a = new double[124];
} else {
a = new double[37];
}
}
}
As I understand the instantiation of the object fields in the constructor is the normal case, while the instantiation with the declaration is just an additional feature of Java.
As for the speed it does not make a difference, if you specify the size by a literal value, a constant or a variable. A more interesting question is, if you should use ArrayList instead of an array. See here.
public class Station {
ArrayList<Double> a;
public Station(String location) {
if(location.equals("Anchorage")) {
a = new ArrayList<>(124);
} else {
a = new ArrayList<>(37);
}
}
}
My choice would be ArrayList as it is more flexible. Eight times 124 is not a very large number anyway. No reason to worry about performance for this.

Sorting of List<Object>

I need to sort the products by High to Low & Low to High of its Price.I have done Low to High by using following code.But dont know how to implement High To Low?
Your answer is more appreciated...
public static Comparator<Restaurant_Beam> strPriceFilterL2H = new Comparator<Restaurant_Beam>() {
#Override
public int compare(Restaurant_Beam lhs, Restaurant_Beam rhs) {
int CompareResult = 0;
if (Config.L2HFilterClicked == "L2H") {
CompareResult = (int) Math.round(Double.parseDouble(lhs.getIG_SALES_PRICE()) - Double.parseDouble(rhs.getIG_SALES_PRICE()));
}
//Used else if for H2L.But did not get it as perfect
else if (Config.L2HFilterClicked == "H2L") {
CompareResult = (int) Math.round(Double.parseDouble(lhs.getIG_SALES_PRICE()) + Double.parseDouble(rhs.getIG_SALES_PRICE()));
}
return CompareResult;
}
};
Change the second compare expression to this one:
CompareResult = (int) Math.round(Double.parseDouble(Double.parseDouble(rhs.getIG_SALES_PRICE()) - Double.parseDouble(lhs.getIG_SALES_PRICE()));
Also I'd like to point out a few things
consider doing Double comparison with epsilon
it's a good practice to check objects before comparing them
parsing each time you compare is really bad design. It would be better if you parse values somewhere in advance, consider changing your type. Currently it's not efficient.

Selecting correct ArcGisFeatureLayer to query onSingleTap event?

My problem: I'm working on a Android (ArcGis) Attribute Editor following the sample code from their tutorials. The problem occurs when I have more than 1 ArcGisDynamicLayer and, more important, more than 1 ArcGisFeatureLayer.
My question: What is the (correct) approach to IDENTIFY the right Feature Layer to query when I tap on a "field" displayed on the map?
I read a similar question (Arcgis SDK for Android - Query multiple feature layers), but I didn't understand how to use IdentifyTask using only a single tap.
EDIT: I tried to get the graphics that I tapped on to compare it to the graphics from every feature layer. If they are equal, that's the feature layer that I'm looking for. But that's not working, because my feature layers don't seem to have any graphics.
mapView.setOnSingleTapListener(new OnSingleTapListener() {
public void onSingleTap(float x, float y) {
/*...construct query...*/
Graphic g = null;
for (Layer layer : mapView.getLayers()) {
if (layer instanceof ArcGISFeatureLayer) {
ArcGISFeatureLayer fLayer = (ArcGISFeatureLayer) layer;
// Get the Graphic at location x,y
final int[] ids = fLayer.getGraphicIDs(x, y, 10, 1);
if (ids != null && ids.length != 0) {
g = fLayer.getGraphic(ids[0]);
System.out.println("Graphic: " + g);
}
}
/*...loop through every FeatureLayer and verify like :...*/
for(...)
if(layer.getGraphic(g.getUid()) != null && && g.equals(layer.getGraphic(g.getUid()))) {
/* call the select features method and implement the callbacklistener */
/* rest of the story */
}
}

How to make a random falling of bitmaps in Android?

I wanted to create something like jetboy asteroids that I found on android samples but i'ts too complicated to get the right code and to make it work properly.
I don't want on touch simply the asteroids should begin themselves.
Can someone link me some good stuff or give me some examples?
private void doAsteroidCreation() {
// Log.d(TAG, "asteroid created");
Asteroid _as = new Asteroid();
int drawIndex = mRandom.nextInt(4);
// TODO Remove hard coded value
_as.mDrawY = mAsteroidMinY + (drawIndex * 63);
_as.mDrawX = (mCanvasWidth - mAsteroids[0].getWidth());
_as.mStartTime = System.currentTimeMillis();
mDangerWillRobinson.add(_as);
}

Categories

Resources