Only this pageAll pages
Powered by GitBook
1 of 77

ZEP Guidebook (KR)

Loading...

ZEP Script 가이드

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

ZEP SCRIPT API

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Hello ZEP Script

ZEP Script 란?

ZEP Script는 ZEP에서 동작하는 스크립트 언어(Scripting Language)입니다.

아바타 및 오브젝트 조작, 결제, 채팅, 실시간 멀티플레이 등 다양한 기반 시스템을 제공하여,

코딩이 처음이라도 쉽게 시작할 수 있습니다.

독창적인 게임부터, 캘린더와 방명록 등의 생산성 앱까지!

나만의 메타버스 앱을 만들어 메타버스를 운영하고 ZEP을 더 풍부하게 즐겨보세요!

ZEP Script 활용 사례

ZEP Script는 사용하기 편리합니다. 그리고 누구나 사용할 수 있습니다.

ZEP Script를 활용하여 만든 맵

🕹️게임

ZEP Script를 활용해 다양하고 재미있는 게임을 만들 수 있습니다.

토끼산 오르기

🐰 토끼산 오르기 맵 구경하기

떨어지는 장애물(눈덩이)을 피해 산 정상으로 올라가는 게임입니다. 미니게임 [똥피하기]를 활용하여 떨어지는 눈덩이를 구현할 수 있습니다.

봉봉학교 탐정단

🕵️ 봉봉학교 탐정단 맵 구경가기

제한 시간 내에 탐정이 되어 맵 곳곳을 탐험할 수 있는 방탈출 맵입니다. 비네팅 효과로 인해 스릴 넘치는 공간을 체험헤보세요!

결투 게임

🥊 결투장 맵 구경가기

미니게임 [결투]를 기반으로 만들어진 게임입니다. [z]키를 눌러 상대를 공격하고 HP가 깎이지 않게 다른 사람들을 피해 끝까지 살아남으세요. 가장 끝까지 살아남은 우승자는 왕으로 변할 수 있습니다.

🎉행사

ZEP Script를 활용하여 인터랙티브한 행사를 기획할 수 있습니다.

클레이튼 뮤지엄

✈️ 클레이튼 뮤지엄 구경가기

클레이튼의 기술적 강점 등을 ZEP Script를 활용해 효과적으로 보여준 사례입니다

유니세프 후원자 참여행사 메타버스

유니세프가 하는 일을 ZEP Script로 구현하여 , 이용자들이 맵을 체험하며 자연스럽게 학습을 할 수 있도록 제작했습니다

더블제트 제트랜드

✈️ 더블제트 제트랜드 구경가기

삼성 청소기 제트 브랜드를 체험할 수 있는 테마파크로 ZEP Script를 통해 게임 공간을 구현해두었습니다. 행사 시에 이용자들에게 재미요소를 줄 수 있습니다.

ZEP Script 개발 가이드

시작하기

소개

메타버스 플랫폼 ZEP에서 동작하는 앱을 누구나 제작할 수 있습니다.

캐릭터 및 오브젝트 조작 기능, 커스텀 UI 기능 등 다양한 기능을 지원합니다.

독창적인 게임부터 캘린더, 방명록 등의 생산성 앱까지, 누구나 자신만의 메타버스 앱을 만들 수 있습니다.

🔥

개발 환경

  • Javascript (ES6)

  • Typescript

  • Canvas

  • Web (Desktop/Mobile)

ZEP-Script SDK를 이용한 개발 및 배포

ZEP-Script SDK를 사용하면 APP 개발 및 배포를 쉽게 할 수 있습니다.

💡

💻

유형

ZEP Script를 통해 개발된 앱은 아래 세가지 형태 중 하나를 선택하여 맵에 적용할 수 있습니다. 앱과 맵을 연결하기 위해서, 를 참고해주세요.

1) 미니게임 ( Mini game )

미니게임은 어느 맵에서나 사용할 수 있는 설치형 앱입니다. 임베드 권한이 있는 스페이스에서 [사이드바] > [미니게임 아이콘]을 클릭해 설치할 수 있습니다. 설치한 순간부터 작동하며, 실행한 사용자가 됩니다.

2) 노말 앱 ( Normal app )

노말 앱은 특정 맵에서만 작동할 수 있는 앱입니다. 노말 앱을 개발하여 업로드하면, 관리자이상의 권한을 가진 유저가 맵의 [설정] > [맵 설정] > [노말 앱 추가]에서 적용할 수 있습니다. 별도의 앱 설치 과정 없이 내가 제작한 맵에만 Script를 적용할 수 있습니다.

3) 사이드바 앱 ( Sidebar app )

사이드바 앱은 PC 좌측 사이드바에 아이콘으로 표시되는 형태의 앱 입니다. 사이드바 앱을 개발하여 업로드하면, 관리자권한을 가진 맵의 플레이 화면에서 [사이드바] > [앱 추가] > [앱 관리] 리스트에서 설치하여 추가할 수 있습니다. 사이드바 앱이 추가된 스페이스에서는 입장한 모든 사람에게 사이드바 앱이 노출됩니다.

페이지를 참고해 사이드바 앱을 개발해보세요.


ZEP 스크립트 활용 사례
Javascript 개발 가이드
Typescript 개발 가이드
배포 가이드
점프(space bar)를 통해 박스를 밟을 경우 앱이 종료
사이드바 앱 예제

쉬운 개발 가이드

이 가이드에서는 ZEP Script 개발을 시도해 보고자 하는 분들을 위한 간단한 개발 환경 및 방법에 대해 설명합니다.

프로그램 설치

ZEP Script를 이용해 앱을 개발하기 위해서는 코드를 작성할 수 있는 IDE(편집기)가 필요합니다. 이 가이드에서는 Visual studio code 소프트웨어를 설치해 보도록 하겠습니다.

  1. Visual studio code 홈페이지()에 방문합니다.

  2. PC에 설치된 OS에 맞는 소프트웨어를 다운로드하고 설치합니다.

프로그램 실행 및 코드 작성 준비

  1. 설치한 소프트웨어를 실행합니다.

  2. 좌측 상단에 [파일] → [새 파일]을 선택합니다.

  3. 중앙 상단에 표시되는 파일 형식 입력에 main.js라고 입력합니다.

    (ZEP Script로 제작한 앱에는 반드시 main.js 파일이 있어야 합니다)

  4. 파일을 저장할 폴더를 선택하신 후 파일 생성을 클릭합니다.

샘플 코드 작성하기

미니 게임을 실행하면 간단한 인사 메시지가 나오는 앱을 제작해 보도록 합니다.

사용할 API는 아래 3가지입니다.

  • - 앱이 실행될 때 이 API함수 내에 있는 명령들이 1회 실행됩니다.

  • - 채팅 창에 메시지를 표시하는 기능입니다.

  • - 화면에 메시지를 표시하는 기능입니다.

더 많은 기능에 대해 알아보려면 ZEP-Script 가이드()를 참고해 주세요.

아래와 같이 테스트 코드를 작성한 후 저장합니다.

저장이 완료되었으면, 앱을 업로드하기 위해 main.js파일이 있는 폴더로 이동해 해당 파일을 *.zip파일로 압축합니다.

앱 업로드 및 실행하기

앱 업로드 하기

  1. 홈페이지 로그인한 후 우측 상단에 내 닉네임을 클릭해서 [나의 앱]을 클릭합니다.

  2. [나의 앱]에서 앱을 등록하거나 수정할 수 있습니다. 새로운 앱을 업로드하기 위해 우측 상단에 [앱 업로드] 버튼을 클릭합니다.

  3. 앱 업로드에 필요한 정보를 작성합니다.

  • 이름, 설명 등을 작성합니다.

  • 앱 타입은 [미니 게임]으로 합니다.

  • [ZIP 파일 업로드] 버튼을 눌러, 작성한 코드가 포함된 압축 파일을 등록합니다.

  • 다 완료되었으면 [업로드] 버튼을 눌러 완료합니다.

앱 종류 및 업로드에 대한 자세한 사항은 를 참고해 주세요.

앱 테스트 하기

테스트를 위해 홈페이지에 로그인 후 스페이스를 만들어 주세요.

  1. 좌측의 사이드바에서 [미니 게임]을 클릭합니다.

  2. 실행 가능한 미니 게임 목록 중에 내가 업로드한 앱을 클릭합니다. [Hello, ZEP]

  3. 앱이 실행됨과 동시에 화면 상단과 채팅 창에 코드로 작성했던 Hello, ZEP! 이라는 메시지가 나타나는 것을 볼 수 있습니다.

ZEP Script 따라하기

기본적인 앱 제작, 업로드 방식에 대해 알아봤는데요,

ZEP Script에 대해 조금 더 학습하면, 자신이 원하는 앱을 만들 수 있습니다.

[]를 따라서 만들어 보는 것부터 시작해 보세요!

ZEP Script 따라하기

ZEP Script의 기본부터 고급 기능까지 실습할 수 있는 튜토리얼과 예제 코드를 따라하며 ZEP Script를 손쉽게 익혀보세요.

기초 예제 코드

기초 예제 코드를 따라 차근차근 ZEP Script를 익혀보세요!

ZEP Script 예제코드

ZEP Script 개발에 도움을 주는 다양한 예제 코드들을 확인해보세요.

UI 예제 코드

기초 유형

심화 유형

기초 예제 코드

튜토리얼을 따라 차근차근 ZEP Script를 익혀보세요!

사이드 바 앱 예제 코드

사이드 바 앱 예제 코드를 이용해 나만의 앱을 만들어 보세요!

각 항목에 포함된 zip파일을 받아서 사이드바 앱으로 업로드한 후 맵에 설치하시면 바로 실행해 보실 수 있습니다

기초 유형

심화 유형

App.onStart.Add(function(){
    App.sayToAll("Hello, Zep!");
    App.centerLabel("Hello, Zep!")
})
링크
App.onStart
App.sayToAll
App.showCenterLabel
링크
[배포 가이드]
기초 예제 코드

UtilityClass

유용하게 사용할 수 있는 작은 단위의 클래스들을 소개합니다.

Time

ZEP 서버의 시간을 조회하는 등 시간 데이터를 가져오는 함수를 모아 놓은 클래스입니다.

♻️ZEP APP lifecycle 이해하기
✉️메시지 출력해보기
👤캐릭터 이미지 바꾸기
🖼️나만의 UI 만들기
💢2초 스턴 효과 만들어보기
⏰타이머
🤛사이드바 앱
🧟‍♂️좀비 게임
🎨페인트맨 게임
🙆‍♀️초성 퀴즈
💩똥피하기 게임
🥊결투 게임
🏃‍♂️달리기
🔹Image List
🔹Text List
🔹Text Button List
🔹Ranking List
🔹Select List
♻️ZEP APP lifecycle 이해하기
✉️메시지 출력해보기
👤캐릭터 이미지 바꾸기
🖼️나만의 UI 만들기
💢2초 스턴 효과 만들어보기
🔹Image List
🔹Text List
🔹Text Button List
🔹Ranking List
🔹Select List

메시지 출력해보기

  1. App의 중앙 상단부에 메시지를 출력해봅니다.

    기본 Center label 색상은 검은 배경에 흰색 텍스트로 노출됩니다.

// main.js

App.showCenterLabel("Hello world");

2. 실행 결과

참고

- 튜토리얼의 App type은 미니게임(Mini game)을 권장합니다.

- .js파일의 파일이름은 반드시 main 이어야 합니다. main.js 파일을 준비합니다.

- 배포 방법을 아직 모르신다면, ZEP Script 배포 가이드를 참고해주세요.

스페이스와 맵 이해하기

space와 map의 관

space는 1개 이상의 map을 포함하고 있는 그룹의 단위 입니다.

space와 map은 다른 스페이스 또는 맵과 자신을 구분하기 위한 HashID를 가지고 있습니다.

space에 포함된 1개 이상의 map들 중 하나는 ‘첫 방문 맵’ 속성을 가지고 있으며, mapID 없이 spaceHashID만 가지고 스페이스에 접근 할 경우 ‘첫 방문 맵’ 속성을 가진 맵으로 이동하게 됩니다.

젭 맵 에디터에 들어가면 다음과 같은 URL 형식을 볼 수 있는데요,

여기서 Ak42Xz가 SpaceHashID, 25g3RQ가 MapHashID 입니다.

ZEP 스크립트 상호작용 오브젝트 가이드

맵 에디터에서 오브젝트를 설치하여 상호작용시 ZEP 스크립트에서 감지해 상호 작용시 발생하는 이벤트를 스크립트로 작성할 수 있습니다.

맵 에디터에서 다음과 같이 ZEP Script 상호작용 오브젝트를 설치 할 수 있습니다.

[맵 에디터] → [오브젝트설치] → [개발자 기능] → [ZEP Script 상호작용 선택]

오브젝트 설정에서 값을 입력한 뒤,

다음과 같이 스크립트를 작성하여 앱 실행 후, 오브젝트와 상호작용시 입력한 값이 출력되는 것을 확인할 수 있습니다.

App.onObjectTouched.Add(function (sender, x, y, tileID, obj) {
    if (obj !== null) {
        if (obj.type == ObjectEffectType.INTERACTION_WITH_ZEPSCRIPTS) {
            App.sayToAll(`Number = ${obj.text}, Value = ${obj.param1}`, 0xFFFFFF);
        }
    } else {
        App.sayToAll(`obj is null`, 0xFFFFFF);
    }
});

Script에서 [obj.type == ObjectEffectType.INTERACTION_WITH_ZEPSCRIPTS]를 조건으로 활용해 주세요.

기준 좌표

기준 좌표란 오브젝트 배치 시 이미지가 그려지는 기준을 의미합니다.

다음과 같은 맵에 Left-Top 기준으로 오른쪽의 오브젝트 이미지를 3, 3에 삽입하면 어떻게 될까요?

기준 좌표가 Left-Top일 경우 다음 그림과 같이 3, 3 박스의 왼쪽 위 지점부터 이미지가 그려집니다.



2초 스턴 효과 만들어보기

2초 스턴 효과 만들어보기

참고

- 튜토리얼의 App type은 미니게임(Mini game)을 권장합니다.

- .js파일의 파일이름은 반드시 main 이어야 합니다. main.js 파일을 준비합니다.

- 배포 방법을 아직 모르신다면, 를 참고해주세요.


ZEP APP lifecycle 이해하기

앱이 실행되어 종료될 때까지를 하나의 생애 주기(Lifecycle)라고 합니다.

Lifecycle 함수는 앱의 생애 주기를 관리해주는 함수입니다. 아래 그림과 같이 앱이 실행될 때는 Enter 단계의 함수들이 동작하고, 앱이 실행 중 일 때는 Update 단계의 함수들이 주기적으로 동작하며, 앱이 종료될 때는 Exit 단계의 함수들이 동작하게 됩니다.


Image List

Image List 위젯은 이미지와 텍스트를 함께 표시하고 선택할 수 있는 UI입니다.

활용 예) 탑승 앱, 찌르기 앱, 아바타 특수 효과 등

Image

Example

타이머

Example (Timer)


Type-B

Type-B는 미니 게임에서 목표를 알려줄 때 자주 쓰이며, 두 줄로 된 긴문장을 쓸 때 주로 사용합니다.

이미지

예제 코드

ScriptMap

ScriptApp 클래스는 아래와 같이 2개의 카테고리로 구성되어있습니다

Map과 관련된 속성 값들이 모여있는 카테고리 입니다.

Map에 타일 효과나, 오브젝트 설치 등 맵과 관련된 편리한 기능을 제공하는 함수들이 모여있는 카테고리 입니다.

ScriptPlayer

ScriptApp 클래스는 아래와 같이 2개의 카테고리로 구성되어있습니다

플레이어와 관련된 속성 값들 입니다.

맵 참가자의 이름이나 위치를 조회하거나, 캐릭터의 외형이나 이동 속도 변경 등 다양한 활용한 가능함 함수들이 모여있는 카테고리 입니다.

맵에 참가한 플레이어에게 UI나 사운드를 출력하거나, 특정 위치로 이동시키는 등 편리한 기능을 제공하는 함수들이 모여있는 카테고리입니다.

Type-A

Type-A는 미니게임의 종류를 알려줄 때 인트로 Custom Label로 자주 사용됩니다.

이미지

예제코드

모바일버튼 이미지 변경하기

모바일 버튼의 이미지를 App.loadSpriteSheet로 불러온 이미지로 변경 할 수 있습니다.

참고: 모바일 점프버튼의 이미지 크기는 180 x 180px 입니다.

ScriptWidget

ScriptWidget 클래스는 아래와 같이 3개의 카테고리로 구성되어있습니다.

Widget 과 관련된 속성 값들이 모여있는 카테고리입니다.

현재는 위젯의 id 필드만 가지고 있으며, 업데이트 시 추가 될 수 있습니다.

위젯에서 App으로 데이터를 보낼 때 동작 하는 함수들이 모여있는 카테고리입니다. onMessage 함수가 있으며, 업데이트 시 추가 될 수 있습니다.

위젯으로 데이터를 보내는 함수, 위젯을 종료하는 함수 등이 모여있는 카테고리입니다.

let _timer = 90;
let _stateTimer = 0;

App.onUpdate.Add(function(dt){
	_stateTimer += dt;
	
	if(_stateTimer >= 1){
		_stateTimer = 0;
		_timer -= 1;
	}

	
	if(_timer <= 0){
			// time over then...
	}
})
let _players = App.players;

// 플레이어가 스페이스에 입장 했을 때 이벤트
App.onJoinPlayer.Add(function(p) {
  p.tag = {
      sturn : false, // 스턴에 걸림 여부, false
      sTime : 2, //스턴 효과 2초로 설정
  };
	_players = App.players;
});

// 플레이어가 다른 플레이어를 공격혔을 때 (Z키)
App.onUnitAttacked.Add(function(sender, x, y, target) {
    if(!target.tag.sturn)
    {
        target.tag.sturn = true; // 스턴에 걸림 여부를 true로 변경
        target.moveSpeed = 0; // 이동속도를 0으로 변경
        target.sendUpdated();
    }
});

App.onUpdate.Add(function(dt){
	for(let i in _players) {
		let p = _players[i];
		// 스턴에 걸림 여부가 true 이면
		if(p.tag.sturn)
		{
		    p.tag.sTime -= dt; //스턴 효과 지속시간이 0이 될 때까지 update마다 dt만큼 빼줌
		    if(p.tag.sTime <= 0) // 스턴 효과 지속시간이 0 이하가 되면,
		    {
		        p.tag.sturn = false; // 스턴에 걸림 여부를 false로 변경
		        p.tag.sTime = 2; // 지속시간 2초로 초기화
		        p.moveSpeed = 80; // 이동속도 정상화
		        p.sendUpdated();
		    }
		}
	}
});

// 플레이어가 스페이스를 나갔을 때 이벤트
App.onLeavePlayer.Add(function(p) {
    p.moveSpeed = 80; // 이동속도 정상화
    p.sendUpdated();
    _players = App.players;
});
ZEP Script 배포 가이드
Field
Method
Field
Methods
Field
Event Listeners
Methods
// App이 최초로 시작될 때
App.onInit.Add(function(){
	// 이 시점에 App에는 플레이어들이 참가하지 않은 상태
  // App의 나머지 필요한 부분을 초기화시킨다.
});

// 플레이어가 들어올 때
App.onJoinPlayer.Add(function(player){
  // 해당하는 모든 플레이어가 이 이벤트를 통해 App에 입장
});

// 플레이어가 모두 입장한 뒤에 한번 호출
App.onStart.Add(function(){
  // App에서 원하는 플레이어 속성값을 부여할 수 있다.
});

// 플레이어가 떠날 때
App.onLeavePlayer.Add(function(player){
  // 플레이어가 단순히 중간에 나갔을 때
  // App이 종료될 때에서 이 이벤트를 통해 모두 App에서 퇴장합니다.
})

// 매 20ms(0.02초) 마다 실행
App.onUpdate.Add(function(){
})

// App이 종료될 때
App.onDestroy.Add(function(){
   // 이미 모든 플레이어가 App에서 나간 상태
   // App을 나머지를 정리한다.
})
function showLabelTypeB(player, key, text1, text2) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
     * Size-based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 90 : 50;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "16px" : "18px"};
    font-weight: 700; 
    color: #FFEB3A;`;

    const secondRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700;
    color: white;`;

    const customLabelOption = {
        key: key,
        borderRadius: "12px",
        fontOpacity: false,
        padding: "8px",
    };

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${secondRowStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}
function showLabelTypeA(player, key, text1, text2) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // gap between top and label ( mobile 60px,  pc 48px ) 60px, pc는 48px이 되도록 설정한 값
    /**
     * Size-based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 70 : 28;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`;

    const secondRowStyle = `
    font-size: ${isMobile ? "16px" : "24px"};
    font-weight: 800;
    color: #FFEB3A;`;

    const customLabelOption = {
        key: key,
        borderRadius: "12px",
        fontOpacity: false,
        padding: "8px",
    };

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${secondRowStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

let button_image = App.loadSpritesheet("button_image.png");
let button;

App.onStart.Add(function () {
	// button 추가
	button = App.addMobileButton(8, 145, 75, function (player) {
		App.sayToAll(`${player.name}, 님이 버튼을 눌렀습니다.`)
	});

	button.image = button_image;
	button.sendUpdated();
});

모바일 버튼 이미지 변경 예시
function openWidget(p)
{
    //Check if player is mobile or not
    if(p.isMobile)
        //Set the widget's name, anchor position, width, and height.
        p.tag.widget = p.showWidget('widget.html','sidebar',300,550);
    else
        p.tag.widget = p.showWidget('widget.html','sidebar',280,520);

    //Send data to widget
    p.tag.widget.sendMessage({
        isMobile : p.isMobile,
    })

    //Register events for messages sent from widgets
    p.tag.widget.onMessage.Add(function(sender, msg) {
        switch (msg.type)
        {
            case 'close':
                if(p.tag.widget)
                {
                    p.tag.widget.destroy();
                    p.tag.widget = null;
                }
                
                break;
            case 'submit':
                let choiceItem = msg.choice;
                p.showAlert('Item' + choiceItem);
                break;
        }
    })
}

//Event when a player joins the app
App.onJoinPlayer.Add(function(p) {
    //Reset the player's tag value
    p.tag = {};
});
// Action when the player clicks the sidebar app
App.onSidebarTouched.Add(function(p) {
    openWidget(p);
});

17KB
ImageList.zip
archive

Storage

App storage는 스페이스 내의 App 데이터의 저장공간 입니다.

App storage에 데이터 저장하기

권장하는 데이터 저장 방식

App.setStorage(string);

App.setStorage 함수는 기존 App storage 데이터 저장 방식을 보완한 데이터 저장 함수입니다.

권장하지 않는 데이터 저장 방식

App.storage = string; App.save();

위 방식은 앱이 같은 스페이스 내 여러 맵에서 실행중인 경우, 데이터를 덮어쓰는 등의 문제가 발생할 수 있어 사용을 권장드리지 않습니다.

App storage 값 읽어오기

App.getStorage(function(){})

App.getStorage 함수는 앱이 실행중인 같은 스페이스 내 다른 맵의 App storage 데이터 변경 여부를 체크하여 같은 데이터를 가지도록 동기화 해주는 함수입니다.

App.getStorage 함수는 비동기 함수이기 때문에 App.getStorage 함수 다음 라인에 App.storage를 사용하는 코드를 작성할 경우 동기화를 보장할 수 없습니다.

App storage 사용 예제

아래 예제코드를 사이드바 앱으로 설치해 같은 스페이스 내 다른 맵에서 Q를 눌러 count 값이 동기화되는지 확인해보세요.


App.onStart.Add(function(){
	if(App.storage == null){
		App.setStorage(JSON.stringify({count: 0}))
	}
})
// Q 키를 누르면 동작하는 함수
App.addOnKeyDown(81,function(player){
	// App.storage를 최신화 한 후 callback 함수를 실행합니다.
	App.getStorage(function () {
		let appStorage = JSON.parse(App.storage);
		appStorage.count += 1;
		App.sayToAll(`count: ${appStorage.count}`)
		// App.setStorage를 사용해 변경내용을 저장합니다.
		App.setStorage(JSON.stringify(appStorage));
	});
	// App.getStorage 함수 다음 라인에 App.storage 코드를 작성할 경우 동기화를 보장할 수 없습니다.
	App.sayToAll(App.storage);
})

ScriptApp

ScriptApp 클래스는 아래와 같이 5개의 카테고리로 구성되어있습니다. 각 카테고리의 제목을 선택하시면, 자세한 내용을 확인할 수 있습니다.

Lifecycle

앱이 시작되어, 실행되고 종료될 때까지를 하나의 생애 주기(Lifecycle)라고 합니다. 앱이 시작될 때, 실행 중일 때 그리고 종료될 때 등의 상황에서 필요한 동작들을 실행해 전체적인 앱의 생애주기를 만들어 나갈 수 있는 함수들이 모여있는 카테고리 입니다.

Field

스페이스나 맵, 플레이어 정보 등을 조회하거나, 저장공간을 활용할 수 있는 App과 관련된 속성이 모여있는 카테고리입니다.

Event Listeners

플레이어가 지정된 채팅을 입력하거나, 특정한 오브젝트를 공격할 와 같이 맵 내에서 발생할 수 있는 다양한 상황을 감지하여 동작하는 함수들이 모여있는 카테고리 입니다.

Callbacks

스크립트 개발자가 지정한 키를 플레이어가 누르거나 특정 지점에 도착했을 때와 같이 조건을 설정하고 조건이 만족되었을 때 동작하는 함수들이 모여있는 카테고리 입니다.

Methods

화면에 UI를 표시, 유저 이동 또는 강퇴, 사운드 재생 등 편리한 기능을 제공하는 함수들이 모여있는 카테고리 입니다.

Field

소개

맵과 관련된 속성 값을 조회 할 수 있습니다. 현재는 맵의 너비(width), 높이(height) 필드를 조회할 수 있습니다

🔒 아이콘이 있는 필드는 수정이 불가능한 읽기 전용 필드입니다.

이름
설명

🔒 width

맵의 너비 값을 가져옵니다.

🔒 height

맵의 높이 값을 가져옵니다

📚 API 설명 및 예제

width & height

Map.width : Number Map.height : Number

맵의 너비와 높이 값을 가져옵니다.

예제

맵의 너비, 높이 값을 채팅창에 출력해보기.

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	App.sayToAll(`맵 가로 크기: ${Map.width}`);
	App.sayToAll(`맵 세로 크기: ${Map.height}`);
});

위젯에서 사용가능한 문법

새 창에서 웹페이지 열기

위젯의 <script> 태그 안에 다음과같은 코드를 입력해 새 창에서 웹페이지를 열 수 있습니다.

let url = "https://zep.us/";
window.parent.postMessage({
	type: 'ScriptAction:OPEN_WINDOW',
	link: url,
	zepSystem: true
}, '*');

Type-C

Type-C는 페인트 맨 팀 전 게임에서 사용된 Custom Label로, 대결 구도의 게임에서 자주 사용됩니다.

이미지

예제 코드

