#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
//////데이터 초기화 부분//////
winSize = Director::getInstance()->getWinSize();
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listener, this);
srand(time(NULL));
SimpleAudioEngine::getInstance()->playBackgroundMusic("Sounds/menu_bg.mp3");
missiles.clear();
initGameData();
//////초기화 함수 호출 부분//////
initBG();
initPlayer();
initTitle();
initGameOver();
return true;
}
void HelloWorld::initBG()
{
//화면 스크롤을 위한 parallax node
auto node = ParallaxNode::create();
this->addChild(node);
//스크롤 액션 설정
node->runAction(RepeatForever::create(Sequence::create(
MoveTo::create(30, Vec2(0, -720)),
Place::create(Vec2::ZERO),
NULL)));
//1배속 스크롤 배경
for (int i = 0; i < 2; i++)
{
auto spr = Sprite::create("BG/bg_back.png");
spr->setAnchorPoint(Vec2(0, 0));
node->addChild(spr, 0, Vec2(0, 1), Vec2(0, 720 * i));
}
//2배속 스크롤 배경
for (int i = 0; i < 3; i++)
{
auto spr = Sprite::create("BG/bg_middle.png");
spr->setAnchorPoint(Vec2(0, 0));
node->addChild(spr, 1, Vec2(0, 2), Vec2(0, 720 * i));
}
//4배속 스크롤 배경
for (int i = 0; i < 5; i++)
{
auto spr = Sprite::create("BG/bg_front.png");
spr->setAnchorPoint(Vec2(0, 0));
node->addChild(spr, 2, Vec2(0, 4), Vec2(0, 720 * i));
}
}
void HelloWorld::initTitle()
{
auto label = Label::createWithTTF("Tiny Flight", "Fonts/acknowtt.ttf", 140);
label->setTag(TAG_LABEL_TITLE);
//TTF는 setColor를 하게되면 글자와 outline 모두 해당색으로 변경됨. outline을 쓰기위해서는 setTextColor를 사용
label->setTextColor(Color4B::RED);
label->enableOutline(Color4B::WHITE, 3);
label->setPosition(Vec2(winSize.width / 2, winSize.height / 2));
this->addChild(label);
}
void HelloWorld::initGameData()
{
isTitle = true;
isPlayerDie = false;
isGameOver = false;
missiles.clear();
enemies.clear();
attacks.clear();
}
void HelloWorld::initPlayer()
{
auto spr = Sprite::create();
spr->setTag(TAG_SPRITE_PLAYER);
spr->setPosition(Vec2(winSize.width / 2, -50));
this->addChild(spr);
//plist없는 묶음texture를 사용한 애니메이션 구현
auto texture = TextureCache::getInstance()->addImage("Sprites/Spaceship.png");
auto animation = Animation::create();
animation->setDelayPerUnit(0.1f);
//등록한 그림파일 내에서 사용할 텍스쳐가 4열까지이고 한 그림의 크기가
for (int i = 0; i < 4; i++)
{
animation->addSpriteFrameWithTexture(texture, Rect(i * 96, 0, 96, 105));
}
auto animate = Animate::create(animation);
spr->runAction(RepeatForever::create(animate));
}
void HelloWorld::resetPlayer()
{
SimpleAudioEngine::getInstance()->playEffect("Sounds/boom.wav");
isPlayerDie = true;
auto player = (Sprite*)this->getChildByTag(TAG_SPRITE_PLAYER);
player->setVisible(false); //보이지않게 해줌
//폭발 이펙트
auto spr = Sprite::create();
spr->setPosition(player->getPosition());
this->addChild(spr);
auto animation = Animation::create();
animation->setDelayPerUnit(0.05f);
auto texture = TextureCache::getInstance()->addImage("Sprites/Explosion.png");
for (int i = 0; i < 11; i++)
{
//x는 모듈러, y는 나누기라고 생각하면 편하다
animation->addSpriteFrameWithTexture(texture, Rect(i % 5 * 160, i / 5 * 160, 160, 160));
}
auto animate = Animate::create(animation);
spr->runAction(Sequence::create(
animate,
RemoveSelf::create(),
DelayTime::create(1.0f),
CallFunc::create(CC_CALLBACK_0(HelloWorld::setGameOver,this)),
NULL));
this->unschedule(schedule_selector(HelloWorld::setMissile));
setGameOver();
}
void HelloWorld::setTitle()
{
isTitle = false;
auto label = (Label*)this->getChildByTag(TAG_LABEL_TITLE);
label->runAction(FadeOut::create(1.0f));
//타이틀이 종료된 후 플레이어 등장
SimpleAudioEngine::getInstance()->playBackgroundMusic("Sounds/bgm.mp3");
auto player = (Sprite*)this->getChildByTag(TAG_SPRITE_PLAYER);
player->setPosition(Vec2(winSize.width / 2, -50));
player->setVisible(true);
player->runAction(MoveBy::create(1.0f, Vec2(0, 250)));
//반복횟수에 -1을 주면 무한반복...?
this->scheduleUpdate();
this->schedule(schedule_selector(HelloWorld::setMissile), 0.1);
this->schedule(schedule_selector(HelloWorld::setEnemy), 1);
}
void HelloWorld::initGameOver()
{
auto label1 = Label::createWithTTF("GAME OVER", "Fonts/SAM_5C_27TRG.ttf", 120);
label1->setTag(TAG_LABEL_GAMEOVER1);
label1->setPosition(Vec2(winSize.width / 2, winSize.height / 2));
label1->setTextColor(Color4B::RED);
label1->setOpacity(0);
label1->setVisible(false);
this->addChild(label1, 1);
auto label2 = Label::createWithTTF("TOUCH TO START", "Fonts/SAM_5C_27TRG.ttf", 60);
label2->setTag(TAG_LABEL_GAMEOVER2);
label2->setPosition(Vec2(winSize.width / 2, winSize.height / 2-150));
label2->setTextColor(Color4B::GRAY);
label2->setOpacity(0);
label2->setVisible(false);
this->addChild(label2, 1);
CCLOG("initGameOver()");
}
void HelloWorld::setGameOver()
{
isGameOver = true;
auto label1 = (Label*)this->getChildByTag(TAG_LABEL_GAMEOVER1);
auto label2 = (Label*)this->getChildByTag(TAG_LABEL_GAMEOVER2);
label1->runAction(Sequence::create(Show::create(), FadeIn::create(1), NULL));
label2->runAction(Sequence::create(Show::create(), DelayTime::create(1), FadeIn::create(2), NULL));
}
void HelloWorld::resetGameOver()
{
//게임오버 라벨들 모두 숨겨줌
//플레이어 위치 원위치
auto label1 = (Label*)this->getChildByTag(TAG_LABEL_GAMEOVER1);
label1->setOpacity(0);
label1->setVisible(false);
auto label2 = (Label*)this->getChildByTag(TAG_LABEL_GAMEOVER2);
label2->setOpacity(0);
label2->setVisible(false);
for (SpriteEnemy* enemy : enemies)
{
this->removeChild(enemy);
}
for (Sprite* attack : attacks)
{
this->removeChild(attack);
}
for (Sprite* missile : missiles)
{
this->removeChild(missile);
}
this->unscheduleAllSelectors();
initGameData();
setTitle();
}
bool HelloWorld::onTouchBegan(Touch* touch, Event* unused_event)
{
//타이틀화면일때는 터치 불가능
if (isTitle)
{
setTitle();
return true;
}
else if (isGameOver)
{
resetGameOver();
return true;
}
if (!isPlayerDie)
{
auto spr = (Sprite*)this->getChildByTag(TAG_SPRITE_PLAYER);
//터치했을 때, 이미 동작중인 액션이 있다면
if (spr->getActionByTag(TAG_ACTION_PLAYER_MOVE))
{
//해당 동작을 멈춤
spr->stopActionByTag(TAG_ACTION_PLAYER_MOVE);
}
Vec2 touchPos = touch->getLocation();
Vec2 pos = spr->getPosition();
//아래와 같이 시간값을 고정으로 주면, 거리가 멀수록 빠르고 가까울수록 느리게가는 현상 발생
//spr->runAction(MoveTo::create(1.0f, touchPos));
//그렇기 때문에, 피타고라스의 정리를 이용해 현위치-터치위치간 거리를 구해서
//시간을 나눠줌으로써 거리에 관계없이 일정한 속도로 움직이도록 해준다.
float distance = sqrtf((touchPos.x - pos.x)*(touchPos.x - pos.x) + (touchPos.y - pos.y)*(touchPos.y - pos.y));
//단, 이렇게 구한 값은 픽셀값이므로 값이 크기때문에 적당히 줄여서 계산해준다.
auto action = MoveTo::create(distance / 500, touchPos);
//이미 이동중 반복 입력시 해당 동작을 중지하고 다음 위치로 옮기기위한 액션 태그 설정
action->setTag(TAG_ACTION_PLAYER_MOVE);
spr->runAction(action);
return true;
}
else
return false;
}
void HelloWorld::setMissile(float delta)
{
SimpleAudioEngine::getInstance()->playEffect("Sounds/shoot.wav");
auto spr = (Sprite*)this->getChildByTag(TAG_SPRITE_PLAYER);
Vec2 pos = spr->getPosition();
auto missile = Sprite::create("Sprites/fire_0.png");
//비행기 본체보다 약간 위에서 미사일 생성
missile->setPosition(pos + Vec2(0, 20));
this->addChild(missile);
missiles.pushBack(missile);
missile->runAction(Sequence::create(
MoveBy::create(2.0f, Vec2(0, 720)),
CallFuncN::create(CC_CALLBACK_1(HelloWorld::delMissile, this)),
NULL));
}
void HelloWorld::delMissile(Ref* sender)
{
auto spr = (Sprite*)sender;
missiles.eraseObject(spr);
this->removeChild(spr);
}
void HelloWorld::setEnemy(float delta)
{
//화면 양극단에 나오는걸 방지하기위해 좌우 50씩 패딩을 줌
float x = 50 + rand() % (1280 - 50 * 2); //50~1230 내의좌표에서 생성
float y = winSize.height+50;
auto spr = SpriteEnemy::create();
spr->setTag(TAG_SPRITE_ENEMY);
spr->setPosition(Vec2(x, y));
spr->isAttack = false;
this->addChild(spr);
//랜덤한 모양의 적 생성
//3종류의 적을 각각의 확률에 따라 생성
int random = rand() % 100;
float speed; //스피드
if (random < 60)
{
spr->type = 1;
spr->hp = 5;
speed = 10;
}
else if (random < 90)
{
spr->type = 2;
spr->hp = 10;
spr->setScaleX(1.5f);
speed = 15;
}
else
{
spr->type = 3;
spr->type = 3;
spr->setScaleX(0.7f);
spr->setScaleY(1.4f);
speed = 20;
}
enemies.pushBack(spr);
auto animation = Animation::create();
animation->setDelayPerUnit(0.1f);
auto texture = TextureCache::getInstance()->addImage("Sprites/enemy.png");
for (int i = 0; i < 4; i++)
{
//animation->addSpriteFrameWithTexture(texture, Rect(i*85, 0, 85, 70));
animation->addSpriteFrameWithTexture(texture, Rect(i * 96, 0, 50, 72));
}
auto animate = Animate::create(animation);
spr->runAction(RepeatForever::create(animate));
spr->runAction(Sequence::create(
MoveBy::create(speed, Vec2(0, -(y + 100))),
CallFuncN::create(CC_CALLBACK_1(HelloWorld::delEnemy, this)),
NULL));
}
void HelloWorld::delEnemy(Ref* sender)
{
auto spr = (SpriteEnemy*)sender;
enemies.eraseObject(spr);
this->removeChild(spr);
}
void HelloWorld::update(float delta)
{
if (!isPlayerDie)
{
auto player = (Sprite*)this->getChildByTag(TAG_SPRITE_PLAYER);
for (SpriteEnemy* enemy : enemies)
{
//충돌 1) 유저 - 적 간 충돌 발생시
if ( player->getBoundingBox().intersectsRect(enemy->getBoundingBox()) )
{
//delEnemy((Ref*)enemy);
enemy->runAction( CallFuncN::create(CC_CALLBACK_1(HelloWorld::delEnemy, this)));
resetPlayer();
CCLOG("Player : Enemy");
break;
}
if (enemy->type == 2)
{
//화면 절반정도에 도달했고 공격한 전력이 없을 경우
if ( !enemy->isAttack && enemy->getPositionY() < winSize.height * 2/3)
{
enemy->isAttack = true;
attackEnemy_2(enemy->getPosition());
}
}
else if (enemy->type == 3)
{
//주인공과 일직선상에 위치해있을때
if (!enemy->isAttack && player->getPositionX() > enemy->getPositionX() - 20 && player->getPositionX() < enemy->getPositionX() + 20)
{
enemy->isAttack = true;
attackEnemy_3(enemy->getPosition());
}
}
//충돌 2) 미사일 - 적 충돌 발생시
for (Sprite* missile : missiles)
{
if (enemy->getBoundingBox().intersectsRect(missile->getBoundingBox()))
{
enemy->runAction(CallFuncN::create(CC_CALLBACK_1(HelloWorld::delEnemy, this)));
missile->runAction(CallFuncN::create(CC_CALLBACK_1(HelloWorld::delMissile, this)));
CCLOG("missile : Enemy");
}
}
}
//충돌 3) 플레이어 - 공격 충돌 발생시
for (Sprite* attack : attacks)
{
if (player->getBoundingBox().intersectsRect(attack->getBoundingBox()) )
{
attack->runAction(CallFuncN::create(CC_CALLBACK_1(HelloWorld::delAttack, this)));
resetPlayer();
break;
CCLOG("Player : attack");
}
}
}
}
void HelloWorld::attackEnemy_2(Vec2 pos)
{
SimpleAudioEngine::getInstance()->playEffect("Sounds/enemy_shoot.wav");
for (int i = 0; i < 5; i++)
{
auto spr = Sprite::create("Sprites/fire_1.png");
spr->setPosition(pos);
this->addChild(spr);
attacks.pushBack(spr);
spr->runAction(Sequence::create(
MoveBy::create(3.5f, Vec2(-400+i*200, -winSize.height)),
CallFuncN::create(CC_CALLBACK_1(HelloWorld::delAttack, this)),
NULL));
}
}
void HelloWorld::attackEnemy_3(Vec2 pos)
{
SimpleAudioEngine::getInstance()->playEffect("Sounds/enemy_shoot.wav");
for (int i = 0; i < 3; i++)
{
auto spr = Sprite::create("Sprites/fire_1.png");
spr->setPosition(pos-Vec2(0, 20));
this->addChild(spr);
attacks.pushBack(spr);
spr->runAction(Sequence::create(
DelayTime::create(0.05f*i),
MoveBy::create(1.5f, Vec2(0, -winSize.height)),
CallFuncN::create(CC_CALLBACK_1(HelloWorld::delAttack, this)),
NULL));
}
}
void HelloWorld::delAttack(Ref* sender)
{
auto spr = (Sprite*)sender;
attacks.eraseObject(spr);
this->removeChild(spr);
}