How do I detect when the pointer is over a sprite while I'm dragging another sprite? - phaser-framework

In my update() function, I use pointerOver() to detect when the pointer is over a sprite. This normally works fine. However, if I happen to be dragging another sprite at the time, the pointerOver() function always returns false.
I thought I'd work around it by getting the location of the pointer and comparing it with the location and bounds of my sprite, but the pointer location is always (-1, -1).
Here's some example code to demonstrate the problem:
var game = new Phaser.Game( 800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update, render: render });
var triangle;
var square;
var isOver;
var pointerX;
var pointerY;
function preload()
{
game.load.image( 'triangle', 'assets/triangle.png' );
game.load.image( 'square', 'assets/square.png' );
}
function create()
{
triangle = game.add.sprite( 100, 100, "triangle" );
triangle.inputEnabled = true;
triangle.input.enableDrag( false, true );
square = game.add.sprite( 100, 200, "square" );
square.inputEnabled = true;
}
function update()
{
isOver = square.input.pointerOver() ? "Yes" : "No";
}
function render()
{
game.debug.text( "Mouse over square: " + isOver, 200, 100 );
game.debug.text( "Pointer: (" + game.input.pointer1.x + ", " + game.input.pointer1.y + ")", 200, 116 );
}
I found this post about using a sprite's input.priorityID: Phaser JS how to stop event Propagation(firing) from textButton.events.onInputDown event to game.input.onDown event?
Using the priorityID fixed it for when the triangle is on top the square and the triangle is not being dragged, but the problem remains when it's being dragged.
How do I detect when the pointer is over a sprite, even when I'm dragging another sprite?
Thanks.

