"snake" in html5 canvas. Math curve issue - html

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);
}

Related

Phaser 3 - set min and max zoom levels

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;
}

Issue with rectangle collision testing

I am having trouble with testing if a rectangle has collided with another rectangle and where the collision is in relation to each object (Left, right, Top, Bottom).
My code works well in theory but there are logical issues, there are false positives when an object enters the left side of another object. The parameters I have set mean that both the top collision and left collision become true when in truth only the left should be true.
image of problem
How can I stop a double positive happening in my code, I only need basic rectangle collision and nothing more. Thank you.
//Col on top?
if (Obj1.getRect(this).bottom - vSpeed < Obj2.getRect(this).bottom &&
Obj1.getRect(this).bottom - vSpeed > Obj2.getRect(this).top)
{
if (Obj1.getRect(this).right + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).left + hSpeed < Obj2.getRect(this).right)
{
Obj1.y = Obj2.y - Obj1.height;
vSpeed = 0;
colTop = true;
}
}
//Col on Bottom?
else if (Obj1.getRect(this).top - vSpeed > Obj2.getRect(this).top &&
Obj1.getRect(this).top - vSpeed < Obj2.getRect(this).bottom)
{
if (Obj1.getRect(this).right + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).left + hSpeed < Obj2.getRect(this).right)
{
Obj1.y = Obj2.y + Obj2.height;
vSpeed = 0;
colBot = true;
}
}
//Col on left side?
if (Obj1.getRect(this).right + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).right + hSpeed < Obj2.getRect(this).right)
{
if (Obj1.getRect(this).bottom - vSpeed > Obj2.getRect(this).top &&
Obj1.getRect(this).top - vSpeed < Obj2.getRect(this).bottom)
{
Obj1.x = Obj2.x - (Obj2.width * 0.5);
hSpeed = 0;
colLeft = true;
}
}
//Col on right side?
else if (Obj1.getRect(this).left + hSpeed > Obj2.getRect(this).left &&
Obj1.getRect(this).left + hSpeed < Obj2.getRect(this).right)
{
if (Obj1.getRect(this).bottom - vSpeed > Obj2.getRect(this).top &&
Obj1.getRect(this).top - vSpeed < Obj2.getRect(this).bottom)
{
Obj1.x = (Obj2.x + Obj2.width) + (Obj1.width * 0.5);
hSpeed = 0;
colRight = true;
}
}
You need to separate top collision from left collision by parsing the objects' relative speed. You cannot parse top/left collision using coordinates only because they are really both true if one rectangle's top left point goes inside another rectangle. So, move your Obj1 and velocity altering code out of your array of if statements, and leave the rest of the code as is. Then you check for velocities like this:
if (colTop)
if (Obj1.getRect(this).bottom > Obj2.getRect(this).top)
colTop=false;
Etc., for other collision vars. This code means: "If the bottom of Obj1 already was under the top of Obj2, then in this frame we are not hitting the top side". So, only the left side collision flag should remain. And only then you check for resultant collision vars and adjust coordinates and velocities
The starling library provides great collision detection, including simple rectangle collision. Are your methods being called on every frame?
All you would need for Starling collision detection
is:
var bounds1:Rectangle = image1.bounds;
var bounds2:Rectangle = image2.bounds;
if (bounds1.intersects(bounds2))
trace("Collision!");
collision detection

processing: how to make things move in random curve instead of a line?

