123

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              .viewport
  .background
  .foreground#ground
  .turtle.is-facing-left#player
  .intro
    h1 Le Tortue fou
    p Written with pure JavaScript and SASS with OOP in mind...
    p Control the crazy turtle with LEFT, RIGHT, SPACE KEYS...
    ul.credits
      li: a(href='http://gameprogrammingpatterns.com') Game Programming Patterns [Robert Nystrom]
      li: a(href='http://www.spriters-resource.com/fullview/22504/') Sprite source
      li: a(href='http://lea.verou.me/css3patterns/') CSS3 Patterns Gallery
      li: a(href='https://codepen.io/zessx/full/rDEAl/') Sky Gradients

.narrative
  h2 Atari
  p Atari (from a Japanese verb meaning "to hit the target") is a corporate and brand name owned by several entities since its inception in 1972, currently by Atari Interactive, a subsidiary of the French publisher Atari, SA (ASA).[1][2][3] The original Atari, Inc. founded in 1972 by Nolan Bushnell and Ted Dabney was a pioneer in arcade games, home video game consoles, and home computers. The company's products, such as Pong and the Atari 2600, helped define the electronic entertainment industry from the 1970s to the mid-1980s.
  cite: a(href='https://en.wikipedia.org/wiki/Atari') Wiki/Atari
  
.narrative.right
  h2 Ingredients
  dl
    dt HTML/CSS
    dd
      ul
        li Jade and SASS
        li Auto Prefixer
        li CSS Sprites
    dt JS
    dd
      ul
        li Request Animation Frame API
        li Frame Rate Control Technique
        li Simultaneous Key Event Handling Technique
            
          
!
            
              $sprite-turtle: 'http://www.spriters-resource.com/resources/sheets/21/22504.png';
$sprite-width: 110px;
$sprite-height: 70px;

@mixin sprite($name, $index: 1) {
  $map: (
    rest: (-100px -2px) (0px 0px),
    horizontal: (0 -635px) (-$sprite-width -635px) (-2*$sprite-width -635px) (-3*$sprite-width -635px)  (-4*$sprite-width -635px) (-5*$sprite-width -635px),
    jump: (0 -234px) (0 0),
    blink: (0 0px) (0 0),
    );
  
  $coords: nth(map-get($map, $name), $index);
  
  background-position: $coords;
}

%passive-element {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  z-index: 0;
}

%sprite-element {
  background-repeat: no-repeat;
  position: absolute;
  z-index: 100;
  width: 64px;
  height: 128px;
}

body {
  padding: 0;
  margin: 0;
  
  overflow: hidden;
}

