Phaser 3 - set min and max zoom levels - zooming

I'm using Phaser to create an online comic. One functionality I want to have is the option to zoom into images, for the sake of legibility on small screens.
I'm using the following on a container holding the image.
container.scale = 1;
this.input.on('wheel', function (pointer, gameObjects, deltaX, deltaY, deltaZ) {
var x = deltaY * 0.002;
container.scale += x;
console.log(container.scale);
});
So far so good, the image zooms.
I want to set a minimum zoom level of 1 and a maximum zoom level of 1.5.
I thought this modification to the code would do it:
container.scale = 1;
this.input.on('wheel', function (pointer, gameObjects, deltaX, deltaY, deltaZ) {
var x = deltaY * 0.002;
function between(x, min, max) {
return x >= min && x <= max;
}
if (between(x, 1, 1.5)) {
container.scale += x;
console.log(x, container.scale);
}
});
But the code won't fire at all. I've tried variations and gotten nowhere - can anyone help with this?

The WheelEvent.deltaY read-only property is a double representing the vertical scroll amount in the WheelEvent.deltaMode unit.
You're comparing the set amount the wheel is actually spinning versus that range, which is why it'll never fire. On my end your x value will be either 0.24 (down-spin) or -0.24 (up-spin) depending on the wheel spin direction.
This is closer to what you might want to be achieving:
if((x < 0 && container.scale + x >= 1) || (x > 0 && container.scale + x <= 1.5)) {
container.scale += x;
}

Related

as3 move towards function jitters on Y axis

As the title suggests, I've written a move towards function that works perfectly fine, except that it jitters on the Y axis.
The problem
It happens when the function is operating on the Y axis and the difference between the starting value and the target value is less than that between the starting value and the value to return - meaning that the value has gone past its target. as this point, it's supposed to set the returning value to the target value, but for the most part it's not. Except when it's operating on the x axis, in which case it works fine. It's really strange.
The code
here's the code I'm using for the function:
public static function LookAt(thisX:Number, thisY:Number, targetX:Number, targetY:Number, speed:Number = 0, startRot:Number = 0):Number
{
// Get the distances between the two parsed points
var xDif:Number = targetX - thisX;
var yDif:Number = targetY - thisY;
// Use a tangent formula to get the rotation to return in radians, then convert to degrees
var rot:Number = Math.atan2(xDif, yDif) * 180/Math.PI * -1 - 180;
// If a speed has been parsed
if (speed != 0)
{
// Ensure the parsed starting rotation is between -180 and 180
while (startRot > 180)
{startRot -= 360;}
while (startRot < -180)
{startRot += 360;}
// If the rotation previously calculated is less than the parsed starting rotation,
// return the starting rotation minus the speed. Otherwise, return the starting rotation
// plus the speed
return (rot > startRot) ? startRot + speed : startRot - speed;
}
else
{
return rot;
}
}
public static function PointAround (axisPos:Number, angle:Number, speed:Number, axis:String = "x"):Number
{
// Convert the parsed angle into radians
var fixedRot = angle * Math.PI / 180;
// Return the parsed position plus speed multiplied by the sine of the angle in radians for the x axis,
// or the cosine of the angle in radians for the y axis
return (axis == "x") ? axisPos + speed * Math.sin(fixedRot) : axisPos + speed * Math.cos(fixedRot) * -1;
}
public static function PointTowards(thisX:Number, thisY:Number, targetX:Number, targetY:Number, speed:Number, axis:String = "x"):Number
{
// Use the LookAt function to calculate a rotation for later use in this function
var workingAngle:Number = ExtraMath.LookAt (thisX, thisY, targetX, targetY);
var toReturn;
var thisVar:Number = (axis == "x") ? thisX : thisY;
var targetVar:Number = (axis == "x") ? thisX : thisY;
toReturn = ExtraMath.PointAround (thisVar, workingAngle, speed);
// BUGGY LINE
toReturn = (thisVar >= targetVar && toReturn <= targetVar
|| thisVar <= targetVar && toReturn >= targetVar)
? targetVar
: toReturn;
return toReturn;
}
and here's the code I'm using to test it:
public var c:Sprite;
public function TestZone()
{
// constructor code
stage.addEventListener(Event.ENTER_FRAME, Update);
}
private function Update (e:Event):void
{
c.x = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5, "y");
}
things I've tried already
turning the line into regular a regular if statement with curly brackets and all, and tracing the variables thisY, targetY and toReturn after it has been operated on. the really annoying thing is that it turns out it sometimes actually returns the right number, but then proceeds to bug out again
Using an absolute value instead of stage.mouseY in testing. bug occurs as usual
Performing the function on the Y axis before the X axis. no difference
Changing the condition for setting the variables thisVar and targetVar to (axis != x) and switching the if/else values. do difference
A few things may be causing the problem:
[LookAt]
return (rot > startRot) ? startRot + speed : startRot - speed;
This may cause overshooting. If rot is only very slightly different from startRot, you are still adding (or subtracting) a full speed increment. If the absolute distance between rot and startRot is less than speed, it should return rot regardless.
This reduces angular jittering.
[PointAround]
return (axis == "x") ? axisPos + speed * Math.sin(fixedRot) : axisPos + speed * Math.cos(fixedRot) * -1;
Watch out for operator precedence. You are expecting this line to be parsed as
(axis == "x") ?
(axisPos + speed * Math.sin(fixedRot)) :
(axisPos + speed * Math.cos(fixedRot) * -1);
But that may not be the case. The line may instead be interpreted as
(
(axis == "x") ?
(axisPos + speed * Math.sin(fixedRot)) :
axisPos
)
+ speed * Math.cos(fixedRot) * -1;
You can either memorize all precedence rules and make sure you never mistake them, or put parenthesis around to ensure it's doing the right thing. In this case you can simplify the expression to
axisPos + speed * (axis == "x" ? Math.sin(fixedRot) ? -Math.cos(fixedRot))
var thisVar:Number = (axis == "x") ? thisX : thisY;
var targetVar:Number = (axis == "x") ? thisX : thisY;
So thisVar and targetVar always have the same value? I don't understand what was supposed to happen here, and nobody seems to reassign those variables later.
c.x = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(c.x, c.y, stage.mouseX, stage.mouseY, 5, "y");
You are changing the position separately in each axis. It may work, but it's harder and error prone. For example
c.x = ...PointTowards(c.x ...);
c.y = ...PointTowards(c.x ...);
You are changing the value of c.x between the calls, so the first call to PointTowards see a different point from the second call. That may be the reason why the jittering only happens on the y axis. I suggest making a function that deals with box axis at once, or at the very least storing the old values of c.x and c.y:
var oldX:Number = c.x;
var oldY:Number = c.y;
c.x = ExtraMath.PointTowards(oldX, oldY, stage.mouseX, stage.mouseY, 5);
c.y = ExtraMath.PointTowards(oldX, oldY, stage.mouseX, stage.mouseY, 5, "y");
I fixed it! I changed the function to return a point object instead of a number and now it works perfectly

