이전 글 (Vue+Canvas)에 더해서 p5.js를 활용하여 간단한 게임을 작성해보도록 하겠습니다.
yarn create cue
yarn
yarn format
yarn dev
<script setup lang="ts">
import { RouterView } from 'vue-router'
</script>
<template>
<RouterView />
</template>
<template>
<div id="game-container" ref="gameContainer"></div>
</template>
<script>
export default {
name: 'Game', // Vue 컴포넌트의 이름을 'Game'으로 설정
mounted() {
this.sketch = new p5(this.gameSetup) // 컴포넌트가 마운트될 때 p5 인스턴스를 생성하고 gameSetup 메서드로 초기화
},
beforeDestroy() {
if (this.sketch) {
this.sketch.remove() // 컴포넌트가 파괴되기 전 p5 인스턴스가 존재하면 제거
}
},
methods: {
gameSetup(p) {
let player
let obstacles = []
let score = 0
let gameOver = false
p.setup = function () {
p.createCanvas(800, 400).parent('game-container') // p5 캔버스를 생성하고 game-container div 요소에 붙임
player = new Player() // Player 인스턴스를 생성
}
p.draw = function () {
if (gameOver) {
p.background(200, 0, 0) // 게임 오버 시 배경을 빨간색으로 설정
p.textAlign(p.CENTER)
p.textSize(32)
p.fill(255)
p.text('Game Over', p.width / 2, p.height / 2) // 화면 중앙에 'Game Over' 텍스트 표시
p.text(`Score: ${score}`, p.width / 2, p.height / 2 + 40) // 점수 표시
return
}
p.background(220) // 게임 진행 중 배경을 회색으로 설정
player.update() // player 상태 업데이트
player.show() // player 그리기
if (p.frameCount % 60 === 0) {
obstacles.push(new Obstacle()) // 60프레임마다 새로운 장애물 생성
score++ // 점수 증가
}
for (let i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].update() // 각 장애물 상태 업데이트
obstacles[i].show() // 각 장애물 그리기
if (player.hits(obstacles[i])) {
gameOver = true // 플레이어가 장애물과 충돌하면 게임 오버
}
if (obstacles[i].offscreen()) {
obstacles.splice(i, 1) // 화면 밖으로 나간 장애물 제거
}
}
p.textSize(16)
p.fill(0)
p.text(`Score: ${score}`, p.width - 100, 30) // 현재 점수를 화면에 표시
}
p.keyPressed = function () {
if (p.key == ' ' && !gameOver) {
player.jump() // 스페이스바를 누르면 플레이어가 점프
}
if (gameOver && p.key == 'R') {
score = 0
gameOver = false
obstacles = []
player = new Player() // 'R' 키를 누르면 게임이 리셋됨
}
}
class Player {
constructor() {
this.r = 50 // 플레이어의 크기 설정
this.x = 50 // 플레이어의 초기 x 위치 설정
this.y = p.height - this.r // 플레이어의 초기 y 위치 설정
this.vy = 0 // 플레이어의 초기 속도 설정
this.gravity = 1 // 중력 설정
}
jump() {
if (this.y == p.height - this.r) {
this.vy = -20 // 플레이어가 바닥에 있을 때 점프
}
}
hits(obstacle) {
return p.collideRectRect(
this.x,
this.y,
this.r,
this.r,
obstacle.x,
obstacle.y,
obstacle.w,
obstacle.h
) // 플레이어와 장애물의 충돌 검사
}
update() {
this.y += this.vy // 플레이어의 y 위치 업데이트
this.vy += this.gravity // 속도에 중력을 더함
this.y = p.constrain(this.y, 0, p.height - this.r) // 플레이어가 화면을 벗어나지 않도록 위치 제한
}
show() {
p.fill(255, 50, 50)
p.rect(this.x, this.y, this.r, this.r) // 플레이어 그리기
}
}
class Obstacle {
constructor() {
this.w = 40 // 장애물의 너비 설정
this.h = p.random(50, 150) // 장애물의 높이를 무작위로 설정
this.x = p.width // 장애물의 초기 x 위치 설정
this.y = p.height - this.h // 장애물의 y 위치 설정
this.speed = 6 // 장애물의 이동 속도 설정
}
update() {
this.x -= this.speed // 장애물의 x 위치 업데이트
}
offscreen() {
return this.x < -this.w // 장애물이 화면 밖으로 나갔는지 확인
}
show() {
p.fill(50, 50, 255)
p.rect(this.x, this.y, this.w, this.h) // 장애물 그리기
}
}
}
}
}
</script>
<style scoped>
#game-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh; /* 게임 컨테이너를 화면 중앙에 배치하고 전체 높이를 100vh로 설정 */
}
</style>
<template>
<div id="game-container" ref="gameContainer"></div>
</template>
template
태그 안에 div
요소가 있으며, id
가 game-container
로 설정되어 있습니다.ref="gameContainer"
는 Vue.js에서 이 요소에 직접 접근할 수 있게 하는 참조(reference)를 설정합니다.<script>
export default {
name: 'Game', // Vue 컴포넌트의 이름을 'Game'으로 설정
mounted() {
this.sketch = new p5(this.gameSetup) // 컴포넌트가 마운트될 때 p5 인스턴스를 생성하고 gameSetup 메서드로 초기화
},
beforeDestroy() {
if (this.sketch) {
this.sketch.remove() // 컴포넌트가 파괴되기 전 p5 인스턴스가 존재하면 제거
}
},
methods: {
gameSetup(p) {
let player
let obstacles = []
let score = 0
let gameOver = false
p.setup = function () {
p.createCanvas(800, 400).parent('game-container') // p5 캔버스를 생성하고 game-container div 요소에 붙임
player = new Player() // Player 인스턴스를 생성
}
p.draw = function () {
if (gameOver) {
p.background(200, 0, 0) // 게임 오버 시 배경을 빨간색으로 설정
p.textAlign(p.CENTER)
p.textSize(32)
p.fill(255)
p.text('Game Over', p.width / 2, p.height / 2) // 화면 중앙에 'Game Over' 텍스트 표시
p.text(`Score: ${score}`, p.width / 2, p.height / 2 + 40) // 점수 표시
return
}
p.background(220) // 게임 진행 중 배경을 회색으로 설정
player.update() // player 상태 업데이트
player.show() // player 그리기
if (p.frameCount % 60 === 0) {
obstacles.push(new Obstacle()) // 60프레임마다 새로운 장애물 생성
score++ // 점수 증가
}
for (let i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].update() // 각 장애물 상태 업데이트
obstacles[i].show() // 각 장애물 그리기
if (player.hits(obstacles[i])) {
gameOver = true // 플레이어가 장애물과 충돌하면 게임 오버
}
if (obstacles[i].offscreen()) {
obstacles.splice(i, 1) // 화면 밖으로 나간 장애물 제거
}
}
p.textSize(16)
p.fill(0)
p.text(`Score: ${score}`, p.width - 100, 30) // 현재 점수를 화면에 표시
}
p.keyPressed = function () {
if (p.key == ' ' && !gameOver) {
player.jump() // 스페이스바를 누르면 플레이어가 점프
}
if (gameOver && p.key == 'R') {
score = 0
gameOver = false
obstacles = []
player = new Player() // 'R' 키를 누르면 게임이 리셋됨
}
}
class Player {
constructor() {
this.r = 50 // 플레이어의 크기 설정
this.x = 50 // 플레이어의 초기 x 위치 설정
this.y = p.height - this.r // 플레이어의 초기 y 위치 설정
this.vy = 0 // 플레이어의 초기 속도 설정
this.gravity = 1 // 중력 설정
}
jump() {
if (this.y == p.height - this.r) {
this.vy = -20 // 플레이어가 바닥에 있을 때 점프
}
}
hits(obstacle) {
return p.collideRectRect(
this.x,
this.y,
this.r,
this.r,
obstacle.x,
obstacle.y,
obstacle.w,
obstacle.h
) // 플레이어와 장애물의 충돌 검사
}
update() {
this.y += this.vy // 플레이어의 y 위치 업데이트
this.vy += this.gravity // 속도에 중력을 더함
this.y = p.constrain(this.y, 0, p.height - this.r) // 플레이어가 화면을 벗어나지 않도록 위치 제한
}
show() {
p.fill(255, 50, 50)
p.rect(this.x, this.y, this.r, this.r) // 플레이어 그리기
}
}
class Obstacle {
constructor() {
this.w = 40 // 장애물의 너비 설정
this.h = p.random(50, 150) // 장애물의 높이를 무작위로 설정
this.x = p.width // 장애물의 초기 x 위치 설정
this.y = p.height - this.h // 장애물의 y 위치 설정
this.speed = 6 // 장애물의 이동 속도 설정
}
update() {
this.x -= this.speed // 장애물의 x 위치 업데이트
}
offscreen() {
return this.x < -this.w // 장애물이 화면 밖으로 나갔는지 확인
}
show() {
p.fill(50, 50, 255)
p.rect(this.x, this.y, this.w, this.h) // 장애물 그리기
}
}
}
}
}
</script>
export default { name: 'Game', ... }
: Vue 컴포넌트를 정의합니다. 컴포넌트의 이름은 Game
입니다.mounted() { ... }
: 컴포넌트가 DOM에 마운트될 때 호출됩니다. 이때 p5 인스턴스를 생성하여 gameSetup
메서드로 초기화합니다.beforeDestroy() { ... }
: 컴포넌트가 파괴되기 전에 호출됩니다. p5 인스턴스가 존재하면 제거합니다.methods: { gameSetup(p) { ... } }
: p5 인스턴스의 설정과 게임 로직을 정의하는 메서드입니다.p.setup()
: p5의 setup
함수로, 캔버스를 생성하고 플레이어 인스턴스를 초기화합니다.p.draw()
: p5의 draw
함수로, 매 프레임마다 게임 화면을 업데이트하고 그립니다. 게임이 진행 중이면 배경을 그리고 플레이어와 장애물을 업데이트 및 표시합니다. 게임 오버 시 "Game Over" 메시지를 표시합니다.p.keyPressed()
: 키가 눌렸을 때 호출됩니다. 스페이스바를 누르면 플레이어가 점프하고, 'R' 키를 누르면 게임이 리셋됩니다.constructor()
: 플레이어의 속성을 초기화합니다.jump()
: 플레이어가 점프할 때 호출됩니다.hits(obstacle)
: 플레이어가 장애물과 충돌했는지 확인합니다.update()
: 플레이어의 위치를 업데이트합니다.show()
: 플레이어를 화면에 그립니다.constructor()
: 장애물의 속성을 초기화합니다.update()
: 장애물의 위치를 업데이트합니다.offscreen()
: 장애물이 화면 밖으로 나갔는지 확인합니다.show()
: 장애물을 화면에 그립니다.<style scoped>
#game-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh; /* 게임 컨테이너를 화면 중앙에 배치하고 전체 높이를 100vh로 설정 */
}
</style>
#game-container
: 게임 컨테이너를 화면 중앙에 배치하고 높이를 화면 전체 높이(100vh)로 설정합니다.<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdn.jsdelivr.net/npm/p5.collide2d@0.7.3/p5.collide2d.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>