지난 글에서 여러 공들이 화면 안에서 튕기게 만들었는데, 이제 여기다가 중력을 가미해보자.

 

 

Javascript 캔버스에서 튕기는 공들 (w/o 중력)

이전 글에서는 공 하나가 화면 안에서 생각없이 튕기는 내용이었다. Javascript 캔버스에서 움직이는 공 그리기 유튜브에서 브라우저에서 각종 도형의 움직임을 Javascript로 구현하는 걸 보고 반해

mrkool.tistory.com

이 공들에 중력이 더해지면 아래와 같이 실제와 비슷한 운동을 보인다.

https://repl.it/join/vtffyilt-bryanko555 

대부분 동일하고, 프레임마다 다르게 그려주는 update()함수에 가속도 요소를 넣어준다. 

 

update(){ // 프레임마다 속성들을 변화시킴

  this.y += this.weight; // y방향 움직임을 무게로 가정해서 설정

  this.x += this.directionX;

  this.weight += 0.1; // 프레임마다 떨어질때의 가속도 증가

  this.directionX *= 0.997; // 가로방향 속도 감소. 가장 자연스러운 움직임을 보이는 수치로 찾은 값 

  if(this.y+this.size>canvas.height || this.y-this.size<0){ // 세로 방향 바운드 처리

    this.weight *= -0.8; // y 방향을 바꿔주면서 점차 바운스가 감소하게

  if(Math.abs(this.weight)<0.9){this.y = canvas.height-this.size; // 바운드가 어느 정도 이하가 되면 더 이상 바운드 안하게, 즉, 바닥에 붙게 한다.

  this.weight =1;} else { this.y -=1 ;} //

  };

  if(this.x>canvas.width-this.size || this.x-this.size < 0 ) { // 가로 방향 바운드 처리

    this.directionX *= -1; // x 방향 전환. 

  }

}

 

전체 코드 :

 

window.onload = function () {
 
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = window.innerWidth-15;
  canvas.height = window.innerHeight-15;
 
  balls=[];
  ballNumber = 50;
 
  class Ball{
    constructor(x,y){ // ball의 기본 속성들을 정의 
      this.x = x;
      this.y = y;
      this.c = 'rgba('+Math.random()*255+','+Math.random()*255+','+Math.random()*255+')';
      this.size = Math.random()*10+5;
      this.angle = (Math.random()*360)*((Math.PI*2)/360);
      this.power = Math.random()*3+2;
      this.directionX = this.power * Math.cos(this.angle);
      this.weight = this.power * Math.sin(this.angle);
    }
    update(){ // 프레임마다 속성들을 변화시킴 
      this.y += this.weight; 
      this.x += this.directionX;
      this.weight += 0.1// 프레임마다 떨어질때의 가속도 증가
      this.directionX *= 0.997// 가로방향 속도 감소. 
      if(this.y+this.size>canvas.height || this.y-this.size<0){ // 세로 방향 바운드 처리
        this.weight *= -0.8// y 방향을 바꿔주면서 점차 바운스가 감소하게
        if(Math.abs(this.weight)<0.9){this.y = canvas.height-this.size; // 바운드가 어느 정도 이하가 되면 더 이상 바운드 안하게, 즉, 바닥에 붙게 한다.
        this.weight =1;} else { this.y -=1 ;} // 
        };
      if(this.x>canvas.width-this.size || this.x-this.size < 0 ) { // 가로 방향 바운드 처리 
        this.directionX *= -1// x 방향 전환.  
      } 
    }
    draw(){ // 넘어온 속성값대로 캔버스에 그림을 그려줌
       ctx.fillStyle= this.c;
       ctx.beginPath();
       ctx.arc(this.x, this.y, this.size, 0, Math.PI*2true);
       ctx.closePath();
       ctx.fill();
       ctx.strokeStyle = 'black';
       ctx.strokeWidth = 4;
       ctx.stroke();
    }
  }
 
  function init(){
      for(i=0;i<ballNumber;i++){
        balls[i] = new Ball(canvas.width*0.5, canvas.height*0.5)
      }
  }
 
  function animate(){ // 매 프레임마다 벌어지는 일들
    ctx.fillStyle='rgba(255,255,255,0.51)';
    ctx.fillRect(0,0,canvas.width,canvas.height);
    for(i=0;i<ballNumber;i++){
      balls[i].update();
      balls[i].draw();
    }
 
    window.addEventListener('resize',function(){ // 화면 크기가 변하면 캔버스 크기도 변경해줌
      canvas.width=window.innerWidth;
      canvas.height=window.innerHeight;
    })
    requestAnimationFrame(animate);
  }
 
  init();
  animate();
 
  }