function showLabelTypeC(player, key, text1, text2) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
    /**
     * size-based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 60 : 20;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`;

    const secondRowStyle = `
    font-size: ${isMobile ? "16px" : "24px"};
    font-weight: 800;
    color: white;`;

    const customLabelOption = {
        key: key,
        borderRadius: "12px",
        fontOpacity: false,
        padding: "8px",
    };

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${secondRowStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

Type-H

Type-H는 제한 시간이 있는 게임에서 남은 시간을 표시할 때 쓰는 Custom Label입니다.

이미지

예제 코드

function showLabelTypeH(player, key, text1, text2) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
    /**
     * size-based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 60 : 20;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    align-items: center;
    justify-content: center;
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`;

    const highlightSpanStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: #FFEB3A;`; // Show red(#FF5353) when timeout is about to expire 


    const customLabelOption = {
        key: key,
        borderRadius: '12px',
        fontOpacity: false,
        padding: '8px',
    }

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${highlightSpanStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

Type-E

Type-E는 가위바위보 게임에서 나온 Custom Label로, 이미지를 삽입할 수 있습니다.

이미지

예제 코드

function showLabelTypeE(player, key, text1, text2) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
    /**
     * size-based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 60 : 20;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`;

    const secondRowStyle = `
    font-size: ${isMobile ? "48px" : "64px"};
    font-weight: 800;
    color: white;`;

    const customLabelOption = {
        key: key,
        borderRadius: "12px",
        fontOpacity: false,
        padding: "8px",
    };

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${secondRowStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

Type-D

Type-D는 Type-C와 마찬가지로 팀 전 점수를 기록할 때 자주 쓰이며, 3줄로 나누어 쓸 수 있습니다.

이미지

예제 코드

function showLabelTypeD(player, key, text1, text2, text3) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
    /**
     * size based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 60 : 20;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "16px" : "18px"};
    font-weight: 700; 
    color: #FFEB3A;`;

    const secondRowStyle = `
    font-size: ${isMobile ? "16px" : "18px"};
    font-weight: 700; 
    color: white;`;

    const thirdRowStyle = `
    font-size: ${isMobile ? "16px" : "24px"};
    font-weight: 800;
    color: white;`;

    const customLabelOption = {
        key: key,
        borderRadius: "12px",
        fontOpacity: false,
        padding: "8px",
    };

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${secondRowStyle}">${text2}</span>
        <span style="${thirdRowStyle}">${text3}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

Type-J

Type-J는 레벨이 있는 미니 게임에서 자주 쓰이는 Custom Label 입니다.

똥 피하기 게임에서 쓰인 것을 볼 수 있습니다.

이미지

예제 코드

function showLabelTypeJ(player, key, text1, text2, text3) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
    /**
     * size based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 60 : 20;
    const labelDisplayTime = 300000;

    const directionColumnStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    justify-content: center; 
    text-align: center;`.trim();

    const rowStyle = `
    display: flex; 
    align-items: center;
    justify-content: center;
    text-align: center;
    `.trim();

    const spanStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`.trim();

    const highlightSpanStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: #FFEB3A;`.trim(); // Show red(#FF5353) when timeout is about to expire 

    const customLabelOption = {
        key: key,
        borderRadius: '12px',
        fontOpacity: false,
        padding: '8px',
    }

    let htmlStr = `
<span style="${directionColumnStyle}">
    <span style="${spanStyle}">${text1}</span>
    <span style="${rowStyle}">
        <span style="${spanStyle}">${text2}</span>
        <span style="${highlightSpanStyle}">${text3}</span>
    </span>
</span>`;

    player.showCustomLabel(htmlStr.trim(), 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

Type-G

Type-G는 한 줄 text를 사용할 수 있는 Custom Label입니다.

이미지

예제 코드

function showLabelTypeG(player, key, text) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
    /**
     * size based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 60 : 20;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`;

    const customLabelOption = {
        key: key,
        borderRadius: '12px',
        fontOpacity: false,
        padding: '8px',
    }

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

외부 API 통신하기

외부 API에 GET, POST 등의 요청을 인자와 함께 보낼 수 있습니다.

httpGet

한국어 별명 생성기 API를 이용해 입장하는 플레이어의 닉네임을 바꿔보기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function (player) {
	App.httpGet(
		"https://nickname.hwanmoo.kr/?format=json&count=1&max_length=6&whitespace=_",
		null,
		function (res) {
			// 응답 결과를 JSON 오브젝트로 변경
			let response = JSON.parse(res);
			player.name = response.words[0];
			player.sendUpdated();
		}
	);
});

httpPost

앱에서 보낸 헤더와 데이터를 응답으로 받아 채팅창에 출력해보기.

// q를 눌렀을 때 실행되는 함수
App.addOnKeyDown(81, function (player) {
	App.httpPost(
		"https://postman-echo.com/post",
		{
			"test-header": "zep",
		},
		{
			name: "zepscript",
		},
		(res) => {
			let response = JSON.parse(res);
			App.sayToAll(`보낸 헤더: ${response.headers["test-header"]}`, 0xffffff);
			App.sayToAll(`보낸 데이터: ${response.form.name}`, 0xffffff);
		}
	);
});

참고

- 튜토리얼의 App type은 미니게임(Mini game)을 권장합니다.

- .js파일의 파일이름은 반드시 main 이어야 합니다. main.js 파일을 준비합니다.

- 배포 방법을 아직 모르신다면, ZEP Script 배포 가이드를 참고해주세요.

부록

Type-F

Type-F는 ox 퀴즈, 퀴즈 골든벨에서 자주 사용되는 Custom Label로 본문 text를 길게 넣을 수 있습니다.

이미지

예제 코드

function showLabelTypeF(player, key, text1, text2) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; // 60px from the top on mobile and 48px on PC.
    /**
     * size-based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 90 : 50;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "16px" : "18px"};
    font-weight: 700; 
    color: #FFEB3A;`;

    const secondRowStyle = `
    font-size: ${isMobile ? "16px" : "18px"};
    font-weight: 700;
    color: white;`;

    const thirdRowStyle = `
    font-size: ${isMobile ? "11px" : "13px"};
    font-weight: 700;
    color: #D5D9E0;
    margin-top: 18px;
    `;

    const customLabelOption = {
        key: key,
        borderRadius: '12px',
        fontOpacity: false,
        padding: '8px',
    }

    let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${secondRowStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}

ZEP 스크립트 활용 사례

🕹️ 게임

ZEP 스크립트를 활용해 개발한 게임 사례를 확인해 보실 수 있습니다.

🗃 ZEP 스크립트 활용 사례

ZEP Script를 활용해 다양하고 재미있는 게임을 만들 수 있습니다.

ZEP Script를 활용한 게임 개발 사례들을 만나보세요.

강일병 구하기 게임 : 링티 X 바람의나라:연

바람:연 X ZEP X 링티가 콜라보하여 ‘강일병 구하기’ 라는 퀘스트 형태의 게임을 만들어 선보인 사례입니다.

팩맨 미니게임 : 링티 X 바람의나라:연

바람:연 X ZEP X 링티가 콜라보하여 ‘팩맨’ 게임에 다람쥐, 도토리 등 바람의나라:연 이미지를 입혀 귀엽게 표현한 미니게임 입니다

슈퍼캣 채용박람회에서 진행된 페인트맨 게임

슈퍼캣 채용박람회에서 진행된 페인트맨 게임입니다.

ZEP Script를 활용하면 100명 이상이 참가하는 단체 게임 개발도 가능합니다!

ZEP 유저가 개발한 오목 게임

ZEP 유저들이 ZEP Script를 활용해 개발한 게임 사례가 많아지고 있습니다.

ZEP 캐릭터를 움직여 두는 오목! 재미있지 않나요?

🎉 행사

행사에서 ZEP 스크립트를 활용한 사례를 확인해 보실 수 있습니다.

🗃 ZEP Script 활용 사례

ZEP Script를 활용하면 좀 더 인터랙티브한 행사를 기획할 수 있습니다.

ZEP Script를 활용한 행사 사례들을 만나보세요.

클레이튼 뮤지엄 ( Klaytn Museum )

클레이튼의 기술적 강점 등을 ZEP Script를 활용해 효과적으로 보여준 사례입니다.



Type-I

Type-I 는 미니 게임에서 우승자가 나오거나 플레이어가 특정될 때 나오는 Custom Label 입니다.

이미지

예제 코드

사이드바 앱

사이드바 앱 위젯을 표시하고, x 버튼을 눌러 닫는 간단한 예제입니다.

위젯에 이미지를 넣으려면 예제 html 코드처럼 이미지를 base64 인코딩해서 img 태그로 감싸주어야 합니다.

1) 파일

2) main.js

3) 사이드바 앱이 실행된 모습

Text List

Text List 형 위젯은 텍스트와 서브 텍스트를 같이 사용한 UI입니다.

활용 예 ) 내 포인트 앱 이용자 내역

Image

Example

Field

소개

위젯과 관련된 속성 값을 조회 할 수 있습니다.

🔒 아이콘이 있는 필드는 수정이 불가능한 읽기 전용 필드입니다.

이름
설명

📚 API 설명 및 예제

id

widget.id

위젯의 아이디 값을 가져옵니다.

예제

위젯 아이디 값 출력해보기


부록

🎡ZEP 스크립트 활용 사례
🗺️스페이스와 맵 이해하기
⌨️자바스크립트 키코드 표
🎨스프라이트시트 이해하기
🌀TileEffectType 상세 설명
📌기준 좌표
🛰️외부 API 통신하기
🔰URL 쿼리스트링 활용하기
🪧위젯에서 사용가능한 문법
🔘모바일버튼 이미지 변경하기
✳️ZEP 스크립트 상호작용 오브젝트 가이드
📇오브젝트 npcProperty
ZEP 스크립트로 구현한 컨베이어벨트
ZEP 스크립트로 구현한 보안 체험관
function showLabelTypeI(player, key, text1, text2) {
    const isMobile = player.isMobile;
    const topGap = isMobile ? 10 : -2; //60px from the top on mobile and 48px on PC.
    /**
     * size-based @labelPercentWidth
     * XL: isMobile ? 90 : 50;
     * L: isMobile ? 80 : 40;
     * M: isMobile ? 70 : 28;
     * S: isMobile ? 60 : 20
     */
    const labelPercentWidth = isMobile ? 60 : 20;
    const labelDisplayTime = 300000;

    const parentStyle = `
    display: flex; 
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    `;

    const firstRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`;

    const highlightSpanStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: #74FADA;
    `;

    const customLabelOption = {
        key: key,
        borderRadius: '12px',
        fontOpacity: false,
        padding: '8px',
    }

    let htmlStr = `<span style="${parentStyle}">
        <span style="${highlightSpanStyle}">${text1}</span>
        <span style="${firstRowStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}
// 사이드바 앱이 터치(클릭)되었을 때 동작하는 함수
App.onSidebarTouched.Add(function (p) {
	p.tag.widget = p.showWidget("widget.html", "sidebar", 350, 350);
	p.tag.widget.onMessage.Add(function (player, data) {
		if (data.type == "close") {
			player.showCenterLabel("위젯이 닫혔습니다.");
			player.tag.widget.destroy();
			player.tag.widget = null;
		}
	});
});

// 플레이어가 입장 할 때 동작하는 함수
App.onJoinPlayer.Add(function (p) {
	p.tag = {
		widget: null,
	};
});

// 플레이어가 퇴장 할 때 동작하는 함수
App.onLeavePlayer.Add(function (p) {
	if (p.tag.widget) {
		p.tag.widget.destroy();
		p.tag.widget = null;
	}
});
16KB
예제_사이드바.zip
archive
/**
 * Widget Handle Function
 * @param player Script Player Object
 * @returns 
 */
function openWidget(player)
{
    if (player.isMobile) {
        player.tag.widget = player.showWidget('widget.html', 'sidebar', 300, 500);
    } else {
        player.tag.widget = player.showWidget('widget.html', 'sidebar', 290, 460);
    }

    // send message to widget
    player.tag.widget.sendMessage({
        isMobile : player.isMobile,
    });

    // handle message from child frame(widget)
    player.tag.widget.onMessage.Add(function(sender, msg) {
        switch (msg.type)
        {
            case 'close':
                if(sender.tag.widget)
                {
                    sender.tag.widget.destroy();
                    sender.tag.widget = null;
                }
                break;
            case 'submit':
                sender.showAlert('OK');
                break;
        }
    })
}

App.onJoinPlayer.Add(function(player) {
    // Initialize tag properties of player
    player.tag = {};
});

App.onSidebarTouched.Add(function(player) {
    openWidget(player);
});

4KB
TextList.zip
archive

🔒 id

위젯의 id 값을 가져옵니다.

let _widget = null;
// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	_widget = player.showWidget("sample.html","top",300,300);
	App.sayToAll(`위젯 아이디: ${_widget.id}`)
});
15KB
sample.zip
archive
위젯에서 사용가능한 문법

방탈출 타이머

Example (방탈출)타이머

예제 코드

6KB
Timer_Sample.zip
archive
// Set game time
const GAME_TIME = 60 * 90;
let ONE_SEC = 1;

App.onJoinPlayer.Add(function (player) {
  player.tag = {};
});


App.onUpdate.Add(function (dt) {
  if (ONE_SEC > 0) {
    ONE_SEC -= dt;
  } else {
    ONE_SEC = 1;
    for (let player of App.players) {
      
      if (player.tag.playtime) {
        if(player.tag.startTimer){
          player.tag.playtime += ONE_SEC;
        }
      } else {
        player.tag.playtime = ONE_SEC;
      }
      // When GAME_TIME is exceeded
      if (player.tag.playtime >= GAME_TIME) {
        let minutes = Math.floor((GAME_TIME - player.tag.playtime) * -1 / 60);
        let seconds = Math.floor((GAME_TIME - player.tag.playtime) * -1 % 60);
        let minutes_string = minutes < 10 ? "0" + String(minutes) : String(minutes);
        let seconds_string = seconds < 10 ? "0" + String(seconds) : String(seconds);

        showTimerLabel(player, "main", "⏰ Overtime : ", `${minutes_string} : ${seconds_string}`);
      }
      else {
        let minutes = Math.floor((GAME_TIME - player.tag.playtime) / 60);
        let seconds = Math.floor((GAME_TIME - player.tag.playtime) % 60);
        let minutes_string = minutes < 10 ? "0" + String(minutes) : String(minutes);
        let seconds_string = seconds < 10 ? "0" + String(seconds) : String(seconds);

        showTimerLabel(player, "main", "⏰ Remaining time : ",`${minutes_string} : ${seconds_string}`);
      }
    }
  }
});



// Set timer start location name
App.addOnLocationTouched("start_timer", function (player) {
  player.tag.startTimer = true;
});

// Set timer end location name
App.addOnLocationTouched("end_timer", function (player) {
  player.tag.startTimer = false;
});

// Set timer widget view
function showTimerLabel(player, key, text1, text2) {
	const isMobile = player.isMobile;
	const topGap = isMobile ? 10 : -2; 

	const labelPercentWidth = isMobile ? 50 : 20;
	const labelDisplayTime = 300000;

	const parentStyle = `
    display: flex; 
    align-items: center;
    justify-content: center;
    text-align: center;
    `;

	const firstRowStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: white;`;

	const highlightSpanStyle = `
    font-size: ${isMobile ? "14px" : "18px"};
    font-weight: 700; 
    color: #FFEB3A;`;

    const customLabelOption = {
        key: key,
        borderRadius: '12px',
        fontOpacity: false,
        padding: '8px 24px',
    }

	let htmlStr = `<span style="${parentStyle}">
        <span style="${firstRowStyle}">${text1}</span>
        <span style="${highlightSpanStyle}">${text2}</span>
    </span>`;

    player.showCustomLabel(htmlStr, 0xffffff, 0x27262e, topGap, labelPercentWidth, 0.64, labelDisplayTime, customLabelOption);
}
```

방탈출 타이머 지정 영역

방탈출 타이머를 사용하려면 지정 영역을 꼭 설정해주세요

  • start_timer : 이 영역을 지나면 타이머가 시작됩니다.

  • end_time : 이 영역을 지나면 타이머가 종료됩니다.

ZEP Script 예제 코드

ZEP Script 개발에 도움을 주는 다양한 예제 코드들을 확인해보세요

타이머


ZEP에서 동작하는 타이머를 만들 수 있습니다.

개인의 개발 스타일에 맞게 수정해서 사용할 수 있습니다.

⏰ 타이머 예제

방탈출 타이머


방탈출 스페이스에서 동작하는 타이머를 만들 수 있습니다.

필수 지정 영역을 적용해서 방탈출 타이머를 만들어보세요

🕐 방탈출 타이머 예제

사이드바 앱


사이드바 앱은 PC 좌측 사이드바에 아이콘으로 표시되는 형태의 앱 입니다.

다음 예제를 개인의 개발 스타일에 맞게 수정해 사이드바 앱을 개발하실 수 있습니다..

🤛 사이드바 앱 예제

좀비 게임


[하단바] > [미디어 추가] > [미니게임]에서 실제로 작동하는 좀비 게임 스크립트입니다.

개인의 개발 스타일에 맞게 수정해서 사용할 수 있습니다.

🧟 좀비게임 예제

페인트맨 게임


[하단바] > [미디어 추가] > [미니게임]에서 실제로 작동하는 페인트맨 게임 스크립트입니다.

개인의 개발 스타일에 맞게 수정해서 사용할 수 있습니다.

🎨 페인트맨 게임 예제

초성 퀴즈


[하단바] > [미디어 추가] > [미니게임]에서 실제로 작동하는 초성 퀴즈 스크립트입니다.

개인의 개발 스타일에 맞게 수정해서 사용할 수 있습니다.

🙆‍♀️ 초성 퀴즈 예제

똥피하기 게임


[하단바] > [미디어 추가] > [미니게임]에서 실제로 작동하는 똥피하기 게임 스크립트입니다.

개인의 개발 스타일에 맞게 수정해서 사용할 수 있습니다.

💩 똥피하기 게임 예제

결투 게임


[하단바] > [미디어 추가] > [미니게임]에서 실제로 작동하는 결투 게임 스크립트입니다.

개인의 개발 스타일에 맞게 수정해서 사용할 수 있습니다.

🥊 결투 게임 예제

달리기


[하단바] > [미디어 추가] > [미니게임]에서 실제로 작동하는 달리기 게임 스크립트입니다.

개인의 개발 스타일에 맞게 수정해서 사용할 수 있습니다.

🏃 달리기 예제

Time

소개

ZEP 서버의 시간을 조회하는 등 시간 데이터를 가져오는 함수를 모아놓은 클래스입니다.

Methods

이름
설명

getTime

ZEP 서버의 현재 시간을 milliseconds 단위의 값으로 리턴합니다.

getUtcTime

현재 UTC 시간을 milliseconds 단위의 값으로 리턴합니다.

getTimeInterval

파라미터로 입력한 두 시간의 차이를 계산해 값을 리턴합니다.

getTime

Time.getTime()

ZEP 서버의 현재 시간을 milliseconds 단위의 값으로 리턴합니다.

예제

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.q, (player)=>{
    player.sendMessage(`Server Time: ${Time.getTime()}`);
});

getUtcTime

Time.getUtcTime()

현재 UTC 시간을 milliseconds 단위의 값으로 리턴합니다.

예제

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.q, (player)=>{
    player.sendMessage(`UTC Time: ${Time.getUtcTime()}`);
});

getTimeInterval

Time.getTimeInterval(timeA: number, timeB: number, returnType: DateType)

timeB - timeA를 계산하고, 그 결과를 지정한 returnType으로 반환합니다.

파라미터

이름
타입
설명

timeA, timeB

Number

차이를 계산할 시간 (milliseconds)

returnType

DateType

DateType.MILLISECONDS ( ms 단위 )

DateType.SECONDS (초 단위 )

DateType.MINUTES ( 분 단위 )

DateType.HOURS ( 시간 단위 )

DateType.DAYS ( 일 단위 )

예제

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.q, (player)=>{
    const currentTime = Time.getTime();
    const utcTime = Time.getUtcTime();
    player.sendMessage(`timeInterval: ${Time.getTimeInterval(currentTime, utcTime, DateType.MILLISECONDS)}`);
})

Custom Label 예제 코드

Cutom Label 예제 코드를 통해 미니게임에서 자주 쓰이는 Label을 구현할 수 있습니다.

예제코드

예제코드를 다운받아서 자신의 스페이스에 노말 앱으로 설치해보세요

0-9까지 숫자를 누르면 타입에 따른 Custom Label이 보여집니다.

2KB
main.zip
archive

타입 별 예제코드 보러가기

Type-A >

Type-A는 미니게임의 종류를 알려줄 때 인트로 Custom Label로 자주 사용됩니다.

Type-B >

Type-B는 미니 게임에서 목표를 알려줄 때 자주 쓰이며, 두 줄로 된 긴문장을 쓸 때 주로 사용합니다.

Type-C >

Type-C는 페인트 맨 팀 전 게임에서 사용된 Custom Label로, 대결 구도의 게임에서 자주 사용됩니다.

Type-D >

Type-D는 Type-C와 마찬가지로 팀 전 점수를 기록할 때 자주 쓰이며, 3줄로 나누어 쓸 수 있습니다.

Type-E >

Type-E는 가위바위보 게임에서 나온 Custom Label로, 이미지를 삽입할 수 있습니다.

Type-F >

Type-F는 ox 퀴즈, 퀴즈 골든벨에서 자주 사용되는 Custom Label로 본문 text를 길게 넣을 수 있습니다.

Type-G >

Type-G는 한 줄 text를 사용할 수 있는 Custom Label입니다.

Type-H >

Type-H는 제한 시간이 있는 게임에서 남은 시간을 표시할 때 쓰는 Custom Label입니다.

Type-I >

Type-I 는 미니 게임에서 우승자가 나오거나 플레이어가 특정될 때 나오는 Custom Label 입니다.

Type-J >

Type-J는 레벨이 있는 미니 게임에서 자주 쓰이는 Custom Label 입니다.

Ranking List

Ranking List 형 위젯은 플레이어의 랭킹을 정렬한 UI입니다.

활용 예 ) 달리기 공식 맵, 결투장 공식 맵, 승부 예측 앱, 포인트 앱

Image

Example

5KB
RankingBoard.zip
archive
const dummyData = {
    'a': {
        id: 'a',
        name: 'user1',
        point: 100,
    },
    'b': {
        id: 'b',
        name: 'user2',
        point: 200,
    },
    'c': {
        id: 'c',
        name: 'user3',
        point: 400,
    },
    'd': {
        id: 'd',
        name: 'user4',
        point: 1000,
    },
    'e': {
        id: 'e',
        name: 'user5',
        point: 2000,
    },
    'f': {
        id: 'f',
        name: 'user6',
        point: 4000,
    },
};
function openWidget(p)
{
    //Check if player is mobile or not
    if (p.isMobile)
        //Set the widget's name, anchor position, width, and height.
        p.tag.widget = p.showWidget('widget.html', 'sidebar', 350, 480);
    else
        p.tag.widget = p.showWidget('widget.html', 'middle', 400, 448);
    
    //Send data to widget
    p.tag.widget.sendMessage({
        data: dummyData,
        id: p.id,
        isMobile: p.isMobile,
    });
    
    //Register events for messages sent from widgets
    p.tag.widget.onMessage.Add(function (sender, msg) {
        if (msg.type == "close") {
            if (sender.tag.widget) {
                sender.tag.widget.destroy();
                sender.tag.widget = null;
            }
        }
    });
}

//Event when a player joins the app
App.onJoinPlayer.Add(function(p) {
    //Reset the player's tag value
    p.tag = {};
});

// Action when the player clicks the sidebar app
App.onSidebarTouched.Add(function(p) {
    openWidget(p);
});

ZEP Script 배포 가이드

이 문서는 ZEP 스크립트로 만든 앱을 적용하는배포 과정에 대해 설명합니다.

STEP 1.

ZEP Script로 개발한 코드(main.js), 사용할 이미지 파일, 위젯 파일들을 선택한 후 압축합니다.

주의:

- 앱의 파일명과 형식은 반드시 “main.js”로 지정해주어야 합니다.

- 폴더를 압축하는 것이 아니라 파일을 멀티 선택한 후 압축해야 합니다.

- *.zip 형식의 확장자를 지원합니다.

예시 파일

STEP 2.

ZEP에 회원가입 또는 로그인 후, 페이지에서 [프로필] > [나의 앱(Beta)]를 클릭합니다

STEP 3.

에서 [앱 업로드] 버튼을 클릭합니다.

STEP 4.

앱 이름, 설명, 유형, 아이콘, 압축 파일 (스크립트, 위젯, 이미지 파일의 압축된 버전)을 설정 후 업로 버튼을 눌러줍니다.

STEP 5.

앱의 종류에 따라 원하는 맵에 설치하기.

  • 노멀 앱(Normal app):

    Owner 권한을 가진 맵의 사이드바에서 [설정] > [맵 설정] > [노말 앱 추가]에서

    적용할 수 있습니다.

  • 미니게임(Mini game):

    플레이 화면의 [좌측 사이드바] > [미니게임 버튼]을 클릭해 설치할 수 있습니다.

🚧 디버깅 및 에러 메시지

앱 실행 시, 에러 메시지는 스태프 권한 이상의 유저 채팅창에 빨간색 글씨로 노출됩니다.

스프라이트시트 이해하기

App.loadSpritesheet

frameWidth 와 frameHeight

블루맨 스프라이트 한 칸의 이미지 크기는 48픽셀 x 64픽셀 입니다.

frameWidth, frameHeight 파라미터에 48, 64를 지정하면 블루맨 스프라이트 각각의

이미지가 0번부터 41번까지 번호를 가지게 됩니다.

anims

anims는 애니메이션을 지정할 배열을 의미합니다.

캐릭터 스프라이트로 사용 할 때와 오브젝트 스프라이트로 사용 할 때 양식이 다릅니다.

예시1) 단일 이미지인 경우

스프라이트가 단일 이미지인 경우 파일 이름을 제외한 나머지 파라미터는 입력하지 않아도 됩니다.

단일 이미지도 캐릭터 이미지로 적용할 수 있습니다. 다만, 애니메이션을 지정하지 않았으니 어느 방향으로 움직여도 같은 이미지일 것 입니다.

예시2) 단일 애니메이션을 정의하는경우

단일 애니메이션을사용하는 경우에는 아래와 같이 하나의 배열로 애니메이션 프레임을 정의 할 수 있습니다. 춤추는 블루맨 오브젝트 애니메이션은 아래 코드와 같이 21번째 ~ 38번째 이미지에 해당하는 이미지 번호를 배열로 지정해 사용할 수 있습니다.

예시3) 2개 이상의 애니메이션을 정의하는 경우

여러 애니메이션을 정의하는 경우에는 아래와 같이 anims 파라미터에 { } 브라켓을 입력 한뒤 다음과 같이 애니메이션 이름에 대한 이미지 배열을 지정해주면 됩니다. 아래 예시에서는 Map.playObjectAnimationWithKey 함수를 사용해 정의한 여러 애니메이션 중 "dance"에 대한 애니메이션을 실행합니다.

예시4) 캐릭터 애니메이션 정의

캐릭터는 오브젝트와 달리 플레이어가 조작하는 키에 따라 다양한 애니메이션 실행 할 수 있습니다. 캐릭터에 지정 가능한 애니메이션의 종류는 다음과 같이 총 9가지 이며, 특정 애니메이션에 대한 이미지가 부족한 경우 생략할 수 있습니다.

✅ 추가로 사용가능한 캐릭터 애니메이션 키

앉기 모션: down_sit, left_sit, right_sit, up_sit

서있는 모션: down_idle, left_idle, right_idle, up_idle

공격 모션: down_attack, left_attack, right_attack, up_attack



오브젝트 npcProperty

npcProperty는Map.putObjectWithKey(x, y, dynamicResource, option)함수의

option 파라미터에서 지정할 수 있으며, 다음 7가지 속성을 지정할 수 있습니다.

이름
타입
설명

예제 1 - npcProperty를 사용해 오브젝트 생성하기

예제 2 - npcProperty로 체력이 깎이는 효과 구현하기

Event Listeners

소개

위젯에서 App으로 데이터를 보낼때 동작 하는 함수들 입니다.

이름
설명

📚 API 설명 및 예제

onMessage

widget.onMessage.Add(function(player, data: any){});

위젯에서 App으로 메시지를 보내면 callback 함수가 동작합니다.

파라미터

이름
타입
설명

예제

x 버튼을 눌러서 위젯창 닫는 기능 만들어보기

sample.html: 버튼과 스크립트 부분

부록

// 이미지를 객체화 시키는 함수
App.loadSpritesheet(fileName: string, frameWidth: integer, frameHeight: integer, anims: array, frameRate: integer)
let blueman = App.loadSpritesheet('blueman.png')
let blueman_dance = App.loadSpritesheet(
	"blueman.png",
	48,
	64,
	[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37], // 21번째 ~ 38번째 이미지로 애니메이션을 구성
	8
);
var blueman_sprite = App.loadSpritesheet(
	"blueman.png",
	48,
	64,
	{
		left: [5, 6, 7, 8, 9],
		up: [15, 16, 17, 18, 19],
		down: [0, 1, 2, 3, 4],
		right: [10, 11, 12, 13, 14],
		dance: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37],
		idle: [0],
	},
	8
);

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.Q, function (player) {
	Map.putObjectWithKey(10, 10, blueman_sprite, {
		overlap: true,
		movespeed: 80, // default: 80
		key: "blueman",
	});
	// "dance" 애니메이션 재생
	Map.playObjectAnimationWithKey("blueman", "dance", -1);
});
let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], // 좌방향 이동 이미지
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
    dance: [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],
    down_jump: [38],
    left_jump: [39],
    right_jump: [40],
    up_jump: [41],
}, 8);
// 플레이어가 입장하면 캐릭터 이미지가 바뀜
App.onJoinPlayer.Add(function(player){
	player.sprite = blueman;
	player.sendUpdated();
});
14KB
Zombie_Sample.zip
archive
나의 스페이스 목록
나의 앱 페이지
앱 이름과 아이콘이 적용된 모습 (미니게임)

name

string

오브젝트 위에 표시될 이름

hp

number

오브젝트의 현재 체력

hpMax

number

오브젝트의 최대 체력

gaugeWidth

number

체력 게이지바의 너비

생략시 이미지의 너비 크기로 설정됩니다.

hpColor

number

체력 게이지바의 색상 ex) 0x03ff03 (초록색)

let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9],
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
    dance: [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],
    down_jump: [38],
    left_jump: [39],
    right_jump: [40],
    up_jump: [41],
}, 8);
App.addOnKeyDown(81, function (player) {
    const objectKey = "TestBlueMan";
    const bluemanObject = Map.putObjectWithKey(18, 6, blueman, {
	npcProperty: { name: "BlueMan", hpColor: 0x03ff03, hp: 100, hpMax: 100 },
	overlap: true,
	movespeed: 100,
	key: objectKey, 
	useDirAnim: true,
        offsetX: -8,
        offsetY: -32,
    });

    Map.playObjectAnimationWithKey(objectKey, "down", -1);
});
let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], // 좌방향 이동 이미지
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
    dance: [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],
    down_jump: [38],
    left_jump: [39],
    right_jump: [40],
    up_jump: [41],
}, 8);

App.addOnKeyDown(81, function (player) {
    const objectKey = "TestBlueMan";
    const bluemanObject = Map.putObjectWithKey(18, 6, blueman, {
            npcProperty: { name: "BlueMan", hpColor: 0x03ff03, hp: 100, hpMax: 100 },
            overlap: true,
            collide: true, // ★ collid: true
            movespeed: 100, 
            key: objectKey, 
            useDirAnim: true,
            offsetX: -8,
            offsetY: -32,
    });

    Map.playObjectAnimationWithKey(objectKey, "down", -1);
});

App.onAppObjectAttacked.Add(function (p, x, y, layer, key) {
    const targetObject = Map.getObjectWithKey(key);
    targetObject.npcProperty.hp -= 10;
    if(targetObject.npcProperty.hp > 0) {
        const hpPercentage = targetObject.npcProperty.hp / targetObject.npcProperty.hpMax;
        if (hpPercentage < 0.3) {
            targetObject.npcProperty.hpColor = 0xff0000;
        } else if (hpPercentage < 0.7) {
            targetObject.npcProperty.hpColor = 0xffa500;
        }
        targetObject.sendUpdated();
    } else {
        Map.putObjectWithKey(targetObject.tileX, targetObject.tileY, null, { key: key })
    }
});
29KB
npcProperty.zip
archive
예제 파일
29KB
npcProperty2.zip
archive
예제 파일

onMessage

위젯에서 App으로 보낸 메시지를 수신 할 때 동작하는 함수입니다.

player

Player

player는 위젯을 소유한 플레이어를 가르킴 player 파라미터의 이름은 임의로 변경 가능

data

Object

data는 위젯에서 App으로 보낸 메시지를 가르킴 data 파라미터의 이름은 임의로 변경 가능

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function (player) {
	player.tag = {
		widget: null,
	};

	player.tag.widget = player.showWidget("sample.html.html", "top", 600, 500);
	player.tag.widget.onMessage.Add(function (player, msg) {
		// 위젯에서 App으로 'type: close'라는 메시지를 보내면 위젯을 파괴함
		if (msg.type == "close") {
			player.showCenterLabel("위젯이 닫혔습니다.");
			player.tag.widget.destroy();
			player.tag.widget = null;
		}
	});
});
<i onclick="closeWidget()" class="fa-solid fa-xmark"></i>
<script type="text/javascript">
	// x 버튼을 누르면 호출되는 함수
	function closeWidget() {
		// App으로 메시지를 보냄.
		window.parent.postMessage(
			{
				type: "close",
			},
			"*"
		);
	}
</script>
15KB
sample_EventListener.zip
archive
위젯에서 사용가능한 문법

ZEP Script API

이 문서는 ZEP Script 를 구성하는 4가지 클래스를 안내합니다.

소개

ZEP Script는 다음과 같은 4가지 클래스로 구성되어 있습니다.

ScriptApp

앱이 설치된 스페이스와 관련된 전반적인 기능을 담당하는 스크립트

  • Lifecycle

  • Field

  • Event Listeners

  • Callbacks

  • Methods

ScriptMap

맵에 타일이나 오브젝트를 추가, 수정, 삭제하는 기능을 담당하는 스크립트

  • Field

  • Methods

ScriptPlayer

플레이어의 설정이나 좌표 지정, 유저의 정보를 불러오는 기능을 담당하는 스크립트

  • Field

  • Methods

ScriptWidget

미리 만들어놓은 HTML을 위젯 형태로 맵 내에서 사용할 수 있는 스크립트

  • Field

  • Event Listeners

  • Methods

캐릭터 이미지 바꾸기

  1. 애니메이션이 들어간 SpriteSheet 샘플을 준비합니다. 없다면 아래 이미지를 다운 받아주세요.

*주의: 직접 제작한 경우가 아닌 SpriteSheet는 저작권에 유의해서 사용해야 합니다.

2. 위 이미지 같이 낱장의 이미지들이 동일한 규격으로 합쳐진 형태의 SpriteSheet 그림의 확장자는 png 파일이어야 합니다.

  • TIP: 캐릭터의 경우 한 동작의 그림 규격으로 48(px) x 64(px)를 권장합니다.

스프라이트 시트(sprite sheet)는 여러 개의 작은 그래픽을 정렬하여 구성한 비트맵 이미지 파일입니다. 게임 개발에서 캐릭터의 연속적인 포즈를 한 장의 이미지에 구성하여 2D 애니메이션 제작에 사용됩니다.

3. SpriteSheet를 읽는 API 로는 App.loadSpritesheet 를 사용합니다.


App.loadSpritesheet(fileName: string, frameWidth: integer, frameHeight: integer, anims: array, frameRate: integer): ScriptDynamicResource
  • 첫 번째 인자(fileName): 확장자가 포함된 SpriteSheet 시트의 파일 명을 넣습니다.

  • 두 번째 인자(frameWidth): 하나의 동작이 그려져 있는 그림의 가로 크기(px)

  • 세 번째 인자(frameHeight): 하나의 동작이 그려져 있는 그림의 높이(px)

  • 네 번째 인자(anims): ZEP에서 정해져 있는 애니메이션 이름(left, right, up, down)과 각각에 사용될 그림 인덱스 넘버들

그림 순서의 인덱스 넘버는 왼쪽 상단 가로 줄 부터 0으로 시작합니다.

  • 다섯 번째 인자(frameRate): 1초에 몇 장을 돌아가게 할 것인지 정합니다.

4. player의 속성을 변경하였으므로 player.sendUpdated() 를 써주어야 실제 반영됩니다.

5. 예시 코드

// 블루맨이라는 변수를 자바스크립트 문법에 맞게 생성
let blueman = null;

// 변수에 SpriteSheet를 읽어 저장
blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], // left 라는 이미 정해진 왼쪽 방향으로 걸을 때의 애니메이션 이름
    up: [15, 16, 17, 18, 19], // 그 이름에 쓰일 전체 파일에서의 인덱스 넘버들
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
}, 8); // 1초에 8장으로 한다.

// 플레이어가 입장할 때 바로 블루맨 그림으로 교체해준다.
App.onJoinPlayer.Add(function(player){
	player.sprite = blueman;
  
  // 플레이어 속성이 변경되었으므로 호출해서 실제 반영해준다.
	player.sendUpdated();
})

6. 활용

// 변수 선언과 동시에 바로 저장이 가능하다.
let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], 
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
}, 8);

플레이어가 입장할 때가 아닌, 다른 특수한 조건일 때에도 캐릭터가 변할 수 있겠죠? 생각해보세요. 그리고 만들어 보세요!

참고

- 튜토리얼의 App type은 미니게임(Mini game)을 권장합니다.

- .js파일의 파일이름은 반드시 main 이어야 합니다. main.js 파일을 준비합니다.

- 배포 방법을 아직 모르신다면, ZEP Script 배포 가이드를 참고해주세요.

URL 쿼리스트링 활용하기

URL 쿼리스트링이란 URL의 뒤에 입력 데이터를 함께 제공하는 데이터 전달 방법 입니다. 예시) https://zep.us/play/{mapHashId}?{파라미터}={값}

URL 쿼리스트링을 이용해 ZEP 스페이스 또는 ZEP Script로 데이터를 전달 할 수 있습니다.

ZEP에서 사용 가능한 URL 쿼리스트링 파라미터

1. name

비로그인 유저 입장 시 닉네임을 name 파라미터로 전달한 값으로 설정할 수 있습니다.

  • 예시: https://zep.us/play/{mapHashId}?name=학생A

⚠️ 게스트 유저 닉네임 설정 팝업 비활성화 설정을 해주세요!

2. customData

ZEP Script player 객체에 데이터를 전달할 수 있습니다. SSO 토큰 정보 등의 유저 식별 정보를 ZEP Script로 전달하여 유저를 식별하는 등 화이트리스트 기능을 만드는 데 활용할 수 있습니다.

예제 1 customData로 플레이어 정보를 받아 적용해보기 (노멀앱, 사이드바앱 권장)

⚡예제에서 사용한 URL

https://zep.us/play/{mapHashId}?customData={"name":"커스텀유저", "moveSpeed":150, "title":"커스텀타이틀"}

예제 2

간단한 토큰기반 화이트리스트 기능 만들어보기 (노멀앱, 사이드바앱 권장)

✅ 브라우저 개발자 도구(F12)에서 다음과 같이 base64 암호화를 하여 토큰을 만들었습니다.

⚡ 예제에서 사용한 URL

https://zep.us/play/{mapHashId}?customData=JTdCJTIydG9rZW4lMjIlM0ElMjIlRUMlOUMlQTAlRUMlQTAlODAxJTJGd2hpdGVMaXN0JTIyJTdE

Methods

소개

App에서 위젯으로 데이터를 보내거나, 위젯을 파괴하는 함수입니다.

이름
설명

📚 API 설명 및 예제

Methods 함수 한 눈에 보기

sendMessage

widget.sendMessage(object: any)

App에서 위젯으로 데이터를 보냅니다.

파라미터

이름
타입
설명

예제

위젯에 텍스트와 이미지 데이터 보내기

sample.html: 텍스트, 이미지 데이터를 받아 표시하는 부분

destroy

widget.destroy()

위젯을 삭제하는 함수입니다.

파라미터

  • 없음

예제

🔥

부록

App.onJoinPlayer.Add(function (player) {
  // 전달받은 customData가 있는지 체크
	if (player.customData) {
		// customData를 오브젝트로 변환하여 사용
		let playerCustomData = JSON.parse(player.customData);
		if (playerCustomData.hasOwnProperty("name")) {
			player.name = playerCustomData["name"];
		}
		if (playerCustomData.hasOwnProperty("moveSpeed")) {
			player.moveSpeed = playerCustomData["moveSpeed"];
		}
		if (playerCustomData.hasOwnProperty("title")) {
			player.title = playerCustomData["title"];
		}
		App.sayToAll("커스텀 데이터를 적용했습니다");
		player.sendUpdated();
	}
  // 전달받은 customData가 없다면 메시지 출력 
  else {
		App.sayToAll("커스텀 데이터를 전달받지 못했습니다.");
	}
});
App.onJoinPlayer.Add(function (player) {
	player.tag = {};
	if (player.customData) {
		let token = player.customData;
		// 위젯으로 토큰을 보내 복호화하기
		player.tag.widget = player.showWidget("main.html", "topleft", 1, 1);
		player.tag.widget.sendMessage({
			type: "decode",
			token: token,
		});
		player.tag.widget.onMessage.Add(function (player, data) {
			if (data.type == "decode") {
				let decodedToken = data.decodedToken;
				App.sayToAll(decodedToken);
				// 복호화 된 코드를 받은 후 오브젝트로 변환
				let playerData = JSON.parse(decodedToken);

				let playerName = playerData["token"].split("/")[0];
				let isTrusted = playerData["token"].split("/")[1];

				if (isTrusted == "whiteList") {
					player.name = playerName;
					player.title = "인증된유저";
					player.sendUpdated();
				}
				player.tag.widget.destroy();
				player.tag.widget = null;
			}
		});
	} else {
		App.sayToAll("커스텀 데이터를 전달받지 못했습니다.");
	}
});
// 위젯에서 사용한 스크립트
window.addEventListener("message", function (e) {
  if (e.data.type == "decode") {
		// base64 토큰 복호화
    decodedToken = decodeURIComponent(atob(e.data.token));
    window.parent.postMessage({
      type: "decode",
      decodedToken: decodedToken
    }, "*")
  }
})
1KB
customData_예제.zip
archive
customData를 전달받지 못한 경우
customData를 전달받아 적용한 경우
토큰으로 인증에 성공한 경우

sendMessage

App에서 위젯으로 데이터를 보내는 함수입니다.

destroy

위젯을 삭제하는 함수입니다.

// 생성한 위젯에 메세지를 보냄
widget.sendMessage(object: any)

// 위젯을 제거
widget.destroy()

object

any

앱에서 위젯으로 보내는 데이터 Ex) { name : “test”, message : “message” }


// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function (player) {
	player.tag = {
		widget: null,
	};
	
	player.tag.widget = player.showWidget("sample.html", "top", 300, 300);
	//위젯에서 앱으로 메시지를 보내면 동작하는 함수
	player.tag.widget.onMessage.Add(function (player, data) {
		if (data.type == "close") {
			player.showCenterLabel("위젯이 닫혔습니다.");
			player.tag.widget.destroy();
			player.tag.widget = null;
		}
	});
	player.sendUpdated();
});

// q를 누르면 동작하는 함수
// 위젯에 텍스트와 blueman 이미지를 보냄
// App.addOnKeyDown 설명(링크)
App.addOnKeyDown(81, function (player) {
	if (player.tag.widget) {
		player.tag.widget.sendMessage({
			text: "블루맨",
		});
	}
});
//앱에서 위젯으로 메시지를 보내면 동작하는 함수
window.addEventListener("message", function (e) {
	let text = e.data.text;
	let content = document.getElementById("content");
	if (content) {
		while (content.hasChildNodes()) {
			content.removeChild(content.firstChild);
		}
		let content_image = document.createElement("img");
		content_image.setAttribute("src", BLUEMAN);
		content_image.setAttribute("class", "cover");
		content.append(content_image);
	}
	if (text) {
		document.getElementById("text").innerText = text;
	}
});
// 이미지로 사용할 블루맨 이미지 (base64 인코딩)
const BLUEMAN = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAABACAYAAABcIPRGAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAF+UlEQVRoge2ZX2hTVxzHPxk+lOFDbJCtHYVYmpCO7qHEsDWtyFrppkLtxDD3v8hgm7TLgw42Xeek6wSnD7XgJhvi0G1CpDphlHWdIiN3jrb40CDlRmyhKA7XGvaw9WFw9nBzbm9u7k1zb9L5sHzhkntPfuec7/fc3/md8zsXKqigggoqqKCCCiqooIL/Kzyr3L5Y7b5Wo1GddF1wUC+cVw+udr+uIYxXXXBQv4/0Tgvz/zbXQ4VoVoRoVoSo2fORkM8YCMpn46+8KFHAIyXTz+JG1MPjbx7OKYv0The0vxEt3ZNKFaCPXrMiuPfVoaIrNiuCZkWv/lDcSPh83bYuwgouhGHOyHbckCjpDTzqi+j3htHUn41ldv/Pqwdz2vmvIHy+7pyIY3cVa+P2LbidRULG+Cp/EID0WAzQYr8ss8LSnKqvCYHOhF4G+lrhiNMaR7RtkB6LrUhcosofJOBPsDSnkh6L6SLcomQBVuS9wZCtfUadATQhdQzq9d2iJAHSFSR5SVyStILRpsofBFVrpxQRbiAA0REKC0AEOhMi0jtd1IQlO2kjvdMi0JnIaQcXk9j1G+gIhfX7pTmVibEYi+p1oKOo+tXBp3JGvSMU5ueZKcc8XAuo9Xq4mxFc7uujKbwWb2AU7j/Q/ly/zr5i1mYxOUomrZKa6mPoJ4Var7uAWNJCVuv10BRuwBswRR8pxAxTuTcQpCnc4Jo8lCmMAkRfeifnWfnuc1c2TuFGurjc10di4lcAho4PlEwivq8fgFikha7hYUe8Cr0Bc0TQG20KN9AUbuDQF2dzDIwjbDe6djaH336tCLr5sBMgIr3TejxfnPqGhYVLAtPIxCItlpWLcQ2jjV07xcBqEotAZ4J/Xm4CtJW2OvwKPl83aG/FU98TJzV1a7nG+nW5kUc+W102dVJTt6jviYNDtzYLEIHORN5WQIowwJP11Rwo4+dRxs8X7NDOxqnvSxgFCJ+vWye/5tsUaz/eVWiz5ekaHiaTVu3D5kq4/4BMWnVNHpYFCJ+vO2eUM+oMf/R8Cmjb3ux22dyJp74nbikiumV3wWdJ3o3b5BAwk/cGQ2TUGX2PDkXt08XtM0PagmbwayPpHLcpE3lLAbCcYPy1MMHCwqViOxEAuhAbGIgX225B5IVRt+TlXKnv0TIzbY/UoBucnIdj/RpxaZsei+WFZqfwACLyzCbuLT5vJu6IvJz8H+xcQkkq7K3LNzw5D9HWKEdGqgBtntnMLUcCkCImrv/ipA6AGLkyAaCT+nqfD4DT5xJEW6O6oZJU2POq9nbeOL4AaGIBdrZHzO06FgBZEQCbt+/KM7z2wwVC/M3Z65Oynhi5MkFowxMA7HgriTcYYvNjV/jsw3dtO3zvkxNc+72djDrD96daAZiZveNahLlCwYxIJjHGxOPm7F1dgIT6Y/4ASASfu6DfSwHgXoR5JfYUuiTxjlCY22eGWEyO8uSGWk6fS7BjkyZkaU7l4tVJy84uXp3Ug4S0b/TX0Oiv4YVnN5J1R0dppZN8QFzu66NreJiOUBhv9GlAizZd/XH2Dwzp5I6MVHFkJGXRhDZPluZU2LSWmdk7NPpr9H+liJ3tkaKjk6OMrG33NhaTo3z5/utawf0HtO3elmOTOnqK8S3byagzedf4lu2kjp7SbZWkkteHVVkhOPE3YVykMmlVv69u3cr+gSF9cq50LiQn+7H+ODdn79Lor+Hi1UmUpMKB9qCjVdrprBdygUpN3cK4IzWuBTKPMAoxl8k1YP/AEAAHXtRcUu6pqlu3FsXPcVIvk3iz65hHvZjnQGeCvXUG8i7gNKn31PfE86JEXXDQctTtYDy5q++JZ8+TllHs6IO7U4m8tcN4Lip3svfm1JzzUhmhMhYNZpTf9HunO9RyfO4UVkmP8cjcfBRvhDyWd8upbB/5rCDzCNM3YisYF0xHKIsAY/IjYUqCLEVY1XOKcggoZoQt4eaLjBllO1r0/nlCv89uy/Py5/RYTN/xlgvleAOuSWXrPfwv9W0tG2lr2QjYjr6ERyZNxjr/a/wLheTsVoCMSQ0AAAAASUVORK5CYII="
18KB
예제_widgetSendmessage.zip
archive
Event Listeners
위젯에서 사용가능한 문법

Javascript 개발 가이드

zep-script-SDK 라이브러리를 사용해 폴더 구조를 깔끔하게 유지하고, 프로젝트 압축 및 배포를 명령어만으로 간단하게 진행 할 수 있습니다. zep-script-SDK 사용 방법을 알아보겠습니다.

1. node.js 설치

https://nodejs.org/ko/ 링크로 이동해 node.js를 설치합니다.

안정적인 버전인 LTS 버전으로 설치하는 것을 추천 드립니다.

2. 프로젝트 폴더 구성하기

라이브러리 사용을 위해 프로젝트 폴더는 다음과 같이 구성합니다.

res 폴더는 앱에서 쓰일 이미지, 사운드, html 파일 등을 넣는 폴더입니다.

→ 폴더 이름을 res로 지정해야합니다.

3. CLI를 사용해 프로젝트를 .zip 파일로 만들기

CLI를 사용해 명령어로 프로젝트를 .zip 파일로 만들 수 있습니다.

CLI는 MacOS의 경우 터미널, Windows의 경우 Windows PowerShell 또는 Windows 11부터 제공되는 터미널 환경에서 실행하실 수 있습니다.

Windows에서 PowerShell 실행하기

main.js가 있는 폴더 빈 공간에 Shift + 오른쪽클릭을 해서

여기에 PowerShell 창 열기 또는 여기서 명령창 열기 메뉴를 누르면

다음과 같이 Windows PowerShell 또는 명령 프롬프트 창이 실행됩니다.

main.js 파일이 있는 폴더에서 명령 창을 열어서 다음과 같이 압축 파일을 만들기 위한 명령어를 입력합니다.

npx zep-script archive

압축 과정이 성공했다면 다음과 같이 폴더에 압축 파일이 생긴 것을 확인 할 수 있습니다.

압축 파일을 만드는 것을 마지막으로, 앱을 배포할 준비가 끝났습니다.

4. 프로젝트 배포하기

1.홈페이지에서 배포하기

위에서 만든 압축(ZIP) 파일을 업로드 하는 방식으로 앱을 배포할 수 있습니다.

ZEP Script 배포 가이드 페이지를 참고해서 앱을 배포해보세요!

2. 명령어(CLI)로 배포하기

CLI를 사용해 위에서 만든 압축(ZIP) 파일을 배포할 수 있습니다.

다음과 같이 zep-script.json 파일을 생성해 업로드 될 앱을 설정합니다. (이름이 동일해야 합니다.)

{
    "appId": "Zjkgoj",  // app ID
    "name": "Template", // app 이름
    "description": "Template application" , // app 설명
    "type": "normal" // app 타입 ( "normal" or "minigame" or "sidebar" )
}

⭐ appID: 업로드할 앱의 ID를 입력합니다

  • 기존 앱을 변경하시려면 https://zep.us/me/apps/에 접속해서 업로드할 앱을 선택 후 주소창에 apps/뒤에 붙는 값을 입력하시면 됩니다.(아래 예시 이미지에서 Zjkoj입니다)

main.js 파일이 있는 폴더에서 명령 창을 열어서 다음과 같이 압축 파일을 만들기 위한 명령어를 입력합니다.

npx zep-script publish

앱 업로드 과정에서 계정 인증이 필요합니다.

다음과 같이 email을 입력하는 부분이 나오면 배포 할 앱을 소유하고 있는 계정의 email을 입력합니다.

Sending login code to your email이라는 메시지가 나오면 위에서 입력한 이메일의 메일 함에서 인증 코드를 확인한 뒤 입력해주면 배포가 시작됩니다.

인증 메일 예시

다음 그림과 같이 Publishing… 텍스트 좌측에 초록색 체크 표시가 생기면 배포가 완료된 것 입니다.

위에서 zep-script.json 파일에 작성한 내용대로 나의 앱 페이지에 앱이 배포된 것을 확인 할 수 있습니다.

배포에 실패하는 경우 아래의 단계를 따라 재시도 해보세요.

npx 명령어를 사용하는 경우

  1. 터미널에서 "npm i zep-script -g" 명령 실행하여 SDK 패키지를 최신화

  2. root 폴더에서 ".zscsession" 파일 삭제

  3. "npx zep-script publish" 명령어로 배포 시도

npm 명령어를 사용하는 경우

  1. package.json 파일의 "zep-script" 버전이 최신이 아니라면 최신 버전으로 업데이트

  2. root 폴더에서 ".zscsession" 파일 삭제

  3. "npm run deploy" 명령어로 배포 시도

라이브러리 관련하여 보다 자세한 내용은 아래 github 레포지토리 내용을 참고해주세요.

👏zep-script-sdk/packages/zep-script-cli at main · zep-us/zep-script-sdk


초성 퀴즈

1) 파일

2) main.js

const STATE_INIT = 3000;
const STATE_READY = 3001;
const STATE_PLAYING = 3002;
const STATE_JUDGE = 3004;
const STATE_END = 3005;

const WORD_LINES = [
    '한글날 휴게소 현기증 형광펜 호날두 허니문 하노이 핫도그 홍두깨 헤드셋 해돋이 한라봉 한라산 호랑이 허리띠 현미경 흰머리 황무지 햄버거 학부모 휘발유 허벅지 하수구 호신술 홍수아 화승총 허스키 햄스터 해운대 활주로 휴지통 화장품 회초리 핫팬츠 하회탈',
    '피규어 팔꿈치 포도주 피라냐 피라미 피뢰침 프리킥 프라하 포미닛 팥빙수 표백제 판소리 피시방 팔씨름 피아노 편의점 파자마 포장지 표지판 파충류 파출부 파출소 포청천 팬클럽 포켓볼 피카소 피카츄 폭탄주 피터팬',
    '태극기 태권도 턱걸이 테니스 트렁크 터미널 퇴마사 토마토 탬버린 투석기 턱시도 탕수육 토스트 태양계 티아라 타이밍 탈의실 트위터 퇴직금 통조림 태진아 태평양 테헤란',
    '캥거루 콧구멍 코끼리 캐나다 콩나물 코너킥 컨디션 코러스 카메라 카메오 키보드 콤바인 코뿔소 케이크 코코넛 카타르 칵테일 커플링',
    '참기름 철가방 초능력 축농증 취두부 청계천 책갈피 책꽂이 초능력 축농증 창덕궁 차두리 최루탄 최면술 칠면조 청바지 청소년 출석부 찹쌀떡 청와대 첫인상 치와와 초인종 추어탕 초음파 침전물 청첩장 초콜릿 치트키 출판사 챔피언 침팬지 최홍만',
    '계기판 개나리 기내식 강낭콩 교도관 고드름 골동품 기러기 가로등 가래떡 글러브 그림자 기모노 금메달 거머리 교무실 공무원 건망증 구미호 김병만 국방부 거북선 광복절 곱빼기 가속도 각선미 기숙사 가오리 걸음마 강의실 거짓말 교차로 골키퍼 과태료 김태원 김태희 건포도 곰팡이 골판지 공포탄 김흥국 광화문 공휴일 고현정 꽹과리 까나리 깍두기 꽃다발 꽃등심 꼽등이 까마귀 까치발 깐풍기',
    '케첩 킹카 킹콩 컨닝 채찍 창문 참깨 천국 축구 출근 친구 치과 취권 채권 초과 족발 절벽 젖병 주부 전복 중복 정복 절반 족보 쟁반 주번 좀비 제비 사과 사기 시급 시계 손금 선거 수능 설날 스님',
    '모래 미래 미로 만루 떡밥 딸기 떡국 두유 득음 뉴욕 노을 낙엽 녹용 노인 눈물 냉면 나비 나방 가위 거울 근육 기타 깃털 구토 굴뚝 개념 그네 구름 기린 경마 경매 고막 가방 공부 고백 간병 갈비 김밥 거봉 군밤 기분 건빵 가시'
];

let WORDS = [];

let _state = STATE_INIT;
let _stateTimer = 0;
let _timer = 0;
let _choanswer = '';
let _answer = '';
let _start = false;
let _widget = null; // using for contents UI
let _players = App.player;
let _result = '';

for(let w in WORD_LINES)
    WORDS =  WORDS.concat(WORD_LINES[w].trim().split(' '));

function cho_hangul(str) {
    cho = ["ㄱ","ㄲ","ㄴ","ㄷ","ㄸ","ㄹ","ㅁ","ㅂ","ㅃ","ㅅ","ㅆ","ㅇ","ㅈ","ㅉ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"];
    result = "";
    for (let i = 0; i < str.length; ++i ) {
      code = str.charCodeAt(i)-44032;
      if(code>-1 && code<11172) result += cho[Math.floor(code/588)];
      else result += str.charAt(i);
    }
    return result;
}

App.onStart.Add(function(){
    startState(STATE_INIT);
});

// when chatting event
// 채팅을 치면 호출되는 이벤트
// player : person who chatted
// text : chat text
// return : enter chatting box
// return false or true : not appear in chatting box
App.onSay.add(function(player, text) {
    if(_state == STATE_PLAYING)
    {
        if(_answer == text)
        {
            _result = player.name + '님 정답!\n정답은 ' + _answer;

            startState(STATE_JUDGE);
        }
    }
});

function startState(state) {
    _state = state;
    _stateTimer = 0;

    switch(_state)
    {
        case STATE_INIT:
            if(_widget)
            {
                _widget.destroy();
                _widget = null;
            }
            _answer = WORDS[Math.floor(Math.random() * WORDS.length)];
            _timer = 60;
    
            _choanswer = cho_hangul(_answer);
    
            // called html UI
            // param1 : file name
            // param2 : position 
            // [ top, topleft, topright, middle, middleleft, middleright, bottom, bottomleft, bottomright, popup ]
            // param3 : width size
            // param4 : height size
            _widget = App.showWidget('widget.html', 'top', 200, 300);
            
            _widget.sendMessage({
                state: _state,
                timer: _timer,
                answer: _choanswer,
            });

            startState(STATE_READY);
            break;
        case STATE_READY:
            _start = true;
            startState(STATE_PLAYING);
            break;
        case STATE_PLAYING:
            App.showCenterLabel('목표: 초성힌트로 단어를 찾아내세요.',0xFFFFFF, 0x000000, 115);
            _widget.sendMessage({
                state: _state,
                timer: _timer,
                answer: _choanswer,
            });
            break;
        case STATE_JUDGE:
            break;
        case STATE_END:
            if(_widget)
            {
                _widget.destroy();
                _widget = null; // must to do for using again
            }

            _start = false;
            break;
    }
}

App.onLeavePlayer.Add(function(p) {
    p.title = null;
    p.sprite = null;
    p.moveSpeed = 80;
    p.sendUpdated();
});

App.onDestroy.Add(function() {
    _start = false;
    
    if(_widget)
    {
        _widget.destroy();
        _widget = null;
    }
});

App.onUpdate.Add(function(dt) {
    if(!_start)
        return;

    _stateTimer += dt;

    switch(_state)
    {
        case STATE_INIT:
            break;
        case STATE_READY:
            _start = true;
            break;
        case STATE_PLAYING:
            if(_stateTimer >= 1)
            {
                _stateTimer = 0;
                _timer -= 1;
            }

            if(_timer == 0)
            {
                _result = '정답은 ' + _answer + ' 입니다.';
                startState(STATE_JUDGE);
            }
            break;
        case STATE_JUDGE:
            App.showCenterLabel(_result, 0xFFFFFF, 0x000000, 115);

            if(_stateTimer >= 3)
                startState(STATE_END);
            break;
        case STATE_END:
            break;
    }
});
4KB
WordGame_Sample.zip
archive

Typescript 개발 가이드

⭐ 타입스크립트 개발 시 주의할 점

타입스크립트로 개발 시 App, Map, Player, Widget 키워드 앞에 Script 키워드를 붙여야 합니다.

// 자바스크립트 개발 시
App.sayToAll("Hello World!");

// 타입스크립트 개발 시
ScriptApp.sayToAll("Hello World!");

1. node.js 설치

https://nodejs.org/ko/ 링크로 이동해 node.js를 설치합니다.

안정적인 버전인 LTS 버전으로 설치하는 것을 추천드립니다.

2. 프로젝트 폴더 만들기

CLI를 통해 ZEP Script 프로젝트를 개발하기 위한 샘플 프로젝트를 다운로드 받을 수 있습니다.

CLI는 MacOS의 경우 터미널, Windows의 경우 Windows PowerShell 또는 Windows 11부터 제공되는 터미널 환경에서 실행하실 수 있습니다.

Windows에서 PowerShell 실행하기

폴더를 생성하고 싶은 곳 빈 공간에 Shift + 오른쪽클릭을 해서

여기에 PowerShell 창 열기 또는 여기서 명령창 열기 메뉴를 누르면

다음과 같이 Windows PowerShell 또는 명령 프롬프트 창이 실행됩니다.

다음과 같이 프로젝트 폴더를 생성하기 위한 명령어를 입력합니다.

npx zep-script init {폴더 이름} --npm

npx zep-script@latest init MyZepApp --npm

위와 같이 Project {폴더 이름} initialized successfully 메시지가 출력 되었다면,

이제 타입스크립트로 ZEP Script를 개발할 수 있는 환경이 준비 된 것 입니다.

3. 폴더 구조 살펴보기

생성된 프로젝트 폴더로 이동하면 다음과 같이 파일들이 있는 것을 볼 수 있습니다.

여러 기능을 모듈로 나누고 싶다면, src 폴더내부에 파일을 추가해 확장할 수 있습니다. 예: playerUtils.ts, eventHandlers.ts 등

res 폴더는 ‘resources’의 약자로, 앱에 필요한 이미지나 html 파일 등을 넣는 폴더입니다.

여기서 생성된 main.ts 파일은 앱 로직을 작성하는 메인 파일입니다.

main.ts 에는 다음과 같이 샘플 코드가 적혀있습니다. 해당 코드를 참고하여 개발을 하시면 됩니다.

4. 빌드하기

ZEP Script는 자바스크립트가 아닌 언어로 실행 할 수 없기 때문에 타입스크립트를 자바스크립트로 변환하는 빌드 과정을 거쳐야 합니다.

main.ts 파일이 있는 폴더에서 명령 창을 열어 다음과 같이 빌드를 위한 명령어를 입력합니다.

npm run build 또는 npx zep-script build

빌드 완료 시 res 폴더에 main.js 파일이 생성됩니다.

5. 압축 파일 만들기

ZEP Script로 만든 앱을 배포하려면 main.js 파일과 앱에서 사용 할 이미지, html 파일 등을 함께 압축한 .zip 파일을 준비해야 합니다.

main.ts 파일이 있는 폴더에서 명령 창을 열어서 다음과 같이 압축 파일을 만들기 위한 명령어를 입력합니다.

npm run archive 또는 npx zep-script archive

압축 과정이 성공했다면 다음과 같이 폴더에 압축 파일이 생긴 것을 확인 할 수 있습니다.

압축 파일을 만드는 것을 마지막으로, 앱을 배포할 준비가 끝났습니다.

6. 프로젝트 배포하기

  1. 홈페이지에서 배포하기

위에서 만든 압축(ZIP) 파일을 홈페이지에서 업로드 하는 방식으로 앱을 배포할 수 있습니다.

ZEP Script 배포 가이드 페이지를 참고해서 앱을 배포해보세요!

2. 명령어(CLI)로 배포하기

CLI를 사용해 위에서 만든 압축(ZIP) 파일을 배포할 수 있습니다.

프로젝트 폴더에서 zep-script.json 파일을 열어 업로드 될 앱을 설정합니다.

( zep-script.json 파일이 없다면 생성해주세요 )

{
    "appId": "Zjkgoj",  // app ID
    "name": "Template", // app 이름
    "description": "Template application" , // app 설명
    "type": "normal" // app 타입 ( "normal" or "minigame" or "sidebar" )
}

⭐ appID: 업로드할 앱의 ID를 입력합니다

  • 기존 앱을 변경하시려면 https://zep.us/me/apps/에 접속해서 업로드할 앱을 선택 후 주소창에 apps/뒤에 붙는 값을 입력하시면 됩니다.(아래 예시 이미지에서 Zjkoj입니다)

설정이 끝났으면 프로젝트 폴더 경로에서 PowerShell 을 실행 한 뒤, 다음과 같이 배포를 하기 위한 명령어를 입력합니다.

npm run deploy 또는 npx zep-script publish

다음과 같이 email을 입력하는 부분이 나오면 배포 할 앱을 소유하고 있는 계정의 email을 입력합니다.

Sending login code to your email..이라는 메시지가 나오면 위에서 입력한 이메일의 메일 함에서 인증 코드를 확인한 뒤 입력해주면 배포가 시작됩니다.

메일 인증 예시

다음 그림과 같이 Publishing… 텍스트 좌측에 초록색 체크 표시가 생기면 배포가 완료된 것 입니다.

나의 앱 페이지에서 zep-script.json 에 작성한 내용대로 앱이 배포된 것을 확인 할 수 있습니다.

배포에 실패하는 경우 아래의 단계를 따라 재시도 해보세요.

npx 명령어를 사용하는 경우

  1. 터미널에서 "npm i zep-script@latest -g "명령을 실행하여 SDK 패키지를 최신화

  2. root 폴더에서 ".zscsession" 파일 삭제

  3. "npx zep-script publish" 명령어로 배포 시도

npm 명령어를 사용하는 경우

  1. package.json 파일의 "zep-script" 버전이 최신이 아니라면 최신 버전으로 업데이트

    1. npm i zep-script@latest 명령어 실행

  2. root 폴더에서 ".zscsession" 파일 삭제

  3. "npm run deploy" 명령어로 배포 시도

라이브러리 관련하여 보다 자세한 내용은 아래 github 레포지토리 내용을 참고해주세요.

zep-script-sdk/packages/zep-script-cli at main · zep-us/zep-script-sdk


Select List

Select List 형 위젯은 텍스트와 텍스트 입력 창, 혹은 선택 버튼을 함께 사용할 수 있는 UI입니다.

활용 예 ) 탑승 앱, 찌르기 앱, 아바타 특수 효과 앱

Image

Example

9KB
SelectList.zip
archive
// Editable fields. Used for validation.
const RequiredFields = {
    password: "string",
    lock: "boolean"
};

const SELECT_ITEM_DATA = {
    Select_1: {
        id: 1,
        order: 1, // Item sorting order
        displayName: "Item 1", // Item display name
        password: "password", // password
        lock: false, // lock(boolean)
    },
    Select_2: {
        id: 2,
        order: 2,
        displayName: "Item 2",
        password: "password",
        lock: false,
    },
    Select_3: {
        id: 3,
        order: 3,
        displayName: "Item 3",
        password: "password",
        lock: false,
    },
    Select_4: {
        id: 4,
        order: 4,
        displayName: "Item 4",
        password: "password",
        lock: false,
    },
    Select_5: {
        id: 5,
        order: 5,
        displayName: "Item 5",
        password: "password",
        lock: false,
    },
    Select_6: {
        id: 6,
        order: 6,
        displayName: "Item 6",
        password: "password",
        lock: false,
    },
    Select_7: {
        id: 7,
        order: 7,
        displayName: "Item 7",
        password: "password",
        lock: false,
    },
    Select_8: {
        id: 8,
        order: 8,
        displayName: "Item 8",
        password: "password",
        lock: false,
    },
};


App.onStart.Add(() => {
    init();
});

App.onJoinPlayer.Add(function (player) {
    player.tag = {};
});

App.onSidebarTouched.Add(function (player) {
    showMainWidget(player);
})

function init() {
    // ensureStorageInitialized
    if (!App.storage) {
        App.storage = "{}";
        App.save();
    }
    const appStorage = JSON.parse(App.storage);

    // updateItemDataFromStorage
    if (appStorage.setting) {
        for (let key in SELECT_ITEM_DATA) {
            if (appStorage.setting.hasOwnProperty(key)) {
                SELECT_ITEM_DATA[key] = Object.assign({}, SELECT_ITEM_DATA[key], appStorage.setting[key]);
            } else {
                SELECT_ITEM_DATA[key] = Object.assign({}, SELECT_ITEM_DATA[key]);
            }
        }
    }
}

function showMainWidget(player) {
    App.getStorage(() => {
        const appStorage = JSON.parse(App.storage);
        if (player.tag.widget) {
            player.tag.widget.destroy();
            player.tag.widget = null;
        }
        if (player.isMobile) {
            player.tag.widget = player.showWidget("SelectList.html", "sidebar", 0, 0);
        } else {
            player.tag.widget = player.showWidget("SelectList.html", "bottomleft", 0, 0);
        }

        let selectItemData = SELECT_ITEM_DATA;
        if (appStorage.setting) {
            let newList = {};
            for (let key in selectItemData) {
                if (appStorage.setting.hasOwnProperty(key)) {
                    newList[key] = Object.assign({}, selectItemData[key], appStorage.setting[key]);
                } else {
                    newList[key] = Object.assign({}, selectItemData[key]);
                }
            }
            selectItemData = newList;
        }

        player.tag.widget.sendMessage({
            type: "init",
            isMobile: player.isMobile,
            isAdmin: isAdmin(player),
            selectItemData: selectItemData,
        });

        player.tag.widget.onMessage.Add(function (sender, msg) {
            switch (msg.type) {
                case "close":
                    if (sender.tag.widget) {
                        sender.tag.widget.destroy();
                        sender.tag.widget = null;
                    }
                    break;
                case "requestSaveSetting":
                    const result = validateDatabase(msg.editData);
                    if (!result) {
                        sender.showAlert("Invalid Input");
                        return;
                    }
                    App.getStorage(() => {
                        const appStorage = JSON.parse(App.storage);
                        appStorage.setting = result;
                        App.setStorage(JSON.stringify(appStorage));

                        if (sender.tag.widget) {
                            sender.tag.widget.sendMessage({
                                type: "responseSaveSetting",
                            });
                        }

                        let selectItemData = {};
                        for (let key in SELECT_ITEM_DATA) {
                            if (appStorage.setting.hasOwnProperty(key)) {
                                selectItemData[key] = Object.assign({}, SELECT_ITEM_DATA[key], appStorage.setting[key]);
                                SELECT_ITEM_DATA[key] = selectItemData[key];
                            } else {
                                SELECT_ITEM_DATA[key] = Object.assign({}, SELECT_ITEM_DATA[key]);
                            }
                        }
                        if (selectItemData == {}) {
                            selectItemData = SELECT_ITEM_DATA;
                        }
                        handleUpdateSetting(selectItemData);
                    });

                    break;
            }
        });
    });

}

function handleUpdateSetting(selectItemData) {
    for (const player of App.players) {
        if (!player) continue;
        if (player.tag.widget) {
            player.tag.widget.sendMessage({
                type: "update",
                selectItemData: selectItemData,
            });
        }
    }
}

function validateDatabase(database) {
    let newDB = {};
    for (let key in database) {
        let item = database[key];
        newDB[key] = {};
        for (let field in RequiredFields) {
            // validateFields
            if (!item.hasOwnProperty(field) || typeof item[field] !== RequiredFields[field]) {
                return false;
            }
            if (field === "password" && (!item[field] && item["lock"])) {
                return false;
            }

            newDB[key][field] = item[field];
        }
    }
    return newDB;
}

function isAdmin(player) {
    return player.role >= 3000;
}

Lifecycle

소개

앱이 시작되어, 실행되고 종료될 때까지를 하나의 생애 주기(Lifecycle)라고 합니다. 관련 함수들을 이용하여 앱이 시작될 때, 실행 중일 때 그리고 종료될 때 등의 상황에서 필요한 동작들을 실행해 전체적인 앱의 생애주기를 만들 수 있습니다.

이름
설명

App 생애 주기(Lifecycle) 이해하기

Lifecycle 함수들은 생애 주기에 맞게 기능을 만들 수 있는 필수적인 함수입니다. 아래 그림과 같이 앱이 실행될 때는 Enter 단계의 함수들이 동작하고, 앱이 실행 중 일때는 Update 단계의 함수들이 주기적으로 동작하며, 앱이 종료될 때는 Exit 단계의 함수들이 동작하게됩니다.

앱이 실행되어 종료될 때 까지 각 단계의 함수들을 잘 활용해 생애 주기에 맞게 App을 만들어보세요.

Lifecycle 함수 한 눈에 보기

1️⃣ Enter 단계 함수

App의 실행과 함께 호출되는 라이프사이클 Enter 단계에서 호출되는 함수를 안내합니다.

onInit

App.onInit.Add(function(){})

App이 최초로 시작될 때 한 번 호출됩니다.

파라미터

  • 없음

예제

onInit 에서 채팅 출력해보기. ( 미니게임으로 만들어 확인해보세요. )

onJoinPlayer

App.onJoinPlayer.Add(function(player){})

onInit이 호출된 후, 접속해 있는 모든 플레이어를 해당 이벤트를 통해 입장시키고, 이후 입장하는 플레이어가 있을 때 마다 동작합니다.

파라미터

이름
타입
설명

예시

플레이어 입장시 메시지 출력해보기

onStart

App.onStart.Add(function(){})

모든 플레이어가 onJoinPlayer를 통해 입장한 후 한 번 호출 됩니다.

파라미터

  • 없음

예제

onStart에서 채팅 출력해보기 ( 미니게임으로 만들어 확인해보세요. )

Enter 단계 함수의 흐름 이해하기

Lifecycle Enter 단계의 흐름을 코드로 확인해보세요. 아래 코드를 미니게임으로 만들어 실행해보세요!

2️⃣ Update

Update에는 약 20ms 마다 주기적으로 실행되는 함수 onUpdate가 있습니다.

onJoinPlayer, onLeavePlayer 등의 이벤트가 발생하면 해당 이벤트 처리 후 다시 onUpdate가 주기적으로 실행됩니다.

onUpdate

App.onUpdate.Add(function(dt){})

약 20ms 마다 주기적으로 실행되는 함수입니다.

파라미터

이름
타입
설명

예시

onUpdate 함수를 이용해 10초 카운트다운 만들어보기

3️⃣ Exit 단계 함수

앱이 종료될 때 실행되는 함수 입니다.

onLeavePlayer

App.onLeavePlayer.Add(function(player){})

퇴장하는 플레이어가 있을 때 마다 동작합니다. 이후, 다른 App이 실행되거나 설치한 Game Block이 파괴될 때 모든 플레이어를 이 함수를 통해 퇴장시킵니다.

파라미터

이름
타입
설명

예시

플레이어 퇴장 시 메시지 출력해보기

onDestroy

App.onDestroy.Add(function(){})

다른 App이 실행되거나, 설치한 Game Block이 파괴될 때 동작합니다.

파라미터

  • 없음

예제

Game Block 파괴 시 메시지 출력해보기 ( 미니게임 )

onInit

App이 최초로 시작될 때 한 번 호출되는 함수입니다.

onJoinPlayer

onInit이 호출된 후, 접속해 있는 모든 플레이어를 해당 이벤트를 통해 입장 시키고, 이후 입장하는 플레이어가 있을 때 마다 동작합니다.

onStart

모든 플레이어가 onJoinPlayer를 통해 입장한 후 한 번 호출 됩니다.

onUpdate

약 20ms 마다 주기적으로 실행되는 함수입니다.

onLeavePlayer

퇴장하는 플레이어가 있을 때 마다 동작합니다. 이후, 다른 App이 실행되거나 설치한 Game Block이 파괴될 때 모든 플레이어를 이 함수를 통해 퇴장시킵니다.

onDestroy

다른 App이 실행되거나 설치한 Game Block이 파괴될 때 동작합니다.

// App 실행 시에 최초로 호출되는 이벤트 (유저 진입 전)
// Normal App과 Sidebar App은 Script 적용 후 맵이 실행될 때 호출 [ Enter ]
App.onInit.Add(function(){
});

// 모든 플레이어를 이 이벤트를 통해 App에 진입시킴 [ Enter ]
// 이후 플레이어가 입장 할 때마다 호출 [ Events ]
App.onJoinPlayer.Add(function(player) {
});

// 플레이어 모두 진입 시 최초로 시작되는 이벤트 [ Enter ]
App.onStart.Add(function(){
});

// 20ms 마다 호출되는 이벤트
// dt: deltatime(전 프레임이 완료되기까지 걸린 시간) [ Update ]
App.onUpdate.Add(function(dt){
});

// 이벤트 콜백 처리 후 다시 onUpdate

// App 종료 시 모든 플레이어를 App에서 나가게 함 [ Exit ]
App.onLeavePlayer.Add(function(player){
});

// App 종료 시 마지막으로 호출 [ Exit ]
// Normal App과 Sidebar App은 별도의 종료
App.onDestroy.Add(function(){
});
App.onInit.Add(function(){
	App.sayToAll("-- onInit --")
	App.sayToAll("   ready..  ")
	App.sayToAll("------------")
});

player

Player

player는 입장하는 플레이어를 가르킴 파라미터의 이름은 임의로 변경가능

App.onJoinPlayer.Add(function(player){
	App.showCenterLabel(`${player.name}님이 입장하셨습니다.`)
});
App.onStart.Add(function(){
	App.sayToAll("-- App Start --")
});
// main.js
App.onInit.Add(function(){
	App.sayToAll("-- onInit --")
	App.sayToAll("   ready..  ")
	App.sayToAll("------------")
});

App.onJoinPlayer.Add(function(player){
	App.sayToAll(`${player.name}님이 입장하셨습니다.`)
});

App.onStart.Add(function(){
	App.sayToAll("-- App Start --")
});

dt

Number

deltatime (전 프레임이 완료되기까지 걸린 시간, 약 20ms) dt 파라미터의 이름은 임의로 변경 가능

let countdown = 10;
let timer = 0;
App.onUpdate.Add(function(dt){
	timer += dt;
	if(timer >=1){
		if(countdown >= 0)
		{
			App.showCenterLabel(countdown--);
		}
		else{
			App.showCenterLabel("Time Over");
		}
		timer = 0;
	}
})

player

Player

player 파라미터는 퇴장하는 플레이어를 가르킴 player 파라미터의 이름은 임의로 변경 가능

// 플레이어가 퇴장할 때 실행
App.onLeavePlayer.Add(function(player){
	App.showCenterLabel(`${player.name}님이 퇴장하셨습니다.`)
});
// Game Block 파괴 시 실행
App.onDestroy.Add(function(){
	App.showCenterLabel("게임 블록이 파괴되었습니다.")
});
Enter 단계 함수들이 순서대로 동작하는 모습.

좀비 게임

1) 파일

14KB
Zombie_Sample.zip
archive

2) main.js

// load sprite
let monster = App.loadSpritesheet('monster.png', 96, 96, {
    // defined base anim
    left: [8, 9, 10, 11],
    up: [12, 13, 14, 15],
    down: [4, 5, 6, 7],
    right: [16, 17, 18, 19],
}, 8);

const STATE_INIT = 3000;
const STATE_READY = 3001;
const STATE_PLAYING = 3002;
const STATE_JUDGE = 3004;
const STATE_END = 3005;

let _start = false; // Whether the game starts
let _players = App.players; // App.players : get total players
let _lastSurvivor = null;
let _zombieKing = [];
let _state = STATE_INIT;
let _stateTimer = 0; // Timer to check status value
let _live = 0; // number of survivors
let _resultstr;

function startApp()
{
    if(_players.length > 2)
    {
        // Set the number of zombies
        let zombiCnt = Math.floor(_players.length * 0.1);
        if(zombiCnt < 1)
            zombiCnt = 1;

        let allPlayer = [];
        let zombieIdx = [];

        for(let i = 0; i < _players.length; ++i)
        {
            allPlayer.push(_players[i]);
        }

        for(let i = 0; i < zombiCnt; ++i)
        {
            let index = Math.floor(allPlayer.length * Math.random());
            if(!zombieIdx.includes(allPlayer[index].id))
            {
                zombieIdx.push(allPlayer[index].id);
                allPlayer.splice(index, 1);
            }
        }

        // give players zombie attribute
        for(let i in _players)
        {
            let p = _players[i];
            // create and utilize option data using tags.
            p.tag = {};
            if(zombieIdx.includes(p.id))
            {
                p.tag.zombie = true;
                p.tag.attack = 0;
            }
            else
            {
                p.tag.zombie = false;
                p.tag.attack = 0;
                _live++;
            }
            
            // when player property changed have to call this method
            // 플레이어 속성 변경 시 반드시 호출하여 업데이트 한다.
            p.sendUpdated();
        }                                                                                               
        
        _start = true;
    }
    else
    {
        App.showCenterLabel(`Playable number of people: 3 or more`);
        startState(STATE_END);
    }
}

function startState(state)
{
    _state = state;
    _stateTimer = 0;

    switch(_state)
    {
        case STATE_INIT:
            startApp();
            break;
        case STATE_READY:
            App.showCenterLabel("The game will start soon.");
            for(let i in _players)
            {
                let p = _players[i];
                p.moveSpeed = 0;
                // Change the attribute of zombies
                if(p.tag.zombie)
                {
                    p.title = '<P:ZERO>';
                    p.sprite = monster;
                }
                p.sendUpdated();
            }
            break;
        case STATE_PLAYING:
            for(let i in _players)
            {
                let p = _players[i];
                // Change speed and label text according to player status
                if(p.tag.zombie)
                {
                    p.moveSpeed = 85;
                    p.showCenterLabel('Infect people!');
                }
                else
                {
                    p.moveSpeed = 80;
                    p.showCenterLabel('Survive from zombies!');
                }
                p.sendUpdated();
            }
            break;
        case STATE_JUDGE:
            for(let i in _players) {
                let p = _players[i];
                p.moveSpeed = 0;
                p.sendUpdated();
            }

            judgement(_live);
            break;
        case STATE_END:
            _start = false;

            for(let i in _players)
            {
                let p = _players[i];
                p.moveSpeed = 80;
                p.title = null;
                p.sprite = null;
                p.sendUpdated();
            }

            // Clear all objects in the map
            Map.clearAllObjects();
            break;
    }
   
}

function checkSuvivors()
{
    let resultlive = 0;
    for(let i in _players)
    {
        let p = _players[i];
        if(!p.tag.zombie)
        {
            _lastSurvivor = p;
            ++resultlive;
        }
    }

    return resultlive;
}

function judgement(number)
{   
    // The highest number of attacks among all players
    let attack = 0;

    for(let i in _players)
    {
        let p = _players[i];

        if(p.tag.attack > attack)
            attack = p.tag.attack;            
    }
    
    zombieKing = [];
    for(let i in _players)
    {   
        let p = _players[i];
        
        if(p.tag.attack == attack)
            zombieKing.push(p);
    }

    let index = Math.floor(Math.random() * zombieKing.length);

    if(number == 1) // when there are survivors
        resultstr = `${_lastSurvivor.name} is the last survivor!\nThe Strongest Zombie
 [` + zombieKing[index].name + '] Number of infections : ' + attack;
    else if(number == 0) // when there are no survivors
        resultstr = `No survivors.\nThe Strongest Zombie [` + zombieKing[index].name +
 '] Number of infections : ' + attack;
}

App.onStart.Add(function(){
    startState(STATE_INIT);
});

// when player join the space event
// 플레이어가 스페이스에 입장 했을 때 이벤트
App.onJoinPlayer.Add(function(p) {
    p.tag = {
        zombie : false,
        attack : 0,
    };

    if(_start) {
        p.tag.zombie = true;
        p.sprite = monster;
        p.sendUpdated();
        
        judgement(checkSuvivors());
    }   
    _players = App.players;
});

// when player leave the space event
// 플레이어가 스페이스를 나갔을 때 이벤트
App.onLeavePlayer.Add(function(p) {
    if(_start) {
        judgement(checkSuvivors());
    }

    p.title = null;
    p.sprite = null;
    p.moveSpeed = 80;
    p.sendUpdated();

    _players = App.players;
});

// when the game block is pressed event
// 게임 블록을 밟았을 때 호출되는 이벤트
App.onDestroy.Add(function() {
    App.stopSound();
});

// when player touched other player event
// 플레이어가 다른 플레이어와 부딪혔을 때 
App.onPlayerTouched.Add(function(sender, target, x, y) {
    if(_state != STATE_PLAYING)
        return;

    if(!sender.tag.zombie)
        return;

    if(target.tag.zombie)
        return;

    target.tag.zombie = true;
    target.sprite = monster;
    sender.tag.attack += 1;
    target.sendUpdated();
    
    _live = checkSuvivors();
    if(_live >= 2)
    {
        App.showCenterLabel(`${target.name} is infected!\n(${_live} survivors!)`);
        return;
    }
    else
        startState(STATE_JUDGE);
});

// called every 20ms
// 20ms 마다 호출되는 업데이트
// param1 : deltatime ( elapsedTime )
App.onUpdate.Add(function(dt) {
    if(!_start)
        return;
    
    _stateTimer += dt;

    switch(_state)
    {
        case STATE_INIT:
            App.showCenterLabel("Become a zombie and infect people. The host is faster
    .\nPeople do their best to run away and be the last survivor.");
            
            if(_stateTimer >= 5)
                startState(STATE_READY);
            break;
        case STATE_READY:
            if(_stateTimer >= 3)
                startState(STATE_PLAYING);
            break;
        case STATE_PLAYING:
            break;
        case STATE_JUDGE:
            App.showCenterLabel(resultstr);

            if(_stateTimer >= 5)
                startState(STATE_END);
            break;
        case STATE_END:
            break;    
    }
});

Field

소개

Field는 App과 관련된 속성 값들 입니다. 이 필드를 활용해 참가 중인 스페이스나 맵, 플레이어 정보 등을 조회하거나, 저장공간을 활용할 수 있습니다.

🔒 아이콘이 있는 필드는 수정이 불가능한 읽기 전용 필드입니다.

이름
설명

🔒 spaceHashID

App이 설치된 스페이스의 해쉬값을 보여줍니다.

🔒 mapHashID

App이 설치된 맵의 해쉬값을 가져옵니다.

🔒 creatorID

App을 실행한 플레이어의 ID 값을 가져옵니다.

🔒 players

맵에 있는 모든 플레이어 리스트를 배열로 가져옵니다.

🔒 playerCount

앱이 설치된 맵에 있는 플레이어의 수를 가져옵니다.

cameraEffect

카메라 이펙트의 종류를 셋팅할 변수 값

cameraEffectParam

카메라 이펙트 효과의 범위 값

displayRatio

화면의 줌을 컨트롤 하는 값

storage

스페이스 내의 App 데이터의 저장공간(스페이스 한정)

followPlayer

App의 따라가기 기능 활성화 여부 값

showName

플레이어 닉네임 숨김 여부

🔒 appHashID

앱의 HashID 값을 가져옵니다.

enableFreeView

앱이 설치된 맵의 둘러보기 허용 여부를 설정할 수 있습니다.

📚 API 설명 및 예제

spaceHashID & mapHashID

App.spaceHashID: String App.mapHashID: String

앱이 설치된 스페이스의 spaceHashID와 mapHashID를 가져옵니다. (스페이스와 맵 이해하기)

예제

앱이 설치된 맵의 spaceHashID와 mapHashID 출력해보기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
  // 채팅창에 spaceHashID와 mapHashID 출력해보기
	App.sayToAll(`spaceHashID: ${App.spaceHashID}`); // spaceHashID: Ak42Xz
	App.sayToAll(`mapHashID: ${App.mapHashID}`) // mapHashId: 25g3RQ
})

creatorID

App.creatorID

미니게임을 실행한 플레이어의 ID 값을 가져옵니다. ⛔맵 내장형 앱인 노멀 앱과 사이드바 앱에서는 동작하지 않습니다.

예제

미니게임을 실행한 플레이어의 닉네임을 채팅창에 출력해보기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
	if(player.id == App.creatorID){
		App.sayToAll(`${player.name}님이 앱을 실행했습니다.`)
	}
})

players

App.players: ScriptPlayer[]

맵에 있는 모든 플레이어 리스트를 배열로 가져옵니다.

예제

맵에 있는 모든 플레이어 닉네임을 출력해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown 설명
App.addOnKeyDown(81,function(p){
	//App.players를 이용해 채팅창에 접속해있는 모든 플레이어의 닉네임을 출력하기
	let players = App.players;
	for(let i in players){
		let player = players[i]
		App.sayToAll(player.name)
	}
})

playerCount

App.playerCount: Number

앱이 설치된 맵에 있는 플레이어의 수를 가져옵니다.

예시

맵에 있는 플레이어 수 출력해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown 설명
App.addOnKeyDown(81,function(p){
	// 현재 접속자 수를 채팅창에 출력
	App.sayToAll(`접속자 수: ${App.playerCount}`)
})

cameraEffect & cameraEffectParam1

App.cameraEffect: NONE = 0, SPOTLIGHT = 1 App.cameraEffectParam1: Number

App.cameraEffect: 카메라 이펙트의 종류를 셋팅할 변수 값

App.cameraEffectParam1: 카메라 이펙트 효과의 범위 값

예제

비네팅 효과를 On/Off 하는 키 함수 만들어보기

// q 키를 누르면 동작하는 함수
// 한 번 누르면 비네팅 효과가 켜지고, 두 번 누르면 비네팅 효과가 꺼짐
// App.addOnKeyDown 설명
App.addOnKeyDown(81,function(p){
	if(App.cameraEffect == 0){
		App.cameraEffect = 1; // 1 = 비네팅 효과
		App.cameraEffectParam1 = 500; // 비네팅 효과의 범위를 500으로 지정
	}
	else if(App.cameraEffect == 1){
		App.cameraEffect = 0; // 비네팅 효과 끄기
	}
	App.sendUpdated(); // 앱의 Field값이 변경되면 App.sendUpdated()로 변경값을 적용
})
비네팅 효과 범위 500이 적용된 모습

displayRatio

App.displayRatio

화면의 줌을 컨트롤 하는 값 ( 기본 값: 1 )

예제

화면의 줌을 컨트롤 하는 키 만들어보기

// q 키를 누르면 동작하는 함수
// 한 번 누르면 화면의 줌 값이 커지고, 한 번 더 누르면 원래대로 돌아오는 키 함수 
// App.addOnKeyDown 설명
App.addOnKeyDown(81,function(p){
	if(App.displayRatio == 1){
		App.displayRatio = 5;
	}else{
		App.displayRatio = 1;
	}
	App.sendUpdated(); //* 앱의 Field값이 변경되면 App.sendUpdated()로 변경값을 적용
})
displayRatio 값이 5일 때
displayRatio 값이 1일 때

storage

App.storage: String

스페이스 내의 App 데이터의 저장공간 입니다 (스페이스 한정)

예제

Storage 페이지를 참고해주세요

followPlayer

App.followPlayer: Boolean

App의 따라가기 기능 활성화 여부 값 입니다. ( 기본 값 : false )

노멀 앱, 미니게임 앱이 실행 중인 경우 followPlayer 값이 false로 설정되어 ‘따라가기’ 기능이 비활성화됩니다.

예제

따라가기 기능을 끄고 키는 함수 만들어보기

// q 키를 누르면 동작하는 함수
// followPlayer 값을 바꾸는 키 함수
App.addOnKeyDown(81,function(p){
	if(App.followPlayer){
		App.followPlayer = false;
	}else{
		App.followPlayer = true;
	}
	App.sayToAll(`App.followPlayer: ${App.followPlayer}`)
	App.sendUpdated(); //* 앱의 Field값이 변경되면 App.sendUpdated()로 변경값을 적용
})

showName

App.showName: Boolean

플레이어 닉네임 숨김 여부

App.showName 을 false로 설정하면 모든 플레이어의 닉네임이 숨김 처리됩니다.

예제

// q 키를 누르면 동작하는 함수
// showName 값을 바꾸는 키 함수
App.addOnKeyDown(81,function(p){
	if(App.showName){
		App.showName = false;
	}else{
		App.showName = true;
	}
	App.sayToAll(`App.showName: ${App.showName}`)
	App.sendUpdated(); //* 앱의 Field값이 변경되면 App.sendUpdated()로 변경값을 적용
})

appHashID

App.appHashID: String

앱의 HashID를 가져옵니다.

예제

채팅창에앱의 HashID 출력하기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
	App.sayToAll(`appHashID: ${App.appHashID}`); 
})

enableFreeView

App.enableFreeView

앱이 설치된 맵의 둘러보기 허용 여부를 설정할 수 있습니다.

예제

단축키로 맵 둘러보기 허용 여부 설정하기

// Q를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	if (App.enableFreeView) {
		App.enableFreeView = false;
	} else {
		App.enableFreeView = true;
	}
	App.sendUpdated();
	player.sendMessage(`App.enableFreeView = ${App.enableFreeView}`, 0x00ffff);
});


부록

스페이스와 맵 이해하기

결투 게임

1) 파일

4MB
결투.zip
archive

2) main.js

let ghost = App.loadSpritesheet("ghost.png", 32, 48, {
	left: [2],
	right: [1],
	up: [3],
	down: [0],
});

let redBoxing = App.loadSpritesheet("redBoxing.png");

const STATE_INTRO = 3001;
const STATE_INIT = 3002;
const STATE_RULE = 3003;
const STATE_PLAYING = 3004;
const STATE_JUDGE = 3005;
const STATE_END = 3006;

let lastSurvivor = null;
let _start = false;
let _players = App.players;

let _state = STATE_INIT;
let _stateTimer = 0;

let _alive = 0;

let _widget = null;

function init() {
	for (let i in _players) {
		let p = _players[i];
		setHPgage(p, p.tag.hp);
		p.sendUpdated();
	}
	_alive = checkSuvivors();
}

function startState(state) {
	_state = state;
	_stateTimer = 0;
	switch (_state) {
		case STATE_INTRO:
			for (let i in _players) {
				let p = _players[i];
				if (p.tag.widget) {
					p.tag.widget.destroy();
					p.tag.widget = null;
				}
			}
			// 게임시작
			_start = true;
			_widget = App.showWidget("intro.html", "middle", 350, 340);
			App.playSound("intro.wav");
			break;
		case STATE_INIT:
			init();
			break;
		case STATE_RULE:
			_widget = App.showWidget("rule.html", "middle", 400, 200);

			for (let i in _players) {
				let p = _players[i];
				p.moveSpeed = 0;
				p.sendUpdated();
			}
			break;
		case STATE_PLAYING:
			if (_widget) {
				_widget.destroy();
				_widget = null;
			}

			_widget = App.showWidget("status.html", "top", 700, 300);

			for (let i in _players) {
				let p = _players[i];
				p.moveSpeed = 80;
				p.sendUpdated();
			}

			break;
		case STATE_JUDGE:
			break;
		case STATE_END:
			if (_widget) {
				_widget.destroy();
				_widget = null;
			}

			_start = false;

			for (let i in _players) {
				let p = _players[i];
				p.sprite = null;
				p.attackSprite = null;
				p.title = null;
				p.sprite = null;
				p.moveSpeed = 80;
				p.sendUpdated();
			}

			Map.clearAllObjects();
			break;
	}
}

// 체력바 표시 함수
function setHPgage(p, hp) {
	switch (hp) {
		case 5:
			p.title = "▮▮▮▮▮";
			break;
		case 4:
			p.title = "▮▮▮▮";
			break;
		case 3:
			p.title = "▮▮▮";
			break;
		case 2:
			p.title = "▮▮";
			break;
		case 1:
			p.title = "▮";
			break;
	}
}

// 생존자 수 체크 함수
function checkSuvivors() {
	let alive = 0;
	for (let i in _players) {
		let p = _players[i];
		if (p.tag.alive) {
			lastSurvivor = p;
			++alive;
		}
	}
	return alive;
}

App.onStart.Add(function () {
	startState(STATE_INTRO);
});

App.onJoinPlayer.Add(function (p) {
	p.tag = {
		widget: null,
		alive: true,
		hp: 5,
		shield: false,
		time: 1, // 피격 후 1초간 무적 상태를 설정하기 위한 속성
	};

	p.attackSprite = redBoxing;

	// 게임 시작 후 새로운 플레이어 입장시 해당 플레이어를 죽음으로 처리
	if (_start) {
		p.moveSpeed = 5;
		p.sprite = ghost;
		p.tag.alive = false;
		p.sendUpdated();
	}

	_players = App.players;
});

App.onLeavePlayer.Add(function (p) {
	p.attackSprite = null;
	p.title = null;
	p.sprite = null;
	p.moveSpeed = 80;
	p.sendUpdated();

	_players = App.players;
});

// 다른 플레이어를 공격할 때 
App.onUnitAttacked.Add(function (sender, x, y, target) {
	if (_state != STATE_PLAYING) return;

	// 공격자가 죽은 상태이면 return
	if (!sender.tag.alive) return;

	// 타겟이 살아 있는 상태이면서 shield가 false인 경우
	if (target.tag.alive && !target.tag.shield) {
		target.tag.hp--; // 타겟의 체력이 1 만큼 감소

		// 타겟의 체력이 0이된 경우
		if (target.tag.hp == 0) {
			target.title = null; // 타이틀 삭제
			target.tag.alive = false; // alive 속성 false
			target.sprite = ghost; // 캐릭터 이미지를 ghost로 변경
			target.moveSpeed = 5; // moveSpeed 80 -> 5
			target.sendUpdated(); // 타겟 속성 업데이트

			_alive = checkSuvivors();
			// 생존자가 1명이면
			if (_alive == 1) {
				if (_widget) {
					_widget.destroy();
					_widget = null;
				}
				// 접속중인 모든 플레이어에게 result.html 위젯을 보여줌
				_widget = App.showWidget("result.html", "top", 1055, 500);
				// 위젯에 표시할 마지막 생존자의 닉네임을 전달
				_widget.sendMessage({
					alive: _alive,
					name: lastSurvivor.name,
				});

				App.playSound("result.wav");

				_stateTimer = 0;
				startState(STATE_JUDGE);
			}
		} else {
			// 타겟을 공격하면 타겟이 1초간 무적 상태가 됨. (shield = true)
			target.tag.shield = true;
			setHPgage(target, target.tag.hp);
			target.sendUpdated();
		}
	}
});

App.onUpdate.Add(function (dt) {
	if (!_start) return;

	_stateTimer += dt;
	switch (_state) {
		case STATE_INTRO:
			// intro.html 위젯을 5초 동안 표시하고 STATE_INIT 시작
			if (_stateTimer >= 5) {
				if (_widget) {
					_widget.destroy();
					_widget = null;
				}
				App.stopSound();

				startState(STATE_INIT);
			}
			break;
		case STATE_INIT:
			startState(STATE_RULE);
			break;

		// rule.html 위젯을 3초 동안 표시하고 STATE_PLAYING 시작
		case STATE_RULE:
			if (_stateTimer >= 3) {
				if (_widget) {
					_widget.destroy();
					_widget = null;
				}
				startState(STATE_PLAYING);
			}
			break;

		case STATE_PLAYING:
			// status.html 위젯의 생존자 수 업데이트
			if (_widget) {
				_widget.sendMessage({
					suvivors: _alive,
				});
			}

			for (let i in _players) {
				let p = _players[i];

				// 플레이어가 죽은 상태라면 건너뜀
				if (!p.tag.alive) continue;

				// 피격 후 1초가 지나면 shield 속성을 false로 변경
				if (p.tag.shield) {
					p.tag.time -= dt;
					if (p.tag.time <= 0) {
						p.tag.shield = false;
						p.tag.time = 1; // shield 지속시간 1초로 초기화
					}
				}
			}

			_alive = checkSuvivors();

			// 생존자가 1명 또는 0명일 경우 result.html 위젯 표시
			if (_alive == 1) {
				if (_widget) {
					_widget.destroy();
					_widget = null;
				}

				_widget = App.showWidget("result.html", "top", 1055, 500);
				_widget.sendMessage({
					alive: _alive,
					name: lastSurvivor.name,
				});

				App.playSound("result.wav");
				startState(STATE_JUDGE);
			} else if (_alive == 0) {
				if (_widget) {
					_widget.destroy();
					_widget = null;
				}

				_widget = App.showWidget("result.html", "top", 1055, 500);
				_widget.sendMessage({
					alive: _alive,
				});

				App.playSound("result.wav");
				startState(STATE_JUDGE);
			}

			break;
		//result.html 위젯을 5초 동안 표시하고 STATE_END 시작
		case STATE_JUDGE:
			if (_stateTimer >= 5) {
				startState(STATE_END);
			}
			break;
		case STATE_END:
			break;
	}
});

// App이 종료되거나 Game Block이 파괴 될 때 실행되는 함수
App.onDestroy.Add(function () {
	// 앱으로 설치된 모든 오브젝트 삭제
	Map.clearAllObjects();
});

페인트맨 게임

1) 파일

61KB
PaintMan_Sample.zip
archive

2) main.js

// load sprite
let redman = App.loadSpritesheet('redman.png', 48, 64, {
    left: [5, 6, 7, 8, 9],       // defined base anim 
    up: [15, 16, 17, 18, 19],    // defined base anim 
    down: [0, 1, 2, 3, 4],       // defined base anim 
    right: [10, 11, 12, 13, 14], // defined base anim 
}, 8);

// load sprite
let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9],
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
}, 8);

const STATE_INIT = 3000;
const STATE_READY = 3001;
const STATE_PLAYING = 3002;
const STATE_JUDGE = 3004;
const STATE_END = 3005;

// load sprite
let red = App.loadSpritesheet('red.png');
let blue = App.loadSpritesheet('blue.png');
let tomb = App.loadSpritesheet('tomb.png');

let _start = false;
let _gameEnd = false;
let _state = STATE_INIT;
let _stateTimer = 0;
let _timer = 90;

let _objects = {};

let _redScore = 0;
let _blueScore = 0;

// for using hash key
let HEIGHT_KEY = 10000000;

let _blueTeam = [];
let _redTeam = [];

let _players = App.players; // App.players : get total players
let TEAM_COUNTER = 0;
let _resultStr = '';

function startState(state)
{
    _state = state;
    _stateTimer = 0;

    switch(_state)
    {
        case STATE_INIT:
            _start = true;
            _stateTimer = 0;
            _timer = 90;

            _redScore = 0;
            _blueScore = 0;
            _objects = {};

            for(let i in _players) {
                let p = _players[i];
                 // create and utilize option data using tags.
                p.tag = {
                    x: p.tileX,
                    y: p.tileY,
                    sturn : false,
                    sTime : 1,
                    super : false,
                    team: Math.floor(TEAM_COUNTER % 2),
                };

                TEAM_COUNTER++;

                if(p.tag.team == 0)
                    _redTeam.push(p);
                else if(p.tag.team == 1)
                    _blueTeam.push(p);

                p.sprite = p.tag.team == 0 ? redman : blueman;
                p.sendUpdated();
            }
            break;
        case STATE_READY:
            for(let i in _players) {
                let p = _players[i];
                p.moveSpeed = 0;
                p.sendUpdated();
            }
            break;
        case STATE_PLAYING:
            for(let i in _players) {
                let p = _players[i];
                p.moveSpeed = 80;
                p.sendUpdated();
            }
            break;
        case STATE_JUDGE:
            for(let i in _players) {
                let p = _players[i];
                p.moveSpeed = 0;
                p.sendUpdated();
            }
            break;
        case STATE_END:
            _start = false;

            for(let i in _players) {
                let p = _players[i];
                p.moveSpeed = 80;
                p.title = null;
                p.sprite = null;
                p.sendUpdated();
            }

            // remove all objects created by the zep-scripts
            // 젭스크립트로 생성된 모든 오브젝트를 제거
            Map.clearAllObjects();
            break;
    }
}

App.onStart.Add(function(){
    startState(STATE_INIT);
});

// when player join the space event
// 플레이어가 스페이스에 입장 했을 때 이벤트
App.onJoinPlayer.Add(function(p) {
    p.tag = {
        x: p.tileX,
        y: p.tileY,
        sturn : false,
        sTime : 1,
        super : false,
        team: Math.floor(TEAM_COUNTER % 2),
    };

    if(p.tag.team == 0)
        _redTeam.push(p);
    else if(p.tag.team == 1)
        _blueTeam.push(p);

    TEAM_COUNTER++;
    p.nameColor = p.tag.team == 0 ? 16711680 : 255;
    p.sprite = p.tag.team == 0 ? redman : blueman;
    p.sendUpdated();

    _players = App.players;
});

// when player leave the space event
// 플레이어가 스페이스를 나갔을 때 이벤트
App.onLeavePlayer.Add(function(p) {
    p.moveSpeed = 80;
    p.title = null;
    p.sprite = null;
    p.sendUpdated();

    _players = App.players; // update all plyers for update(dt)
});

// when player touched objects event
// 플레이어가 오브젝트와 부딪혔을 때 
App.onDestroy.Add(function() {
    Map.clearAllObjects();
})

// when player attacked other player event (z key)
// 플레이어가 다른 플레이어를 공격혔을 때 (Z키)
App.onUnitAttacked.Add(function(sender, x, y, target) {
    if(_state != STATE_PLAYING)
        return;

    // not stun, not invincible, not on the same team
    // 스턴상태가 아니고, 무적이 아니고, 같은 팀이 아니면 스턴을 건다.
    if(!target.tag.sturn && sender.tag.team != target.tag.team && !target.tag.super)
    {
        target.tag.sturn = true;
        target.moveSpeed = 0;
        target.sendUpdated();
    }
});

// called every 20ms
// 20ms 마다 호출되는 업데이트
// param1 : deltatime ( elapsedTime )
App.onUpdate.Add(function(dt) {
    if(!_start)
        return;

    _stateTimer += dt;

    switch(_state)
    {
        case STATE_INIT:
            App.showCenterLabel("The team that has painted the most land wins.\nHitting another teammate stuns them for 1 second.");
            
            if(_stateTimer >= 5)
            {
                startState(STATE_READY);
            }
            break;
        case STATE_READY:
            App.showCenterLabel("The game will start soon.");
            if(_stateTimer >= 3)
            {
                startState(STATE_PLAYING);
            }
            break;
        case STATE_PLAYING:
            App.showCenterLabel(_timer +  `\nRED TEAM  VS  BLUE TEAM\n` + _redScore + "  VS  " + _blueScore);
            if(_stateTimer >= 1) {
                _stateTimer = 0;
                _timer--;
            }

            // time over
            if(_timer <= 0)
            {
                if(_redScore > _blueScore)
                {
                    for(let i in _players) {
                        let p = _players[i];
                        p.title = null;
                        if(p.tag.team == 1)
                        {
                            p.sprite = tomb;
                            p.moveSpeed = 0;
                            p.sendUpdated();
                        }
                    }
                    _resultStr = 'RED TEAM  VS  BLUE TEAM\n' + _redScore + "  VS  " + _blueScore + '\nRED TEAM WIN';
                }
                else if(_redScore < _blueScore)
                {
                    for(let i in _players) {
                        let p = _players[i];
                        p.title = null;
                        if(p.tag.team == 0)
                        {
                            p.sprite = tomb;
                            p.moveSpeed = 0;
                            p.sendUpdated();
                        }
                        _resultStr = 'RED TEAM  VS  BLUE TEAM\n' + _redScore + "  VS  " + _blueScore + '\nBLUE TEAM WIN';
                    }
                }
                else
                {
                    for(let i in _players) {
                        let p = _players[i];
                        p.title = null;
                        p.sprite = null;
                        p.sendUpdated();
                    }  
                    _resultStr = 'RED TEAM  VS  BLUE TEAM\n' + _redScore + "  VS  " + _blueScore + '\nDRAW';
                }
                startState(STATE_JUDGE);
            }
            else
            {
                for(let i in _players) {
                    let p = _players[i];
                    
                    // for speed buff
                    if(_timer == 30 || _timer == 20 || _timer == 10)
                    {
                        if(_redScore > _blueScore)
                        {
                            if(p.tag.team == 1)
                            {
                                // set player title
                                p.title = '<SPEED UP>';
                                p.moveSpeed = 90;
                                p.sendUpdated();
                            }
                            else
                            {
                                p.title = null;
                                p.moveSpeed = 80;
                                p.sendUpdated();
                            }
                        }
                        else if(_redScore < _blueScore)
                        {
                            if(p.tag.team == 0)
                            {
                                p.title = '<SPEED UP>';
                                p.moveSpeed = 90;
                                p.sendUpdated();
                            }
                            else
                            {
                                p.title = null;
                                p.moveSpeed = 80;
                                p.sendUpdated();
                            }
                        }
                    }

                    // strun state check
                    if(p.tag.sturn)
                    {
                        p.tag.sTime -= dt;
                        if(p.tag.sTime <= 0)
                        {
                            p.tag.sturn = false;
                            p.tag.super = true;
                            p.tag.sTime = 1;
                            p.moveSpeed = 80;
                            p.sendUpdated();
                        }
                    }

                    // invincible state check
                    if(p.tag.super)
                    {
                        p.tag.sTime -= dt;
                        if(p.tag.sTime <= 0)
                        {
                            p.tag.super = false;
                            p.tag.sTime = 1;
                            p.sendUpdated();
                        }
                    }

                    // paint tile and update score
                    if(p.tag.x != p.tileX || p.tag.y != p.tileY) {
                        p.tag.x = p.tileX;
                        p.tag.y = p.tileY;
                        
                        let oldValue = _objects[p.tileY * HEIGHT_KEY + p.tileX];
                        if(oldValue == p.tag.team)
                            continue;
        
                        if(oldValue == 0) {
                            _redScore--;
                        } else if(oldValue == 1) {
                            _blueScore--;
                        }
        
                        if(p.tag.team == 0)
                            _redScore++;
                        else
                            _blueScore++;
                        
                        _objects[p.tileY * HEIGHT_KEY + p.tileX] = p.tag.team;
                        
                        Map.putObject(p.tileX, p.tileY, p.tag.team == 0 ? red : blue,
                        {
                            overlap: true,
                        });
                    }
                }
            }
            break;
        case STATE_JUDGE:
            App.showCenterLabel(_resultStr);

            if(_stateTimer >= 5)
            {
                startState(STATE_END);
            }
            break;
        case STATE_END:
            break;
    }
});

TileEffectType 상세 설명

Map.putTileEffect 함수에서 사용되는 타일 효과(TileEffectType)의 사용 방법을 안내하는 페이지입니다. 타일 효과에 대한 자세한 설명은 아래 링크를 참고해주세요.

🔥 타일 효과

📗 기본 타일 효과

TileEffectType.NONE

NONE 타입의 타일 효과 입니다.

예시

//해당 좌표에 있는 타일 효과를 지웁니다.
Map.putTileEffect(x, y, TileEffectType.NONE);

TileEffectType.IMPASSABLE

이동불가(IMPASSABLE) 타일 효과 입니다

예시

//해당 좌표에 이동불가(IMPASSABLE) 타일 효과를 설치합니다.
Map.putTileEffect(x, y, TileEffectType.IMPASSABLE);

TileEffectType.SPAWN

플레이어가 맵에 진입 할 때 진입 지점을 설정하는 타일 효과 입니다

예시

//해당 좌표에 스폰(SPAWN) 타일 효과를 설치합니다.
Map.putTileEffect(x, y, TileEffectType.SPAWN);

🌀 포털 관련 타일 효과

TileEffectType.PORTAL

스페이스 내 다른 맵으로 이동 또는 맵 내 지정 영역으로 이동 하는 타일 효과 입니다.

파라미터

이름
타입
설명

type

Number

type이 0 인 경우 스페이스 내 다른맵으로 이동하는 포털 타일을 설치합니다. type이 1 인 경우 맵 내 지정 영역으로 이동하는 포털 타일을 설치합니다.

targetMapID

String

이동할 맵의 MapID 값

label

String

포털 위에 표시할 텍스트 값

triggerByTouch

Boolean

true인 경우: 닿았을 때 실행 false인 경우: F를 눌러 실행

invisible

Boolean

true인 경우: 기본 포털 이미지를 숨김 false인 경우: 기본 포털 이미지가 보임

locationName

String

이동할 로케이션의 이름 값 ( type이 1인 경우 필수 입력 )

예시

// type: 0 인 경우
// 스페이스 내 다른맵으로 이동하는 포털 타일을 설치합니다.
Map.putTileEffect(x, y, TileEffectType.PORTAL, {
	type: 0, // 필수
	locationName: "TEST",  // 선택
	targetMapID: "gyV1N2", // 필수
	label: "PORTAL-TYPE0", // 선택
  triggerByTouch: true // 선택, 기본값 false
});

// type: 1 인 경우
// 맵 내 지정 영역으로 이동하는 포털 타일을 설치합니다.
Map.putTileEffect(x, y, TileEffectType.PORTAL, {
	type: 1, // 필수
	label: "PORTAL-TYPE1",  // 선택
	locationName: "TEST", // 필수
	invisible: true, // 선택, 기본 포털이미지 숨기기
	triggerByTouch: true  // 선택, 기본값 false
});

TileEffectType.SPACE_PORTAL

외부 스페이스로 이동하는 타일 효과 입니다.

파라미터

이름
타입
설명

label

String

포털 위에 표시할 텍스트 값

targetMapID

String

이동할 외부 스페이스 ID값 * https://zep.us/play/[스페이스 ID]

locationName

String

이동할 로케이션의 이름 값

triggerByTouch

Boolean

true인 경우: 닿았을 때 실행 false인 경우: F를 눌러 실행

invisible

Boolean

true인 경우: 기본 포털 이미지를 숨김 false인 경우: 기본 포털 이미지가 보임

예시

// 외부 스페이스로 이동하는 포털 타일을 설치합니다.
Map.putTileEffect(x, y, TileEffectType.SPACE_PORTAL, {
	label: "SPACE_PORTAL",  // 선택
	targetMapID: "zydmYD", //필수
	locationName: "SPACE1",  // 선택
	invisible: true,  // 선택, 기본값 false
	triggerByTouch: true,  // 선택, 기본값 false
});

🌐 임베드 관련 타일 효과

TileEffectType.EMBED

팝업으로 웹 링크를 여는 타일 효과입니다.

파라미터

이름
타입
설명

link

String

웹 URL 값

align2

String

팝업을 표시할 위치 ’popup’, ‘sidebar’, ‘top’, ‘topleft’, ‘topright’, ‘middle’, ‘middleleft’, ‘middleright’, ‘bottom’, ‘bottomleft’, ‘bottomright’

label

String

포털 위에 표시할 텍스트 값

triggerByTouch

Boolean

true인 경우: 닿았을 때 실행 false인 경우: F를 눌러 실행

invisible

Boolean

true인 경우: 기본 포털 이미지를 숨김 false인 경우: 기본 포털 이미지가 보임

예시

// 팝업으로 웹 링크를 여는 타일 효과를 설치합니다.
Map.putTileEffect(x, y, TileEffectType.EMBED, {
	link: "https://zep.us/", // 필수
	align2: "top", // 필수
	label: "ZEP-SCRIPT-EMBED",  // 선택
});

TileEffectType.WEB_PORTAL

새 탭으로 웹 링크를 여는 타일 효과입니다.

파라미터

이름
타입
설명

link

String

웹 URL 값

label

String

포털 위에 표시할 텍스트 값

invisible

Boolean

true인 경우: 기본 포털 이미지를 숨김 false인 경우: 기본 포털 이미지가 보임

예시

// 새탭으로 웹 링크를 여는 타일 효과를 설치합니다.
Map.putTileEffect(x, y, TileEffectType.WEB_PORTAL, {
	link: "https://zep.us/", // 필수
	label: "ZEP-SCRIPT-WEB-PORTAL", // 선택
	invisible: true, // 선택, 기본값 false
});

TileEffectType.TILE_EMBED

웹 화면을 고정 영역에 표시하는 타일 효과입니다.

파라미터

이름
타입
설명

link

String

웹 URL 값

width

number

고정 영역의 너비 ( 타일 수 )

height

number

고정 영역의 높이 ( 타일 수 )

예시

// 웹 화면을 고정 영역에 설치합니다.
Map.putTileEffect(x, y, TileEffectType.TILE_EMBED, {
	link: "https://zep.us/", // 필수
	width: 5, // 필수
	height: 5, // 필수
});

💠 유틸 타일 효과

TileEffectType.PRIVATE_AREA

프라이빗 영역 타일 효과 입니다.

파라미터

이름
타입
설명

id

Number

프라이빗 영역의 ID 값

impassable

Boolean

true일 경우 프라이빗 영역을 impassable(통과 불과)로 만듭니다

param1

String

param1 이 “true” 일 경우 인원제한 프라이빗 영역이 설정됩니다.

예시

// 해당 좌표에 프라이빗 영역을 설치합니다.
Map.putTileEffect(18, 15, TileEffectType.PRIVATE_AREA, {
		id: 3, // 필수
		impassable: false,  // 선택, 기본 값 false
		param1: "true",   // 선택, 기본 값 "false"
	});

TileEffectType.LOCATION

지정영역 타일 효과 입니다.

파라미터

이름
타입
설명

label

String

타일 위에 표시할 텍스트 값

name

String

지정 영역의 이름

width

number

고정 영역의 너비 ( 타일 수 )

height

number

고정 영역의 높이 ( 타일 수 )

예시

// 해당 좌표에 지정 영역을 설치합니다.
Map.putTileEffect(x, y, TileEffectType.LOCATION, {
	label: "LOCATION",  // 선택
	name: "zep-script-location", // 필수
	width: 3, // 필수
	height: 2, // 필수
});

TileEffectType.AMBIENT_SOUND

배경 음악 타일 효과 입니다.

파라미터

이름
타입
설명

link

String

재생할 음악 파일의 이름 ( 압축 파일에 포함 )

activeDistance

Number

음악의 재생 범위 ( 타일 )

triggerByTouch

Boolean

true인 경우: 닿았을 때 실행 false인 경우: F를 눌러 실행

예시

Map.putTileEffect(x, y, TileEffectType.AMBIENT_SOUND, {
	link: "ring.mp3", // 필수
	activeDistance: 1, // 필수
	triggerByTouch: false, // 선택
});

Callbacks

소개

스크립트 개발자가 지정한 키를 플레이어가 눌렀을 때 또는 스크립트 개발자가 지정한 지점에 도착했을 때 등, 조건을 설정하여 플레이어가 조건을 달성했을 경우 동작하는 함수들 입니다.

이름
설명

runLater

지정한 시간(초) 후 동작하는 함수 입니다.

addOnTileTouched

지정한 x, y 좌표에 플레이어가 도착했을 때 실행되는 함수입니다.

addOnLocationTouched

지정한 ‘지정영역’에 플레이어가 도착했을 때 실행되는 함수입니다.

addOnKeyDown

플레이어가 지정된 키를 눌렀을 때 실행되는 함수 입니다.

setTimeout

지정한 시간(ms) 후 함수를 실행합니다.

setInterval

지정한 시간(ms) 간격으로 함수를 실행합니다.

addMobileButton

모바일 환경에서 커스텀 모바일 버튼을 추가하고, 버튼을 눌렀을 때 동작하는 함수를 지정합니다.

putMobilePunch

모바일 환경에서 펀치 버튼을 추가합니다.

putMobilePunchWithIcon

로드한 이미지로 펀치 버튼을 만들어 추가합니다.

📚 API 설명 및 예제

Callbacks 함수 한 눈에 보기

// time(초) 후에 callback 함수를 실행
App.runLater(callback, time: number)

// 플레이어가 해당 위치의 타일과 부딪혔을 때 실행
App.addOnTileTouched(x: number, y: number, callback)

// 플레이어가 지정한 위치와 부딪혔을 때 실행
App.addOnLocationTouched(name: string, callback)

// 플레이어가 지정된 키를 눌렀을 때 실행
App.addOnKeyDown(keycode : number, callback);

// time(ms) 후에 callback 함수를 실행
setTimeout(callback, time: number)

// time(ms) 후 함수를 실행
setInterval(callback, time: number)

// 모바일 환경에서 커스텀 모바일 버튼을 추가하고, 버튼을 눌렀을 때 동작하는 함수를 지정
App.addMobileButton(anchor: number, posX: number, posY: number, function(player){} )

// 모바일 환경에서 펀치 버튼 추가/제거합니다.
App.putMobilePunch(enable: boolean = true)

// 로드한 이미지로 펀치 버튼을 만들어 추가합니다.
App.putMobilePunchWithIcon(icon: ScriptDynamicResource)

runLater

App.runLater(function(){}, time: number);

time(초) 후에 callback 함수를 실행합니다.

파라미터

이름
타입
설명

time

Number

몇 초 후에 실행 될 지를 정하는 시간 (초)

예제

앱이 시작되고 5초 후 메시지 출력해보기

App.onStart.Add(function () {
	App.runLater(function() {
		App.showCenterLabel("메시지");
	}, 5);
});

addOnTileTouched

App.addOnTileTouched(x: number, y: number, function(player){})

지정한 x, y좌표에 플레이어가 도착할 경우 callback 함수를 실행합니다.

파라미터

이름
타입
설명

x, y

number

지정 할 x, y 좌표

예제

플레이어가 지정 좌표에 도착 했을 때 메시지 출력해보기

// 플레이어가 5, 5 좌표에 도착한 경우
App.addOnTileTouched(5, 5, function (player) {
	App.showCenterLabel(`${player.name}님이 (5, 5) 좌표에 도착!`);
});

addOnLocationTouched

addOnLocationTouched(name: string, function(player){})

플레이어가 맵에디터에서 지정한 ‘지정영역’에 도착했을 때 callback 함수를 실행합니다.

파라미터

이름
타입
설명

name

String

맵 에디터에서 지정한 ‘지정 영역’의 이름

player

Player

지정 영역에 도착한 플레이어를 가르킴 파라미터의 이름은 임의로 지정 가능

예제

플레이어가 지정 영역에 도착했을 때 메시지 출력해보기

// 플레이어가 이름이 "myLocation"인 영역에 도착했을 때 실행
App.addOnLocationTouched("myLocation", function(player){
	App.showCenterLabel(`${player.name}님이 myLocation에 도착했습니다.`)
});

addOnKeyDown

App.addOnKeyDown(keycode : number, function(player){});

플레이어가 지정된 키를 눌렀을 때 callback 함수를 실행합니다.

파라미터

이름
타입
설명

keycode

Number

키에 해당하는 숫자

player

Player

해당 키를 누른 플레이어를 가르킴 player 파라미터 이름은 임의로 변경 가능

예제

a를 눌렀을 때 메시지 출력해보기 ( a의 키코드: 65 )

// 플레이어가 a를 눌렀을 때 실행
App.addOnKeyDown(65, function(player){
	App.sayToAll(`${player.name}님이 a키를 눌렀습니다.`)
});

setTimeout

setTimeout(function(){}, time: number);

time(ms) 후에 callback 함수를 실행합니다.

파라미터

이름
타입
설명

time

Number

callback 함수 실행 전 대기 시간 (ms)

예제

앱이 시작되고 5초 후 메시지 출력해보기

App.onStart.Add(function () {
	setTimeout(function () {
		App.sayToAll("5초후 메시지 출력");
	}, 5000);
});

setInterval

setInterval(function(){}, time: number);

time(ms) 간격으로 callback 함수를 실행합니다.

파라미터

이름
타입
설명

time

Number

callback 함수 실행 주기 (ms)

예제

앱이 시작되고 1초 간격으로 메시지 출력해보기

let time = 0;
App.onStart.Add(function () {
	setInterval(function () {
		App.sayToAll(`앱 실행 ${++time}초 경과`);
	}, 1000);
});

addMobileButton

App.addMobileButton( anchor: number, posX: number, posY: number, function(player){} )

모바일 환경에서 커스텀 모바일 버튼을 추가하고, 버튼을 눌렀을 때 동작하는 함수를 지정합니다.

모바일버튼의을이미지를 원하는 이미지로 변경 할 수 있습니다.

모바일버튼 이미지 변경하기

파라미터

이름
타입
설명

anchor

Number

모바일 버튼의 위치를 숫자로 입력합니다. TOP = 0, TOP_LEFT = 1, TOP_RIGHT = 2, MIDDLE = 3, MIDDLE_LEFT = 4, MIDDLE_RIGHT = 5, BOTTOM = 6, BOTTOM_LEFT = 7, BOTTOM_RIGHT = 8

posX

Number

모바일 버튼 x 방향 오프셋 수치

posY

Number

모바일 버튼 y 방향 오프셋 수치

player

Player

모바일 버튼을 누른 플레이어를 가르킴

예제

모바일 버튼 추가해보기

예제를 참고해 버튼의 위치를 설정해보세요!
App.onStart.Add(function () {
	// Bottom_Right
	App.addMobileButton(8, 145, 75, function (player) {
		App.sayToAll(`${player.name}, Bottom 버튼A`);
	});
	// Bottom_Right
	App.addMobileButton(8, 145, -20, function (player) {
		App.sayToAll(`${player.name}, Bottom 버튼B`);
	});
	// Top
	App.addMobileButton(0, 0, 400, function (player) {
		App.sayToAll(`${player.name}, TOP 버튼`);
	});
	// Top_Left
	App.addMobileButton(1, 50, 400, function (player) {
		App.sayToAll(`${player.name}, TOP_LEFT 버튼`);
	});
	// Top_right
	App.addMobileButton(2, 50, 400, function (player) {
		App.sayToAll(`${player.name}, TOP_RIGHT 버튼`);
	});
	// Middle
	App.addMobileButton(3, 0, 100, function (player) {
		App.sayToAll(`${player.name}, MIDDLE 버튼`);
	});
	// Middle_left
	App.addMobileButton(4, 50, 100, function (player) {
		App.sayToAll(`${player.name}, MIDDLE LEFT 버튼`);
	});
	// Middle_right
	App.addMobileButton(5, 50, 100, function (player) {
		App.sayToAll(`${player.name}, MIDDLE RIGHT 버튼`);
	});
});

putMobilePunch

App.putMobilePunch(enable: boolean = true)

enable이 true이면 모바일 환경에서 펀치 버튼이 추가됩니다.

파라미터

이름
타입
설명

enable

Boolean

모바일 펀치 버튼 활성화 여부 ( 기본 값 true )

예제

Q 버튼을 눌러 모바일 환경에 펀치 버튼을 추가/제거 해보기

let punchButton = false;
// Q 버튼을 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	if (!punchButton) {
		punchButton = true;
		App.putMobilePunch();
	} else {
		punchButton = false;
		App.putMobilePunch(false);
	}
});

putMobilePunchWithIcon

App.putMobilePunchWithIcon(icon: ScriptDynamicResource)

로드한 이미지로 펀치 버튼을 만들어 추가합니다.

파라미터

이름
타입
설명

icon

ScriptDynamicResource

App.loadSpriteSheet 함수로 로드한 이미지 리소스

예제

Q 버튼을 눌러 모바일 환경에 로드한 이미지로 펀치 버튼 추가하기

펀치아이콘
const punchIcon = App.loadSpritesheet("punchIcon.png")
// Q 버튼을 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	App.putMobilePunchWithIcon(punchIcon);
});

부록

자바스크립트 키코드 표

똥피하기 게임

1) 파일

2) main.js


const poop = App.loadSpritesheet("poop.png");
let tomb = App.loadSpritesheet("tomb.png", 32, 48, {
	left: [0],
	right: [0],
	up: [0],
	down: [0],
});

const STATE_INIT = 3000;
const STATE_READY = 3001;
const STATE_PLAYING = 3002;
const STATE_JUDGE = 3004;
const STATE_END = 3005;
const STATE_INTRO = 3006;

let _level = 1;
let _levelTimer = 15;
let _levelAddTimer = 0;

let _start = false;
let _ending = false;
let _timer = 5;

let _poops = [];
let _stateTimer = 0;

let _genTime = 0;
let _dropTime = 0;
let _flushTime = 0;

let _viewPlayer = false;
let _viewTimer = 1;
let _viewAddTimer = 0;

let _live = 0;
let _liveList = "";

let _widget = null;
let _widget2 = null;
let _players = App.players;

let HEIGHT_KEY = 10000000;
let _areaPoint = {};

let poopkeys = [];

// CustomLabel을 span 태그로 스타일을 지정해출력하는 함수
function customShowLabelWithRadius(player, str, width, radius) {
	if (player) {
		if (player.isMobile) {
			width = 100;
		}
	}
	// span 태그
	let spanStyle = `<span style="
			position: absolute;
			margin: auto;
			display: flex;
			align-items: center;
			justify-content: center;
			padding: 10px;
			width: ${width}%;
			top: 200px;
			left: ${(100 - width) / 2}%;
			background-color: rgba(0, 0, 0, 0.6);
			flex-direction: column;
			border-radius: ${radius}px;">`;

	let res = spanStyle + str + "</span>";
	if (player) {
		player.showCustomLabel(res, 0xffffff, 0x000000, -150, 100, 1);
	} else {
		App.showCustomLabel(res, 0xffffff, 0x000000, -150, 100, 1);
	}
}

// 모든 플레이어에게 라벨을 출력하는 함수
function showLabel(str) {
	for (let i in _players) {
		let p = _players[i];
		customShowLabelWithRadius(p, str, 40, 14);
	}
}

function startState(state) {
	_state = state;
	_stateTimer = 0;

	if (_widget) {
		_widget.destroy();
		_widget = null;
	}

	switch (_state) {
		case STATE_INTRO:
			// 게임시작 라벨 및 사운드 출력
			showLabel("\n 미니게임 - 똥피하기 \n\n");
			App.playSound("intro.ogg");

			// 게임시작 변수(_start)를 true로 변경
			_start = true;
			break;

		case STATE_INIT:
			// 게임 목표 라벨 출력 및 게임 변수 초기화
			showLabel("- 게임목표 - \n\n 위에서 떨어지는 똥을 피해 마지막까지 생존하세요 \n\n ( 잠시 후 게임이 시작됩니다 )");

			_stateTimer = 0;
			_genTime = 0;
			_dropTime = 0;
			_timer = 90;

			for (let i in _players) {
				let p = _players[i];
				p.tag = {
					alive: true,
				};
			}
			break;

		case STATE_PLAYING:
			break;

		case STATE_JUDGE:
			// 최종 생존자 라벨 출력
			Map.clearAllObjects();
			App.playSound("result.ogg", false);

			if (_live == 1) {
				showLabel(`- 최종 생존자는 - \n\n ${lastSurvivor.name}`);
			} else if (_live == 0) {
				showLabel(`생존자가 없습니다.`);
			} else {
				showLabel(`- 최종 생존자는 - \n\n ${_liveList}`);
			}

			break;

		case STATE_END:
			// 게임시작 변수(_start)를 false로 변경, 모든 플레이어의 속도 80, 스프라이트를 null(기본상태)로 변경
			_start = false;
			for (let i in _players) {
				let p = _players[i];
				p.sprite = null;
				p.moveSpeed = 80;
				p.sendUpdated();
			}
			App.stopSound(); // 사운드 Off
			break;
	}
}

App.onInit.Add(function () {
	if (_widget) {
		_widget.destroy();
		_widget = null;
	}
});

App.onStart.Add(function () {
	startState(STATE_INTRO);
});

App.onJoinPlayer.Add(function (p) {
	// 게임이 시작 된 후 새로운 플레이어가 입장하면 탈락으로 처리
	if (_start) {
		p.tag = {
			alive: false,
		};

		p.moveSpeed = 20;
		p.sprite = tomb;
		p.sendUpdated();
	} else {
		p.moveSpeed = 80;
		p.sprite = null;
		p.sendUpdated();
	}
	_players = App.players;
});

// 플레이어가 나갈 때 sprite title moveSpeed 초기화
App.onLeavePlayer.Add(function (p) {
	p.title = null;
	p.sprite = null;
	p.moveSpeed = 80;
	p.sendUpdated();

	_players = App.players;
});

// 똥 오브젝트에 닿았을 때
App.onAppObjectTouched.Add(function (player, key, x, y, tileID) {
	// 플레이어가 탈락하지 않은 상태일 경우 똥에 닿으면 탈락
	if (player.tag.alive) {
		_viewPlayer = true;
		_viewAddTimer = 0;
		player.tag.alive = false;
		player.sprite = tomb;
		player.moveSpeed = 20;
		player.sendUpdated();
		App.playSound("poopp.mp3");
		if (checkSuvivors() !== 1) {
			showLabel(`${player.name} 님 탈락!`);
		}
	}
});

// 생존자 수를 체크하는 자바스크립트 일반 함수
function checkSuvivors() {
	if (!_start) return;

	let alive = 0;
	for (let i in _players) {
		let p = _players[i];
		if (!p.sprite) {
			lastSurvivor = p;
			++alive;
		}
	}
	return alive;
}

// 최종 생존자의 이름을 _liveList에 추가하는 자바스크립트 일반 함수
function broardCastingSuvivors() {
	if (!_start) return;
	_liveList = "";
	for (let i in _players) {
		let p = _players[i];
		if (p.tag.alive) {
			_liveList += p.name + " ";
		}
	}
}

// 게임 종료 블록을 눌렀을 때
App.onDestroy.Add(function () {
	// 스크립트로 설치한 모든 오브젝트 제거
	Map.clearAllObjects();

	if (_widget) {
		_widget.destroy();
		_widget = null;
	}
	// 사운드 전체 Off
	App.stopSound();
});

App.onUpdate.Add(function (dt) {
	if (!_start) return;

	_stateTimer += dt;
	switch (_state) {
		case STATE_INTRO:
			// STATE_INTRO 상태에서 3초동안 똥피하기 인트로 라벨을 보여주고 STATE_INIT 로직 실행
			if (_stateTimer >= 3) {
				startState(STATE_INIT);
			}
			break;
		case STATE_INIT:
			// STATE_INIT 상태에서 3초동안 게임 목표 라벨을 보여주고 STATE_INIT 로직 실행
			if (_stateTimer >= 3) {
				startState(STATE_PLAYING);
			}
			break;

		case STATE_PLAYING:
			// 탈락 메시지를 표시중이지 않을 때 라벨을 표시하도록 처리
			if (!_viewPlayer) {
				if (_level < 6) {
					showLabel(`- 레벨 ${_level} - \n ${_timer} 초`);
				} else {
					showLabel(`- 최고레벨 - \n ${_timer} 초`);
				}
			}
			_genTime -= dt;
			let poopkey;
			if (_genTime <= 0) {
				// 오브젝트 생성주기(genTime)가 똥피하기 난이도 단계(_level)에 따라 짧아짐
				_genTime = Math.random() * (0.5 - _level * 0.05);
				let rand_X = Math.floor(Map.width * Math.random());

				// 똥 오브젝트의 key 값 생성
				poopkey = new Date().getTime() + Math.random();
				poopkeys.push(poopkey);

				// 똥피하기 난이도 단계(_level)에 따라 똥의 이동속도가 증가
				Map.putObjectWithKey(rand_X, 0, poop, {
					overlap: true,
					movespeed: 80 + _level * 10,
					key: poopkey,
				});

				// 똥 오브젝트를 맵 끝으로 이동
				Map.moveObjectWithKey(poopkey, rand_X, Map.height - 1, false);
			}

			//_flushTime(3초) 마다 맵 하단에 쌓인 똥 오브젝트를 삭제
			_flushTime += dt;
			if (_flushTime >= 3) {
				_flushTime = 0;
				for (let i = 0; i < poopkeys.length; i++) {
					let key = poopkeys[i];
					if (Map.getObjectWithKey(key).tileY == Map.height - 1) {
						Map.putObjectWithKey(Map.getObjectWithKey(key).tileX, Map.height - 1, null, {
							key: key,
						});
						poopkeys.splice(i--, 1);
					}
				}
			}

			// _levelTimer(15초) 마다 난이도 단계가 1씩 증가
			_levelAddTimer += dt;
			if (_levelAddTimer >= _levelTimer) {
				_level++;
				_levelAddTimer = 0;

				// 최고 난이도 단계를 6으로 제한
				if (_level > 6) {
					_level = 6;
				}
			}

			// 생존자 수 체크 함수 호출
			_live = checkSuvivors();

			// 생존자 수 가 1명 또는 0명일 경우 게임 종료 로직 실행
			if (_live == 1 || _live == 0) {
				startState(STATE_JUDGE);
			} else {
				if (_stateTimer >= 1) {
					_stateTimer = 0;
					_timer--;
					// 시간(_timer)변수가 0이 되면 최종 생존자 발표
					if (_timer <= 0) {
						broardCastingSuvivors();
						startState(STATE_JUDGE);
					}
				}
			}

			// 플레이어가 탈락 한 경우 1초 동안 탈락 메시지를 보여주고 _viewPlayer를 false로 변경
			if (!_viewPlayer) {
			} else {
				_viewAddTimer += dt;
				if (_viewAddTimer >= _viewTimer) {
					_viewAddTimer = 0;
					_viewPlayer = false;
				}
			}
			break;

		case STATE_JUDGE:
			// STATE_JUDGE 상태에서 5초 동안 최종 생존자 라벨을 보여준 뒤 게임 종료로직 실행
			if (_stateTimer >= 5) {
				startState(STATE_END);
			}
			break;
		case STATE_END:
			break;
	}
});
301KB
AvoidPoop_V2_Sample.zip
archive
자바스크립트 키코드 표

나만의 UI 만들기

  1. ZEP 캔버스 위에 내가 원하는 형태의 UI를 그릴 수 있습니다. ZEP에서는 HTML 을 통해 UI를 표현할 수 있습니다.

  2. App.showWidget() 함수를 통해, HTML 파일을 특정 위치에 불러올 수 있습니다. 이 때 인자로 파일명, 정렬, 위젯의 가로와 세로 크기를 정해줍니다. 정렬에 들어가는 값은 ScriptApp에서 Methods를 참고해주세요.

  3. 생성한 변수에 sendMessage() 메소드를 통해, 원하는 값을 HTML 파일에 전달할 수 있습니다.

  4. main.js에서 전달 받은 값을 사용하려면, HTML 문법에 맞게 작성한 다음 addEventListener를 통해 message 타입을 아래 예시와 같이 값을 받아올 수 있습니다.

  • main.js

//UI가 노출될 위치(정렬, 가로, 세로) 값을 변수로 사전에 제작
let postion = 'middle';
let width = 400;
let height = 400;

// my.html로 state라는 태그를 만들어 hello라는 값을 전달
let _widget = App.showWidget('my.html', position, width, height);
_widget.sendMessage({
	state: "hello",
}); 
  • my.html

<html>
<div id="test">-</div>
<script type="text/javascript">
		**window**.**addEventListener**('message', function(e) {
			// state라는 태그 밸류를 보냈으니 아래 형태로 받는다
			var state = e.data.state; 
			// 위의 id test 라는 곳의 텍스트로 적용한다
			document.getElementById("test").innerText = state;
		})
</script>
</html>

참고

- 튜토리얼의 App type은 installable App을 권장합니다.

- .js파일의 파일이름은 반드시 main 이어야 합니다. main.js 파일을 준비합니다.

- 배포 방법을 아직 모르신다면, ZEP Script 배포 가이드를 참고해주세요.


외부 API 통신하기

자바스크립트 키코드 표

⌨️ 자바스크립트 키코드 표

backspace

8

tab

9

enter

13

shift(left)

16

shift(right)

16

ctrl(left)

17

ctrl(right)

17

alt(left)

18

alt(right)

18

pause/break

19

caps lock

20

escape

27

space

32

page up

33

page down

34

end

35

home

36

left arrow

37

up arrow

38

right arrow

39

down arrow

40

print screen

44

insert

45

delete

46

0

48

1

49

2

50

3

51

4

52

5

53

6

54

7

55

8

56

9

57

a

65

b

66

c

67

d

68

e

69

f

70

g

71

h

72

i

73

j

74

k

75

l

76

m

77

n

78

o

79

p

80

q

81

r

82

s

83

t

84

u

85

v

86

w

87

x

88

y

89

z

90

left window key

91

right window key

92

select key

93

numpad 0

96

numpad 1

97

numpad 2

98

numpad 3

99

numpad 4

100

numpad 5

101

numpad 6

102

numpad 7

103

numpad 8

104

numpad 9

105

multiply

106

add

107

substract

109

decimal point

110

divide

111

f1

112

f2

113

f3

114

f4

115

f5

116

f6

117

f7

118

f8

119

f9

120

f10

121

f11

122

f12

123

num lock

144

scroll lock

145

audio volume mute

173

audio volume down

174

media player

175

audio volume up

181

launch application 1

182

launch application 2

183

semi-colon

186

equal sign

187

comma

188

dash

189

period

190

forward slash

191

Backquote

192

open bracket

219

back slash

220

close bracket

221

single quote

222

달리기

1) 파일

