>웹 프론트엔드 >JS 튜토리얼 >전설적인 게임의 JS 모방(코드 포함)

전설적인 게임의 JS 모방(코드 포함)

php中世界最好的语言
php中世界最好的语言원래의
2018-04-12 16:40:393986검색

이번에는 Legend of Blood 게임의 JS 모방 (코드 포함)을 가져 왔습니다. Legend of Blood 게임의 JS 모방의 주의 사항은 무엇입니까? 아래는 실제 사례입니다.

게임의 첫 번째 버전은 2014년에 개발되었습니다. 브라우저는 html+css+js를 사용하고, 서버는 asp+php를 사용하고, 통신은 ajax를 사용하고, 데이터 저장소는 access+mySql을 사용합니다. 그러나 몇 가지 문제로 인해 (당시에는 노드 사용법을 몰랐기 때문에 ASP에서 복잡한 로직을 작성하는 것이 정말 어려웠습니다. 당시에는 캔버스에 쓰기가 거의 없었고 DOM 렌더링이 쉽게 성능 병목 현상에 도달할 수 있었습니다) , 버려졌습니다. 나중에 캔버스를 사용하여 버전이 다시 만들어졌습니다. 이 글은 2018년에 작성되었습니다.

1. 개발 전 준비

비교적 복잡한 PC 게임을 구현하기 위해 Javascript를 사용하는 이유

1. js를 사용하여 PC측 온라인 게임 구현이 가능합니다. PC와 휴대폰 하드웨어 구성의 업그레이드, 브라우저의 업데이트, 다양한 H5 라이브러리의 개발로 인해 js로 온라인 게임을 구현하는 것이 점점 더 어려워지고 있습니다. 여기서 어려움은 주로 두 가지 측면에 있습니다. 브라우저의 성능, js 코드가 매우 복잡한 논리를 가진 게임의 반복을 충족할 만큼 확장하기 쉬운지 여부입니다.

2. 현재 js 게임 중 참고할만한 대규모 게임은 거의 없습니다. 멀티 플레이어 연결, 서버 측 데이터 저장 및 복잡한 상호 작용과 관련된 대부분의(거의 모든) 게임은 Flash를 사용하여 개발됩니다. 하지만 결국 플래시는 쇠퇴하고 있는 반면, js는 빠르게 발전하고 있으며 브라우저가 있는 한 실행할 수 있습니다.

2001년 전설적인 게임을 선택한 이유

첫 번째 이유는 물론 오래된 게임에 대한 감정입니다. 다른 더 중요한 이유는 다른 게임을 할 줄 모르거나 게임 방법을 알지만 자료(사진, 사운드)가 없다는 것입니다. 효과 등). 게임의 맵, 캐릭터 및 몬스터 모델, 아이템, 장비 다이어그램 등을 수집하고 이를 처리하고 파싱한 후 js 개발에 사용하는 데 많은 노력을 쏟는 것은 시간 낭비라고 생각합니다.

이전에 Legend 게임에 대한 몇 가지 자료를 수집했고 운 좋게 Legend of Legend 클라이언트 리소스 파일(github 주소)을 추출하는 방법을 찾았으므로 직접 코드 작성을 시작할 수 있어 준비 시간이 절약됩니다.

가능한 어려움

1. 브라우저 성능: 이것이 가장 어려운 점일 것입니다. 게임이 40프레임을 유지하려는 경우 js가 계산할 수 있도록 각 프레임에는 25ms만 남습니다. 그리고 렌더링은 일반적으로 계산보다 더 많은 성능을 소비하므로 js에 실제로 남은 시간은 약 10밀리초에 불과합니다.

2. 부정행위 방지: 사용자가 인터페이스를 직접 호출하거나 네트워크 요청 데이터를 변조하는 것을 방지하는 방법은 무엇입니까? 목표는 js를 사용하여 보다 복잡한 게임을 구현하는 것이고 모든 온라인 게임은 이를 고려해야 하기 때문에 상대적으로 성숙한 솔루션이 있어야 합니다. 이것은 이 글의 초점이 아닙니다.

