trying to make a drawn line move like a laser in javascript - javascript

I made this red line in JavaScript that goes to closest target (balloon 1 to 3) to the player but I need to make it so that it moves like a laser starting from player position into the target position. I thought about multiple ways of implementing this with no luck.
function Tick() {
// Erase the sprite from its current location.
eraseSprite();
for (var i = 0; i < lasers.length; i++) {
lasers[i].x += lasers[i].direction.x * laserSpeed;
lasers[i].y += lasers[i].direction.y * laserSpeed;
//Hit detection here
}
function detectCharClosest() {
var ballon1char = {
x: balloon1X,
y: balloon1Y
};
var ballon2char = {
x: balloon2X,
y: balloon2Y
};
var ballon3char = {
x: balloon3X,
y: balloon3Y,
};
ballon1char.distanceFromPlayer = Math.sqrt((CharX - balloon1X) ** 2 + (CharY - balloon1Y) ** 2);
ballon2char.distanceFromPlayer = Math.sqrt((CharX - balloon2X) ** 2 + (CharY - balloon2Y) ** 2);
ballon3char.distanceFromPlayer = Math.sqrt((CharX - balloon3X) ** 2 + (CharY - balloon3Y) ** 2);
var minDistance = Math.min(
ballon1char.distanceFromPlayer,
ballon2char.distanceFromPlayer,
ballon3char.distanceFromPlayer);
console.log(ballon1char);
console.log(ballon2char);
console.log(ballon3char);
for (let i = 0; i < 3; i++) {
if (minDistance == ballon1char.distanceFromPlayer)
return ballon1char
if (minDistance == ballon2char.distanceFromPlayer)
return ballon2char
if (minDistance == ballon3char.distanceFromPlayer)
return ballon3char
}
}
function loadComplete() {
console.log("Load is complete.");
canvas = document.getElementById("theCanvas");
ctx = canvas.getContext("2d");
myInterval = self.setInterval(function () { Tick() }, INTERVAL);
myInterval = self.setInterval(function () { laserTicker(detectCharClosest()) }, 2000);
function laserTicker(balloon) {
//gets the closest ballon to go to
laserDo(balloon);
}
function laserDo(balloon) {
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = "#F44336"; // "red";
ctx.moveTo(CharX + 16, CharY + 16);
ctx.lineTo(balloon.x, balloon.y);
// lasers.push({x: })
ctx.stroke();
}
I didn't put all of my code here so If something doesn't make sense please tell me. I'm still new to JavaScript and learning it. One way I thought I could make this work was by taking the distance between the player and the target and dividing it by the speed on the x and y axis then changing having it start from the player position and keeps on adding up on both axis until it reaches the target. That didn't work out though. If you have any suggestions then please tell me.
Thanks

Related

paper.js animate point on graph

I'm trying to build an animated graph with paper.js that can react to different input. So I want to smoothly animate one point vertically to a different point.
I've looked at different examples and the closest ones to mine is this one:
paper.tool.onMouseDown = function(event) {
x = event.event.offsetX;
y = event.event.offsetY;
paper.view.attach('frame', moveSeg);
}
var x;
var y;
function moveSeg(event) {
event.count = 1;
if(event.count <= 100) {
myPath.firstSegment.point._x += (x / 100);
myPath.firstSegment.point._y += (y / 100);
for (var i = 0; i < points - 1; i++) {
var segment = myPath.segments[i];
var nextSegment = segment.next;
var vector = new paper.Point(segment.point.x - nextSegment.point.x,segment.point.y - nextSegment.point.y);
vector.length = length;
nextSegment.point = new paper.Point(segment.point.x - vector.x,segment.point.y - vector.y);
}
myPath.smooth();
}
}
This Code animates one Point to the click position, but I couldn't change it to my needs.
What I need is:
var aim = [120, 100];
var target = aim;
// how many frames does it take to reach a target
var steps = 200;
// Segment I want to move
myPath.segments[3].point.x
And then I dont know how to write the loop that will produce a smooth animation.
example of the graph:
I worked out the answer. The following steps in paperscript:
Generate Path
Set aim for the point
OnFrame Event that does the moving (eased)
for further animations just change the currentAim variable.
var myPath = new Path({
segments: [[0,100],[50,100],[100,100]]});
// styling
myPath.strokeColor = '#c4c4c4'; // red
myPath.strokeWidth = 8;
myPath.strokeJoin = 'round';
myPath.smooth();
// where the middle dot should go
var currentAim = [100,100];
// Speed
var steps = 10;
//Animation
function onFrame(event) {
dX1 = (currentAim[0] - myPath.segments[1].point.x )/steps;
dY1 = (currentAim[1] - myPath.segments[1].point.y )/steps;
myPath.segments[1].point.x += dX1;
myPath.segments[1].point.y += dY1;
}

how to optimize snap svg animations

I have a snap svg animation which animates a bunch of circles, and draws a line between them if they are within a certain proximity of each other. However, I realize that there is a lot of optimizing I can do, but I'm not exactly sure how to do it. I feel like it would be useful to
have a good example of proximity detection in snap
have some more information on optimizing animations in snap svg. It hasn't been easy to find.
Here is a working example of the animation:
http://jsfiddle.net/heaversm/sbj4W/1/
and here are the things I believe can be optimized:
Each circle calls its own animation function - the circles have all been added to a group, and I'm guessing there is a way to apply random motion to all members of a group that is more performant than call a function for each and every element within the group.
for (var i=0; i<this.drawingConfig.circles.amount;i++){
...
this.animateSingle(circleShape);
}
The proximity function is awkward - for each circle, for each update cycle, I have to loop through an array of all the other circles and find out if the X and Y coordinates are close enough to draw a line to. Plus, that means you're getting duplicate lines, because each circle will draw a line to its neighbors, instead of having a single shared line between the two.
for (var i=0;i<circles.length;i++){
var nextCircle = circles[i].node;
var nextCircleX = nextCircle.cx.baseVal.value;
var distance = Math.abs(nextCircleX-thisCircleX);
var proximity = mainModule.drawingConfig.circles.proximity;
if (distance < proximity){
var nextCircleY = nextCircle.cy.baseVal.value;
var thisCircleY = shape.node.cy.baseVal.value;
var distanceY = Math.abs(nextCircleY - thisCircleY);
if (distanceY < proximity){
var line = mainModule.s.line(thisCircleX, thisCircleY, nextCircleX, nextCircleY).attr({stroke: '#a6a8ab', strokeWidth: '1px'});
mainModule.drawingConfig.circles.circleGroup.add(line);
}
}
}
Correspondingly, I each circle's animation function clears all the lines on the screen. Ideally all the circles would be sharing one update function, and in that function, you'd clear the lines.
Snap.animate(startX, animX, function (val) {
var lines = Snap.selectAll('line');
lines.remove();
...
}, mainModule.drawingConfig.circles.animTime);
Right now, I can tell the renderer can't keep up with all of the various animations / loops. Any help optimizing the above things (or anything else you can see that I'm doing weird, would be greatly appreciated.
I cleaned this up by running only one animation loop, on a timer every 10ms, and animated the position of the circles by just giving them a slope and, each update, continuing them further along that slope. You can see an updated fiddle here:
http://jsfiddle.net/heaversm/fJ6fj/
var mainModule = {
s: Snap("#svg"),
drawingConfig: {
circles: {
amount: 20,
sizeMin: 10,
sizeMax: 20,
proximity: 100,
circleGroup: null,
circleArray: [],
animTime: 2000
},
canvas: {
width: 800,
height: 600
}
},
init: function(){
//this.sizeCanvas();
this.makeCircles();
},
sizeCanvas: function(){
$('#svg').width(800).height(600);
},
makeCircles: function(){
this.drawingConfig.circles.circleGroup = this.s.g();
for (var i=0; i<this.drawingConfig.circles.amount;i++){
var circleX = this.randomNumber(0, this.drawingConfig.canvas.width);
var circleY = this.randomNumber(0, this.drawingConfig.canvas.height);
var circleRadius = this.randomNumber(this.drawingConfig.circles.sizeMin,this.drawingConfig.circles.sizeMax);
var circleFill = '#'+Math.floor(Math.random()*16777215).toString(16);
var circleShape = this.s.circle(circleX, circleY, circleRadius);
circleShape.attr({
fill: circleFill
});
this.drawingConfig.circles.circleGroup.add(circleShape);
var circleIncline = this.setIncline();
var circleObj = { incline: circleIncline, shape: circleShape };
this.drawingConfig.circles.circleArray.push(circleObj);
}
this.update();
},
setIncline: function(){
return { incX: this.randomNumber(-5,5), incY: this.randomNumber(-5,5) }
},
update: function(){
var lines = Snap.selectAll('line');
lines.remove();
for (var i=0; i<this.drawingConfig.circles.amount; i++){
var circle = this.drawingConfig.circles.circleArray[i];
var circleX = circle.shape.node.cx.animVal.value;
var circleY = circle.shape.node.cy.animVal.value;
this.move(circle,circleX,circleY);
for (var j=0;j<i;j++){
if (i != j){
var circle2 = this.drawingConfig.circles.circleArray[j];
var circle2X = circle2.shape.node.cx.animVal.value;
var circle2Y = circle2.shape.node.cy.animVal.value;
var dist = mainModule.distance(circleX,circleY,circle2X,circle2Y);
if (dist <= mainModule.drawingConfig.circles.proximity){ //
var lineWeight = 10/dist;
var line = mainModule.s.line(circleX, circleY, circle2X, circle2Y).attr({stroke: '#a6a8ab', strokeWidth: '1px'});
}
if (dist <= 10) { //collision
circle.incline = mainModule.setIncline();
circle2.incline = mainModule.setIncline();
}
}
}
}
setTimeout(function(){ mainModule.update(); },10);
},
distance: function(circleX,circleY,circle2X,circle2Y){
var distX = circle2X - circleX;
var distY = circle2Y - circleY;
distX = distX*distX;
distY = distY*distY;
return Math.sqrt(distX + distY);
},
move: function(circle,curX,curY){
if (curX > this.drawingConfig.canvas.width || curX < 0) {
circle.incline.incX = -circle.incline.incX;
}
if (curY > this.drawingConfig.canvas.height || curY < 0) {
circle.incline.incY = -circle.incline.incY;
}
curX = curX + circle.incline.incX;
curY = curY + circle.incline.incY;
if (curX > this.drawingConfig.canvas.width) {
curX = this.drawingConfig.canvas.width;
circle.incline = this.setIncline();
} else if (curX < 0) {
curX = 0;
circle.incline = this.setIncline();
}
if (curY > this.drawingConfig.canvas.height) {
curY = this.drawingConfig.canvas.height;
circle.incline = this.setIncline();
} else if (curY < 0) {
curY = 0;
circle.incline = this.setIncline();
}
circle.shape.attr({ cx: curX, cy: curY });
},
randomNumber: function(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
},
getBounds: function(shape){
shapeBox = shape.node.getBoundingClientRect();
}
}
mainModule.init();

How to detect if an object is at cordinates on canvas element?

I'm working on creating a tic-tac-toe game in canvas. I'm currently stuck at a point where I detect if there is a symbol(X or O) already at x/y cordinates on the canvas.
I tried using ImageData to check if an element is present but it returns an error if nothing is there. I also thought perhaps I could assign an ID to the square or the symbol. However that doesn't seem to be possible from what I've read.
Any help would be appreciated.
You can see the game running here http://jsfiddle.net/weeklygame/dvJ5X/30/
function TTT() {
this.canvas = document.getElementById('ttt');
this.context = this.canvas.getContext('2d');
this.width = this.width;
this.height = this.height;
this.square = 100;
this.boxes = [];
this.turn = Math.floor(Math.random() * 2) + 1;
this.message = $('.message');
};
var ttt = new TTT();
TTT.prototype.currentPlayer = function() {
var symbol = (this.turn === 1) ? 'X' : 'O';
ttt.message.html('It is ' + symbol + '\'s turn');
};
// Draw the board
TTT.prototype.draw = function(callback) {
// Draw Grid
for(var row = 0; row <= 200; row += 100) {
var group = [];
for(var column = 0; column <= 200; column += 100) {
group.push(column);
this.context.strokeStyle = 'white';
this.context.strokeRect(column,row,this.square,this.square);
};
this.boxes.push(group);
};
callback;
};
// Get center of the click area cordinates
TTT.prototype.cordinates = function(e) {
var row = Math.floor(e.clientX / 100) * 100,
column = Math.floor(e.clientY / 100) * 100;
return [row, column];
};
// Check if the clicked box has symbol
TTT.prototype.check = function(row, column) {
};
// Get cordinates and set image in container
TTT.prototype.click = function(e) {
var cordinates = ttt.cordinates(e),
x = cordinates[0] + 100 / 2,
y = cordinates[1] + 100 / 2,
image = new Image();
if (ttt.turn === 1) {
image.src = 'http://s8.postimg.org/tdp7xn6lt/naught.png';
ttt.turn = 2;
} else {
image.src = 'http://s8.postimg.org/9kd44xt81/cross.png';
ttt.turn = 1;
};
ttt.context.drawImage(image, x - (image.width / 2), y - (image.height / 2));
ttt.currentPlayer();
};
function render() {
ttt.draw($('#ttt').on("click", ttt.click));
ttt.currentPlayer();
};
(function init() {
render();
})();
Would it not be easier for you to keep track of the grid positions using an array. When you place something on the grid allocate that position in the array. That way rather than having to work out a way to read it from the Canvas you just look in the array. This also allows you to quickly redraw the canvas from the array when needed, such as when the screen resizes...
To detect which field was clicked, iterate through your 9 fields and check if the clicked position is in the area where the field is drawn.
To be able to do this, store the state of your fields (position and if it has a X, O or nothing in it). You should also store the 9 fields in an array, so you can easily iterate over them. I would store it in a two dimensional array (3x3).
function Field(x, y) {
this.x = x;
this.y = y;
this.value = null; // Can be null, 'X' or 'O'
}
Initialisation of the tic tac toe field:
var fields = [
[new Field(0,0), new Field(1,0), new Field(2,0)],
[new Field(0,1), new Field(1,1), new Field(2,1)],
[new Field(0,2), new Field(1,2), new Field(2,2)]
];
Iteration:
for (var y = 0; y <= 2; y++) {
for (var x = 0; x <= 2; x++) {
var field = fields[y][x];
// Do something with the field.
}
}
I would store the position of the fields with model coordinates. So you multiply the coordinates with a value to get the coordinates for drawing on the canvas.

Raphael transform object diagonally and infinite setIntervals

I'm working on a small animation where the user drags a circle and the circle returns back to the starting point. I figured out a way to have the circle return to the starting point. The only problem is that it will hit one of the sides of the frame before returning. Is it possible for it to go straight back (follow the path of a line drawn between the shape and starting point).
The other problem is that my setInterval doesn't want to stop. If you try pulling it a second time it would pull it back before you release your mouse. It also seems to speed up after every time. I have tried using a while loop with a timer but the results weren't as good. Is this fixable?
var paper = Raphael(0, 0, 320, 200);
//var path = paper.path("M10 10L40 40").attr({stoke:'#000000'});
//var pathArray = path.attr("path");
var circle = paper.circle(50, 50, 20);
var newX;
var newY;
circle.attr("fill", "#f00");
circle.attr("stroke", "#fff");
var start = function () {
this.attr({cx: 50, cy: 50});
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
},
move = function (dx, dy) {
var X = this.cx + dx,
Y = this.cy + dy;
this.attr({cx: X, cy: Y});
},
up = function () {
setInterval(function () {
if(circle.attr('cx') > 50){
circle.attr({cx : (circle.attr('cx') - 1)});
} else if (circle.attr('cx') < 50){
circle.attr({cx : (circle.attr('cx') + 1)});
}
if(circle.attr('cy') > 50){
circle.attr({cy : (circle.attr('cy') - 1)});
} else if (circle.attr('cy') < 50){
circle.attr({cy : (circle.attr('cy') + 1)});
}
path.attr({path: pathArray});
},2);
};
circle.drag(move, start, up);
Here's the Jfiddle: http://jsfiddle.net/Uznp2/
Thanks alot :D
I modified the "up" function to the one below
up = function () {
//starting x, y of circle to go back to
var interval = 1000;
var startingPointX = 50;
var startingPointY = 50;
var centerX = this.getBBox().x + (this.attr("r")/2);
var centerY = this.getBBox().y + (this.attr("r")/2);
var transX = (centerX - startingPointX) * -1;
var transY = (centerY - startingPointY) * -1;
this.animate({transform: "...T"+transX+", "+transY}, interval);
};
and the "start" function as follows:
var start = function () {
this.cx = this.attr("cx"),
this.cy = this.attr("cy");
}
Is this the behavior you are looking for? Sorry if I misunderstood the question.
If the circle need to get back to its initial position post drag, we can achieve that via simple animation using transform attribute.
// Assuming that (50,50) is the location the circle prior to drag-move (as seen in the code provided)
// The animation is set to execute in 1000 milliseconds, using the easing function of 'easeIn'.
up = function () {
circle.animate({transform: 'T50,50'}, 1000, 'easeIn');
};
Hope this helps.

Collisions in simple javascript game

I'm writing a simple game in javascript and I'm wondering what the best way to handle collisions between the player and the world objects.
<script>
var isJumping = false;
var isFalling = false;
var w = 1;
var recwidth = 400;
var recheight = 400;
var xpos = 50;
var ypos = 279;
window.onload = function() {
var FPS = 30;
var ground = new myObject();
setInterval(function() {
clear();
draw();
ground.draw(0, 325);
ground.draw(125,325)
}, 1000/FPS);
};
function myObject(){
this.draw = function drawground(groundx, groundy){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
//context.fillRect(xpos,ypos,100,100);
var img=new Image()
img.src="ground.png"
img.onload = function() {
context.drawImage(img,groundx,groundy)}
}
};
function jump()
{
var t=.1;
isJumping=true;
var jumpint= setInterval(function() {
yup = 12*t-(5*t*t);
ypos= ypos - yup;
t = t + .1
if(yup < 0)
{
isJumping = false;
isFalling = true;
clearInterval(jumpint);
jumpint = 0;
fall();
return;
}
}, 20);
}
function fall()
{
t=.10
var fallint= setInterval(function() {
ydown = (5*t*t);
ypos= ypos + ydown;
t = t + .1
if(ypos > 275)
{
isFalling == false;
clearInterval(fallint);
fallint = 0;
return;
}
}, 20);
}
function changex(x){
xpos = xpos + (x);
//clear();
//draw();
}
function changey(y){
ypos = ypos + (y);
//clear();
//draw();
}
function draw(){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
var img=new Image()
img.src="character.png"
img.onload = function() {
context.drawImage(img,xpos,ypos)}
}
function clear(){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
context.clearRect(0,0, canvas.width, canvas.height);
}
document.onkeydown = function(event) {
var keyCode;
if(event == null)
{
keyCode = window.event.keyCode;
}
else
{
keyCode = event.keyCode;
}
switch(keyCode)
{
// left
case 37:
//left
changex(-5);
break;
// up
case 38:
// action when pressing up key
jump();
break;
// right
case 39:
// action when pressing right key
changex(5);
break;
// down
case 40:
// action when pressing down key
changey(5);
break;
default:
break;
}
}
</script>
So, as you can see I'm creating two objects so far, and the player stops falling at any arbitrary point. I feel collisions at this stage wont be too difficult, but once I start adding more I feel it's going to get more difficult. I'm not going to be using the instance of the object with the same image for each instance of the object, so at some point I'm going to change the myobject function to be able to accept the image as a parameter, and then checking for collisions will be a bit more tricky. I also plan on making this into a side scroller, so once one end the map is hit it changes into the next area, which is going to cause performance issues. If I'm checking for collisions on every single object in the entire game every interval I imagine things are going to get slow. What is going to be the best way to limit the number of collisions checked? Obviously, if the object isn't on screen there is no need to check it, but is there a way to limit that. I'm thinking of making an array for every frame of the game, and filling that array with it's objects. Then, only check the array the of the frame the player is currently in. Is this feasible or still going to cause too many issues? Any help is greatly appreciated.
If you want pixel perfect collisions, I have some plain javascript code that worked for me with canvas2d rendering context.
function collide(sprite, sprite2, minOpacity=1) {
// Rectangular bounding box collision
if (sprite.x < sprite2.x + sprite2.width && sprite.x + sprite.width > sprite2.x && sprite.y < sprite2.y + sprite2.height && sprite.y + sprite.height > sprite2.y) {
// Finds the x and width of the overlapping area
var overlapX = (this.rect.x > other.rect.x) ? [this.rect.x, (other.rect.x + other.rect.width) - this.rect.x + 1] : [other.rect.x, (this.rect.x + this.rect.width) - other.rect.x + 1];
// Finds the y and height of the overlapping area
var overlapY = (this.rect.y + this.rect.height > other.rect.y + other.rect.height) ? [this.rect.y, (other.rect.y + other.rect.height) - this.rect.y + 1] : [other.rect.y, (this.rect.y + this.rect.height) - other.rect.y + 1];
// Creates a canvas to draw sprite.image to
var spriteImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
var spriteImageCanvasContext = spriteImageCanvas.getContext("2d");
// Draws sprite.image to spriteImageCanvasContext
spriteImageCanvasContext.drawImage(this.image, sprite.x, sprite.y, sprite.width, sprite.height);
// Creates a canvas to draw sprite2.image to
var sprite2ImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
var sprite2ImageCanvasContext = otherImageCanvas.getContext("2d");
// Draws sprite2.image to sprite2ImageCanvasContext
sprite2ImageCanvasContext.drawImage(sprite2.image, sprite2.x, sprite2.y, sprite2.width, sprite2.height);
// Loops through the x coordinates in the overlapping area
for (var x = overlapX[0]; x <= overlapX[0] + overlapX[1]; x++) {
// Loops through the y coordinates in the overlapping area
for (var y = overlapY[0]; y <= overlapY[0] + overlapY[1]; y++) {
if (/* Checks if the pixel at [x, y] in the sprite image has an opacity over minOpacity input */ thisImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity && /* Checks if the pixel at [x, y] in the sprite2 image has an opacity over minOpacity input */ otherImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity) {
return true;
};
};
};
};
}
Or if you just want rectangular collision, use the first if statement in the function.

Categories

Resources