2MB
달리기.zip
archive

2) main.js

const STATE_INIT = 3000;
const STATE_READY = 3001;
const STATE_PLAYING = 3002;
const STATE_JUDGE = 3004;
const STATE_END = 3005;
const STATE_INTRO = 3006;

let _players = App.players; 
let _state = STATE_INIT; 
let _start = false; 
let _stateTimer = 0; 
let _countDown = 10; 
let _finishCountDown = false;
let _finishCount = 30;
let _finishTimer = 0; 
let _delayTimer = 0; 
let _playerBaseSpeed = 120; 
let _rank = 1; 
let _rankList = []; 

//모든 플레이어가 결승점을 통과한 경우 처리  
function finishCheck(){
	
	let allPlayer = 0; 
	let finishedPlayer = 0; 

	for(let p of _players){

		if(!p)
			continue; 

		if(p.tag.isNotPlayer)
			continue; 

		if(p.tag.isFinish)
			finishedPlayer++;
		
		allPlayer++; 
	}

	if(finishedPlayer == allPlayer)
		return true 
	
	return false; 
}

//달리기 맵 설정이 정상적으로 되어있는 지 확인하고, 아닌 경우 에러메세지를 리턴한다. 
function checkSetting(){

	let warningMsg = '';

	if (!Map.hasLocation("race_start_point")) {
		warningMsg = 'race_start_point';

	}
	if (!Map.hasLocation("race_end_point")) {
		warningMsg = 'race_end_point';

	}
	if (!Map.hasLocation("race_finish_point")) {
		warningMsg = 'race_finish_point';

	}
	
	return warningMsg; 
}

function startState(state){

	_state = state; 
  	_stateTimer = 0; 

  	switch (state) {
		
    	case STATE_INIT:
     		for(let p of _players){
				//달리기 앱을 실행한 유저를 확인한다.
				if(p.id == App.creatorID){
					if(p.isMobile)
					//유저의 환경이 모바일인 경우의 위젯 
						p.tag.widget = p.showWidget("setting.html", "bottom", 440, 340);
					else
					//유저의 환경이 pc인 경우의 위젯
						p.tag.widget = p.showWidget("setting.html", "middle", 440, 340);
					
					p.tag.widget.sendMessage({ 
						str_title         : 'Run',
						str_title_text1   : "A race to see who reaches the finish line the fastest",
						str_title_text2   : 'Click the Start Running button to start the game',
						str_title_start   : "Start",
						str_title_how     : "How to set up a map"
					});
					
					p.tag.widget.onMessage.Add(function (sender, msg) {
						//게임을 취소하는 경우, 나타난 위젯을 제거한다 
						if (msg.type == "cancle") {
							if(p.tag.widget_warning){
								p.tag.widget_warning.destroy();
								p.tag.widget_warning = null;
							}

							if(p.tag.widget){
								p.tag.widget.destroy();
								p.tag.widget = null;
							}
						
						} else {
							// 맵에 필요한 설정 체크							
							let warning = checkSetting();
		
							if(warning !== ''){
								if(p.tag.widget_warning){
									p.tag.widget_warning.destroy();
									p.tag.widget_warning = null;
								}
		
								if(p.isMobile)
									p.tag.widget_warning = App.showWidget("warning.html", "top", 440, 60);
								else 
									p.tag.widget_warning = App.showWidget("warning.html", "bottom", 440, 250);
								
								p.tag.widget_warning.sendMessage({
									str_warningText :  'Race track needs to be setup',
									str_warningText2 : ' needed)',
									str_warningText3 : 'Run',
									warningMsg: warning,
								})
							} else {
								if(p.tag.widget){
									p.tag.widget.destroy();
									p.tag.widget = null;
								}
								
								startState(STATE_INTRO);
								_start = true;
							}
						}
					})  
				}
			}
    		break;

		case STATE_INTRO: 
			App.showCenterLabel("\n minigame - RUN \n\n", 0xffffff, 0x000000, 120); ;
			break; 
		
		case STATE_READY:
			for(let p of _players){
				if(p.tag.start)
				   continue; 
				
				//모든 플레이어가 "race_start_point" 로케이션으로 이동한다 
				p.spawnAtLocation("race_start_point"); 

				p.moveSpeed = 0; 
			   	p.sendUpdated();
			}
			break;
		
		case STATE_PLAYING:	
			
			App.showCenterLabel("Start!!", 0xffffff, 0x000000, 120); ;
			
			for(let p of _players){
				if(p.tag.start)
				continue; 
				p.moveSpeed = _playerBaseSpeed; 
				p.sendUpdated();
			}
			break

		// 게임 결과 처리 
		case STATE_JUDGE:
			for(let i =0 ; i <_rankList.length; i++){
				let rank = ''; 
				if(i == 0)
					rank = '1st'; 
				else if(i == 1)
					rank = '2nd';
				else if(i == 2)
					rank = '3rd'; 
				else
					rank = `${i + 1}th`;
				//게임종료 후 등수 표시 
				App.sayToAll(`${rank} : ${_rankList[i].name} !`);
			}
			for(let p of _players){
				p.moveSpeed = 80; 
				p.sendUpdated();
			}
			_start = false;

			break

		// 게임 종료 처리 
		case STATE_END:
			for(let p of _players){
				//게임이 종료되면 모든 플레이어는 "race_end_point" 로케이션으로 이동한다 
				p.spawnAtLocation("race_end_point"); 
				
				if(p.tag.isNotPlayer)
					p.tag.isNotPlayer = false;
			}
			_start = false;
			
			break
  }
}

