{{:raylib:상속을이용하여아이템추가.png?600|상속을 이용하여 아이템 추가}}
~~stoggle_buttons~~
===== 스타팅 파일 =====
* [[raylib:flappybird:아이템을_스폰시키는_에이전트_만들기|아이템을 스폰시키는 에이전트 만들기]]에서 이어지는 포스팅이다.
* {{ :raylib:flappybird_8.zip |아이템을 스폰시키는 에이전트 만들기}} 파일이 스타팅파일이다.
* 생명력 아이템의 스프라이트 시트는 오픈게임아트의 [[https://opengameart.org/content/shining-coin-shining-health-shining-power-up-sprite-sheets|https://opengameart.org/content/shining-coin-shining-health-shining-power-up-sprite-sheets]]이다((그림의 크기가 너무 크기 때문에 사이즈를 조정해주어야 한다. 귀찮으면 내가 첨부해 놓은 완성 파일에서 리소스를 받아 써도 된다.)).
* 효과음역시 오픈게임아트에서 [[https://opengameart.org/content/level-up-sound-effects|레벨업 효과음]]을 구했다.
===== 코인 아이템 상속시키기 =====
==== 1. 목적 ====
이번에는 생명력을 높이는 아이템을 만들 것이다. 그런데 생명력을 높이는 아이템은 이미지와 효과를 주는 것만 다를 뿐, 나머지는 코인 아이템과 동일하다. 따라서 코인 아이템을 상속시키고 부가적인 것들만 파생 함수로 만들면 된다.
==== 2. coin.h ====
아래와 같이 코인을 상속받는 life라는 클래스를 만들었다.
#pragma once
#include "raylib.h"
class Coin
{
public:
Coin();
Coin(Texture2D* _image);
void Update();
void Draw();
int GetPosition();
bool GetCollision(Rectangle bRect);
int GetAddValue();
protected:
Vector2 pos; // 코인 위치
Texture2D* coinImage; // Texture의 주소값
Rectangle frameRec; // 보여줄 프레임
int currentFrame = 0;
int framesCounter = 0;
int framesSpeed = 16; // Number of spritesheet frames shown by second
int SPRITE_NUMBERS ; // 스프라이트 개수
int speed; // 스피드
int addValue; // 넘겨줄 값
};
class Life : public Coin
{
public :
Life(Texture2D* _image, int _sprite_numbers, int _speed, int _addValue);
};
여기서 주의할 것은, 부모클래스의 생성자인 Coin()을 반드시 선언과 정의해 줘야 한다는 것이다. 그렇지 않으면 오류가 나왔다.
왜 명시적으로만 선언해줘야 하는지는 잘 모르겠다.
==== 2. coin.cpp ====
#include "coin.h"
Coin::Coin()
{}
Coin::Coin(Texture2D* _image)
{
int x = GetScreenWidth();
int y = GetRandomValue(100, GetScreenHeight() - 100);
pos = {float(x), float(y)};
coinImage = _image;
currentFrame = 0;
framesCounter = 0;
framesSpeed = 16; // Number of spritesheet frames shown by second
SPRITE_NUMBERS = 31; // 스프라이트 개수
speed = 2; // 코인 스피드
addValue = 3;
frameRec = { 0.0f, 0.0f, (float)coinImage->width / SPRITE_NUMBERS, (float)coinImage->height };
}
int Coin::GetAddValue()
{
return addValue;
}
int Coin::GetPosition()
{
return (int)pos.x + (float)coinImage->width / SPRITE_NUMBERS; // 47은 코인의 너비이다.
}
bool Coin::GetCollision(Rectangle bRect)
{
Rectangle rect = {pos.x, pos.y, (float)coinImage->width / SPRITE_NUMBERS, (float)coinImage->height};
return CheckCollisionRecs(bRect, rect);
}
void Coin::Update()
{
framesCounter++;
if (framesCounter >= (60/framesSpeed)) // FPS를 60으로 설정했으므로 60을 프레임스피드로 나눈다. 그러면 1초당 몇번 돌릴지 결과 값이 나온다.
{
framesCounter = 0;
currentFrame++;
if (currentFrame > SPRITE_NUMBERS) currentFrame = 0;
frameRec.x = (float)currentFrame * (float)coinImage->width / SPRITE_NUMBERS;
}
pos.x -= speed;
}
void Coin::Draw()
{
DrawTextureRec(*coinImage, frameRec, pos, WHITE); // Draw part of the texture
}
Life::Life(Texture2D* _image, int _sprite_numbers, int _speed, int _addValue)
{
int x = GetScreenWidth();
int y = GetRandomValue(100, GetScreenHeight() - 100);
pos = {float(x), float(y)};
coinImage = _image;
currentFrame = 0;
framesCounter = 0;
framesSpeed = 8; // Number of spritesheet frames shown by second
SPRITE_NUMBERS = _sprite_numbers; // 스프라이트 개수
speed = _speed; // 코인 스피드
addValue = _addValue;
frameRec = { 0.0f, 0.0f, (float)coinImage->width / SPRITE_NUMBERS, (float)coinImage->height };
}
Life의 생성자만 새롭게 정의해줬다. Coin의 경우에는 생성자에 텍스쳐파일의 주소만 인자로 받았는데, Life는 스프라이트 수와 코인스피드, 그리고 더하려는 밸류의 값도 줘야 하므로 인자가 4개가 되었다.
기존에는 Init()함수로 아이템을 초기화해줬는데, 굳이 그렇게 할 필요 없이 생성할 때 초기화해주면 되므로 Init()함수는 제거하고 생성자에 인자를 넣는 방식으로 교체하였다. 기존 파일을 보는 사람은 이를 유의하자.
===== 아이템 에이전트에서 Life 호출하기 =====
사실 별반 다를바 없다. Coin의 뒤에 Life의 배열을 일정한 확률에 따라 생성하게 만들어 주자.
이번에는 new와 delete 지시어를 써서 동적으로 호출한 것에 유의하자.
items.cpp에서 Update()메서드의 일부 코드는 다음과 같다.
int spawn = GetRandomValue(0, 1000); // 코인이 발생할 확률
if (spawn < 2 && canSpawn == true) // 코인이 발생할 확률을 1000분의 10 즉 1%로 설정함
{
Coin* coin = new Coin(&CoinSprite);
coins.push_back(*coin);
delete coin;
}
int spawnlife = GetRandomValue(0, 1000); // 코인이 발생할 확률
if (spawnlife < 1 && canSpawn == true) // 코인이 발생할 확률을 1000분의 10 즉 1%로 설정함
{
Life* life = new Life(&LifeSprite, 8, 3, 1);
// life.Init(&LifeSprite, 8, 2, 1);
// life->Init();
lifes.push_back(*life);
delete life;
}
나머지 충돌처리와 Draw()메서드는 Coin에서 만들었떤 것을 그대로 Life에서도 가져다 쓰면 된다.
이를테면 Life 클래스서는 따로 Draw()메서드를 선언하지 않았지만 Coin클래스를 상속받으므로 다음과 같이 자연스레 쓸 수 있다.
void Items::Draw()
{
if (coins.size() >0 )
{
for (unsigned int i = 0;i< coins.size();i++)
{
coins[i].Draw();
}
}
if (lifes.size() >0 )
{
for (unsigned int i = 0;i< lifes.size();i++)
{
lifes[i].Draw();
}
}
}
===== 결론 =====
{{:raylib:상속을이용하여아이템추가.png?600|상속을 이용하여 아이템 추가}}
상속을 이용하면 공통된 로직을 반복할 필요가 없으므로 코딩 속도가 매우 빨라진다.
이런 식으로 아이템을 여러개 만들 수 있을 것이다.
지금까지의 소스파일은 아래에 첨부한다.
{{ :raylib:flappybird_9.zip |상속을 이용하여 다른 아이템 만들기}}
다음번에는 [[raylib:flappybird:게임패드_입력|게임패드 입력]]을 해보겠다.