[cub3d](3)2D지도와 가상의3D 구현(javascript이용)



1️⃣ 자바스크립트를 이용한 레이케스팅 구현

(1) 자바스크립트로 먼저 구현하는 이유

  • 최종적으로 cub3d과제C언어를 이용하여 구현할 예정입니다.
  • 그러나 C언어로 처음부터 구현을 하게 되면 다소 어려울 수 있습니다. 그렇기 때문에 자바스크립트를 이용하여 먼저 구현한 뒤 C언어로 다시 구현할 예정입니다.
  • 자바스크립트의 특징으로는
    1. 인터프리터 언어이기 때문에 동작을 확인해 가면서 프로그램을 개발할 수 있습니다.
    2. 동적 타입 언어이기 때문에 당장에 자료형을 신경쓸 필요가 없습니다.
    3. 객체 지향 언어이기 때문에 캡슐화, 추상화, 상속성등 장점이 있습니다.
  • 결론적으로 빠르게 가시적으로 프로그램을 구성할 수 있기 때문에 레이케스팅에 대해 이해하기가 좀 더 수월해질 것 입니다.

(2) p5.js

  • p5.js를 이용하여 레이케스팅을 구현할 계획입니다.
  • p5.js“Lauren McCarthy”에 의해 자바스크립트로 만들어진 새로운 라이브러리입니다. >>(p5.js 사이트)

  • p5.js는 다양한 드로잉 기능을 제공하며, 인터넷 브라우저 전체를 스케치북 삼아 그릴 수 있을 뿐 아니라, 텍스트, 입력, 비디오, 웹캠, 그리고 사운드 등을 비롯한 각종 HTML 요소를 사용할 수 있습니다.
  • p5.js강의도 존재할 만큼 유명한 툴입니다.

(3) 레이케스팅 프로그램

raycasting_files

  • <index.html>:css파일, js파일을 모아 출력하는 용도로 사용할 파일입니다.
  • <styles.css>: 약간의 디자인을 보충해 줍니다.
  • <p5.js>: p5의 라이브러리가 담긴 파일입니다.
  • <raycast.js>: 앞으로 구현할 레이케스팅코드가 들어가게 됩니다.
  • html파일을 실행하는 방법에 대해서는 다음 포스트에 정리해두었습니다. >>>>html작성 환경 만들기
  • Live Server 작업툴을 이용하여 html파일을 실행했습니다.

(4) raycast.js

  • 자세한 파일의 코드 >>PIKUMA사이트의 레이케스팅 강의에서 볼 수 있습니다.
  • 이 파일에 구현할 기능은 다음과 같습니다.
    • < setup() >: 모든 오브젝트들을 초기화하는 기능
    • < update() >: 다음 프레임으로 넘어갈 때 업데이트해주는 기능
    • < draw() >: 프레임마다 렌더링을 해주는 기능
    • < Map클래스(class) >: 지도의 디자인을 담당하는 클래스
  • 여기서 렌더링은 컴퓨터 프로그램을 사용하여 모델 또는 이들을 모아놓은 장면인 씬 파일(scene file)로부터 영상을 만들어내는 과정을 말합니다.


2️⃣ 2D 지도 구현

(1) 2D부터 구현?

  • 레이케스팅은 2D상의 동작을 가상의 3D환경보여줄 뿐입니다.
  • 그렇기 때문에 2D지도와 동작을 먼저 구현하면 레이케스팅의 절반이상 과정이 끝난(?)거라고 할 수 있습니다.

(2) 2D지도

  • 2D지도는 이차원 배열을 이용하여 기본 틀을 만드는 것이 가독성면에서도 좋습니다.
  • 요소로 1(벽이 있을때)0(벽이 없을때)로 구성했습니다. grid_array
  • 각각의 벽은 화면 해상도를 고려해서 적당한 크기로 만들어주며 적절한 색을 입혀줍니다.( p5.js 라이브러리를 이용하여 쉽게 구현이 가능합니다.) 2D_map


3️⃣ Player 구현

(1) 초기 위치

this.x = WINDOW_WIDTH / 2;
this.y = WINDOW_HEIGHT / 2;
  • 위처럼 플레이어의 초기위치를 정중아으로 잡아줍니다.

(2) player위치 업데이트

  • 정해준 프레임마다 플레이어의 위치를 다음과 같이 업데이트 해줍니다.
this.rotationAngle += this.turnDirection * this.rotationSpeed;

