phaser - How to zoom around point of focus (mouse or pinch) - game-engine

I want that my phaser world scales at the point where my mouse pointer is (or between 2 fingers/pinch). Per default, phaser scales/zooms around the top left corner.
Here is an example (but not for phaser) that shows what exactly I want to achive: https://cloudup.com/blog/how-we-made-zoom-on-mobile-using-css3-and-js

To scale a group of items based on a the clicked point.
Capture the start position of the group (x,y) and the mouse click point:
startPosX = this.position.x;
startPosY = this.position.y;
clickX = pointer.x;
clickY = pointer.y;
Perform the scale of the group:
var tweenScale = game.add.tween(this.scale).to( { x: 2, y: 2 }, 10,
Phaser.Easing.Linear.None, true, 0, 0, false);
On scale complete, tween back to mouse click location:
tweenScale.onComplete.add(function() {
var u = ((1-this.scale.x) * clickX) + startPosX;
var v = ((1-this.scale.y) * clickY) + startPosY;
game.add.tween(this.position)
.to({ x: this.position.x + u, y: this.position.y + v}, 10,
Phaser.Easing.Linear.None, true, 0, 0, false );
}, this);
I added startPosX to get u (and v respectively) to account for the location of the original starting point of the group or else it would be off, by that amount. I'm sure there are different ways to approach the tween chaining, but the calculations for u and v are what's important.
Used this answer to derive the basic calculation: https://stackoverflow.com/a/39344716/1956540

See these examples of working implementations:
http://test.xapient.net/phaser/springtest/indexzoom.html (a and o)

Related

Build a javascript sliding puzzle

I am attempting to build a game that follows the same mechanic as Dragon's Tail featured at the following link http://bit.ly/1CR0iha. I have built a very basic version of level one working using mouse clicks with one open space. I really want it to move on pressmove so that I can drag the pieces to move them rather than on click which will help when there are more than one open space as on level two.
How to confine the movement of the pieces so that they can ONLY move into the open space? My initial though was to place all the pieces into a grid and check for collisions between the various pieces on pressmove and only allow a move if there is no collision.
After some coding I need a sanity check because I am now second guessing my decision. I am asking for on a high level sanity check on my method any insights are most welcome ;);
Once I have set my x and y on mousedown I them do the following on pressmove. I am not limiting my direction which is what I am trying to achieve.
pressMove = function(event){
var pt = this.globalToLocal(event.stageX, event.stageY);
if(pt.x > this.startPosX){
this.dragDirection = 'right';
}
if(pt.x < this.startPosX){
this.dragDirection = 'left';
}
if(pt.y < this.startPosY){
this.dragDirection = 'up';
}
if(pt.y > this.startPosY){
this.dragDirection = 'down';
}
movePiece(event);
}
Moving the piece
movePiece = function(event){
var obj = event.target;
var pt = this.globalToLocal(event.stageX, event.stageY);
switch(this.dragDirection){
case 'up':
obj.y = pt.y;
break;
case 'down':
obj.y = pt.y;
break;
case 'left':
obj.x = pt.x;
break;
case 'right':
obj.x = pt.x;
break;
};
}
you don't need sofisticated collision detection - all you need to know is which are the possible moves (up, down, right, left) allowed for a clicked piece.
you can do this by having a 2d field representing blocked spaces 1 and free spaces 0 like so
A B C D
X 1 0 1 1
Y 1 1 1 1
Z 1 1 1 1
say you click the piece (Y,B) you can see by checking its four neighbouring entries that only the move up is available, similarly for XA and XC. for all other pieces there are no free spaces and thus no possible moves. after moving a piece, make sure its again in a grid position and update the 2d field. i.e. after moving YB up the field should look like this:
A B C D
X 1 1 1 1
Y 1 0 1 1
Z 1 1 1 1
you can make boundary handling easier by surrounding your actual playfield with blocked spaces
Edit: say you only move up/down and left/right. Every move starts at the center of a piece. Around the center there's a deadzone where it's not clear (yet) which direction the move will take. Then your code might look like this:
startDrag(x, y) {
// remember clicked piece p
p <- ...
// remember center positions for p (cX, cY)
(cX, cy) <- ...
// remember offset of cursor to center (oX, oY)
(oX, oY) <- (x - cX, y - cY)
}
continueDrag(x, y) {
// select move direction
if distance between (cX, cY) and (x - oX, y - oY) is within the deadzone
select move direction (up, down, left, right) with least projection error
else
select last move direction
end
// get constrained move direction
switch selected move direction
case up: perform move to (cX, y - oY)
case down: perform move to (cX, y - oY)
case left: perform move to (x - oX, cY)
case right: perform move to (y - oX, cY)
end
}
you can see all of this in action (and a bit more robust) here: http://js.do/code/sliding-puzzle