Function to convert numbers over 1000 to 1k etc. AS3

G'day,
The function is coded, but it's on the stage frame. I'm looking to get it converted into a more dynamic function so I can just call it on all my textfields.
Here's the code:
function numtolet():void
{
output.text = String(int(earner * 100) / 100);
if (earner >= 1000 && earner < 1000000)
{
output.text = String(int((earner/1000) * 100) / 100 + "k");
}
else if (earner >=1000000 && earner < 1000000000)
{
output.text = String(int((earner/ 1000000) * 100 ) / 100 + " M");
}
}
I'm looking to turn the 'output.text' portion into a variable that changes based on the text field calling the function and 'earner' to the variable the textfield reads.
Cheers,
-Aidan.
You'd better write your function as proper function that can return a String value to assign to a text property or use elsewhere. Also, you should use a pattern that is easily extendable to bigger prefixes, should you need them. Say, I have found a game with a W prefix being used, which is one beyond the common "yotta" prefix, and there was a set of subsequent prefixes as well. So, this is how you should devise such a function:
function numtolet(x:Number):String {
const prefixes:Vector.<String> = Vector.<String>(["","k","m","g","t"]);
// add more to taste. Empty prefix is used if the number is less than 1000
var y:Number=x;
var i:int=1;
// provided x>0, if not, store a minus somewhere and attach later
while((y>=1000) && (i<prefixes.length)) {
y=y/1000;
i++;
}
// there, you have just divided X by 1000 a couple of times and selected the prefix
var s:String = y.toFixed(2)+prefixes[i-1];
// if there was a minus, add it here: s="-"+s;
return s;
}
Then you just call it like this:
output.text=numtolet(earner);
You can do this using the CHANGE event:
output.addEventListener(Event.CHANGE, numtolet);
function numtolet(e:Event):void
{
output.text = String(int(earner * 100) / 100);
if (earner >= 1000 && earner < 1000000)
{
output.text = String(int((earner/1000) * 100) / 100 + "k");
}
else if (earner >=1000000 && earner < 1000000000)
{
output.text = String(int((earner/ 1000000) * 100 ) / 100 + " M");
}
}
This will make the function run every time the text is changed by a user, but you'd probably want to add a few conditional (if)'s to the function, or use a variable to keep track of the curent number. When the number converts to 1k, how does it know what do to at 1000k?
Feel free to ask if you need help on this.
When you use the CHANGE event like Neguido said, and add listeners to different text fields, you can use e.target.text = to change the text in the calling text field.
To target a different variable for each text field is more difficult, because you cant pass extra arguments into the event handlers, and you cant add your own variables/properties to textFields. You could stick each textField into a parent MovieClip and then create variables in there like MovieClip1.earner = 0 and retrieve the values with e.target.parent.earner. You could also write a dynamic extension to the TextField class where you add custom variables. Alternatively you could use a switch statement in your event handler to use different variables for different callers.
Here's a quick function I wrote up for something else. It can easily be adapted to larger numbers by adding another if statement and adding 000 to the number. It also doesn't include the output display, but that can very easily be added as well. Hope it helps!
Call the function via numToLet(earner).
function numToLet(x) {
if (x > 1000000000000000000) {
x = x / 1000000000000000000
x = Number(x.toFixed(2));
return x + "Quin";
}
if (x > 1000000000000000) {
x = x / 1000000000000000
x = Number(x.toFixed(2));
return x + "Quad";
}
if (x > 1000000000000) {
x = x / 1000000000000
x = Number(x.toFixed(2));
return x + "Tril";
}
if (x > 1000000000) {
x = x / 1000000000
x = Number(x.toFixed(2));
return x + "Bil";
}
if (x > 1000000) {
x = x / 1000000
x = Number(x.toFixed(2));
return x + "Mil";
}
if (x < 1000000) {
x = Number(x.toFixed(2));
return x;
}
}

