Tomasz Cz.
Tomasz Cz.

Reputation: 334

Smooth and fast sprite animation (CSS)

I'm trying to make a game of a Space Invaders using JavaScript/jQuery. Now my problem is I cannot make the spaceship move smoothly on the screen.

I am using a keydown event to change sprite's CSS position by n pixels. When I give it a high value, the sprite moves fast but not smoothly. When I choose to move the sprite by 1px it looks smoother, but is very slow. Any ideas how to approach it?

The live demo is here. Use WSAD keys to move the sprite.

PS I'm not asking for animation libraries. I'd like to achieve it using JavaScript/jQuery only.

$(document).ready(function() {
  var xPos, yPos;

  var height = $(document).height();
  var width = $(document).width();

  function move(dir) {
          console.log(height + "," + width);
    xPos = parseInt($('#sprite').css('left'));
    yPos = parseInt($('#sprite').css('top'));

    switch (dir) {
      case 'up':
      if (yPos > 10) { $('#sprite').css('top', yPos - 10 + "px"); }
      break;
      case 'down':
      if (yPos < height-140) { $('#sprite').css('top', yPos + 10 + "px"); }
      break;
      case 'left':
      if (xPos > 10) { $('#sprite').css('left', xPos - 10 + "px"); }
      break;
      case 'right':
      if (xPos < width-140) { $('#sprite').css('left', xPos + 10 + "px"); }
      break;
    }
  }

    $(document).keydown(function(event) {
      var mykey = String.fromCharCode(event.which);          
      if (mykey == "W" || mykey == "w") { move('up'); }
      if (mykey == "S" || mykey == "s") { move('down') }
      if (mykey == "A" || mykey == "a") { move('left') }
      if (mykey == "D" || mykey == "d") { move('right') }          
    });

});

Upvotes: 1

Views: 2135

Answers (2)

user985399
user985399

Reputation:

I give you a pure Javascript snippet that is as simple as possible, so you easily replace the divs with images or animated sprites.

enter image description here

Also the positions you control with Javascript exactly as you want.

For space invader this is ideal, because all ships will move smoothly from one position to the other. It is a misstake from old tween experiences to believe it get slow with Javascript, but the intermediate pixels is not needed to tween. The GPU takes care of it by CSS transitions.

Here is 100 moving divs ...

let d = document, html = "", css3 = "", x, y, z, t = 10, n = 100

for (let i = 0; i < n; i++) {
  css3 += "#sprite" + i + " {display:inline-box; position:absolute; width:10px; height:10px; background-color:red; transition:top " + t + "s, left " + t + "s;}"
  html += "<div id='sprite" + i + "'></div>"
}

var e = d.createElement('style')
e.innerHTML = css3
d.head.appendChild(e)

var e = d.createElement('div')
e.innerHTML = html
d.body.appendChild(e)

move();
setTimeout(move, 0)
setInterval(move, 1000*t)

function move() {
  for (let i = 0; i < n; i++) {
    x = 600*Math.random()
    y = 200*Math.random()
    z = d.getElementById("sprite" + i)
    if (z) {
      z.style.top = y + "px"
      z.style.left = x + "px"
    }
  }
}

This is more Space Invader like but the code got cluttered ...

let d = document, html = "", css3 = "", t = 2, n = 300
let  x, y, z, xx = 40, yy = 40, xxx = 30, yyy = 30
document.write(n + " space invaders, " + t + "s moves by " + xxx + "px")

for (let i = 0; i < n; i++) {
  css3 += "body {background:black; color:pink;} #sprite" + i + " {display:inline-box; position:absolute; width:30px; height:30px; background-color:blue; transition:top 1s, left " + t + "s;}"
  html += "<img id='sprite" + i + "' src='https://i.gifer.com/PCTg.gif'></img>"
}

var e = d.createElement('style')
e.innerHTML = css3
d.head.appendChild(e)

var e = d.createElement('div')
e.innerHTML = html
setTimeout(function(){d.body.appendChild(e)}, 100)

setInterval(move, 1000*t)

function move() {
  if (xxx > 0? xx > 100: xx < 40) {
    xx += xxx
    xxx = -xxx
    yy += 10
  }
  xx += xxx
  for (let i = 0; i < n; i++) {
    x = 30*(i % 30) + xx
    y = 30*parseInt(i / 30) + yy
    z = d.getElementById("sprite" + i)
    if (z) {
      z.style.top = y + "px"
      z.style.left = x + "px"
    }
  }
}

Upvotes: 0

Jeremy Thille
Jeremy Thille

Reputation: 26410

Just add CSS3 transition to your ship :

#sprite{
    -webkit-transition : all 0.4s ease-in-out;
    -moz-transition : all 0.4s ease-in-out;
    -o-transition : all 0.4s ease-in-out;
    transition : all 0.4s ease-in-out;
}

However, animating with translate is better than position top/left (article by Paul Irish).

Besides, optimize your code by caching $('#sprite') with

 var $sprite = $('#sprite');

function move(dir) {
    xPos = parseInt($sprite.css('left'));
    yPos = parseInt($sprite.css('top'));
    ...

If you don't do this (as the code is currently), you force jQuery to go all over the whole DOM many times, every time a key is pressed. If you do, the ship is cached so jQuery doesn't have to look for it every time. I suggest you to have a look at jQache for easy caching.

Upvotes: 3

Related Questions