App.onLeavePlayer.add(function(p){
	_players = App.players;

	p.moveSpeed = 80; 
	p.sendUpdated();

})

// 플레이어가 해당 맵에 들어왔을 때 처리
App.onJoinPlayer.Add(function (p) {
	p.tag = {};
  
	//게임시작 후 들어온 플레이어 처리 
	if (_start) {
		p.tag.isNotPlayer = true;  

	} else {
		p.tag.isNotPlayer = false; 
		p.tag.speedTimer = 0;
		p.tag.isFinish = false;  
		p.tag.speedChnage = false; 
	  	p.sendUpdated();
	}
	//게임이 시작된 후 들어온 플레이어는 'race_end_point' 로케이션으로 이동시킨다 
	if(p.tag.isNotPlayer)
		p.spawnAtLocation("race_end_point"); 

	_players = App.players;
  });

App.onStart.Add(function(){
	startState(_state);

	//"speed_set_40", "speed_set_60", "speed_set_140", "speed_set_160" 으로 지정한 로케이션을 밟으면 
	//2초간 해당 로케이션 이름의 숫자와 같이 플레이어의 속도가 변한다. (2초 후 플레이어의 속도는 _playerBaseSpeed 가 된다 )
	//ex) "speed_set_40"를 밟으면 속도가 40으로 변함 
	let speed = [40,60,140,160]; 

	for(let i =0; i<speed.length; i++){
		App.addOnLocationTouched(`speed_set_${speed[i]}`, function(p){

			if(_state !== STATE_PLAYING)
				return; 

			p.moveSpeed = speed[i]; 
			p.tag.speedTimer = 0; 
			p.tag.speedChnage = true;

			//로케이션에 설정된 속도에 따라 "속도가 빨라졌습니다", "속도가 느려졌습니다" 등의 라벨이 해당 플레이어에게 띄워진다
			let str = '';
			if(i == 0)
				str = 'Speed greatly reduced';
			else if(i == 1)
				str = 'Speed reduced';
			else if(i == 2)
				str = 'Speed increased';
			else
				str = 'Speed greatly increased';

			p.showCenterLabel(`${str}`, 0xffffff, 0x000000, 120); 
			p.sendUpdated();	
		})
	}

	//"speed_set_random" 로케이션을 밟는 경우 플레이어의 속도가 40, 60, 140, 160 중 하나의 속도로 2초간 바뀐다 (2초 후 플레이어의 속도는 _playerBaseSpeed 가 된다 )
	App.addOnLocationTouched(`speed_set_random`, function(p){
		if(_state !== STATE_PLAYING)
			return; 

		p.moveSpeed = speed[Math.floor(Math.random() * 4)]; 
		//로케이션에 설정된 속도에 따라 "속도가 빨라졌습니다", "속도가 느려졌습니다" 등의 라벨이 해당 플레이어에게 띄워진다
		let str = '';
		if(p.moveSpeed == 40)
			str = 'Speed greatly reduced';
		else if(p.moveSpeed == 60)
			str = 'Speed reduced';
		else if(p.moveSpeed == 140)
			str = 'Speed increased';
		else
			str = 'Speed greatly increased';

		p.showCenterLabel(`${str}`, 0xffffff, 0x000000, 120); 
		p.tag.speedTimer = 0; 
		p.tag.speedChnage = true;
		p.sendUpdated();
	})

	//플레이어가 결승선을 통과하는 경우
	App.addOnLocationTouched("race_finish_point", function(p){
		if(p.tag.isFinish)
			return; 

		if(p.tag.isNotPlayer)
			return;

		_delayTimer = 0; 

		// 처음으로 플레이어가 결승선을 통과하는 경우 레이스 종료 카운트 다운을 시작한다(디폴트 30초)
		if(!_finishCountDown)
			_finishCountDown = true;

		// 플레이어가 결승선을 최초에 통과한 경우를 체크한다 
		p.tag.isFinish = true; 
		_rankList.push(p); 
		_rank++;
	})
});
 
App.onUpdate.Add(function(dt){
	_stateTimer += dt; 

	if(_start){
		for(let p of _players){
			if(p.tag.isNotPlayer)
				p.showCenterLabel("game is in progress, please wait..",0xffffff, 0x000000, 120);
		}
	}

	switch (_state) {
		case STATE_INIT:
		break;
		case STATE_INTRO: 
			if(_stateTimer >= 3){
				startState(STATE_READY);
			}
			break; 
		case STATE_READY:
			//출발선에서 _countDown 초(디폴트 10초) 간 레이스 카운트 다운을 시작한다 
			App.showCenterLabel(`${_countDown}  seconds later the race will start. `,0xffffff, 0x000000, 120); 

			if(_stateTimer >= 1){
				_stateTimer = 0; 
				_countDown --; 
			} 
			
			//카운트가 종료되면 (_countDown 가 0이 되면) 레이스가 시작된다  
			if(_countDown <= 0)
				startState(STATE_PLAYING); 
			break;
		case STATE_PLAYING:
			
			for(let p of _players){

				if(finishCheck() && _rankList.length == 0){
					p.showCenterLabel("There is no winner",0xffffff, 0x000000, 120);
					p.spawnAtLocation("race_end_point"); 
					startState(STATE_END);
				}
				// else if(finishCheck())
				// 	startState(STATE_JUDGE);
				
				if(p.tag.isNotPlayer)
					continue; 
				if(p.tag.speedChnage)
					p.tag.speedTimer += dt; 
				
				//어떤 속도 지정 타일을 밟아도 2초 후 기본 속도(_playerBaseSpeed)로 돌아온다 
				if(p.tag.speedTimer >= 2 && p.tag.speedChnage){
					p.moveSpeed = _playerBaseSpeed; 
					p.tag.speedChnage = false; 
					p.sendUpdated(); 
				}
			}
			
			if(_finishCountDown)
				{	
					_delayTimer += dt; 
					//플레이어가 결승선을 통과하면 1.5초간 플레이어의 이름과 레이스 카운트다운을 라벨로 나타낸다 
					if(_delayTimer <=1.5)
						App.showCenterLabel(`${_rankList[_rankList.length -1].name}  ${_rank - 1} place ! \n\n ${_finishCount} seconds later the race will end`,0xffffff, 0x000000, 120);
					else
						App.showCenterLabel(`${_finishCount} seconds later the race will end`,0xffffff, 0x000000, 120);

					_finishTimer += dt; 

					if(_finishTimer >= 1){
						_finishCount--; 
						_finishTimer = 0; 
					}
					// 레이스 카운트다운이 종료(0초)되었거나, 모든 플레이어가 결승선을 통과한 경우, 다음 단계로 넘어간다  
					if(_finishCount == 0 || finishCheck()){
						startState(STATE_JUDGE); 
					}
				}
			break; 
		case STATE_JUDGE:
			//5초간 우승자(1등)의 이름을 라벨로 나타낸다 
			if(_stateTimer <= 5){
				App.showCenterLabel(`- Winner - \n\n ${_rankList[0].name}`,0xffffff, 0x000000, 120);
			} 
			//5초간 "잠시 후 대기실로 이동합니다"라는 라벨을 띄웁다 
			else if(_stateTimer > 5 && _stateTimer <= 10 ){
				App.showCenterLabel("You will soon be transported to the waiting room",0xffffff, 0x000000, 120);
			}
			else {
				startState(STATE_END);
			}
			break;
		case STATE_END:
			
			break;
	}
});

ZEP Script FAQ

Q. ZEP Script로 캐릭터의 외양을 변경할 수 있나요?

Spritesheet를 로드한 후, 특정 이벤트 발생 시 Player에 적용시키면 캐릭터의 외양을 변경할 수 있습니다. 아래 예제 문서를 참고해주세요.

🧚 캐릭터 이미지 바꾸기

Q. ZEP Script 사용은 유료인가요?

ZEP Script를 사용해 앱을 개발하고 배포하는 과정은 무료입니다.

Q. ZEP Script로 유저에게 과금을 하는 것이 가능한가요?

ZEP Script에서 제공하는 결제 함수를 사용해 과금을 하는 것이 가능합니다.

자세한 내용은 API 문서를 참고해주세요,

Q. ZEP Script로 만든 앱을 유료로 판매할 수 있나요?

ZEP 에셋스토어에 앱을 업로드하고 판매할 수 있습니다.

Q. ZEP Script 로 만든 앱을 배포하지 않고 보다 쉽게 디버깅 할 수 있는 방법이 있나요?

보다 나은 개발 환경을 현재 준비 중에 있습니다.

Q. 다른 언어로도 ZEP Script를 개발할 수 있나요?

현재 ZEP Script 개발 환경은 Javascript 와 Typescript 언어로 제공하고 있습니다.

Javascript 개발 가이드 | Typescript 개발 가이드

Q. 저희의 자체 서버 API와 연동을 하고 싶습니다. 어떻게 할 수 있나요?

ZEP Script는 아래 방식을 통해서 Javascript 통신을 지원하고 있습니다. 자세한 내용은 아래 문서를 참고해주세요.

🛰️ 외부 API 통신하기


Event Listeners

소개

플레이어가 지정된 채팅을 입력하거나, 특정한 오브젝트를 공격할 와 같이 ZEP 스페이스에서 발생하는 특정 상황에 반응하여 동작하는 함수들 입니다.

EventListener가 비정상적으로 많이 추가되는 경우 앱이 종료될 수 있습니다.

onUpdate문 또는 반복적으로 실행되는 문에서 EventListener를 등록하는 것을 지양해주세요.

이름
설명

onSay

플레이어가 채팅을 입력할 때 동작하는 함수입니다.

onPlayerTouched

캐릭터들끼리 충돌할 때 동작하는 함수입니다.

onObjectTouched

캐릭터가 오브젝트와 충돌할 때 동작하는 함수입니다.

onAppObjectTouched

플레이어가 키 값을 가진 오브젝트와 충돌할 때 동작하는 함수입니다.

onUnitAttacked

공격 키(Z)로 다른 캐릭터를 공격할 때 동작하는 함수입니다.

onObjectAttacked

공격 키(Z)로 오브젝트를 공격할 때 동작하는 함수입니다.

onSidebarTouched

플레이어가 사이드바 앱을 클릭(터치)할 때 실행되는 함수입니다.

onTriggerObject

오브젝트와 F 상호작용 시 동작하는 함수입니다.

onAppObjectAttacked

플레이어가 키 값을 가진 오브젝트를 공격(Z키) 할 때 동작하는 함수입니다.

📚 API 설명 및 예제

Event Listener 함수 한 눈에 보기

// 플레이어들이 채팅창에 입력하는 모든 채팅에 대해 호출 되는 이벤트
// !로 시작하는 텍스트는 채팅창에 나오지 않으나, onSay 함수에는 사용 가능
App.onSay.Add(function(player, text) {
});

// 플레이어가 다른 플레이어와 부딪혔을 때 호출 되는 이벤트
App.onPlayerTouched.Add(function(sender, target, x, y){
});

// 플레이어가 오브젝트와 부딪혔을 때 호출 되는 이벤트
App.onObjectTouched.Add(function(sender, x, y, tileID, obj) {  
});

// 플레이어가 키 값을 가진 오브젝트와 부딪혔을 때 호출 되는 이벤트
App.onAppObjectTouched.Add(function(key, sender, x, y){});

// 플레이어가 다른 플레이어를 공격할 때 (Z키) 호출 되는 이벤트
App.onUnitAttacked.Add(function(sender, x, y, target) {
});

// 플레이어가 오브젝트를 공격(Z키)했을 때 호출 되는 이벤트
App.onObjectAttacked.Add(function(sender, x, y){
});

// 플레이어가 사이드바 앱을 클릭(터치)할 때 호출 되는 이벤트
App.onSidebarTouched.Add(function(player){
}); 

// 오브젝트와 F 상호작용 시 동작하는 함수
App.onTriggerObject.Add(function(player, layer, x, y){
});

// 플레이어가 키 값을 가진 오브젝트를 공격할 때 (Z키) 호출 되는 이벤트
App.onAppObjectAttacked.Add(function (sender, x, y, layer, key) {
});

onSay

App.onSay.Add(function(player, text){});

플레이어가 채팅을 입력할 때 동작합니다.

파라미터

이름
타입
설명

player

Player

player 파라미터는 채팅을 입력한 플레이어를 가르킴 player 파라미터의 이름은 임의로 변경 가능

text

String

text는 입력한 채팅 내용을 가르킴 text 파라미터의 이름은 임의로 변경 가능

예제

초성퀴즈 - 채팅으로 정답 맞추는 기능 만들어보기

_answer = "ZEP" // 정답
// 플레이어가 채팅을 칠 때 실행
App.onSay.add(function(player, text) {
    if(_answer == text){
        App.showCenterLabel(player.name + '님 정답!\n정답은 ' + _answer);
    }
});

onPlayerTouched

App.onPlayerTouched.Add(function(sender, target, x, y){});

캐릭터들끼리 충돌할 때 동작합니다.

파라미터

이름
타입
설명

sender

Player

sender는 부딪힌 주체자를 가르킴

target

String

target은 부딪힘을 당한 플레이어를 가르킴

x, y

Number

x, y는 충돌한 x, y 좌표를 가르킴

sender, target, x, y 파라미터의 이름은 임의로 변경 가능

예제

캐릭터끼리 부딪힐 때 메시지 출력해보기

// 플레이어끼리 부딪힐 때 실행
App.onPlayerTouched.Add(function (sender, target, x, y) {
	App.showCenterLabel(
		`${sender.name}님과 ${target.name}님이 좌표: (${x}, ${y}) 에서 부딪혔습니다.`
	);
});

onObjectTouched

App.onObjectTouched.Add(function(sender, x, y, tileID, obj){});

캐릭터가 오브젝트와 충돌 또는 상호작용 할 때 한 번 실행됩니다.

파라미터

이름
타입
설명

sender

Player

sender는 오브젝트와 부딪힌 플레이어를 가르킴

x, y

Number

오브젝트와충돌한 x, y 좌표를 가르킴

tileID

Number

오브젝트의 타일 ID 입니다.

obj

Object

오브젝트 객체

예제

⭐ overlap: true 속성이 없는 오브젝트는 충돌해도 함수가 실행되지 않습니다.

2KB
예제_onObjectTouched.zip
archive
let testObject = App.loadSpritesheet("object.png");
// 사용 가능한 ObjectEffectType
const ObjectEffectType = {
    NONE = 0,
    SHOW_NOTE = 1,
    SHOW_IMAGE = 2,
    PASSWORD_DOOR = 3,
    LINK_WEBSITE = 4,
    EMBED_WEBSITE = 5,
    API_CALL = 6,
    REPLACE_IMAGE = 7,
    NFT_GIVEAWAY = 8,
    NFT_DOOR = 9,
    POST_MESSAGE = 10,
    SHOW_CHAT_BALLOON = 11,
    FT_DOOR = 12,
    POST_MESSAGE_TO_APP = 13,
    DONATION_DOOR = 14,
    IMPASSABLE = 15,
    STAMP = 16,
    TOKEN_DONATION_DOOR = 17,
    CHANGE_OBJECT = 18,
    ANIMATION = 19,
    NFT_DOOR_MOVE = 20,
    INTERACTION_WITH_ZEPSCRIPTS = 21,
    MULTIPLE_CHOICE = 22,
}

App.onStart.Add(function () {
	Map.putObject(5, 5, testObject, { overlap: true });
});

// 플레이어와 오브젝트가 부딪힐 때 실행
App.onObjectTouched.Add(function (sender, x, y, tileID, obj) {
	Map.putObject(x, y, null);
	App.showCenterLabel(
		`${sender.name}님이 좌표: (${x}, ${y}) 에서 오브젝트와 부딪혔습니다.(타입: ${obj.type})`
	);
});

onAppObjectTouched

App.onAppObjectTouched.Add(function(sender, key, x, y){});

️ 캐릭터가 키 값을 가진 오브젝트와 충돌할 때 동작합니다.

파라미터

이름
타입
설명

sender

Player

sender는 오브젝트와 부딪힌 플레이어를 가르킴

key

String

오브젝트의 Key 값

x, y

Number

x, y는 충돌한 x, y 좌표를 가르킴

예제

라벨 출력 예제

⭐ overlap: true 속성이 없는 오브젝트는 충돌해도 함수가 실행되지 않습니다.

29KB
예제_onAppObjectTouched.zip
archive
let blueman_dance = App.loadSpritesheet(
	"blueman.png",
	48,
	64,
	[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37],
	8
);

// Q를 누르면 동작 하는 함수
App.addOnKeyDown(81, function (player) {
	App.sayToAll("키 값을 가진 오브젝트 충돌 테스트");
	Map.putObjectWithKey(8, 5, blueman_dance, { overlap: true, key: "blueman" });
});

App.onAppObjectTouched.Add(function (player, key, x, y) {
	App.sayToAll(
		`${player.name}이 키 값이 ${key}인 오브젝트와 ${x},${y}에서 충돌!`
	);
});

onUnitAttacked

App.onUnitAttacked.Add(function(sender, x, y, target){});

플레이어가 공격 키(Z)로 다른 캐릭터를 공격할 때 동작합니다.

파라미터

이름
타입
설명

sender

Player

sender는 공격한 플레이어를 가르킴

x, y

Number

x, y는 공격한 플레이어의 x, y 좌표 값을 가르킴

target

Player

target은 공격 받은 플레이어를 가르킴

sender, x, y, target 파라미터의 이름은 임의로 변경 가능

예제

플레이어를 공격 할 때 메시지 출력해보기

// Z 키로 플레이어를 공격할 때 실행
App.onUnitAttacked.Add(function (sender, x, y, target) {
	App.showCenterLabel(`${sender.name}님이 ${target.name}님을 공격했습니다.`);
	App.sayToAll(`(${x}, ${y})`);
});

onObjectAttacked

App.onObjectAttacked.Add(function(sender, x, y){});

플레이어가 공격 키(Z)로 오브젝트를 공격할 때 동작합니다.

파라미터

이름
타입
설명

sender

Player

sender는 공격한 플레이어를 가르킴

x, y

Number

x, y는 오브젝트의 x, y 좌표 값을 가르킴

sender, x, y 파라미터의 이름은 임의로 변경 가능

예제

오브젝트를 공격할 때 메시지 출력해보기

⭐ overlap: true 속성이 없는 오브젝트는 공격해도 함수가 실행되지 않습니다.

2KB
예제_onObjectAttacked.zip
archive
let testObject = App.loadSpritesheet("object.png");

App.onStart.Add(function () {
	Map.putObject(5, 5, testObject, { overlap: true });
});
// Z 키로 오브젝트를 공격할 때 실행
App.onObjectAttacked.Add(function(sender, x, y){
	App.showCenterLabel(
		`${sender.name}님이 좌표: (${x}, ${y})에 위치 오브젝트를 공격했습니다.`
	);
})

onSidebarTouched

App.onSidebarTouched.Add(function(player){});

플레이어가 사이드바 앱을 클릭(터치) 할 때 동작합니다.

파라미터

이름
타입
설명

player

Player

사이드바 앱을 클릭한 player를 가르킴

예제

사이드바 앱 클릭 시 채팅창 메시지 출력하기.

App.onSidebarTouched.Add(function (player) {
	App.sayToAll(`${player.name}님이 사이드바 앱을 클릭했습니다.`)
});

관련 페이지

사이드바 앱 예제

onTriggerObject

App.onTriggerObject.Add(function(player, layerID, x, y, key){});

오브젝트와 F 상호작용 시 동작하는 함수를 작성할 수 있습니다.

맵에디터로 설치한 오브젝트 또는 스크립트에서 type을 INTERACTION_WITH_ZEPSCRIPTS(21)로 설치한 오브젝트와 상호작용 시 동작합니다.

파라미터

이름
타입
설명

player

Player

오브젝트와 상호작용한 player를 가르킴

layerID

Number

오브젝트가 설치된 레이어 ID 오브젝트인 경우 layerID = 3 상단 오브젝트인 경우 layerID = 5

x, y

Number

상호작용한 오브젝트의 x, y 좌표

key

String

상호작용한 오브젝트의 key 값

예제

오브젝트와 F 상호작용시 메시지 출력하기

2KB
onTriggerObject.zip
archive
let blueman = App.loadSpritesheet("blueman.png");

App.onJoinPlayer.Add(function (player) {
	Map.putObjectWithKey(player.tileX + 1, player.tileY, blueman, {
		type: ObjectEffectType.INTERACTION_WITH_ZEPSCRIPTS,
		impassable: true,
		key: "objectKey",
	});
});


App.onTriggerObject.Add(function (player, layerID, x, y, key) {
	App.sayToAll(`playerName: ${player.name} / layer: ${layerID} / coordinates:(${x}, ${y}) / key: ${key}`);
});

onAppObjectAttacked

App.onAppObjectAttacked.Add(function (sender, x, y, layer, key) {});

플레이어가 공격 키(Z)로 키 값을 가진 오브젝트를 공격할 때 동작합니다.

관련 문서: 오브젝트 npcProperty

파라미터

이름
타입
설명

sender

Player

sender는 공격한 플레이어를 가르킴

x, y

Number

x, y는 오브젝트의 x, y 좌표 값을 가르킴

layer

Number

오브젝트가 설치된 레이어

key

String

공격한 오브젝트의 키 값

예제

키 값을 가진 오브젝트를 공격할 때 메시지 출력해보기

29KB
onAppObjectAttacked.zip
archive

⭐ collide: true 속성이 없는 키 값 오브젝트는 공격해도 함수가 실행되지 않습니다.

App.onAppObjectAttacked.Add(function (sender, x, y, layer, key) {
    App.showCenterLabel(
        `sender: ${sender.name} 
        coordinates: (${x}, ${y})
        layer: ${layer}
        key: ${key}`
    );
});

관련 페이지

사이드바 앱 예제

Text Button List

Text Button List 형 위젯은 텍스트와 버튼을 함께 표시할 수 있는 UI입니다.

활용 앱 ) 카메라 관전 프리미엄 기능

Image

Example

5KB
TextButtonList.zip
archive
function openWidget(p)
{
    //Check if player is mobile or not
    if(p.isMobile)
        //Set the widget's name, anchor position, width, and height.
        p.tag.widget = p.showWidget('widget.html','sidebar',300,550);
    else
        p.tag.widget = p.showWidget('widget.html','sidebar',280,520);

    //Send data to widget
    p.tag.widget.sendMessage({
        isMobile : p.isMobile,
    })
    
    //Register events for messages sent from widgets
    p.tag.widget.onMessage.Add(function(sender, msg) {
        switch (msg.type)
        {
            case 'close':
                if(p.tag.widget)
                {
                    p.tag.widget.destroy();
                    p.tag.widget = null;
                }
                
                break;
        }
    })
}

//Event when a player joins the app
App.onJoinPlayer.Add(function(p) {
    // Initialize tag properties of player
    p.tag = {};
});

// Action when the player clicks the sidebar app
App.onSidebarTouched.Add(function(p) {
    openWidget(p);
});

API Summary

ZEP-Script API 한눈에 보기

🔍Ctrl + F를 이용해 원하는 API를 찾아보세요!

═════════════════════

🕹️ ScriptApp

═════════════════════

♻️ Lifecycle

onInit

App.onInit.Add(function(){})

App이 최초로 시작될 때 한 번 호출됩니다.

onJoinPlayer

App.onJoinPlayer.Add(function(player){})

onInit이 호출된 후, 접속해 있는 모든 플레이어를 해당 이벤트를 통해 입장시키고, 이후 입장하는 플레이어가 있을 때 마다 동작합니다.

onStart

App.onStart.Add(function(){})

모든 플레이어가 onJoinPlayer를 통해 입장한 후 한 번 호출 됩니다.

onUpdate

App.onUpdate.Add(function(dt){})

약 20ms 마다 주기적으로 실행되는 함수입니다.

onLeavePlayer

App.onLeavePlayer.Add(function(player){})

퇴장하는 플레이어가 있을 때 마다 동작합니다. 이후, 다른 App이 실행되거나 설치한 Game Block이 파괴될 때 모든 플레이어를 이 함수를 통해 퇴장시킵니다.

onDestroy

App.onDestroy.Add(function(){})

다른 App이 실행되거나, 설치한 Game Block이 파괴될 때 동작합니다.

🗃️ Field

spaceHashID & mapHashID

App.spaceHashID: String App.mapHashID: String

앱이 설치된 스페이스의 spaceHashID와 mapHashID를 가져옵니다. (스페이스와 맵 이해하기)

creatorID

App.creatorID

App을 실행한 플레이어의 ID 값을 가져옵니다.

players

App.players: ScriptPlayer[]

맵에 있는 모든 플레이어 리스트를 배열로 가져옵니다.

playerCount

App.playerCount: Number

앱이 설치된 맵에 있는 플레이어의 수를 가져옵니다.

cameraEffect & cameraEffectParam1

App.cameraEffect: NONE = 0, SPOTLIGHT = 1 App.cameraEffectParam1: Number

App.cameraEffect: 카메라 이펙트의 종류를 셋팅할 변수 값

App.cameraEffectParam1: 카메라 이펙트 효과의 범위 값

displayRatio

App.displayRatio

화면의 줌을 컨트롤 하는 값 (기본 값: 1)

storage

App.storage: String

스페이스 내의 App 데이터의 저장공간 입니다. (스페이스 한정) Storage 페이지를 참고해주세요.

followPlayer

App.followPlayer: Boolean

App의 따라가기 기능 활성화 여부 값 입니다. (기본 값 : false)

showName

App.showName: Boolean

플레이어의닉네임 숨김여부 값 입니다. (기본 값: true)

appHashID

App.appHashID: String

앱의 HashID를 가져옵니다.

️💾 Storage

setStorage, getStorage

App.setStorage(string) / App.getStorage(function())

App.setStorage 함수로 App storage를 저장하고, 앱을 사용중인 다른 맵의 App storage 변경사항이 있을 경우 App.getStorage 함수로 App storage를 동기화 할 수 있습니다.

App.getStorage 함수는 비동기 함수이기 때문에 App.getStorage 함수 다음 라인에 App.storage를 사용하는 코드를 작성할 경우 동기화를 보장할 수 없습니다.

🛰️ EventListeners

onSay

App.onSay.Add(function(player, text){});

플레이어가 채팅을 입력할 때 동작합니다.

onPlayerTouched

App.onPlayerTouched.Add(function(sender, target, x, y){});

캐릭터들끼리 충돌할 때 동작합니다.

onObjectTouched

App.onObjectTouched.Add(function(sender, x, y, tileID, obj){});

캐릭터가 오브젝트와 충돌할 때 동작합니다.

onAppObjectTouched

App.onAppObjectTouched.Add(function(sender, key, x, y ){});

️ 캐릭터가 키 값을 가진 오브젝트와 충돌할 때 동작합니다.

onUnitAttacked

App.onUnitAttacked.Add(function(sender, x, y, target){});

플레이어가 공격 키(Z)로 다른 캐릭터를 공격할 때 동작합니다.

onObjectAttacked

App.onObjectAttacked.Add(function(sender, x, y){});

플레이어가 공격 키(Z)로 오브젝트를 공격할 때 동작합니다.

onSidebarTouched

App.onSidebarTouched.Add(function(player){});

플레이어가 사이드바 앱을 클릭(터치) 할 때 동작합니다.

onTriggerObject

App.onTriggerObject.Add(function(player, layerID, x, y, key){});

오브젝트와 F 상호작용 시 동작하는 함수입니다.

onAppObjectAttacked

App.onAppObjectAttacked.Add(function (sender, x, y, layer, key) {});

플레이어가 공격 키(Z)로 키 값을 가진 오브젝트를 공격할 때 동작합니다.

☎️ Callbacks

runLater

App.runLater(function(){}, time: number);

time(초) 후에 callback 함수를 실행합니다.

addOnTileTouched

App.addOnTileTouched(x:number, y: number, function(player){})

지정한 x, y좌표에 플레이어가 도착할 경우 callback 함수를 실행합니다.

addOnLocationTouched

App.addOnLocationTouched(name: string, function(player){})

플레이어가 맵에디터에서 지정한 ‘지정영역’에 도착했을 때 callback 함수를 실행합니다.

addOnKeyDown

App.addOnKeyDown(keycode : number, function(player){});

플레이어가 지정된 키를 눌렀을 때 callback 함수를 실행합니다.

setTimeout

setTimeout(function(){}, time: number);

time(ms) 후에 callback 함수를 실행합니다.

setInterval

setInterval(function(){}, time: number);

time(ms) 간격으로 callback 함수를 실행합니다.

addMobileButton

App.addMobileButton( anchor: number, posX: number, posY: number, function(player){} )

모바일 환경에서 커스텀 모바일 버튼을 추가하고, 버튼을 눌렀을 때 동작하는 함수를 지정합니다.

putMobilePunch

App.putMobilePunch(enable: boolean = true)

enable이 true이면 모바일 환경에서 펀치 버튼이 추가됩니다.

putMobilePunchWithIcon

App.putMobilePunchWithIcon(icon: ScriptDynamicResource)

로드한 이미지로 펀치 버튼을 만들어 추가합니다.

💠 Methods

loadSpritesheet

App.loadSpritesheet(fileName: string, frameWidth: number, frameHeight: number, anims: array, frameRate: number): ScriptDynamicResource

스프라이트 시트 그림 파일을 읽어 객체화하는 함수입니다.

showCenterLabel

App.showCenterLabel(text: string, color: uint = 0xFFFFFF, bgColor: uint = 0x000000, offset: number = 0, time: number = 3000)

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

showCustomLabel

App.showCustomLabel(text: string, color: number = 0xFFFFFF, bgColor: number = 0x000000, offset: number = 0, width = 100, opacity = 0.6, time: number = 3000);

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

text 부분에 span 태그를 넣어 텍스트를 꾸밀 수 있습니다.

sayToAll

App.sayToAll(text: string, color: uint = 0xFFFFFF)

채팅창에 text 내용을 출력합니다.

sayToStaffs

App.sayToStaffs(text: string, color: uint = 0xFFFFFF)

Staff이상 권한의 유저 채팅창에 text 내용을 출력합니다.

showWidget

App.showWidget(fileName: string, align: string, width: number, height: number): ScriptWidget

모든 플레이어에게 지정된 align의 위치에 해당 html파일을 위젯으로 불러오는 함수입니다.

showYoutubeWidget

App.showYoutubeWidget(link: string, align: string, width: number, height: number): ScriptWidget

링크에 해당하는 Youtube 컨텐츠를 위젯으로 불러옵니다.

spawnPlayer

App.spawnPlayer(playeID: string, tileX: number, tileY: number)

playerID 에 해당하는 플레이어를 tileX, tileY 좌표로 이동시키는 함수입니다.

kickPlayer

App.kickPlayer(playerID: string)

playerID 에 해당하는 플레이어를 추방하는 함수입니다.

forceDestroy

App.forceDestroy();

미니게임 앱을 강제 종료하는 함수입니다.

clearChat

App.clearChat();

모든 채팅 내용을 삭제하는 함수입니다.

getPlayerByID

App.getPlayerByID(playerID: string);

id 에 해당하는 플레이어를 반환하는 함수입니다.

playSound

App.playSound(fileName: string, loop: boolean = false, overlap: boolean = false)

모든 플레이어에게 사운드를 재생하는 함수입니다.

playSoundLink

App.playSoundLink(link: string, loop: boolean = false)

모든 플레이어에게 사운드를 재생하는 함수입니다.

stopSound

App.stopSound();

재생되고 있는 사운드를 멈추는 함수입니다.

httpGet

App.httpGet(url: string, headers: object, function(res: string){})

http get 요청을 보내는 함수입니다.

httpPost

App.httpPost(url: string, headers: object, body: object, function(res: string))

Form-Data 형태의 http post 요청을 보내는 함수 입니다.

httpPostJson

App.httpPostJson(url: string, headers: object, body: object, function(res: string))

Json 형태의 http post 요청을 보내는 함수 입니다.

sendUpdated

App.sendUpdated()

App 관련 필드 값이 변경되면 변경 값을 적용하는 함수입니다.

changeAttackSound

App.changeAttackSound(fileName:string)

찌르기(Z키) 공격 효과음을 변경하는 함수입니다.

═════════════════════

🗺️ ScriptMap

═════════════════════

🗃️ Field

width & height

Map.width : Number Map.height : Number

맵의 너비와 높이 값을 가져옵니다.

💠 Methods

putTileEffect

Map.putTileEffect(x: number, y: number, tileID: TileEffectType)

지정한 좌표에 타일 효과를 적용하는 함수입니다.

putObject

Map.putObject(x: number, y: number, dynamicResource: ScriptDynamicResource, option: JsValue)

지정한 좌표에 오브젝트를 놓는 함수입니다. (기준 좌표: Left Top) → 기준 좌표란?

putObjectMultiple

Map.putObjectMultiple(tileArray: array, type: PutObjectType, dynamicResource: ScriptDynamicResource);

오브젝트를 배치할 좌표들을 2차원 배열로 입력하여 한 번에 오브젝트를 설치하는 기능입니다. 이 기능을 사용하면 한 번에 많은 오브젝트를 설치할 경우 부하를 줄이는 효과를 얻을 수 있습니다.

putObjectWithKey

Map.putObjectWithKey(x: number, y: number, dynamicResource: ScriptDynamicResource, option: JsValue)

지정한 좌표에 키 값을 가진 오브젝트를 놓는 함수입니다. (기준 좌표: Left Top)

getObjectWithKey

Map.getObjectWithKey(key: String)

해당 키 값을 가지고 있는 오브젝트의 정보를 가져옵니다.

playObjectAnimation

Map.playObjectAnimation(x: number, y: number, name: string)

해당 좌표에 있는 오브젝트의 스프라이트 애니메이션을 실행시키는 함수입니다.

해당 좌표 지점에 Map.putObject 함수가 선행되어야합니다.

playObjectAnimationWithKey

Map.playObjectAnimation(key: string, animName: string, repeatCount: number)

key 값이 일치하는 오브젝트의 스프라이트 애니메이션을 실행시키는 함수입니다.

moveObject

Map.moveObject(x: number, y: number, targetX: number, targetY: number, time: number)

x, y 좌표에 위치한 오브젝트를 targetX, targetY로 time 초 동안 움직이는 함수입니다.

해당 좌표 지점에 Map.putObject 함수가 선행되어야합니다.

moveObjectWithKey

Map.moveObjectWithKey(key: string, targetX: number, targetY: number, path:boolean = true)

key 값을 가진 오브젝트를 targetX, targetY로 움직이는 함수입니다.

clearAllObjects()

Map.clearAllObjects()

ZEP 스크립트로 생성된 모든 오브젝트를 제거하는 함수입니다.

getTile

Map.getTile(layer: number, x: number, y: number): number

해당하는 레이어의 x, y 좌표에 있는 타일의 타입 값을 리턴, 타일이 없으면 -1을 리턴합니다.

getLocation

Map.getLocation(locationName: string)

파라미터로 전달한 로케이션이 존재하는 경우, 로케이션 설치 좌표를 리턴합니다.

getLocationRandom

Map.getLocationRandom(locationName: string)

파라미터로 전달한 로케이션이 2개이상 존재하는 경우, 무작위로 선택하여 로케이션 설치 좌표를 리턴합니다.

hasLocation

Map.hasLocation(locationName: String): Boolean

맵에 해당 로케이션이 있는지 체크하여 true/false 값을 리턴합니다.

getObjectsByType

Map.getObjectsByType(type: numer) : array

Type에 해당하는 오브젝트들을 리턴하는 함수입니다.

getTopObjectsByType

Map.getTopObjectsByType(type: numer) : array

Type에 해당하는 상단오브젝트들을 리턴하는 함수입니다.

sayObjectWithKey

Map.sayObjectWithKey( key: string, message: string )

key 값을 가진 오브젝트 위에 말풍선을 표시하는 함수입니다.

═════════════════════

👥 ScriptPlayer

═════════════════════

🗃️ Field

id , name

player.id : Number player.name : String

플레이어의 id, 닉네임 값을 가져옵니다.

title

player.title : String

title은 캐릭터 닉네임 위에 노란색으로 노출되는 텍스트입니다.

role

player.role : Number

role은 플레이어의 권한을 나타내는 숫자 값 입니다. 플레이어의 role에 따라 다음과 같은 값을 출력합니다.

게스트

-1

스태프

2000

멤

0

관리자

3000

에디터

1000

맵소유자

3001

tileX, tileY

player.tileX: Number player.tileY: Number

플레이어의 캐릭터가 서있는 X 좌표 값과 Y 좌표 값입니다.

dir

player.dir : Number

플레이어의 캐릭터가 바라보고 있는 방향입니다.

캐릭터가 바라보고 있는 방향에 따라 다음과 같은 값을 출력합니다.

캐릭터가 바라보는 방향에 따른 dir 값

moveSpeed

player.moveSpeed : Number

플레이어의 이동속도 값입니다. (기본 값: 80)

sprite

player.sprite : ScriptDynamicResource

플레이어 캐릭터의 스프라이트 이미지입니다. (null 입력 시 기본 아바타 이미지로 초기화)

tag

player.tag: Any

tag를 사용해 플레이어에게 필요한 속성 값을 부여 할 수 있습니다.

hidden

player.hidden: Boolean

hidden 값이 true 이면, 해당 플레이어는 다른 플레이어에게 보이지 않습니다.

spotlight

player.spotlight: Boolean

플레이어의 스팟 라이트 기능 활성화 여부입니다.

attackType

player.attackType : Number

플레이어의 공격(Z키) 타입입니다. (기본: 0)

attackParam1

player.attackParam1: Number

공격(Z키) 이미지가 날아가는 거리 속성입니다. 공격 가능 거리는 늘어나지 않습니다.

attackParam2

player.attackParam2: Number

공격 가능 거리 속성입니다. attackType이 원거리 공격으로 설정 된 경우에만 유효합니다.

attackSprite

player.attackSprite : ScriptDynamicResource

공격(Z키) 이미지 속성입니다.

walletAddress

player.walletAddress : String

플레이어의 전자지갑 주소입니다.

storage

player.storage : String

스페이스 내의 Player 값 저장 공간 입니다 (스페이스 한정)

isMobile

player.isMobile : Boolean

플레이어의 모바일 접속 여부를 true/false 로 출력합니다.

isMoving

player.isMoving : Boolean

플레이어가 움직이고 있으면 True, 아니면 False를 반환합니다.

isJumping

player.isJumping : Boolean

플레이어가 점프하고 있으면 True, 아니면 False를 반환합니다.

customData

player.customData : String

URL 쿼리스트링으로 전달 받은 값을 저장하는 필드입니다. 🔥 URL 쿼리스트링 활용하기

displayRatio

player.displayRatio: number

플레이어화면의 줌을 컨트롤 하는 값 ( 기본 값: 1 )

titleColor

player.titleColor: number

플레이어의 타이틀 색상을 읽거나 수정 할 수 있습니다.

emailHash

player.emailHash

플레이어의 이메일 Hash 값을 가져옵니다.

isGuest

player.isGuest

비로그인 플레이어인 경우 true 값을 가집니다.

away

player.away

5분 이상 비활성화된 유저인 경우 true값을 가집니다.

💠 Methods

showCenterLabel

player.showCenterLabel(text: string, color: uint = 0xFFFFFF, bgColor: uint = 0x000000, offset: number = 0, time: number = 3000)

해당 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

showCustomLabel

player.showCustomLabel(text: string, color: number = 0xFFFFFF, bgColor: number = 0x000000, offset: number = 0, width = 100, opacity = 0.6, time: number = 3000);

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

text 부분에 span 태그를 넣어 텍스트를 꾸밀 수 있습니다.

showWidget

player.showWidget(fileName: string, align: string, width: number, height: number): ScriptWidget

해당 플레이어에게 지정된 align의 위치에 해당 html파일을 위젯으로 불러오는 함수입니다.

showBuyAlert

player.showBuyAlert(itemName: string, price: number, callback: function, payToSpaceOwner: boolean = false)

플레이어에게 구매 위젯을 표시하고, 구매 후 동작하는 콜백함수를 작성할 수 있습니다. payToSpaceOwner 옵션이 false인 경우 앱 소유자에게 수익이 전달되며, true인 경우에는 앱이 설치된 맵의 소유자에게 수익이 전달됩니다.

hideBuyAlert

player.hideBuyAlert()

플레이어의 구매 위젯을 닫습니다.

sendMessage

player.sendMessage(text: string, color: uint = 0xFFFFFF)

유저 개인에게 채팅 메시지를 보내는 함수입니다.

showPrompt

player.showPrompt(text: string, function(inputText), option = {})

플레이어에게 입력창을 보여주고, 플레이어의 응답에 따라 동작하는 callback 함수를 작성할 수 있습니다.

showConfirm

player.showConfirm(text: string, function(result), option = {})

플레이어에게 확인창을 보여주고, 플레이어가 OK를 눌렀을 때 동작하는 callback 함수를 작성할 수 있습니다. cancel을 누를 경우에는 callback 함수가 동작하지 않습니다.

showAlert

player.showAlert(text: string, function(), option = {})

플레이어에게 경고창을 보여주고, 플레이어가 OK를 눌렀을 때 동작하는 callback 함수를 작성할 수 있습니다.

showWidgetResponsive

player.showWidgetResponsive(fileName:string, marginTop:number, marginRight:number, marginBottom:number, marginLeft:number)

위젯의 상/하/좌/우 여백을 화면 크기에 대한 %비율로 정의 하여 위젯을 표시합니다.

화면의 크기가 여백을 포함한 위젯 영역보다 작아질 경우, 위젯의 크기가 비례하여 작아집니다

openWebLink

player.openWebLink(url:string, popup:boolean=false)

플레이어에게 웹 URL을 새 창이나 팝업 창으로 표시합니다.

showEmbed

player.showWidget(url: string, align: string, width: number, height: number, hasBackdrop: boolean = true)

해당 플레이어에게 지정된 align의 위치에 url 임베드 화면을 표시하는 함수입니다.

showImageModal

player.showImage(url: string)

플레이어에게 입력한 이미지 주소에 해당하는 이미지를 표시합니다.

showNoteModal

player.showNoteModal(text: string)

플레이어에게 텍스트 창을 보여주는 함수입니다.

isEmail

player.isEmail(email: string): boolean

해당 플레이어의 이메일이 파라미터 값과 같다면 true, 아니면 false를 리턴합니다.

getLocationName

player.getLocationName : string

플레이어가 서있는 지정영역의 이름을 출력합니다.

spawnAt

player.spawnAt(tileX: int ,tileY: int, dir: int = 0)

플레이어의 캐릭터를 tileX, tileY 좌표로 지정한 방향을 바라보게 이동시킵니다.

spawnAtLocation

player.spawnAtLocation(name: string, dir:int = 0)

플레이어의 캐릭터를 name에 해당하는 지정 영역으로 지정한 방향을 바라보게 이동시킵니다.

spawnAtMap

player.spawnAtMap(spaceHashID string, mapHashID:string = null)

플레이어를 해당 스페이스 해당 맵으로 이동시킵니다.

setCameraTarget

[1] player.setCameraTarget( tileX: Number, tileY: Number, time: Number )

[2] player.setCameraTarget( key: String, time: Number )

[1] 플레이어의 시점을 지정된 좌표로 중심 이동시킵니다.

[2] 플레이어의 시점을 특정 오브젝트로 중심 이동시킵니다.

setEffectSprite

player.setEffectSprite(resource: ScriptDynamicResource, offsetX: Number, offsetY: Number, type: Number)

플레이어의 배경 또는 전경 이미지를 설정 할 수 있습니다.

playSound

Player.playSound(fileName: string, loop: boolean = false, overlap: boolean = false)

해당 플레이어에게 사운드를 재생하는 함수입니다.

playSoundLink

player.playSoundLink(link: string, loop: boolean = false)

모든 플레이어에게 사운드를 재생하는 함수입니다.

sendUpdated

player.sendUpdated()

App, Player 관련 필드 값이 변경되면 변경 값을 적용하는 함수입니다.

save

player.save()

App, Player storage 값이 변경되면 변경 값을 적용하는 함수입니다.

═════════════════════

🧙‍♂️ ScriptWidget

═════════════════════

🗃️ Field

id

widget.id

위젯의 아이디 값을 가져옵니다.

🛰️ EventListeners

onMessage

widget.onMessage.Add(function(player, data: any){});

위젯에서 App으로 메시지를 보내면 callback 함수가 동작합니다.

💠 Methods

sendMessage

widget.sendMessage(object: any)

App에서 위젯으로 데이터를 보냅니다.

destroy

widget.destroy()

위젯을 삭제하는 함수입니다.

═════════════════════

🔩 Utility Class

═════════════════════

⏳ Time

getTime

Time.getTime()

ZEP 서버의 현재 시간을 milliseconds 단위의 값으로 리턴합니다.

getUtcTime

Time.getUtcTime()

현재 UTC 시간을 milliseconds 단위의 값으로 리턴합니다.

getTimeInterval

Time.getTimeInterval(timeA: number, timeB: number, returnType: DateType)

timeB - timeA를 계산하고, 그 결과를 지정한 returnType으로 반환합니다.

Methods

소개

화면에 UI를 표시, 유저 이동 또는 강퇴, 사운드 재생 등 편리한 기능을 제공하는 함수들 입니다.

Methods 함수는 용도에 따라 UI , User Control, Sound, 통신, 공통 메소드로 나눌 수 있습니다.

UI

이름
설명

loadSpritesheet

스프라이트 시트 그림 파일을 읽어 객체화하는 함수입니다.

showCenterLabel

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

showCustomLabel

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다. text 부분에 span 태그를 넣어 텍스트를 꾸밀 수 있습니다.

sayToAll

모든 플레이어의 채팅창에 text 를 출력하는 함수입니다.

sayToStaffs

Staff 권한 이상의 플레이어의 채팅창에 text 를 출력하는 함수입니다.

showWidget

모든플레이어에게 지정된 align의 위치에 html파일을 위젯으로 불러오는 함수입니다.

showYoutubeWidget

링크에 해당하는 Youtube 컨텐츠를 위젯으로 불러옵니다.

Control

이름
설명

spawnPlayer

플레이어를 지정한 x, y 좌표로 이동 시키는 함수입니다.

kickPlayer

플레이어를 추방하는 함수입니다.

forceDestroy

미니게임 앱을 강제 종료하는 함수입니다.

clearChat

모든 채팅 내용을 삭제하는 함수입니다.

getPlayerByID

id 에 해당하는 플레이어를 반환하는 함수입니다.

Sound

이름
설명

playSound

사운드 파일을 재생하는 함수입니다.

playSoundLink

사운드 URL을 재생하는 함수입니다.

stopSound

재생 중인 모든 사운드를 멈추는 함수입니다.

통신

이름
설명

httpGet

http get 요청을 보내는 함수입니다.

httpPost

Form-Data 형태의 http post 요청을 보내는 함수 입니다.

httpPostJson

JSON 형태의 http post 요청을 보내는 함수입니다.

공통

이름
설명

sendUpdated

App, Player 관련 필드 값이 변경되면 변경 값을 적용하는 함수입니다.

📚 API 설명 및 예제

🎨 UI Methods

UI 함수 한 눈에 보기

// 스프라이트 시트 그림 파일을 읽어 객체화
App.loadSpritesheet(fileName: string, frameWidth: number, frameHeight: number, anims: array, frameRate: number): ScriptDynamicResource

// 모든 플레이어에게 지정된 위치에 해당 text를 1초간 표시
App.showCenterLabel(text: string, color: uint = 0xFFFFFF, bgColor: uint = 0x000000, offset: int = 0, time: int = 3000)

// 모든 플레이어에게 지정된 위치에 해당 text를 3초간 표시, 커스터마이징 가능
App.showCustomLabel(text: string, color: number = 0xFFFFFF, bgColor: number = 0x000000, offset: number = 0, width = 100, opacity = 0.6, time: int = 3000);

// 채팅창에 해당 text내용을 출력
App.sayToAll(text: string, color: uint = 0xFFFFFF)

// 모든 플레이어에게 지정된 align의 위치에 해당 html파일을 위젯으로 불러오기
App.showWidget(fileName: string, align: string, width: number, height: number): ScriptWidget

// 모든 플레이어에게 지정된 align의 위치에 해당 YouTube link의 동영상을 재생시킴
App.showYoutubeWidget(link: string, align: string, width: number, height: number): ScriptWidget

loadSpritesheet

App.loadSpritesheet(fileName: string, frameWidth: number, frameHeight: number, anims: array, frameRate: number): ScriptDynamicResource

스프라이트 시트 그림 파일을 읽어 객체화하는 함수입니다.

ScriptDynamicResource에 대한 이해를 돕는 스프라이트시트 이해하기 문서를 확인해보세요!

파라미터

이름
타입
설명

fileName

String

불러올 파일의 이름

frameWidth frameHeight

number

한 프레임의 가로, 세로 픽셀 크기

anims

Array

애니메이션으로 지정할 frame 이미지 번호 배열

frameRate

number

프레임 하나 당 데이터를 표시하는 속도 frameRate: 8 → 1초에 8개의 이미지를 보여줌

예제

페인트맨 - 블루맨 스프라이트 이미지 적용해보기

29KB
예제_loadSpritesheet.zip
archive
// 한 프레임의 사이즈 48x64
let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], // 좌방향 이동 이미지
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
		dance: [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],
		down_jump: [38],
		left_jump: [39],
		right_jump: [40],
		up_jump: [41],
}, 8);
// 플레이어가 입장하면 캐릭터 이미지가 바뀜
App.onJoinPlayer.Add(function(player){
	player.sprite = blueman;
	player.sendUpdated();
});

showCenterLabel

App.showCenterLabel(text: string, color: uint = 0xFFFFFF, bgColor: uint = 0x000000, offset: number = 0, time: number = 3000)

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

파라미터

이름
타입
설명

text

String

라벨에 출력할 텍스트

color

Uint

출력할 글씨의 색 (HexCode) 값을 입력하지 않을 경우, 흰색(0xFFFFFF)으로 적용됩니다. ➡️

bgColor

Uint

메시지가 출력되는 라벨의 배경색 값을 입력하지 않을 경우, 검은색(0x000000)으로 적용됩니다.

offset

number

offset 값이 클수록 표시되는 위치가 화면 아래쪽 방향으로 가까워집니다. 값을 입력하지 않을 경우, 0으로 지정됩니다.

time

number

라벨 표시 시간 (ms), 기본 값 3000 ( 3초 )

예제

노란색 배경 입장 메시지 라벨을 2초간 출력해보기

App.onJoinPlayer.Add(function(player){
	App.showCenterLabel(`${player.name}님이 입장하셨습니다.`, 0x000000, 0xFFFF00, 200, 2000); // 노란색 배경, 검정색 글씨로 표시하기
});

showCustomLabel

App.showCustomLabel(text: string, color: number = 0xFFFFFF, bgColor: number = 0x000000, offset: number = 0, width = 100, opacity = 0.6, time: number = 3000, option: object = null);

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

text 부분에 span 태그를 넣어 텍스트를 꾸밀 수 있습니다.

파라미터

이름
타입
설명

text

String

라벨에 출력할 텍스트 ( span 태그 허용 )

color

Uint

출력할 글씨의 색 (HexCode) 값을 입력하지 않을 경우, 흰색(0xFFFFFF)으로 적용됩니다.

bgColor

Uint

메시지가 출력되는 라벨의 배경색 값을 입력하지 않을 경우, 검은색(0x000000)으로 적용됩니다. ➡️

offset

number

offset 값이 클수록 표시되는 위치가 화면 아래쪽 방향으로 가까워집니다. 값을 입력하지 않을 경우, 0으로 지정됩니다.

width

number

라벨의 너비를 n%로 설정하는 값 입니다. (기본 값 100)

opacity

number

라벨의 배경 투명도를 설정하는 값 입니다. (기본 값 0.6, 범위 0 ~ 1)

time

number

라벨 표시 시간 (ms), 기본 값 3000 ( 3초 )

옵션

이름
타입
설명

key

String

라벨에 키 값을 할당하여, 서로 다른 키 값을 가진 라벨은 동시에 표시할 수 있습니다.

borderRadius

String

라벨의 모서리에 둥글기를 설정할 수 있습니다. ex) borderRadius: "8px"

fontOpacity

boolean

false로 설정시 폰트에 투명도가 적용되지 않습니다.

padding

String

라벨 내부에 padding 값을 지정 할 수 있습니다. ex) padding: "8px"

예제

커스텀 라벨 예제 코드 페이지를 참고해주세요

sayToAll

App.sayToAll(text: string, color: uint = 0xFFFFFF)

채팅창에 text 내용을 출력합니다.

파라미터

이름
타입
설명

text

String

채팅창에 출력할 텍스트

color

Uint

출력할 글씨의 색을 지정합니다. (HexCode) 값을 입력하지 않을 경우, 흰색(0xFFFFFF)으로 적용됩니다. ➡️

예제

입장메시지를 하늘색으로 출력해보기

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	App.sayToAll(`[info]${player.name} has entered.`, 0x00ffff); // 하늘색으로 표시하기
});

sayToStaffs

App.sayToStaffs(text: string, color: uint = 0xFFFFFF)

Staff이상 권한의 유저 채팅창에 text 내용을 출력합니다.

파라미터

이름
타입
설명

text

String

채팅창에 출력할 텍스트

color

Uint

출력할 글씨의 색을 지정합니다. (HexCode) 값을 입력하지 않을 경우, 흰색(0xFFFFFF)으로 적용됩니다. ➡️

예제

입장메시지를 하늘색으로 출력해보기

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	App.sayToStaffs(`[Staff] ${player.name} has entered.`, 0x00ffff); // 하늘색으로 표시하기
});

showWidget

App.showWidget(fileName: string, align: string, width: number, height: number): ScriptWidget

모든 플레이어에게 지정된 align의 위치에 해당 html파일을 위젯으로 불러오는 함수입니다.

파라미터

이름
타입
설명

fileName

String

불러올 파일의 이름

align

String

위젯을 표시할 위치 ’popup’, ‘sidebar’, ‘top’, ‘topleft’, ‘topright’, ‘middle’, ‘middleleft’, ‘middleright’, ‘bottom’, ‘bottomleft’, ‘bottomright’

width height

number

위젯을 표시할 영역의 가로, 세로 크기(px)

예제

초성퀴즈 위젯 따라해보기

1KB
예제_showWidget.zip
archive

let _widget = null;
// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	_widget = App.showWidget("widget.html", "top", 200, 300); // 화면 상단, 200x300 영역에 위젯을 보여줌
	_widget.sendMessage({
		timer: 15,
		answer: "ㅅㅍㅋ",
	});
});

showYoutubeWidget

App.showYoutubeWidget(link: string, align: string, width: number, height: number): ScriptWidget

링크에 해당하는 Youtube 컨텐츠를 위젯으로 불러옵니다.

파라미터

이름
타입
설명

link

String

Youtube 컨텐츠 url

align

String

위젯을 표시할 위치 ’popup’, ‘sidebar’, ‘top’, ‘topleft’, ‘topright’, ‘middle’, ‘middleleft’, ‘middleright’, ‘bottom’, ‘bottomleft’, ‘bottomright’

width height

number

위젯을 표시할 영역의 가로, 세로 크기(px)

예제

Youtube 위젯 표시해보기

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	App.showYoutubeWidget("https://www.youtube.com/watch?v=SXnMGIR8cjY","top",600,500);
});

🔗Control Methods

Control 함수 한 눈에 보기

// playerID 에 해당하는 플레이어를 tileX, tileY 좌표로 이동시킨다.
App.spawnPlayer(playeID: string, tileX: number, tileY: number)

// playerID 에 해당하는 플레이어를 추방한다.
// 추방당한 유저는 24시간 동안 해당 스페이스에 접근하지 못하게 된다.
App.kickPlayer(playeID: string)

// 미니게임 앱을 강제종료시킵니다.
App.forceDestroy();

// 모든 채팅내용을 삭제합니다.
App.clearChat();

//id 에 해당하는 플레이어를 반환하는 함수입니다.
App.getPlayerID(playerID:string);

spawnPlayer

App.spawnPlayer(playeID: string, tileX: number, tileY: number)

playerID 에 해당하는 플레이어를 tileX, tileY 좌표로 이동시키는 함수입니다.

파라미터

이름
타입
설명

playerID

String

플레이어의 ID 값

tileX tileY

number

플레이어를 이동시킬 x, y 좌표 값

예제

입장하는 플레이어를 지정 좌표로 이동시키기

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	App.spawnPlayer(player.id, 5, 5); // 플레이어를 5,5 위치로 이동시키기
});

kickPlayer

App.kickPlayer(playerID: string)

playerID 에 해당하는 플레이어를 추방하는 함수입니다.

파라미터

이름
타입
설명

playerID

String

플레이어의 ID 값

예제

강퇴 명령어 만들어보기

⛔ 강퇴 당한 참가자는 24시간 동안 해당 스페이스에 접속하지 못합니다.

// 플레이어가 채팅을 입력할 때 실행
// 명령어 형식 '!강퇴 닉네임'
App.onSay.Add(function (player, text) {
	let players = App.players;
	if (text.indexOf("!강퇴 ") == 0) {
		let nickname = text.slice(4);
		for (let i in players) {
			let p = players[i];
			if (p.name == nickname) {
				App.kickPlayer(p.id);
				break;
			}
		}
	}
});

forceDestroy

App.forceDestroy();

미니게임 앱을 강제 종료하는 함수입니다.

예제

미니게임 앱을 강제 종료시키기

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	App.forceDestroy();
});

clearChat

App.clearChat();

모든 채팅 내용을 삭제하는 함수입니다.

예제

Q를 눌러 채팅 내용 지우기

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	App.clearChat();
});
채팅이 삭제되는 모습

getPlayerByID

App.getPlayerByID(playerID: string);

id 에 해당하는 플레이어를 반환하는 함수입니다.

예제

App.getPlayerByID 사용 방법

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	const myPlayer = App.getPlayerByID(player.id);
});

🔉 Sound Methods

Sound 함수 한 눈에 보기

// 모든 플레이어에게 사운드를 재생
App.playSound(fileName: string, loop: boolean = false, overlap: boolean = false)

// 모든 플레이어에게 링크에 해당하는 사운드를 재생
App.playSoundLink(link: string, loop: boolean = false)

// 모든 재생되는 사운드를 멈춤
App.stopSound()

// 찌르기(Z키) 공격 효과음을 변경하는 함수입니다.
App.changeAttackSound(fileName:string)

playSound

App.playSound(fileName: string, loop: boolean = false, overlap: boolean = false)

모든 플레이어에게 사운드를 재생하는 함수입니다.

파라미터

이름
타입
설명

fileName

String

불러올 파일의 이름

loop

boolean

true: 반복 재생 false: 1회 재생

overlap

boolean

사운드 오버랩(겹침) 재생 가능 여부

예제

플레이어가 입장할 때 입장음 적용해보기 ( 파일 )

20KB
join.mp3
//플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	App.playSound("join.mp3", false, true);
});

playSoundLink

App.playSoundLink(link: string, loop: boolean = false)

모든 플레이어에게 사운드를 재생하는 함수입니다.

💡 올바른 링크를 입력했는데 재생이 되지 않는 경우

CORS 정책을 위반한 경우일 가능성이 높습니다. CORS 정책을 맞출 수 없는 경우에는 playSoundLink 대신 음악 파일을 업로드 하여 playSound 함수를 사용하는 것을 권장드립니다.

파라미터

이름
타입
설명

link

String

사운드 url

loop

boolean

true: 반복 재생 false: 1회 재생

예제

플레이어가 입장할 때 입장음 적용해보기 ( 사운드 url )

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	App.playSoundLink("https://zep.us/assets/sounds/ring.mp3",false);
});

stopSound

App.stopSound();

재생되고 있는 사운드를 멈추는 함수입니다.

파라미터

  • 없음

예제

q를 누르면 사운드가 멈추는 기능 만들어보기

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81,function(p){
	App.stopSound();
})

changeAttackSound

App.changeAttackSound(fileName:string)

찌르기(Z키) 공격 효과음을 변경하는 함수입니다.

파라미터

이름
타입
설명

fileName

String

적용할 사운드 파일명

예제

changeAttackSound 사용 방법

App.onStart.Add(function(){
	App.changeAttackSound("attack.mp3");
})

📡 통신 Methods

통신 함수 한 눈에 보기

// 해당 URL에 HTTP Get 요청을 실행
App.httpGet(url: string, headers: object, callback: ((string) => void))

// 해당 URL에 HTTP Post 포스팅을 실행
App.httpPost(url: string, headers: object, body: object, callback: ((string) => void))

// 해당 URL에 HTTP Post 포스팅을 실행
App.httpPostJson(url: string, headers: object, body: object, callback: ((string) => void))

httpGet

App.httpGet(url: string, headers: object, function(res: string){})

http get 요청을 보내는 함수입니다.

파라미터

이름
타입
설명

url

String

요청을 보낼 주소

headers

Object

요청 헤더

res

String

요청에 대한 응답

예제

한국어 별명 생성기 API를 이용해 입장하는 플레이어의 닉네임을 바꿔보기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function (player) {
	App.httpGet(
		"https://nickname.hwanmoo.kr/?format=json&count=1&max_length=6&whitespace=_",
		null,
		function (res) {
			// 응답 결과를 JSON 오브젝트로 변경
			let response = JSON.parse(res);
			player.name = response.words[0];
			player.sendUpdated();
		}
	);
});

httpPost

App.httpPost(url: string, headers: object, body: object, function(res: string))

Form-Data 형태의 http post 요청을 보내는 함수 입니다.

파라미터

이름
타입
설명

url

String

요청을 보낼 주소

headers

Object

요청 헤더, 요청 헤더가 없으면 { } 를 입력.

body

Object

요청 바디 ( Form data )

res

String

요청에 대한 응답

예제

앱에서 보낸 헤더와 데이터를 응답으로 받아 채팅창에 출력해보기.

예제와 같이 key, value 값을 string 형태로 작성해야하며, 요청 서버에서 폼데이터를 받아 처리가 가능해야합니다.

// q를 눌렀을 때 실행되는 함수
App.addOnKeyDown(81, function (player) {
	App.httpPost(
		"https://postman-echo.com/post",
		{
			"test-header": "zep",
		},
		{
			"name": "zepscript",
		},
		(res) => {
			// 요청 결과를 JSON 오브젝트로 변환
			let response = JSON.parse(res);
			App.sayToAll(`보낸 헤더: ${response.headers["test-header"]}`, 0xffffff);
			App.sayToAll(`보낸 데이터: ${response.form.name}`, 0xffffff);
		}
	);
});

httpPostJson

App.httpPostJson(url: string, headers: object, body: object, function(res: string))

Json 형태의 http post 요청을 보내는 함수 입니다.

파라미터

이름
타입
설명

url

String

요청을 보낼 주소

headers

Object

요청 헤더, 요청 헤더가 없으면 { } 를 입력.

body

Object

요청 바디 ( JSON data )

res

String

요청에 대한 응답

예제

앱에서 보낸 데이터를 응답으로 받아 채팅창에 출력해보기.

// q를 눌렀을 때 실행되는 함수
App.addOnKeyDown(81, function (player) {
	App.httpPostJson(
		"https://postman-echo.com/post",
		{},
		{
			name: "zepscript",
		},
		(res) => {
			App.sayToAll(`${res}`, 0xffffff);
			// 요청 결과를 JSON 오브젝트로 변환
			let response = JSON.parse(res);
			App.sayToAll(`보낸 데이터: ${response.data.name}`, 0xffffff);
		}
	);
});

💠 공통 Methods

공통 함수 한 눈에 보기

// App 관련 필드값이 변경되면 변경값을 적용함
App.sendUpdated()

sendUpdated

App.sendUpdated()

App 관련 필드 값이 변경되면 변경 값을 적용하는 함수입니다.

파라미터

  • 없음

구글 색상 선택 도구
구글 색상 선택 도구
구글 색상 선택 도구
구글 색상 선택 도구

Methods

소개

Map에 타일 효과나 오브젝트 설치 등 맵과 관련된 편리한 기능을 제공하는 함수들 입니다.

이름
설명

putTileEffect

지정한 좌표에 타일 효과를 적용하는 함수입니다

putObject

지정한 좌표에 오브젝트를 놓는 함수입니다.

putObjectMultiple

오브젝트를 배치할 좌표들을 2차원 배열로 입력하여 한 번에 오브젝트를 설치하는 기능입니다.

putObjectWithKey

지정한 좌표에 키 값을 가진 오브젝트를 놓는 함수입니다.

getObjectWithKey

해당 키 값을 가지고 있는 오브젝트의 정보를 가져옵니다.

playObjectAnimation

해당 좌표에 있는 오브젝트의 스프라이트 애니메이션을 실행시키는 함수입니다.

playObjectAnimationWithKey

key 값이 일치하는 오브젝트의 스프라이트 애니메이션을 실행시키는 함수입니다.

moveObject

x, y 좌표에 위치한 오브젝트를 지정한 x, y 좌표로 time 초 동안 움직이는 함수입니다.

moveObjectWithKey

키 값을 가진 오브젝트를 지정한 x,y 좌표로 움직이는 함수입니다.

clearAllObjects

ZEP 스크립트로 생성된 모든 오브젝트를 제거하는 함수입니다.

getTile

해당하는 레이어의 x,y 좌표에 있는 타일의 enum 값을 반환하는 함수입니다.

hasLocation

맵에 해당 로케이션이 있는지 체크하여 true/false를 반환하는 함수입니다.

getObjectsByType

Type에 해당하는 오브젝트들을 반환하는 함수입니다.

getTopObjectsByType

Type에 해당하는 상단 오브젝트들을 반환하는 함수입니다.

sayObjectWithKey

key 값을 가진 오브젝트 위에 말풍선을 표시하는 함수입니다.

getLocation

파라미터로 전달한 로케이션이 존재하는 경우, 로케이션 설치 좌표를 리턴합니다.

getLocationRandom

파라미터로 전달한 로케이션이 2개이상 존재하는 경우, 무작위로 선택하여 해당 로케이션의 설치 좌표를 리턴합니다.

getLocationList

파라미터로 전달된 로케이션 이름과 일치하는 모든 로케이션의 정보를 배열 형태로 반환합니다

📚 API 설명 및 예제

Methods 함수 한 눈에 보기

// 지정된 좌표에 타일효과를 적용
Map.putTileEffect(x: number, y: number, tileID: TileEffectType)

// 지정된 좌표에 오브젝트를 놓음 (기준 좌표 : Left-Top)
Map.putObject(x: number, y: number, dynamicResource: ScriptDynamicResource)

// 오브젝트를 배치할 좌표들을 2차원 배열로 입력하여 한 번에 오브젝트를 설치하는 기능입니다.
Map.putObjectMultiple(tileArray: array, type: PutObjectType, dynamicResource: ScriptDynamicResource, option: object);

// 지정된 좌표에 키 값을 가진 오브젝트를 놓음 (기준 좌표 : Left-Top)
Map.putObjectWithKey(x: number, y: number, dynamicResource: ScriptDynamicResource, option: JsValue)

// 해당 좌표에 있는 오브젝트의 스프라이트 애니메이션을 실행시킴 (putObject가 선행되어야 함)
Map.playObjectAnimation(x: number, y: number, name: string, loop: number)

// key 값이 일치하는 오브젝트의 스프라이트 애니메이션을 실행시키는 함수입니다.
Map.playObjectAnimationWithKey(key: string,) animName: string, repeatCount: number)

// ZEP 스크립트로 생성된 모든 오브젝트를 제거
Map.clearAllObjects()

// 해당 좌표의 오브젝트를 타겟 좌표로 time(초) 동안 이동
Map.moveObject(x: number, y: number, targetX: number, targetY: number, time: number)

// 해당 키 값을 가진 오브젝트를 타겟 좌표로 이동
Map.moveObjectWithKey(key: string, targetX: number, targetY: number, usePath:Boolean= true, time=0 )

// 해당하는 레이어의 x, y 좌표에 있는 타일의 타입 값을 리턴
Map.getTile(layer: number, x: number, y: number)

// 맵에 해당하는 로케이션이 있는지 true/false 값으로 리턴
Map.hasLocation(locationName: string)

// Type에 해당하는 오브젝트들을 반환하는 함수입니다.
Map.getObjectsByType(type: number)

// Type에 해당하는 상단 오브젝트들을 반환하는 함수입니다.
Map.getTopObjectsByType(type: number)

// key 값을 가진 오브젝트 위에 말풍선을 표시하는 함수입니다.
Map.sayObjectWithKey( key: string, message: string )

putTileEffect

Map.putTileEffect(x: number, y: number, tileID: TileEffectType)

지정한 좌표에 타일 효과를 적용하는 함수입니다.

파라미터

TileEffectType에 대한 자세한 설명은 TileEffectType 상세 설명 페이지를 참고해주세요.

이름
타입
설명

x, y

Number

타일 효과를 설치할 x, y 좌표

tileID

TileEffectType

• TileEffectType.NONE : 없음 •TileEffectType.IMPASSABLE : 통과 불가 • TileEffectType.SPAWN : 스폰

• TileEffectType.PORTAL : 포털 • TileEffectType.PRIVATE_AREA : 프라이빗 공간 • TileEffectType.SPOTLIGHT : 스포트라이트 • TileEffectType.EMBED : 웹 링크 • TileEffectType.LOCATION : 지정 영역 • TileEffectType.AMBIENT_SOUND : 배경 음악 • TileEffectType.TILE_EMBED : 웹 임베드 • TileEffectType.WEB_PORTAL : 웹 포털 • TileEffectType.SPACE_PORTAL : 스페이스 포털

예제

IMPASSABLE 타일 효과 설치해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	// 5, 5 좌표에 IMPASSABLE 타일 효과 설치
	Map.putTileEffect(5, 5, TileEffectType.IMPASSABLE);
});

putObject

Map.putObject(x: number, y: number, dynamicResource: ScriptDynamicResource, option: JsValue)

지정한 좌표에 오브젝트를 놓는 함수입니다. ( 기준 좌표: Left Top ) → 기준 좌표란?

ScriptDynamicResource에 대한 이해를 돕는 스프라이트시트 이해하기 문서를 확인해보세요!

null 값을 파라미터로 전달하여 스크립트로 설치한 오브젝트를 삭제 할 수 있습니다.

Map.putObject(x, y, null);

파라미터

이름
타입
설명

x, y

Number

오브젝트를 놓을 x, y 좌표

dynamicResource

ScriptDynamicResource

App.loadSpritesheet 함수로 로드한 이미지

option

Object

onObjectTouched, onObjectAttacked 와 같은 App EventListener에서 오브젝트를 인식하려면 파라미터 란에 { overlap: true } 입력해야 합니다.

예제

블루맨 오브젝트 생성해보기

// blueman.png를 객체화 하여 blueman 변수에 저장
let blueman = App.loadSpritesheet("blueman.png");

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	// 5, 5 좌표에 blueman 오브젝트 생성
	Map.putObject(5, 5, blueman, {overlap: true});
});
// w 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	// 5, 5 좌표에 있는 오브젝트를 삭제
	Map.putObject(5, 5, null);
});

putObjectMultiple

Map.putObjectMultiple(tileArray: array, type: PutObjectType, dynamicResource: ScriptDynamicResource, option: object);

오브젝트를 배치할 좌표들을 2차원 배열로 입력하여 한 번에 오브젝트를 설치하는 기능입니다. 이 기능을 사용하면 한 번에 많은 오브젝트를 설치할 경우 부하를 줄이는 효과를 얻을 수 있습니다.

파라미터

이름
타입
설명

tileArray

Array

오브젝트를 배치할 좌표들을 정의할 2차원 배열을 입력할 수 있습니다. (최대 길이 10 제한)

type

PutObjectType

PutObjectType.STROKE

  • tileArray 배열에 정의된 좌표들을 순서대로 연결하여 경로를 생성한 다음, 생성된 경로 상의 모든 좌표들에 오브젝트를 배치합니다.

dynamicResource

ScriptDynamicResource

App.loadSpritesheet 함수로 로드한 이미지

option

Object

onObjectTouched, onObjectAttacked 와 같은 App EventListener에서 오브젝트를 인식하려면 파라미터 란에 { overlap: true } 입력해야 합니다.

예제

사각형, 원 모양으로 오브젝트 배치하기

PutObjectType.STROKE 사각형 모양으로 배치
PutObjectType.STROKE 원 모양으로 배치
const _mark = App.loadSpritesheet("mark.png");

// Q를 누르면 동작하는 함수 - 사각형 모양으로 배치
App.addOnKeyDown(81, function (player) {
	const tileArray = [
		[5, 5],
		[9, 5],
		[9, 9],
		[5, 9],
		[5, 5],
	];
	Map.putObjectMultiple(tileArray, PutObjectType.STROKE, _mark, { overlap: true });
});

// W를 누르면 동작하는 함수 - 원 모양으로 배치
App.addOnKeyDown(87, function (player) {
	const tileArray = [
		[10, 5],
		[8, 7],
		[8, 10],
		[10, 12],
		[13, 12],
		[15, 10],
		[15, 7],
		[13, 5],
		[10, 5],
	];
	Map.putObjectMultiple(tileArray, PutObjectType.STROKE, _mark, { overlap: true });
});

putObjectWithKey

Map.putObjectWithKey(x: number, y: number, dynamicResource: ScriptDynamicResource, option: JsValue)

지정한 좌표에 키 값을 가진 오브젝트를 놓는 함수입니다. ( 기준 좌표: Left Top )

dynamicResource 파라미터에 null 입력시 해당 좌표의 오브젝트를 삭제할 수 있습니다.

관련 문서: 오브젝트 npcProperty

파라미터

이름
타입
설명

x, y

Number

오브젝트를 놓을 x, y 좌표

dynamicResource

ScriptDynamicResource

App.loadSpritesheet 함수로 로드한 이미지

옵션 (option)

값을 입력하지 않아도 Default 값이 적용됩니다

이름
타입
설명

key

String

오브젝트의 키 값 (Default: null)

moveSpeed

Number

오브젝트의 이동속도

(Default: 80)

useDirAnim

Boolean

방향을 인지해서 애니메이션을 재생하는 옵션 (Default: false)

impassable

Boolean

오브젝트 통과불가 옵션 (Default: false)

offsetX

Number

오브젝트 이미지의 배치 기준점의 X좌표 (Default: 0)

offsetY

Number

오브젝트 이미지의 배치 기준점의 Y좌표 (Default: 0)

npcProperty

Object

npcProperty 관련 내용은 를 참고해주세요. (Default: null)

topObject

Boolean

true 로 설정하면 오브젝트가 상단오브젝트로 설치됩니다. (Default: false)

예제

키 값을 가진 블루맨 오브젝트 생성해보기

let blueman = App.loadSpritesheet("blueman.png");
// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	Map.putObjectWithKey(18, 6, blueman, {
		type:21, // zep-script 상호작용 오브젝트
		overlap: true, // 충돌 이벤트 감지
		movespeed: 100, // 이동속도, 기본값: 80
		key: "TestBlueMan", // 키 값
		impassalbe:true, // 통과 불가 오브젝트 설정
		useDirAnim: true, // 방향을 인지해서 애니메이션을 재생하는 옵션
        	offsetX: -8, // 오브젝트 이미지의 배치 기준점의 X좌표를 조정
              	offsetY: -32,// 오브젝트 이미지의 배치 기준점의 Y좌표를 조정
	});
});

// w 키를 누르면 동작하는 함수
App.addOnKeyDown(87, function (player) {
	// 오브젝트 삭제
	Map.putObjectWithKey(18, 6, null, {
		key: "TestBlueMan", // 키 값
	});
});

getObjectWithKey

Map.getObjectWithKey(key: String)

해당 키 값을 가지고 있는 오브젝트의 정보를 가져옵니다.

파라미터

이름
타입
설명

key

String

정보를 가져올 오브젝트의 key 값

예제

키 값을 가진 오브젝트를 생성하고 관련 데이터를 채팅창에 출력해보기.

let blueman = App.loadSpritesheet("blueman.png");
App.onStart.Add(function() {
	Map.putObjectWithKey(18, 6, blueman, {
		overlap: true,
		movespeed: 80,
		key: "TestBlueMan", // 키 값
	});
});
// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	let object_blueman = Map.getObjectWithKey("TestBlueMan");
	for(let data in object_blueman){
		App.sayToAll(`${data}: ${object_blueman[data]}`)
	}
})

playObjectAnimation

Map.playObjectAnimation(x: number, y: number, name: string, loop: number)

해당 좌표에 있는 오브젝트의 스프라이트 애니메이션을 실행시키는 함수입니다.

해당 좌표 지점에 Map.putObject 함수가 선행되어야합니다.

스프라이트 이미지를 처음 들어보신다면 스프라이트시트 이해하기 문서를 확인해보세요!

파라미터

이름
타입
설명

x, y

Number

타일 효과를 적용할 x, y 좌표

name

String

let 변수 = App.loadSpritesheet(...) 스프라이트를 저장한 변수 이름을 다음과 같이 입력해야 합니다. → ‘#’ + 변수.id

loop

number

애니메이션 반복 횟수 ( -1: 계속반복)

예제

춤추는 블루맨 오브젝트 설치해보기

// 한 프레임의 사이즈 48x64
let blueman_dance = App.loadSpritesheet(
	"blueman.png",
	48,
	64,
	[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37], // 21번째 ~ 38번째 이미지로 애니메이션을 구성
	8
);
// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	Map.putObject(5, 5, blueman_dance, { overlap: true });
	Map.playObjectAnimation(5, 5, "#" + blueman_dance.id, -1);
});

playObjectAnimationWithKey

Map.playObjectAnimation(key: string, animName: string, repeatCount: number)

key 값이 일치하는 오브젝트의 스프라이트 애니메이션을 실행시키는 함수입니다.

Map.putObjectWithKey함수가 선행되어야합니다.

스프라이트 이미지를 처음 들어보신다면 스프라이트시트 이해하기 문서를 확인해보세요!

파라미터

이름
타입
설명

key

String

오브젝트의 키값

animName

String

플레이할 애니메이션의 이름

repeatCount

Number

애니메이션 반복 횟수 ( -1 입력시 무한반복 )

예제

춤추는 블루맨 오브젝트 설치해보기

var blueman_sprite = App.loadSpritesheet(
	"blueman.png",
	48,
	64,
	{
		left: [5, 6, 7, 8, 9],
		up: [15, 16, 17, 18, 19],
		down: [0, 1, 2, 3, 4],
		right: [10, 11, 12, 13, 14],
		dance: [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37],
		idle: [0],
	},
	8
);

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	Map.putObjectWithKey(10, 10, blueman_sprite, {
		overlap: true,
		movespeed: 80, // default: 80
		key: "blueman",
	});
	// "dance" 애니메이션 재생
	Map.playObjectAnimationWithKey("blueman", "dance", -1);
});

moveObject

Map.moveObject(x: number, y: number, targetX: number, targetY: number, time: number)

x, y 좌표에 위치한 오브젝트를 targetX, targetY로 time 초 동안 움직이는 함수입니다.

해당 좌표 지점에 Map.putObject 함수가 선행되어야합니다.

스프라이트 이미지를 처음 들어보신다면 스프라이트시트 이해하기 문서를 확인해보세요!

파라미터

이름
타입
설명

x, y

Number

오브젝트가 위치한 x, y 좌표

targetX, targetY

Number

목적지 x, y 좌표

time

Number

목적지까지 걸리는 시간(초)

예제

블루맨 이동시켜보기

// 한 프레임의 사이즈 48x64
let blueman_right = App.loadSpritesheet(
	"blueman.png",
	48,
	64,
	[10, 11, 12, 13, 14], // 11 ~ 15번째 이미지로 애니메이션 구성
	8
);
// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	Map.putObject(5, 5, blueman_right, { overlap: true });
	Map.playObjectAnimation(5, 5, "#" + blueman_right.id, -1);
	Map.moveObject(5, 5, 10, 5, 3) // (5,5)에서 (10,5)로 3초 동안 이동
});

moveObjectWithKey

Map.moveObjectWithKey(key: string, targetX: number, targetY: number, usePath:boolean = true)

key 값을 가진 오브젝트를 targetX, targetY로 움직이는 함수입니다.

💡 path 파라미터가 true인 경우, 목표 지점이 impassable 타일이거나 도달 할 수 없는 좌표이면 오브젝트가 움직이지 않습니다.

파라미터

이름
타입
설명

key

String

오브젝트의 key 값

targetX, targetY

Number

목적지 x, y 좌표

usePath

Boolean

true일 경우 Impassable 타일을 통과하지 못합니다. false일 경우 Impassable 타일을 무시하고 지나갑니다.

예제

moveObejctWithKey 동작 방식

// 한 프레임의 사이즈 48x64
let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], // 좌방향 이동 이미지
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
    dance: [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],
    down_jump: [38],
    left_jump: [39],
    right_jump: [40],
    up_jump: [41],
}, 10);
// q 키를 누르면 동작하는 함수
// App.addOnKeyDown 설명(링크)
App.addOnKeyDown(81, function (player) {
	Map.putObjectWithKey(18, 6, blueman, {
		overlap: true,
		movespeed: 50, // default: 80
		key: "TestBlueMan",
                useDirAnim: true // 방향을 인지해서 애니메이션 재생하는 옵션
                offsetX: -8,
                offsetY: -32,
	});
	Map.moveObjectWithKey("TestBlueMan", 10, 10, true);
});

clearAllObjects()

Map.clearAllObjects()

ZEP 스크립트로 생성된 모든 오브젝트를 제거하는 함수입니다.

파라미터

  • 없음

예제

생성된 모든 오브젝트 삭제하기

let blueman = App.loadSpritesheet("blueman.png");

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	// 5, 5 좌표부터 blueman 오브젝트 5개 생성
	Map.putObject(5, 5, blueman, {overlap: true});
	Map.putObject(6, 5, blueman, {overlap: true});
	Map.putObject(7, 5, blueman, {overlap: true});
	Map.putObject(8, 5, blueman, {overlap: true});
	Map.putObject(9, 5, blueman, {overlap: true});
});

// w 키를 누르면 동작하는 함수
App.addOnKeyDown(87, function (player) {
	// 스크립트로 생성된 모든 오브젝트 제거
	Map.clearAllObjects();
});

getTile

Map.getTile(layer: number, x: number, y: number)

해당하는 레이어의 x, y 좌표에 있는 타일의 타입 값을 리턴, 타일이 없으면 -1을 리턴합니다.

파라미터

이름
타입
설명

layer

Number

레이어에 해당하는 값 0 = Floor (바닥타일), 1 = WALL (벽타일), 2 = TileEffect (타일효과), 3 = Object (오브젝트), 5 = TopObject (상단오브젝트),

x, y

Number

x, y 좌표

예제

맵에 있는 모든 오브젝트의 타입을 조회하기

const LayerType = {
	FLOOR: 0,
	WALL: 1,
	TILE_EFFECTS: 2,
	OBJECTS: 3,
	TOP_OBJECTS: 5,
};
// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	let layer = LayerType.OBJECTS;
	for (let x = 0; x < Map.width; x++) {
		for (let y = 0; y < Map.height; y++) {
			let data = Map.getTile(layer, x, y);
			if (data >= 0) {
				App.sayToAll(`(${x},${y})  type: ${data}`);
			}
		}
	}
});

getLocation

Map.getLocation(locationName: string)

파라미터로 전달한 로케이션이 존재하는 경우, 로케이션 설치 좌표를 리턴합니다.

파라미터

이름
타입
설명

locationName

String

로케이션 이름

예제

맵 에디터에서 타일효과 > 지정 영역 선택 후 영역 이름을 "testLocation" 으로 지정해 맵에 설치한 후 예제 코드를 실행해보세요.

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
    const targetLocationName = "testLocation";
    const targetLocation = Map.getLocation(targetLocationName);
    
    if(targetLocation) {
        player.sendMessage(`targetLocation placed at (${targetLocation.x}, ${targetLocation.y})`)
    } else {
        player.sendMessage(`${targetLocationName} is not exist!`)
    }
});

getLocationRandom

Map.getLocationRandom(locationName: string)

파라미터로 전달한 로케이션이 2개이상 존재하는 경우, 무작위로 선택하여 해당 로케이션의 설치 좌표를 리턴합니다.

파라미터

이름
타입
설명

locationName

String

로케이션 이름

예제

맵 에디터에서 타일효과 > 지정 영역 선택 후 영역 이름을 "testLocation" 으로 지정해 맵에 여러개 설치한 후 예제 코드를 실행해보세요.

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
    const targetLocationName = "testLocation";
    const targetLocation = Map.getLocationRandom(targetLocationName);
    
    if(targetLocation) {
        player.sendMessage(`targetLocation placed at (${targetLocation.x}, ${targetLocation.y})`)
    } else {
        player.sendMessage(`${targetLocationName} is not exist!`)
    }
});

getLocationList

Map.getLocationList(locationName: string): Array

파라미터로 전달된 로케이션 이름과 일치하는 모든 로케이션의 정보를 배열 형태로 반환합니다. 각각의 로케이션 정보는 로케이션 설치 좌표(x, y), 크기(width, height), 그리고 라벨 값(label)을 포함하고 있습니다.

파라미터

이름
타입
설명

locationName

String

로케이션 이름

예제

맵 에디터에서 타일효과 > 지정 영역 선택 후 영역 이름을 "testLocation" 으로 지정해 맵에 여러개 설치한 후 예제 코드를 실행해보세요.

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	const targetLocationName = "testLocation";
	const targetLocationList = Map.getLocationList(targetLocationName);

	if (targetLocationList) {
		for (const location of targetLocationList) {
			player.sendMessage(`${JSON.stringify(location)}`);
		}
	} else {
		player.sendMessage(`${targetLocationName} is not exist!`);
	}
});

hasLocation

Map.hasLocation(locationName: String): Boolean

맵에 해당 로케이션이 있는지 체크하여 true/false 값을 리턴합니다.

파라미터

이름
타입
설명

locationName

String

로케이션 이름

예제

로케이션이 설치되어있는지 체크하는 키 함수 만들어보기

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	if(Map.hasLocation("test")){
		App.sayToAll("test 로케이션이 설치되어있습니다.")
	} else {
		App.sayToAll("test 로케이션이 설치되어있지 않습니다.")
	}
});

getObjectsByType

Map.getObjectsByType(type: numer) : Array

Type에 해당하는 오브젝트들을 리턴하는 함수입니다.

파라미터

이름
타입
설명

type

Number

오브젝트의 type 값

예제

모든 타입의 오브젝트 조회하기

const ObjectEffectType = {
    NONE = 0,
    SHOW_NOTE = 1,
    SHOW_IMAGE = 2,
    PASSWORD_DOOR = 3,
    LINK_WEBSITE = 4,
    EMBED_WEBSITE = 5,
    API_CALL = 6,
    REPLACE_IMAGE = 7,
    NFT_GIVEAWAY = 8,
    NFT_DOOR = 9,
    POST_MESSAGE = 10,
    SHOW_CHAT_BALLOON = 11,
    FT_DOOR = 12,
    POST_MESSAGE_TO_APP = 13,
    DONATION_DOOR = 14,
    IMPASSABLE = 15,
    STAMP = 16,
    TOKEN_DONATION_DOOR = 17,
    CHANGE_OBJECT = 18,
    ANIMATION = 19,
    NFT_DOOR_MOVE = 20,
    INTERACTION_WITH_ZEPSCRIPTS = 21,
    MULTIPLE_CHOICE = 22,
}
// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81,function(player){
    for(let key in ObjectEffectType){
        let type = ObjectEffectType[key];
        let arr = Map.getObjectsByType(type);
        let index = 0;
        for(let obj of arr){
            for(let key in obj){
                App.sayToAll(`${key}: ${obj[key]}`);        
            }
        }
    }
})
오브젝트 조회

getTopObjectsByType

Map.getTopObjectsByType(type: numer) : array

Type에 해당하는 상단오브젝트들을 리턴하는 함수입니다.

파라미터

이름
타입
설명

type

Number

오브젝트의 type 값

예제

모든 타입의 상단오브젝트 조회하기

const ObjectEffectType = {
    NONE : 0,
    SHOW_NOTE : 1,
    SHOW_IMAGE : 2,
    PASSWORD_DOOR : 3,
    LINK_WEBSITE : 4,
    EMBED_WEBSITE : 5,
    API_CALL : 6,
    REPLACE_IMAGE : 7,
    NFT_GIVEAWAY : 8,
    NFT_DOOR : 9,
    POST_MESSAGE : 10,
    SHOW_CHAT_BALLOON : 11,
    FT_DOOR : 12,
    POST_MESSAGE_TO_APP : 13,
    DONATION_DOOR : 14,
    IMPASSABLE : 15,
    STAMP : 16,
    TOKEN_DONATION_DOOR : 17,
    CHANGE_OBJECT : 18,
    ANIMATION : 19,
    NFT_DOOR_MOVE : 20,
    INTERACTION_WITH_ZEPSCRIPTS : 21,
    MULTIPLE_CHOICE : 22,
}
// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81,function(player){
    for(let key in ObjectEffectType){
        let type = ObjectEffectType[key];
        let arr = Map.getTopObjectsByType(type);
        for(let obj of arr){
            for(let key in obj){
                App.sayToAll(`${key}: ${obj[key]}`);        
            }
        }
    }
})
상단 오브젝트 조회

sayObjectWithKey

Map.sayObjectWithKey( key: string, message: string )

key 값을 가진 오브젝트 위에 말풍선을 표시하는 함수입니다.

파라미터

이름
타입
설명

key

String

대상오브젝트의 키 값

message

String

말풍선에 표시할 메시지

예제

key 값을 가진 오브젝트 위에 말풍선 표시하기

29KB
sayObjectWithKey.zip
archive
const objectKey = "TestBlueMan";
// Q를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	Map.putObjectWithKey(18, 6, blueman, {
		npcProperty: { name: "BlueMan", hpColor: 0x03ff03, hp: 100, hpMax: 100 },
		overlap: true,
		movespeed: 100, 
		key: objectKey, 
		useDirAnim: true
	});
});

// W를 누르면 동작하는 함수
App.addOnKeyDown(87, function(player){
    Map.sayObjectWithKey(objectKey, `I'm BlueMan!`)
})

부록

스프라이트시트 이해하기

기준좌표란?


문서

Field

소개

플레이어와 관련된 속성 값들 입니다.

플레이어의 닉네임(name), 위치(tileX / tileY) 등을 조회하거나 플레이어에게 스팟라이트(spotlight), 안보임(hidden) 기능을 활성화 할 수 있으며, 캐릭터의 이동속도(moveSpeed), 이미지(sprite)를 변경하거나 플레이어의 저장공간(storage)을 활용할 수도 있습니다.

🔒 아이콘이 있는 필드는 수정이 불가능한 읽기 전용 필드입니다.

이름
설명

🔒 id

플레이어의 id 값

name

플레이어의 닉네임 값

title

아바타 닉네임 위에 노란색으로 노출되는 텍스트

🔒 role

플레이어의 권한을 나타내는 숫자 값

🔒 tileX / tileY

아바타가 서있는 X 좌표 값과 Y 좌표 값

🔒 dir

아바타가 바라보고 있는 방향 값

moveSpeed

플레이어의 이동속도 값

sprite

아바타의 스프라이트 이미지 값

tag

필요한 속성 값을 부여 가능한 값 저장 공간

hidden

값이 true 이면, 다른 플레이어에게 보이지 않습니다.

spotlight

플레이어의 스팟라이트 기능 활성화 여부

attackType

플레이어의 공격(Z키) 타입

attackSprite

플레이어의 공격(Z키) 이미지 값

attackParam1

공격 이미지가 날아가는 거리 값

attackParam2

공격 가능 거리 값 attackType이 2(원거리 공격)으로 설정 된 경우에만 유효

🔒 walletAddress

플레이어의 전자지갑 주소 값

storage

스페이스 내의 Player 값 저장 공간(스페이스 한정)

🔒 isMobile

플레이어의 모바일 접속여부

🔒 isMoving

플레이어가 움직이고 있으면 True, 아니면 False를 반환

🔒 isJumping

플레이어가 점프하고 있으면 True, 아니면 False를 반환

🔒 customData

URL 쿼리 스트링을 읽어 값을 저장 할 수 있습니다.

displayRatio

플레이어의 화면 줌 비율을 조절 할 수 있습니다.

titleColor

플레이어의 타이틀 색상

🔒 emailHash

플레이어의 이메일 Hash 값

🔒 isGuest

비로그인 플레이어인 경우 true 값을 가집니다.

🔒 language

플레이어의 브라우저에서 사용하는 언어 설정 값

🔒 away

5분 이상 비활성화된 유저인 경우 true값을 가집니다.

enableFreeView

맵 둘러보기 허용 여부를 설정할 수 있습니다.

📚 API 설명 및 예제

id , name

player.id : Number player.name : String

플레이어의 id, 닉네임 값을 가져옵니다.

예제

플레이어가 입장 할 때 플레이어의 id, name 값 출력해보기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
  App.sayToAll(`id: ${player.id} name: ${player.name}`);
})

title

player.title : String

title은 캐릭터 닉네임 위에 노란색으로 노출되는 텍스트입니다.

예제

플레이어가 입장 할 때 title 설정해보기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
	player.title = "타이틀";
	player.sendUpdated();
})

role

player.role : Number

role은 플레이어의 권한을 나타내는 숫자 값 입니다.

플레이어의 role에 따라 다음과 같은 값을 출력합니다.

일반/비로그인유저
-1
스태프
2000

멤버

0

관리자

3000

에디터

1000

맵소유자

3001

예제

권한 값을 채팅 창에 표시해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	App.sayToAll(`${player.name}님의 권한: ${player.role}`)
})

tileX, tileY

player.tileX: Number player.tileY: Number

플레이어의 캐릭터가 서있는 X 좌표 값과 Y 좌표 값입니다.

예제

내 캐릭터의 x, y 좌표 출력해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	App.sayToAll(`현재 좌표: (${player.tileX}, ${player.tileY})`)
})

dir

player.dir : Number

플레이어의 캐릭터가 바라보고 있는 방향입니다.

캐릭터가 바라보고 있는 방향에 따라 다음과 같은 값을 출력합니다.

캐릭터가 바라보는 방향에 따른 dir 값

예제

캐릭터가 바라보고 있는 방향을 출력해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	App.sayToAll(`바라보고 있는 방향: ${player.dir}`)
})

moveSpeed

player.moveSpeed : Number

플레이어의 이동속도 값입니다.( 기본 값: 80 )

이동속도 값이 0이면 움직일 수 없습니다.

예제

q 키를 누르면 이동속도가 빨라지는 함수 만들어보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	player.moveSpeed = 150;
	player.sendUpdated();
})

sprite

player.sprite : ScriptDynamicResource

플레이어 캐릭터의 스프라이트 이미지입니다. ( null 입력 시 기본 아바타 이미지로 초기화 )

스프라이트 이미지를 처음 들어보신다면 스프라이트시트 이해하기 문서를 확인해보세요!

예제

페인트맨 - 블루맨 이미지를 캐릭터 이미지로 적용해보기

29KB
예제_loadSpritesheet.zip
archive
// 한 프레임의 사이즈 48x64
let blueman = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], // 좌방향 이동 이미지
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
		dance: [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],
		down_jump: [38],
		left_jump: [39],
		right_jump: [40],
		up_jump: [41],
}, 8);
// 플레이어가 입장하면 캐릭터 이미지가 바뀜
App.onJoinPlayer.Add(function(player){
	player.sprite = blueman;
	player.sendUpdated();
});

tag

player.tag: Any

tag를 사용해 플레이어에게 필요한 속성 값을 부여 할 수 있습니다.

예제

플레이어에게 ‘alive’ 속성 값 부여해보기. ‘alive’ 속성 값은 임의로 생성한 속성 입니다.

쓰이지 않는다면 아무 의미 없는 속성 값 이지만, 게임을 만들 때 플레이어의 생존 여부를 체크하는 중요한 속성으로 사용할 수 있습니다.

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function (player) {
	player.tag = {
		alive: true,
	};
	player.sendUpdated();

	App.sayToAll(`alive: ${player.tag.alive}`);
});

hidden

player.hidden: Boolean

hidden 값이 true 이면, 해당 플레이어는 다른 플레이어에게 보이지 않습니다.

⚠️ hidden인 상태에서 모습은 보이지 않지만, 오디오와 비디오 연결은 됩니다.

예제

캐릭터에게 hidden 속성을 부여해서 다른 플레이어에게 안 보이게 해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	player.hidden = true;
	player.sendUpdated();
});

spotlight

player.spotlight: Boolean

플레이어의 스팟 라이트 기능 활성화 여부입니다.

예제

q 키를 누르면 스팟 라이트 기능을 ON/OFF 하는 함수 만들어보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	if(player.spotlight){
		player.spotlight = false;
	}
	else{
		player.spotlight = true;
	}
	player.sendUpdated();
});

attackType

player.attackType : Number

플레이어의 공격(Z키) 타입입니다. ( 기본: 0 )

attackType
설명

0

attackType을 설정 하지 않았을 때 기본 공격 타입을 의미합니다.

2

원거리 공격 타입입니다. attackParam2와 함께 설정할 때 유효합니다.

예제

캐릭터의 attackType 변경해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	player.attackType = 0;
	App.sayToAll(`attackType: ${player.attackType}`);
	player.sendUpdated();
});

attackParam1

player.attackParam1: Number

공격(Z키) 이미지가 날아가는 거리 속성입니다. 공격 가능 거리는 늘어나지 않습니다.

예제

attackParam1 변경해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	player.attackType = 0;
	player.attackParam1 = 10;
	App.sayToAll(`attackType: ${player.attackType}`);
	App.sayToAll(`attackParam1: ${player.attackParam1}`);
	player.sendUpdated();
});

attackParam2

player.attackParam2: Number

공격 가능 거리 속성입니다. attackType이 원거리 공격으로 설정 된 경우에만 유효합니다.

예제

attackParam2 이용해 원거리 공격 설정 해보기.

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	player.attackType = 2;
	player.attackParam2 = 5;
	App.sayToAll(`attackType: ${player.attackType}`);
	App.sayToAll(`attackParam2: ${player.attackParam2}`);
	player.sendUpdated();
});

attackSprite

player.attackSprite : ScriptDynamicResource

공격(Z키) 이미지를 지정할 수 있습니다.

예제

공격 이미지 적용해보기

예시 이미지 - 단일 이미지
예시 이미지 - 연속된 이미지
연속된 이미지의 경우
방향별 애니메이션 적용
16KB
animation_attack_sprite.zip
archive
방향별 애니메이션 예제 코드
// 단일 이미지의 경우
let redBoxing = App.loadSpritesheet("redBoxing.png");
App.onJoinPlayer.Add(function (player) {
	player.attackSprite = redBoxing;
	player.sendUpdated();
});
// 연속된 이미지의 경우
let attackSprite = App.loadSpritesheet("attack_sprite.png", 32, 32, [0, 1, 2], 3);
App.onJoinPlayer.Add(function (player) {
	player.attackSprite = attackSprite;
	player.sendUpdated();
});
// 방향별 애니메이션을 적용하는경우
let attackSprite = App.loadSpritesheet(
    "animation_sprite.png", // 이미지 파일명
    48, 48, // 이미지 프레임 사이즈
    {
        left: [0, 1, 2, 3, 4, 5], // 좌측을 보고 공격하는 경우
        right: [6, 7, 8, 9, 10, 11], // 우측을 보고 공격하는 경우
        down: [12, 13, 14, 15, 16, 17], // 위를 보고 공격하는 경우
        up: [18, 19, 20, 21, 22, 23], // 아래를 보고 공격하는 경우
    },
    6
);
App.onJoinPlayer.Add(function (player) {
	player.attackSprite = attackSprite;
	player.sendUpdated();
});

walletAddress

player.walletAddress : String

플레이어의 전자지갑 주소입니다.

예제

전자지갑 주소 출력해보기 ( 전자지갑 주소가 없을 경우 null이 출력 )

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81, function (player) {
	App.sayToAll(`${player.walletAddress}`)
});

storage

player.storage : String

스페이스 내의 Player 값 저장 공간 입니다 (스페이스 한정)

예제

플레이어 storage에 데이터를 저장하고, 확인해보기

💡 앱을 종료했다가 다시 켜도 저장된 값이 사라지지 않습니다.

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	player.storage = "data";
	player.save(); // storage의 값이 변경되면 player.save()로 변경값을 적용
})

// w 키를 누르면 동작하는 함수
App.addOnKeyDown(87,function(player){
	App.sayToAll(player.storage); // player storage에 저장된 값을 채팅창에 출력
})

isMobile

player.isMobile : Boolean

플레이어의 모바일 접속 여부를 true/false 로 출력합니다.

예제

플레이어가 입장 할 때 입장메시지에 모바일/PC 표시해보기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){	
	if(player.isMobile){
		App.sayToAll(`${player.name}님이 모바일에서 접속했습니다.`)	
	} else{
		App.sayToAll(`${player.name}님이 PC에서 접속했습니다.`)
	}
});

isMoving

player.isMoving : Boolean

플레이어가 움직이고 있으면 True, 아니면 False를 반환합니다.

예제

플레이어의 움직임을 감지해서 메시지 출력해보기.

App.onUpdate.Add(function (dt) {
	let _players = App.players;
	for (let i in _players) {
		let p = _players[i];
		if (p.isMoving) {
			App.sayToAll(`${p.name}님이 움직이는 중..`);
		}
	}
});

isJumping

player.isJumping : Boolean

플레이어가 점프하고 있으면 True, 아니면 False를 반환합니다.

예제

플레이어의 점프를 감지해서 메시지 출력해보기.

App.onUpdate.Add(function (dt) {
	let _players = App.players;
	for (let i in _players) {
		let p = _players[i];
		if (p.isJumping) {
			App.sayToAll(`[시스템] ${p.name}님이 점프 중..`);
		}
	}
});

customData

player.customData : String

URL 쿼리스트링으로 전달 받은 값을 저장하는 필드입니다.

예제

🔥 URL 쿼리스트링 활용하기

displayRatio

player.displayRatio

플레이어화면의 줌을 컨트롤 하는 값 ( 기본 값: 1 )

예제

화면의 줌을 컨트롤 하는 키 만들어보기

// q 키를 누르면 동작하는 함수
// 한 번 누르면 화면의 줌 값이 커지고, 한 번 더 누르면 원래대로 돌아오는 키 함수 
App.addOnKeyDown(81,function(player){
	if(player.displayRatio == 1){
		player.displayRatio = 5;
	}else{
		player.displayRatio = 1;
	}
	player.sendUpdated(); //* player의 Field값이 변경되면 player.sendUpdated()로 변경값을 적용
})
displayRatio = 1
displayRatio = 5

titleColor

player.titleColor

플레이어의 타이틀 색상을 읽거나 수정 할 수 있습니다.

Enum 값 또는 컬러 Hex Code 값을 입력할 수 있습니다.

사용 가능한 Enum ColorType
{ WHITE, BLACK, RED, GREEN, BLUE, ORANGE, PURPLE, GRAY, YELLOW, MAGENTA, CYAN }

예제

타이틀 색상 바꿔보기

// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	player.title = "🔸Title🔸";
	// Enum 값으로 입력하는 경우
	player.titleColor = ColorType.CYAN;
	
	// Hex Code로 입력하는 경우 (주석을 해제해주세요)
	// player.titleColor = 0x00FFFF;
	
	player.sendUpdated(); //* player의 Field값이 변경되면 player.sendUpdated()로 변경값을 적용
});