simple snake game - libgdx

I'm trying to develop a simple snake game with libgdx. My problem is that everytime I want to spawn some apples(texture, 20px width X 20px height) it always overlaps the body of the snake.
I'm trying to avoid this but it keeps occuring during the game.
The snake is compiled of parts - every part is a texture of 20px width X 20px height(The screen width is 480px width X 800px height)
Here is what I have tried so far:
public void addApple() {
accomplishedSnake = false;
accomplishedApples = false;
while (!accomplishedSnake && !accomplishedApples) {
xApple = 20 * MathUtils.random(0, 23);
yApple = 20 * MathUtils.random(20, 36);
if (!accomplishedSnake) {
for (int i = 0; i < snake.getSize(); i++) {
if (snake.getPart(i).getX() <= xApple
&& snake.getPart(i).getX() + 20 >= xApple
&& yApple >= snake.getPart(i).getY()
&& yApple <= snake.getPart(i).getY() + 20)
break;
if (i == snake.getSize() - 1) {
accomplishedSnake = true;
break;
}
}
}
if (!accomplishedApples) {
for (int i = 0; i < apples.size; i++) {
if (apples.get(i).getX() <= xApple && apples.get(i).getX()+20 >= xApple
&& yApple >= apples.get(i).getY() && yApple <= apples.get(i).getY()+20)
break;
if (i == apples.size - 1) {
accomplishedApples = true;
break;
}
}
}
}
apples.add(new Apple(xApple, yApple));
}
The code is pretty self-explanatory. In every moment I have 3 different apples on the screen. This code tries to raffle x-y coordinates to the new apple but before the apple is added to the screen and rendered, I want to make sure that it doesn't overlaps the body of the snake or the other apples.
I just can't see what's wrong with this code.
P.S I tried to use the overlaps method in the Rectangle class but it doesn't work.
You test conditions are too simple. Each item snake body part/apple has size, you need to consider their locus. So [snake.X,snake.X+20] and [snake.Y,snake.Y+20] is occupied by each body part, you need to ensure both apple.X and apple.X+20 aren't in range, the same for apple.Y
try this ..
if (snake.getPart(i).getX() >= xApple
&& snake.getPart(i).getX() + 20 <= xApple
&& snake.getPart(i).getX() >= xApple+20
&& snake.getPart(i).getX() + 20 <= xApple+20
&& snake.getPart(i).getY() >= yApple
&& snake.getPart(i).getY() + 20 <= yApple
&& snake.getPart(i).getY() >= yApple +20
&& snake.getPart(i).getY() + 20 <= yApple +20)
break;
also you need to ensure accomplishedApple and accomplishedSnake are set to false both before entering the while loop and after each calculation of random coordinates.
you also need to mimic this logic when establishing accomplishedApple further down your code.
This game should be tile-based.
This means, that the game is organized as a grid, where every cell has the same size.
This is done by using a camera:
OrthographicCamera cam = new OrthographicCamera(viewportWidth, viewportHeight)
The viewportWidth and viewportHeight define how many cells there should be in x and y.
As you are using a resolution of 480*800px and pictures with a size of 20*20px, the viewportWidth should be 480/20=24 and the viewportHeight should be 800/20 = 40.
Note, that the cameras P(0/0) is in the middle of the screen and you see objects from -12 to 12 (x) and -20 to 20 (y). Just set the position of the camera to P(12/20) and the P(0/0) is the lower left corner, as usual.
Now the spawning would be easy:
I use a int[][] world, where i store, if there is a:
Snakepart at P(x,y) (world[x][y] == 1)
Apple at P(x,y) (world[x][y] == 2)
Nothing at P(x,y) (world[x][y] == 0)
This means, that if the snake leaves a tile, you need to set its value to 0 (world[x][y] = 0) and every tile it enters to 1 (world[x][y] = 1)
public void spawnApple() {
boolean spawned = false;
while(!spawned) {
// The lower left corner is always an int-value
int x = (int)MathUtils.random(0, 23);
int y = (int)MathUtils.random(0, 40);
if (world[x][y] == 0) {
// Add Apple at x,y
world[x][y] = 2;
}
}
}
Hope it helps!
private void checkAndPlaceApple() {
if(!appleAvailable) {
do {
appleX = MathUtils.random(Gdx.graphics.getWidth / 20 - 1) * 20;
appleY = MathUtils.random(Gdx.graphics.getHeight / 20 - 1) * 20;
appleAvailable = true;
} while(appleX == snakeX && appleY == snakeY);
}
}
The previous code shows the required rule for placing the apple on the
screen. First, we check whether we need to place an apple, then we randomly pick
a location on the screen, which is a multiple of 20, and we repick it if the picked location contains the snake. As we are working in a 0-indexed environment we need to subtract one (1-20 becomes 0-19 and 1-15 becomes 0-14).