cs

여기다가 좀 변화를 줘서, 공들을 360도 회전하면서 발사되게 해봤다.

https://repl.it/join/zxxofqqj-bryanko555

공이 한꺼번에 생성되는 것이 아니라, 하나씩 생성되서 발사되야 하기 때문에 setInterval로 시간 간격을 두면서 공을 생성해야 한다. requestAnimationFrame으로는 공들이 순식간에 생성되서 그냥 동시에 터지는 것처럼 보인다.

setInterval이 호출될 때마다 공을 한개씩 생성한다.그리고 생성된 공이 시계방향으로 돌아가면서 발사되야하기 때문에, 공(ball)의 속성에 각도(angle)를 추가했다.

 

class Ball{

  constructor(x,y,angle){ // ball의 기본 속성들을 정의

    :

}

 

function init(){

  if(num < ballNumber){ num++;} // 호출될 때마다 공을 1개씩 생성한다.

  balls[num] = new Ball(canvas.width*0.5, canvas.height*0.5, Math.PI*2/360 + i) // 각도를 순차적으로 증가시키면서 공을 생성한다.

}

 

setInterval(init,100);

 

오늘은 여기까지....

 

 

자바스크립트를 깨우치다:객체로 풀어보는 JavaScript의 원리

COUPANG

www.coupang.com

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

이전 글에서는 공 하나가 화면 안에서 생각없이 튕기는 내용이었다.

 

 

Javascript 캔버스에서 움직이는 공 그리기

유튜브에서 브라우저에서 각종 도형의 움직임을 Javascript로 구현하는 걸 보고 반해서 바로 공부를 시작. 이제서야 조금씩 머리속에서 그림이 그려져가고 있다. bouncing-ball-3.bryanko555.repl.co 이건 그

mrkool.tistory.com

이번에는 컴퓨터의 강점을 살려, 공의 갯수를 확 늘리게되면 이제 좀 볼만해진다.

이전에는 ball 클래스를 만들어놓고 공을 하나만 만들었었는데, 이번에는 초기화 함수, init()에서 공을 여러개 생성하고,

animate() 함수에서 그 공들을 매 순간 업데이트 할 때마다 계산해준다.

 

function init(){ // 공의 갯수만큼 공의 객체 생성

  for(i=0;i<ballNumber;i++){

    balls[i] = new Ball(canvas.width*0.5, canvas.height*0.5)

   }

}

 

function animate(){ // 매 프레임마다 벌어지는 일들

   ctx.fillStyle='rgba(255,255,255,0.8)'; // 전체 화면 지우기. 하얀색의 alpha값을 변경함에 따라 공의 잔상이 달라진다.

   ctx.fillRect(0,0,canvas.width,canvas.height);

   for(i=0;i<ballNumber;i++){

      balls[i].update();

      balls[i].draw();

    }

}

 

위 데모에서는 30개의 공을 그렸는데, requestAnimationFrame()이 1초에 60번 동작하고, 그 때마다 30개 공의 좌표를 이동시키고 그림을 그리고 벽에 닿았는지 판단해준다. 문돌이에게는 놀라운 일이다. ㅎ

 

다양성을 주기 위해서 각 공마다 랜덤 특성을 부여했다.

시작할때 공이 랜덤한 방향으로 날라가고,

