有很多方法可以实现动画。我用的很简单,看起来像这样:
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)
}
序列是一个数组,其中包含如下所示的对象:{column: 0, row: 0, t: 12} 每个对象都是一个框架。 column 值是精灵的当前x,row 是精灵的y。该脚本会自动创建 x 和 y 值,因此您只需添加 0、1、3、5 等值。(只是它在 x 或 y 轴上的哪个帧。)因此,例如,如果列为 0,行为 0,则这是位于左上角的帧(第一帧)。 t 值代表tick,这决定了动画进入下一帧之前必须发生多少tick。
Player.sprite也有属性width和height,即帧的宽度和高度,通常与播放器对象的宽度和高度相同。
您必须在 onLeft 和 onRight 函数中创建自己的 player.animation.sequence,以便它按照您希望的方式制作动画。
我希望你明白我的意思。这可能看起来很复杂,但实际上并非如此,如果你现在不明白,别担心,你最终会明白的。如果你真的有理解上的困难,就问吧。
编辑:首先我强烈建议使用对象来存储有关实体的信息。它使游戏制作更容易和更清洁。看看player 对象,简单地写player.x 比PlayerX 更容易得到x。它看起来也更专业。 :) 当您必须提供有关您的实体的大量信息时,您不必传递很多参数,而是传递整个对象。 示例:
//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...
}
不管怎样,回到你的问题。首先,我们必须添加存储精灵信息的变量。
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.
然后,在您的handleTick 函数中,您必须添加一个代码来为精灵设置动画。您必须在绘制实体之前添加此代码。
//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++;
}
}
好的,现在你有一个动画精灵的代码,但是我们如何运行它呢?好吧,我看到你有一个名为moving 的变量。它告诉我们玩家正在向哪个方向移动以及它是否正在移动。看起来你实现它有点错误。现在你操作按键的函数如下所示:
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;
}
}
如果实体向右移动,变量应该返回 1,如果向左移动,则返回 2,如果实体静止不动,则返回 0,对吗?现在它在实体向右移动时显示 0,在向左移动时显示 1。如果实体处于空闲状态,它也不会向我们显示。我们必须修复它。把它改成这样:
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;
}
}
好的,现在我们必须将这段代码添加到 handleTick 函数中。此代码启动动画并更改序列。:
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;
*/
}
现在,我们要做的最后一件事是绘制实体。在你的情况下,这看起来像这样:
context.drawImage(avatarImage, avatarSpriteColumn * avatarSpriteWidth, avatarSpriteRow * avatarSpriteHeight, avatarWidth, avatarHeight, avatarX, avatarY, 64, 64);
最后你的整个代码看起来像这样:
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);
}
您现在唯一要做的就是创建自己的animationSequences 并检查它是否有效,如果您有任何问题,请告诉我。
当然,我使用的代码更复杂,有更多的“能力”,更容易使用(后面的代码更复杂),但希望对你有所帮助。
我还必须为让这件事看起来如此复杂而道歉,而事实并非如此。我不擅长解释。