Breakout with Flash: I need help to improve my Brick n Ball collision

I've been stuck on this problem for a very long time now, I've searched around alot and tried stuff, but nothing works. Some explanations are just very hard for me to understand as Im pretty new to programming overall and got alot to learn.
I have two problems
1: The ball wont collide with the bricks sometimes when the speed is too fast.
2: The ball is capable of hitting 2 bricks.
Both problems is related to the fact that 60 fps isnt enough for my type of collision detection to work properly.
I just need someone to explain in a simple way as possible what I need to do to make a collision detection that will prevent this from happen.
Here's my current collision code:
private function checkCollision(): void {
grdx = Math.floor((ball.x) / 28);
grdy = Math.floor((ball.y) / 14);
ngrdx = Math.floor((ball.x + dx) / 28);
ngrdy = Math.floor((ball.y + dy) / 14);
var flipX: Boolean = false;
var flipY: Boolean = false;
if ((grdy <= level.length - 1) &&
(ngrdy <= level.length - 1) &&
(grdy >= 0 && ngrdy >= 0)) {
if (testBlock(grdx, ngrdy)) {
flipY = true;
paddleFlag = 1;
}
if (testBlock(ngrdx, grdy)) {
flipX = true;
paddleFlag = 1;
}
if (testBlock(ngrdx, ngrdy)) {
flipX = true;
flipY = true;
paddleFlag = 1;
}
dx *= flipX ? -1 : 1;
dy *= flipY ? -1 : 1;
}
}
private function testBlock(xPos: int, yPos: int): Boolean {
if (level[yPos][xPos] > 0 && level[yPos][xPos] != 13) {
trace("hit on X,Y");
level[yPos][xPos] = 0;
breakBlock("Block_" + yPos + "_" + xPos);
trace("Block: " + totalBreaks + " / " + totalBlocks);
return true;
}
return false;
}
private function breakBlock(blockName: String): void {
if (this.getChildByName(blockName)) {
this.removeChild(this.getChildByName(blockName));
totalBreaks++;
}
}
Thank you and sorry for my bad english, its not my motherlanguage.
One solution is to move the ball in smaller iterations, multiple times in a given frame.
For example, and I am giving this solution assuming that you are moving the ball based on the time elapsed from the last frame.
Suppose that 30 milliseconds have elapsed since the last frame update. In that case you would update the movement/collision twice in that frame using 15 millisecond as your time elapsed.
The higher resolution of collision you want, the more iterations you would do.
Here's an example :
// class declarations
var lastFrame:Number;
var iterationsPerFrame:int;
function startGame():void
{
// lets specify 3 updates per frame
iterationsPerFrame = 3;
// save initial time
lastFrame = getTimer();
// create your listener
addEventListener(Event.ENTER_FRAME, update);
}
function update(e:Event):void
{
var currentFrame:Number = getTimer();
var deltaTime:Number = (currentFrame - lastFrame)/1000;
var iterationDelta:Number = deltaTime/iterationsPerFrame;
for (var index:int = 0;index < iterationsPerFrame;index++)
{
// I'm assuming dx,dy are the velocity of the ball, in pixels per second
ball.x += dx * iterationDelta;
ball.y += dy * iterationDelta;
// check collision
}
// set lastFrame to the currentFrame time, preparing for next frame
lastFrame = currentFrame;
// after this, your frame is going to render
}
You could work out how far the ball travels each frame (A) based on its speed, how far the ball is from the paddle (B) and if A > B manually trigger a collision that frame.
You're essentially checking every bricks X and Y coordinate to the balls X and Y coordinate, so if the bricks are stored in an array this becomes: Sqrt( Sqrd(p2.x - p1.x) + Sqrd(p2.y - p1.y))
for(var i=0; i<brickArray.length; i++)
{
var distance:Number = Math.sqrt((brickArray[i].x - ball.x) * (brickArray[i].x - ball.x) +
(brickArray[i].y - ball.y) * (brickArray[i].y - ball.y));
}
This is a very good tutorial on high speed collison detection:
http://www.newgrounds.com/bbs/topic/1072673