Android - Trying to gradually fill a circle bottom to top

I'm trying to fill a round circle (transparent other than the outline of the circle) in an ImageView.
I have the code working:
public void setPercentage(int p) {
if (this.percentage != p ) {
this.percentage = p;
this.invalidate();
}
}
#Override public void onDraw(Canvas canvas) {
Canvas tempCanvas;
Paint paint;
Bitmap bmCircle = null;
if (this.getWidth() == 0 || this.getHeight() == 0 )
return ; // nothing to do
mergedLayersBitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888);
tempCanvas = new Canvas(mergedLayersBitmap);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setFilterBitmap(false);
bmCircle = drawCircle(this.getWidth(), this.getHeight());
tempCanvas.drawBitmap(bmCircle, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
tempCanvas.clipRect(0,0, this.getWidth(), (int) FloatMath.floor(this.getHeight() - this.getHeight() * ( percentage/100)));
tempCanvas.drawColor(0xFF660000, PorterDuff.Mode.CLEAR);
canvas.drawBitmap(mergedLayersBitmap, null, new RectF(0,0, this.getWidth(), this.getHeight()), new Paint());
canvas.drawBitmap(mergedLayersBitmap, 0, 0, new Paint());
}
static Bitmap drawCircle(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(drawColor);
c.drawOval(new RectF(0, 0, w, h), p);
return bm;
}
It kind of works. However, I have two issues: I run out of memory quickly and the GC goes crazy. How can I utilize the least amount of memory for this operation?
I know I Shouldn't be instantiating objects in onDraw, however I'm not sure where to draw then. Thank you.
pseudo would look something like this.
for each pixel inside CircleBitmap {
if (pixel.y is < Yboundary && pixelIsInCircle(pixel.x, pixel.y)) {
CircleBitmap .setPixel(x, y, Color.rgb(45, 127, 0));
}
}
that may be slow, but it would work, and the smaller the circle the faster it would go.
just know the basics, bitmap width and height, for example 256x256, the circles radius, and to make things easy make the circle centered at 128,128. then as you go pixel by pixel, check the pixels X and Y to see if it falls inside the circle, and below the Y limit line.
then just use:
CircleBitmap .setPixel(x, y, Color.rgb(45, 127, 0));
edit: to speed things up, don't even bother looking at the pixels above the Y limit.
in case if you want to see another solution (perhaps cleaner), look at this link, filling a circle gradually from bottom to top android

Rotating camera around the X-axis (three.js)

