简体   繁体   English

在JavaScript中向左或向右移动时如何运行动画?

[英]How do I run an animation when moving to the left or right in JavaScript?

How do I run a sprite animation when pressing the left or right arrow keys in JavaScript? 在JavaScript中按向左或向右箭头键时如何运行Sprite动画? 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. 该序列是一个包含以下对象的数组: {column: 0, row: 0, t: 12}每个对象都是一个框架。 The column value is the current x of the sprite, the row is the y of the sprite. column值是子画面的当前xrow是子画面的y 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). 该脚本会自动创建xy值,因此您只需添加0、1、3、5等值(仅在x或y轴上是哪一帧即可)。例如,如果列为0,行为0,则这是左上角的框架(第一帧)。 The t value stands for tick, this determines how many ticks there has to happend before the animation goes to the next frame. t值代表滴答声,它确定动画进入下一帧之前必须发生多少滴答声。

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. Player.sprite还具有属性widthheight ,即框架的宽度和高度,它通常与播放器对象的宽度和高度相同。

You have to make your own player.animation.sequence in onLeft and onRight function so it animates how you want it to animate. 您必须在onLeftonRight函数中onLeft自己的player.animation.sequence ,以便对其进行动画处理。

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 . 只需查看player对象,只需编写player.x即可获得x, player.xPlayerX player.x得多。 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. 然后,在handleTick函数中,您必须添加将使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 . 好吧,我看到您有一个名为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? 如果实体向右移动,则该变量应返回1;如果实体向左移动,则该变量应返回2;如果实体静止不动,则该变量返回0,对吗? Right now it shows 0 when the entity is moving to the right and 1 when it is moving to the left. 现在,当实体向右移动时,它显示0;当实​​体向左​​移动时,它显示1。 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. 好的,现在我们必须将此代码添加到handleTick函数中。 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. 您现在唯一要做的就是制作自己的animationSequences并检查其是否有效,如果您有任何问题,请告诉我。

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. 我不好解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM