HTML5 Surround the Nervous Cat 게임의 웹 버전은 HTML5, jquery, Typescript 및 기타 기술을 기반으로 한 게임입니다.
데모와 소스 코드 다운로드가 첨부되어 있습니다. 여기를 클릭하세요 데모 보기 소스 코드 다운로드
지난해 위챗 모멘트에서 인기를 끌었던 미니게임 '신경질적인 고양이를 둘러싸라'도 플레이해봤습니다. 게임은 Egret 엔진을 사용하여 개발되었으며, Egret은 Typescript 언어를 사용하여 빌드되었으므로 여기의 게임도 Typescript를 사용하여 개발됩니다.
게임 규칙:
화면의 회색 그리드를 클릭하면 불안한 고양이를 천천히 둘러싸서 잡을 수 있습니다. 고양이가 플레이 영역 가장자리에 도달하면 게임에서 패배합니다.
재료 준비
인터넷에서 "신경질적인 고양이 둘러싸기" 게임을 검색하고, 디버깅 인터페이스를 엽니다. 네트워크나 리소스에서 고양이, 회색 원, 주황색 원 등의 사진을 제거하고 로컬에 저장합니다.
Egret의 새로운 MovieCilp 아키텍처 디자인과 MovieClip 데이터 형식 표준은 이전 표준과 다소 다릅니다. 인터넷에서 가져온 내용은 새 데이터에 따라 수정된 후 mc의 json 파일을 더 이상 적용할 수 없습니다. 형식 표준은 다음과 같습니다.
{"mc":{ "stay":{ "frameRate":20, "labels":[], "frames":[ {"res":"stay0000","x":0,"y":0}, {"res":"stay0001","x":0,"y":0}, {"res":"stay0002","x":0,"y":0}, {"res":"stay0003","x":0,"y":0}, {"res":"stay0004","x":0,"y":0}, {"res":"stay0005","x":0,"y":0}, {"res":"stay0006","x":0,"y":0}, {"res":"stay0007","x":0,"y":0}, {"res":"stay0008","x":0,"y":0}, {"res":"stay0009","x":0,"y":0}, {"res":"stay0010","x":0,"y":0}, {"res":"stay0011","x":0,"y":0}, {"res":"stay0012","x":0,"y":0}, {"res":"stay0013","x":0,"y":0}, {"res":"stay0014","x":0,"y":0}, {"res":"stay0015","x":0,"y":0} ] }}, "res":{ "stay0000": {"x":0,"y":0,"w":61,"h":93}, "stay0001": {"x":61,"y":0,"w":61,"h":93}, "stay0002": {"x":122,"y":0,"w":61,"h":93}, "stay0003": {"x":183,"y":0,"w":61,"h":93}, "stay0004": {"x":0,"y":93,"w":61,"h":93}, "stay0005": {"x":61,"y":93,"w":61,"h":93}, "stay0006": {"x":122,"y":93,"w":61,"h":93}, "stay0007": {"x":183,"y":93,"w":61,"h":93}, "stay0008": {"x":0,"y":186,"w":61,"h":93}, "stay0009": {"x":61,"y":186,"w":61,"h":93}, "stay0010": {"x":122,"y":186,"w":61,"h":93}, "stay0011": {"x":183,"y":186,"w":61,"h":93}, "stay0012": {"x":0,"y":279,"w":61,"h":93}, "stay0013": {"x":61,"y":279,"w":61,"h":93}, "stay0014": {"x":122,"y":279,"w":61,"h":93}, "stay0015": {"x":183,"y":279,"w":61,"h":93} }}
코드 작성
개발 과정에서 겪은 두 가지 주요 문제를 주로 요약합니다.
질문 1, 고양이는 어떻게 탈출해야 할까요?
이 게임에서 각 원은 세 가지 상태를 가질 수 있습니다
은 통과 가능, 회색 원은
을 나타냄오렌지색 원으로 표시되는 장애물이 있으나 실행 불가능합니다
고양이가 차지하고 있는 회색 원 위에 고양이 애니메이션이 겹쳐져 있음
회색 원을 클릭할 때마다 장애물 상태인 주황색 원으로 변합니다. 동시에 고양이는 클릭을 따라가며 주변 지역으로 이동합니다.
걷는 방향
게임 영역은 9*9개의 원으로 구성되어 있으며, 짝수 선은 원의 반경 너비만큼 들여쓰기되어 있습니다. 이 레이아웃은 이론적으로 고양이가 걷는 방향을 6개(한 걸음만 걸을 수 있음) 가질 수 있다는 것을 의미합니다. a 시간), 즉 왼쪽, 왼쪽 위, 오른쪽 위, 오른쪽, 오른쪽 아래, 왼쪽 아래 이 위치의 원이 장애물이면 해당 방향을 통과할 수 없습니다.
6방향의 6개 이웃 중 5개가 장애물이라면 당연히 경로를 선택하기 쉽고 나머지 하나가 유일한 탈출구이지만 분명히 상황은 그렇게 간단할 수 없습니다. 우리가 직면하는 더 일반적인 상황은 6개 방향의 이웃 중 일부는 직접적으로 장애물 상태에 있고(당연히 우리는 이 단계를 수행하지 않을 것임) 일부는 통과 가능한 상태에 있지만 각 가장자리에 대한 접근성이 떨어지는 것입니다. 다른 것은 다릅니다.
예를 들어 위 그림에서 현재 고양이는 왼쪽 방향으로 세 걸음, 오른쪽 상단과 오른쪽 아래 방향으로 네 걸음, 오른쪽 방향으로 한 걸음만 가면 가장자리에 도달할 수 있습니다. 왼쪽 위, 오른쪽 방향으로 가다가 장애물을 만나고, 왼쪽 아래 방향으로 3걸음 걸어가면 장애물에 부딪혀 가장자리에 도달할 수 있습니다. 이때 우리는 물론 이 6가지 방향의 우선순위를 매겨야 합니다.
우선순위
저는 우선순위를 이렇게 정합니다.
교통 방향>은 그림과 같이 장애물의 방향을 표시합니다: 왼쪽, 오른쪽 위, 오른쪽 아래> 왼쪽 위, 오른쪽, 왼쪽 아래
진행 방향에서는 그림과 같이 가장자리에 가까울수록 우선순위가 높습니다. 왼쪽 > 오른쪽, 오른쪽 위, 오른쪽 아래
방책물이 나타나는 방향에서 아래 그림과 같이 더 많은 걸음을 내디딜수록 우선순위가 높아집니다. 왼쪽 > 오른쪽, 왼쪽 위
이러한 계약의 접근성은 비교를 위해 수치로 표현됩니다. 이 값이 클수록 우선순위가 높습니다.
이동방향
accessibility = 1/stepToEdge; //stepToEdge는 가장자리에서 남은 단계 수를 나타냅니다.
로드블록이 나타나는 방향
접근성 = (-1)/stepToBlock;//stepToBlock은 장애물로부터의 거리를 나타냅니다
다음으로 분모가 0이면 어떻게 할지 생각해 보세요. 첫 번째의 경우 분모가 0인데 이는 고양이가 현재 가장자리에 있다는 뜻이므로 우선순위를 판단할 필요가 없으며 게임이 진행됩니다. 실패한. 두 번째 경우 분모가 0이라는 것은 나갈 때 장애물에 부딪히게 된다는 의미이므로 이 방향은 고려하지 않고는 절대 도달할 수 없으므로 우선순위는 -1로 설정됩니다.
이 계산 후 6개 방향의 접근성 값은 다음과 같습니다.
왼쪽: 1/3
왼쪽 상단: -1
오른쪽 상단: 1/4
오른쪽: -1
오른쪽 하단: 1/4
왼쪽 하단: -1/3
이 비교에서는 우선순위는 왼쪽 > 오른쪽 위 > 오른쪽 아래 > 왼쪽 아래 > 왼쪽 위 > 오른쪽 순이어야 합니다.
왼쪽 위, 오른쪽, 오른쪽 위, 오른쪽 아래 그룹 내부의 값이 분명히 동일한데 왜 순서대로 정렬되어 있나요? 계산이 왼쪽 방향에서 시작하여 시계 방향으로 회전하기 때문입니다. 값이 동일한 경우 표시되는 순서에 따라 다릅니다.
그래서 위 사진의 이런 상황에서는 고양이가 왼쪽으로 한 발짝 이동하게 됩니다.
질문 2, 고양이가 둘러싸여 있는지 어떻게 알 수 있나요?
이 게임을 온라인으로 플레이하면서 고양이가 포위되면 '포위' 동작으로 변경된다는 사실을 발견했습니다. 그러면 고양이가 포위되었다고 판단한 후 동작 애니메이션을 변경하는 방법은 무엇일까요?
"포위됨"은 "잡음"과 동일하지 않으며 "잡음"상태보다 우선합니다. 고양이가 갈 곳이 없으면 '잡히'고 게임에서 승리합니다. "Surrounded"는 고양이가 아직 갈 길이 멀지만, 아래 그림과 같이 포위되어 죽을 지경에 처해 있음을 의미합니다.
제 생각은 이렇습니다.
고양이의 현재 위치를 기준으로 6방향에서 통행 가능한 이웃을 찾고, 이 이웃에서 시작하여 각각의 통행 가능한 이웃을 찾으세요. 이런 식으로 계속 검색하면서 지금까지 찾은 것을 판단하세요. 놀이 공간 가장자리에 이웃이 있습니까? 그렇다면 검색 과정이 일찍 종료되고 판단 결과는 고양이가 포위되지 않았습니다. 통과할 수 있는 모든 이웃이 발견되고 그 중 어느 것도 게임 영역 가장자리에 없으면 판단 결과는 고양이가 포위되었습니다.
다음으로 코드를 사용하여 이 판단 프로세스를 구현합니다.
首先,需要准备一个方法,判断圆圈是否已经处在圆圈边缘了,假设这个方法名及参数如下,内部实现比较简单这里就不贴了。
/* 判断传入的circle是否在边界上 */ private isCircleAtEdge(circle:Circle):boolean { ... }
再准备一个方法,获取某圆圈周围某方向的邻居。
private getCircleNeighbor(circle:Circle,direction:Direction):Circle{ ... }
最后,是判断的核心方法。
/* 能否在circle位置出发找到路线到达边缘 */ private canExitAt(circle:Circle):boolean{ var ignoreArr=[];//不用再处理的circle集合 var toDealWithArr=[circle];//还需进行判断的circle集合 while(true){ if(toDealWithArr.length<1){ return false; }else{ var _first=toDealWithArr.shift(); ignoreArr.push(_first); if(_first.getStatus()!==CircleStatus.Blocked&&this.isCircleAtEdge(_first)){ return true; }else{ for(var i=Direction.LEFT;i<=Direction.BOTTOM_LEFT;i++){ var nbr=this.getCircleNeighbor(_first,i); if(!(ignoreArr.indexOf(nbr)>-1||toDealWithArr.indexOf(nbr)>-1)) if(nbr.getStatus()!==CircleStatus.Available){ ignoreArr.push(nbr); }else{ toDealWithArr.push(nbr); } } } } } }
在方法体的最开始,准备好两个数组,一个用来存储不用再处理的圆圈集合ignoreArr,另一个用来存储还需要进行判断的圆圈集合toDealWithArr。每找到一个可通行的邻居,首先要判断它是不是第一次出现(因为几个圆圈可能会有共同的邻居,所以一个圆圈可能因为它是多个圆圈的邻居而被找到多次),判断的标准就是它有没有出现在ignoreArr或toDealWithArr里,如果没有那么就是第一次出现,如果它是路障,那么塞到ignoreArr,如果不是路障,那么推入toDealWithArr尾部等待判断。
每次循环开始时,我们会从toDealWithArr头部弹出一个圆圈对象,对它是否在边缘做判断,如果是,那么返回true跳出循环,猫没有被围住,它可以通过某条路线到达边缘。如果toDealWithArr全部判断完了都不在边缘,那么返回false,猫被围住了,它的直接邻居及众多间接邻居中没有一个是在边缘的。