펑션과 코딩의 행방불명

[JS Canvas] 힘을 받은 물체의 궤적 시뮬레이터 본문

Web Projects/Frontend

[JS Canvas] 힘을 받은 물체의 궤적 시뮬레이터

Function_Develop 2022. 9. 17. 14:03

어제 동아리에서 형이 Matlab을 이용하여 각도와 힘의 크기를 이용하여 물체의 궤도를 계산하는 프로그램을 만들 것을 보고 나도 한번 만들고 싶었다. 요즘 나의 관심사는 Javascript이기 때문에 나는 Matlab 대신 JS를 이용하여 프로그램을 재구성을 해보았다.

 

<!--index.html-->
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <style>
        canvas {
            border: 3px solid rgb(0,0,0);
        }
        </style>
        <title>Canon</title>
    </head>
    <body>
        <canvas id="canvas" width="1000px" height="800px"></canvas>
        <script src="script.js"></script>
    </body>
    
</html>

항상 그래왔듯이 Canvas를 이용하여 만들었다.

 

const canvas = document.getElementById("canvas")
const ctx = canvas.getContext("2d")

//상수 설정
const WIDTH = canvas.width
const HEIGHT = canvas.height
//중력 가속도 상수
const GRAVITY_SCALE = 10

const print = (t) => console.log(t)

//현재 각도
let degree = 0

//벡터 클래스
class Vector {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    add(b) {
        this.x += b.x
        this.y += b.y
    }
    clone() {
        return new Vector(this.x, this.y)
    }
}

매일 써왔던 코드라서 간단하게 설명하자면,  코드가 길어지면 복잡해지니까 WIDTH와 HEIGHT 상수를 설정한다. 그리고 물체에 작용하는 중력가속도를 상수로 선언해 준다.

그리고 degree는 것은 마우스의 위치가 변하면 어떤 각도로 발사할지 알려주기 위한 변수이다.

 

 

const ExperimentObjects = []//물체를 담을 리스트
let ToDestroyObjects = []	//삭제할 물체의 인덱스를 담을 리스트

function render() {
    ctx.clearRect(0, 0, canvas.width, canvas.height)
	
    //물체의 각도 표시
    ctx.beginPath()
    ctx.font = "bold 20px Arial, sans-serif"
    ctx.fillText(`${degree.toFixed(2)}º`, 5, 25, 350)
    ctx.closePath()
	
    //물리엔진
    for (var i in ExperimentObjects) {
        ExperimentObjects[i].draw()
        ExperimentObjects[i].move()
        //물체가 바닥 아래로 들어가면 삭제 목록에 추가
        if (ExperimentObjects[i].y - ExperimentObjects[i].r * 2 > HEIGHT) {
            ToDestroyObjects.push(i)
        }
    }
    //삭제 목록에 있는 물체 삭제
    for (var i of ToDestroyObjects) {
        ExperimentObjects.splice(i, 1)
    }
    //삭제 목록 초기화
    ToDestroyObjects = []
    requestAnimationFrame(render)
}

다음으로는 render 함수가 있다. render 함수에서는 물체의 움직임과 렌더를 담당하게 만들었다. 각도가 생명이기 때문에 각도도 표시하게 만들어 주었다.

 

function getDegree(x, y) {
    return Math.atan2(y, x) * 180 / Math.PI;
}

function onMove_Canvas(event) {
    let _x = event.offsetX
    let _y = HEIGHT - event.offsetY
    degree = getDegree(_x, _y)
}

canvas.addEventListener("mousemove", onMove_Canvas)

이번 프로젝트의 핵심인 각도 계산이다. 아크탄젠트를 이용하면 x, y좌표를 이용하여 각도를 구할 수 있다.

 

function onClick_Canvas(event) {
    let _x = event.offsetX
    let _y = HEIGHT - event.offsetY
    let _power = Math.sqrt(_x ** 2 + _y ** 2) * 0.02
    //new Ball(반지름, 힘의 크기, 각도)
    ExperimentObjects.push(new Ball(15, _power, degree))
}

canvas.addEventListener("click", onClick_Canvas)

그리고  Canvas를 클릭하면 물체가 생성되게 하였다.  피타고라스의 정리를 이용해서 힘을 계산하고 위에서 계산했던 각도로 물체를 발사해준다.

 

class Ball {
    constructor(radius, power, angle) {
        this.pos = new Vector(0, HEIGHT - radius)
        //속도
        let radian = (angle * Math.PI) / 180
        this.vel = new Vector(
            power * Math.cos(radian),
            -power * Math.sin(radian)
        )
        this.r = radius
    }
    ...

다음은 Ball 클래스이다. 물체가 생성될때 위치를 바닥에 배치해 준다. 그리고 Math.sin, Math.cos로 x축에대한 속도와 y축에 대한 속도를 분해 줘야한다. sin cos에 들어가는 매개변수는 라디안이므로 각도를 라디안으로 바꿔주고 대입해준다.

 

class Ball{
	...
	draw() {
        //Object
        ctx.beginPath()
        ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
        ctx.fill()
        ctx.closePath()
        //Speed
        ctx.beginPath()
        ctx.strokeStyle = "red"
        ctx.moveTo(this.x, this.y)
        ctx.lineTo(this.x + this.vel.x * 10, this.y + this.vel.y * 10)
        ctx.stroke()
        ctx.closePath()
    }
    move() {
        this.vel.y += GRAVITY_SCALE * 0.025
        this.pos.add(this.vel)
    }
    ...

 

.이번에는 속도를 표시해보고 싶어서 위 사진 처럼 선으로 표시해봤다. 속도의 x, y성분이 작아서 10을 곱해주니까 이쁘게(?) 완성되었다. 그리고 move함수에는 중력을 구현하기 위해 속도의 y성분에다가 중력가속도 상수를 더해준다. 그냥 대입하면 너무 커서 0.025를 곱해주었다.

 

이렇게 하면 간단(?)하게 완성할 수 있다. 이번에는 확실히 쉬운 프로젝트였지만 왜인지 모르게 3시간 정도 걸렸다.

 

 

물리엔진을 만드는건 항상 재밌지만 버그가 나면 고치기 너무 힘들다. 요즘 학업과 추석으로 인해 코딩을 많이 못하고 있지만 오랜만에 하니까 즐거운 시간을 보냈다.

 

소스코드 : https://github.com/Function1790/CanonGravity

 

GitHub - Function1790/CanonGravity: 물체가 힘을 받았을때 중력 시뮬레이터

물체가 힘을 받았을때 중력 시뮬레이터. Contribute to Function1790/CanonGravity development by creating an account on GitHub.

github.com

Comments