emailHash

player.emailHash

플레이어의 이메일 Hash 값을 가져옵니다.

예제

플레이어의 이메일 Hash 값 출력하기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
  App.sayToAll(`name: ${player.name} emailHash: ${player.emailHash}`);
})

isGuest

player.isGuest

비로그인 플레이어인 경우 true 값을 가집니다.

예제

비로그인 유저 입장시 타이틀에 "GUEST" 표시하기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
  if(player.isGuest){
    player.title = "GUEST";
    player.sendUpdated();
  }
})

language

player.language

브라우저에서 사용하는 언어 설정값에 따라 다음과 같은 값을 가집니다.

한국어 "ko", 일본어: "ja", 영어: "en"

예제

유저 입장시 브라우저에서 사용 하는 언어 설정 값 표시하기

// 플레이어가 입장할 때 동작하는 함수
App.onJoinPlayer.Add(function(player){
  App.sayToAll(player.language);
})

away

player.away

장시간 비활성화된 유저인 경우 true값을 가집니다.

enableFreeView

player.enableFreeView

플레이어의 맵 둘러보기 허용 여부를 설정할 수 있습니다.

예제

단축키로 플레이어의 맵 둘러보기 허용 여부 설정하기

// Q를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	if (player.enableFreeView) {
		player.enableFreeView = false;
	} else {
		player.enableFreeView = true;
	}
	player.sendUpdated();
	player.sendMessage(`player.enableFreeView = ${player.enableFreeView}`, 0x00ffff);
});

Methods

소개

UI, 유저 컨트롤, 사운드 등 ZEP에서 일어날 수 있는 전반적인 기능을 제공하는 함수입니다.

플레이어 개인 화면에 UI를 표시, 플레이어를 이동, 플레이어 개인에게 사운드 재생 등 편리한 기능을 제공합니다.

UI

이름
설명

showCenterLabel

플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

showCustomLabel

플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다. text 부분에 span 태그를 넣어 텍스트를 꾸밀 수 있습니다.

showWidget

플레이어에게 지정된 위치에 위젯을 불러오는 함수입니다.

showBuyAlert

플레이어에게 구매 위젯을 표시하고, 구매 후 동작하는 콜백함수를 작성할 수 있습니다.

hideBuyAlert

플레이어의 구매 위젯을 숨깁니다.

sendMessage

유저 개인에게 채팅 메시지를 보냅니다.

showPrompt

플레이어에게 입력창을 보여주고, 플레이어의 응답에 따라 동작하는 callback 함수를 작성할 수 있습니다.

showConfirm

플레이어에게 확인창을 보여주고, 플레이어가 OK를 눌렀을 때 동작하는 콜백함수를 작성할 수 있습니다.

showAlert

플레이어에게 경고창을 보여주고, 플레이어가 OK를 눌렀을 때 동작하는 callback 함수를 작성할 수 있습니다.

showImageModal

플레이어에게 입력한 이미지 주소에 해당하는 이미지를 표시합니다.

showNoteModal

플레이어에게 텍스트 창을 보여주는 함수입니다.

showWidgetResponsive

위젯의 상/하/좌/우 여백을 화면 크기에 대한 %비율로 정의하여 위젯을 표시합니다.

openWebLink

플레이어에게 웹 URL을 새 창이나 팝업 창으로 열어 보여줍니다.

showEmbed

URL을 임베드 형태로 표시합니다.

크기와 위치를 설정할 수 있습니다.

Data Load

이름
설명

isEmail

플레이어의 이메일을 비교하는 함수입니다.

getLocationName

플레이어가 서있는 지정 영역의 이름을 출력합니다.

User Control

이름
설명

spawnAt

플레이어의 캐릭터를 지정한 좌표로 이동시키는 함수입니다.

spawnAtLocation

플레이어의 캐릭터를 지정 영역으로 이동시키는 함수입니다.

spawnAtMap

플레이어를 다른 스페이스 또는 맵으로 이동시키는 함수입니다.

setCameraTarget

플레이어의 시점을 지정된 좌표로 중심 이동시킵니다.

setCameraTargetWithKey

플레이어의 시점을 특정 오브젝트로 중심 이동시킵니다.

setEffectSprite

플레이어의 배경 또는 전경 이미지를 설정 할 수 있습니다.

playEffectSprite

플레이어에게 애니메이션 효과를 입력 횟수만큼 반복 재생하고 사라지는 효과를 적용합니다.

disappearObject

플레이어 개인 화면 상에서 key 값을 가지는 오브젝트를 사라지게하는 함수입니다.

Sound

이름
설명

playSound

플레이어에게 사운드 파일을 재생하는 함수입니다.

playSoundLink

플레이어에게 사운드 URL을 재생하는 함수입니다.

stopSound

플레이어에게 재생중인 사운드를 중지시키는 함수입니다.

공통

이름
설명

sendUpdated

Player 관련 필드 값이 변경되면 변경 값을 적용하는 함수입니다.

save

Player storage 값이 변경되면 변경 값을 적용하는 함수입니다.

📚 API 설명 및 예제

🎨 UI Methods

UI 함수 한 눈에 보기

// 플레이어에게 지정된 위치에 해당 text를 3초간 표시
player.showCenterLabel(text: string, color: uint = 0xFFFFFF, bgColor: uint = 0x000000, offset: int = 0, time: int = 3000)

// 모든 플레이어에게 지정된 위치에 해당 text를 3초간 표시, 커스터마이징 가능
player.showCustomLabel(text: string, color: number = 0xFFFFFF, bgColor: number = 0x000000, offset: number = 0, width = 100, opacity = 0.6, time: int = 3000);

// 플레이어에게 지정된 align의 위치에 해당 html파일을 위젯으로 불러옴
player.showWidget(fileName: string, align: string, width: number, height: number): ScriptWidget

// 플레이어에게 구매 위젯을 표시하고, 구매 후 동작하는 콜백함수를 작성할 수 있습니다.
player.showBuyAlert(itemName: string, price: number, callback: function);

// 플레이어의 구매 위젯을 닫습니다.
player.hideBuyAlert();

// 플레이어 개인에게 채팅 메시지를 보냅니다.
player.sendMessage(message: string, color: number = 0xFFFFFF)

// 플레이어에게 입력창을 보여주고, 플레이어의 응답에 따라 동작하는 callback 함수를 작성할 수 있습니다.
player.showPrompt(text: string, function(inputText))

// 플레이어에게 확인창을 보여주고, 플레이어가 OK를 눌렀을 때 동작하는 콜백함수를 작성할 수 있습니다.
player.showConfirm(text: string, function(result))

// 플레이어에게 경고창을 보여주고, 플레이어가 OK를 눌렀을 때 동작하는 callback 함수를 작성할 수 있습니다.
player.showAlert(text: string, function())

// 위젯의 상/하/좌/우 여백을 화면 크기에 대한 % 비율로 정의하여 위젯을 표시합니다.
player.showWidgetResponsive(fileName:string, marginTop:number, marginRight:number, marginBottom:number, marginLeft:number)

//플레이어에게 웹 URL을 새 창이나 팝업 창으로 표시합니다.
player.openWebLink(url:string, popup:boolean);

// 지정된 align의 위치에 url 임베드창을 표시합니다.
player.showEmbed(url: string, align: string, width: number, height: number, hasBackdrop: boolean = true)

showCenterLabel

player.showCenterLabel(text: string, color: uint = 0xFFFFFF, bgColor: uint = 0x000000, offset: number = 0, time: number = 3000)

해당 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

파라미터

이름
타입
설명

text

String

라벨에 출력할 텍스트

color

Uint

출력할 글씨의 색을 지정합니다. (HexCode) 값을 입력하지 않을 경우, 흰색(0xFFFFFF)으로 적용됩니다. ➡️

bgColor

Uint

메시지가 출력되는 라벨의 배경색을 지정합니다. 값을 입력하지 않을 경우, 검은색(0x000000)으로 적용됩니다.

offset

number

offset 값이 클수록 표시되는 위치가 화면 아래쪽 방향으로 가까워집니다. 값을 입력하지 않을 경우, 0으로 지정됩니다.

time

number

라벨 표시 시간 (ms), 기본 값 3000 ( 3초 )

예제

노란색 라벨을 2초간 출력해보기

App.onJoinPlayer.Add(function(player){
	player.showCenterLabel(`${player.name}님 환영합니다.`, 0x000000, 0xFFFF00, 500, 2000); // 노란색 배경, 검정색 글씨로 표시하기
});

showCustomLabel

player.showCustomLabel(text: string, color: number = 0xFFFFFF, bgColor: number = 0x000000, offset: number = 0, width = 100, opacity = 0.6, time: number = 3000, option: object = null);

모든 플레이어에게 지정된 위치에 text를 3초간 표시하는 함수입니다.

text 부분에 span 태그를 넣어 텍스트를 꾸밀 수 있습니다.

파라미터

이름
타입
설명

text

String

라벨에 출력할 텍스트 ( span 태그 허용 )

color

Uint

출력할 글씨의 색 (HexCode) 값을 입력하지 않을 경우, 흰색(0xFFFFFF)으로 적용됩니다. ➡️

bgColor

Uint

메시지가 출력되는 라벨의 배경색 값을 입력하지 않을 경우, 검은색(0x000000)으로 적용됩니다.

offset

number

offset 값이 클수록 표시되는 위치가 화면 아래쪽 방향으로 가까워집니다. 값을 입력하지 않을 경우, 0으로 지정됩니다.

width

number

라벨의 너비를 n%로 설정하는 값 입니다. (기본 값 100)

opacity

number

라벨의 배경 투명도를 설정하는 값 입니다. (기본 값 0.6, 범위 0 ~ 1)

time

number

라벨 표시 시간 (ms), 기본 값 3000 ( 3초 )

옵션

이름
타입
설명

key

String

라벨에 키 값을 할당하여, 서로 다른 키 값을 가진 라벨은 동시에 표시할 수 있습니다.

borderRadius

String

라벨의 모서리에 둥글기를 설정할 수 있습니다. ex) borderRadius: "8px"

fontOpacity

boolean

false로 설정시 폰트에 투명도가 적용되지 않습니다.

padding

String

라벨 내부에 padding 값을 지정 할 수 있습니다. ex) padding: "8px"

예제

커스텀 라벨 예제 코드 페이지를 참고해주세요

showWidget

player.showWidget(fileName: string, align: string, width: number, height: number): ScriptWidget

해당 플레이어에게 지정된 align의 위치에 해당 html파일을 위젯으로 불러오는 함수입니다.

파라미터

이름
타입
설명

fileName

String

불러올 파일의 이름

align

String

위젯을 표시할 위치 ’popup’, ‘sidebar’, ‘top’, ‘topleft’, ‘topright’, ‘middle’, ‘middleleft’, ‘middleright’, ‘bottom’, ‘bottomleft’, ‘bottomright’

width height

number

위젯을 표시할 영역의 가로, 세로 크기(px)

예제

초성퀴즈 위젯 따라해보기

1KB
예제_showWidget(player).zip
archive
let _widget = null;
// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	_widget = player.showWidget("widget.html", "top", 200, 300); // 화면 상단, 200x300 영역에 위젯을 보여줌
	_widget.sendMessage({
		timer: 15,
		answer: "ㅅㅍㅋ",
	});
});

showBuyAlert

player.showBuyAlert(itemName: string, price: number, callback: function, payToSpaceOwner: Boolean, option: object)

플레이어에게 구매 위젯을 표시하고, 구매 후 동작하는 콜백함수를 작성할 수 있습니다.

소모된 ZEM은 앱 제작자에게 돌아가며, 내 후원 내역 페이지에서 내역을 확인 할 수 있습니다.

ZEM 정산 관련 내용은 정산가이드 페이지에서 확인하실 수 있습니다.

파라미터

이름
타입
설명

itemName

String

구매창에 표시할 아이템의 이름

price

number

아이템의 가격 (화폐단위: ZEM)

callback

function

구매 성공시 동작할 콜백함수

구매 성공 여부(success)와 구매정보(buyAlertResult) 데이터를 전달 받으며, buyAlertResult는 환불 기능에 사용됩니다.

payToSpaceOwner

Boolean

기본 값은 false로 설정되며 false인 경우 앱 소유자에게 수익이 전달되고,

true인 경우 맵 소유자에게 수익이 전달됩니다.

option

Object

다음 옵션들을 설정 할 수 있습니다. message : 구매창에 표시할 텍스트를 설정 할 수 있습니다.

timer : 구매창을 표시할 시간(ms)을 설정할 수 있습니다.

예제

구매정보 저장 및 환불 기능 예제

const itemName = "ITEM";

// Q를 누르면 동작하는 함수 - 아이템을 구매하고 player.storage에 구매정보 저장하기
App.addOnKeyDown(81, function (player) {
	let pStorage = JSON.parse(player.storage);
	if (!player.tag) {
		player.tag = {};
	}
	if (pStorage == null) {
		pStorage = {};
	}
	//이미 아이템을 구매했다면 메시지 출력
	if (pStorage[itemName]) {
		player.showCenterLabel(`${itemName}을 이미 구매했습니다.`);
	} else {
		player.showBuyAlert(itemName, 0, function (success, buyAlertResult) {
			if (success) {
				App.sayToAll(`[정보] ${player.name}님이 ${itemName}을 구매했습니다!`);
				pStorage[itemName] = true;
				player.tag.buyAlertResult = buyAlertResult
				player.storage = JSON.stringify(pStorage);
				player.save();
			}
		}, 
		false,// false 인 경우 앱 소유자에게 수익전달, true이면 스페이스 소유자에게 수익 전달
		{
			message: `${itemName} custom message`,//message에 itemName에 해당하는 text가 있을 경우 강조되어 표시됨
			timer: 10000 // 10초 - 구매창 표시시간(ms)
		}
		);
	}
});

// W를 누르면 동작하는 함수 - 환불 기능
App.addOnKeyDown(87, function (player) {
	let pStorage = JSON.parse(player.storage);
	if( pStorage && player.tag.buyAlertResult )
	{	
		if( player.tag.buyAlertResult.Refund() )
		{
			App.sayToAll("===== refund success!");
			pStorage[itemName] = false;
		}
		else {
			App.sayToAll("===== refund failed");
		}
		player.tag.buyAlertResult = null;
		player.storage = JSON.stringify(pStorage);
		player.save();
	}
})
구매위젯 예시
timer와 message 옵션을 설정한 경우

hideBuyAlert

player.hideBuyAlert()

플레이어의 구매 위젯을 닫습니다.

파라미터

없음

sendMessage

player.sendMessage(text: string, color: uint = 0xFFFFFF)

유저 개인에게 채팅 메시지를 보내는 함수입니다.

파라미터

이름
타입
설명

text

String

라벨에 출력할 텍스트

color

Uint

출력할 글씨의 색을 지정합니다. (HexCode) 값을 입력하지 않을 경우, 흰색(0xFFFFFF)으로 적용됩니다. ➡️

예제

플레이어 개인에게만 보이는 환영메시지 출력하기.


App.onJoinPlayer.Add(function(player){
    player.sendMessage(`${player.name}님 어서오세요!\nhttps://docs-kr.zep.us/ 링크 클릭시 가이드로 연결됩니다.`,0xffffff);
});

showPrompt

player.showPrompt(title: string, function(inputText), option = {})

플레이어에게 입력창을 보여주고, 플레이어의 응답에 따라 동작하는 callback 함수를 작성할 수 있습니다.

파라미터

이름
타입
설명

title

String

입력창의 타이틀

inputText

String

플레이어가 입력한 텍스트

옵션

값을 입력하지 않아도 default 값이 적용됩니다.

이름
타입
설명

content

String

입력창 위에 출력할 텍스트 (Default: null)

confirmVariant

'primary' | 'alert'

confirm 버튼의 색상 (Default: "primary") - 'primary': 푸른색, - 'alert': 붉은색

cancelText

String

cancel 버튼의 텍스트 (Default: "취소")

confirmText

String

confirm 버튼의 텍스트 (Default: "확인")

placeholder

String

input placeholder 텍스트 (Default: null)

textType

'text' | 'password'

입력 타입 (Default: "text")

- 'text': 입력 값을 텍스트로 표시 - 'password': 입력 값을 *로 표시

예제

"1234"를 입력하는 경우 "Correct" 메시지 출력해보기

// Q키를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.q, function (player) {
    player.showPrompt("Password", function (inputText) {
            if (inputText == "1234") {
                player.showCenterLabel("Correct");
            } else {
                player.showCenterLabel("Incorrect");
            }
        },
        {
            content: 'Description', // 설명
            confirmVariant: 'primary', // 확인 버튼 색상 'primary' | 'alert'
            cancelText: 'custom cancel', // 취소 버튼 텍스트
            confirmText: 'custom confirm',// 확인 버튼 텍스트
            placeholder: 'Custom Placeholder',// 입력칸의 placeholder
            textType: 'password' // 입력 텍스트의 표시 형식 'text' | 'password'
        }
    );
});

showConfirm

player.showConfirm(text: string, function(result), option = {})

플레이어에게 확인창을 보여주고, 플레이어가 확인 버튼을 눌렀을 때 동작하는 callback 함수를 작성할 수 있습니다. cancel을 누를 경우에는 callback 함수가 동작하지 않습니다.

파라미터

이름
타입
설명

text

String

확인창에 출력할 텍스트

result

Boolean

플레이어가 OK를 누르는 경우 true

옵션

값을 입력하지 않아도 default 값이 적용됩니다.

이름
타입
설명

content

String

content 영역에 출력할 텍스트 (Default: null)

confirmVariant

'primary' | 'alert'

confirm 버튼의 색상 (Default: 'primary') - primary: 푸른색, - alert: 붉은색

cancelText

String

cancel 버튼의 텍스트 (Default: "취소")

confirmText

String

confirm 버튼의 텍스트 (Default: "확인")

예제

확인 버튼을 누른 경우 채팅창에 텍스트 출력하기

// Q 키를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.q, function (player) {
    player.showConfirm("Confirm", function (result) {
            if (result) {
                App.sayToAll("ok");
            }
        },
        {
            content: 'Description', // 설명
            confirmVariant: 'alert', // 확인 버튼 색상 'primary' | 'alert';
            cancelText: 'custom cancel', // 취소 버튼 텍스트
            confirmText: 'custom confirm',// 확인 버튼 텍스트
        }
    );
});

showAlert

player.showAlert(text: string, function(), option = {})

플레이어에게 경고창을 보여주고, 플레이어가 OK를 눌렀을 때 동작하는 callback 함수를 작성할 수 있습니다.

파라미터

이름
타입
설명

text

String

경고창에 출력할 텍스트

옵션

값을 입력하지 않아도 default 값이 적용됩니다.

이름
타입
설명

content

String

content 영역에 출력할 텍스트 (Default: null)

confirmText

String

confirm 버튼의 텍스트 (Default: "확인")

예제

버튼을 누른 경우 채팅창에 텍스트 출력하기

// Q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
    player.showAlert("Alert", function () {
            App.sayToAll("ok");
        },
        {
            content: 'Description', // 설명
            confirmText: 'custom confirm',// 확인 버튼 텍스트
        }
    );
});

showWidgetResponsive

player.showWidgetResponsive(fileName:string, marginTop:number, marginRight:number, marginBottom:number, marginLeft:number)

위젯의 상/하/좌/우 여백을 화면 크기에 대한 %비율로 정의하여 위젯을 표시합니다.

화면의 크기가 여백을 포함한 위젯 영역보다 작아질 경우, 위젯의 크기가 비례하여 작아집니다.

파라미터

이름
타입
설명

fileName

String

불러올 파일의 이름

margin top/left/right/bottom

String

상/하/좌/우 여백의 % 값

예제

화면 크기를 줄이는 경우 위젯의 크기 변화

16KB
showWidgetResponsive.zip
archive
App.onJoinPlayer.Add(function (player) {
	player.tag = {};
});

// Q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	player.tag.widget = player.showWidgetResponsive("result.html", 15, 15, 15, 15);
	player.tag.widget.onMessage.Add(function (player, data) {
		if (data.type == "close") {
			player.tag.widget.destroy();
			player.tag.widget = null;
		}
	});
});

openWebLink

player.openWebLink(url:string, popup:boolean=false)

플레이어에게 웹 URL을 새 창이나 팝업 창으로 표시합니다.

파라미터

이름
타입
설명

url

String

연결할 웹 url 주소

popup

boolean

true 인 경우, url 창을 팝업 형태로 표시합니다.

예제

openWebLink 팝업으로 여는 경우

// Q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	player.openWebLink("https://docs-kr.zep.us", true);
});
팝업으로 url 창을 표시

showEmbed

player.showEmbed(url: string, align: string, width: number, height: number, hasBackdrop: boolean = true)

해당 플레이어에게 지정된 align의 위치에 url 임베드 화면을 표시하는 함수입니다.

파라미터

이름
타입
설명

url

String

웹 url 주소

align

String

임베드를 표시할 위치 ‘sidebar’, ‘top’, ‘topleft’, ‘topright’, ‘middle’, ‘middleleft’, ‘middleright’, ‘bottom’, ‘bottomleft’, ‘bottomright’

width height

number

임베드 영역의 가로, 세로 크기(px)

hasBackdrop

boolean

true일 경우 임베드의 바깥 배경에 그림자를 표시합니다.

예제

url 임베드창 표시하기

// Q를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	player.showEmbed("https://youtu.be/ztuTrpXJyks", "middle", 900, 600, true);
});

showImageModal

player.showImage(url: string)

플레이어에게 입력한 이미지 주소에 해당하는 이미지를 표시합니다.

파라미터

이름
타입
설명

url

String

표시할이미지 url

예제

이미지 모달창 표시하기

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.Q, function (player) {
    player.showImageModal("https://cdn-static.zep.us/static/images/thumbnail.png");
});

showNoteModal

player.showNoteModal(text: string)

플레이어에게 텍스트 창을 보여주는 함수입니다.

파라미터

이름
타입
설명

text

String

표시할 텍스트

예제

텍스트 창 표시하기

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.Q, function (player) {
    player.showNoteModal("Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatibus autem nisi soluta commodi a eius distinctio facilis est ea ullam. Dolorum a quis, impedit nisi voluptates magni architecto odit amet.");
});

💻 Data Load Methods

Data Load Methods 함수 한 눈에 보기

// 지정한 이메일과 플레이어의 이메일을 비교
player.isEmail(email: string): boolean

// 플레이어가 서있는 구역이름을 호출
player.getLocationName(): string

isEmail

player.isEmail(email: string): boolean

해당 플레이어의 이메일이 파라미터 값과 같다면 true, 아니면 false를 리턴합니다.

파라미터

이름
타입
설명

email

String

비교할 이메일 텍스트

예제

플레이어의 이메일이 지정한 텍스트와 같은지 비교해보기

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
	let check = player.isEmail("[email protected]");
	App.sayToAll(`이메일 일치 여부: ${check}`)
})

getLocationName

player.getLocationName : string

플레이어가 서있는 지정 영역의 이름을 출력합니다.

지정 영역은 ‘맵에디터 > 타일효과’ 에서 설정 할 수 있습니다.

파라미터

  • 없음

예제

캐릭터가 서있는 타일의 영역이름 출력해보기

→ 지정 영역 설정이 안되있다면 공백으로 출력됩니다

// q 키를 누르면 동작하는 함수
// App.addOnKeyDown
App.addOnKeyDown(81,function(player){
	App.sayToAll(`현재 서있는 구역: ${player.getLocationName()}`)
})

🙍‍♂️ User Control

User Control 함수 한 눈에 보기

// 플레이어를 해당 좌표로 소환
player.spawnAt(tileX: int ,tileY: int, dir: int = 0)

// 플레이어를 해당 구역으로 소환
player.spawnAtLocation(name: string ,dir:int = 0)

// 플레이어를 해당 스페이스 해당 맵으로 이동시키기
player.spawnAtMap(spaceHashID string, mapHashID:string = null)

spawnAt

player.spawnAt(tileX: int ,tileY: int, dir: int = 0)

플레이어의 캐릭터를 tileX, tileY 좌표로 지정한 방향을 바라보게 이동시킵니다.

파라미터

이름
타입
설명

tileX tileY

number

플레이어를 이동시킬 x, y 좌표 값

dir

number

- 캐릭터가 바라볼 방향 • 왼쪽 : 1 • 위쪽 : 2 • 오른쪽 : 3 • 아래쪽 : 4 • 왼쪽위 : 5 • 왼쪽아래 : 6 • 오른쪽위: 7 • 오른쪽아래: 8

예제

입장하는 플레이어를 지정한 좌표로 이동시키기

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	player.spawnAt(5, 5, 1); // 플레이어를 5,5 위치로 왼쪽 방향을 바라보게 이동시키기
});

spawnAtLocation

player.spawnAtLocation(name: string, dir:int = 0)

플레이어의 캐릭터를 name에 해당하는 지정 영역으로 지정한 방향을 바라보게 이동시킵니다.

파라미터

이름
타입
설명

name

String

플레이어를 이동시킬 지정 영역의 이름

dir

number

- 캐릭터가 바라볼 방향 • 왼쪽 : 1 • 위쪽 : 2 • 오른쪽 : 3 • 아래쪽 : 4 • 왼쪽위 : 5 • 왼쪽아래 : 6 • 오른쪽위: 7 • 오른쪽아래: 8

예제

입장하는 플레이어를 지정 영역으로 이동시키기

⚠️ 같은 이름의 지정 영역이 여러 곳 있다면 해당 영역들 중 한 곳으로 랜덤 이동합니다.

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
// 플레이어를 "test"라는 이름의 지정영역으로 왼쪽 방향을 바라보게 소환하기
	player.spawnAtLocation("test", 1); 
});

spawnAtMap

player.spawnAtMap(spaceHashID string, mapHashID:string)

플레이어를 해당 스페이스 해당 맵으로 이동시킵니다.

파라미터

이름
타입
설명

spaceHashID

String

이동할 스페이스의 spaceHashID

mapHashID

String

이동할 맵의 mapHashID

예제

입장하는 플레이어를 ZEP 튜토리얼 맵으로 이동시키기 ( 스페이스와 맵 이해하기 )

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	// 플레이어를 ZEP 튜토리얼 맵으로 이동시키기
	player.spawnAtMap("65jeBA", "2YvXMJ");
});

setCameraTarget

[1] player.setCameraTarget( tileX: Number, tileY: Number, time: Number )

[2] player.setCameraTarget( key: String, time: Number )

[1] 플레이어의 시점을 지정된 좌표로 중심 이동시킵니다.

[2] 플레이어의 시점을 특정 오브젝트로 중심 이동시킵니다.

파라미터

이름
타입
설명

tileX

Number

x좌표

tileY

Number

y좌표

key

String

오브젝트의 키 값

time

Number

시점이 목표 지점까지 이동하는데 걸리는 시간(초)

예제

[1] 플레이어가 보고 있는 화면의 중심을 입력한 좌표로 이동 및 초기화 시키기

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.Q, function (player) {
    player.setCameraTarget(0, 0, 2); // 플레이어의 시점을 [0,0] 좌표로 2초동안 이동시킵니다.
    player.sendUpdated(); 
});

// W를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.W, function (player) {
    player.setCameraTarget("");
    player.sendUpdated();
});

[2] 플레이어의 시점을 오브젝트로 이동 및 초기화 시키기

16KB
setCameraTarget_object.zip
archive
예제 파일

let sprite = App.loadSpritesheet('blueman.png', 48, 64, {
    left: [5, 6, 7, 8, 9], // 좌방향 이동 이미지
    up: [15, 16, 17, 18, 19],
    down: [0, 1, 2, 3, 4],
    right: [10, 11, 12, 13, 14],
    dance: [20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],
    down_jump: [38],
    left_jump: [39],
    right_jump: [40],
    up_jump: [41],
},5);

App.onJoinPlayer.Add(function (player) {
    App.runLater(function () {
        Map.putObjectWithKey(player.tileX, player.tileY, sprite, {
            overlap: true,
            movespeed: 50,
            key: "TestBlueMan",
            useDirAnim: true
        });
        Map.moveObjectWithKey("TestBlueMan", Map.width - 1, player.tileY, false);
    }, 2);
});

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.Q, function (player) {
    player.setCameraTarget("TestBlueMan",1);
    player.sendUpdated();
});

// W를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.W, function (player) {
    player.setCameraTarget("");
    player.sendUpdated();
});

setEffectSprite

player.setEffectSprite(resource: ScriptDynamicResource, offsetX: Number, offsetY: Number, type: Number)

플레이어의 배경 또는 전경 이미지를 설정 할 수 있습니다.

파라미터

이름
타입
설명

resource

ScriptDynamicResource

스크립트에 로드한 이미지 객체

offsetX

Number

px 단위로 x 축 방향의 오프셋을 설정할 수 있는 속성

offsetY

Number

px 단위로 y 축 방향의 오프셋을 설정할 수 있는 속성

type

0 | 1

설정타입 - 0: 배경 설정 - 1 : 전경 설정

사용 가능한 이펙트 애니메이션 키 값

캐릭터의 움직임에 맞춰 애니메이션을 재생할 수 있도록 설정이 가능합니다. (참고 문서)

캐릭터의 움직임에 대응하는 애니메이션이 정의되어 있지 않은 경우 애니메이션이 재생되지 않습니다.

down_idle // 정면에서 대기 상태
down // 정면에서 이동 중
left_idle // 왼쪽 방향에서 대기 상태
left // 왼쪽 방향으로 이동 중
right_idle // 오른쪽 방향에서 대기 상태
right // 오른쪽 방향으로 이동 중
up_idle // 위쪽 방향에서 대기 상태
up // 위쪽  방향으로 이동 중
dance // 춤추는 모션
down_jump, // 정면에서 점프하는 모션
left_jump, // 왼쪽 방향으로 점프하는 모션
right_jump, // 오른쪽 방향으로 점프하는 모션
up_jump, // 위쪽 방향으로 점프하는 모션
down_sit, // 정면 방향으로 앉아있는 모션
left_sit, // 왼쪽 방향에서 앉아있는 모션
right_sit, // 오른쪽 방향에서 앉아있는 모션
up_sit // 후면에서 앉아있는 모션
down_attack // 정면 방향 공격 모션
left_attack // 왼쪽 방향 공격 모션
right_attack // 오른쪽 방향 공격 모션
up_attack // 위쪽 방향 공격 모션

예제

플레이어 배경이미지 설정해보기

2KB
setEffectSprite.zip
archive
예제 코드
let effect = App.loadSpritesheet(
	"effect_sprite.png",
	32,
	32,
	{
		down_idle: [0, 1, 2, 3],
		down_attack: [0, 1, 2, 3],
		down: [0, 1, 2, 3],

		left_idle: [4, 5, 6, 7],
		left_attack: [4, 5, 6, 7],
		left: [4, 5, 6, 7],

		right_idle: [8, 9, 10, 11],
		right_attack: [8, 9, 10, 11],
		right: [8, 9, 10, 11],

		up_idle: [0, 1, 2, 3],
		up_attack: [0, 1, 2, 3],
		up: [0, 1, 2, 3],

		dance: [0, 1, 2, 3],

		down_jump: [0],
		left_jump: [4],
		right_jump: [8],
		up_jump: [0],

		down_sit: [0, 1, 2, 3],
		left_sit: [4, 5, 6, 7],
		right_sit: [8, 9, 10, 11],
		up_sit: [0, 1, 2, 3],
	},
	5
);

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.Q, function (player) {
	player.setEffectSprite(effect, -32, -30, 0); // 배경 이미지 설정
	player.sendUpdated();
});

// W를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.W, function (player) {
	player.setEffectSprite(null); // 배경 이미지 삭제
	player.sendUpdated();
});

playEffectSprite

player.playEffectSprite(resource: ScriptDynamicResource, repeatNum, offsetX: Number, offsetY: Number)

플레이어에게 애니메이션 효과를 repeatNum 횟수만큼 반복 재생하고 사라지는 효과를 적용합니다.

파라미터

이름
타입
설명

resource

ScriptDynamicResource

스크립트에 로드한 이미지 객체

repeatNum

Number

애니메이션 재생을 반복할 횟수

offsetX

Number

px 단위로 x 축 방향의 오프셋을 설정할 수 있는 속성

offsetY

Number

px 단위로 y 축 방향의 오프셋을 설정할 수 있는 속성

예제

1회 재생 후 사라지는 이펙트 효과

2KB
playEffectSprite.zip
archive
// 단일 애니메이션으로 정의해 사용합니다.
let effect = App.loadSpritesheet("effect_sprite.png", 32, 32, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 5);

// Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.Q, function (player) {
	player.playEffectSprite(effect, 1, -32, -30);
});

disappearObject

player.disappearObject(key: String)

플레이어 개인 화면 상에서 key 값을 가지는 오브젝트를 사라지게하는 함수입니다.

파라미터

이름
타입
설명

key

String

사라지게할 오브젝트의 key 값

예제

오브젝트와 상호작용 시 개인에게 사라지게 하기

2KB
disappearObject.zip
archive
let blueman = App.loadSpritesheet("blueman.png");

App.onJoinPlayer.Add(function (player) {
	Map.putObjectWithKey(player.tileX + 1, player.tileY, blueman, {
		type: 21,
		impassable: true,
		key: "objectKey",
	});
});

App.onTriggerObject.Add(function (player, layerID, x, y, key) {
	if(key){
	    player.disappearObject(key);
	}
});

putIndividualObject

player.putObjectWithKey(x: number, y: number, dynamicResource: ScriptDynamicResource, option: JsValue)

지정한 좌표에 플레이어에게만 보이는 오브젝트를 설치하는 함수입니다. ( 기준 좌표: Left Top )

dynamicResource 파라미터에 null 입력시 해당 좌표의 오브젝트를 삭제할 수 있습니다.

관련 문서: 오브젝트 npcProperty

파라미터

이름
타입
설명

x, y

Number

오브젝트를 놓을 x, y 좌표

dynamicResource

ScriptDynamicResource

App.loadSpritesheet 함수로 로드한 이미지

옵션 (option)

값을 입력하지 않아도 Default 값이 적용됩니다

이름
타입
설명

key

String

오브젝트의 키 값 (Default: null)

moveSpeed

Number

오브젝트의 이동속도

(Default: 80)

useDirAnim

Boolean

방향을 인지해서 애니메이션을 재생하는 옵션 (Default: false)

impassable

Boolean

오브젝트 통과불가 옵션 (Default: false)

offsetX

Number

오브젝트 이미지의 배치 기준점의 X좌표 (Default: 0)

offsetY

Number

오브젝트 이미지의 배치 기준점의 Y좌표 (Default: 0)

npcProperty

Object

npcProperty 관련 내용은 를 참고해주세요. (Default: null)

topObject

Boolean

true 로 설정하면 오브젝트가 상단오브젝트로 설치됩니다. (Default: false)

예제

키 값을 가진 블루맨 오브젝트 생성해보기

let blueman = App.loadSpritesheet("blueman.png");
// q 키를 누르면 동작하는 함수
App.addOnKeyDown(81, function (player) {
	player.putIndividualObject(18, 6, blueman, {
		type:21, // zep-script 상호작용 오브젝트
		overlap: true, // 충돌 이벤트 감지
		movespeed: 100, // 이동속도, 기본값: 80
		key: "TestBlueMan", // 키 값
		impassalbe:true, // 통과 불가 오브젝트 설정
		useDirAnim: true, // 방향을 인지해서 애니메이션을 재생하는 옵션
        	offsetX: -8, // 오브젝트 이미지의 배치 기준점의 X좌표를 조정
              	offsetY: -32,// 오브젝트 이미지의 배치 기준점의 Y좌표를 조정
	});
});

// w 키를 누르면 동작하는 함수
App.addOnKeyDown(87, function (player) {
	// 오브젝트 삭제
	player.putIndividualObject(18, 6, null, {
		key: "TestBlueMan", // 키 값
	});
});

🔉 Sound Methods

Sound Methods 함수 한 눈에 보기

// 플레이어에게 사운드를 재생
player.playSound(fileName: string, loop: boolean = false)

// 플레이어에게 링크에 해당하는 사운드를 재생
player.playSoundLink(link: string, loop: boolean = false)

playSound

player.playSound(fileName: string, loop: boolean = false, overlap: boolean = false, key: string = "ambient", volume: number = 1)

해당 플레이어에게 사운드를 재생하는 함수입니다.

파라미터

이름
타입
설명

fileName

String

불러올 파일의 이름

loop

boolean

true: 반복 재생 false: 1회 재생

overlap

boolean

사운드 오버랩(겹침) 재생 가능 여부

key

string

재생하는 사운드를 식별하는 데 사용되는 문자열입니다. 기본 값은 "ambient"로 설정되어 있습니다.

volume

number

사운드의 볼륨을 조절하는 데 사용되는 숫자입니다. 값의 범위는 0에서 1까지이며, 0은 소리가 없음을 나타내고, 1은 최대 볼륨을 나타냅니다.

예제

입장음 설정해보기(파일)

20KB
join.mp3
// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	player.playSound("join.mp3",false, true, "join", 0.5);
});

playSoundLink

player.playSoundLink(link: string, loop: boolean = false, overlap: boolean = false, key: string = "ambient", volume: number = 1)

모든 플레이어에게 사운드를 재생하는 함수입니다.

💡 올바른 링크를 입력했는데 재생이 되지 않는 경우

CORS 정책을 위반한 경우일 가능성이 높습니다. CORS 정책을 맞출 수 없는 경우에는 playSoundLink 대신 음악 파일을 업로드 하여 playSound 함수를 사용하는 것을 권장 드립니다.

파라미터

이름
타입
설명

link

String

사운드 url

loop

boolean

true: 반복 재생 false: 1회 재생

overlap

boolean

사운드 오버랩(겹침) 재생 가능 여부

key

string

재생하는 사운드를 식별하는 데 사용되는 문자열입니다. 기본 값은 "ambient"로 설정되어 있습니다.

volume

number

사운드의 볼륨을 조절하는 데 사용되는 숫자입니다. 값의 범위는 0에서 1까지이며, 0은 소리가 없음을 나타내고, 1은 최대 볼륨을 나타냅니다.

예제

입장음 설정해보기(사운드 url)

// 플레이어가 입장할 때 실행
App.onJoinPlayer.Add(function (player) {
	player.playSoundLink("https://cdn-static-stage.zep.us/static/assets/sounds/new_donation_1.wav", false, true, "join", 0.5);
});

stopSound

player.stopSound(key: string)

key에 해당하는 사운드의 재생을 중지하는 함수입니다.

이름
타입
설명

key

string

중지하려는 사운드의 키 값

예제

재생 중인 사운드 중지 시키기 아래 예제 코드 실행 후 Q, W 키를 연속으로 입력해보세요.

//Q를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.q,(player)=>{
	player.playSoundLink("https://cdn-static-stage.zep.us/static/assets/sounds/new_donation_1.wav", false, true, "donation", 0.5);
});

//W를 누르면 동작하는 함수
App.addOnKeyDown(KeyCodeType.w, (player)=>{
    player.stopSound("donation")
});

💠 공통 Methods

공통 Methods 함수 한 눈에 보기

// 플레이어 필드값을 수정한 후 업데이트
player.sendUpdated()

// 플레이어 스토리지값을 저장
player.save()

sendUpdated

player.sendUpdated()

App, Player 관련 필드 값이 변경되면 변경 값을 적용하는 함수입니다.

파라미터

  • 없음

save

player.save()

App, Player storage 값이 변경되면 변경 값을 적용하는 함수입니다.

파라미터

  • 없음


구글 색상 선택 도구
구글 색상 선택 도구
구글 색상 선택 도구
문서