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
I created a sprite which moves in a circular motion. I want to change the direction if the mouse button (touch) is clicked, but when the mouse is clicked, the direction does not change.
This is my code:
create: function() {
this.stage.backgroundColor = '#FFFFFF';
x = this.world.centerX;
y = this.world.centerY;
this.direction = 1;
this.speedDelta = 0.002;
this.radius = 114;
this.physics.startSystem(Phaser.Physics.ARCADE);
//adding player
this.player = this.add.sprite(x, y, 'player');
this.player.anchor.setTo(0.5, 0.5);
this.game.physics.arcade.enable(this.player);
this.input.onDown.add(this.changeDirection, this);
},
update: function() {
if (this.direction == 1) {
this.speedDelta = 0.002;
} else if (this.direction == 1) {
this.speedDelta = -0.002;
}
var period = this.time.now * this.speedDelta;
this.player.x = Math.cos(period) * this.radius;
this.player.y = d + Math.sin(period) * this.radius;
},
changeDirection: function() {
this.direction = -this.direction;
}
}
Your basic assumptions about the behavior of cos and sin are incorrect. You can't simply change the sign of the input and get a different answer.
Notice:
cos(pi/4) = 0.707
cos(-pi/4) = 0.707
sin(pi/4) = -0.707
sin(-pi/4) = -0.707
Also I think your code would benefit by using a slightly different approach in general.
Currently you're recalculating the position from scratch on every update cycle. To get the behavior you want, I think it would be simpler to instead calculate a location delta based off of the speed and direction, then simply add the delta to the current location.
That would also allow you to eliminate your conditional statement, which will make the code cleaner.
I'm trying my hands at some simple game programming in Javascript and have come to realize I need to change the way I handle sprites. The only question is, "how"?
I have a hero that moves around with the arrow keys and fires laser rays with WASD. This is how I define rays:
function Ray (x, y, width, height, direction, index) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.direction = direction;
this.index = index;
this.speed = 512;
this.disabled = false;
}
The index just indicates where in an array of rays (heh) it is being stored. I currently have a hard-coded limit of 5 simultaneous rays, although the other restrictions (screen size, ray size, speed, hero size etc) shouldn't allow for more than 4:
var rays = [];
var numberOfRays = 0;
var rayLimit = 5;
var shotClock = 300;
And so, in the update() function that gets called by the game loop, I have listeners for the WASD keys. They look like this:
// D
if (68 in keysDown && numberOfRays <= rayLimit && Date.now() - lastShootTime > shotClock) {
lastShootTime = Date.now();
var newRayIndex = findFreeRay();
rays[newRayIndex] = new Ray(hero.x + hero.width + 12, hero.y + hero.height / 2, rayImage.width, rayImage.height, 'right', newRayIndex);
numberOfRays++;
}
(findFreeRay() just returns the lowest unused or disabled (off the screen) index in rays[])
Earlier in the update() method (I have also tried putting it later) I have the logic for updating ray movement:
rays.forEach(function(ray) {
if (ray != null && !ray.disabled) {
switch(ray.direction) {
case 'right':
ray.x += ray.speed * modifier;
break;
case 'left':
ray.x -= ray.speed * modifier;
break;
case 'up':
ray.y -= ray.speed * modifier;
break;
case 'down':
ray.y += ray.speed * modifier;
break;
}
}
});
Finally, there is the image for the ray (actually, one for horizontal rays and another one for vertical rays). Currently, I am using one Image object of each globally, that the existing rays share. But I have also tried, without much luck, to create individual image objects for every ray.
// Ray images
var rayReady = false;
var rayImage = new Image();
rayImage.onload = function() {
rayReady = true;
};
rayImage.src = "images/ray.png";
var rayVertReady = false;
var rayVertImage = new Image();
rayVertImage.onload = function() {
rayVertReady = true;
};
rayVertImage.src = "images/ray_vert.png";
And here is how they get drawn:
if (rayReady && rayVertReady && numberOfRays > 0) {
rays.forEach(function(ray) {
if (ray.x > canvas.width
|| ray.x + ray.width < 0
|| ray.y > canvas.height
|| ray.y + ray.height < 0) {
numberOfRays--;
ray.disabled = true;
}
else if (ray.direction == 'right' || ray.direction == 'left'){
ctx.drawImage(rayImage, ray.x, ray.y);
}
else {
ctx.drawImage(rayVertImage, ray.x, ray.y);
}
});
}
The problem
After shooting only a few rays, new ones start to either flicker and disappear, or stay invisible altogether. They actually exist as gameplay objects though, as they can hit targets.
What likely causes this flickering?
(credit to Matt Hackett for the base of this code)
fiddle: http://jsfiddle.net/Vr3MW/
How do I run a sprite animation when pressing the left or right arrow keys in JavaScript? Here's my code:
var avatarX = 0;
var avatarY = 240;
var avatarImage;
var counter = 1;
var XWIDTH = 0;
var WIDTH = 400;
var dx = 5;
var tt;
var gameCanvas;
var context;
var moving;
var animationCounter = 1;
window.addEventListener('keydown', KeyDown);
function setUpGame() { //This is the function that is called from the html document.
gameCanvas = document.getElementById("gameCanvas"); //Declare a new variable & assigns it the id of the CANVAS from the html document.
context=gameCanvas.getContext("2d");
context.font = "18px Iceland";
context.textBaseline = "top";
avatarImage = new Image(); //Declaring a new variable. This is so that we can store the image at a later date.
avatarImage.onload=function(){
// avatarImage is now fully loaded and ready to drawImage
context.drawImage(avatarImage, Math.random() * 100, avatarY);
// start the timer
tt = setInterval(function(){counTer()},1000);
setInterval(handleTick, 25);
}
avatarImage.addEventListener('load', startLoop, false);
avatarImage.src = "img/ships.png"; //Ditto from above.
}
function startLoop() {
console.log("Detecting whether moving to the right is: " + moving);
if(moving == 0) {
gameLoop();
}
}
function gameLoop() {
setTimeout(gameLoop, 100);
handleTick();
}
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 0;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 1;
}
break;
}
}
function counTer() {
if(counter == 60) {
clearInterval(tt);
} else {
counter++;
}
}
function handleTick() {
context.clearRect(0,0,gameCanvas.width,gameCanvas.height);
context.drawImage(avatarImage, 32*animationCounter, 0, 32,32, avatarX, avatarY, 64, 64);
context.fillText("Seconds: " + counter, 5, 5);
context.fillText("1 is Right, 2 is Left, 0 is idle: " + moving, 20, 20);
animationCounter++
if(animationCounter >1) {
animationCounter = 0;
}
}
There are many ways to implement animation. The one i use is pretty easy and looks something like this:
var player = {
x: 0,
y: 0,
width: 50,
height: 100,
sprite: {
column: 0,
row: 0,
height: 50,
width: 100
},
image: PlayerImage,
animation: {
active: true, //Determines if the animation is running or not.
counter: 0,
progress: 0,
sequence: []
}
}
//This functions fires when the left arrow is pressed.
var onLeft = function(){
player.animation.sequence = [{column: 0, row: 0, t: 12}, {column: 1, row: 0, t: 12}, {column: 2, row: 0, t: 12}];
player.animation.active = true;
}
//This functions fires when the right arrow is pressed.
var onRight = function(){
player.animation.sequence = [{column: 0, row: 1, t: 12}, {column: 1, row: 1, t: 12}, {column: 2, row: 1, t: 12}];
player.animation.active = true;
}
//This function fires when no arrow are pressed.
var standingStill = function(){
player.animation.active = false;
}
var handleTick = function(){
//Clear the canvas.
context.canvas.width = context.canvas.width;
//If the animation is active.
if(player.animation.active){
//If the counter >= tick in the sequence. If not, just keep on increasing the counter value.
if(player.animation.counter >= player.animation.sequence[player.animation.progress]){
player.animation.counter = 1;
if(player.animation.progress >= player.animation.sequence.length - 1){
player.animation.progress = 0;
}else{
player.animation.progress++;
}
var currentFrame = player.animation.sequence[player.animation.progress];
//Change player.sprite column and row.(new frame)
player.sprite.column = currentFrame.column;
player.sprite.row = currentFrame.row;
}else{
player.animation.counter++;
}
}
context.drawImage(player.image, player.sprite.column * player.sprite.width, player.sprite.row * player.sprite.height, player.sprite.width, player.sprite.height, player.x, player.y, player.width, player.height)
}
The sequence is an array that contains objects that look like that: {column: 0, row: 0, t: 12} Every object is a frame. The column value is the current x of the sprite, the row is the y of the sprite. The script automatically creates the x and the y value, so all you need to add is value like 0, 1, 3, 5, and so on.(Just which frame it is on either x or y axis.) So for example, if the column is 0 and the row is 0, this is the frame that is in the top-left corner(the first frame). The t value stands for tick, this determines how many ticks there has to happend before the animation goes to the next frame.
Player.sprite also has properties width and height, that's the width and the height of the frame, it is often the same value as the width and height of the player object.
You have to make your own player.animation.sequence in onLeft and onRight function so it animates how you want it to animate.
I hope you understood what i meant. This might seem complicated, but it really isn't and if you don't get it now, don't worry, you'll get it eventually. If you really have difficulties with understanding, just ask.
Edit: First of all i highly recommend using objects to store information about entities. It makes game making much easier and cleaner. Just look at the player object, it's much easier to get the x simply by writing player.x than PlayerX. It looks more professional too. :) And when you have to give a lot of information about your entity you don't have to pass many arguments, you pass the whole object. Example:
//Entity properties stored in separate variable.
function animate(EntityX, EntityY, EntitySpriteX, EntitySpriteY, ...){
var x = EntityX;
//And so on...
}
//Entity stored in an object.
function animate(Entity){
var x = Entity.x;
//And so on...
}
Anyway, back to your question. First, we have to add variables that store information about our sprite.
var avatarSpriteColumn = 0; //Sprite frame on the x axis.
var avatarSpriteRow = 0; //Sprite frame on the y axis.
var avatarSpriteWidth = 50; //The width of a frame.
var avatarSpriteHeight = 100; //The height of a frame.
We also have to add variables that store the information about the animation.
var animationActive = false; //A variable that controls if the animation is 'running'.
var animationCounter = 0; //How many frames(ticks) have passed since the last frame(animation frame) has changed. (I'm not good at describing variables. :P)
var animationProgress = 0; //Current animation frame.
var animationSequence = []; //Array that stores the sequence of animation, as i explained.
Then, in your handleTick function you have to add a code that will animate the sprite. You have to add this code before you draw your entity.
//If the animation is active.
if(animationActive){
//If the counter >= tick in the sequence. If not, just keep on increasing the counter value.
if(animationCounter >= animationSequence[animationProgress]){
animationCounter = 1;
//Reset the progress, so that next time another animation frame shows up.
if(animationProgress >= animationSequence.length - 1){
animationProgress = 0;
}else{
animationProgress++;
}
//Select information about the current animation frame and store it in a variable so it is easier to access.
var currentFrame = animationSequence[animationProgress];
//Change player.sprite column and row.(new frame);
avatarSpriteColumn = currentFrame.column;
avatarSpriteRow = currentFrame.row;
}else{
animationCounter.counter++;
}
}
Ok, now you have a code that animates the sprite, but how do we run it? Well, i see you have a variable called moving. It tells us in what direction the player is moving and if it is moving at all. It looks like you implemented it a little bit wrong. Right now your function that operates the keys looks like this:
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 0;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 1;
}
break;
}
}
The variable is supposed to return 1 if the entity is going to the right, 2 if it is going to the left and 0 if the entity is standing still, right? Right now it shows 0 when the entity is moving to the right and 1 when it is moving to the left. It also doesn't show us if the entity is idle. We have to fix it. Change it to something like this:
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 1;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 2;
}
break;
default:
moving = 0;
}
}
Ok, now we have to add this code to the handleTick function. This code starts the animation and changes the sequence.:
if(moving == 1){ //Moving in the right direction.
animationSequence = []; //Animation of moving in the right direction. Change the sequence to your own.
animationActive = true; //Run the animation.
}else if(moving == 2){ //Moving to the left.
animationSequence = []; //Animation of moving to the left. Change the sequence to your own.
animationActive = true; //Run the animation.
}else{
animationActive = false; //Stops the animation, but the last frame stays.
/*
Alternatively, if you want a separate frame or animation that is animating when the entity is standing, you run this code.
animationSequence = []; // Your sequence. If you want a single frame, with no animation just add one frame to the sequence.
animationActive = true;
*/
}
Now, the last thing we have to do is to draw the entity. In your case, this will look something like this:
context.drawImage(avatarImage, avatarSpriteColumn * avatarSpriteWidth, avatarSpriteRow * avatarSpriteHeight, avatarWidth, avatarHeight, avatarX, avatarY, 64, 64);
In the end your whole code will look something like this:
var avatarX = 0;
var avatarY = 240;
var avatarImage;
var counter = 1;
var XWIDTH = 0;
var WIDTH = 400;
var dx = 5;
var tt;
var gameCanvas;
var context;
var moving;
var animationCounter = 1;
var avatarSpriteColumn = 0; //Sprite frame on the x axis.
var avatarSpriteRow = 0; //Sprite frame on the y axis.
var avatarSpriteWidth = 50; //The width of a frame.
var avatarSpriteHeight = 100; //The height of a frame.
var animationActive = false; //A variable that controls if the animation is 'running'.
var animationCounter = 0; //How many frames(ticks) have passed since the last frame(animation frame) has changed. (I'm not good at describing variables. :P)
var animationProgress = 0; //Current animation frame.
var animationSequence = []; //Array that stores the sequence of animation, as i explained.
window.addEventListener('keydown', KeyDown);
function setUpGame() { //This is the function that is called from the html document.
gameCanvas = document.getElementById("gameCanvas"); //Declare a new variable & assigns it the id of the CANVAS from the html document.
context=gameCanvas.getContext("2d");
context.font = "18px Iceland";
context.textBaseline = "top";
avatarImage = new Image(); //Declaring a new variable. This is so that we can store the image at a later date.
avatarImage.onload=function(){
// avatarImage is now fully loaded and ready to drawImage
context.drawImage(avatarImage, Math.random() * 100, avatarY);
// start the timer
tt = setInterval(function(){counTer()},1000);
setInterval(handleTick, 25);
}
avatarImage.addEventListener('load', startLoop, false);
avatarImage.src = "img/ships.png"; //Ditto from above.
}
function startLoop() {
console.log("Detecting whether moving to the right is: " + moving);
if(moving == 0) {
gameLoop();
}
}
function gameLoop() {
setTimeout(gameLoop, 100);
handleTick();
}
function KeyDown(evt) {
switch (evt.keyCode) {
case 39: /*Arrow to the right*/
if(avatarX + dx <WIDTH && avatarX + dx >XWIDTH) {
avatarX += dx;
moving = 1;
}
break;
case 37: /*Arrow to the left*/
if(avatarX - dx >XWIDTH) {
avatarX -= dx;
moving = 2;
}
break;
default:
moving = 0;
}
}
function counTer() {
if(counter == 60) {
clearInterval(tt);
} else {
counter++;
}
}
function handleTick() {
context.clearRect(0,0,gameCanvas.width,gameCanvas.height);
if(moving == 1){ //Moving in the right direction.
animationSequence = []; //Animation of moving in the right direction. Change the sequence to your own.
animationActive = true; //Run the animation.
}else if(moving == 2){ //Moving to the left.
animationSequence = []; //Animation of moving to the left. Change the sequence to your own.
animationActive = true; //Run the animation.
}else{
animationActive = false; //Stops the animation, but the last frame stays.
/*
Alternatively, if you want a separate frame or animation that is animating when the entity is standing, you run this code.
animationSequence = []; // Your sequence. If you want a single frame, with no animation just add one frame to the sequence.
animationActive = true;
*/
}
//If the animation is active.
if(animationActive){
//If the counter >= tick in the sequence. If not, just keep on increasing the counter value.
if(animationCounter >= animationSequence[animationProgress]){
animationCounter = 1;
//Reset the progress, so that next time another animation frame shows up.
if(animationProgress >= animationSequence.length - 1){
animationProgress = 0;
}else{
animationProgress++;
}
//Select information about the current animation frame and store it in a variable so it is easier to access.
var currentFrame = animationSequence[animationProgress];
//Change player.sprite column and row.(new frame);
avatarSpriteColumn = currentFrame.column;
avatarSpriteRow = currentFrame.row;
}else{
animationCounter.counter++;
}
}
context.drawImage(avatarImage, avatarSpriteColumn * avatarSpriteWidth, avatarSpriteRow * avatarSpriteHeight, avatarWidth, avatarHeight, avatarX, avatarY, 64, 64);
context.fillText("Seconds: " + counter, 5, 5);
context.fillText("1 is Right, 2 is Left, 0 is idle: " + moving, 20, 20);
}
The only thing you have to do right now is to make your own animationSequences and check if it works, let me know if you have any problems with that.
Of course, the code i am using is more complicated and has more "abilities" and is easier to use(the code behind is more complicated), but hopefully this will help you.
I must also apoligize for making this thing seem so complicated, when it's not. I am bad at explaining.
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.