console
<div class="frame">
<div class="corner_topleft"></div>
<div class="corner_topright"></div>
<div class="corner_bottomleft"></div>
<div class="corner_bottomright"></div>
<div class="camera">
<div class="map pixel-art">
<div class="character" facing="down" walking="true">
<div class="shadow pixel-art"></div>
<div class="character_spritesheet pixel-art"></div>
</div>
</div>
<div class="dpad">
<div class="DemoDirectionUI flex-center">
<button class="dpad-button dpad-left">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h4M7 4h5M1 5h3M7 5h5M1 6h4M7 6h5M1 7h5M7 7h5M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M6 3h1M5 4h1M4 5h1" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M5 5h2M5 6h2M6 7h1" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
<button class="dpad-button dpad-up">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h5M7 4h5M1 5h4M8 5h4M1 6h3M9 6h3M1 7h11M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M6 4h1M5 5h1M7 5h1" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 5h1M4 6h5" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
<button class="dpad-button dpad-down">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h11M1 4h3M9 4h3M1 5h4M8 5h4M1 6h5M7 6h5M1 7h11M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M4 4h5" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M5 5h3M6 6h1" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
<button class="dpad-button dpad-right">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -0.5 13 13" shape-rendering="crispEdges">
<path class="Arrow_outline-top" stroke="#5f5f5f" d="M1 0h11M0 1h1M12 1h1M0 2h1M12 2h1M0 3h1M12 3h1M0 4h1M12 4h1M0 5h1M12 5h1M0 6h1M12 6h1M0 7h1M12 7h1M0 8h1M12 8h1" />
<path class="Arrow_surface" stroke="#f5f5f5" d="M1 1h11M1 2h11M1 3h5M7 3h5M1 4h5M8 4h4M1 5h5M9 5h3M1 6h5M8 6h4M1 7h5M7 7h5M1 8h11" />
<path class="Arrow_arrow-inset" stroke="#434343" d="M6 3h1M7 4h1M8 5h1" />
<path class="Arrow_arrow-body" stroke="#5f5f5f" d="M6 4h1M6 5h2M6 6h2M6 7h1" />
<path class="Arrow_outline-bottom" stroke="#434343" d="M0 9h1M12 9h1M0 10h1M12 10h1M0 11h1M12 11h1M1 12h11" />
<path class="Arrow_edge" stroke="#ffffff" d="M1 9h11" />
<path class="Arrow_front" stroke="#cccccc" d="M1 10h11M1 11h11" />
</svg>
</button>
</div>
</div>
</div>
</div>
<style>
:root {
--pixel-size: 2px;
--grid-cell: calc( var(--pixel-size) * 16);
--bg: #9fa7e4;
}
@media( min-width: 700px ) {
:root {
--pixel-size: 3px;
}
}
@media( min-width: 1000px ) {
:root {
--pixel-size: 4px;
}
}
html, body {
height: 100%;
}
body {
background: var(--bg);
display: flex;
align-items: center;
justify-content: center;
}
.pixel-art {
image-rendering: pixelated;
}
.frame {
width: calc(var(--pixel-size) * 160);
height: calc(var(--pixel-size) * 144);
outline: var(--pixel-size) solid #fff;
z-index:1;
position:relative;
}
.camera {
width: calc(var(--pixel-size) * 160);
height: calc(var(--pixel-size) * 144);
overflow: hidden;
background: #61ddf7;
position:relative;
}
.map {
image-rendering: pixelated;
background-image: url("https://assets.codepen.io/21542/CameraDemoMap.png");
background-size: 100%;
width: calc(13 * var(--grid-cell));
height: calc(10 * var(--grid-cell));
position: relative;
}
.character {
width: calc( var(--grid-cell)* 2 );
height: calc( var(--grid-cell)* 2 );
position: absolute;
overflow:hidden;
}
.shadow {
width: calc( var(--grid-cell)* 2 );
height: calc( var(--grid-cell)* 2 );
position: absolute;
left:0;
top:0;
background: url("https://assets.codepen.io/21542/DemoRpgCharacterShadow.png") no-repeat no-repeat;
background-size: 100%;
}
.character_spritesheet {
position: absolute;
background: url("https://assets.codepen.io/21542/DemoRpgCharacter.png") no-repeat no-repeat;
background-size: 100%;
width: calc( var(--grid-cell)* 8 );
height: calc( var(--grid-cell)* 8 );
}
.character[facing="right"] .character_spritesheet {
background-position-y: calc( var(--pixel-size) * -32 );
}
.character[facing="up"] .character_spritesheet {
background-position-y: calc( var(--pixel-size) * -64 );
}
.character[facing="left"] .character_spritesheet {
background-position-y: calc( var(--pixel-size) * -96 );
}
.character[walking="true"] .character_spritesheet {
animation: walkAnimation 0.6s steps(4) infinite;
}
@keyframes walkAnimation {
from {
transform: translate3d(0%,0%,0);
}
to {
transform: translate3d(-100%,0%,0);
}
}
.dpad {
position:absolute;
right: calc(var(--pixel-size) * 2);
bottom: calc(var(--pixel-size) * 2);
width: calc(var(--pixel-size) * 37);
height: calc(var(--pixel-size) * 38);
}
.dpad-button {
appearance:none;
outline:0;
border:0;
background:transparent;
padding:0;
cursor:pointer;
}
.dpad-button svg {
display:block;
height: calc(var(--pixel-size) * 13);
}
.dpad-button.pressed .Arrow_arrow-inset { stroke:#07c2cc; }
.dpad-button.pressed .Arrow_arrow-body { stroke:#17dfea; }
.dpad-up {
position: absolute;
left: calc(var(--pixel-size) * 12);
top:0;
}
.dpad-down {
position: absolute;
bottom:var(--pixel-size);
left: calc(var(--pixel-size) * 12);
}
.dpad-left {
position: absolute;
top: calc(var(--pixel-size) * 12);
left:0;
}
.dpad-right {
position: absolute;
top: calc(var(--pixel-size) * 12);
right:0;
}
.dpad {
user-select:none;
}
.corner_topleft,
.corner_topright,
.corner_bottomleft,
.corner_bottomright {
position: absolute;
width: var(--pixel-size);
height: var(--pixel-size);
background: var(--bg);
z-index:2;
}
.corner_topleft {
top: calc(var(--pixel-size) * -1);
left: calc(var(--pixel-size) * -1);
}
.corner_topright {
top: calc(var(--pixel-size) * -1);
right: calc(var(--pixel-size) * -1);
}
.corner_bottomleft {
bottom: calc(var(--pixel-size) * -1);
left: calc(var(--pixel-size) * -1);
}
.corner_bottomright {
bottom: calc(var(--pixel-size) * -1);
right: calc(var(--pixel-size) * -1);
}
</style>
<script>
var character = document.querySelector(".character");
var map = document.querySelector(".map");
var x = 90;
var y = 34;
var held_directions = [];
var speed = 1;
const placeCharacter = () => {
var pixelSize = parseInt(
getComputedStyle(document.documentElement).getPropertyValue('--pixel-size')
);
const held_direction = held_directions[0];
if (held_direction) {
if (held_direction === directions.right) {x += speed;}
if (held_direction === directions.left) {x -= speed;}
if (held_direction === directions.down) {y += speed;}
if (held_direction === directions.up) {y -= speed;}
character.setAttribute("facing", held_direction);
}
character.setAttribute("walking", held_direction ? "true" : "false");
var leftLimit = -8;
var rightLimit = (16 * 11)+8;
var topLimit = -8 + 32;
var bottomLimit = (16 * 7);
if (x < leftLimit) { x = leftLimit; }
if (x > rightLimit) { x = rightLimit; }
if (y < topLimit) { y = topLimit; }
if (y > bottomLimit) { y = bottomLimit; }
var camera_left = pixelSize * 66;
var camera_top = pixelSize * 42;
map.style.transform = `translate3d( ${-x*pixelSize+camera_left}px, ${-y*pixelSize+camera_top}px, 0 )`;
character.style.transform = `translate3d( ${x*pixelSize}px, ${y*pixelSize}px, 0 )`;
}
const step = () => {
placeCharacter();
window.requestAnimationFrame(() => {
step();
})
}
step();
const directions = {
up: "up",
down: "down",
left: "left",
right: "right",
}
const keys = {
38: directions.up,
37: directions.left,
39: directions.right,
40: directions.down,
}
document.addEventListener("keydown", (e) => {
var dir = keys[e.which];
if (dir && held_directions.indexOf(dir) === -1) {
held_directions.unshift(dir)
}
})
document.addEventListener("keyup", (e) => {
var dir = keys[e.which];
var index = held_directions.indexOf(dir);
if (index > -1) {
held_directions.splice(index, 1)
}
});
var isPressed = false;
const removePressedAll = () => {
document.querySelectorAll(".dpad-button").forEach(d => {
d.classList.remove("pressed")
})
}
document.body.addEventListener("mousedown", () => {
isPressed = true;
})
document.body.addEventListener("mouseup", () => {
isPressed = false;
held_directions = [];
removePressedAll();
})
const handleDpadPress = (direction, click) => {
if (click) {
isPressed = true;
}
held_directions = (isPressed) ? [direction] : []
if (isPressed) {
removePressedAll();
document.querySelector(".dpad-"+direction).classList.add("pressed");
}
}
document.querySelector(".dpad-left").addEventListener("touchstart", (e) => handleDpadPress(directions.left, true));
document.querySelector(".dpad-up").addEventListener("touchstart", (e) => handleDpadPress(directions.up, true));
document.querySelector(".dpad-right").addEventListener("touchstart", (e) => handleDpadPress(directions.right, true));
document.querySelector(".dpad-down").addEventListener("touchstart", (e) => handleDpadPress(directions.down, true));
document.querySelector(".dpad-left").addEventListener("mousedown", (e) => handleDpadPress(directions.left, true));
document.querySelector(".dpad-up").addEventListener("mousedown", (e) => handleDpadPress(directions.up, true));
document.querySelector(".dpad-right").addEventListener("mousedown", (e) => handleDpadPress(directions.right, true));
document.querySelector(".dpad-down").addEventListener("mousedown", (e) => handleDpadPress(directions.down, true));
document.querySelector(".dpad-left").addEventListener("mouseover", (e) => handleDpadPress(directions.left));
document.querySelector(".dpad-up").addEventListener("mouseover", (e) => handleDpadPress(directions.up));
document.querySelector(".dpad-right").addEventListener("mouseover", (e) => handleDpadPress(directions.right));
document.querySelector(".dpad-down").addEventListener("mouseover", (e) => handleDpadPress(directions.down));
document.querySelector(".dpad-left").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
document.querySelector(".dpad-up").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
document.querySelector(".dpad-right").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
document.querySelector(".dpad-down").addEventListener("touchend", () => { held_directions = []; removePressedAll(); });
</script>