I have the classic balls that move in a line:
class Circle{
float x,y,vx,vy,size;
Circle(float ax,float ay){
x = ax;
y = ay;
size = 5;
vx = random(-.1,.1);
vy = random(-.1,.1);
}
void update(int w,int h){
x += vx;
y += vy;
if(x < 0 || x > w) vx *= -1;
if(y < 0 || y > h) vy *= -1;
}
void draw(){
pushStyle();
noStroke();
fill(0);
ellipse(x,y,size,size);
popStyle();
}
}
However, i don't want them moving in a line.
I want them moving in unregular curves. What can i add? noise? sin? cos?
Many thanks.
You may like to have a look in this page. An easy guide in easing equations with working samples. Note that the same technique may be used for curves in time or space.
edit: forgot to say the website is for flash but anyway is simple enough to understand even if you, as me, has no flash knowledge. And code examples are easily adapted.
Depends on what you want but if you want them to move according to a sinusoidal curve, for example, you could do this:
class Circle{
float x,y,vx,vy,size;
float sinCtr = 0;
// ...
void update(int w,int h){
x += vx;
y = h/2 + 50 * Math.sin(sinCtr);
sinCtr += 0.02;
}

Dual fire and positioning problems

I'm working on a space shooter and have this problem:
My space ship fires bullets. The bullets are centered in reference to the ship. Currently all bullets are single shots.
Now what I'd like to have is dual fire for the ship. Basically that means I have to double the shots and place them at the left and right
side of the ship, right? But I don't know how to do it exactly.
The other problem I have is that when I change the x and y-coordinates of the bullet in order to move the bullets from the center of the ship to the left (or right) and rotate the ship and fire I get a strange behavior of the bullets. In this case they move from the left to the center and vice versa referring to the ship (while rotating and firing).
Please - has anyone any idea where the problems might be and how to do it? I appreciate very much any help. TIA!:)
private function autoShoot(step:Number):void {
projectileManager.projectilePoolCount = projectileManager.projectilePool.length - 1;
asteroidManager.asteroidCount = asteroidManager.asteroids.length;
if (projectileManager.lastProjectileShot > projectileOffset && projectileManager.projectilePoolCount > 0 &&
playerStarted && asteroidManager.asteroidCount > 0 && projNumber < projectileNumber)
{
dispatchEvent(new CustomEventSound(CustomEventSound.PLAY_SOUND, Main.SOUND_PLAYER_SHOOT,
false, 0, 8, 1));
tempProjectile = projectileManager.projectilePool.pop();
tempProjectile.projectileDelayCount = 0;
var projectileRadians:Number = (player.frame / 360) * 6.28;
projShots++;
projNumber++;
if (projNumber >= projectileNumber)
{
player.startDelay = true;
}
tempProjectile.x = (player.point.x) + Math.cos(projectileRadians);
tempProjectile.y = (player.point.y) + Math.sin(projectileRadians);
tempProjectile.x = player.x + 13;
tempProjectile.y = player.y + 13;
tempProjectile.nextX = tempProjectile.x;
tempProjectile.nextY = tempProjectile.y;
tempProjectile.dx = rotationVectorList[player.frame].x;
tempProjectile.dy = rotationVectorList[player.frame].y;
tempProjectile.speed = projectileSpeed;
tempProjectile.frame = 0;
tempProjectile.bitmapData = tempProjectile.animationList[0];
tempProjectile.projectileDelay = 10;
projectileManager.projectiles.push(tempProjectile);
projectileManager.lastProjectileShot = 0;
} else {
projectileManager.lastProjectileShot += step;
}
}
The autoShoot function only fires one projectile each time it is called. There is only one tempProjectile per call to push to the projectiles list.
So one fix can be to add a tempProjectile2 (in the same place tempProjectile is defined) as a variable that can be used. Now in each call the projectile pool gets depleted by two, as long an adequate number of projectiles are popped
tempProjectile = projectileManager.projectilePool.pop();
tempProjectile2 = projectileManager.projectilePool.pop();
then adjust the offset accordingly
tempProjectile.x
tempProjectile.y
tempProjectile2.x
tempProjectile2.y
And so on with the rest of the code. The only thing I am unclear about is why tempProjectile.x and y are assigned twice.
tempProjectile.x = (player.point.x) + Math.cos(projectileRadians);
tempProjectile.y = (player.point.y) + Math.sin(projectileRadians);
tempProjectile.x = player.x + 13;
tempProjectile.y = player.y + 13;
only the second pair will be used, I think.
Hopefully the rest of the functions should not change because at the end the projectiles are pushed to the projectiles list
projectileManager.projectiles.push(tempProjectile);
projectileManager.projectiles.push(tempProjectile2);
So the whatever function used to update and render should remain the same, assuming tempProjectile is not tied down elsewhere in the program.

Algorithm to solve the points of a evenly-distributed / even-gaps spiral?