2. 전체적인 디자인

브라우저 쪽

화면 렌더링은 캔버스를 사용합니다.

dom(p)+css와 비교하여 캔버스는 더 복잡한 장면 렌더링 및 이벤트 관리를 처리할 수 있습니다. 예를 들어 다음 장면에는 플레이어, 동물, 지상의 항목 및 가장 낮은 지도 그림의 네 가지 그림이 포함됩니다. (사실 바닥에 그림자도 있는데, 마우스가 캐릭터, 동물, 사물을 가리킬 때 나타나는 해당 이름과 땅에 보이는 그림자도 있습니다. 읽기의 편의를 위해 내용은 그렇게 많이 고려하지 않겠습니다. )

이때, "동물을 클릭하면 동물을 공격하고, 아이템을 클릭하면 아이템을 줍는" 효과를 얻으려면 동물과 아이템의 이벤트를 모니터링해야 합니다. dom 방법을 사용하면 처리해야 할 몇 가지 어려운 문제가 있습니다.

a. 렌더링 순서는 이벤트 처리 순서와 다르며(z-index가 작은 경우 이벤트를 먼저 처리해야 하는 경우도 있음) 추가 처리가 필요합니다. 예를 들어 위의 예에서는 몬스터나 아이템을 클릭할 때 캐릭터를 클릭하기 쉽기 때문에 해당 캐릭터에 대해 "클릭 이벤트 침투" 처리를 수행해야 합니다. 게다가 이벤트 처리 순서는 고정되어 있지 않습니다. 캐릭터를 해제해야 하는 스킬(예: 게임 내 치료)이 있는 경우 이때 캐릭터에 이벤트 모니터링이 필요합니다. 따라서 요소가 이벤트를 처리해야 하는지 여부와 이벤트가 처리되는 순서는 게임 상태에 따라 다릅니다. DOM의 이벤트 바인딩은 더 이상 요구 사항을 충족할 수 없습니다.

b. 동일한 dom 노드에 배치하는 것은 어렵습니다. 플레이어의 모델, 플레이어의 이름, 플레이어의 스킬 효과 등은

또는 <섹션> 컨테이너에서는 관리가 쉽습니다(이런 방식으로 여러 요소의 위치 지정을 상위 요소에서 상속할 수 있으며 위치를 별도로 처리할 필요가 없습니다). 하지만 이렇게 하면 z-index를 처리하기가 어려워집니다. 예를 들어, 플레이어 A가 플레이어 B 위에 있으면 A는 B에 의해 가려집니다. 따라서 A의 z-index는 더 작아야 하지만 플레이어 A의 이름은 B의 이름이나 그림자에 의해 가려져서는 안 됩니다. . 간단히 말해서, DOM 구조의 유지 관리 가능성으로 인해 화면 표시 효과가 희생되고 그 반대의 경우도 마찬가지입니다.

c.성능 문제. 효과가 희생되더라도 렌더링에 DOM을 사용하면 필연적으로 많은 중첩 관계가 발생하고 모든 요소의 스타일이 자주 변경되어 브라우저의 다시 그리기 또는 리플로우가 지속적으로 트리거됩니다.

캔버스 렌더링 로직과 프로젝트 로직 분리

캔버스의 다양한 렌더링 작업(drawImage, fillText 등)이 등)이 프로젝트 코드와 함께 배치되므로 필연적으로 나중에 프로젝트를 유지 관리할 수 없게 됩니다. 여러 기존 캔버스 라이브러리를 살펴본 후 vue의 데이터 바인딩+ 디버깅 도구와 결합하여 새로운 캔버스 라이브러리 Easycanvas(github 주소)를 만들었고 vue와 마찬가지로 플러그인 요소를 통해 캔버스 디버깅을 지원합니다.

