{{: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:게임패드_입력|게임패드 입력]]을 해보겠다.