Basically the problem is that after you rotate the camera, the points that are given as arguments in the callback for dragging are not what I expected. I'm guessing I have to Rotate the given points also but I just couldn't.
Can Someone explain what is going on, is this some kind of bug or what should I do in order the sprite to follow the mouse cursor?
Easiest way to explain the problem is to reproduce it:
1) Go to Phaser Example Runner
2) Copy- Paste this code:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}
function create ()
{
var image = this.add.sprite(200, 300, 'eye').setInteractive();
this.cameras.main.setRotation(Math.PI);
image.on('pointerover', function () {
this.setTint(0x00ff00);
});
image.on('pointerout', function () {
this.clearTint();
});
this.input.setDraggable(image);
this.input.on('dragstart', function (pointer, gameObject) {
gameObject.setTint(0xff0000);
});
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
console.log(`x: ${dragX}, y: ${dragY}`);
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', function (pointer, gameObject) {
gameObject.clearTint();
});
}
3) Open the console, drag around the Eye and look at what coordinates are given.
4) If you remove line 24 (the rotation of the camera) Everything works as expected.
(The example is taken from Phaser 3 Official examples and a bit changed for the bug)
According to Phaser's API Documentation on the setRotation() method, the rotation given in radians applies to everything rendered by the camera. Unfortunately, your pointer isn't rendered by the camera so it doesn't get the same rotated coordinates. Not sure if this is a bug with the library or just a poorly documented exception, but I believe there is a workaround.
Create 2 variables to hold an initial position and a final position:
var image = this.add.sprite(200, 300, 'eye').setInteractive();
var initial = [];
var final = [];
Populate the initial position in your .on('dragstart') method:
this.input.on('dragstart', function (pointer, gameObject) {
initial = [
gameObject.x,
gameObject.y,
pointer.x,
pointer.y
];
gameObject.setTint(0xff0000);
});
Then, populate the final variable in your .on('drag') method:
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
final = [
gameObject.x, // not necessary but keeping for variable shape consistency
gameObject.y, // not necessary but keeping for variable shape consistency
pointer.x,
pointer.y
];
gameObject.x = initial[0] + (initial[2] - final[2]);
gameObject.y = initial[1] + (initial[3] - final[3]);
});
All we're doing here is keeping track of the change in pointer position and mimicking that change in our gameObject.
Related
I need to write a game like: https://robowhale.com/html5/drawing-letters/ with phaser.io library. I mean user must follow a path and draw for example letter "A".
basically, need to draw over a path, I checked almost all examples and tutorials, But couldn't find any proper tutorial or algorithm.
any help, link, source code or tutorial can helps me to figure out algorithm and start project.
Usually you will never find the full solution, you will have to merge multiple.
Here ist how I would approach this task (a quick and dirty solution):
Step 1)
Find an Example that solves a part of the problem and work from there
(Based on the example Quadratic Bezier Curve)
Then I :...
I removed the tween
added Mouse Input
split the path in segments
calculate until where the path should be draw
draw path segments
... And slowly adding missing features:
just update path when close enough
just allow move forwards
...
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: {
create: create,
update: update
}
};
var path;
var curve;
var graphics;
var game = new Phaser.Game(config);
var _myMaxPointIndex = 6;
function create() {
graphics = this.add.graphics();
path = { t: 0, vec: new Phaser.Math.Vector2() };
var startPoint = new Phaser.Math.Vector2(100, 500);
var controlPoint1 = new Phaser.Math.Vector2(50, 100);
var endPoint = new Phaser.Math.Vector2(700, 500);
curve = new Phaser.Curves.QuadraticBezier(startPoint, controlPoint1, endPoint);
}
function _myDrawPath(g, points) {
let startPoint = points.shift();
graphics.lineStyle(30, 0x0000ff, 1);
g.beginPath();
g.moveTo(startPoint.x, startPoint.y);
let maxPointsToDraw = _myMaxPointIndex == -1 ? points.length : _myMaxPointIndex + 1;
for (let index = 0; index < maxPointsToDraw; index++) {
const point = points[index];
g.lineTo(point.x, point.y);
}
g.strokePath();
}
function update() {
graphics.clear();
graphics.lineStyle(2, 0x00ff00, 1);
curve.draw(graphics);
// get 20 Point from the Curve (can be more if to jaggy)
let _myPoints = curve.getPoints(20);
_myDrawPath(graphics, _myPoints)
if (this.input.activePointer.isDown) {
// Here I update the Max point that should be draw
let _myMouse = this.input.activePointer.position;
let _myNearestPoint = _myPoints.reduce((p, c, i) => {
let distance = Phaser.Math.Distance.BetweenPoints(_myMouse, c)
if (p.distance == -1 || p.distance > distance) {
p.distance = distance
p.idx = i
}
return p
}, { distance: -1 })
_myMaxPointIndex = _myNearestPoint.idx
}
}
h1 {
font-family:arial
}
#phaser-example{
transform: translate(-20%, -20%) scale(.5);
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.min.js"></script>
<h1>Click to calculate path</H1>
<div id="phaser-example"></div>
And with a bit of luck:
While building this solution, I had to "google" for some documentation details, and found this in a Phaser forum, that points to a interesting solution with a working CodePen, with a more complex full working example (Just adding the codepen link if the forum entry gets deleted).
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
simple by using rapheal i successfully make animation along path , but i can't reverse the animation direction ,,, just how to make it animate to the other direction when clicking the same path .
var paper = Raphael(0,0,1024,768);
var pathOne = paper.path(['M', 15,15 , 100,75]).attr({'stroke-width':18}).data("id",1);
//and this is just the circle
var circle = paper.circle(0, 0, 13).attr({
fill: '#09c', cursor: 'pointer'
});
//make the path as custom attribute so it can ba accessed
function pathPicker(thatPath){
paper.customAttributes.pathFactor = function(distance) {
var point = thatPath.getPointAtLength(distance * thatPath.getTotalLength());
var dx = point.x,
dy = point.y;
return {
transform: ['t', dx, dy]
};
}
}
//initialize for first move
pathPicker(pathOne);
circle.attr({pathFactor: 0}); // Reset
//Asign first path and first move
function firstMove(){
circle.animate({pathFactor: 1}, 1000});
}
pathOne.click(function(){
firstMove();
});
I couldn't get the original to run, so here is something using the main bits that should suit...
There's not a lot to it, get the length of the path, iterate over the length, draw object at the path. It uses the Raphael customAttributes to be able to animate it. I've added a custom toggle to make it easy to switch between them.
These are the key changes..
var len = pathOne.getTotalLength();
paper.customAttributes.along = function (v) {
var point = pathOne.getPointAtLength(v * len);
return {
transform: "t" + [point.x, point.y] + "r" + point.alpha
};
};
circle.attr({ along: 0 });
function animateThere( val ) {
val = +!val; // toggle
pathOne.click( function() { animateThere( val ) } );
circle.animate({ along: val }, 2000 );
};
pathOne.click( function() { animateThere(0) } );
jsfiddle
For completeness, you may want to do some extra checks like only allow the click if the animation has finished or something, as there may be a problem if you quickly click a lot and it buffering up animations.
Is it possible to animate the images filter in Fabric.js? Such as a "pixelate" filter.
I solved it in the same way like the demo.
Unfortunately filters aren't able to be animated - they need too much processing time.
Here's my Code:
image = ... //Image, where the filter should be applied
var filter = new fabric.Image.filters.RemoveWhite({
threshold: 0,
distance: 140
});
image.filters.push(filter);
image.applyFilters(canvas.renderAll.bind(canvas));
animate(image,1, 400); //Start the Animation
function animate(image,value, stop){
value += ((stop-value)*0.02); //Change the threshold-value
if (image.filters[0]) {
image.filters[0]['threshold'] = value;
console.log(value);
image.applyFilters(canvas.renderAll.bind(canvas)); //Start creating the new image
if(value<stop-100){
setTimeout(function(){act(image,value,stop);},1);
}
}
}
I know the code isn't the most efficient one, but it works. And you can see that Animating filters consumes too much time.
(I tested it with a 1920x1080px image, maybe you can use it with smaller images)
Here is a updated version for the brightness filter
var brightnessValue = 0.9;
var brightnessFilter = new fabric.Image.filters.Brightness({
brightness: brightnessValue
});
fabricImage.filters.push(brightnessFilter);
fabric.util.requestAnimFrame(function brightnessFilterAnimation() {
brightnessValue = brightnessValue - 0.04;
brightnessFilter.brightness = brightnessValue;
fabricImage.applyFilters();
if (brightnessValue > 0) {
fabric.util.requestAnimFrame(brightnessFilterAnimation);
}
});
II want an user to draw a route with polylines on GoogleMaps. I've found a way to snap a polyline ( example in the link, code in below ). The code works with the directions service to draw a polyline on a road, the only problem is this only works with G_TRAVELMODE_DRIVING but I want to use G_TRAVELMODE_WALKING and when you use WALKING you have to supply the map and a div in the constructor of the directions object. When I do that it automatically removes the last line and only displays the current line. I've tried several things like supplying the map as null, or leaving out the map in the constructor.. I've also tried to give a second map in the constructor, get the polylines from the directions service and display them on the right map. But nothing works! I'd appreciate it if someone could help me!
http://econym.org.uk/gmap/example_snappath.htm
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng(53.7877, -2.9832),13)
map.addControl(new GLargeMapControl());
map.addControl(new GMapTypeControl());
var dirn = new GDirections();
var firstpoint = true;
var gmarkers = [];
var gpolys = [];
var dist = 0;
GEvent.addListener(map, "click", function(overlay,point) {
// == When the user clicks on a the map, get directiobns from that point to itself ==
if (!overlay) {
if (firstpoint) {
dirn.loadFromWaypoints([point.toUrlValue(6),point.toUrlValue(6)],{getPolyline:true});
} else {
dirn.loadFromWaypoints([gmarkers[gmarkers.length-1].getPoint(),point.toUrlValue(6)],{getPolyline:true});
}
}
});
// == when the load event completes, plot the point on the street ==
GEvent.addListener(dirn,"load", function() {
// snap to last vertex in the polyline
var n = dirn.getPolyline().getVertexCount();
var p=dirn.getPolyline().getVertex(n-1);
var marker=new GMarker(p);
map.addOverlay(marker);
// store the details
gmarkers.push(marker);
if (!firstpoint) {
map.addOverlay(dirn.getPolyline());
gpolys.push(dirn.getPolyline());
dist += dirn.getPolyline().Distance();
document.getElementById("distance").innerHTML="Path length: "+(dist/1000).toFixed(2)+" km. "+(dist/1609.344).toFixed(2)+" miles.";
}
firstpoint = false;
});
GEvent.addListener(dirn,"error", function() {
GLog.write("Failed: "+dirn.getStatus().code);
});
}
else {
alert("Sorry, the Google Maps API is not compatible with this browser");
}
I know it's an old post, but since no answer came forward and I've recently been working on this sort of code (and got it working), try swapping the addListener with this.
GEvent.addListener(map, "click", function(overlay,point) {
// == When the user clicks on a the map, get directiobns from that point to itself ==
if (!overlay) {
if (firstpoint) {
dirn1.loadFromWaypoints([point.toUrlValue(6),point.toUrlValue(6)],{ getPolyline:true,travelMode:G_TRAVEL_MODE_WALKING});
} else {
dirn1.loadFromWaypoints([gmarkers[gmarkers.length-1].getPoint(),point.toUrlValue(6)],{ getPolyline:true,travelMode:G_TRAVEL_MODE_WALKING});
}
}
});