이런 방식으로 전체 게임의 렌더링 부분이 훨씬 쉬워집니다. 게임의 현재 상태를 관리하고 서버가 소켓에서 반환한 데이터를 기반으로 데이터를 업데이트하기만 하면 됩니다. "데이터 변경으로 인해 뷰 변경"은 Easycanvas의 책임입니다. . 예를 들어 아래 그림의 플레이어 포장 항목 구현에서는 포장 컨테이너의 위치와 배낭에 있는 각 요소의 배열 규칙만 제공한 다음 포장된 각 항목을 배열에 바인딩하고 그런 다음 예(Easycanvas는 데이터를 화면에 매핑하는 프로세스를 담당합니다).

예를 들어 5행 8열 총 40개 항목의 스타일을 다음과 같은 형태로 Easycanvas에 전달할 수 있습니다. (index는 항목 인덱스, 항목 x방향 간격은 36, y방향 간격은 32입니다.) ). 그리고 이 논리는 항목 배열이 어떻게 변경되거나 패키지가 드래그되는 위치에 관계없이 변경할 수 없습니다. 각 항목의 상대적 위치는 고정됩니다. 캔버스 렌더링의 경우 프로젝트 자체를 고려할 필요가 없으므로 유지 관리성이 더 좋습니다.

rreee

캔버스 레이어 렌더링

가정: 게임은 40 프레임을 유지해야 하며, 브라우저는 가로 800, 세로 600, 영역 480,000(이하 화면 영역으로 480,000)입니다.

동일한 캔버스를 사용하여 렌더링하는 경우 이 캔버스의 프레임 번호는 40이고 초당 최소 40개의 화면 영역을 그려야 합니다. 그러나 동일한 좌표점에서 여러 요소가 겹칠 가능성이 높습니다. 예를 들어 하단의 UI, 체력 표시줄, 버튼이 겹쳐서 장면 맵을 공동으로 차단합니다. 따라서 이를 합치면 브라우저의 초당 그리기 양은 쉽게 100개 이상의 화면 영역에 도달할 수 있습니다.

이 그림은 뷰가 전체 캔버스의 모든 곳에서 업데이트되기 때문에 최적화하기 어렵습니다. 이는 플레이어와 동물의 움직임일 수도 있고, 버튼의 특수 효과일 수도 있고, 특정 스킬 효과의 변화일 수도 있습니다. 이 경우 플레이어가 움직이지 않더라도 옷이 "바람에 펄럭이는" 효과(실제로는 스프라이트 애니메이션이 다음 그림으로 재생됨)나 물약 병이 나타나는 효과로 인해 캔버스 전체가 다시 그려집니다. 땅. 왜냐하면 게임의 특정 프레임이 이전 프레임과 구별되지 않는 것은 거의 불가능합니다. 심지어 게임 화면의 일부도 그대로 유지하기 어렵습니다. 전체 게임 화면은 항상 업데이트됩니다.

게임의 특정 프레임이 이전 프레임과 구별되지 않는 것은 거의 불가능하고, 화면은 항상 업데이트되기 때문입니다.

그래서 이번에는 세 장의 캔버스를 겹쳐 배치하는 방식을 채택했습니다. Easycanvas의 이벤트 처리는 전달을 지원하므로 상단 캔버스를 클릭하더라도 클릭을 종료하는 요소가 없으면 후속 캔버스도 이벤트를 수신할 수 있습니다. 세 개의 캔버스는 UI, 그라운드(지도), 엘프(캐릭터, 동물, 스킬 특수 효과 등)를 담당합니다.

이 레이어링의 장점은 필요에 따라 레이어당 최대 프레임 수를 조정할 수 있다는 것입니다.

예를 들어 UI 레이어의 경우, 대부분의 UI는 일반적으로 움직이지 않고, 움직여도 너무 세밀한 드로잉이 필요하지 않기 때문에 프레임 수를 예를 들어 20 정도로 적당히 줄일 수 있습니다. 이런 식으로 플레이어의 체력이 100에서 20으로 감소하면 50ms 이내에 뷰가 업데이트될 수 있으며 플레이어는 50ms 스위치를 느낄 수 없습니다. 체력 같은 것 때문에 UI 레이어 데이터는 짧은 시간 내에 여러 번 연속적으로 변경되기 어렵고, 50ms 지연은 사람이 인지하기 어렵기 때문에 자주 그릴 필요가 없습니다. . 초당 20프레임을 저장한다면 아마도 그림의 화면 영역 10개를 저장할 수 있을 것입니다.

