스폰되는 물고기들을 클릭하면 점수를 추가하고, 아래로 떨어지면 라이프가 감소하도록 한다.
물고기를 클릭하거나 바닥에 떨어트렸을 경우 물고기를 비활성화하고 오브젝트 풀에 반납하도록 한다.
1.Status UI 배치
먼저 현재 점수, 현재 라이프, 기존 최고 점수를 표시할 UI를 추가한다.
크롬 디버거를 통해 여러 디바이스에서 게임을 실행하면 iPad 시리즈에서는 화면 양 옆과 UI가 잘리는 것을 볼 수 있다.
캔버스 속성을 Fit Height에 맞춰놓았기 때문이다.
이런 경우에는 StatusUI 노드에는 Widget 컴포넌트를 추가하고 패딩을 설정한다. 유니티의 RectTransform의 Anchor와 비슷한 역할을 한다.
StatusUI 아래의 UI 노드들은 항상 화면 왼쪽으로부터 간격을 띄우고 위치하도록 한다.
Target은 기준으로 할 캔버스를 가리킨다. None으로 해두면 부모 노드를 가리킨다.
이렇게 위젯을 설정하면 iPad 같이 가로 비율이 짧은 해상도에서 양 옆이 잘려도 정상적인 위치에 UI가 표시되지만
가로 비율이 긴 일반 스마트폰 해상도에서는 사진과 같이 화면 밖을 벗어나서 그려진다.
타겟이 가리키는 캔버스는 저 검은색 여백을 포함하고, 그 캔버스 영역을 기준으로 위젯의 패딩이 적용되기 때문인듯하다.
하지만 타겟을 백그라운드 이미지 노드로 설정해도 달라지는건 없다.
검은색 여백을 포함하지 않는 영역을 위젯의 타겟으로 잡는 방법은 더 알아봐야할 것 같다.
때문에 실행 환경이 태블릿인지 스마트폰인지에 따라서, 또는 직접 비율을 계산해서 양옆이 잘리게되는 비율인지 체크해서
Widget 컴포넌트를 활성화할지 스크립트로 분기를 하는게 최선인것 같다.
예를 들어, canvas.width / canvas.height > screen.width / canvas.height 일때만 활성화하는 것이다.
관련 API는 구글링하면 나오는데, 빌드를 뽑지 않고 크롬 디버거에서 디바이스별로 스크린 사이즈를 구하는 방법은 모르겠다.
그리고 Fit Width로 설정한 게임의 경우는 위아래가 잘릴 때 똑같은 문제가 생긴다.
2. Status 초기화
@property(cc.Label)
scoreLabel:cc.Label = null;
@property(cc.Label)
lifeLabel:cc.Label = null;
@property(cc.Label)
bestScoreLabel:cc.Label = null;
score:number;
life:number;
best_score:number;
씬에 추가한 라벨들은 스크립트에서 제어하기 위해 연결해준다. 그리고 각 라벨에 대응되는 변수도 만들어준다.
start() {
this.InitFishPool();
this.InitStatus();
}
InitStatus()
{
this.score = 0;
this.life = 3;
this.best_score = cc.sys.localStorage.getItem("best_score");
this.scoreLabel.string = "X" + this.score;
this.lifeLabel.string = "X" + this.life;
this.bestScoreLabel.string = "X" + this.best_score;
}
GameScene이 시작되면 점수, 라이프, 최고점수 초기화와 함께 StatusUI도 업데이트한다.
cc.sys.localStorage는 유니티의 PlayerPrefs와 비슷한 로컬 저장소다.
주로 게임의 환경설정 세팅에 대한 정보 등을 저장할 때 이용한다.
주의할 점은, 유니티에서는 GetString이나 GetInt처럼 값을 가져오는 함수가 타입별로 존재해서
데이터 타입 오류를 컴파일 이전에 잡아낼 수 있지만
코코스 크리에이터는 자바스크립트라서 any 타입을 반환하는 GetItem 함수 하나만 존재한다.
3. 물고기 노드에 클릭 이벤트를 붙이고 Score 갱신하기
// Fish.ts
import GameScene from "./GameScene";
const {ccclass, property} = cc._decorator;
@ccclass
export default class Fish extends cc.Component {
public touchCallback:cc.ActionInstant;
start () {
this.node.on("touchend", function() {
var remove = cc.callFunc(() => { this.RemoveFish(true); });
var sequence = cc.sequence(remove, this.touchCallback);
this.node.runAction(sequence);
}, this);
}
public RemoveFish()
{
this.node.active = false;
this.node.stopAllActions();
}
}
Fish.ts 스크립트를 만들고 Fish 프리팹에 붙여준다.
물고기 노드를 터치하면 remove 액션과 touchCallback 콜백 액션을 묶은 시퀀스를 실행한다.
remove 액션은 Fish 스크립트의 멤버함수인 RemoveFish이고
touchCallback 콜백 액션은 GameScene의 함수(점수 증가 함수)를 실행할 것이다.
아예 GameScene 노드와 스크립트를 찾아서 해당 함수를 참조하는 방법도 있겠지만
Fish ↔ GameScene 양쪽에서 서로를 참조하는 것보다 Fish ← GameScene 구조가 나은 것 같아서 이렇게 작성했다.
InitFishPool()
{
for (var i = 0; i < this.fishPrefabs.length; i++)
{
var pool = [];
for (var j = 0; j < 10; j++)
{
var fish = cc.instantiate(this.fishPrefabs[i]);
fish.active = false;
// Fish의 touchCallback 초기화
fish.getComponent(Fish).touchCallback = cc.callFunc(() => {
this.AddScore(1);
});
this.fishLayout.node.addChild(fish);
pool.push(fish);
}
this.fishPool.push(pool);
}
}
AddScore(score:number)
{
this.score += score;
this.scoreLabel.string = "X" + this.score;
}
Fish의 touchCallback 초기화는 오브젝트 풀을 만들면서 Fish 노드를 생성할 때 해준다.
여기까지 하면 위에서 떨어지는 물고기를 클릭했을 때, 물고기가 사라지고 점수가 업데이트된다.
사라진 물고기는 오브젝트 풀에 반납된다.
4. 물고기를 떨어트렸을 때 Life 갱신하기
SpawnFish(dt : number)
{
// 스폰 쿨타임 적용
this.spawnDelta += dt;
if (this.spawnDelta < this.spawnCoolTime) return;
// 물고기 오브젝트 풀에서 랜덤한 피쉬 꺼내오기
var fishNum = Math.floor(Math.random() * this.fishPrefabs.length);
var fish = this.GetFish(fishNum);
// 물고기의 시작 좌표를 랜덤한 위치로 초기화
fish.setPosition(this.GetRandomPosition());
// 이동 로직 실행
var startPos = fish.position;
var targetPos = new cc.Vec3(startPos.x, -this.fishLayout.node.getContentSize().height * 1.2, startPos.z);
var duration = Math.random() * 2 + 1.5;
cc.tween(fish)
.to(duration, {position:targetPos})
.call(() => {
// fish를 삭제하는 기존 코드는 RemoveFish 함수로 대체하고 DropFish 실행
fish.getComponent(Fish).RemoveFish();
this.DropFish();
})
.start();
// 스폰 쿨타임 초기화
this.spawnDelta = 0;
this.spawnCoolTime = Math.random() + 0.4;
}
DropFish()
{
this.life--;
this.lifeLabel.string = "X" + this.life;
}
기존에 물고기를 비활성화했던 코드는 Fish의 RemoveFish 함수를 실행하는 것으로 대체한다.
RemoveFish 함수는 물고기를 터치했을 때도 실행되는 함수다. 코드의 중복을 줄일 수 있다.
그리고 GameScene의 DropFish 함수를 실행하는데, 만약 물고기 터치로 인해 RemoveFish 함수가 실행된다면
this.node.StopAllActions에 의해서 이동 액션이 중지되고 DropFish는 실행되지 않을 것이다.
여기까지 하면 물고기가 터치하지 못하고 떨어트렸을 때 라이프가 감소하는 것을 확인할 수 있다.
'Cocos Creator' 카테고리의 다른 글
[예제 게임] GameScene #4 (오디오 매니저) (0) | 2021.06.04 |
---|---|
[예제 게임] GameScene #3 (게임 일시 정지) (0) | 2021.06.03 |
[예제 게임] GameScene #1 (오브젝트 풀링) (0) | 2021.06.01 |
[예제 게임] 씬 전환과 버튼 클릭 이벤트 (0) | 2021.05.31 |
[예제 게임] TitleScene 만들기 (해상도와 UI) (0) | 2021.05.31 |