Skip to content

Commit

Permalink
Merge pull request #33 from Crazy-Cow/feature/CD-85
Browse files Browse the repository at this point in the history
feat: 물리엔진 제거 [CD-85]
  • Loading branch information
ddubbu-dev authored Nov 16, 2024
2 parents 8a964a0 + b2046c6 commit 64b6b74
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 226 deletions.
89 changes: 3 additions & 86 deletions src/game/maps/common.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import * as CANNON from 'cannon-es'
import { Character, Position } from '../objects/player'

const GROUND_SIZE = {
x: 25,
y: 1,
z: 25,
}
const GROUND_POS = {
x: 0,
y: -1, // y축에서 바닥이 약간 아래로 설정됩니다.
Expand All @@ -14,105 +8,33 @@ const GROUND_POS = {

export class CommonMap {
private updateInterval: number
world: CANNON.World
characters: Character[] = []
characterMaterial: CANNON.Material
groundMaterial: CANNON.Material
private intervalId?: NodeJS.Timeout

constructor() {
this.updateInterval = 1 / 60 // 60 FPS
this.world = new CANNON.World()
this.characterMaterial = new CANNON.Material('characterMaterial')
this.groundMaterial = new CANNON.Material('groundMaterial')

this.initWorld()
}

private initWorld() {
this.world.gravity.set(0, -9.82, 0) // 중력 설정 (y축 방향으로 -9.82)
this.createGround()
this.setCollision()
}

private setCollision() {
this.world.addContactMaterial(
new CANNON.ContactMaterial(
this.characterMaterial,
this.groundMaterial,
{ friction: 0.9, restitution: 0.0 }
)
)
}

private createGround() {
const groundBody = new CANNON.Body({
mass: 0, // 바닥은 움직이지 않도록 질량을 0으로 설정
material: this.groundMaterial,
shape: new CANNON.Box(
new CANNON.Vec3(GROUND_SIZE.x, GROUND_SIZE.y, GROUND_SIZE.z)
),
position: new CANNON.Vec3(GROUND_POS.x, GROUND_POS.y, GROUND_POS.z),
})

this.world.addBody(groundBody)
return groundBody
}

private generateRandomPosition(): Position {
// TODO: 안겹치게 생성되도록
return {
x: GROUND_POS.x + Math.random() * 10,
y: GROUND_POS.y + 2,
z: GROUND_POS.z + Math.random() * 10,
}
}

private resetPosition(character: Character) {
const newPosition = this.generateRandomPosition()
if (character.cannonBody) {
character.cannonBody.position.set(
newPosition.x,
newPosition.y,
newPosition.z
)
character.cannonBody.velocity.set(0, 0, 0) // 속도도 초기화
character.cannonBody.angularVelocity.set(0, 0, 0) // 각속도 초기화
}
character.position = newPosition
character.velocity = { x: 0, y: 0, z: 0 }
character.isOnGround = true
console.log(`Character ${character.id} reset to initial position.`)
}

private checkAndUpdatePosition(character: Character) {
if (
Math.abs(character.position.x) > GROUND_SIZE.x + 5 ||
Math.abs(character.position.y) > GROUND_SIZE.y + 5 ||
Math.abs(character.position.z) > GROUND_SIZE.z + 5
) {
this.resetPosition(character)
} else {
character.updatePosition()
}
}

findCharacter(id: string) {
return this.characters.find((char) => char.id === id)
}

addCharacter(id: string) {
const position = this.generateRandomPosition()
const character = new Character(id, position, this.characterMaterial)

const character = new Character(id, position)
this.characters.push(character)
if (character.cannonBody) this.world.addBody(character.cannonBody)
}

removeCharacter(id: string) {
const character = this.characters.find((char) => char.id === id)
if (character && character.cannonBody) {
this.world.removeBody(character.cannonBody)
}
this.characters = this.characters.filter((char) => char.id !== id)
}

Expand All @@ -124,17 +46,12 @@ export class CommonMap {
hairColor: char.hairColor,
bellyColor: char.bellyColor,
velocity: char.velocity,
isOnGround: char.isOnGround,
hasTail: char.hasTail,
facingAngleRad: char.facingAngleRad,
}))
}

updateGameState() {
this.world.step(this.updateInterval)
this.characters.forEach((character) => {
this.checkAndUpdatePosition(character)
})
// TODO: 검증로직
}