또 다른 예는 플레이어가 움직일 때만 지도가 변경되는 것입니다. 이런 방식으로 플레이어가 움직이지 않는 경우 프레임당 1개의 화면 영역을 저장할 수 있습니다. 플레이어가 움직일 때 부드러움을 보장해야 하기 때문에 지상의 최대 프레임 속도가 너무 낮으면 안 됩니다. 지면 프레임이 30프레임이라면 플레이어가 정지해 있을 때 초당 30개의 화면 영역을 저장할 수 있습니다(이 프로젝트에서는 화면을 가득 채울 정도로 지도가 거의 그려집니다). 또한, 다른 플레이어나 동물의 움직임으로 인해 지면이 바뀌지 않으며 지면 레이어를 다시 그릴 필요도 없습니다.

스프라이트 레이어의 최대 프레임 속도는 줄일 수 없습니다. 이 레이어는 캐릭터 이동과 같은 게임의 핵심 부분을 표시하므로 최대 프레임 속도는 40으로 설정됩니다.

이런 식으로 초당 그려지는 영역은 플레이어가 움직일 때 80~100개의 화면 영역일 수 있지만, 플레이어가 움직이지 않을 때는 50개의 화면 영역만 될 수 있습니다. 게임에서는 플레이어가 가만히 서서 몬스터와 싸우고, 아이템을 정리하고, 스킬을 공개하기 위해 멈춰서 있기 때문에 오랜 시간 동안 땅 뽑기가 발동되지 않아 성능이 크게 절약됩니다.

서버측

js를 사용하여 멀티플레이어 온라인 게임을 구현하는 것이 목표이므로 서버는 Node와 소켓을 사용하여 브라우저와 통신합니다. 이것의 또 다른 장점은 지도의 특정 좌표 지점에 장애물이 있는지 확인하는 것과 같은 일부 공통 논리를 양쪽 끝에서 재사용할 수 있다는 것입니다.

Node 측의 플레이어, 장면 등 게임 관련 데이터는 모두 메모리에 저장되며 정기적으로 파일과 동기화됩니다. 노드 서비스가 시작될 때마다 파일에서 메모리로 데이터를 읽어옵니다. 이와 같이 플레이어가 많아지면 파일 읽기 및 쓰기 빈도가 기하급수적으로 증가하여 성능 문제가 발생합니다. (나중에 안정성을 높이기 위해 파일 읽기 및 쓰기를 위한 버퍼를 추가했으며, 읽기 및 쓰기 과정에서 서버 재시작으로 인한 파일 손상을 방지하기 위해 "메모리-파일-백업" 방식을 사용했습니다.)

노드 측은 인터페이스, 데이터, 인스턴스 등 여러 계층으로 구분됩니다. "인터페이스"는 브라우저와의 상호 작용을 담당합니다. "데이터"는 특정 약의 이름과 효과, 특정 몬스터의 속도와 체력 등의 정적 데이터이며 게임 규칙의 일부입니다. "인스턴스"는 게임의 현재 상태입니다. 예를 들어 특정 플레이어의 약은 "약물 데이터"의 인스턴스입니다. 또 다른 예를 들어, "사슴 인스턴스"에는 "현재 혈액량" 속성이 있습니다. 사슴 A는 10이고, 사슴 B는 14일 수 있으며, "사슴" 자체는 "초기 혈액량"만 갖습니다.

3. 씬맵 구현

지도 장면

렌더링을 위해 여전히 Easycanvas를 사용하는 지도 장면 부분부터 시작해 보겠습니다.

생각