.viewport {
  position: relative;
  width: 480px;
  height: 800px;
  margin: 0 auto;
  
  background: linear-gradient(to bottom, #94c5f8 1%,#a6e6ff 70%,#b1b5ea 100%);
  overflow: hidden;
}

.background,
.foreground {
  @extend %passive-element;
}

.foreground {
  $SPAN: 181.019335px;
  
  top: 70%;
  left: -$SPAN;
  right: -$SPAN;
  background: linear-gradient(135deg, #708090 22px, #d9ecff 22px, #d9ecff 24px, transparent 24px, transparent 67px, #d9ecff 67px, #d9ecff 69px, transparent 69px),
linear-gradient(225deg, #708090 22px, #d9ecff 22px, #d9ecff 24px, transparent 24px, transparent 67px, #d9ecff 67px, #d9ecff 69px, transparent 69px)0 64px;
  background-color:#708090;
  background-size: 64px 128px;
}

.turtle {
  @extend %sprite-element;
  width: $sprite-width;
  height: $sprite-height;
  left: 50%;
  top: calc(70% - #{$sprite-height});
  background-image: url($sprite-turtle);
  
  @include sprite(rest);
  transform: translate(-50%, 0);
  /*
  STOPPED
   */
  &.is-facing-left {
    
  }
  
  &.is-facing-right {
    transform: translate(-50%, 0) scale(-1, 1);
  }
  /*
  MOVING
   */
  &.is-moving.is-facing-left {
    @include sprite(horizontal, 1);
    transform: translate(-50%, 0);
    
    @for $i from 1 through 6 {
      &.f-#{$i} {
        @include sprite(horizontal, $i);
      }
    }
  }
  &.is-moving.is-facing-right {
    @include sprite(horizontal, 1);
    transform: translate(-50%, 0) scale(-1, 1);
    // Generate f-1, f-2, f-3....
    @for $i from 1 through 6 {
      &.f-#{$i} {
        @include sprite(horizontal, $i);
      }
    }
  }
  /*
  JUMPING - up...down
   */
  &.is-jumping.is-facing-left {
    @include sprite(jump);
  }
  &.is-jumping.is-facing-right {
    @include sprite(jump);
  }
  &.is-blinking {
    @include sprite(blink);
  }
}

// THIS SECTION HAS NOTHING TO DO WITH THE GAME
.intro {
  position: relative;
  z-index: 100;
  margin: 5%;
  padding: 5%;
  
  font-family: monospace;
  
  background: hsla(0, 0%, 0%, 0.8);
  
  h1 {
    color: crimson;
    text-align: center;
  }
  
  p, ul, a {
    padding: 0;
    color: white;
  }
  .credits:before {
    font-size: 20px;
    content: "CREDITS";
  }
}

.narrative {
  position: absolute;
  box-sizing: border-box;
  z-index: -1;
  width: calc(100% - #{2 * 480px});
  top: 0;
  
  padding: 40px;
  
  font-family: monospace;
  line-height: 2;
  
  overflow: hidden;
  
  &.right {
    right: 0;
    left: auto;
    
    text-align: right;
    
    ul {
      text-align: left;
    }
  }
  
  dt {
    font-weight: bold;
    font-size: 18px;
  }
}
            
          
!
            
              class Ground {
  constructor (dom) {
    this.dom = dom;
    
    var SPAN = 181.019335;
    this.x = -SPAN;
  }
  
  moveRight () {
    var SPAN = 181.019335;
    this.x += SPAN / FPS;
    // Cycle thru SPAN
    if (this.x > 0) {
      this.x = -SPAN;
    }
  }
  
  moveLeft () {
    var SPAN = 181.019335;
    this.x -= SPAN / FPS;
    // Cycle thru SPAN
    if (this.x < (SPAN * -2)) {
      this.x = -SPAN;
    }
  }
  
  stop () {
    
  }
  
  draw () {
    this.dom.style.left = this.x + 'px';
  }
}

class Turtle {
  constructor (dom) {
    this.dom = dom;
    
    this.spriteCount = 6;
    this.currentSpriteIndex = 0;
    this.spiteClasses = ['f-1', 'f-2', 'f-3', 'f-4', 'f-5', 'f-6'];
    
    this.jumpStepsCount = 6;
    this.currentJumpStepIndex = 0;
    this.jumpSteps = [20, 30, 32, 30, 20, 0];
    
    this.isMoving = false;
    this.isJumping = false;
    this.facingDirection = '';
  }
  
  moveLeft () {
    this.isMoving = true;
    this.facingDirection = 'LEFT';
  }
  
  moveRight () {
    this.isMoving = true;
    this.facingDirection = 'RIGHT';
  }
  
  jump () {
    if (this.isJumping) {
      return;  
    }

    this.isJumping = true;
    this.isMoving = false;
  }
  
  stop () {
    this.isMoving = false;
    this.facingDirection = '';
  }
  
  draw () {
    var style = this.dom.style;
    var classList = this.dom.classList;
    
    if (this.isMoving) {
      classList.add('is-moving');
      classList.remove('is-jumping');
      
      if (this.facingDirection === 'LEFT') {
        classList.remove('is-facing-right');
        classList.add('is-facing-left');
      }
      else if (this.facingDirection === 'RIGHT') {
        classList.remove('is-facing-left');
        classList.add('is-facing-right');
      }
      
      classList.remove(this.spiteClasses[this.currentSpriteIndex]);
      // Cycling sprites
      this.currentSpriteIndex = (this.currentSpriteIndex + 1) % this.spriteCount;
      classList.add(this.spiteClasses[this.currentSpriteIndex]);
    }
    else { // Updated next frame
      classList.remove('is-moving');
      classList.remove(this.spiteClasses[this.currentSpriteIndex]);
    }
    
    if (this.isJumping) {
      classList.add('is-jumping');
      classList.remove('is-moving');
      
      style.marginTop = (-this.jumpSteps[this.currentJumpStepIndex]) + 'px';
      this.currentJumpStepIndex++;
      
      if (this.currentJumpStepIndex === this.jumpStepsCount) {
        this.isJumping = false;
        this.currentJumpStepIndex = 0;
      }
    }
    else { // Updated next frame
      classList.remove('is-jumping');
    }
  }
}

var FPS = 15;
var USER_INPUT_REGISTRY = {};
var gameLoopHandler;
// Game World Objects
var domPlayer = document.querySelector('#player');
var domGround = document.querySelector('#ground');

var player = new Turtle(domPlayer);
var ground = new Ground(domGround);
/**
Shortcut of user input check
*/
function is(inputType) {
  if (inputType in USER_INPUT_REGISTRY) {
    return USER_INPUT_REGISTRY[inputType];    
  }
  
  return false;
}

function draw(timestamp) {
  player.draw();
  ground.draw();
}

function keydownHandler(e) {
  e.preventDefault();
  var keyCode = e.keyCode;
  // console.debug('Keydown: ', e.keyCode);
  
  switch (keyCode) {
    case 37: 
      USER_INPUT_REGISTRY['left'] = true;
      break;// LEFT
    case 38: 
      USER_INPUT_REGISTRY['up'] = true;
      break;// UP
    case 40: 
      USER_INPUT_REGISTRY['down'] = true;
      break;// DOWN
    case 39:
      USER_INPUT_REGISTRY['right'] = true;
      break;// RIGHT
    case 32:
      USER_INPUT_REGISTRY['space'] = true;
      break;// RIGHT
    case 17:
      USER_INPUT_REGISTRY['ctrl'] = true;
      break;// CTRL
  }
}

function keyupHandler(e) {
  e.preventDefault();
  var keyCode = e.keyCode;
  switch (keyCode) {
    case 37: 
      USER_INPUT_REGISTRY['left'] = false;
      break;// LEFT
    case 38: 
      USER_INPUT_REGISTRY['up'] = false;
      break;// UP
    case 40: 
      USER_INPUT_REGISTRY['down'] = false;
      break;// DOWN
    case 39:
      USER_INPUT_REGISTRY['right'] = false;
      break;// RIGHT
    case 32:
      USER_INPUT_REGISTRY['space'] = false;
      break;// SPACE
    case 17:
      USER_INPUT_REGISTRY['ctrl'] = false;
      break;// CTRL
  }
}

function gameloop() {
  if (is('right')) {
    console.info('User is holding RIGHT KEY');
    player.moveRight();
    ground.moveLeft();
  } 
  else if (is('left')) {
    console.info('User is holding LEFT KEY');
    player.moveLeft();
    ground.moveRight();
  }
  else {
    player.stop();
  }
  
  if (is('space')) {
    console.info('User is holding SPACE KEY');
    player.jump();
  }
  
  requestAnimationFrame(draw);
}

function startGame () {
  window.addEventListener('keydown', keydownHandler);
  window.addEventListener('keyup', keyupHandler);
  gameLoopHandler = setInterval(gameloop, 1000 / FPS);
}

startGame();
            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.

Console