I havet this codepen: https://codepen.io/sp2012/pen/VwpyWdp . Unfortunately, this code is too advanced for me. The game has three maps. I want to keep only the first map and when the game is finished, if you click on the map the game restarts.
The code follows:
This is the HTML:
<div id="game-container-1" class="game-container"> <div id="map-and-controls"> <div id="game-map-1" class="game-map"> <div id="tiles" class="layer"></div> <div id="sprites" class="layer"></div> <div id="success-msg">Goal reached! Tap the maze to change levels.</div> </div> <!-- controls--> <div id="controls"> <button id="up"></button> <div id="horiz"> <button id="left"></button> <button id="right"></button> </div> <button id="down"></button> </div> </div> <p id="text-1" class="text">Use cursor keys or buttons to move the marble.</p> </div>
This is the CSS:
/* * General Styling */ body { font-family: Calibri; transition: 0.2s ease; text-align: center; } body.success { background-color: #b7f0b7; transition: 0.2s ease; } /* center everything in game container */ .game-container { margin: 0px auto; } /* * Map screen */ .game-map { position: relative; } /* * Output text styles */ p { margin: 10px 0px; padding: 0px; } /* * Map on left, controls on right * Adapted for the mobile Medium app */ #map-and-controls { display: flex; justify-content: center; } /* * Controls */ #controls { margin-left: 10px; text-align: center; } /* * Container for right and left buttons */ #controls #horiz { display: flex; align-items: center; justify-content: center; } /* * General button styles */ #controls button { padding: 10px 10px; margin-top: 10px; background-color: #DDD; border: 1px solid #000; width: 38px; height: 38px; border-radius: 3px; cursor: pointer; position: relative; } /* * Spacing between horiz buttons */ button#right { margin-left: 5px; } button#left { margin-right: 5px; } /* * General button arrow styles */ #controls button::before { content:''; width: 0px; position: absolute; } /* * Specific Arrow Styles */ button#left::before { border-top: 10px solid transparent; border-right: 15px solid #000; border-bottom: 10px solid transparent; left: 10px; top: 9px; } button#right::before { border-top: 10px solid transparent; border-left: 15px solid #000; border-bottom: 10px solid transparent; left: 12px; top: 9px; } button#up::before { border-right: 10px solid transparent; border-left: 10px solid transparent; border-bottom: 15px solid #000; left: 9px; top: 9px; } button#down::before { border-right: 10px solid transparent; border-left: 10px solid transparent; border-top: 15px solid #000; left: 9px; top: 12px; } #success-msg { opacity: 0; transition: opacity 0.2s ease; position: absolute; padding: 5px 5px; background-color: rgba(0,0,0,0.5); color: white; width: calc(100% - 8px); } body.success #success-msg { opacity: 1; transition: opacity 0.2 ease; } /* * Layers and tiles are positioned absolutely * within coordinate system of .game-map */ div.layer, div.layer div { position: absolute; } /* border for floors and wall */ #tiles div { border: 1px solid grey; } /* * Default wall and floor styles */ .default .floor { background-color: lightgrey; } .default .wall { background-color: skyblue; } /* * grassland theme */ .grassland .floor { background-color: #7bb76d; } .grassland .wall { background-color: #806d51; } .grassland #player { background-color: #b2ccec; } /* * dungeon theme */ .dungeon .floor { background-color: darkgrey; } .dungeon .wall { background-color: #9c649c; } .dungeon #player { background-color: #ab1431; } /* * player and goal are slightly smaller than tiles */ .player, .goal { transform-origin: center; transform:scale(0.85); } /* * Goal colors */ .goal { background-color: #FFD700; border: 1px solid #98720b; } /* * Player default colors */ .player { background-color: #90ee90; border: 1px solid #008000; transition: left 0.2s ease, top 0.2s ease; } /* * Player wobbles when colliding with wall or border */ .player.collide { animation: wobble 0.5s; animation-iteration-count: infinite; transition: background-color 0.2s; } /* * Wobble animation */ @keyframes wobble { 0% { transform: scale(0.85) translate(1px, 1px); } 10% { transform: scale(0.85) translate(-1px, -2px); } 20% { transform: scale(0.85) translate(-3px, 0px); } 30% { transform: scale(0.85) translate(3px, 2px); } 40% { transform: scale(0.85) translate(1px, -1px);} 50% { transform: scale(0.85) translate(-1px, 2px); } 60% { transform: scale(0.85) translate(-3px, 1px); } 70% { transform: scale(0.85) translate(3px, 1px); } 80% { transform: scale(0.85) translate(-1px, -1px); } 90% { transform: scale(0.85) translate(1px, 2px); } 100% { transform: scale(0.85) translate(1px, -2px);; } }
Here is the JavaScript:
let app = {}; (function(context) { /* * Build an array of levels. * This will scale better if it is stored in a separate JSON File. */ let levels = []; levels[0] = { map:[ [1,1,0,0,1], [1,0,0,0,0], [0,0,1,1,0], [0,0,0,1,0], [0,1,0,1,0] ], player:{ x:0, y:4 }, goal:{ x:4, y:1 }, theme:'default', }; // second level levels[1] = { map:[ [1,0,1,1,1,1], [0,0,0,0,0,0], [0,1,1,1,0,0], [0,0,0,1,1,0], [0,1,0,1,0,0] ], theme:'grassland', player:{ x:2, y:4 }, goal:{ x:4, y:4 } }; // third level levels[2] = { map:[ [1,0,1,0,0,1,0], [0,0,0,0,0,1,0], [1,0,1,1,0,0,0], [1,0,0,1,0,1,0], [1,1,0,0,1,0,0] ], theme:'dungeon', player:{ x:2, y:4 }, goal:{ x:6, y:4 } }; /* * The game object constructor. * @param {String} id - the id of the game container DOM element. * @param {Object} level - the starting level of the game. */ function Game(id,level) { this.el = document.getElementById(id); // level addition this.level_idx = 0; // establish the basic properties common to all this objects. this.tileTypes = ['floor','wall']; this.tileDim = 32; // inherit the level's properties: map, player start, goal start. this.map = level.map; // level switch this.theme = level.theme; // make a copy of the level's player. this.player = {...level.player}; // create a property for the DOM element, to be set later. this.player.el = null; // make a copy of the goal. this.goal = {...level.goal}; } /* * Create a tile or sprite <div> element. * @param {Number} x - the horizontal coordinate the 2D array. * @param {Number} y - the vertical coordinate in the 2D array. */ Game.prototype.createEl = function(x,y,type) { // create one tile. let el = document.createElement('div'); // two class names: one for tile, one or the tile type. el.className = type; // set width and height of tile based on the passed-in dimensions. el.style.width = el.style.height = this.tileDim + 'px'; // set left positions based on x coordinate. el.style.left = x*this.tileDim + 'px'; // set top position based on y coordinate. el.style.top = y*this.tileDim + 'px'; return el; } /* * Applies the level theme as a class to the game element. * Populates the map by adding tiles and sprites to their respective layers. */ Game.prototype.populateMap = function() { // add theme call this.el.className = 'game-container ' + this.theme; // make a reference to the tiles layer in the DOM. let tiles = this.el.querySelector('#tiles'); // set up our loop to populate the grid. for (var y = 0; y < this.map.length; ++y) { for (var x = 0; x < this.map[y].length; ++x) { let tileCode = this.map[y][x]; // determine tile type using code // index into the tileTypes array using the code. let tileType = this.tileTypes[tileCode]; // call the helper function let tile = this.createEl(x,y,tileType); // add to layer tiles.appendChild(tile); } } } /* * Place the player or goal sprite. * @param {String} type - either 'player' or 'goal', used by createEl and becomes DOM ID */ Game.prototype.placeSprite = function(type) { // syntactic sugar let x = this[type].x let y = this[type].y; // reuse the createTile function let sprite = this.createEl(x,y,type); sprite.id = type; // set the border radius of the sprite. sprite.style.borderRadius = this.tileDim + 'px'; // get half the difference between tile and sprite. // grab the layer let layer = this.el.querySelector('#sprites'); layer.appendChild(sprite); return sprite; } /* * Triggers a collide animation on the player sprite. */ Game.prototype.collide = function() { this.player.el.className += ' collide'; let obj = this; window.setTimeout(function() { obj.player.el.className = 'player'; },200); return 0; }; /* * Moves the player sprite left. */ Game.prototype.moveLeft = function() { // if at the boundary, return if (this.player.x == 0) { this.collide(); return; } // itentify next tile let nextTile = this.map[this.player.y][this.player.x-1]; // if next tile is a wall, add collide effect and return if (nextTile ==1) { this.collide(); return; } // change coordinates of player object this.player.x -=1; // update location of DOM element this.updateHoriz(); }; /* * Moves the player sprite up. */ Game.prototype.moveUp = function() { if (this.player.y == 0) { // at end: these could be combined this.collide(); return; } let nextTile = this.map[this.player.y-1][this.player.x]; if (nextTile ==1) { this.collide(); return; } this.player.y -=1; this.updateVert(); }; /* * Moves the player sprite right. */ Game.prototype.moveRight = function() { if (this.player.x == this.map[this.player.y].length-1) { this.collide(); return; } nextTile = this.map[this.player.y][this.player.x+1]; if (nextTile ==1) { this.collide() return; } this.player.x += 1; this.updateHoriz(); }; /* * Moves player sprite down. */ Game.prototype.moveDown = function() { if (this.player.y == this.map.length-1) { this.collide(); return; } // find the next tile in the 2D array. let nextTile = this.map[this.player.y+1][this.player.x]; if (nextTile ==1) { this.collide() return; } this.player.y += 1; this.updateVert(); }; /* * Updates vertical position of player sprite based on object's y coordinates. */ Game.prototype.updateVert = function() { this.player.el.style.top = this.player.y * this.tileDim+ 'px'; }; /* * Updates horizontal position of player sprite based on object's x coordinates. */ Game.prototype.updateHoriz = function() { this.player.el.style.left = this.player.x * this.tileDim + 'px'; }; /* * Moves player based on keyboard cursor presses. */ Game.prototype.movePlayer = function(event) { event.preventDefault(); if (event.keyCode < 37 || event.keyCode > 40) { return; } switch (event.keyCode) { case 37: this.moveLeft(); break; case 38: this.moveUp(); break; case 39: this.moveRight(); break; case 40: this.moveDown(); break; } } /* * Check on whether goal has been reached. */ Game.prototype.checkGoal = function() { let body = document.querySelector('body'); if (this.player.y == this.goal.y && this.player.x == this.goal.x) { body.className = 'success'; } else { body.className = ''; } } /* * Changes the level of the game object. */ Game.prototype.changeLevel = function() { // update the level index. this.level_idx ++; // if higher than max index, set back to zero. if (this.level_idx > levels.length -1) { this.level_idx = 0; } // get the level at this index. let level = levels[this.level_idx]; // sync the map with the level map. this.map = level.map; // sync the theme with the level theme. this.theme = level.theme; // make a copy of the level's player object, since x and y change during the game. this.player = {...level.player}; // make a copy of the level's goal object, since x and y change between levels. this.goal = {...level.goal}; } /* * If goal has been reached, */ Game.prototype.addMazeListener = function() { // grab the map let map = this.el.querySelector('.game-map'); // grab reference to game object since we are going into a function // and "this" will no longer refer to the game object let obj = this; // if game board is clicked or tapped, see if we should change levels map.addEventListener('mousedown',function(e) { // if not at the goal, then get outta here if (obj.player.y != obj.goal.y || obj.player.x != obj.goal.x) { return; } // change level of game object by changing it's properties obj.changeLevel(); // get the two layers let layers = obj.el.querySelectorAll('.layer'); // clear tiles and sprites from layers for (layer of layers) { layer.innerHTML = ''; } // place the new level. obj.placeLevel(); // check the goal to reset the message. obj.checkGoal(); }); }; /* * Responds to a keydown event by moving the player and checking the goal. */ Game.prototype.keyboardListener = function() { document.addEventListener('keydown', event => { this.movePlayer(event); this.checkGoal(); }); } /* * Adds mouse down listeners to buttons */ Game.prototype.buttonListeners = function() { let up = document.getElementById('up'); let left = document.getElementById('left'); let down = document.getElementById('down') let right = document.getElementById('right'); // the sprite is out of date let obj = this; up.addEventListener('mousedown',function() { obj.moveUp(); obj.checkGoal(); }); down.addEventListener('mousedown',function() { obj.moveDown(); obj.checkGoal(); }); left.addEventListener('mousedown',function() { obj.moveLeft(); obj.checkGoal(); }); right.addEventListener('mousedown',function() { obj.moveRight(); obj.checkGoal(); }); } /* * Sets the message of the text element. * @param {String} msg - The message to be printed. */ Game.prototype.setMessage = function(msg) { let text_el = this.el.querySelector('.text'); text_el.textContent = msg; }; /* * Sizes up the map based on array dimensions. */ Game.prototype.sizeUp = function() { // inner container so that text can be below it let map = this.el.querySelector('.game-map'); // inner container, height. Need this.map map.style.height = this.map.length * this.tileDim + 'px'; map.style.width = this.map[0].length * this.tileDim + 'px'; }; /* * Populates the map. * Sizes up the map based on array dimensions. * Gives the goal and player some references. */ Game.prototype.placeLevel = function() { this.populateMap(); this.sizeUp(); this.placeSprite('goal'); // we want the DOM element that gets returned... let playerSprite = this.placeSprite('player'); // ..so we can store it in the playerSprite element. this.player.el = playerSprite; } /* * Add keyboard, button, and maze tap listeners */ Game.prototype.addListeners = function() { this.keyboardListener(); this.buttonListeners(); // changing levels this.addMazeListener(); } /* * Initialization function called once */ context.init = function () { let myGame = new Game('game-container-1',levels[0]); // encapsulate for multi-level myGame.placeLevel(); // add listeners myGame.addListeners(); } })(app); /* * Tell app to activate the init() function. */ app.init();
Any ideas please?
Advertisement
Answer
Just comment out the second and third level section of the levels[0] object (maps). Change the HTML content that makes reference to other levels.
let app = {}; (function(context) { /* * Build an array of levels. * This will scale better if it is stored in a separate JSON File. */ let levels = []; levels[0] = { map:[ [0,0,1,1,1,0,1,1,0], [1,0,1,1,0,0,0,0,0], [0,0,1,1,0,1,1,0,1], [1,0,0,0,0,0,1,0,1], [1,1,1,1,1,0,1,0,1] ], player:{ x:0, y:0 }, goal:{ x:7, y:4 }, theme:'default', }; /* second level levels[1] = { map:[ [1,0,1,1,1,1], [0,0,0,0,0,0], [0,1,1,1,0,0], [0,0,0,1,1,0], [0,1,0,1,0,0] ], theme:'grassland', player:{ x:2, y:4 }, goal:{ x:4, y:4 } }; // third level levels[2] = { map:[ [1,0,1,0,0,1,0], [0,0,0,0,0,1,0], [1,0,1,1,0,0,0], [1,0,0,1,0,1,0], [1,1,0,0,1,0,0] ], theme:'dungeon', player:{ x:2, y:4 }, goal:{ x:6, y:4 } }; /* * The game object constructor. * @param {String} id - the id of the game container DOM element. * @param {Object} level - the starting level of the game. */ function Game(id,level) { this.el = document.getElementById(id); // level addition this.level_idx = 0; // establish the basic properties common to all this objects. this.tileTypes = ['floor','wall']; this.tileDim = 32; // inherit the level's properties: map, player start, goal start. this.map = level.map; // level switch this.theme = level.theme; // make a copy of the level's player. this.player = {...level.player}; // create a property for the DOM element, to be set later. this.player.el = null; // make a copy of the goal. this.goal = {...level.goal}; } /* * Create a tile or sprite <div> element. * @param {Number} x - the horizontal coordinate the 2D array. * @param {Number} y - the vertical coordinate in the 2D array. */ Game.prototype.createEl = function(x,y,type) { // create one tile. let el = document.createElement('div'); // two class names: one for tile, one or the tile type. el.className = type; // set width and height of tile based on the passed-in dimensions. el.style.width = el.style.height = this.tileDim + 'px'; // set left positions based on x coordinate. el.style.left = x*this.tileDim + 'px'; // set top position based on y coordinate. el.style.top = y*this.tileDim + 'px'; return el; } /* * Applies the level theme as a class to the game element. * Populates the map by adding tiles and sprites to their respective layers. */ Game.prototype.populateMap = function() { // add theme call this.el.className = 'game-container ' + this.theme; // make a reference to the tiles layer in the DOM. let tiles = this.el.querySelector('#tiles'); // set up our loop to populate the grid. for (var y = 0; y < this.map.length; ++y) { for (var x = 0; x < this.map[y].length; ++x) { let tileCode = this.map[y][x]; // determine tile type using code // index into the tileTypes array using the code. let tileType = this.tileTypes[tileCode]; // call the helper function let tile = this.createEl(x,y,tileType); // add to layer tiles.appendChild(tile); } } } /* * Place the player or goal sprite. * @param {String} type - either 'player' or 'goal', used by createEl and becomes DOM ID */ Game.prototype.placeSprite = function(type) { // syntactic sugar let x = this[type].x let y = this[type].y; // reuse the createTile function let sprite = this.createEl(x,y,type); sprite.id = type; // set the border radius of the sprite. sprite.style.borderRadius = this.tileDim + 'px'; // get half the difference between tile and sprite. // grab the layer let layer = this.el.querySelector('#sprites'); layer.appendChild(sprite); return sprite; } /* * Triggers a collide animation on the player sprite. */ Game.prototype.collide = function() { this.player.el.className += ' collide'; let obj = this; window.setTimeout(function() { obj.player.el.className = 'player'; },200); return 0; }; /* * Moves the player sprite left. */ Game.prototype.moveLeft = function() { // if at the boundary, return if (this.player.x == 0) { this.collide(); return; } // itentify next tile let nextTile = this.map[this.player.y][this.player.x-1]; // if next tile is a wall, add collide effect and return if (nextTile ==1) { this.collide(); return; } // change coordinates of player object this.player.x -=1; // update location of DOM element this.updateHoriz(); }; /* * Moves the player sprite up. */ Game.prototype.moveUp = function() { if (this.player.y == 0) { // at end: these could be combined this.collide(); return; } let nextTile = this.map[this.player.y-1][this.player.x]; if (nextTile ==1) { this.collide(); return; } this.player.y -=1; this.updateVert(); }; /* * Moves the player sprite right. */ Game.prototype.moveRight = function() { if (this.player.x == this.map[this.player.y].length-1) { this.collide(); return; } nextTile = this.map[this.player.y][this.player.x+1]; if (nextTile ==1) { this.collide() return; } this.player.x += 1; this.updateHoriz(); }; /* * Moves player sprite down. */ Game.prototype.moveDown = function() { if (this.player.y == this.map.length-1) { this.collide(); return; } // find the next tile in the 2D array. let nextTile = this.map[this.player.y+1][this.player.x]; if (nextTile ==1) { this.collide() return; } this.player.y += 1; this.updateVert(); }; /* * Updates vertical position of player sprite based on object's y coordinates. */ Game.prototype.updateVert = function() { this.player.el.style.top = this.player.y * this.tileDim+ 'px'; }; /* * Updates horizontal position of player sprite based on object's x coordinates. */ Game.prototype.updateHoriz = function() { this.player.el.style.left = this.player.x * this.tileDim + 'px'; }; /* * Moves player based on keyboard cursor presses. */ Game.prototype.movePlayer = function(event) { event.preventDefault(); if (event.keyCode < 37 || event.keyCode > 40) { return; } switch (event.keyCode) { case 37: this.moveLeft(); break; case 38: this.moveUp(); break; case 39: this.moveRight(); break; case 40: this.moveDown(); break; } } /* * Check on whether goal has been reached. */ Game.prototype.checkGoal = function() { let body = document.querySelector('body'); if (this.player.y == this.goal.y && this.player.x == this.goal.x) { body.className = 'success'; } else { body.className = ''; } } /* * Changes the level of the game object. */ Game.prototype.changeLevel = function() { // update the level index. this.level_idx ++; // if higher than max index, set back to zero. if (this.level_idx > levels.length -1) { this.level_idx = 0; } // get the level at this index. let level = levels[this.level_idx]; // sync the map with the level map. this.map = level.map; // sync the theme with the level theme. this.theme = level.theme; // make a copy of the level's player object, since x and y change during the game. this.player = {...level.player}; // make a copy of the level's goal object, since x and y change between levels. this.goal = {...level.goal}; } /* * If goal has been reached, */ Game.prototype.addMazeListener = function() { // grab the map let map = this.el.querySelector('.game-map'); // grab reference to game object since we are going into a function // and "this" will no longer refer to the game object let obj = this; // if game board is clicked or tapped, see if we should change levels map.addEventListener('mousedown',function(e) { // if not at the goal, then get outta here if (obj.player.y != obj.goal.y || obj.player.x != obj.goal.x) { return; } // change level of game object by changing it's properties obj.changeLevel(); // get the two layers let layers = obj.el.querySelectorAll('.layer'); // clear tiles and sprites from layers for (layer of layers) { layer.innerHTML = ''; } // place the new level. obj.placeLevel(); // check the goal to reset the message. obj.checkGoal(); }); }; /* * Responds to a keydown event by moving the player and checking the goal. */ Game.prototype.keyboardListener = function() { document.addEventListener('keydown', event => { this.movePlayer(event); this.checkGoal(); }); } /* * Adds mouse down listeners to buttons */ Game.prototype.buttonListeners = function() { let up = document.getElementById('up'); let left = document.getElementById('left'); let down = document.getElementById('down') let right = document.getElementById('right'); // the sprite is out of date let obj = this; up.addEventListener('mousedown',function() { obj.moveUp(); obj.checkGoal(); }); down.addEventListener('mousedown',function() { obj.moveDown(); obj.checkGoal(); }); left.addEventListener('mousedown',function() { obj.moveLeft(); obj.checkGoal(); }); right.addEventListener('mousedown',function() { obj.moveRight(); obj.checkGoal(); }); } /* * Sets the message of the text element. * @param {String} msg - The message to be printed. */ Game.prototype.setMessage = function(msg) { let text_el = this.el.querySelector('.text'); text_el.textContent = msg; }; /* * Sizes up the map based on array dimensions. */ Game.prototype.sizeUp = function() { // inner container so that text can be below it let map = this.el.querySelector('.game-map'); // inner container, height. Need this.map map.style.height = this.map.length * this.tileDim + 'px'; map.style.width = this.map[0].length * this.tileDim + 'px'; }; /* * Populates the map. * Sizes up the map based on array dimensions. * Gives the goal and player some references. */ Game.prototype.placeLevel = function() { this.populateMap(); this.sizeUp(); this.placeSprite('goal'); // we want the DOM element that gets returned... let playerSprite = this.placeSprite('player'); // ..so we can store it in the playerSprite element. this.player.el = playerSprite; } /* * Add keyboard, button, and maze tap listeners */ Game.prototype.addListeners = function() { this.keyboardListener(); this.buttonListeners(); // changing levels this.addMazeListener(); } /* * Initialization function called once */ context.init = function () { let myGame = new Game('game-container-1',levels[0]); // encapsulate for multi-level myGame.placeLevel(); // add listeners myGame.addListeners(); } })(app); /* * Tell app to activate the init() function. */ app.init();
/* * General Styling */ body { font-family: Calibri; transition: 0.2s ease; text-align: center; } body.success { background-color: #b7f0b7; transition: 0.2s ease; } /* center everything in game container */ .game-container { margin: 0px auto; } /* * Map screen */ .game-map { position: relative; } /* * Output text styles */ p { margin: 10px 0px; padding: 0px; } /* * Map on left, controls on right * Adapted for the mobile Medium app */ #map-and-controls { display: flex; justify-content: center; } /* * Controls */ #controls { margin-left: 10px; text-align: center; } /* * Container for right and left buttons */ #controls #horiz { display: flex; align-items: center; justify-content: center; } /* * General button styles */ #controls button { padding: 10px 10px; margin-top: 10px; background-color: #DDD; border: 1px solid #000; width: 38px; height: 38px; border-radius: 3px; cursor: pointer; position: relative; } /* * Spacing between horiz buttons */ button#right { margin-left: 5px; } button#left { margin-right: 5px; } /* * General button arrow styles */ #controls button::before { content:''; width: 0px; position: absolute; } /* * Specific Arrow Styles */ button#left::before { border-top: 10px solid transparent; border-right: 15px solid #000; border-bottom: 10px solid transparent; left: 10px; top: 9px; } button#right::before { border-top: 10px solid transparent; border-left: 15px solid #000; border-bottom: 10px solid transparent; left: 12px; top: 9px; } button#up::before { border-right: 10px solid transparent; border-left: 10px solid transparent; border-bottom: 15px solid #000; left: 9px; top: 9px; } button#down::before { border-right: 10px solid transparent; border-left: 10px solid transparent; border-top: 15px solid #000; left: 9px; top: 12px; } #success-msg { opacity: 0; transition: opacity 0.2s ease; position: absolute; padding: 5px 5px; background-color: rgba(0,0,0,0.5); color: white; width: calc(100% - 8px); } body.success #success-msg { opacity: 1; transition: opacity 0.2 ease; } /* * Layers and tiles are positioned absolutely * within coordinate system of .game-map */ div.layer, div.layer div { position: absolute; } /* border for floors and wall */ #tiles div { border: 1px solid grey; } /* * Default wall and floor styles */ .default .floor { background-color: lightgrey; } .default .wall { background-color: skyblue; } /* * grassland theme */ .grassland .floor { background-color: #7bb76d; } .grassland .wall { background-color: #806d51; } .grassland #player { background-color: #b2ccec; } /* * dungeon theme */ .dungeon .floor { background-color: darkgrey; } .dungeon .wall { background-color: #9c649c; } .dungeon #player { background-color: #ab1431; } /* * player and goal are slightly smaller than tiles */ .player, .goal { transform-origin: center; transform:scale(0.85); } /* * Goal colors */ .goal { background-color: #FFD700; border: 1px solid #98720b; } /* * Player default colors */ .player { background-color: #90ee90; border: 1px solid #008000; transition: left 0.2s ease, top 0.2s ease; } /* * Player wobbles when colliding with wall or border */ .player.collide { animation: wobble 0.5s; animation-iteration-count: infinite; transition: background-color 0.2s; } /* * Wobble animation */ @keyframes wobble { 0% { transform: scale(0.85) translate(1px, 1px); } 10% { transform: scale(0.85) translate(-1px, -2px); } 20% { transform: scale(0.85) translate(-3px, 0px); } 30% { transform: scale(0.85) translate(3px, 2px); } 40% { transform: scale(0.85) translate(1px, -1px);} 50% { transform: scale(0.85) translate(-1px, 2px); } 60% { transform: scale(0.85) translate(-3px, 1px); } 70% { transform: scale(0.85) translate(3px, 1px); } 80% { transform: scale(0.85) translate(-1px, -1px); } 90% { transform: scale(0.85) translate(1px, 2px); } 100% { transform: scale(0.85) translate(1px, -2px);; } }
<div id="game-container-1" class="game-container"> <div id="map-and-controls"> <div id="game-map-1" class="game-map"> <div id="tiles" class="layer"></div> <div id="sprites" class="layer"></div> <div id="success-msg">Goal reached! Tap the maze to play again.</div> </div> <!-- controls--> <div id="controls"> <button id="up"></button> <div id="horiz"> <button id="left"></button> <button id="right"></button> </div> <button id="down"></button> </div> </div> <p id="text-1" class="text">Use cursor keys or buttons to move the marble.</p> </div>