I had a similar issue recently and I hope this will still be of help to you.
I found out that upon initiating a drag, the input on all sprites except for the one you're dragging becomes disabled. That probably happens internally and I couldn't find a way to override it.
What I did is the following: all the objects (in my case instances of Tile) I want to be draggable are held within a Board object, which in turn holds a reference to my Game state. In the Game state I have a flag dragIsActive. In the object class itself I have (TypeScript)
this.tileSprite.events.onDragStart.add(() => {
this.parentBoard.gameState.dragIsActive = true;
});
this.tileSprite.events.onDragStop.add(() => {
this.parentBoard.gameState.dragIsActive = false;
});
The equivalent JavaScript would be
this.tileSprite.events.onDragStart.add(function() {
this.parentBoard.gameState.dragIsActive = true;
});
this.tileSprite.events.onDragStop.add(function() {
this.parentBoard.gameState.dragIsActive = false;
});
called from the object's constructor.
In Game I have the following:
update() {
if (this.dragIsActive) {
var firstTile : Tile = this.game.input.activePointer.targetObject.sprite.parent;
this.board.tiles.forEach((t : Tile) => {
if (firstTile.tileSprite.overlap(t.tileSprite)) {
this.board.tilesToSlide.push(t);
}
});
this.board.tilesToSlide.forEach((tileToSlide) => {
// Do the processing on that array here.
});
}
}
JavaScript (just the forEach loop):
this.board.tiles.forEach(fucntion(t) {
if (firstTile.tileSprite.overlap(t.tileSprite)) {
this.board.tilesToSlide.push(t);
}
});
In my case the check is for two sprites overlapping, but in your case you can check if one just point of interest (e.g. the position of the sprite you're dragging) is inside the bounds of another sprite:
if (secondSprite.getBounds().containsPoint(firstSprite.position)) {
// This should be equivalent to pointerOver() while dragging something.
}
P.S. I hope you're aware that a sprite's position is at the top left of its bounding box unless explicitly set to something else. Set via sprite.anchor.set(x, y) - (0, 0) being top left (the default) and (1, 1) - bottom right.

you can also use this method to see if the point is contained within the sprite bounds.
if( Phaser.Rectangle.contains( sprite.body, this.game.input.x, this.game.input.y) ){
console.log('collide')
}

Related

Phaser 3: How to detect if there's a group member at location

Okay I have a little game going on Phaser with a slider which the player can move up or down:
As you can see, the slider is on a track which one would assume restricts where the slider can go. At the moment, this isn't the case and the slider can run right off the rail.
How can I detect if there's a slider track in the target location before moving the slider?
Here's where I'm creating the static groups for slider and slider track.
sliders = this.physics.add.staticGroup();
slider_tracks = this.physics.add.staticGroup();
Here's where the objects themselves are being added to the game:
add_slider: function (x, y, data) {
map.add_slider_track(x, y, data);
var slider = sliders.create(x, y, data.direction + '_slider');
for (var key in data) {
slider[key] = data[key];
}
},
add_slider_track: function (x, y, data) {
slider_tracks.create(x, y, data.direction + '_track');
},
And here's the functions which move it:
hitSlider: function (player, slider) {
if (slider.direction == 'vertical') {
if (player.body.onFloor() && player.slamming) {
interaction.moveSliderDown(slider)
} else if (player.body.onCeiling()) {
interaction.moveSliderUp(slider);
}
}
player.slamming = false;
},
moveSliderUp: function (slider) {
slider.setY(slider.y - block_size);
slider.body.position.y = (slider.y - (block_size / 2));
player.setVelocityY(100);
},
moveSliderDown: function (slider) {
slider.setY(slider.y + block_size);
slider.body.position.y = (slider.y - (block_size / 2));
}
I've tried using slider_track.getFirst (https://rexrainbow.github.io/phaser3-rex-notes/docs/site/group/) but it seems to change the location of a given piece of track, not just detect if there's one there.
Just to don't let this question without answer as we normally start a chat, effectively I see in the js/slider_actions.js the solution but I can just say you can use velocity but seriously my level of coding even if I am for a long time in the Phaser community is lower than yours ;)
sliderTrackRight: function (slider) {
track = slider_tracks.children.entries.find(
function (track) {
return (
track.body.y == slider.body.y &&
track.body.x == (slider.body.x + block_size) &&
track.direction == 'horizontal'
)
}
);
return (typeof track != 'undefined');
},

How to make a sprite with fixed movement distance in Phaser

I have a grid-based top-down game with arcade physics and I'd like my player to move precisely between grid positions. But how can I do this?
I'm moving the player now on keydown using velocity, then resetting the velocity to 0 on update so there's no inertia.
function update(){
this.player.body.velocity.x = 0
this.player.body.velocity.y = 0
if(this.keyIsLeft){
this.player.body.velocity.x -= 200
}
else if(this.keyIsRight){
this.player.body.velocity.x += 200
}
else if(this.keyIsUp){
this.player.body.velocity.y -= 200
}
else if(this.keyIsDown){
this.player.body.velocity.y += 200
}
But how can I get the player to only move a square (or half square) at a time?
The ideal movement is shown here in this Lolo game, where at any given time the player is on a grid or half-grid space: https://www.youtube.com/watch?v=QQKScIJYUxU&t=5m30s
I can't just set the x and y position or use tweening because I need to keep arcade physics, and those adjustments interfere with them.
Updated:
Here is my updated Answer to solve this case.
I implemented this method based on the example provided by Phaser.
The key part to making this fixed movement happen is to disable the keyboard input while the item is moving.
Let's say if you pressed left key, the player is moving towards left. While the player starts moving, you set a variable notMoving to false, which notifies the update function to not take any cursor inputs. After a timeout, let's say 1000ms, you can set the variable notMoving to true. Then the update function will continue take the cursor input.
Here is the example code snippet:
function create ()
{
cursors = this.input.keyboard.createCursorKeys();
player = this.physics.add.image(400, 300, 'block');
player.setCollideWorldBounds(true);
player.setData('notMoving', true);
}
function setfixedMovement(velocity, direction) {
if (direction === 'up') {
player.setVelocityY(-velocity);
} else if (direction === 'down') {
player.setVelocityY(velocity);
} else if (direction === 'left') {
player.setVelocityX(-velocity);
} else {
player.setVelocityX(velocity);
}
player.setData('notMoving', false);
setTimeout(() => {
player.setData('notMoving', true);
}, 1000);
}
function update () {
if (player.getData('notMoving')) {
player.setVelocity(0);
if (cursors.left.isDown) {
setfixedMovement(300, 'left');
} else if (cursors.right.isDown) {
setfixedMovement(300, 'right');
}
if (cursors.up.isDown) {
setfixedMovement(300, 'up');
} else if (cursors.down.isDown) {
setfixedMovement(300, 'down');
}
}
}
I put the phaser example link in the previous answer. If you edit the example and replace the part of example code with the snippet I provided above, you can see it working as you expected.
Previous Answer:
I think what this phaser example does exactly what you want to do
In short,
in each update, set the velocity to 0,
then detect the check the cursor key status and set the velocity accordingly
the following code is copied from Official Phaser 3 Example
function update ()
{ player.setVelocity(0);
if (cursors.left.isDown)
{
player.setVelocityX(-300);
}
else if (cursors.right.isDown)
{
player.setVelocityX(300);
}
if (cursors.up.isDown)
{
player.setVelocityY(-300);
}
else if (cursors.down.isDown)
{
player.setVelocityY(300);
}
}
Here is the link to the example
https://labs.phaser.io/view.html?src=src\input\keyboard\cursor%20keys.js

how to drag/resize and rotate rectangle in paperjs?

I want to drag and resize a rectangle in paperjs, I also want to rotate the rectangle and resize it while maintaining its relative dimensions.
Ideally I'd like to do so with my mouse by dragging one of its corners (anchors). What mathematics or feature is helpful in doing this in paperjs?
I have tried this by using scaling and modifying the corners but it doesn't work as I want it to. Could someone point me to a solution?
Thanks in advance.
Here's a simple solution that should get you started. It doesn't handle rotation because I'm not sure how you envision the UI working, but by modifying the bounding box to resize the rectangle you should be able to rotate it without problems.
paperjs sketch
I decided to make up my own UI and go ahead and make the example more complicated to address as much of you question as I can without more information. Here's the new sketch:
new sketch
The UI is
click in rectangle to move it by dragging
click on a corner and drag to resize it
control-click on a corner to rotate it
It's a bit tricky to click the corners, but that's an exercise left to the reader. They are colored circles just to emphasize where each segment point of the Path is located.
Key points of the code:
Use the rectangle's bounds to scale. Path.Rectangle is not a rectangle as far as paper is concerned. It is four curves (which happen to be straight) connecting four segment points. When you need to work with a rectangle to get its center, top left, etc., you need a Rectangle. Scale the visible rectangle by using the rectangle's bounds (Path.Rectangle.bounds). The code illustrates the bounds with an additional aqua rectangle so it's visible (it's easiest to see when rotating).
onMouseDown() sets the state for onMouseDrag() and sets up data needed for each state, e.g., saving the scale base for resizing.
onMouseDrag() implements moving, resizing, and rotating.
tool.onMouseDrag = function(e) {
if (rect.data.state === 'moving') {
rect.position = rect.position + e.point - e.lastPoint;
adjustRect(rect);
} else if (rect.data.state === 'resizing') {
// scale by distance from down point
var bounds = rect.data.bounds;
var scale = e.point.subtract(bounds.center).length /
rect.data.scaleBase.length;
var tlVec = bounds.topLeft.subtract(bounds.center).multiply(scale);
var brVec = bounds.bottomRight.subtract(bounds.center).multiply(scale);
var newBounds = new Rectangle(tlVec + bounds.center, brVec + bounds.center);
rect.bounds = newBounds;
adjustRect(rect);
} else if (rect.data.state === 'rotating') {
// rotate by difference of angles, relative to center, of
// the last two points.
var center = rect.bounds.center;
var baseVec = center - e.lastPoint;
var nowVec = center - e.point;
var angle = nowVec.angle - baseVec.angle;
rect.rotate(angle);
adjustRect(rect);
}
}
Moving is pretty easy - just calculate the difference between the current and last points from the event and change the position of the rectangle by that much.
Resizing is not as obvious. The strategy is to adjust the x and y bounds based on the original distance (scaleBase.length) between the mousedown point and the center of the rectangle. Note that while paper-full.js allows using operators ("+", "-", "*", "/") with points, I used the raw subtract() and multiply() methods a few times - I find it natural to chain the calculations that way.
Rotating uses the very nice paper concept that a point also defines a vector and a vector has an angle. It just notes the difference in the angles between the event lastPoint and point relative to the rectangle's center and rotates the rectangle by that difference.
moveCircles() and adjustRect() are just bookkeeping functions to update the corner circles and aqua rectangle.
Consider the following. I just went through the process of figuring this out, based on lots of examples.
My Goals:
use my own bounding box when selecting an item
Move, Resize, and Rotate (with snap to rotation [45 degrees]) the selected item
Show a title / name of the item
Example Sketch
Paper.js Code
var hitOptions = {
segments: true,
stroke: true,
fill: true,
tolerance: 5
};
function drawHex(w, c, n){
var h = new Path.RegularPolygon(new Point(100, 100), 6, w / 2);
h.selectedColor = 'transparent';
c = c != undefined ? c : "#e9e9ff";
n = n != undefined ? n : "Hexayurt";
h.name = n;
h.fillColor = c;
h.data.highlight = new Group({
children: [makeBounds(h), makeCorners(h), makeTitle(h)],
strokeColor: '#a2a2ff',
visible: false
});
return h;
}
function makeCorners(o, s){
s = s != undefined ? s : 5;
var g = new Group();
var corners = [
o.bounds.topLeft,
o.bounds.topRight,
o.bounds.bottomLeft,
o.bounds.bottomRight
];
corners.forEach(function(corner, i) {
var h = new Path.Rectangle({
center: corner,
size: s
});
g.addChild(h);
});
return g;
}
function makeBounds(o){
return new Path.Rectangle({
rectangle: o.bounds
});
}
function makeTitle(o, n, c){
c = c != undefined ? c : 'black';
var t = new PointText({
fillColor: c,
content: n != undefined ? n : o.name,
strokeWidth: 0
});
t.bounds.center = o.bounds.center;
return t;
}
function selectItem(o){
console.log("Select Item", o.name);
o.selected = true;
o.data.highlight.visible = true;
o.data.highlight.bringToFront();
}
function clearSelected(){
project.selectedItems.forEach(function(o, i){
console.log("Unselect Item", o.name);
o.data.highlight.visible = false;
});
project.activeLayer.selected = false;
}
function moveBoxes(o){
var boxes = o.data.highlight.children[1].children;
boxes[0].position = o.bounds.topLeft;
boxes[1].position = o.bounds.topRight;
boxes[2].position = o.bounds.bottomLeft;
boxes[3].position = o.bounds.bottomRight;
}
function moveTitle(o){
var t = o.data.highlight.children[2];
t.bounds.center = o.bounds.center;
}
function adjustBounds(o){
if(o.data.state == "moving"){
o.data.highlight.position = o.position;
} else {
o.data.highlight.children[0].bounds = o.bounds;
moveBoxes(o);
}
}
var hex1 = drawHex(200);
console.log(hex1.data, hex1.data.highlight);
var segment, path;
var movePath = false;
var tool = new Tool();
tool.minDistance = 10;
tool.onMouseDown = function(event) {
segment = path = null;
var hitResult = project.hitTest(event.point, hitOptions);
if (!hitResult){
clearSelected();
return;
}
if(hitResult && hitResult.type == "fill"){
path = hitResult.item;
}
if (hitResult && hitResult.type == "segment") {
path = project.selectedItems[0];
segment = hitResult.segment;
if(event.modifiers.control){
path.data.state = "rotating";
} else {
path.data.state = "resizing";
path.data.bounds = path.bounds.clone();
path.data.scaleBase = event.point - path.bounds.center;
}
console.log(path.data);
}
movePath = hitResult.type == 'fill';
if (movePath){
project.activeLayer.addChild(hitResult.item);
path.data.state = "moving";
selectItem(path);
console.log("Init Event", path.data.state);
}
};
tool.onMouseDrag = function(event) {
console.log(path, segment, path.data.state);
if (segment && path.data.state == "resizing") {
var bounds = path.data.bounds;
var scale = event.point.subtract(bounds.center).length / path.data.scaleBase.length;
var tlVec = bounds.topLeft.subtract(bounds.center).multiply(scale);
var brVec = bounds.bottomRight.subtract(bounds.center).multiply(scale);
var newBounds = new Rectangle(tlVec + bounds.center, brVec + bounds.center);
path.bounds = newBounds;
adjustBounds(path);
} else if(segment && path.data.state == "rotating") {
var center = path.bounds.center;
var baseVec = center - event.lastPoint;
var nowVec = center - event.point;
var angle = nowVec.angle - baseVec.angle;
if(angle < 0){
path.rotate(-45);
} else {
path.rotate(45);
}
adjustBounds(path);
} else if (path && path.data.state == "moving") {
path.position += event.delta;
adjustBounds(path);
}
};
This makes use of .data to store references of the bounding box, handles, and title as a Group. This way, they are always there, they can just visible true or false. This makes it easy to show and hide them as needed.
drawHex( width , color, name )
Width - Required, number of pixels wide
Color - Optional, string that defines the Fill Color. Default: #e9e9ff
Name - Optional, string to be used as the Name and Title. Default: "Hexayurt"
Interactions
click - Select item (show bounding box & Handles)
click + drag - Move item
click + drag handle - Resize item
Ctrl + click + drag handle - Rotate item
This is my first pass at it and I may cleanup a lot of the code. For example, I could bind events to the handles specifically instead of looking at more global events.

AS3 Display Objects & Sprites (Adding children based on a random number)

ActionScript 3.0
Essentially I need a function that displays a random number of butterfly objects. (Also I should make a reset function that resets the first function.)
I find though that the butterflies are not even displayed to begin with, It seems that the children are not being added to the stage even though I used the addChild().
Any help is appreciate thanks!
// Random Number
var randomNumber : int = Math.floor(Math.random() * 8);
// New Sprite
var bContainer: Sprite = new Sprite();
this.addChild(bContainer);
var butterfly: MovieClip = new Butterfly();
bContainer.addChild(butterfly);
//Function to Create Butterfly Objects:
function showButterfly(randomNumber:int):void {
while(bContainer.numChildren < randomNumber){
bContainer.addChild(butterfly);
}
//Reset Function, I am not sure about this (especially the second one)
function button(evt:MouseEvent): void {
if(numChildren>0) {
removeChildAt(0);
}
if(numChildren==0) {
showButterfly();
}
}
// Event Listener
button.addEventListener(MouseEvent.MOUSE_DOWN);
//
I'm not completely sure, but aren't you suppose to set the width and height for the sprite that works as a holder object?
var bContainer: Sprite = new Sprite();
bContainer.graphics.beginFill(0xffffff);
bContainer.graphics.drawRect(0, 0,stage.stageWidth, stage.stageHeight);
addChildAt(bContainer, 0);

YUI3 scrollView and mousewheel

I'm starting to work only with YUI3. I include component scrollView, but it did not work mousewheel event, in the options I have not found how to turn on it. I would appreciate any help.
var scrollView = new Y.ScrollView({
id: "scrollview",
srcNode: '.scrollview-item',
height: 375,
flick: {
minDistance: 10,
minVelocity: 0.3,
axis: "y"
}
});
scrollView.render();
I stumbled upon this as well, after some trial and error, I managed to get that working(note, that it is just plain scrolling, without an easing).
var DOM_MOUSE_SCROLL = 'DOMMouseScroll',
fixArgs = function(args) {
var a = Y.Array(args, 0, true), target;
if (Y.UA.gecko) {
a[0] = DOM_MOUSE_SCROLL;
// target = Y.config.win;
} else {
// target = Y.config.doc;
}
if (a.length < 3) {
// a[2] = target;
} else {
// a.splice(2, 0, target);
}
return a;
};
Y.Env.evt.plugins.mousewheel = {
on: function() {
return Y.Event._attach(fixArgs(arguments));
},
detach: function() {
return Y.Event.detach.apply(Y.Event, fixArgs(arguments));
}
};
This is the YUI mousewheel event, but it's changed a bit. The biggest issue was, that originally, either the window or document elements, which makes no sense(for example when you mousewheel over the #myelement you want that to be the returned target..)
Bellow is the code used to initialize the ScrollView and the function that handles the mousewheel event:
// ScrollView
var scrollView = new Y.ScrollView({
id: "scrollview",
srcNode: '#mycontainer',
height: 490,
flick: {
minDistance:10,
minVelocity:0.3,
axis: "y"
}
});
scrollView.render();
var content = scrollView.get("contentBox");
var scroll_modifier = 10; // 10px per Delta
var current_scroll_y, scroll_to;
content.on("mousewheel", function(e) {
// check whether this is the scrollview container
if ( e.currentTarget.hasClass('container') ) {
current_scroll_y = scrollView.get('scrollY');
scroll_to = current_scroll_y - ( scroll_modifier * e.wheelDelta );
// trying to scroll above top of the container - scroll to start
if ( scroll_to <= scrollView._minScrollY ) {
// in my case, this made the scrollbars plugin to move, but I'm quite sure it's important for other stuff as well :)
scrollView._uiDimensionsChange();
scrollView.scrollTo(0, scrollView._minScrollY);
} else if ( scroll_to >= scrollView._maxScrollY ) { // trying to scroll beneath the end of the container - scroll to end
scrollView._uiDimensionsChange();
scrollView.scrollTo(0, scrollView._maxScrollY);
} else { // otherwise just scroll to the calculated Y
scrollView._uiDimensionsChange();
scrollView.scrollTo(0, scroll_to);
};
// if we have scrollbars plugin, flash the scrollbar
if ( scrollView.scrollbars ) {
scrollView.scrollbars.flash();
};
// prevent browser default behavior on mouse scroll
e.preventDefault();
};
});
So basically that's how I managed to that, but my next challenge is to get the scrollbar work like regular scrollbar(when you drag it, the container should move correspondingly...)
Hope this helps anyone :)

Resources