How to get nearest PositionComponent in a good way? - position

I have a "player" and several "enemy" beside:
Now I just use a loop to get every distance:
void fireBullet() {
var enemies = gameRef.children.whereType<Enemy>();
if (enemies.isEmpty) return;
PositionComponent nearestEnemy = enemies.first;
enemies.forEach((element) {
if (element.distance(playerComponent) < nearestEnemy.distance(playerComponent)) {
nearestEnemy = element;
}
});
// fire bullet to enemy
}
I think it's not the best solution. If there's too many enemy, performance will degrade.
Is there any better way to get nearest PositionComponent?

There is no better built-in method unfortunately.
There are a few improvements that you can do to your game though that will make a big performance difference:
Use distance2, because then it won't have to do a square root operation behind the scenes.
Save the last distance calculation to avoid doing it twice.
Use query instead of whereType (the query is cached).
So it would be something like this:
void fireBullet() {
var enemies = gameRef.children.query<Enemy>();
if (enemies.isEmpty) return;
var nearestEnemy = enemies.first;
var nearestDistance = nearestEnemy.position.distance2(playerComponent);
enemies.forEach((element) {
final distance = element.position.distance2(playerComponent.position);
if (distance < distanceNearest) {
nearestEnemy = element;
nearestDistance = distance;
}
});
// fire bullet to enemy
}
I believe that you would have other performance issues before you hit performance issues with this code in the updated state.

Related

Bing Maps SpatialMath Module Intersection is not accurate with Multiple pins with same coordinates

I figured an issue, while i have thousands of pins over the map, i am using drawing tool to draw shapes free hand and then executing the Intersection on "drawingEnded" event, While i could see the intersection should return more than it actually returns,
Am i missing something ? For Example, If there are around 500 pins under the new area drawn, Intersection method only returns 100 or few more,
My Spider Cluster Configuration:
` Microsoft.Maps.loadModule(['SpiderClusterManager'], function () {
spiderManager = new SpiderClusterManager(map, pinssame, {
//clusteredPinCallback: function (cluster) {
// //Customize clustered pushpin.
// cluster.setOptions({
// color: 'red',
// icon:'https://www.bingmapsportal.com/Content/images/poi_custom.png'
// });
//},
pinSelected: function (pin, cluster) {
if (cluster) {
showInfobox(cluster.getLocation(), pin);
} else {
showInfobox(pin.getLocation(), pin);
}
},
pinUnselected: function () {
hideInfobox();
},
gridSize: 80
});
});
`
Intersection Function Code which gets triggered after "drawingEnded" event:
` function findIntersectingData(searchArea) {
//Ensure that the search area is a valid polygon, should have 4 Locations in it's ring as it automatically closes.
if (searchArea && searchArea.getLocations().length >= 4) {
//Get all the pushpins from the pinLayer.
//var pins = spiderManager._data;
//Using spatial math find all pushpins that intersect with the drawn search area.
//The returned data is a copy of the intersecting data and not a reference to the original shapes,
//so making edits to them will not cause any updates on the map.
var intersectingPins = Microsoft.Maps.SpatialMath.Geometry.intersection(pins, searchArea);
//The data returned by the intersection function can be null, a single shape, or an array of shapes.
if (intersectingPins) {
//For ease of usem wrap individudal shapes in an array.
if (intersectingPins && !(intersectingPins instanceof Array)) {
intersectingPins = [intersectingPins];
}
var selectedPins = [];
//Loop through and map the intersecting pushpins back to their original pushpins by comparing their coordinates.
for (var j = 0; j < intersectingPins.length; j++) {
for (var i = 0; i < pins.length; i++) {
if (Microsoft.Maps.Location.areEqual(pins[i].getLocation(), intersectingPins[j].getLocation())) {
selectedPins.push(pins[i]);
break;
}
}
}
//Return the pushpins that were selected.
console.log(selectedPins);
return selectedPins;
}
}
return null;
}
`
The function is not returning accurate pin data,
Am i missing something here ?
Any Help Appreciated,
Thanks & Regards,
Shohil Sethia
UPDATE :
Just figured, It is an assumption ,I have multiple pins with same coordinates over the layer, Is this the reason that it returns only pins which intersects with different coordinates over the map ?,
Thanks & Regards,
Shohil Sethia
The method returns objects that represent the intersection, not the exact copies of input shapes. So yes, if multiple pushpins with the same coordinates are within the area, only one pushpin of that coordinates will be in the result, since that alone is good enough as a representation.
You can try the sample below, only one pushpin is returned:
// Creates a polygon of current map bounds
var polygon = new Microsoft.Maps.SpatialMath.locationRectToPolygon(map.getBounds());
// Creates a bunch of the pushpins of the same coordinates(map center)
var pushpin1 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin2 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin3 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin4 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin5 = new Microsoft.Maps.Pushpin(map.getCenter());
// Adds the shapes to map for some visualization
map.entities.push([polygon, pushpin1, pushpin2, pushpin3, pushpin4, pushpin5]);
// Only one pushpin is returned as result
var intersectingPin = Microsoft.Maps.SpatialMath.Geometry.intersection([pushpin1, pushpin2, pushpin3, pushpin4, pushpin5], polygon);
Have you checked if the number of results adds up when taking duplicate pins into account?
I got a solution, Since Intersection API ignore multiple pushPins with same coordinates, Therefore there is another API named as contains which takes two parameters which are the shape and the pushpin, and it returns whether it is contained in that shape or not in a boolean form. So true if pushpin is in that shape, and false in the other way.
function findIntersectingData(searchArea) {
//Ensure that the search area is a valid polygon, should have 4 Locations in it's ring as it automatically closes.
if (searchArea && searchArea.getLocations().length >= 4) {
var selectedPins = [];
for (var i = 0; i < pins.length; i++) {
if (Microsoft.Maps.SpatialMath.Geometry.contains(searchArea, pins[i])) {
selectedPins.push(pins[i]);
}
}
//Return the pushpins that were selected.
console.log(selectedPins);
//return updatePrescriberTerr(selectedPins);
return selectedPins;
}
return null;
}
Therefore in the above function the we can loop it from the pushPins array and form the intersection set accordingly based on the boolean values.
Hope it helps to those with similar scenario !
Regards,
Shohil Sethia