I am trying to rotate the camera around to X-axis of the scene.
At this point my code is like this:
rotation += 0.05;
camera.position.y = Math.sin(rotation) * 500;
camera.position.z = Math.cos(rotation) * 500;
This makes the camera move around but during the rotation something weird happens and either the camera flips, or it skips some part of the imaginary circle it's following.
You have only provided a snippet of code, so I have to make some assumptions about what you are doing.
This code:
rotation += 0.05;
camera.position.x = 0;
camera.position.y = Math.sin(rotation) * 500;
camera.position.z = Math.cos(rotation) * 500;
camera.lookAt( scene.position ); // the origin
will cause the "flipping" you refer to because the camera is trying to remain "right side up", and it will quickly change orientation as it passes over the "north pole."
If you offset the camera's x-coordinate like so,
camera.position.x = 200;
the camera behavior will appear more natural to you.
Three.js tries to keep the camera facing up. When you pass 0 along the z-axis, it'll "fix" the camera's rotation. You can just check and reset the camera's angle manually.
camera.lookAt( scene.position ); // the origin
if (camera.position.z < 0) {
camera.rotation.z = 0;
}
I'm sure this is not the best solution, but if anyone else runs across this question while playing with three.js (like I just did), it'll give one step further.
This works for me, I hope it helps.
Rotating around X-Axis:
var x_axis = new THREE.Vector3( 1, 0, 0 );
var quaternion = new THREE.Quaternion;
camera.position.applyQuaternion(quaternion.setFromAxisAngle(x_axis, rotation_speed));
camera.up.applyQuaternion(quaternion.setFromAxisAngle(x_axis, rotation_speed));
Rotating around Y-Axis:
var y_axis = new THREE.Vector3( 0, 1, 0 );
camera.position.applyQuaternion(quaternion.setFromAxisAngle(y_axis, angle));
Rotating around Z-Axis:
var z_axis = new THREE.Vector3( 0, 0, 1 );
camera.up.applyQuaternion(quaternion.setFromAxisAngle(z_axis, angle));
I wanted to move my camera to a new location while having the camera look at a particular object, and this is what I came up with [make sure to load tween.js]:
/**
* Helper to move camera
* #param loc Vec3 - where to move the camera; has x, y, z attrs
* #param lookAt Vec3 - where the camera should look; has x, y, z attrs
* #param duration int - duration of transition in ms
**/
function flyTo(loc, lookAt, duration) {
// Use initial camera quaternion as the slerp starting point
var startQuaternion = camera.quaternion.clone();
// Use dummy camera focused on target as the slerp ending point
var dummyCamera = camera.clone();
dummyCamera.position.set(loc.x, loc.y, loc.z);
// set the dummy camera quaternion
var rotObjectMatrix = new THREE.Matrix4();
rotObjectMatrix.makeRotationFromQuaternion(startQuaternion);
dummyCamera.quaternion.setFromRotationMatrix(rotObjectMatrix);
dummyCamera.up.set(camera)
console.log(camera.quaternion, dummyCamera.quaternion);
// create dummy controls to avoid mutating main controls
var dummyControls = new THREE.TrackballControls(dummyCamera);
dummyControls.target.set(loc.x, loc.y, loc.z);
dummyControls.update();
// Animate between the start and end quaternions
new TWEEN.Tween(camera.position)
.to(loc, duration)
.onUpdate(function(timestamp) {
// Slerp the camera quaternion for smooth transition.
// `timestamp` is the eased time value from the tween.
THREE.Quaternion.slerp(startQuaternion, dummyCamera.quaternion, camera.quaternion, timestamp);
camera.lookAt(lookAt);
})
.onComplete(function() {
controls.target = new THREE.Vector3(scene.children[1].position-0.001);
camera.lookAt(lookAt);
}).start();
}
Example usage:
var pos = {
x: -4.3,
y: 1.7,
z: 7.3,
};
var lookAt = scene.children[1].position;
flyTo(pos, lookAt, 60000);
Then in your update()/render() function, call TWEEN.update();
Full example

Raphael -- How to fit the paper size to the browser's window size?

I want to change the paper(objects base) size of Raphael to fit the window resizing. [ using Firefox_13.0, Raphael_2.1.0, WindowsXP ]
If it is available, I would like to fit full-screen-mode.
==================================================
(steps)
I created the paper : paper = Raphael(0, 50, 800, 600); // initial width and height are 800 and 600.
I placed objects on the paper.
The window size of browser is checked by windowW = window.innerWidth and winnowH = window.innerHeight (on Firefox).
Scaling value is calculated by sv = windowW/800;
And scaling the paper by paper.scale(sv, sv);
==================================================
(the script)
window.onload = function () {
paper = Raphael(0, 50, 800, 600);
var background = paper.rect(0, 0, 800, 600).attr({fill:'#669999'});
// placing the objects
var circle = ...;
var rect = ...;
var ellipse = ...;
winowW = window.innerWidth;
winowH = window.innerHeight;
sv = winowW/800.
paper.scale(sv, sv);
}
==================================================
(result)
Though circle.scale(sv), rect.scale(sv, sv) and ellipse.scale(sv, sv) are valid, paper.scale(sv, sv) and background.scale(sv, sv) are not.
Why this case is happen ? I can get the window size by window.onresize = function() {...} on real-time. If there are better methods, please tell me.
Thanks,
I've succeeded by following two points:
1) "paper" itself is not manipulative object. I think we should look it as billboard.
2) use st = paper.set() and put the objects(circle, rect, ...) in it. And use st.scale(sv, sv, 0, 0);
* third and fourth parameter (0, 0) are very impotent.
(caution)
Serial resizing operation is not good for the function "scale()". Because each of resizing coefficient is piled as the
power of a number. So when one have done 1.1 times resizing operation 5 times, the scale will be 1.1^5.
Use setViewBox()
It should do the work
http://raphaeljs.com/reference.html#Paper.setViewBox