var moveStep = this.walkDirection * this.moveSpeed;
var newPlayerX = this.x + moveStep * Math.cos(this.rotationAngle);
var newPlayerY = this.y + moveStep * Math.sin(this.rotationAngle);
  • rotationAngle변수는 각도를 결정하는 값으로 turnDirection(왼쪽키는 -1, 오른쪽키 -1)과 rotationSpeed(사용자가 지정하는 회전속도)의 곱으로 결정됩니다.
  • 이렇게 결정된 rotationAngle(방향)과 moveStep의 곱만큼 플레이어의 위치가 변하게 됩니다.
  • 여기서 moveStepwalDirection(앞쪽키는 +1, 뒤쪽키는 -1)과 moveSpeed(사용자가 지정하는 움직임속도)의 곱으로 결정됩니다.

(3) 벽일때 player가 지나가지못하도록 설정

  • 매 프레임마다 플레이어의 x,y위치가 newPlaterX, newPlayerY로 업데이트 됩니다.
if (!grid.hasWallAt(newPlayerX, newPlayerY))
{
    this.x = newPlayerX;
    this.y = newPlayerY;
}
  • map을 구성할 때 요소값이 ‘1’이면 벽, ‘0’이면 길로 정의했습니다. hasWallAt는 현재위치가 벽인지 길인지를 판별해주는 함수입니다.
  • 벽이 아닐때만 플레이어의 위치가 업데이트되도록 하면서 벽을 통과하지 못하게만들어줄 수 있습니다.

4️⃣ 가상 3D 구현

(1) 3D 구현의 원리

  • 플레이어의 위치로부터 60도 이내의 범위내에서 일정한 각도로 광선을 쏩니다. 이것을 통해 object(물체)와 플레이어의 거리를 유추할 수 있습니다.
  • 일정한 각도마다 발사된는 광선의 각도를 지정해줄 수 있습니다.

< 각도를 크게했을 때 >

large degree

< 각도를 촘촘하게 했을 때 >

small degree
  • 플레이어를 중심으로 좌우 30도씩 60도의 범위로 광선을 쏘게 됩니다.
  • 각도가 0으로 지정된 가장 왼쪽의 광선으로 부터 사용자가 지정해준 각도만큼 순차적으로 광선을 쏘게되면 이 광선으로 부터 object(물체)의 거리를 측정하게 됩니다. 위의 그림과 같이 맵요소 '1'(벽)을 만난 광선은 더이상 진행하지 않도록 설정해줄 것이며 이 곳에 가상의 3D벽이 나타나게 할 것입니다.

< 새로줄 기준 >

collision col-line

< 가로줄 기준 >

collision row-line
  • 각 광선마다 새로줄과 가로줄따로따로 맵의 요소가 ‘1’(벽)인 곳의 길이를 구합니다.
  • 새로줄 기준(a)일 때와 가로줄 기준(b)의 길이중 짧은 값이 벽의 위치값이 되며 드디어 가상의 벽이 출력됩니다.

(2) 각광선의 각도에 의존적인 각벽의 셀

< 각도를 크게했을 때 >

large degree

< 각도를 촘촘하게 했을 때 >

small degree
  • 가상3D의 벽은 충돌지점에 직사각형모양(사용자 시점)으로 생기기 때문에 각 광선의 사이 각도가 크면 부자연스러워 보입니다.
  • 광선의 각도가 촘촘할 수록 좀 더 자연스럽게 보입니다.

(3) 벽의 굴곡현상

line-bend

  • 플레이어의 시야는 좌우 30도씩 총 60도입니다. 이론적으로 중심으로부터 양옆의 30도(끝)인 부분의 광선의 길이가 깁니다.
  • 그렇기 때문에 위와같이 같은 벽일지라도 굴곡이 생기게 됩니다. 벽과의 거리가 가까워질 수록 굴곡이 점점 심해지게 됩니다.
  • 이러한 굴곡은 이론적으로 정확한 출력입니다 하지만 현실세계에서 두개의 눈으로 실제로 보이는 물체의 거리를 보정(?)해주기 때문에 이러한 굴곡은 불편합니다.
  • 그렇기 때문에 다음과 같이 실제광선 길이 x cos(플레이어기준 각도)를 해줍니다.
var correctWallDistance = ray.distance * Math.cos(ray.rayAngle - player.rotationAngle);

line-bend

(4) 벽의 조도차이

  • 위에서 벽을 생성할 때 각각의 광선이 가로벽에 부딪쳤는지 세로벽에 부딪쳤는지알 수있습니다. 그것을 구분해줄 수 있는 변수를 하나 지정해준다면
  • 그 변수를 이용하여 동, 서, 남, 북방향의 벽의 조도를 각각 다르게 설정해줄 수 있습니다.




© 2021.02. by kirim

Powered by kkrim