How to improve canvas fabric.js performance with large number of Objects

I need make an App that has about 30k Objects, a user can Pan, Zoom or "Select on click" any of those objects.
Fabric.js Canvas is being used
I have done the same using SVG's and the svg-pan-zoom plugin (no Canvas Element) with better results
Problem: there is a significant Lag while Zooming, Panning or Object on Click
will removing Fabric.js improve performance?
will switching to WebGL improve performance?
Have tried Fabric specific options
fabric.Object.prototype.objectCaching = false;
fabric.Object.prototype.statefullCache = false;
fabric.Object.prototype.noScaleCache = true;
fabric.Object.prototype.needsItsOwnCache = false;
UPDATE
Heres the updated Fiddle
for reference :
canvas-vs-svg-vs-div Stackoverflow
Stackoverflow
Don't Render in IO EVENTS!
Though not a complete fix to the update speed this answer will about double the interaction speed.
A common, almost standard, mistake made with mouse and event interaction with the canvas (and DOM) is to delegate rendering to mouse/touch events. This is very bad practice as mouse events fire at much higher rates than the display can display. It becomes worse when your rendering time is high as you queue up mouse events (pseudo render events) and do a re render for every movement of the mouse
Note blocking code will stop mouse events but as soon as the engine is idle the mouse will start firing at full rate again.
Use the mouse events just to get the mouse state. Use an animation loop that is synced to the display to render only when needed and there is time available. Things like the wheel and mouse movement deltas should be recorded cumulatively.
mouse.dx += event.movementX;
mouse.dy += event.movementY;
mouse.wheel += event.wheelDelta;
And consume them in the main render loop...
function update(){
// ... code to use mouse
// consume deltas
mouse.x = mouse.y = mouse.wheel = 0;
...this ensures that the mouse state is accurately followed when you may have many mouse events between render updates.
Example, separating events from rendering.
Change you code in the fiddle you provided to the following, on my machine it about doubled the rendering speed (which is still very slow).
// from just after the function applyZoom replace all the code
var mouse = { // holds the mouse state
x : 0,
y : 0,
down : false,
w : 0,
delta : new fabric.Point(0,0),
}
// event just track mouse state
function zoom(e) {
if(e != null) { e.preventDefault() }
var evt=window.event || e;
mouse.x = e.offsetX;
mouse.y = e.offsetY;
mouse.w += evt.detail? evt.detail*(-120) : evt.wheelDelta;
return false;
}
canvas.on('mouse:up', function (e) { mouse.down = false });
canvas.on('mouse:out', function (e) { mouse.down = false });
canvas.on('mouse:down', function (e) { mouse.down = true });
canvas.on('mouse:move', function(e) {
if (e && e.e) {
mouse.delta.x += e.e.movementX;
mouse.delta.y += e.e.movementY;
}
});
// main animation loop
function update(){
if(mouse.w !== 0){ // if the wheel has moved do zoom
var curZoom = canvas.getZoom();
canvas.zoomToPoint(
{ x : mouse.x, y: mouse.y },
canvas.getZoom() + mouse.w / 4000
);
mouse.w = 0; // consume wheel delta
}else if(mouse.down) { // if mouse button down
canvas.relativePan(mouse.delta);
}
// consume mouse delta
mouse.delta.x = 0;
mouse.delta.y = 0;
requestAnimationFrame(update);
}
requestAnimationFrame(update);

Uncaught TypeError: Cannot read property 'copy' of undefined p5.js/node.js/socket.io

I'm having an error when the second client is connected. My code comparing the two clients current position by p5.Vector.dist() and there's an error, here it is.
And the line in p5.Vector.dist(p5.js:25914) is
p5.Vector.prototype.dist = function (v) {
var d = v.copy().sub(this); //This is the exact line where the error says from
return d.mag();
};
This is my code;
Client side;
//I use for loop to see all the contain of otherCircles
for(var x = 0; x < otherCircles.length; x++){
if(otherCircles[x].id != socket.id){ //To make sure i won't compare the client's data to its own because the data of all connected client's is here
console.log(otherCircles[x].radius); //To see if the data is not null
if(circle.eat(otherCircles[x])){
if(circle.radius * 0.95 >= otherCircles[x].radius){
otherCircles.splice(x,1);
console.log('ATE');
} else if(circle.radius <= otherCircles[x].radius * 0.95){
zxc = circle.radius;
asd = zxc;
circle.radius = null;
console.log('EATEN');
}
}
}
}
//Here's the eat function of the circle
function Circle(positionX,positionY,radius){
//The variables of Circle()
this.position = createVector(positionX, positionY);
this.radius = radius;
this.velocity = createVector(0, 0);
//Here's the eat function
this.eat = function(other) {
var distance = p5.Vector.dist(this.position, other.position); //Heres where the error
if (distance < this.radius + (other.radius * 0.25)) { //Compare there distance
return true;
} else {
return false;
}
}
}
The otherCircles[] contains;
And that is also the output of the line console.log(otherCircles[x].radius);.
I don't think the server side would be necessary because it only do is to receive the current position and size of the client and send the other clients position and size to. All there datas stored in otherCircles(). The line console.log(otherCircles[x].radius); result is not null, so I know there's data where being compared to the clients position, why I'm having an error like this.
It's going to be pretty hard to help you without an MCVE, but I'll try to walk you through debugging this.
You've printed otherCircles[x].radius, which is a good start. But if I were you, I'd want to know much more about otherCircles[x]. What variables and functions does it contain? I'd start by googling "JavaScript print function names of object" and try to figure out exactly what's in that object. What is the value of otherCircles[x].position?
From there, I'd also want to make sure that otherCircles[x].position is defined and an instance of p5.Vector. Does it have a copy() function?
I might also step through the code with a debugger- every browser has one, and you should become familiar with using it.
If you still can't get it work, then please post an MCVE that we can run by copy-pasting it. That means no server code, just hard-code your values so we can see the same error. I'd bet you find your problem while trying to narrow it down to a small example. But if not, we'll go from there. Good luck.

Bullet spawn speed or generation speed phaser

Hi all I have a function that generates bullets every time the player touches the screen.
Is there a way that I can limit the amount of bullets generated? Basically if I press the screen very quickly lots of bullets get generated but I would like to limit it to at least i per second instead of 2 or 3 per second.
Below you can find my firing function and my bullet creation function:
createBullets: function(){
//Bullets
this.bullets = this.add.group();
this.bullets.enableBody = true;
this.bullets.physicsBodyType = Phaser.Physics.P2JS;
this.bullets.createMultiple(500, 'bullet', 0, false);
this.bullets.setAll('anchor.x', 0.5);
this.bullets.setAll('anchor.y', 0.5);
this.bullets.setAll('outOfBoundsKill', true);
this.bullets.setAll('checkWorldBounds', true);
},
fireBullet: function(){
this.bullet = this.bullets.getFirstExists(false);
if (this.bullet) {
this.bullet.reset(this.tanque.x, this.tanque.y - 20);
this.bullet.body.velocity.y = -500;
}
},
and my update function:
if(this.input.activePointer.isDown){
if (!this.mouseTouchDown) {
this.touchDown();
}
}else {
if (this.mouseTouchDown) {
this.touchUp();
}
}
Any help I would really appreciate.
One option is to store two values:
nextShotTime: next time a shot can be fired
shotDelay: delay between shots (can be set like Phaser.Timer.SECOND * 2)
I don't see where you're calling fireBullet() in your example code above, but either before you make the call, or within the function, you could then check to see if the nextShotTime is in the past. If it is, fire another bullet and then update the nextShotTime with the current time plus the shotDelay.
For example:
if (this.nextShotTime < this.time.now) {
this.nextShotTime = this.time.now + this.shotDelay;
// add your code that will fire a bullet.
}
I have had similar problems in games before. The solution I used is the same as the one posted above. I found it in this
Phaser tutorial.
The fire function used in the tutorial is:
fire: function() {
if (this.nextShotAt > this.time.now) {
return;
}
this.nextShotAt = this.time.now + this.shotDelay;
You can modify it to suit your purposes.
This is part of a fire function I used in a game I made:
fire: function() {
//Make sure the player can't shoot when dead and that they are able to shoot another bullet
if(!this.player.alive || this.time.now < this.nextFireTime) {
return;
}
this.nextFireTime = this.time.now + this.fireRate;
var bullet;
//If weaponlvl = 0, shoot a normal bullet
if(this.weaponlvl === 0) {
if(this.bullet1.countDead() === 0) {
return;
}
//Properties for the basic weapon
this.fireRate = 500;
bullet = this.bullet1.getFirstExists(false);
bullet.reset(this.player.x, this.player.y-22);
bullet.body.velocity.y = -300;
}
Hope this helps you in some way.

geolocalisation is very slow

I've a application for the geolocalisation and I retrieve the current geoposition but the display on the application is VERY slow...
The constructor :
public TaskGeo()
{
InitializeComponent();
_geolocator = new Geolocator();
_geolocator.DesiredAccuracy = PositionAccuracy.High;
_geolocator.MovementThreshold = 100;
_geolocator.PositionChanged += _geolocator_PositionChanged;
_geolocator.StatusChanged += _geolocator_StatusChanged;
if (_geolocator.LocationStatus == PositionStatus.Disabled)
this.DisplayNeedGPS();
}
the code for the display on the app :
void _geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
// saving and display of the position
App.RootFrame.Dispatcher.BeginInvoke(() =>
{
this._CurrentPosition = args.Position;
this.lblLon.Text = "Lon: " + this._CurrentPosition.Coordinate.Longitude;
this.lblLat.Text = "Lat: " + this._CurrentPosition.Coordinate.Latitude;
this.LocationChanged(this._CurrentPosition.Coordinate.Longitude, this._CurrentPosition.Coordinate.Latitude);
});
}
And the code for the query :
private void LocationChanged(double lat, double lon)
{
ReverseGeocodeQuery rgq = new ReverseGeocodeQuery();
rgq.GeoCoordinate = new GeoCoordinate(lat, lon);
rgq.QueryCompleted += rgq_QueryCompleted;
rgq.QueryAsync();
}
How can I improve the code to display faster the position ? Thanks in advance !
Getting this sort of information is basically pretty slow. To quote the great Louis C. K. "It is going to space, give it a second". Because you've specified PositionAccuracy.High this means that the location must be found using GPS, which is comparatively slow, and not any of the faster fallback methods such as using local wi-fi or cell phone towers.
You could reduce your demands for accuracy overall or initially request a lower accuracy and then refine it once the information from the GPS is available. The second option is better. If you look at a map application they typically do this by showing you about where you are and then improving it after the GPS lock is acquired.

Resources