startGameLoop(emitter: (data: unknown) => void) {
Expand Down
45 changes: 4 additions & 41 deletions src/game/objects/player.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import * as CANNON from 'cannon-es'

export type Position = { x: number; y: number; z: number }

export type Directions = {
Expand All @@ -9,69 +7,34 @@ export type Directions = {
right: boolean
}

const CHARACTER_SIZE = 1

export class Character {
id: string
position: Position
velocity: Position
acceleration: Position

bodyColor: string
hairColor: string
bellyColor: string
isOnGround: boolean
directions: Directions
velocity: Position
hasTail: boolean
cannonBody: CANNON.Body
angleRad: number
shift: boolean
jump: boolean

facingAngleRad: number
isOnGround: boolean
shift: boolean
isBeingStolen: boolean

constructor(id: string, position: Position, material: CANNON.Material) {
constructor(id: string, position: Position) {
this.id = id
this.position = position
this.bodyColor = this.generateRandomHexColor()
this.hairColor = this.generateRandomHexColor()
this.bellyColor = this.generateRandomHexColor()
this.velocity = { x: 0, y: 0, z: 0 }
this.acceleration = { x: 0, y: 0, z: 0 }
this.isOnGround = true
this.directions = { up: false, down: false, left: false, right: false }
this.hasTail = Math.random() > 0.5 // TODO // characters.length % 2 === 0,
this.shift = false
this.isBeingStolen = false
this.angleRad = 0 // 임시값

const body = new CANNON.Body({
mass: 1,
position: new CANNON.Vec3(position.x, position.y, position.z),
material,
shape: new CANNON.Sphere(CHARACTER_SIZE),
})
this.cannonBody = body
}

private generateRandomHexColor(): string {
const color = Math.floor(Math.random() * 16777215).toString(16)
return '#' + color.padStart(6, '0')
}

updatePosition() {
if (this.cannonBody) {
this.position = {
x: this.cannonBody.position.x,
y: this.cannonBody.position.y,
z: this.cannonBody.position.z,
}
this.velocity = {
x: this.cannonBody.velocity.x,
y: this.cannonBody.velocity.y,
z: this.cannonBody.velocity.z,
}
}
}
}
23 changes: 3 additions & 20 deletions src/game/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Server, Socket } from 'socket.io'
import { Directions } from '../objects/player'
import { SocketOnEvtData } from '../../socket/type'
import { SOCKET_ON_EVT_TYPE } from '../../socket/constant'
import { TailTagMap, CommonMap } from '../maps'
import socketHandler from './index.util'
import { SocketMoveData } from './index.type'

const gameWorld: CommonMap = new TailTagMap() // TODO: 라운드 신규 생성 시 주입 필요

Expand All @@ -18,8 +18,6 @@ class SocketImplement {

private register = () => {
this.socket.on('move', this.handleMove)
this.socket.on('jump', this.handleJump)
this.socket.on('shift', this.handleSift)
this.socket.on('disconnect', this.handleDisconnect)
this.logger('[ingame server] register complete!')
}
Expand All @@ -29,24 +27,9 @@ class SocketImplement {
this.socket.emit('characters', gameWorld.convertGameState())
}

handleMove = (directions: Directions) => {
handleMove = (data: SocketMoveData) => {
const character = gameWorld.findCharacter(this.socket.id)
socketHandler.handleMove(character, directions)
}

handleJump = (jump: boolean) => {
const character = gameWorld.findCharacter(this.socket.id)
socketHandler.handleJump(character, jump)
}

handleAngle = (angleRad: number) => {
const character = gameWorld.findCharacter(this.socket.id)
socketHandler.handleAngle(character, angleRad)
}

handleSift = (shift: boolean) => {
const character = gameWorld.findCharacter(this.socket.id)
socketHandler.handleSift(character, shift)
socketHandler.handleMove(character, data)
}

handleDisconnect = () => {
Expand Down
14 changes: 14 additions & 0 deletions src/game/server/index.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Position } from 'game/objects/player'

type ClientCharacter = {
id: string
position: Position
velocity: Position
isOnGround: boolean
}

export type SocketMoveData = {
// TODO: 시간정보 delta, datetime 정보 발생 시점
shift: boolean
character: ClientCharacter
}
86 changes: 7 additions & 79 deletions src/game/server/index.util.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,13 @@
import { Character, Directions } from '../objects/player'
import * as CANNON from 'cannon-es'
import { Character } from 'game/objects/player'
import { SocketMoveData } from './index.type'

const MAX_SPEED = 10

const handleMove = (character: Character, directions: Directions) => {
if (character && character.cannonBody) {
let angleRad = character.angleRad || 0

// -π ~ π 범위로 각도를 제한
angleRad = ((angleRad + Math.PI) % (2 * Math.PI)) - Math.PI

// 카메라 각도에 따른 방향 벡터 계산
const forwardX = Math.sin(angleRad)
const forwardZ = Math.cos(angleRad)
const rightX = Math.cos(angleRad)
const rightZ = -Math.sin(angleRad)

let targetVelocityX = 0
let targetVelocityZ = 0

if (directions.up) {
targetVelocityX += forwardX * MAX_SPEED
targetVelocityZ += forwardZ * MAX_SPEED
}
if (directions.down) {
targetVelocityX -= forwardX * MAX_SPEED
targetVelocityZ -= forwardZ * MAX_SPEED
}
if (directions.left) {
targetVelocityX += rightX * MAX_SPEED
targetVelocityZ += rightZ * MAX_SPEED
}
if (directions.right) {
targetVelocityX -= rightX * MAX_SPEED
targetVelocityZ -= rightZ * MAX_SPEED
}

// 현재 속도와 목표 속도 간의 차이를 줄여 이동이 부드럽게 되도록 설정
character.cannonBody.velocity.x = targetVelocityX
character.cannonBody.velocity.z = targetVelocityZ

// 키가 떼어진 상태라면 속도를 완전히 0으로 설정
if (targetVelocityX === 0 && targetVelocityZ === 0) {
character.cannonBody.velocity.x = 0
character.cannonBody.velocity.z = 0
character.cannonBody.angularVelocity.set(0, 0, 0) // 각속도도 0으로 설정
}

const facingAngleRad = Math.atan2(
character.cannonBody.velocity.x,
character.cannonBody.velocity.z
)
character.facingAngleRad = facingAngleRad
}
}

const handleJump = (character: Character, jump: boolean) => {
if (character && jump && character.cannonBody && character.isOnGround) {
character.cannonBody.applyImpulse(
new CANNON.Vec3(0, 5, 0),
character.cannonBody.position
)
character.isOnGround = false
}
}

const handleAngle = (character: Character, angleRad: number) => {
if (character) {
character.angleRad = angleRad
}
}

const handleSift = (character: Character, shift: boolean) => {
if (character) {
character.shift = shift
}
const handleMove = (character: Character, data: SocketMoveData) => {
character.shift = data.shift
character.position = data.character.position
character.velocity = data.character.velocity
character.isOnGround = data.character.isOnGround
}

export default {
handleMove,
handleJump,
handleAngle,
handleSift,
}

0 comments on commit 64b6b74

Please sign in to comment.