"snake" in html5 canvas. Math curve issue

Demo
I am trying to make a "snake" game in html 5 canvas. But i have a problem getting the snake to move the right direction. I would assume that the following code would make the snake move horizontal on 0 and 180 degrees, and vertical on 90 and 270 degrees, this is however not the case. What am i doing wrong here? (Use the left and right arrows to navigate).
function move(direction) {
if(direction == left) {
angel = (angel - 5) % 360;
if(angel < 0) angel += 360;
} else if (direction == right) {
angel = (angel + 5) % 360;
}
x = x + Math.floor(Math.cos(angel*0.0174532925)*5);
y = y + Math.floor(Math.sin(angel*0.0174532925)*5);
$("#infoBar").html("Direction: " + direction + " angel: " + angel);
drawPoint(x,y);
}
The multiplier is of course degrees to radiant. But somehow 270 degrees is not a straight vertical line, as i would assumed that it was. What am i doing wrong?
Javascript file.
Html file.
Because of floating point math.
cos(270 degrees) = 0
Buuuut:
Math.cos((Math.PI/180)*270) is not 0. It is: -1.836909530733566e-16
In other words it is -0.000000000000000183, etc. Very close to zero, but slightly less than zero!
But you're using Math.floor, and Math.floor that number (or Math.floor(-0.1) for that matter) = -1.
You don't want that.
Use Math.round instead of Math.floor.
Here's a live example of it fixed for you: http://jsfiddle.net/UtPJz/3/
I haven't compiled the code segment but you could do somehting like this:
function move(direction) {
switch(direction)
{
case RIGHT:
stepX = 1;
stepY = 0;
break;
case LEFT:
stepX = -1;
stepY = 0;
break;
case UP:
stepX = 0;
stepY = -1;
break;
case DOWN:
stepX = 0;
stepY = 1;
break;
}
x += stepX;
y += stepY;
drawPoint(x,y);
}