#app
#canvas_container
#info
h1
|408 Request Timeout <br>
small| - The Game
p|Use the handle to rotate the hourglass.
p|Watch out! It is broken.
p|Don't let the pixe... sand get out.
p|Remember - You have limited time.
button(@click='restartGame()' v-if="gameOver")|Restart
#gui
| Time limit:<br>
span| {{ timeLimit }}s
div.range-slider
p|Controler:
input(v-model='currAngle' type='range' min="-25" max="25" class='range-slider__range')
#timeout(:class="gameOver ? 'shown' : ''")| {{message}}
View Compiled
$mainColor: #4a08a0;
$range-width: 100% !default;
$range-handle-size: 20px !default;
$range-track-height: 10px !default;
$range-label-width: 60px !default;
body {
background: #f6f6f6;
background: -moz-linear-gradient(-45deg, #f6f6f6 0%, #d1d1d1 100%);
background: -webkit-linear-gradient(-45deg, #f6f6f6 0%, #d1d1d1 100%);
background: linear-gradient(135deg, #f6f6f6 0%, #d1d1d1 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#f6f6f6",
endColorstr="#d1d1d1",
GradientType=1);
color: rgb(0, 0, 0);
// color: #fff;
// background-color: #0000fe;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
position: relative;
font-family: 'VT323', monospace;
font-size: 22px;
p {
margin: 0;
line-height: 1;
&:first-of-type {
margin-top: 20px;
}
}
input {
font-family: 'VT323', monospace;
font-size: 50px;
border: none;
background: none;
width: 100px;
}
canvas {
width: 100vw;
height: 100vh;
top: 0;
left: 0;
position: absolute;
cursor: pointer;
}
#info {
position: absolute;
top: 7vh;
left: 5vw;
}
.range-slider {
margin: 60px 0 0 0%;
position: absolute;
top: 50%;
width: 30%;
transform: translateY(-50%);
right: 5%;
text-align: right;
}
.range-slider__range {
-webkit-appearance: none;
width: calc(100% - (#{$range-label-width + 13px}));
height: $range-track-height;
border-radius: 5px;
background: #ccc;
outline: none;
padding: 0;
margin: 0;
&::-webkit-slider-thumb {
appearance: none;
width: $range-handle-size;
height: $range-handle-size;
border-radius: 50%;
background: #000;
cursor: pointer;
transition: background .15s ease-in-out;
&:hover {
background: $mainColor;
}
}
&:active::-webkit-slider-thumb {
background: $mainColor;
}
&::-moz-range-thumb {
width: $range-handle-size;
height: $range-handle-size;
border: 0;
border-radius: 50%;
background: #000;
cursor: pointer;
transition: background .15s ease-in-out;
&:hover {
background: $mainColor;
}
}
}
h1 {
font-weight: 400;
margin: 0;
line-height: 1;
small {
padding-bottom: 30px;
}
}
#gui {
position: absolute;
top: 15%;
right: 5%;
z-index: 999;
text-align: right;
font-size: 24px;
span {
font-size: 60px;
margin-left: 5px;
}
}
#timeout {
position: absolute;
left: 50%;
top: 50%;
opacity: 0;
pointer-events: none;
transform: translate(-50%, -55%) scale3d(1, 1, 1) rotate(-10deg);
transition: all 1s cubic-bezier(0.455, 0.03, 0.515, 0.955);
z-index: 999;
font-size: 10vw;
color: #fff;
background-color: rgba(0, 0, 0, .9);
padding: 0px 35px;
line-height: 1;
&.shown {
opacity: 1;
transform: translate(-50%, -55%) scale3d(1.3, 1.3, 1) rotate(-1deg);
}
}
button {
border: none;
padding: 10px 20px;
margin-top: 20px;
box-shadow: 10px 10px 0 rgba(0, 0, 0, 1);
font-family: 'VT323', monospace;
text-transform: uppercase;
cursor: pointer;
font-size: 30px;
background-color: $mainColor;
transition: all cubic-bezier(0.19, 1, 0.22, 1) .3s;
color: #fff;
&:hover,
&:focus {
background-color: darken($mainColor, 10%);
}
}
}
@media screen and (max-width: 990px) {
body {
font-size: 12px;
#info {
bottom: 25px;
left: 25px;
top: 20px;
}
#gui {
top: 20px;
}
h1 small {
display: none;
}
p:first-of-type {
margin-top: 0;
}
.range-slider {
bottom: 45px;
top: initial;
width: 100%;
right: 35px;
transform: translateY(0);
}
#timeout {
padding: 0px 12px;
}
}
}
View Compiled
/*A physics mini game made as a part of Codepen.io Challenge. Made with p5, Matter and Vue.*/
new Vue({
el: '#app',
data: {
currAngle: 0,
myp5: null,
allowRotate: true,
gameOver: false,
inGame: false,
timeLimit: 11,
timer: null,
sandColor: '#4a08a0',
boundsColor: '#BBBBBB',
message: 'TIME OUT!',
points: 0,
hScale: window.innerHeight / 800
},
watch: {
points(val, newVal) {
if (val >= 110 && !this.gameOver) {
this.gameOver = true
this.message = 'YOU WIN!'
clearInterval(this.timer)
}
},
currAngle(val) {
if (val !== 0) {
if (!this.inGame) {
this.inGame = true
this.timer = setInterval(() => {
this.timeLimit -= 1
}, 1000)
}
}
},
timeLimit(val) {
if (val <= 0) {
clearInterval(this.timer)
this.gameOver = true
this.message = 'TIME OUT!'
}
}
},
methods: {
restartGame() {
window.location.reload(true)
},
initGame() {
let bod = document.getElementsByTagName('body')[0]
bod.addEventListener('click', function () {
_this.allowRotate = true
})
let _this = this
function sketchInit(sketch) {
let engine
let world
let stack
let sand = []
let pixels = []
let pixels2 = []
const SAND_COUNT = 110
const SAND_SIZE = 12
const PIXEL_SIZE = 20
const Engine = Matter.Engine
const Render = Matter.Render
const World = Matter.World
const Bodies = Matter.Bodies
const Composite = Matter.Composite
const Events = Matter.Events
const LEFT_SIDE = [{
x: -40,
y: -20
},
{
x: -40,
y: 0
},
{
x: -60,
y: 0
},
// {
// x: -60,
// y: -20
// },
{
x: -60,
y: -40
},
{
x: -60,
y: 20
},
{
x: -80,
y: 20
},
{
x: -80,
y: 40
},
{
x: -80,
y: -40
},
{
x: -80,
y: -60
},
{
x: -100,
y: -80
},
{
x: -100,
y: -60
},
{
x: -100,
y: 60
},
{
x: -100,
y: 40
},
{
x: -120,
y: -100
},
// {
// x: -120,
// y: -80
// },
{
x: -120,
y: 60
},
{
x: -120,
y: 80
},
{
x: -120,
y: 100
},
{
x: -120,
y: 120
},
{
x: -120,
y: 140
},
{
x: -120,
y: 160
},
{
x: -120,
y: -120
},
{
x: -120,
y: -140
},
{
x: -120,
y: -160
},
{
x: -120,
y: -180
},
{
x: -140,
y: 200
},
{
x: -140,
y: -220
}
]
const RIGHT_SIDE = [{
x: 20,
y: 0
},
{
x: 40,
y: 0
},
{
x: 20,
y: -20
},
{
x: 40,
y: -20
},
{
x: 60,
y: 40
},
{
x: 40,
y: 20
},
{
x: 60,
y: 20
},
// {
// x: 80,
// y: 60
// },
{
x: 80,
y: 40
},
// {
// x: 100,
// y: 60
// },
{
x: 100,
y: 80
},
{
x: 100,
y: 100
},
{
x: 100,
y: 120
},
{
x: 100,
y: 140
},
{
x: 100,
y: 160
},
{
x: 100,
y: -80
},
{
x: 100,
y: -100
},
{
x: 80,
y: -60
},
{
x: 60,
y: -60
},
{
x: 60,
y: -40
},
{
x: 40,
y: -40
},
{
x: 100,
y: -120
},
{
x: 100,
y: -140
},
{
x: 100,
y: -160
},
{
x: 100,
y: -180
},
{
x: 80,
y: -80
},
{
x: 120,
y: 200
},
{
x: 120,
y: -220
}
]
sketch.setup = function () {
sketch.createCanvas(window.innerWidth, window.innerHeight)
sketch.frameRate(30)
engine = Engine.create()
world = engine.world
var render = Render.create({
element: document.getElementById('canvas_container'),
engine: engine,
options: {
width: window.innerWidth,
height: window.innerHeight,
wireframes: false,
background: 'transparent'
}
})
Render.run(render)
World.add(world, [
Bodies.rectangle(window.innerWidth / 2, 0, window.innerWidth, 50, {
isStatic: true,
render: {
fillStyle: _this.boundsColor,
}
}),
Bodies.rectangle(window.innerWidth / 2, window.innerHeight, window.innerWidth, 50, {
isStatic: true,
id: 999,
render: {
fillStyle: _this.boundsColor,
}
}),
Bodies.rectangle(window.innerWidth, window.innerHeight / 2, 50, window.innerHeight, {
isStatic: true,
render: {
fillStyle: _this.boundsColor,
}
}),
Bodies.rectangle(0, window.innerHeight / 2, 50, window.innerHeight, {
isStatic: true,
render: {
fillStyle: _this.boundsColor,
}
})
])
stack = Composite.create()
new Edge(-110, -200, 280, PIXEL_SIZE)
new Edge(-110, -240, 280, PIXEL_SIZE)
new Edge(-110, 180, 280, PIXEL_SIZE)
new Edge(-110, 220, 280, PIXEL_SIZE)
let strongFloor = Bodies.rectangle(-10, 195, 270, 50, {
isStatic: true,
density: 100,
render: {
fillStyle: 'transparent',
}
})
let strongWall = Bodies.rectangle(-120, 135, 20, 170, {
isStatic: true,
density: 100,
render: {
fillStyle: 'transparent',
}
})
let strongWall2 = Bodies.rectangle(100, 150, 20, 160, {
isStatic: true,
density: 100,
render: {
fillStyle: 'transparent',
}
})
let sensor = Bodies.rectangle(-10, -150, 200, 100, {
label: 'sensor',
isStatic: true,
isSensor: true,
render: {
fillStyle: 'transparent',
}
})
let sensor2 = Bodies.rectangle(-10, 110, 200, 200, {
label: 'sensor',
isStatic: true,
isSensor: true,
render: {
fillStyle: 'transparent',
}
})
World.add(world, [sensor, sensor2, strongWall, strongWall2, strongFloor])
Composite.add(stack, [sensor, sensor2, strongWall, strongWall2, strongFloor])
for (let x = 0; x < SAND_COUNT; x++) {
// sand.push(new Sand(sketch.random(-80, 80), sketch.random(-120, -180)))
sand.push(new Sand(sketch.random(-10, 8) * 10, sketch.random(6, 17) * 10))
}
for (let x = 0; x < LEFT_SIDE.length; x++) {
pixels.push(new HourglassPixel(LEFT_SIDE[x].x, LEFT_SIDE[x].y))
pixels2.push(new HourglassPixel(RIGHT_SIDE[x].x, RIGHT_SIDE[x].y))
}
Composite.translate(stack, {
x: window.innerWidth / 2,
y: window.innerHeight / 2
}, true);
Composite.scale(stack, _this.hScale, _this.hScale, {
x: window.innerWidth / 2,
y: window.innerHeight / 2
}, true)
Events.on(engine, 'collisionStart', function (event) {
const pairs = event.pairs;
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i];
if (pair.bodyA.label === "sand" && pair.bodyB.id == 999) {
pair.bodyA.render.fillStyle = '#FF0000'
pair.bodyB.render.fillStyle = '#FF0000'
if (!_this.gameOver) {
_this.gameOver = true
_this.message = 'Game Over'
clearInterval(_this.timer)
}
}
}
})
Events.on(engine, 'collisionEnd', function (event) {
var pairs = event.pairs;
for (var i = 0, j = pairs.length; i != j; ++i) {
var pair = pairs[i];
if (pair.bodyA.label == "sensor") {
// _this.sandColor = '#00FF00'
_this.points++
pair.bodyB.render.fillStyle = _this.sandColor;
}
}
});
Engine.run(engine)
}
sketch.draw = function () {
sketch.clear()
if (!_this.gameOver) {
Composite.rotate(stack, _this.currAngle * 0.001, {
x: window.innerWidth / 2,
y: window.innerHeight / 2
}, true);
}
}
class Sand {
constructor(x, y) {
this.body = Bodies.rectangle(x, y, SAND_SIZE, SAND_SIZE, {
restitution: 0,
friction: .1,
// frictionAir: .1,
density: .000001,
label: 'sand',
render: {
fillStyle: _this.sandColor
}
})
World.add(world, this.body)
Composite.add(stack, this.body)
}
}
class Edge {
constructor(x, y, w, h) {
this.body = Bodies.rectangle(x + 100, y, w, h, {
isStatic: true,
restitution: 0,
friction: 1,
density: 10,
render: {
fillStyle: '#000000'
}
})
this.w = w
this.h = h
World.add(world, this.body)
Composite.add(stack, this.body)
}
}
class HourglassPixel {
constructor(x, y) {
this.pixel = Bodies.rectangle(x, y, PIXEL_SIZE, PIXEL_SIZE, {
friction: .2,
restitution: 0,
isStatic: true,
density: 10,
render: {
fillStyle: '#000000'
}
})
World.add(world, this.pixel)
Composite.add(stack, this.pixel)
}
}
}
let canv = (sketch) => {
sketchInit(sketch)
}
this.myp5 = new p5(canv, 'canvas_container')
}
},
mounted() {
this.initGame()
}
})
View Compiled
This Pen doesn't use any external CSS resources.