Dynamic grid using FlashDevelop & Actionscript 2.0

I'm new to actionscript. What I'm tryin to do is simulate traffic flow near a 2 lane intersection, following Wolfram's rule 184. To begin with, I'm trying to create a grid (8x8 of which the intersection is between the middle two rows and the middle two columns, like a plus sign) whose cells have the following attributes:
color = white;
car = false;
when clicked:
color = red;
car = true (a car is present);
So, after the user clicks cells to position the cars initially and presses the start button, the simulation will begin.
Here's my code so far (apologies for incorrect formatting):
class Main
{
private var parent:MovieClip;
public static function main(mc:MovieClip)
{
var app = new Main(mc);
}
public function Main(mc:MovieClip)
{
this.parent = mc;
//grid settings
var Cell:MovieClip = mc.createEmptyMovieClip("cell", mc.getNextHighestDepth());
var x:Number = 0;
var y:Number = 0;
var color:Number = 0xffffff;
var car:Boolean = false;
for (y = 0; y < 3 * Stage.height / 8; y += Stage.height / 8)
{
for (x = 3*Stage.width/8; x < 5*Stage.width/8; x+=Stage.width/8)
{
UI.drawRect(Cell, x, y, (Stage.width / 8) - 5, (Stage.height / 8) - 5, color, 100);
}
}
for (y = 3*Stage.height/8; y < 5 * Stage.height / 8; y += Stage.height / 8)
{
for (x = 0; x < Stage.width; x+=Stage.width/8)
{
UI.drawRect(Cell, x, y, (Stage.width / 8)-5, (Stage.height / 8)-5, color, 100);
}
}
for (y = 5*Stage.height/8; y < Stage.height; y += Stage.height / 8)
{
for (x = 3*Stage.width/8; x < 5*Stage.width/8; x+=Stage.width/8)
{
UI.drawRect(Cell, x, y, (Stage.width / 8)-5, (Stage.height / 8)-5, color, 100);
}
}
Cell.onMouseDown()
{
Cell.color = UI.RED;
Cell.car = true;
}
}
}
I know there's quite a few things gone wrong here. First of all, the cell color doesn't change on mouse down. Do i need to make movie clip for each cell in the for loops? I think it would be easier to make a grid of objects with given attributes, but i don't know how to do that. Would really appreciate if someone helps me out.
From what I can tell, issue with your current approach is that using drawRect() literally draws pixels on to the stage, which means you'll have no reference to those shapes in future frames. right now, you've got one MovieClip that has been drawn many times. What you need is a lot of MovieClips so you have a reference to each cell that you can update/edit every frame.
Your best bet is to do the following (I'll just provide pseudo because I'm a bit shaky on AS2 syntax):
A) Create an array to hold all of the Cells. Call it:
var Cells:Array = new Array();
B) During each step of the loops in your constructor, do 4 things.
1) Create a new MovieClip `var tempCell:MovieClip = new MovieClip();
2) Draw a rectangle on to each MovieClip: A tutorial for the graphics API in AS2 http://www.actionscript.org/resources/articles/727/1/Drawing-shapes-with-AS2/Page1.html
3) Add an event listenerto each MovieClip that points to a common event handler. This listener listens for mouse clicks on that MovieClip (or MOUSE_DOWN)
4) and use Cells.push(tempClip) to add that new MovieClip to your array so you now have one object that contains a reference to all of your cells.
C) Create an click event handler that redraws the cell that has been clicked. Try MouseEvent.target
You have another option to using the graphics API to draw rectangles, and that is to simply add and remove stock graphics from your Flash library. You'll have to draw these graphics in Flash and then 'Export for Actionscript' to call them up.
Hope this points you in the right direction!
J