First, just to give a visual idea of what I'm after, here's the closest result (yet not exactly what I'm after) image that I've found:
Here's the entire site-reference: http://www.mathematische-basteleien.de/spiral.htm
BUT, it doesn't exactly solve the problem I'm after. I would like to store an array of points of a very specific spiral algorithm.
The points are evenly distributed
The 360 degree cycles have an even gap
If I'm not mistaken, the first two points would be:
point[ 0 ] = new Point(0,0);
point[ 1 ] = new Point(1,0);
But where to go from here?
The only arguments I'd like to provide are:
the quantity of points I wish to resolve (length of array).
the distance between each points (pixels gap).
the distance between cycles.
It almost sounds, to me, that I have to calculate the "spiral-circumference" (if there's such a term) in order to plot the evenly distributed points along the spiral.
Can 2*PI*radius be reliably used for this calculation you think?
If it's been done before, please show some code example!
Fun little problem :)
If you look at the diagram closer, the sequence is clearly stated:
There are probably many solutions to drawing these, maybe more elegant, but here's mine:
You know the hypotenuse is square root of the current segment count+1
and the opposite side of the triangle is always 1.
Also you know that Sine(Math.sin) of the angle is equal to the opposite side divided by the hypotenuse.
from the old mnenonic SOH(Sine,Opposite,Hypotenuse),-CAH-TOA.
Math.sin(angle) = opp/hyp
You know the value of the sine for the angle, you know the two sides, but you don't know the angle yet, but you can use the arc sine function(Math.asin) for that
angle = Math.asin(opp/hyp)
Now you know the angle for each segment, and notice it increments with each line.
Now that you have an angle and a radius(the hypotenuse) you can use for polar to cartesian formula to convert that angle,radius pair to a x,y pair.
x = Math.cos(angle) * radius;
y = Math.sin(angle) * radius;
Since you asked for an actionscript solution, there Point class already provides this function for you through the polar() method. You pass it a radius and angle and it returns your x and y in a Point object.
Here's a little snippet which plots the spiral. You can control the number of segments by moving the mouse on the Y axis.
var sw:Number = stage.stageWidth,sh:Number = stage.stageHeight;
this.addEventListener(Event.ENTER_FRAME,update);
function update(event:Event):void{
drawTheodorus(144*(mouseY/sh),sw*.5,sh*.5,20);
}
//draw points
function drawTheodorus(segments:int,x:Number,y:Number,scale:Number):void{
graphics.clear();
var points:Array = getTheodorus(segments,scale);
for(var i:int = 0 ; i < segments; i++){
points[i].offset(x,y);
graphics.lineStyle(1,0x990000,1.05-(.05+i/segments));
graphics.moveTo(x,y);//move to centre
graphics.lineTo(points[i].x,points[i].y);//draw hypotenuse
graphics.lineStyle(1+(i*(i/segments)*.05),0,(.05+i/segments));
if(i > 0) graphics.lineTo(points[i-1].x,points[i-1].y);//draw opposite
}
}
//calculate points
function getTheodorus(segments:int = 1,scale:Number = 10):Array{
var result = [];
var radius:Number = 0;
var angle:Number = 0;
for(var i:int = 0 ; i < segments ; i++){
radius = Math.sqrt(i+1);
angle += Math.asin(1/radius);//sin(angle) = opposite/hypothenuse => used asin to get angle
result[i] = Point.polar(radius*scale,angle);//same as new Point(Math.cos(angle)*radius.scale,Math.sin(angle)*radius.scale)
}
return result;
}
This could've been written in less lines, but I wanted to split this into two functions:
one that deals only with computing the numbers, and the other which deals with drawing the lines.
Here are some screenshots:
For fun I added a version of this using ProcessingJS here.
Runs a bit slow, so I would recommend Chromium/Chrome for this.
Now you can actually run this code right here (move the mouse up and down):
var totalSegments = 850,hw = 320,hh = 240,segments;
var len = 10;
points = [];
function setup(){
createCanvas(640,480);
smooth();
colorMode(HSB,255,100,100);
stroke(0);
noFill();
//println("move cursor vertically");
}
function draw(){
background(0);
translate(hw,hh);
segments = floor(totalSegments*(mouseY/height));
points = getTheodorus(segments,len);
for(var i = 0 ; i < segments ; i++){
strokeWeight(1);
stroke(255-((i/segments) * 255),100,100,260-((i/segments) * 255));
line(0,0,points[i].x,points[i].y);
// strokeWeight(1+(i*(i/segments)*.01));
strokeWeight(2);
stroke(0,0,100,(20+i/segments));
if(i > 0) line(points[i].x,points[i].y,points[i-1].x,points[i-1].y);
}
}
function getTheodorus(segments,len){
var result = [];
var radius = 0;
var angle = 0;
for(var i = 0 ; i < segments ; i++){
radius = sqrt(i+1);
angle += asin(1/radius);
result[i] = new p5.Vector(cos(angle) * radius*len,sin(angle) * radius*len);
}
return result;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>
George's answer was excellent! I was looking for the solution for quite a while.
Here's the same code adjusted for PHP, in case it helps someone. I use the script to draw dots (= cities) for a map with X, Y coordinates. X starts from left, Y starts from bottom left.
<?
/**
* Initialize variables
**/
// MAXIMUM width & height of canvas (X: 0->400, Y: 0->400)
$width = 400;
// For loop iteration amount, adjust this manually
$segments = 10000;
// Scale for radius
$radiusScale = 2;
// Draw dot (e.g. a city in a game) for every N'th drawn point
$cityForEveryNthDot = 14;
/**
* Private variables
**/
$radius = 0;
$angle = 0;
$centerPoint = $width/2;
/**
* Container print
**/
print("<div style=\"width: ${width}px; height: ${width}px; background: #cdcdcd; z-index: 1; position: absolute; left: 0; top: 0;\"></div>");
/**
* Looper
**/
for($i=0;$i<$segments;$i++) {
// calculate radius and angle
$radius = sqrt($i+1) * $radiusScale;
$angle += asin(1/$radius);
// skip this point, if city won't be created here
if($i % $cityForEveryNthDot != 0) {
continue;
}
// calculate X & Y (from top left) for this point
$x = cos($angle) * $radius;
$y = sin($angle) * $radius;
// print dot
print("<div style=\"width: 1px; height: 1px; background: black; position: absolute; z-index: 2; left: " . round($x+$centerPoint) . "; top: " . round($y+$centerPoint) . ";\"></div>");
// calculate rounded X & Y (from bottom left)
$xNew = round($x+$centerPoint);
$yNew = round($width - ($y+$centerPoint));
// just some internal checks
if($xNew > 1 && $yNew > 1 && $xNew < $width && $yNew < $width) {
/**
* do something (e.g. store to database). Use xNew and yNew
**/
}
}