플레이어는 항상 화면 중앙에 고정되어 있으므로 플레이어의 움직임은 실제로 지도의 움직임과 같습니다. 예를 들어 플레이어가 왼쪽으로 달리면 지도가 오른쪽으로 이동합니다. 방금 말씀드린 것처럼 플레이어는 세 개의 캔버스 중 중간 레이어에 있고, 지도는 맨 아래 레이어에 속하므로 플레이어가 지도를 막아야 합니다.

이는 타당해 보이지만 맵에 나무가 있으면 "플레이어의 레벨이 항상 나무보다 높다"는 것은 잘못된 것입니다. 현재 2가지 큰 솔루션이 있습니다:

지도는 계층화되어 있으며 "지상"과 "지상"이 분리되어 있습니다. 예를 들어 아래 그림에서 왼쪽은 땅에, 오른쪽은 땅에 놓고 플레이어를 가운데에 배치하여 그립니다.

이는 문제를 해결하는 것처럼 보이지만 실제로는 두 가지 새로운 문제를 야기합니다. 첫 번째는 플레이어가 때때로 "바닥에 있는" 물체(예: 나무)에 의해 차단될 수 있고 때로는 "바닥에 있는" 물체를 차단할 수 있어야 한다는 것입니다. 땅"(예를 들어 이 나무 아래에 서 있으면 머리가 나무를 가리게 됩니다). 또 다른 문제는 렌더링 성능 비용이 증가한다는 것입니다. 플레이어는 항상 바뀌기 때문에 "그라운드" 레이어를 자주 다시 그려야 합니다. 이는 또한 원래 디자인을 깨뜨립니다. 즉, 큰 지상 지도의 렌더링을 최대한 저장하기 위해 캔버스의 레이어링을 더욱 복잡하게 만듭니다.

지도는 계층화되지 않고 "지상"과 "지상"이 함께 그려집니다. 플레이어가 나무 뒤에 있을 때 아래와 같이 플레이어의 투명도를 0.5로 설정합니다.

여기에는 단 한 가지 단점이 있습니다. 플레이어의 신체가 불투명하거나 반투명하므로(지도 위를 걷는 몬스터도 이 효과를 갖습니다) 이는 완전히 현실적이지 않습니다. 왜냐하면 이상적인 효과는 플레이어의 신체 일부가 가려지는 장면을 만드는 것이기 때문입니다. 그러나 이는 성능 친화적이고 코드를 유지 관리하기 쉽습니다. 현재 이 솔루션을 사용하고 있습니다.

그렇다면 "지도" 그림의 어느 부분이 나무인지 확인하는 방법은 무엇입니까? 게임에는 일반적으로 0, 1, 2와 같은 숫자를 사용하여 통과할 수 있는 장소, 장애물이 있는 장소, 환승 지점 등을 식별하는 큰 지도 설명 파일(실제로 배열)이 있습니다. Legend of Hot Blood의 '설명 파일'은 가장 작은 단위로 48x32로 설명되므로 Legend에서 플레이어의 행동은 '체스판' 느낌을 갖게 됩니다. 단위가 작을수록 부드럽지만 차지하는 부피가 커지고 이 설명을 생성하는 프로세스에 더 많은 시간이 소요됩니다.

사업을 시작합시다.

달성됨

Legend of Legend 클라이언트에서 "Beech Province" 지도를 내보낼 수 있도록 친구에게 요청했습니다. 너비가 33600이고 높이가 22400으로 내 컴퓨터 크기의 수백 배에 달합니다. 컴퓨터가 폭발하는 것을 방지하려면 여러 개의 청크로 나누어 로드해야 합니다. 범례의 가장 작은 단위는 48x32이므로 지도를 480x320에서 4900(70x70) 이미지 파일로 분할합니다.

이 기사의 사례를 읽은 후 방법을 마스터했다고 생각합니다. 더 흥미로운 정보를 보려면 PHP 중국어 웹사이트의 다른 관련 기사를 주목하세요!

추천 자료:

FileReader는 사진을 업로드하기 전에 로컬 미리보기를 구현합니다

vue-dplayer hls 재생 구현 단계에 대한 자세한 설명

위 내용은 전설적인 게임의 JS 모방(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.