From 9ba885222f4a63c4d63d34b52f01a69bedae6710 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Mon, 16 Dec 2019 12:33:23 +0000 Subject: [PATCH] Added SHMUP Voideo --- Videos/OneLoneCoder_PGE_ShootEmUp.cpp | 484 ++++++++++++++++++++++++++ 1 file changed, 484 insertions(+) create mode 100644 Videos/OneLoneCoder_PGE_ShootEmUp.cpp diff --git a/Videos/OneLoneCoder_PGE_ShootEmUp.cpp b/Videos/OneLoneCoder_PGE_ShootEmUp.cpp new file mode 100644 index 0000000..cf37353 --- /dev/null +++ b/Videos/OneLoneCoder_PGE_ShootEmUp.cpp @@ -0,0 +1,484 @@ +/* + Live 100K Code Party! Code-It-Yourself: SHMUP + "It's done... 2019 is done..." - javidx9 + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018-2019 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Relevant Video: https://youtu.be/CqDfZEX0Yhc + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + https://www.youtube.com/javidx9extra + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Patreon: https://www.patreon.com/javidx9 + Homepage: https://www.onelonecoder.com + + Community Blog: http://community.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019 +*/ + +#define OLC_PGE_APPLICATION +#include "olcPixelGameEngine.h" + +// IMPORTANT!! Requires sprites not provided in repo! +// Sprites are 48x48 pixels. + + +#include +#include +#include + +class Example : public olc::PixelGameEngine +{ +public: + Example() + { + sAppName = "100K Live Special - SHMUP"; + } + + olc::Sprite* sprPlayer = nullptr; + olc::Sprite* sprEnemy[3]; + + olc::vf2d vPlayerPos; + float fPlayerSpeed = 100.0f; + float fScrollSpeed = 60.0f; + float fPlayerShipRad = 24 * 24; + float fPlayerHealth = 1000.0f; + float fPlayerGunTemp = 0.0f; + float fPlayerGunReload = 0.2f; + float fPlayerGunReloadTime = 0.0f; + + double dWorldPos = 0.0f; + + std::array aryStars; + + struct sBullet + { + olc::vf2d pos; + olc::vf2d vel; + bool remove = false; + }; + + struct sEnemy; + + struct sEnemyDefinition + { + double dTriggerTime; + uint32_t nSpriteID = 0; + float fHealth = 0.0f; + std::function funcMove; + std::function& bullets)> funcFire; + float offset = 0.0f; + }; + + struct sEnemy + { + olc::vf2d pos; + sEnemyDefinition def; + + std::array dataMove{ 0 }; + std::array dataFire{ 0 }; + + void Update(float fElapsedTime, float fScrollSpeed, std::list& bullets) + { + def.funcMove(*this, fElapsedTime, fScrollSpeed); + def.funcFire(*this, fElapsedTime, fScrollSpeed, bullets); + } + }; + + + olc::vf2d GetMiddle(const olc::Sprite *s) + { + return { (float)s->width / 2.0f, (float)s->height / 2.0f }; + } + + + std::list listSpawns; + std::list listEnemies; + std::list listEnemyBullets; + std::list listPlayerBullets; + std::list listStars; + std::list listFragments; + +public: + bool OnUserCreate() override + { + // Load resources + sprPlayer = new olc::Sprite("gfx//100k_player.png"); + sprEnemy[0] = new olc::Sprite("gfx//100k_enemy1.png"); + sprEnemy[1] = new olc::Sprite("gfx//100k_enemy2.png"); + sprEnemy[2] = new olc::Sprite("gfx//100k_enemy3.png"); + + // Generate Star Map + for (auto& s : aryStars) s = { (float)(rand() % ScreenWidth()), (float)(rand() % ScreenHeight()) }; + + // MOVEMENT PATTERN FUNCTIONS + auto Move_None = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed) + { + e.pos.y += fScrollSpeed * fElapsedTime; + }; + + auto Move_StraightFast = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed) + { + e.pos.y += 3.0f * fScrollSpeed * fElapsedTime; + }; + + auto Move_StraightSlow = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed) + { + e.pos.y += 0.5f * fScrollSpeed * fElapsedTime; + }; + + auto Move_SinusoidNarrow = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed) + { + e.dataMove[0] += fElapsedTime; + e.pos.y += 0.5f * fScrollSpeed * fElapsedTime; + e.pos.x += 50.0f * cosf(e.dataMove[0]) * fElapsedTime; + }; + + auto Move_SinusoidWide = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed) + { + e.dataMove[0] += fElapsedTime; + e.pos.y += 0.5f * fScrollSpeed * fElapsedTime; + e.pos.x += 150.0f * cosf(e.dataMove[0]) * fElapsedTime; + }; + + // FIRING PATTERN FUNCTIONS + auto Fire_None = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed, std::list& bullets) + { + + }; + + auto Fire_StraightDelay2 = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed, std::list& bullets) + { + constexpr float fDelay = 0.2f; + e.dataFire[0] += fElapsedTime; + if (e.dataFire[0] >= fDelay) + { + e.dataFire[0] -= fDelay; + sBullet b; + b.pos = e.pos + olc::vf2d((float)sprEnemy[e.def.nSpriteID]->width / 2.0f, (float)sprEnemy[e.def.nSpriteID]->height); + b.vel = { 0.0f, 180.0f }; + bullets.push_back(b); + } + }; + + auto Fire_CirclePulse2 = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed, std::list& bullets) + { + constexpr float fDelay = 0.2f; + constexpr int nBullets = 10; + constexpr float fTheta = 2.0f * 3.14159f / (float)nBullets; + e.dataFire[0] += fElapsedTime; + if (e.dataFire[0] >= fDelay) + { + e.dataFire[0] -= fDelay; + for (int i = 0; i < nBullets; i++) + { + sBullet b; + b.pos = e.pos + GetMiddle(sprEnemy[e.def.nSpriteID]); + b.vel = { 180.0f * cosf(fTheta * i), 180.0f * sinf(fTheta * i)}; + bullets.push_back(b); + } + } + }; + + auto Fire_DeathSpiral = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed, std::list& bullets) + { + constexpr float fDelay = 0.01f; + e.dataFire[0] += fElapsedTime; + if (e.dataFire[0] >= fDelay) + { + e.dataFire[1] += 0.1f; + e.dataFire[0] -= fDelay; + sBullet b; + b.pos = e.pos + GetMiddle(sprEnemy[e.def.nSpriteID]); + b.vel = { 180.0f * cosf(e.dataFire[1]), 180.0f * sinf(e.dataFire[1]) }; + bullets.push_back(b); + + } + }; + + auto Fire_DeathSpiralCircle = [&](sEnemy& e, float fElapsedTime, float fScrollSpeed, std::list& bullets) + { + constexpr float fDelay = 0.2f; + constexpr int nBullets = 100; + constexpr float fTheta = 2.0f * 3.14159f / (float)nBullets; + e.dataFire[0] += fElapsedTime; + if (e.dataFire[0] >= fDelay) + { + e.dataFire[0] -= fDelay; + e.dataFire[1] += 0.1f; + for (int i = 0; i < nBullets; i++) + { + sBullet b; + b.pos = e.pos + GetMiddle(sprEnemy[e.def.nSpriteID]); + b.vel = { 180.0f * cosf(fTheta * i + e.dataFire[1]), 180.0f * sinf(fTheta * i + e.dataFire[1]) }; + bullets.push_back(b); + } + } + }; + + // Construct level + listSpawns = + { + {100.0, 0, 3.0f, Move_None, Fire_CirclePulse2, 0.25f}, + {100.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.75f}, + {120.0, 1, 3.0f, Move_SinusoidNarrow, Fire_CirclePulse2, 0.50f}, + + {200.0, 2, 3.0f, Move_SinusoidWide, Fire_CirclePulse2, 0.30f}, + {200.0, 2, 3.0f, Move_SinusoidWide, Fire_CirclePulse2, 0.70f}, + + {500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.2f}, + {500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.4f}, + {500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.6f}, + {500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.8f}, + + {550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.1f}, + {550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.3f}, + {550.0, 0, 3.0f, Move_StraightSlow, Fire_DeathSpiralCircle, 0.5f}, + {550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.7f}, + {550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.9f}, + + {600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.2f}, + {600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.4f}, + {600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.6f}, + {600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.8f}, + + {1100.0, 0, 3.0f, Move_None, Fire_CirclePulse2, 0.25f}, + {1100.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.75f}, + {1120.0, 1, 3.0f, Move_SinusoidNarrow, Fire_CirclePulse2, 0.50f}, + + {1200.0, 2, 3.0f, Move_SinusoidWide, Fire_CirclePulse2, 0.30f}, + {1200.0, 2, 3.0f, Move_SinusoidWide, Fire_CirclePulse2, 0.70f}, + + {1500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.2f}, + {1500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.4f}, + {1500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.6f}, + {1500.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.8f}, + + {1550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.1f}, + {1550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.3f}, + {1550.0, 0, 3.0f, Move_StraightSlow, Fire_DeathSpiralCircle, 0.5f}, + {1550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.7f}, + {1550.0, 0, 3.0f, Move_StraightFast, Fire_DeathSpiral, 0.9f}, + + {1600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.2f}, + {1600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.4f}, + {1600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.6f}, + {1600.0, 0, 3.0f, Move_StraightFast, Fire_StraightDelay2, 0.8f}, + }; + + + vPlayerPos = { (float)ScreenWidth() / 2, (float)ScreenHeight() / 2 }; + + return true; + } + + bool OnUserUpdate(float fElapsedTime) override + { + // AutoScroll World + dWorldPos += fScrollSpeed * fElapsedTime; + + // Scroll Player Object + vPlayerPos.y += fScrollSpeed * fElapsedTime; + + // Handle Player Input + if (GetKey(olc::W).bHeld) vPlayerPos.y -= (fPlayerSpeed + fScrollSpeed) * fElapsedTime; + if (GetKey(olc::S).bHeld) vPlayerPos.y += (fPlayerSpeed - fScrollSpeed) * fElapsedTime; + if (GetKey(olc::A).bHeld) vPlayerPos.x -= fPlayerSpeed * fElapsedTime * 2.0f; + if (GetKey(olc::D).bHeld) vPlayerPos.x += fPlayerSpeed * fElapsedTime * 2.0f; + + // Clamp Player to screen + if (vPlayerPos.x <= 0) vPlayerPos.x = 0; + if (vPlayerPos.x + (float)sprPlayer->width >= ScreenWidth()) vPlayerPos.x = (float)ScreenWidth() - sprPlayer->width; + if (vPlayerPos.y <= 0) vPlayerPos.y = 0; + if (vPlayerPos.y + (float)sprPlayer->height >= ScreenHeight()) vPlayerPos.y = (float)ScreenHeight() - sprPlayer->height; + + // Player Weapon Fire + bool bCanFire = false; + fPlayerGunReloadTime -= fElapsedTime; + if (fPlayerGunReloadTime <= 0.0f) + { + bCanFire = true; + } + + fPlayerGunTemp -= fElapsedTime * 10.0f; + if (fPlayerGunTemp < 0) fPlayerGunTemp = 0; + if (GetMouse(0).bHeld) + { + if (bCanFire && fPlayerGunTemp < 80.0f) + { + fPlayerGunReloadTime = fPlayerGunReload; + fPlayerGunTemp += 5.0f; + if (fPlayerGunTemp > 100.0f) fPlayerGunTemp = 100.0f; + listPlayerBullets.push_back({vPlayerPos + olc::vf2d((float)sprPlayer->width / 2.0f, 0.0f), {0.0f, -200.0f} }); + } + } + + // Update Player Bullets + for (auto& b : listPlayerBullets) + { + // Position Bullet + b.pos += (b.vel + olc::vf2d(0.0f, fScrollSpeed)) * fElapsedTime; + + // Check Enemies Vs Player Bullets + for (auto& e : listEnemies) + { + if ((b.pos - (e.pos + olc::vf2d(24.0f, 24.0f))).mag2() < fPlayerShipRad) + { + // Enemy has been hit + b.remove = true; + e.def.fHealth -= 1.0f; + + // Trigger explosion + if (e.def.fHealth <= 0) + { + for (int i = 0; i < 500; i++) + { + float angle = ((float)rand() / (float)RAND_MAX) * 2.0f * 3.14159f; + float speed = ((float)rand() / (float)RAND_MAX) * 200.0f + 50.0f; + listFragments.push_back({ + e.pos + GetMiddle(sprEnemy[e.def.nSpriteID]), + { cosf(angle)*speed, sinf(angle)*speed }}); + } + } + } + } + } + + + // Perform spawn check + while(!listSpawns.empty() && dWorldPos >= listSpawns.front().dTriggerTime) + { + sEnemy e; + e.def = listSpawns.front(); + e.pos = + { + listSpawns.front().offset * (float)(ScreenWidth() - sprEnemy[e.def.nSpriteID]->width), + 0.0f - sprEnemy[e.def.nSpriteID]->height + }; + listSpawns.pop_front(); + listEnemies.push_back(e); + } + + // Update Enemies + for (auto& e : listEnemies) e.Update(fElapsedTime, fScrollSpeed, listEnemyBullets); + + // Update Enemy Bullets + for (auto& b : listEnemyBullets) + { + // Position Bullet + b.pos += (b.vel + olc::vf2d(0.0f, fScrollSpeed)) * fElapsedTime; + + // Check Player Vs Enemy Bullets + if ((b.pos - (vPlayerPos + olc::vf2d(24.0f, 24.0f))).mag2() < fPlayerShipRad) + { + b.remove = true; + fPlayerHealth -= 10.0f; + } + } + + // Update Fragments + for(auto& f : listFragments) f.pos += (f.vel + olc::vf2d(0.0f, fScrollSpeed)) * fElapsedTime; + + // Remove Offscreen Enemies + listEnemies.remove_if([&](const sEnemy& e) {return (e.pos.y >= (float)ScreenHeight()) || e.def.fHealth <= 0.0f; }); + + // Remove finished enemy bullets + listEnemyBullets.remove_if([&](const sBullet& b) {return b.pos.x<0 || b.pos.x>ScreenWidth() || b.pos.y <0 || b.pos.y>ScreenHeight() || b.remove; }); + + // Remove finished player bullets + listPlayerBullets.remove_if([&](const sBullet& b) {return b.pos.x<0 || b.pos.x>ScreenWidth() || b.pos.y <0 || b.pos.y>ScreenHeight() || b.remove; }); + + // Remove finished fragments + listFragments.remove_if([&](const sBullet& b) {return b.pos.x<0 || b.pos.x>ScreenWidth() || b.pos.y <0 || b.pos.y>ScreenHeight() || b.remove; }); + + // GRAPHICS + Clear(olc::BLACK); + + // Update & Draw Stars + for (size_t i=0; i> 2) ? 0.8f : 1.0f); + if (s.y >= (float)ScreenHeight()) + s = { (float)(rand() % ScreenWidth()), 0.0f }; + + Draw(s, (i < aryStars.size() >> 2) ? olc::DARK_GREY : olc::WHITE); + } + + SetPixelMode(olc::Pixel::MASK); + + // Draw Enemies + for (auto& e : listEnemies) DrawSprite(e.pos, sprEnemy[e.def.nSpriteID]); + + // Draw Player + DrawSprite(vPlayerPos, sprPlayer); + + SetPixelMode(olc::Pixel::NORMAL); + + // Draw Enemy Bullets + for (auto& b : listEnemyBullets) FillCircle(b.pos, 3, olc::RED); + + // Draw Player Bullets + for (auto& b : listPlayerBullets) FillCircle(b.pos, 3, olc::CYAN); + + // Draw Fragments + for (auto& b : listFragments) Draw(b.pos, olc::YELLOW); + + // Draw Player Health Bar + DrawString(4, 4, "HEALTH:"); + FillRect(60, 4, (fPlayerHealth / 1000.0f * 576.0f), 8, olc::GREEN); + + DrawString(4, 14, "WEAPON:"); + FillRect(60, 14, (fPlayerGunTemp / 100.0f * 576.0f), 8, olc::YELLOW); + + return true; + } +}; + + +int main() +{ + Example demo; + if (demo.Construct(640, 480, 2, 2)) + demo.Start(); + return 0; +} \ No newline at end of file