    this.angle = (Math.random()*(Math.PI*2));

공의 색깔이 랜덤하게 생성되고, 

    this.c = 'rgba('+Math.random()*255+','+Math.random()*255+','+Math.random()*255+')';

공의 크기를 다르게 하고,

    this.size = 10+Math.random()*20;

공이 날라가는 속도를 다르게 했다. 

    

this.power = Math.random()*3+2;

 

아예 공의 수를 200개로...

 

이 공이 움직이는 것처럼 보이는 이유가, 매 프레임마다 캔버스 전체를 하얀색 상자를 그려서 덮어버리고 각 공들을 이동한 좌표로 그려주고, 다음 프레임에서 다시 다 지우고, 공들을 다시 그리고 ... 이렇게 반복함으로써 공들이 움직이는 것처럼 보이는 것인데...

캔버스를 덮을 하얀색의 투명도(alpha값)을 다르게 함에 따라서 공의 궤적을 다르게 볼 수 있다.

alpha =1 

alpha = 0.5

alpha = 0.1

 

전체 코드 :

window.onload = function () {
 
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = window.innerWidth-15;
  canvas.height = window.innerHeight-15;
 
  balls=[];
  ballNumber = 20;
 
  class Ball{
    constructor(x,y){ // ball의 기본 속성들을 정의 
      this.x = x;
      this.y = y;
      this.c = 'rgba('+Math.random()*255+','+Math.random()*255+','+Math.random()*255+')'// 공의 색깔을 random으로 설정
      this.size = 10+Math.random()*20;
      this.angle = (Math.random()*(Math.PI*2));
      this.power = Math.random()*3+2;
      this.directionX = this.power * Math.cos(this.angle);
      this.directionY = this.power * Math.sin(this.angle);
    }
    update(){ // 프레임마다 속성들을 변화시킴 
      this.y += this.directionY; 
      this.x += this.directionX;
      if(this.y+this.size>canvas.height || this.y-this.size<0){ // 바운드 처리
        this.directionY *= -1;
        }
      if(this.x>canvas.width-this.size ) {
        this.x = canvas.width-this.size;
        this.directionX *= -1;
      } else if (this.x-this.size < 0){
        this.directionX *= -1;
      }
    }
    draw(){ // 넘어온 속성값대로 캔버스에 그림을 그려줌
       ctx.fillStyle= this.c;
       ctx.beginPath();
       ctx.arc(this.x, this.y, this.size, 0, Math.PI*2true);
       ctx.closePath();
       ctx.fill();
       ctx.lineWidth = 2;
       ctx.strokeStyle= 'black';
       ctx.stroke();
    }
  }
 
  function init(){ // 공의 갯수만큼 공의 객체 생성
      for(i=0;i<ballNumber;i++){
        balls[i] = new Ball(canvas.width*0.5, canvas.height*0.5)
      }
  }
 
  function animate(){ // 매 프레임마다 벌어지는 일들
    ctx.fillStyle='rgba(255,255,255,0.1)'// 전체 화면 지우기. 하얀색의 alpha값을 변경함에 따라 공의 잔상이 달라진다.
    ctx.fillRect(0,0,canvas.width,canvas.height);
    for(i=0;i<ballNumber;i++){
      balls[i].update();
      balls[i].draw();
    }
 
    window.addEventListener('resize',function(){ // 화면 크기가 변하면 캔버스 크기도 변경해줌
      canvas.width=window.innerWidth;
      canvas.height=window.innerHeight;
    })
    requestAnimationFrame(animate);
  }
 
  init();
  animate();
 
  }
cs

다음에는 중력까지 가미해보자.

 

 

자바스크립트를 깨우치다:객체로 풀어보는 JavaScript의 원리

COUPANG

www.coupang.com

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

+ Recent posts